├── css ├── style.css ├── map-after-fields.css ├── dashboard.css └── settings.css ├── languages ├── church-theme-content-af.mo ├── church-theme-content-da_DK.mo ├── church-theme-content-de_DE.mo ├── church-theme-content-es_ES.mo ├── church-theme-content-es_MX.mo ├── church-theme-content-fr_FR.mo ├── church-theme-content-nb_NO.mo ├── church-theme-content-nl_NL.mo ├── church-theme-content-nn_NO.mo ├── church-theme-content-pt_BR.mo ├── church-theme-content-sk_SK.mo ├── church-theme-content-sr_RS.mo ├── church-theme-content-sv_SE.mo └── important.txt ├── includes ├── admin │ ├── admin-posts.php │ ├── admin-enqueue-styles.php │ ├── admin-helpers.php │ ├── admin-enqueue-scripts.php │ ├── notices.php │ ├── dashboard.php │ ├── import.php │ ├── admin-support.php │ ├── upgrade.php │ ├── editor.php │ ├── admin-add-ons.php │ ├── admin-menu.php │ ├── admin-maps.php │ └── admin-person-fields.php ├── event-fields.php ├── mime-types.php ├── classes │ ├── CTC_Dashboard_News.php │ └── CTC_EDD_SL_Plugin_Updater.php ├── schedule.php ├── add-ons.php ├── podcast.php ├── helpers.php ├── support.php └── post-types.php ├── js ├── settings.js ├── map-after-fields.js └── lib │ └── clipboard.min.js ├── readme.txt └── church-theme-content.php /css/style.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Church Content General Styles 3 | */ 4 | 5 | -------------------------------------------------------------------------------- /languages/church-theme-content-af.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/churchthemes/church-theme-content/HEAD/languages/church-theme-content-af.mo -------------------------------------------------------------------------------- /languages/church-theme-content-da_DK.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/churchthemes/church-theme-content/HEAD/languages/church-theme-content-da_DK.mo -------------------------------------------------------------------------------- /languages/church-theme-content-de_DE.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/churchthemes/church-theme-content/HEAD/languages/church-theme-content-de_DE.mo -------------------------------------------------------------------------------- /languages/church-theme-content-es_ES.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/churchthemes/church-theme-content/HEAD/languages/church-theme-content-es_ES.mo -------------------------------------------------------------------------------- /languages/church-theme-content-es_MX.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/churchthemes/church-theme-content/HEAD/languages/church-theme-content-es_MX.mo -------------------------------------------------------------------------------- /languages/church-theme-content-fr_FR.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/churchthemes/church-theme-content/HEAD/languages/church-theme-content-fr_FR.mo -------------------------------------------------------------------------------- /languages/church-theme-content-nb_NO.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/churchthemes/church-theme-content/HEAD/languages/church-theme-content-nb_NO.mo -------------------------------------------------------------------------------- /languages/church-theme-content-nl_NL.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/churchthemes/church-theme-content/HEAD/languages/church-theme-content-nl_NL.mo -------------------------------------------------------------------------------- /languages/church-theme-content-nn_NO.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/churchthemes/church-theme-content/HEAD/languages/church-theme-content-nn_NO.mo -------------------------------------------------------------------------------- /languages/church-theme-content-pt_BR.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/churchthemes/church-theme-content/HEAD/languages/church-theme-content-pt_BR.mo -------------------------------------------------------------------------------- /languages/church-theme-content-sk_SK.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/churchthemes/church-theme-content/HEAD/languages/church-theme-content-sk_SK.mo -------------------------------------------------------------------------------- /languages/church-theme-content-sr_RS.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/churchthemes/church-theme-content/HEAD/languages/church-theme-content-sr_RS.mo -------------------------------------------------------------------------------- /languages/church-theme-content-sv_SE.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/churchthemes/church-theme-content/HEAD/languages/church-theme-content-sv_SE.mo -------------------------------------------------------------------------------- /languages/important.txt: -------------------------------------------------------------------------------- 1 | Do not add your own translations to this directory. They will be lost during plugin updates. The only .mo files that should be loaded from here are pre-made translations that come with the plugin (if any are included). 2 | 3 | Store your translations at wp-content/languages/plugins/church-theme-content-$locale.mo 4 | (e.g. church-theme-content-en_US.mo) to keep them safe from loss during updates. 5 | 6 | Read https://churchthemes.com/go/translate-plugin for more information and please contact us if you would like to contribute your translation for other churches to use. -------------------------------------------------------------------------------- /css/map-after-fields.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Map After Fields (Event / Location) 3 | */ 4 | 5 | /* Container for description and Google Map */ 6 | 7 | #ctc-map-after-fields-container { 8 | display: none; /* shown only when have data */ 9 | } 10 | 11 | /* Google Map element */ 12 | 13 | #ctc-map-after-fields { 14 | width: 100%; /* responsive */ 15 | height: 350px; 16 | max-width: 980px; /* not awkward on wide screen */ 17 | max-height: 65vh; /* not more than 2/3 of mobile screen */ 18 | } 19 | 20 | 21 | /* Description below map */ 22 | 23 | #ctc-map-after-fields-description { 24 | margin-top: 7px; 25 | } 26 | -------------------------------------------------------------------------------- /css/dashboard.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Dashboard 3 | */ 4 | 5 | /******************************************* 6 | * AT A GLANCE 7 | *******************************************/ 8 | 9 | /* Show custom post type icons */ 10 | 11 | #dashboard_right_now a.ctc_sermon-count:before, 12 | #dashboard_right_now span.ctc_sermon-count:before { 13 | content: "\f236"; 14 | } 15 | 16 | #dashboard_right_now a.ctc_event-count:before, 17 | #dashboard_right_now span.ctc_event-count:before { 18 | content: "\f145"; 19 | } 20 | 21 | #dashboard_right_now a.ctc_location-count:before, 22 | #dashboard_right_now span.ctc_location-count:before { 23 | content: "\f230"; 24 | } 25 | 26 | #dashboard_right_now a.ctc_person-count:before, 27 | #dashboard_right_now span.ctc_person-count:before { 28 | content: "\f110"; 29 | } 30 | -------------------------------------------------------------------------------- /includes/admin/admin-posts.php: -------------------------------------------------------------------------------- 1 | true 38 | ), 'objects' ); 39 | 40 | // Check if post slug matches a post type rewrite slug 41 | foreach ( $post_types as $post_type ) { 42 | if ( ! empty( $post_type->rewrite['slug'] ) && $post_type->rewrite['slug'] == $slug ) { 43 | return true; 44 | } 45 | } 46 | 47 | return $current_value; 48 | 49 | } 50 | 51 | add_filter( 'wp_unique_post_slug_is_bad_flat_slug', 'ctc_is_bad_post_slug', 10, 2 ); 52 | add_filter( 'wp_unique_post_slug_is_bad_hierarchical_slug', 'ctc_is_bad_post_slug', 10, 2 ); 53 | -------------------------------------------------------------------------------- /includes/admin/admin-enqueue-styles.php: -------------------------------------------------------------------------------- 1 | base ) { // only on Dashboard screen. 35 | wp_enqueue_style( 'ctc-dashboard', CTC_URL . '/' . CTC_CSS_DIR . '/dashboard.css', false, CTC_VERSION ); 36 | } 37 | 38 | // Plugin Settings. 39 | if ( $ctc_settings->is_settings_page() ) { // only on Plugin Settings page. 40 | wp_enqueue_style( 'ctc-settings', CTC_URL . '/' . CTC_CSS_DIR . '/settings.css', false, CTC_VERSION ); 41 | } 42 | 43 | // Styles for showing map after related fields on event/location screens. 44 | if ( ctc_has_lat_lng_fields() ) { // only if event/location screen with latitude and longitude fields supported. 45 | wp_enqueue_style( 'ctc-map-after-fields', CTC_URL . '/' . CTC_CSS_DIR . '/map-after-fields.css', false, CTC_VERSION ); 46 | } 47 | 48 | } 49 | 50 | add_action( 'admin_enqueue_scripts', 'ctc_admin_enqueue_styles' ); // admin-end only. 51 | -------------------------------------------------------------------------------- /includes/admin/admin-helpers.php: -------------------------------------------------------------------------------- 1 | slug . '&post_type=' . $post_type ) ) . '"> ' . $term->name . ''; 43 | } 44 | 45 | $list = implode( ', ', $terms_array ); 46 | 47 | } 48 | 49 | return apply_filters( 'ctc_admin_term_list', $list, $post_id, $taxonomy ); 50 | 51 | } 52 | 53 | /********************************* 54 | * CONDITIONS 55 | *********************************/ 56 | 57 | /** 58 | * Is this a Church Content plugin-provided custom post type add/edit screen? 59 | * 60 | * Example: Adding a sermon or editing an event. 61 | * 62 | * @since 2.0 63 | * @global bool $multipage 64 | * @return bool True if current post has multiple pages 65 | */ 66 | function ctc_is_cpt_add_edit() { 67 | 68 | // Default result. 69 | $result = false; 70 | 71 | // Get current screen. 72 | $screen = get_current_screen(); 73 | 74 | // Check of adding or editing a Church Content plugin-provided custom post type. 75 | if ( 'post' === $screen->base && preg_match( '/^ctc_.*$/', $screen->post_type ) ) { 76 | $result = true; 77 | } 78 | 79 | // Return filtered. 80 | return apply_filters( 'ctc_is_cpt_add_edit', $result ); 81 | 82 | } 83 | -------------------------------------------------------------------------------- /includes/event-fields.php: -------------------------------------------------------------------------------- 1 | is_settings_page() ) { // only on Plugin Settings page. 33 | 34 | // Settings script. 35 | wp_enqueue_script( 'ctc-settings', CTC_URL . '/' . CTC_JS_DIR . '/settings.js', array( 'jquery' ), CTC_VERSION ); // bust cache on update. 36 | 37 | // Clipboard.js 38 | wp_enqueue_script( 'clipboard-js', CTC_URL . '/' . CTC_JS_DIR . '/lib/clipboard.min.js', false, CTC_VERSION ); 39 | 40 | } 41 | 42 | // Scripts for showing map after related fields on event/location screens 43 | if ( ctc_has_lat_lng_fields() ) { // only if event/location screen with latitude and longitude fields supported. 44 | 45 | // Enqueue Google Maps JavaScript API. 46 | wp_enqueue_script( 'google-maps', '//maps.googleapis.com/maps/api/js?key=' . ctc_setting( 'google_maps_api_key' ), false, null ); // no version, generic name to share w/plugins. 47 | 48 | // Script for initializing and interacting with map. 49 | wp_enqueue_script( 'ctc-map-after-fields', CTC_URL . '/' . CTC_JS_DIR . '/map-after-fields.js', false, CTC_VERSION ); 50 | wp_localize_script( 'ctc-map-after-fields', 'ctc_map_after_fields_data', array( // data to use in JS. 51 | 'get_from_address_failed' => __( 'Address could not be converted. Check the address or enter your city then click the map to pinpoint your location.', 'church-theme-content' ), 52 | 'missing_address' => __( 'Please enter an Address above.', 'church-theme-content' ), 53 | 'missing_key_message' => __( 'Go to Settings > Church Content > Locations to set your Google Maps API Key to use this button.', 'church-theme-content' ), 54 | 'has_api_key' => ctc_setting( 'google_maps_api_key' ) ? true : false, 55 | ) ); 56 | 57 | } 58 | 59 | } 60 | 61 | add_action( 'admin_enqueue_scripts', 'ctc_admin_enqueue_scripts' ); // admin-end only. 62 | -------------------------------------------------------------------------------- /includes/mime-types.php: -------------------------------------------------------------------------------- 1 | (%s)', 'PDFs (%s)', 'church-theme-content' ) 78 | ); 79 | 80 | return $post_mime_types; 81 | 82 | } 83 | 84 | add_filter( 'post_mime_types', 'ctc_add_post_mime_types' ); 85 | -------------------------------------------------------------------------------- /js/settings.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Plugin Settings 3 | */ 4 | 5 | jQuery(document).ready(function ($) { 6 | 7 | /************************************** 8 | * PRO DISABLED 9 | **************************************/ 10 | 11 | // Add .ctc-pro-setting-inactive and .button-disabled to image button when image input readonly. 12 | if ($('#ctps-field-podcast_image').hasClass('ctc-setting-readonly')) { 13 | $('.ctps-upload-file', $('#ctps-field-podcast_image').parent('.ctps-section')).addClass('ctc-pro-setting-inactive button-disabled'); 14 | } 15 | 16 | // Show notice when user engages field requiring Pro while Pro is inactive. 17 | // The Pro fields have readonly attribute so cannot be changed, but will be saved. 18 | $('.ctc-pro-setting-inactive').on('focus, click', function (e) { 19 | 20 | // Prevent clicks on links from having effect. 21 | e.preventDefault(); 22 | 23 | // Remove previous instance of message before showing new. 24 | $('.ctc-pro-setting-inactive-message-inline').remove(); 25 | 26 | // Get parent cell. 27 | var $field_container = $(this).parents('td'); 28 | 29 | // Get message from section description. 30 | var $message = $('.ctc-pro-setting-inactive-message:visible').html(); 31 | 32 | // Have message. 33 | if ($message) { 34 | 35 | // Copy message below field. 36 | $field_container.append('' + $message + ''); 37 | 38 | // Fade it in. 39 | $('.ctc-pro-setting-inactive-message-inline').hide().fadeIn('fast'); 40 | 41 | } 42 | 43 | }); 44 | 45 | // Prevent checkbox/radio changes on inactive fields (due to missing theme support or Pro being required). 46 | // readonly attribute does not stop changes to checkbox states. 47 | $('input[type=checkbox].ctc-setting-readonly, input[type=radio].ctc-setting-readonly').on('click', function (e) { 48 | return false; 49 | }); 50 | 51 | /************************************** 52 | * PODCAST 53 | **************************************/ 54 | 55 | // Open Podcast section when "Podcast Settings" link clicked in Sermons section. 56 | $('.ctps-field-podcast_content a').on('click', function (e) { 57 | 58 | // Prevent regular click action. 59 | e.preventDefault(); 60 | 61 | // Switch to Podcast tab. 62 | ctps_switch_section('podcast'); 63 | 64 | }); 65 | 66 | // Copy Feed URL to clipboard. 67 | var clipboard = new ClipboardJS('#ctc-copy-podcast-url-button', { 68 | 69 | // Get URL. 70 | text: function (trigger) { 71 | return $('#ctc-settings-podcast-feed-link').attr('href'); 72 | } 73 | 74 | }).on('success', function (e) { 75 | 76 | // Show message. 77 | $('#ctc-podcast-url-copied').fadeIn('fast'); 78 | 79 | // Hide message. 80 | setTimeout(function () { 81 | $('#ctc-podcast-url-copied').fadeOut('fast'); 82 | }, 3000); 83 | 84 | // Stop click. 85 | return false; 86 | 87 | }); 88 | 89 | // Stop click to # on Copy button. 90 | $('#ctc-copy-podcast-url-button').on('click', function (e) { 91 | e.preventDefault(); 92 | }); 93 | 94 | }); 95 | -------------------------------------------------------------------------------- /includes/admin/notices.php: -------------------------------------------------------------------------------- 1 | php_is_old ) { 39 | $show = false; 40 | } 41 | 42 | // Show notice only if recurrence is enabled. 43 | if ( ! ctc_field_supported( 'events', '_ctc_event_recurrence' ) ) { 44 | $show = false; 45 | } 46 | 47 | // Show notice only on relevant screens. 48 | // Dashboard, events list and event add/edit. 49 | $screen = get_current_screen(); 50 | if ( 'dashboard' !== $screen->base && 'ctc_event' !== $screen->post_type ) { 51 | $show = false; 52 | } 53 | 54 | // Show notice. 55 | if ( $show ) { 56 | 57 | $content = sprintf( 58 | wp_kses( 59 | /* translators: %1$s is minimum required version of PHP, %2$s is URL with information on updating PHP. */ 60 | __( 'Event recurrence is disabled. PHP %1$s or newer is required for recurrence to work. Update PHP to resolve.', 'church-theme-content' ), 61 | array( 62 | 'strong' => array(), 63 | 'a' => array( 64 | 'href' => array(), 65 | 'target' => array(), 66 | ), 67 | ) 68 | ), 69 | esc_html( $ct_recurrence->php_min_version ), 70 | esc_url( ctc_ctcom_url( 'update-php', array( 'utm_content' => 'recurrence' ) ) ) 71 | ); 72 | 73 | } 74 | 75 | // Return. 76 | return apply_filters( '', $content ); 77 | 78 | } 79 | 80 | /** 81 | * Show PHP update notice when CT Recurrence class needs it. 82 | * 83 | * CT Recurrence methods return empty when PHP version not satisfied. 84 | * Recurring events are treated as non-recurring until PHP is updated. 85 | * 86 | * @since 2.0 87 | */ 88 | function ctc_recurrence_php_notice() { 89 | 90 | // Get notice if it is to be shown. 91 | $notice = ctc_recurrence_php_note(); 92 | 93 | // Have notice. 94 | if ( $notice ) { 95 | 96 | ?> 97 |
98 |

99 | 100 |

101 |
102 | get_feed_urls( $url ); 88 | 89 | // Update the feed sources. 90 | $feed->set_feed_url( $all_feed_urls ); 91 | 92 | } 93 | 94 | /** 95 | * Get the feed URL(s) to add. 96 | * 97 | * @since 1.0 98 | * 99 | * @param string $url Planet Feed URL. 100 | * 101 | * @return array Array of Feed URLs. 102 | */ 103 | public function get_feed_urls( $url ) { 104 | 105 | // Initialize the feeds array. 106 | $feed_urls = array( 107 | 'https://churchthemes.com/feed/?dashboard_news=1', // ?dashboard_news=1 prepends "ChurchThemes.com: " to titles consistent with planet feed 108 | $url, 109 | ); 110 | 111 | // Return the feed URL(s). 112 | return array_unique( $feed_urls ); 113 | 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /includes/admin/dashboard.php: -------------------------------------------------------------------------------- 1 | $feature_data ) { 40 | 41 | // Get post type. 42 | $post_type = $feature_data['post_type']; 43 | 44 | // Only if features is supported and post type is registered. 45 | if ( ! ctc_feature_supported( $feature ) || ! post_type_exists( $post_type ) ) { 46 | continue; 47 | } 48 | 49 | // Get post counts (published, trash, etc.). 50 | $num_posts = wp_count_posts( $post_type ); 51 | 52 | // Only if have count data. 53 | if ( $num_posts ) { 54 | 55 | // Count published posts to show. 56 | $published = intval( $num_posts->publish ); 57 | 58 | // Get post type data. 59 | $post_type_data = get_post_type_object( $post_type ); 60 | 61 | // Singular or plural label based on published post count. 62 | if ( 1 === $published ) { 63 | $text = number_format_i18n( $published ) . ' ' . $post_type_data->labels->singular_name; 64 | } else { 65 | $text = number_format_i18n( $published ) . ' ' . $post_type_data->labels->name; 66 | } 67 | 68 | // Show linked if user can edit posts. 69 | if ( current_user_can( $post_type_data->cap->edit_posts ) ) { 70 | $items[] = sprintf( '%2$s', $post_type, $text ) . "\n"; 71 | } else { // Otherwise show unlinked. 72 | $items[] = sprintf( '%2$s', $post_type, $text ) . "\n"; 73 | } 74 | 75 | } 76 | 77 | } 78 | 79 | return $items; 80 | 81 | } 82 | 83 | add_filter( 'dashboard_glance_items', 'ctc_glance_add_post_types', 10, 1 ); 84 | 85 | /************************************************* 86 | * NEWS 87 | *************************************************/ 88 | 89 | /** 90 | * Show ChurchThemes.com feed items in News widget. 91 | * 92 | * @since 2.0 93 | */ 94 | function ctc_dashboard_news() { 95 | 96 | // Must be enabled in settings. 97 | if ( ctc_setting( 'dashboard_news' ) ) { 98 | 99 | // Create an instance. 100 | new CTC_Dashboard_News(); 101 | 102 | } 103 | 104 | } 105 | 106 | add_filter( 'admin_init', 'ctc_dashboard_news' ); 107 | -------------------------------------------------------------------------------- /includes/admin/import.php: -------------------------------------------------------------------------------- 1 | 38 | 39 |
40 |

41 | More Information', 'church-theme-content' ), 46 | array( 47 | 'b' => array(), 48 | 'a' => array( 49 | 'href' => array(), 50 | 'target' => array(), 51 | ) 52 | ) 53 | ), 54 | CTC_NAME, 55 | 'https://wordpress.org/plugins/church-theme-content/' 56 | ); 57 | ?> 58 |

59 |
60 | 61 | *, /* all but last */ 93 | tr.ctps-field-sermon_series_url_slug > *, 94 | tr.ctps-field-sermon_book_url_slug > *, 95 | tr.ctps-field-sermon_speaker_url_slug > * { 96 | padding-bottom: 0; 97 | } 98 | 99 | tr.ctps-field-sermon_series_url_slug > *, /* all but first */ 100 | tr.ctps-field-sermon_book_url_slug > *, 101 | tr.ctps-field-sermon_speaker_url_slug > *, 102 | tr.ctps-field-sermon_tag_url_slug > * { 103 | padding-top: 14px; 104 | } 105 | 106 | /******************************************* 107 | * PODCAST 108 | *******************************************/ 109 | 110 | #ctc-settings-podcast-feed-buttons { 111 | margin-top: 8px; 112 | margin-bottom: 5px; 113 | } 114 | 115 | #ctc-settings-podcast-feed-link { 116 | padding-right: 4px; 117 | } 118 | 119 | #ctc-podcast-url-copied { 120 | display: none; 121 | font-size: 0.9em; 122 | color: #666; 123 | white-space: nowrap; 124 | } 125 | 126 | /* Make filtering select fields closer */ 127 | 128 | tr.ctps-field-podcast_topic th, 129 | tr.ctps-field-podcast_topic td { 130 | padding-bottom: 0; 131 | } 132 | 133 | tr.ctps-field-podcast_book th, 134 | tr.ctps-field-podcast_book td, 135 | tr.ctps-field-podcast_series th, 136 | tr.ctps-field-podcast_series td, 137 | tr.ctps-field-podcast_speaker th, 138 | tr.ctps-field-podcast_speaker td { 139 | padding-top: 7px; 140 | padding-bottom: 0; 141 | } 142 | 143 | tr.ctps-field-podcast_tag th, 144 | tr.ctps-field-podcast_tag td { 145 | padding-top: 7px; 146 | } 147 | 148 | -------------------------------------------------------------------------------- /includes/admin/admin-support.php: -------------------------------------------------------------------------------- 1 | base, array( 'themes', 'plugins' ) ) && ! preg_match( '/^ctc_.+/', $screen->post_type ) ) { 40 | return; 41 | } 42 | 43 | // Show except on discontinued Risen theme (in that case we show migration notice). 44 | if ( ctc_migrate_risen_show() ) { 45 | return; 46 | } 47 | 48 | // Option ID 49 | $theme_data = wp_get_theme(); 50 | $option_id = 'ctc_hide_theme_support_notice-' . $theme_data['Template']; // unique to theme so if change, message shows again 51 | 52 | // Message has not been dismissed for this theme 53 | if ( ! get_option( $option_id ) ) { 54 | 55 | // Nonce 56 | $nonce = wp_create_nonce( 'ctc_hide_theme_support_notice' ); 57 | 58 | // Dismiss URL 59 | $dismiss_url = add_query_arg( [ 60 | 'ctc_hide_theme_support_notice' => '1', 61 | 'ctc_hide_theme_support_notice_security' => $nonce 62 | ] ); 63 | 64 | ?> 65 |
66 |

67 | %1$s theme does not support the %2$s plugin. More Information, Dismiss', 'church-theme-content' ), 71 | array( 72 | 'b' => array(), 73 | 'a' => array( 74 | 'href' => array(), 75 | 'target' => array(), 76 | ) 77 | ) 78 | ), 79 | wp_get_theme(), 80 | CTC_NAME, 81 | 'https://wordpress.org/plugins/church-theme-content/', 82 | esc_url( $dismiss_url ) 83 | ); 84 | ?> 85 |

86 |
87 | 'ctc_event', 56 | 'nopaging' => true, 57 | 'meta_query' => array( 58 | 'relation' => 'AND', 59 | array( 60 | 'key' => '_ctc_event_end_date', 61 | 'value' => date_i18n( 'Y-m-d' ), // today localized. 62 | 'compare' => '<', // earlier than today. 63 | 'type' => 'DATE', 64 | ), 65 | array( 66 | 'key' => '_ctc_event_recurrence', 67 | 'value' => array( 'weekly', 'monthly', 'yearly' ), 68 | 'compare' => 'IN', 69 | ), 70 | ), 71 | ) ); 72 | 73 | // Loop events. 74 | if ( ! empty( $events_query->posts ) ) { 75 | 76 | // Instantiate recurrence class. 77 | $ctc_recurrence = new CT_Recurrence(); 78 | 79 | // Loop events to modify dates. 80 | foreach ( $events_query->posts as $post ) { 81 | 82 | // Get start and end date. 83 | $start_date = get_post_meta( $post->ID, '_ctc_event_start_date', true ); 84 | $end_date = get_post_meta( $post->ID, '_ctc_event_end_date', true ); 85 | 86 | // Get recurrence. 87 | $recurrence = get_post_meta( $post->ID, '_ctc_event_recurrence', true ); 88 | $recurrence_end_date = get_post_meta( $post->ID, '_ctc_event_recurrence_end_date', true ); 89 | 90 | // Difference between start and end date in seconds. 91 | $time_difference = strtotime( $end_date ) - strtotime( $start_date ); 92 | 93 | // Get soonest occurrence that is today or later. 94 | $args = array( 95 | 'start_date' => $start_date, // first day of event, YYYY-mm-dd (ie. 2015-07-20 for July 15, 2015). 96 | 'frequency' => $recurrence, // weekly, monthly, yearly. 97 | ); 98 | $args = apply_filters( 'ctc_event_recurrence_args', $args, $post ); // Pro / Custom Recurring Events add-ons hook into this. 99 | $new_start_date = $ctc_recurrence->calc_next_future_date( $args ); 100 | 101 | // If no new start date gotten, set it to current start date. 102 | // This could be because recurrence ended, arguments are invalid, etc. 103 | if ( ! $new_start_date ) { 104 | $new_start_date = $start_date; 105 | } 106 | 107 | // Add difference between original start/end date to new start date to get new end date. 108 | $new_end_date = date( 'Y-m-d', ( strtotime( $new_start_date ) + $time_difference ) ); 109 | 110 | // Has recurrence ended? 111 | // Recurrence end date exists and is earlier than new start date. 112 | if ( $recurrence_end_date && strtotime( $recurrence_end_date ) < strtotime( $new_start_date ) ) { 113 | 114 | // Unset recurrence option to keep dates from being moved forward. 115 | update_post_meta( $post->ID, '_ctc_event_recurrence', 'none' ); 116 | 117 | } 118 | 119 | // No recurrence or recurrence end date is still future. 120 | else { 121 | 122 | // Update start and end dates. 123 | update_post_meta( $post->ID, '_ctc_event_start_date', $new_start_date ); 124 | update_post_meta( $post->ID, '_ctc_event_end_date', $new_end_date ); 125 | 126 | // Update the hidden datetime fields for ordering. 127 | ctc_update_event_date_time( $post->ID ); 128 | 129 | } 130 | 131 | // Action after process a single recurring event. 132 | // Example: Pro hooks into this to remove past excluded dates. 133 | do_action( 'ctc_update_recurring_event_dates_after', $post->ID ); 134 | 135 | } 136 | 137 | } 138 | 139 | } 140 | 141 | add_action( 'ctc_update_recurring_event_dates', 'ctc_update_recurring_event_dates' ); 142 | 143 | // Uncomment for development and debugging. Recurrence will run on every page load. 144 | // Or, install the Crontrol plugin to execute the scheduled cron event on demand. 145 | //add_action( 'admin_init', 'ctc_update_recurring_event_dates' ); 146 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === Church Content - Sermons, Events and More === 2 | Contributors: churchthemes, stevengliebe, galengidman 3 | Tags: church, churches, sermon, sermons, ministry, ministries, event, events, calendar, location, locations 4 | Requires at least: 3.6 5 | Tested up to: 6.9 6 | Requires PHP: 7.4 7 | Stable tag: 2.7 8 | License: GPLv2 or later 9 | License URI: http://www.gnu.org/licenses/gpl-2.0.html 10 | 11 | Provides an interface for managing sermons, events, people and locations. A **compatible theme is required** for presenting content from these church-centric post types in a tightly-integrated manner. 12 | 13 | == Description == 14 | 15 | The Church Content WordPress plugin provides an interface for managing sermons, events, people and locations. Please be aware that a **compatible theme is required** for presenting content from these church-centric post types in a tightly-integrated manner. 16 | 17 | = Who this is for = 18 | 19 | This plugin is intended for a very specific audience. If you are *not* one of the following then this plugin will not be useful to you. 20 | 21 | * Users of [official themes](https://churchthemes.com) from ChurchThemes.com 22 | * Users of [third-party themes](https://churchthemes.com/third-party-themes) made *specifically* for this plugin 23 | * Theme developers improving data portability for church sites 24 | 25 | = What it does = 26 | 27 | It provides post types, taxonomies and fields for *sermons*, *events*, *people* and *locations* to be shown by a compatible church WordPress theme. 28 | 29 | Experienced WordPress developers agree that functionality like this does not belong in themes since themes are intended only for controlling the appearance of a WordPress site. Content that users might expect to take with them if they switch themes should "live" in a plugin in order to [avoid theme lock-in](https://churchthemes.com/wordpress-theme-users-avoid-lock-in-effect/). Similarly, our approach is not to present content using the plugin since themes offer more control for that purpose. This is why a **compatible theme is required**. 30 | 31 | See the [Plugin Details](https://churchthemes.com/plugins/church-content/) on ChurchThemes.com for a tour of features. Read the [User Guide](https://churchthemes.com/guides/user/plugins/church-content/) for instructions. 32 | 33 | = Compatible Themes = 34 | 35 | A compatible theme is required for presenting sermons, events, etc. added with the plugin. 36 | 37 | * Official themes provided by [ChurchThemes.com](https://churchthemes.com) 38 | * Third-party themes at [churchthemes.com/third-party-themes](https://churchthemes.com/third-party-themes) 39 | 40 | = Church Content Pro = 41 | 42 | [Church Content Pro](https://churchthemes.com/plugins/church-content-pro/) is an add-on that provides additional features. 43 | 44 | * Sermon Podcasting (iTunes, Google Play, etc.) 45 | * Recurring Events (e.g. "every Monday and Thursday") 46 | * Event Date Exclusions (holidays, weather closings, etc.) 47 | * Enhanced SEO (schema.org Structure Data using JSON-LD) 48 | * Location Memory (autofill and select previous event locations) 49 | * Wording Settings (e.g. rename "Sermons" to "Messages") 50 | * Agency Mode (simplify license management for clients) 51 | 52 | = Developers = 53 | 54 | Please see the [Developer Guide](https://churchthemes.com/guides/developer/church-content/) on ChurchThemes.com for notes on developing themes that use this plugin. A couple benefits are that you will save time and be helping to accomplish better data portability among church websites powered by WordPress. 55 | 56 | You can follow development on [GitHub](https://github.com/churchthemes/church-theme-content). 57 | 58 | = Translations = 59 | 60 | Several translations are available, including English, Spanish, German, French, Portuguese, Swedish, Dutch, Norwegian, Serbian and Slovak. See the [Translations](https://churchthemes.com/guides/user/plugins/church-content/#translations) section in the plugin's user guide for a more complete list. Also see [translate.wordpress.org](https://translate.wordpress.org/projects/wp-plugins/church-theme-content). 61 | 62 | = Follow us = 63 | 64 | * Visit [ChurchThemes.com](https://churchthemes.com) for compatible themes, add-ons and help 65 | * We're on [Twitter](https://twitter.com/churchthemes), [Facebook](https://www.facebook.com/churchthemescom) and have a [Newsletter](https://churchthemes.com/newsletter) 66 | * Lead developer: [stevengliebe.com](http://stevengliebe.com) 67 | 68 | **Note:** This plugin was previously named "Church Theme Content". 69 | 70 | == Installation == 71 | 72 | 1. Log into your WordPress admin area 73 | 2. Go to **Plugins** > **Add New** 74 | 3. Search for *Church Content* 75 | 4. Click **Install Now** for the plugin 76 | 5. Click **Activate Plugin** after installation 77 | 78 | Please see our [Plugin Installation](https://churchthemes.com/guides/user/plugins/plugin-installation/) guide for additional information. 79 | 80 | == Frequently Asked Questions == 81 | 82 | = Can I use this plugin with any theme? = 83 | 84 | No, a theme made specifically for use with this plugin is necessary for displaying content. Please see *Who this is for* in the plugin description. 85 | 86 | = Can I make a theme that uses this plugin? = 87 | 88 | Yes, please see the [Developer Guide](https://churchthemes.com/guides/developer/church-content/). 89 | 90 | = How can I make an event recur in a special way? = 91 | 92 | Install the [Church Content Pro](https://churchthemes.com/plugins/church-content-pro/) add-on for recurrence like "every two weeks" and "third Sunday of every month". 93 | 94 | = How can I make content show differently? = 95 | 96 | Please contact the maker of your theme. This plugin handles the management of content in a way friendly to both users and theme developers. The presentation of content is handled by a compatible theme. 97 | 98 | == Screenshots == 99 | 100 | 1. Sermons 101 | 2. Add sermon 102 | 3. Sermon topics 103 | 4. Events 104 | 5. Add event 105 | 6. Locations 106 | 7. Add location 107 | 8. People 108 | 9. Add person 109 | 110 | == Changelog == 111 | 112 | See [releases](https://github.com/churchthemes/church-theme-content/releases) on GitHub. 113 | -------------------------------------------------------------------------------- /includes/add-ons.php: -------------------------------------------------------------------------------- 1 | __FILE__, // Full path to plugin main file (__FILE__ if this code is in main file) 37 | * 'store_url' => 'https://churchthemes.com', // URL of store running EDD with Software Licensing extension (or proxy) 38 | * 'renewal_url' => 'https://churchthemes.com/renew/?license_key={license_key}', // It is recommended to provide a URL for renewal links (ie. redirecting to EDD checkout); {license_key} will be replaced with key 39 | * 'renewal_info_url' => 'https://churchthemes.com/go/license-renewal', // Optional URL for renewal information 40 | * ) ); 41 | * 42 | * } 43 | * 44 | * } 45 | * 46 | * add_action( 'plugins_loaded', 'prefix_register_add_on' ); 47 | * 48 | * See ctc_register_add_on() for all possible arguments (most are automatically filled but can be overridden). 49 | * It is highly recommended that a renewal_url is provided to make license renewal easy for users. 50 | * 51 | * @since 1.2 52 | * @param array $args Add-on data including what is necessary for EDD Software Licensing 53 | * @global array $ctc_add_ons 54 | */ 55 | function ctc_register_add_on($args) 56 | { 57 | 58 | global $ctc_add_ons; 59 | 60 | // Prepare array to receive add-ons 61 | if (! isset($ctc_add_ons)) { 62 | $ctc_add_ons = array(); 63 | } 64 | 65 | // Add add-on to global 66 | if (! empty($args['plugin_file'])) { 67 | 68 | // Plugin file relative path 69 | // plugin-name/plugin-name.php 70 | $args['plugin_file_base'] = plugin_basename($args['plugin_file']); 71 | 72 | // Plugin's directory name (e.g. plugin-name) 73 | // This serves as a nice clean, unique slug -- good for use in settings 74 | $args['plugin_dir'] = dirname($args['plugin_file_base']); 75 | 76 | // Get plugin data 77 | // wp-admin/includes/plugin.php is already included by Church_Theme_Content class 78 | $plugin_data = current(get_plugins('/' . $args['plugin_dir'])); 79 | 80 | // Plugin name 81 | $args['name_full'] = $plugin_data['Name']; 82 | $name_short = str_replace(array(CTC_NAME . ' - ', 'Church Theme Content - '), '', $args['name_full']); // with "Church Content - " or "Church Theme Content - " prefix removed 83 | 84 | // Plugin author 85 | $author = strip_tags($plugin_data['Author']); 86 | 87 | // Apply defaults 88 | $args = wp_parse_args($args, array( 89 | 90 | // Add-on Information 91 | 'plugin_file' => '', // Required; absolute path to plugin-name/plugin-name.php; use __FILE__ when in main file (Required) 92 | 'plugin_file_base' => '', // This will auto-fill (relative path to main plugin file) 93 | 'plugin_dir' => '', // This will auto-fill (plugin's directory name) 94 | 'name' => $name_short, // Shown on license key settings and notices; default is plugin name with "Church Content - " prefix removed 95 | 'name_full' => '', // This will auto-fill (full name of plugin) 96 | 97 | // EDD Software Licensing 98 | // The translation strings are automatically escaped on use in edd-license.php 99 | 'store_url' => '', // URL of store running EDD with Software Licensing extension (or proxy) 100 | 'version' => $plugin_data['Version'], // current version of the add-on plugin; default is to auto-determine 101 | 'item_name' => $name_short, // must match download's name in EDD store 102 | 'author' => $author, // default is to auto-determine from add-on plugin 103 | 'updates' => true, // default true; enable one-click updates 104 | 'expiring_soon_days' => 7, // days before expiration to consider a license "expiring soon" 105 | 'renewal_url' => '', // optional URL for renewal links (ie. EDD checkout); {license_key} will be replaced with key 106 | 'renewal_info_url' => '', // optional URL for renewal information 107 | 'changelog_url' => '', // optional URL for external changelog 108 | 'activate_success_notice' => __('License key activated.', 'church-theme-content'), 109 | 'activate_fail_notice' => __('License key could not be activated.', 'church-theme-content'), 110 | 'deactivate_success_notice' => __('License key deactivated.', 'church-theme-content'), 111 | 'deactivate_fail_notice' => __('License key could not be deactivated.', 'church-theme-content'), 112 | 'inactive_notice' => __('Add-on License Inactive: Activate Your Add-on License to enable updates for %2$s.', 'church-theme-content'), 113 | 'expired_notice' => __('Add-on License Expired: Renew Your Add-on License for %2$s to re-enable updates (expired on %3$s).', 'church-theme-content'), 114 | 'expiring_soon_notice' => __('Add-on License Expiring Soon: Renew Your Add-on License for %2$s to continue receiving updates (expires on %3$s).', 'church-theme-content'), 115 | 116 | )); 117 | 118 | // Add add-on to global array 119 | $ctc_add_ons[$args['plugin_dir']] = $args; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /includes/admin/upgrade.php: -------------------------------------------------------------------------------- 1 | get_var( $wpdb->prepare( " SELECT COUNT( * ) FROM {$wpdb->posts} WHERE post_type IN( '%s', '%s', '%s', '%s' ) ", 'ctc_sermon', // these four post types existed before 1.2, so no newer ones need to be counted. 'ctc_event', 'ctc_person', 'ctc_location' ) ); if ( $ctc_post_count ) { $old_version = '1.1.1'; // assume 1.1.1, the last version before database upgrades introduced in 1.2. } // Otherwise, treat this as a first-time plugin installation. // In this case, no database upgrade is necessary because there is no data (set current version). else { $old_version = CTC_VERSION; } // Set the initial version number. update_option( 'ctc_version', $old_version ); } // Has plugin just been updated to new version? // We know it did if old version in database is less than plugin's current version. if ( version_compare( CTC_VERSION, $old_version, '>' ) ) { ctc_run_upgrade( $old_version ); } } add_action( 'admin_init', 'ctc_check_upgrade' ); /** * Run Necessary Upgrades * * Sequentially execute upgrade functions for one or more versions. * * @since 1.2 * @param string $old_version Version used before plugin update. */ function ctc_run_upgrade( $old_version ) { // Ensure the upgrade routine finishes. ignore_user_abort( true ); if ( ctc_function_available( 'set_time_limit' ) && ! ini_get( 'safe_mode' ) ) { // check to avoid warnings. set_time_limit( 0 ); } // Get versions requiring database upgrade, in sequential order. $upgrade_versions = ctc_upgrade_versions(); // Sequentially execute upgrade functions for older versions. foreach ( $upgrade_versions as $upgrade_version ) { // Do not run version's upgrade if it is older than version in database. // The upgrade should have already run for that version, since it is in the database. if ( version_compare( $old_version, $upgrade_version, '>=' ) ) { continue; } // Do not run version's upgrade if it is newer than new plugin version. // This would only happen plugin author specifies upgrade function with wrong number. if ( version_compare( $upgrade_version, CTC_VERSION, '>' ) ) { continue; } // Debug (also comment out update_option() for new version below). //echo "
New Version: " . CTC_VERSION . ", Old Version: $old_version, Upgrade Function: $upgrade_version"; // Execute version's upgrade function. // Example: 1.5.2 will execute ctc_upgrade_1_5_2(). $upgrade_version_clean = str_replace( '.', '_', $upgrade_version ); $function = 'ctc_upgrade_' . $upgrade_version_clean; if ( function_exists( $function ) ) { call_user_func( $function ); } } // Always flush rewrite rules in case post types or taxonomies added. flush_rewrite_rules(); // Always update version number in the database, even if no upgrade function is run. update_option( 'ctc_version', CTC_VERSION ); } -------------------------------------------------------------------------------- /js/map-after-fields.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Map after fields on event and location screen 3 | */ 4 | 5 | jQuery(document).ready(function ($) { 6 | 7 | /************************************** 8 | * SHOW MAP 9 | **************************************/ 10 | 11 | // Show initial map 12 | ctc_show_map_after_fields(); 13 | 14 | // Latitude/longitude change or keyup 15 | $('.ctc-map-lat-field, .ctc-map-lng-field').on('change keyup', function () { 16 | ctc_show_map_after_fields('coordinates'); 17 | }); 18 | 19 | // Latitude/longitude paste 20 | $('.ctc-map-lat-field, .ctc-map-lng-field').on('paste', function () { 21 | 22 | // Update map map after timeout 23 | setTimeout(function () { 24 | ctc_show_map_after_fields('coordinates'); 25 | }, 250); 26 | 27 | }); 28 | 29 | // Type change 30 | $('.ctc-map-type-field').on('change', function () { 31 | ctc_show_map_after_fields('type'); 32 | }); 33 | 34 | // Zoom change 35 | $('.ctc-map-zoom-field').on('input', function () { // do it as user drags, not just on release (change) 36 | ctc_show_map_after_fields('zoom'); // zoom only so user can drag/zoom to manually click 37 | }); 38 | 39 | /************************************** 40 | * GEOCODE ADDRESS 41 | **************************************/ 42 | 43 | // Click "Get From Address" button 44 | $('#ctc-get-coordinates-button').on('click', function () { 45 | 46 | var address; 47 | 48 | // Show alert if no API Key 49 | if (!ctc_map_after_fields_data.has_api_key) { 50 | 51 | alert(ctc_map_after_fields_data.missing_key_message); 52 | 53 | // Don't attempt geocoding 54 | return; 55 | 56 | } 57 | 58 | // Get address 59 | address = $('.ctc-address-field').val(); // event or location 60 | address = $("
").html(address).text(); // remove HTML 61 | address = address.replace(/\r?\n/g, ', '); // replace line breaks with commas 62 | address = address.trim(); // remove whitespace 63 | 64 | // Have address 65 | if (address) { 66 | 67 | // Geocode address 68 | geocoder = new google.maps.Geocoder(); 69 | geocoder.geocode({ 70 | 'address': address 71 | }, function (results, status) { 72 | 73 | var coordinates; 74 | 75 | // Success 76 | if (google.maps.GeocoderStatus.OK == status) { 77 | 78 | // Update Latitude and Longitude fields 79 | $('.ctc-map-lat-field').val(results[0].geometry.location.lat); 80 | $('.ctc-map-lng-field').val(results[0].geometry.location.lng); 81 | 82 | // Map not showing (new Add), show it based on fields 83 | if (!$('#ctc-map-after-fields-container').is(':visible')) { 84 | ctc_show_map_after_fields(); 85 | } 86 | 87 | // Map showing, just update it by moving marker/center 88 | else { 89 | 90 | // Get coordinates 91 | coordinates = results[0].geometry.location; 92 | 93 | // Move marker and recenter 94 | ctc_map_after_fields_marker.setPosition(coordinates); 95 | ctc_map_after_fields.panTo(coordinates); 96 | 97 | } 98 | 99 | } 100 | 101 | // Failure 102 | else { 103 | 104 | // Give instructions 105 | alert(ctc_map_after_fields_data.get_from_address_failed); 106 | 107 | } 108 | 109 | }); 110 | 111 | } 112 | 113 | // No address, show alert 114 | else { 115 | alert(ctc_map_after_fields_data.missing_address); 116 | } 117 | 118 | }); 119 | 120 | }); 121 | 122 | /************************************** 123 | * FUNCTIONS 124 | **************************************/ 125 | 126 | // Show Map 127 | function ctc_show_map_after_fields(update) { 128 | 129 | var lat, lng, zoom, type, coordinates, click_timeout; 130 | 131 | // Only if map container exists 132 | if (!jQuery('#ctc-map-after-fields-container').length) { 133 | return; 134 | } 135 | 136 | // Get Coordinates 137 | lat = parseFloat(jQuery('.ctc-map-lat-field').val()); 138 | lng = parseFloat(jQuery('.ctc-map-lng-field').val()); 139 | 140 | // Get Type 141 | type = 'ROAD'; // default if no type field 142 | if (jQuery('.ctc-map-type-field').length) { // field supported 143 | type = jQuery('.ctc-map-type-field:checked').val(); 144 | } 145 | 146 | // Get Zoom 147 | zoom = 14; // default if no zoom field 148 | if (jQuery('.ctc-map-zoom-field').length) { // field supported 149 | zoom = parseFloat(jQuery('.ctc-map-zoom-field').val()); 150 | } 151 | 152 | // Latitude and Longitude entered 153 | if (lat && lng) { 154 | 155 | // Coordinates 156 | coordinates = { lat: lat, lng: lng }; 157 | 158 | // Map not showing (new Add), show it based on fields 159 | if (!jQuery('#ctc-map-after-fields-container').is(':visible')) { 160 | 161 | // Show map container 162 | // Show container first because hidden div has no size 163 | // Doing this first prevents empty gray map in some cases 164 | jQuery('#ctc-map-after-fields-container').show(); 165 | 166 | // Render map first time 167 | ctc_map_after_fields = new google.maps.Map(document.getElementById('ctc-map-after-fields'), { 168 | center: coordinates, 169 | zoom: zoom, 170 | mapTypeId: google.maps.MapTypeId[type], 171 | disableDefaultUI: true, // form fields control zoom, type, etc. 172 | scrollwheel: false, // disable scroll zoom (mistake prone, let use Zoom field) 173 | clickableIcons: false, // place names interfere with manual location clicking 174 | }); 175 | 176 | // Add marker to new map 177 | ctc_map_after_fields_marker = new google.maps.Marker({ 178 | position: coordinates, 179 | map: ctc_map_after_fields, 180 | }); 181 | 182 | // Move marker to point clicked 183 | click_timeout = null; 184 | ctc_map_after_fields.addListener('click', function (event) { 185 | 186 | click_timeout = setTimeout(function () { 187 | 188 | // Move marker 189 | ctc_map_after_fields_marker.setPosition(event.latLng); 190 | 191 | // Center on marker 192 | setTimeout(function () { 193 | ctc_map_after_fields.panTo(event.latLng); 194 | }, 200); 195 | 196 | // Update Latitude/Longitude fields 197 | jQuery('.ctc-map-lat-field').val(event.latLng.lat()); 198 | jQuery('.ctc-map-lng-field').val(event.latLng.lng()); 199 | 200 | }, 200); 201 | 202 | }); 203 | 204 | // Prevent single click from being triggered on double click 205 | ctc_map_after_fields.addListener('dblclick', function () { 206 | clearTimeout(click_timeout); 207 | }); 208 | 209 | // Make double click to zoom update Zoom field 210 | ctc_map_after_fields.addListener('zoom_changed', function () { 211 | jQuery('.ctc-map-zoom-field').val(ctc_map_after_fields.getZoom()); 212 | }); 213 | 214 | } 215 | 216 | // Map already showing, just update it by adjusting zoom, type and moving marker/center 217 | else { 218 | 219 | // Adjust type 220 | if (update == 'type' || !update) { 221 | ctc_map_after_fields.setMapTypeId(google.maps.MapTypeId[type]); 222 | } 223 | 224 | // Adjust zoom 225 | if (update == 'zoom' || !update) { 226 | ctc_map_after_fields.setZoom(zoom); 227 | } 228 | 229 | // Move marker and recenter 230 | if (update == 'coordinates' || !update) { 231 | ctc_map_after_fields_marker.setPosition(coordinates); 232 | ctc_map_after_fields.panTo(coordinates); 233 | } 234 | 235 | } 236 | 237 | } 238 | 239 | // Hide map container if no lat/lng 240 | else { 241 | jQuery('#ctc-map-after-fields-container').hide(); 242 | } 243 | 244 | } 245 | -------------------------------------------------------------------------------- /includes/admin/editor.php: -------------------------------------------------------------------------------- 1 | post_type ) { 44 | 45 | // Page. 46 | case 'page': 47 | 48 | // Gutenberg editor. 49 | if ( $block_editor ) { 50 | $placeholder = sprintf( 51 | /* translators: For title placeholder when adding new page with block editor (example result: "Page Title"). %1$s is "Page" (singular) word, possibly changed by translation. Always use %1$s and not "Page" */ 52 | _x( '%1$s Title', 'page title placeholder', 'church-theme-content' ), 53 | ctc_post_type_label( 'page', 'singular' ) 54 | ); 55 | } 56 | 57 | // Classic editor unchanged: "Enter title here". 58 | 59 | break; 60 | 61 | // Post. 62 | case 'post': 63 | 64 | // Gutenberg editor. 65 | if ( $block_editor ) { 66 | $placeholder = sprintf( 67 | /* translators: For title placeholder when adding new post with block editor (example result: "Post Title"). %1$s is "Post" (singular) word, possibly changed by translation. Always use %1$s and not "Post" */ 68 | _x( '%1$s Title', 'post title placeholder', 'church-theme-content' ), 69 | ctc_post_type_label( 'post', 'singular' ) 70 | ); 71 | } 72 | 73 | // Classic editor unchanged: "Enter title here". 74 | 75 | break; 76 | 77 | // Sermon. 78 | case 'ctc_sermon': 79 | 80 | // Gutenberg editor. 81 | if ( $block_editor ) { 82 | $placeholder = sprintf( 83 | /* translators: For title placeholder when adding new sermon with block editor (example result: "Sermon Title"). %1$s is "Sermon" (singular) word, possibly changed by settings or translation. Always use %1$s and not "Sermon". */ 84 | _x( '%1$s Title', 'sermon title placeholder', 'church-theme-content' ), 85 | ctc_sermon_word_singular() 86 | ); 87 | } 88 | 89 | // Classic editor unchanged: "Enter title here". 90 | 91 | break; 92 | 93 | 94 | // Event. 95 | case 'ctc_event': 96 | 97 | // Gutenberg editor. 98 | if ( $block_editor ) { 99 | $placeholder = sprintf( 100 | /* translators: For title placeholder when adding new event with block editor (example result: "Event Title"). %1$s is "Event" (singular) word, possibly changed by translation. Always use %1$s and not "Event". */ 101 | _x( '%1$s Name', 'event title placeholder', 'church-theme-content' ), 102 | ctc_post_type_label( 'ctc_event', 'singular' ) 103 | ); 104 | } 105 | 106 | // Classic editor. 107 | else { 108 | $placeholder = $enter_name_here; // "event name" is more common than "event title". 109 | } 110 | 111 | break; 112 | 113 | // Location. 114 | case 'ctc_location': 115 | 116 | // Gutenberg editor. 117 | if ( $block_editor ) { 118 | $placeholder = sprintf( 119 | /* translators: For title placeholder when adding new location with block editor (example result: "Location Name"). %1$s is "Location" (singular) word, possibly changed by translation. Always use %1$s and not "Location" */ 120 | _x( '%1$s Name', 'location title placeholder', 'church-theme-content' ), 121 | ctc_post_type_label( 'ctc_location', 'singular' ) 122 | ); 123 | } 124 | 125 | // Classic editor. 126 | else { 127 | $placeholder = $enter_name_here; // "location name" is more common than "location title". 128 | } 129 | 130 | break; 131 | 132 | // Person. 133 | case 'ctc_person': 134 | 135 | // Gutenberg editor. 136 | if ( $block_editor ) { 137 | $placeholder = sprintf( 138 | /* translators: For title placeholder when adding new person with block editor (example result: "Person's Name"). %s is "Person" (singular) word, possibly changed by translation. Always use %s and not "Person" */ 139 | _x( "%s's Name", 'person title placeholder', 'church-theme-content' ), 140 | ctc_post_type_label( 'ctc_person', 'singular' ) 141 | ); 142 | } 143 | 144 | // Classic editor. 145 | else { 146 | $placeholder = $enter_name_here; 147 | } 148 | 149 | break; 150 | 151 | } 152 | 153 | // Unchanged for other post types. 154 | 155 | // Return. 156 | return $placeholder; 157 | 158 | } 159 | 160 | add_filter( 'enter_title_here', 'ctc_title_placeholder' ); 161 | 162 | /** 163 | * Change "Write your story" to "Add content" when adding a new post in block editor. 164 | * 165 | * This generic form is more suitable for pages, sermons, events, etc. 166 | * 167 | * Note: The filter this uses is for Gutenberg only. 168 | * 169 | * @since 2.0 170 | * @param string $placeholder Default body placeholder 171 | * @return string Modified placeholder 172 | */ 173 | function ctc_body_placeholder( $placeholder ) { 174 | 175 | // Get current screen. 176 | $screen = get_current_screen(); 177 | 178 | // Switch post type possibilities. 179 | switch ( $screen->post_type ) { 180 | 181 | // Sermon. 182 | case 'ctc_sermon': 183 | 184 | $placeholder = sprintf( 185 | /* translators: For content placeholder when adding new sermon with block editor (example result: "Enter sermon transcript or details here"). %1$s is "sermon" word (singular, lowercase), possibly changed by settings or translation. Always use %1$s and not "sermon". */ 186 | __( 'Enter %1$s transcript or details', 'church-theme-content' ), 187 | strtolower( ctc_sermon_word_singular() ) 188 | ); 189 | 190 | break; 191 | 192 | // Event. 193 | case 'ctc_event': 194 | 195 | $placeholder = sprintf( 196 | /* translators: For content placeholder when adding new event with block editor (example result: "Describe the event"). %1$s is "event" word (singular, lowercase), possibly changed by translation. Always use %1$s and not "event". */ 197 | _x( 'Describe the %1$s', 'event body placeholder', 'church-theme-content' ), 198 | strtolower( ctc_post_type_label( 'ctc_event', 'singular' ) ) 199 | ); 200 | 201 | break; 202 | 203 | // Location. 204 | case 'ctc_location': 205 | 206 | $placeholder = sprintf( 207 | /* translators: For content placeholder when adding new location with block editor (example result: "Describe the location"). %1$s is "location" word (singular, lowercase), possibly changed by translation. Always use %1$s and not "location". */ 208 | _x( 'Describe the %1$s', 'location body placeholder', 'church-theme-content' ), 209 | strtolower( ctc_post_type_label( 'ctc_location', 'singular' ) ) 210 | ); 211 | 212 | break; 213 | 214 | // Location. 215 | case 'ctc_person': 216 | 217 | /* translators: For content placeholder when adding new person with block editor. */ 218 | $placeholder = __( 'Write a biography', 'church-theme-content' ); 219 | 220 | break; 221 | 222 | // Other post types (post, page). 223 | default: 224 | 225 | // "Add content" makes more sense than "Write your story" for any post type. 226 | $placeholder = __( 'Add content', 'church-theme-content' ); 227 | 228 | break; 229 | 230 | } 231 | 232 | return $placeholder; 233 | 234 | } 235 | 236 | add_filter( 'write_your_story', 'ctc_body_placeholder' ); 237 | 238 | /******************************************* 239 | * HELPERS 240 | *******************************************/ 241 | 242 | /** 243 | * Check if block editor (Gutenberg) is in use on add/edit screen. 244 | * 245 | * @since 2.0 246 | */ 247 | function ctc_is_block_editor() { 248 | 249 | global $post, $pagenow; 250 | 251 | // Default false. 252 | $is = false; 253 | 254 | // Editing single post and Gutenberg is available. 255 | // Using $pagenow instead of get_current_screen() since it's sometimes too early to be available. 256 | if ( in_array( $pagenow, array( 'post.php', 'post-new.php' ) ) && function_exists( 'gutenberg_can_edit_post' ) ) { 257 | 258 | // Not using classic editor. 259 | if ( isset( $_GET['classic-editor'] ) ) { 260 | $is = false; 261 | } 262 | 263 | // Not able to edit with Gutenberg. 264 | elseif ( ! gutenberg_can_edit_post( $post ) ) { 265 | $is = false; 266 | } 267 | 268 | // Gutenburg running. 269 | else { 270 | $is = true; 271 | } 272 | 273 | } 274 | 275 | return $is; 276 | 277 | } 278 | 279 | -------------------------------------------------------------------------------- /includes/admin/admin-add-ons.php: -------------------------------------------------------------------------------- 1 | 'plugins' ) ); $plugin_meta[] = '' . esc_html__( 'Upgrade to Pro', 'church-theme-content' ) . ''; } } } // Return links array, possibly modified. return $plugin_meta; } add_filter( 'plugin_row_meta', 'ctc_pro_upgrade_plugin_meta_link', 10, 4 ); /** * Add "License Key" link to add-on plugin's action links * * This adds "License Key" link after other action links for each add-on supporting licensing on Plugins screen. * * @since 1.2 * @param array $actions An array of plugin action links. * @param string $plugin_file Path to the plugin file. * @param array $plugin_data An array of plugin data. * @param string $context The plugin context. * @return array Modified action links */ function ctc_add_ons_plugin_meta_links( $actions, $plugin_file, $plugin_data, $context ) { $plugin_dirname = dirname( $plugin_file ); // Is this plugin a CTC add-on that supports licensing. if ( ctc_edd_license_supported( $plugin_dirname ) ) { // Have action links array? if ( is_array( $actions ) ) { // Append "License Key" link. $actions[] = '' . esc_html__( 'License Key', 'church-theme-content' ) . ''; } } return $actions; } add_filter( 'plugin_action_links', 'ctc_add_ons_plugin_meta_links', 10, 4 ); /************************************************* * POST FIELDS *************************************************/ /** * Append "Upgrade to Pro" note below Recurrence field description when basic recurrence supported. * * Inform user of Pro add-on by appending note to Recurrence field's description. * This only runs when the basic recurrence field is supported, either by theme or grandfathering. * * Also see ctc_no_recurrence_field_upgrade_note() for note to show when basic recurrence not supported. * * @since 2.0 * @param array $field Field settings. * @return array Modified field settings. */ function ctc_recurrence_field_upgrade_note( $field ) { $note = ''; // Neither Pro nor Custom Recurring Events active. if ( ! ctc_pro_is_active() && ! ctc_cre_is_active() ) { // Note. // Descriptions are sanitized with wp_kses automatically by CT Meta Box. tag allowed. $note = sprintf( /* translators: %1$s is URL for Church Content Pro plugin */ __( 'Upgrade to Church Content Pro for more recurrence options.', 'church-theme-content' ), esc_url( ctc_ctcom_url( 'church-content-pro', array( 'utm_content' => 'settings' ) ) ) ); } // Custom Recurring Events is active. elseif ( ! ctc_pro_is_active() && ctc_cre_is_active() ) { // Note. // Descriptions are sanitized with wp_kses automatically by CT Meta Box. tag allowed. $note = sprintf( /* translators: %1$s is URL for upgrading to Church Content Pro from Custom Recurring Events */ __( 'Upgrade to Church Content Pro for more options than Custom Recurring Events.', 'church-theme-content' ), esc_url( ctc_ctcom_url( 'cre-to-pro', array( 'utm_content' => 'settings' ) ) ) ); } // Append note below Recurrence field. if ( $note ) { // Description key may not be set. $field['desc'] = isset( $field['desc'] ) ? $field['desc'] : ''; // If has desc, break to new line. if ( ! empty( $field['desc'] ) ) { $field['desc'] .= '
'; } // Append note. // Descriptions are sanitized with wp_kses automatically by CT Meta Box. tag allowed. $field['desc'] .= $note; } return $field; } add_action( 'ctmb_field-_ctc_event_recurrence', 'ctc_recurrence_field_upgrade_note' ); /** * Add empty "Recurrence" psuedo-field with "Upgrade to Pro" note when basic recurrence not supported. * * Also see ctc_recurrence_field_upgrade_note() for note to show when basic recurrence is supported. * * @since 2.0 * @param object $instance Meta box instance. */ function ctc_no_recurrence_field_upgrade_note( $instance ) { // Only event's Date & Time metabox. if ( ! isset( $instance->meta_box['id'] ) || 'ctc_event_date' !== $instance->meta_box['id'] ) { return; } // Get event fields that are supported by theme. $supported_fields = ctc_get_theme_support( 'ctc-events', 'fields' ); // Only if specific fields are supported. // Because, if no fields specified, all are automatically supported (including the basic recurrence fields). if ( ! isset( $supported_fields ) ) { return; } // Only if no recurrence field support (no theme support, grandfathering, Pro or Custom Recurring events). if ( in_array( '_ctc_event_recurrence', $supported_fields ) ) { return; } // Add "Recurrence" psuedo-field with message about enabling recurrence with Pro add-on. ?>
Recurrence

Church Content Pro to save time with recurring events.', 'church-theme-content' ), array( 'a' => array( 'href' => array(), 'target' => array(), ) ) ), esc_url( ctc_ctcom_url( 'church-content-pro', array( 'utm_content' => 'recurrence_field' ) ) ) ); ?>

meta_box['id'] ) { return; } // Only if Venue and/or Address field supported. // Otherwise, upgrading to Pro has no advantage with these. if ( ! ctc_field_supported( 'events', '_ctc_event_venue' ) && ! ctc_field_supported( 'events', '_ctc_event_address' ) ) { return; } // Output upgrade message. ?>

Church Content Pro to save time by selecting previously used locations.', 'church-theme-content' ), array( 'a' => array( 'href' => array(), 'target' => array(), ), ) ), esc_url( ctc_ctcom_url( 'church-content-pro', array( 'utm_content' => 'event_location_box' ) ) ) ); ?>

Settings then Sermons > Podcast). 37 | $settings_page_uri = 'options-general.php?page=' . CTC_DIR . '&rand=' . $rand; 38 | 39 | // Capability. 40 | $capability = 'manage_options'; // role/capability with access. 41 | 42 | // Setting word for all post types. 43 | $settings_word = _x( 'Settings', 'custom post type menu', 'church-theme-content' ); 44 | 45 | // Sermons. 46 | $key = 'edit.php?post_type=ctc_sermon'; 47 | 48 | // Podcast. 49 | $submenu[ $key ][] = array( 50 | _x( 'Podcast', 'custom post type menu', 'church-theme-content' ), 51 | $capability, 52 | admin_url( $settings_page_uri . '#podcast' ), 53 | ); 54 | 55 | // Settings. 56 | $submenu[ $key ][] = array( 57 | $settings_word, 58 | $capability, 59 | admin_url( $settings_page_uri . '#sermons' ), 60 | ); 61 | 62 | // Events. 63 | $key = 'edit.php?post_type=ctc_event'; 64 | 65 | // Settings. 66 | $submenu[ $key ][] = array( 67 | $settings_word, 68 | $capability, 69 | admin_url( $settings_page_uri . '#events' ), 70 | ); 71 | 72 | // Locations. 73 | $key = 'edit.php?post_type=ctc_location'; 74 | 75 | // Settings. 76 | $submenu[ $key ][] = array( 77 | $settings_word, 78 | $capability, 79 | admin_url( $settings_page_uri . '#locations' ), 80 | ); 81 | 82 | // People. 83 | $key = 'edit.php?post_type=ctc_person'; 84 | 85 | // Settings. 86 | $submenu[ $key ][] = array( 87 | $settings_word, 88 | $capability, 89 | admin_url( $settings_page_uri . '#people' ), 90 | ); 91 | 92 | } 93 | 94 | add_action( 'admin_menu', 'ctc_add_settings_menu_links' ); 95 | 96 | /******************************************* 97 | * REORDERING 98 | *******************************************/ 99 | 100 | /** 101 | * Enable custom menu order 102 | * 103 | * Note ctc_reorder_admin_menu which does the actual ordering. 104 | * 105 | * @since 0.9 106 | */ 107 | function ctc_custom_menu_order() { 108 | return true; 109 | } 110 | 111 | add_filter( 'custom_menu_order', 'ctc_custom_menu_order' ); // enable custom menu order 112 | 113 | /** 114 | * Friendly admin menu order 115 | * 116 | * Move Pages to top followed by Posts then custom post types. 117 | * Show Comments and Media at bottom. 118 | * 119 | * @since 0.9 120 | * @param array $menu_ord Menu data 121 | * @return array Modified $menu_ord 122 | */ 123 | function ctc_reorder_admin_menu( $menu_ord ) { 124 | 125 | // Move Pages before Posts 126 | ctc_move_admin_menu_item( $menu_ord, 'edit.php?post_type=page', 'edit.php', 'before' ); 127 | 128 | // Move Comments and Media after the LAST CPT 129 | // This is in case some CPT's are not used or reordered 130 | $last_cpt = ''; 131 | foreach ( $menu_ord as $item ) { 132 | if ( preg_match( '/^edit\.php\?post_type=/', $item ) ) { 133 | $last_cpt = $item; 134 | } 135 | } 136 | ctc_move_admin_menu_item( $menu_ord, 'upload.php', $last_cpt ); // Media Library - last 137 | ctc_move_admin_menu_item( $menu_ord, 'edit-comments.php', $last_cpt ); // Comments - second to last 138 | 139 | // Return manipulated menu array 140 | return $menu_ord; 141 | 142 | } 143 | 144 | add_filter( 'menu_order', 'ctc_reorder_admin_menu' ); 145 | 146 | /** 147 | * Function to move admin menu item before or after another 148 | * 149 | * Use this with custom_menu_order and menu_order filters. 150 | * 151 | * @since 0.9 152 | * @param array $menu_ord The original menu from menu_order filter 153 | * @param string $move_item Value of item in array to move 154 | * @param string $target_item Value of item in array to move $move_item before or after 155 | * @param string $position Position 'after' (default) or 'before' in which to place $move_item in relation to $target_item 156 | * @return array Modified $menu_ord 157 | */ 158 | function ctc_move_admin_menu_item( &$menu_ord, $move_item, $target_item, $position = 'after' ) { 159 | 160 | // make sure items given are in array 161 | if ( in_array( $move_item, $menu_ord ) && in_array( $target_item, $menu_ord ) ) { 162 | 163 | // get position of each item 164 | $move_key = array_search( $move_item, $menu_ord ); 165 | $move_key_after = array_search( $target_item, $menu_ord ); 166 | 167 | // move item before instead of after 168 | if ( 'before' == $position ) { 169 | $move_key_after = ( $move_key_after - 1 ) >= 0 ? ( $move_key_after - 1 ) : 0; // move after item before item to move after (unless item to move after is at very top) 170 | } 171 | 172 | // move one item directly after the other 173 | if ( $move_key < $move_key_after ) { // item to move is currently before item to move after 174 | $menu_ord = array_merge( 175 | array_slice( $menu_ord, 0, $move_key ), // everything before item being moved 176 | array_slice( $menu_ord, $move_key + 1, $move_key_after - $move_key ), // everything after item being moved through item to move after 177 | array( $menu_ord[$move_key] ), // add item to move after item to move after 178 | array_slice( $menu_ord, $move_key_after + 1 ) // everything after item to move after 179 | ); 180 | } else if ( $move_key > $move_key_after ) { // item to move is currently after item to move directly after 181 | $menu_ord = array_merge( 182 | array_slice( $menu_ord, 0, $move_key_after + 1 ), // everything from item to move after and before 183 | array( $menu_ord[$move_key] ), // add item to move after item to move after 184 | array_slice( $menu_ord, $move_key_after + 1, $move_key - $move_key_after - 1 ), // everything after item to move after and before item to move 185 | array_slice( $menu_ord, $move_key + 1 ) // everything after item to move 186 | ); 187 | } 188 | 189 | // if moving item before very first item, run again but with $move_item and $target_item inverted 190 | // there was no higher item to move after so we ran as normal and now swap the new two top items 191 | if ( 'before' == $position && 0 == $move_key_after ) { 192 | ctc_move_admin_menu_item( $menu_ord, $target_item, $move_item ); // run again with item to move and item to move after swapped 193 | } 194 | 195 | } 196 | 197 | // return manipulated menu or original menu if no manipulation done 198 | return apply_filters( 'ctc_move_admin_menu_item', $menu_ord, $menu_ord, $move_item, $target_item, $position ); 199 | 200 | } 201 | 202 | /******************************************* 203 | * HIDING 204 | *******************************************/ 205 | 206 | /** 207 | * Hide sermons in admin area. 208 | * 209 | * Apply setting to hide post type in admin area. 210 | * Useful when not using a particular feature (e.g. using a third-party plugin. 211 | * 212 | * This filters post type registration arguments. 213 | * 214 | * @since 2.0 215 | * @param array $args Current post type arguments. 216 | * @return array Modified post type arguments. 217 | */ 218 | function ctc_admin_hide_sermons( $args ) { 219 | return ctc_admin_hide_post_type_args( 'sermons_admin_hide', $args ); 220 | } 221 | 222 | add_filter( 'ctc_post_type_sermon_args', 'ctc_admin_hide_sermons' ); 223 | 224 | /** 225 | * Hide events in admin area. 226 | * 227 | * @since 2.0 228 | * @param array $args Current post type arguments. 229 | * @return array Modified post type arguments. 230 | */ 231 | function ctc_admin_hide_events( $args ) { 232 | return ctc_admin_hide_post_type_args( 'events_admin_hide', $args ); 233 | } 234 | 235 | add_filter( 'ctc_post_type_event_args', 'ctc_admin_hide_events' ); 236 | 237 | /** 238 | * Hide locations in admin area. 239 | * 240 | * @since 2.0 241 | * @param array $args Current post type arguments. 242 | * @return array Modified post type arguments. 243 | */ 244 | function ctc_admin_hide_locations( $args ) { 245 | return ctc_admin_hide_post_type_args( 'locations_admin_hide', $args ); 246 | } 247 | 248 | add_filter( 'ctc_post_type_location_args', 'ctc_admin_hide_locations' ); 249 | 250 | /** 251 | * Hide people in admin area. 252 | * 253 | * @since 2.0 254 | * @param array $args Current post type arguments. 255 | * @return array Modified post type arguments. 256 | */ 257 | function ctc_admin_hide_people( $args ) { 258 | return ctc_admin_hide_post_type_args( 'people_admin_hide', $args ); 259 | } 260 | 261 | add_filter( 'ctc_post_type_person_args', 'ctc_admin_hide_people' ); 262 | 263 | /** 264 | * Update post type arguments array to hide in admin, when a setting is true. 265 | * 266 | * @since 2.0 267 | * @param string $setting Setting ID to check for true value on. 268 | * @param array $args Arguments to merge into. 269 | * @return array Modified post type arguments. 270 | */ 271 | function ctc_admin_hide_post_type_args( $setting, $args ) { 272 | 273 | // Setting is active. 274 | if ( ctc_setting( $setting ) ) { 275 | 276 | // Change post type registration arguments. 277 | $args['show_ui'] = false; 278 | $args['show_in_nav_menus'] = false; 279 | 280 | } 281 | 282 | return $args; 283 | 284 | } 285 | -------------------------------------------------------------------------------- /js/lib/clipboard.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * clipboard.js v2.0.0 3 | * https://zenorocha.github.io/clipboard.js 4 | * 5 | * Licensed MIT © Zeno Rocha 6 | */ 7 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return function(t){function e(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return t[o].call(r.exports,r,r.exports,e),r.l=!0,r.exports}var n={};return e.m=t,e.c=n,e.i=function(t){return t},e.d=function(t,n,o){e.o(t,n)||Object.defineProperty(t,n,{configurable:!1,enumerable:!0,get:o})},e.n=function(t){var n=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},e.p="",e(e.s=3)}([function(t,e,n){var o,r,i;!function(a,c){r=[t,n(7)],o=c,void 0!==(i="function"==typeof o?o.apply(e,r):o)&&(t.exports=i)}(0,function(t,e){"use strict";function n(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}var o=function(t){return t&&t.__esModule?t:{default:t}}(e),r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},i=function(){function t(t,e){for(var n=0;n0&&void 0!==arguments[0]?arguments[0]:{};this.action=t.action,this.container=t.container,this.emitter=t.emitter,this.target=t.target,this.text=t.text,this.trigger=t.trigger,this.selectedText=""}},{key:"initSelection",value:function(){this.text?this.selectFake():this.target&&this.selectTarget()}},{key:"selectFake",value:function(){var t=this,e="rtl"==document.documentElement.getAttribute("dir");this.removeFake(),this.fakeHandlerCallback=function(){return t.removeFake()},this.fakeHandler=this.container.addEventListener("click",this.fakeHandlerCallback)||!0,this.fakeElem=document.createElement("textarea"),this.fakeElem.style.fontSize="12pt",this.fakeElem.style.border="0",this.fakeElem.style.padding="0",this.fakeElem.style.margin="0",this.fakeElem.style.position="absolute",this.fakeElem.style[e?"right":"left"]="-9999px";var n=window.pageYOffset||document.documentElement.scrollTop;this.fakeElem.style.top=n+"px",this.fakeElem.setAttribute("readonly",""),this.fakeElem.value=this.text,this.container.appendChild(this.fakeElem),this.selectedText=(0,o.default)(this.fakeElem),this.copyText()}},{key:"removeFake",value:function(){this.fakeHandler&&(this.container.removeEventListener("click",this.fakeHandlerCallback),this.fakeHandler=null,this.fakeHandlerCallback=null),this.fakeElem&&(this.container.removeChild(this.fakeElem),this.fakeElem=null)}},{key:"selectTarget",value:function(){this.selectedText=(0,o.default)(this.target),this.copyText()}},{key:"copyText",value:function(){var t=void 0;try{t=document.execCommand(this.action)}catch(e){t=!1}this.handleResult(t)}},{key:"handleResult",value:function(t){this.emitter.emit(t?"success":"error",{action:this.action,text:this.selectedText,trigger:this.trigger,clearSelection:this.clearSelection.bind(this)})}},{key:"clearSelection",value:function(){this.trigger&&this.trigger.focus(),window.getSelection().removeAllRanges()}},{key:"destroy",value:function(){this.removeFake()}},{key:"action",set:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"copy";if(this._action=t,"copy"!==this._action&&"cut"!==this._action)throw new Error('Invalid "action" value, use either "copy" or "cut"')},get:function(){return this._action}},{key:"target",set:function(t){if(void 0!==t){if(!t||"object"!==(void 0===t?"undefined":r(t))||1!==t.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===this.action&&t.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===this.action&&(t.hasAttribute("readonly")||t.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');this._target=t}},get:function(){return this._target}}]),t}();t.exports=a})},function(t,e,n){function o(t,e,n){if(!t&&!e&&!n)throw new Error("Missing required arguments");if(!c.string(e))throw new TypeError("Second argument must be a String");if(!c.fn(n))throw new TypeError("Third argument must be a Function");if(c.node(t))return r(t,e,n);if(c.nodeList(t))return i(t,e,n);if(c.string(t))return a(t,e,n);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList")}function r(t,e,n){return t.addEventListener(e,n),{destroy:function(){t.removeEventListener(e,n)}}}function i(t,e,n){return Array.prototype.forEach.call(t,function(t){t.addEventListener(e,n)}),{destroy:function(){Array.prototype.forEach.call(t,function(t){t.removeEventListener(e,n)})}}}function a(t,e,n){return u(document.body,t,e,n)}var c=n(6),u=n(5);t.exports=o},function(t,e){function n(){}n.prototype={on:function(t,e,n){var o=this.e||(this.e={});return(o[t]||(o[t]=[])).push({fn:e,ctx:n}),this},once:function(t,e,n){function o(){r.off(t,o),e.apply(n,arguments)}var r=this;return o._=e,this.on(t,o,n)},emit:function(t){var e=[].slice.call(arguments,1),n=((this.e||(this.e={}))[t]||[]).slice(),o=0,r=n.length;for(o;o0&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof t.action?t.action:this.defaultAction,this.target="function"==typeof t.target?t.target:this.defaultTarget,this.text="function"==typeof t.text?t.text:this.defaultText,this.container="object"===d(t.container)?t.container:document.body}},{key:"listenClick",value:function(t){var e=this;this.listener=(0,f.default)(t,"click",function(t){return e.onClick(t)})}},{key:"onClick",value:function(t){var e=t.delegateTarget||t.currentTarget;this.clipboardAction&&(this.clipboardAction=null),this.clipboardAction=new l.default({action:this.action(e),target:this.target(e),text:this.text(e),container:this.container,trigger:e,emitter:this})}},{key:"defaultAction",value:function(t){return u("action",t)}},{key:"defaultTarget",value:function(t){var e=u("target",t);if(e)return document.querySelector(e)}},{key:"defaultText",value:function(t){return u("text",t)}},{key:"destroy",value:function(){this.listener.destroy(),this.clipboardAction&&(this.clipboardAction.destroy(),this.clipboardAction=null)}}],[{key:"isSupported",value:function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:["copy","cut"],e="string"==typeof t?[t]:t,n=!!document.queryCommandSupported;return e.forEach(function(t){n=n&&!!document.queryCommandSupported(t)}),n}}]),e}(s.default);t.exports=p})},function(t,e){function n(t,e){for(;t&&t.nodeType!==o;){if("function"==typeof t.matches&&t.matches(e))return t;t=t.parentNode}}var o=9;if("undefined"!=typeof Element&&!Element.prototype.matches){var r=Element.prototype;r.matches=r.matchesSelector||r.mozMatchesSelector||r.msMatchesSelector||r.oMatchesSelector||r.webkitMatchesSelector}t.exports=n},function(t,e,n){function o(t,e,n,o,r){var a=i.apply(this,arguments);return t.addEventListener(n,a,r),{destroy:function(){t.removeEventListener(n,a,r)}}}function r(t,e,n,r,i){return"function"==typeof t.addEventListener?o.apply(null,arguments):"function"==typeof n?o.bind(null,document).apply(null,arguments):("string"==typeof t&&(t=document.querySelectorAll(t)),Array.prototype.map.call(t,function(t){return o(t,e,n,r,i)}))}function i(t,e,n,o){return function(n){n.delegateTarget=a(n.target,e),n.delegateTarget&&o.call(t,n)}}var a=n(4);t.exports=r},function(t,e){e.node=function(t){return void 0!==t&&t instanceof HTMLElement&&1===t.nodeType},e.nodeList=function(t){var n=Object.prototype.toString.call(t);return void 0!==t&&("[object NodeList]"===n||"[object HTMLCollection]"===n)&&"length"in t&&(0===t.length||e.node(t[0]))},e.string=function(t){return"string"==typeof t||t instanceof String},e.fn=function(t){return"[object Function]"===Object.prototype.toString.call(t)}},function(t,e){function n(t){var e;if("SELECT"===t.nodeName)t.focus(),e=t.value;else if("INPUT"===t.nodeName||"TEXTAREA"===t.nodeName){var n=t.hasAttribute("readonly");n||t.setAttribute("readonly",""),t.select(),t.setSelectionRange(0,t.value.length),n||t.removeAttribute("readonly"),e=t.value}else{t.hasAttribute("contenteditable")&&t.focus();var o=window.getSelection(),r=document.createRange();r.selectNodeContents(t),o.removeAllRanges(),o.addRange(r),e=o.toString()}return e}t.exports=n}])}); -------------------------------------------------------------------------------- /includes/podcast.php: -------------------------------------------------------------------------------- 1 | General. 109 | * 110 | * @since 2.0 111 | * @return string Default string. 112 | */ 113 | function ctc_podcast_language_default() { 114 | 115 | // Get language setting from Settings > General. 116 | $language = get_bloginfo( 'language' ); // assumed to be church name. 117 | 118 | // Build default string. 119 | $default = $language; 120 | 121 | // Return filterable. 122 | return apply_filters( 'ctc_podcast_language_default', $default ); 123 | 124 | } 125 | 126 | /** 127 | * Default Podcast Link. 128 | * 129 | * URL used for link on podcast. Default is homepage. 130 | * 131 | * @since 2.0 132 | * @return string Default string. 133 | */ 134 | function ctc_podcast_link_default() { 135 | 136 | // Get homepage URL. 137 | $home_url = home_url(); 138 | 139 | // Build default string. 140 | $default = trailingslashit( $home_url ); 141 | 142 | // Return filterable. 143 | return apply_filters( 'ctc_podcast_link_default', $default ); 144 | 145 | } 146 | 147 | /** 148 | * Default Podcast Copyright. 149 | * 150 | * Copyright notice to use when no copyright saved in settings. 151 | * 152 | * @since 2.0 153 | * @return string Default string. 154 | */ 155 | function ctc_podcast_copyright_default() { 156 | 157 | // Get site name. 158 | $site_name = get_bloginfo( 'name' ); // assumed to be church name. 159 | 160 | // Build default title string. 161 | $default = ''; 162 | if ( $site_name && 'WordPress Site' !== $site_name ) { // have site name and not default title 163 | $default = '© ' . $site_name; 164 | } 165 | 166 | // Return filterable. 167 | return apply_filters( 'ctc_podcast_copyright_default', $default ); 168 | 169 | } 170 | 171 | /** 172 | * Default Podcast Limit. 173 | * 174 | * Limit to use when no limit saved in settings. 175 | * 176 | * Use posts_per_rss "Syndication feeds..." in Reading Settings as default. 177 | * 178 | * @since 2.0 179 | * @return string Default string. 180 | */ 181 | function ctc_podcast_limit_default() { 182 | 183 | // Get site name. 184 | $rss_limit = get_option( 'posts_per_rss' ); // assumed to be church name. 185 | 186 | // Build default title string. 187 | $default = $rss_limit; 188 | 189 | // Return filterable. 190 | return apply_filters( 'ctc_podcast_limit_default', $default ); 191 | 192 | } 193 | 194 | /** 195 | * Podcast category options. 196 | * 197 | * Array of select options for podcast settings. 198 | * 199 | * Only iTunes subcategories relevant to Church Content plugin users are presented. 200 | * https://help.apple.com/itc/podcasts_connect/#/itc9267a2f12 201 | * 202 | * @since 2.0 203 | * @return array Key and value pairs. 204 | */ 205 | function ctc_podcast_category_options() { 206 | 207 | $options = array( 208 | 'Religion & Spirituality|Christianity' => __( 'Christianity (Religion)', 'podcast category', 'church-theme-content' ), 209 | 'Government & Organizations|Non-Profit' => __( 'Non-Profit (Organizations)', 'podcast category', 'church-theme-content' ), 210 | 'none' => __( 'None', 'podcast category', 'church-theme-content' ), 211 | ); 212 | 213 | return apply_filters( 'ctc_podcast_category_options', $options ); 214 | 215 | } 216 | 217 | /********************************************* 218 | * FEED 219 | *********************************************/ 220 | 221 | /** 222 | * Podcast feed name. 223 | * 224 | * Filterable feed name used with add_feed() and get_feed_link(). 225 | * 226 | * @since 2.0 227 | * @return string Feed name. 228 | */ 229 | function ctc_podcast_feed_name() { 230 | 231 | $name = 'sermon-podcast'; 232 | 233 | return apply_filters( 'ctc_podcast_feed_name', $name ); 234 | 235 | } 236 | 237 | /** 238 | * Podcast feed URL 239 | * 240 | * Get the podcast feed URL. 241 | * 242 | * @since 2.0 243 | * @return string Feed URL 244 | */ 245 | function ctc_podcast_feed_url() { 246 | 247 | $feed_name = ctc_podcast_feed_name(); 248 | $podcast_feed_url = get_feed_link( $feed_name ); 249 | 250 | return apply_filters( 'ctc_podcast_feed_url', $podcast_feed_url ); 251 | 252 | } 253 | 254 | /********************************** 255 | * META DATA 256 | **********************************/ 257 | 258 | /** 259 | * Church Content plugin version of do_enclose() 260 | * 261 | * When audio URL is provided, save its data to the 'enclosure' field. 262 | * WordPress automatically uses this data to make feeds useful for podcasting. 263 | * 264 | * @since 2.0 265 | * @param int $post_id 266 | */ 267 | function ctc_do_enclose( $post_id ) { 268 | 269 | global $post; 270 | 271 | // Stop if no ID. 272 | if ( empty( $post_id ) ) { 273 | return; 274 | } 275 | 276 | // Get post type. 277 | $post_type = get_post_type( $post_id ); 278 | 279 | // Stop if user lacks permission to edit a post. 280 | $post_type_obj = get_post_type_object( $post_type ); 281 | if ( ! current_user_can( $post_type_obj->cap->edit_post, $post_id ) ) { 282 | return; 283 | } 284 | 285 | // Stop if PowerPress plugin is active 286 | // Solves conflict regarding enclosure field: http://wordpress.org/support/topic/breaks-blubrry-powerpress-plugin?replies=6 287 | if ( defined( 'POWERPRESS_VERSION' ) ) { 288 | return; 289 | } 290 | 291 | // Get audio URL. 292 | $audio = get_post_meta( $post_id , '_ctc_sermon_audio' , true ); 293 | 294 | // Make Dropbox URLs use ?raw=1. 295 | // Note that this will not work on iTunes. 296 | if ( preg_match( '/dropbox/', $audio ) ) { 297 | $audio = remove_query_arg( 'dl', $audio ); 298 | $audio = add_query_arg( 'raw', '1', $audio ); 299 | } 300 | 301 | // Populate enclosure field with URL, length and format, if valid URL found. 302 | do_enclose( $audio, $post_id ); 303 | 304 | } 305 | 306 | /** 307 | * Add support for "Exclude from Podcast" field 308 | * 309 | * This field should automatically be supported whenever Audio field is supported. 310 | * 311 | * @since 1.0 312 | */ 313 | function ctc_support_podcast_exclude_field() { 314 | 315 | // Only if sermon post type and audio field supported. 316 | if ( ! ctc_podcast_content_supported() ) { 317 | return; 318 | } 319 | 320 | // Get sermon fields that are supported by theme. 321 | $sermons_support = ctc_get_theme_support( 'ctc-sermons' ); 322 | $supported_fields = isset( $sermons_support['fields'] ) ? $sermons_support['fields'] : array(); 323 | 324 | // Have supported fields. 325 | if ( ! empty( $supported_fields ) ) { 326 | 327 | // Field to add support for. 328 | $field = '_ctc_sermon_podcast_exclude'; 329 | 330 | // Is field already supported? 331 | if ( ! in_array( $field, $supported_fields ) ) { 332 | 333 | // Add field to supported fields array. 334 | $sermons_support['fields'][] = $field; 335 | 336 | } 337 | 338 | // Update theme support. 339 | remove_theme_support( 'ctc-sermons' ); 340 | add_theme_support( 'ctc-sermons', $sermons_support ); 341 | 342 | } 343 | 344 | } 345 | 346 | add_action( 'init', 'ctc_support_podcast_exclude_field', 2 ); // init 2 is right after ctc_set_default_theme_support in Church Content. 347 | 348 | /********************************************* 349 | * HELPERS 350 | *********************************************/ 351 | 352 | /** 353 | * Podcast content supported? 354 | * 355 | * Determine if content necessary for podcast is supported by theme. 356 | * Sermon feature and audio field must be supported. 357 | * 358 | * Note that this does NOT consider whether or not Pro plugin is active or not. 359 | * 360 | * @since 2.0 361 | * @return bool True if supported. 362 | */ 363 | function ctc_podcast_content_supported() { 364 | 365 | $supported = false; 366 | 367 | // Audio field and thus sermon feature supported? 368 | if ( ctc_field_supported( 'sermons', '_ctc_sermon_audio' ) ) { 369 | $supported = true; 370 | } 371 | 372 | return apply_filters( 'ctc_podcast_content_supported', $supported ); 373 | 374 | } -------------------------------------------------------------------------------- /church-theme-content.php: -------------------------------------------------------------------------------- 1 | compatible theme is required for presenting content from these church-centric post types in a tightly-integrated manner. 7 | * Version: 2.7 8 | * Author: ChurchThemes.com 9 | * Author URI: https://churchthemes.com 10 | * License: GPLv2 or later 11 | * Text Domain: church-theme-content 12 | * Domain Path: /languages 13 | * 14 | * @package Church_Theme_Content 15 | * @copyright Copyright (c) 2013 - 2025, ChurchThemes.com, LLC 16 | * @link https://github.com/churchthemes/church-theme-content 17 | * @license GPLv2 or later 18 | */ 19 | 20 | // No direct access 21 | if (! defined('ABSPATH')) exit; 22 | 23 | /** 24 | * Main class 25 | * 26 | * @since 0.9 27 | */ 28 | class Church_Theme_Content 29 | { 30 | 31 | /** 32 | * Plugin data from get_plugins() 33 | * 34 | * @since 0.9 35 | * @var object 36 | */ 37 | public $plugin_data; 38 | 39 | /** 40 | * Includes to load 41 | * 42 | * @since 0.9 43 | * @var array 44 | */ 45 | public $includes; 46 | 47 | /** 48 | * Constructor 49 | * 50 | * Add actions for methods that define constants and load includes. 51 | * 52 | * @since 0.9 53 | * @access public 54 | */ 55 | public function __construct() 56 | { 57 | 58 | // Set plugin data. 59 | add_action('plugins_loaded', array($this, 'set_plugin_data'), 1); 60 | 61 | // Define constants. 62 | add_action('plugins_loaded', array($this, 'define_constants'), 1); 63 | 64 | // Load language file for old versions of WordPress. 65 | add_action('plugins_loaded', array($this, 'load_textdomain'), 1); 66 | 67 | // Set includes. 68 | add_action('plugins_loaded', array($this, 'set_includes'), 1); 69 | 70 | // Load includes. 71 | add_action('plugins_loaded', array($this, 'load_includes'), 1); 72 | 73 | // Trigger flushing of rewrite rules on plugin activation. 74 | // This must be done early (not on plugins_loaded or init). 75 | register_activation_hook(__FILE__, array(&$this, 'trigger_flush_rewrite_rules')); 76 | 77 | // Check if rewrite rules should be flushed. 78 | add_action('init', array($this, 'ctc_check_flush_rewrite_rules'), 1); 79 | } 80 | 81 | /** 82 | * Set plugin data 83 | * 84 | * This data is used by constants. 85 | * 86 | * @since 0.9 87 | * @access public 88 | */ 89 | public function set_plugin_data() 90 | { 91 | 92 | // Load plugin.php if get_plugins() not available 93 | if (! function_exists('get_plugins')) { 94 | require_once ABSPATH . 'wp-admin/includes/plugin.php'; 95 | } 96 | 97 | // Get path to plugin's directory 98 | $plugin_dir = plugin_basename(dirname(__FILE__)); 99 | 100 | // Get plugin data 101 | $plugin_data = current(get_plugins('/' . $plugin_dir)); 102 | 103 | // Set plugin data 104 | $this->plugin_data = apply_filters('ctc_plugin_data', $plugin_data); 105 | } 106 | 107 | /** 108 | * Define constants 109 | * 110 | * @since 0.9 111 | * @access public 112 | */ 113 | public function define_constants() 114 | { 115 | 116 | // Plugin details 117 | define('CTC_VERSION', $this->plugin_data['Version']); // plugin version 118 | define('CTC_NAME', $this->plugin_data['Name']); // plugin name 119 | define('CTC_AUTHOR', strip_tags($this->plugin_data['Author'])); // plugin author 120 | define('CTC_INFO_URL', $this->plugin_data['PluginURI']); // plugin's info page URL 121 | define('CTC_FILE', __FILE__); // plugin's main file absolute path 122 | define('CTC_FILE_BASE', plugin_basename(CTC_FILE)); // plugin's main file path relative to plugin directory 123 | define('CTC_DIR', dirname(CTC_FILE_BASE)); // plugin's directory 124 | define('CTC_PATH', untrailingslashit(plugin_dir_path(CTC_FILE))); // plugin's absolute path 125 | define('CTC_URL', untrailingslashit(plugin_dir_url(CTC_FILE))); // plugin's directory URL 126 | 127 | // Directories 128 | define('CTC_INC_DIR', 'includes'); // includes directory 129 | define('CTC_ADMIN_DIR', CTC_INC_DIR . '/admin'); // admin directory 130 | define('CTC_CLASS_DIR', CTC_INC_DIR . '/classes'); // classes directory 131 | define('CTC_LIB_DIR', CTC_INC_DIR . '/libraries'); // libraries directory 132 | define('CTC_CSS_DIR', 'css'); // stylesheets directory 133 | define('CTC_JS_DIR', 'js'); // JavaScript directory 134 | define('CTC_IMG_DIR', 'images'); // images directory 135 | define('CTC_LANG_DIR', 'languages'); // languages directory 136 | 137 | // CT Meta Box 138 | if (! defined('CTMB_URL')) { // in case also used in theme or other plugin 139 | define('CTMB_URL', CTC_URL . '/' . CTC_LIB_DIR . '/ct-meta-box'); // for enqueueing JS/CSS 140 | } 141 | } 142 | 143 | /** 144 | * Load language file 145 | * 146 | * For WordPress versions under 4.6 only. https://make.wordpress.org/core/2016/07/06/i18n-improvements-in-4-6/ 147 | * 148 | * This will load the MO file for the current locale. 149 | * The translation file must be named church-theme-content-$locale.mo. 150 | * 151 | * First it will check to see if the MO file exists in wp-content/languages/plugins. 152 | * If not, then the 'languages' directory inside the plugin will be used. 153 | * It is ideal to keep translation files outside of the plugin to avoid loss during updates. 154 | * 155 | * @since 0.9 156 | * @access public 157 | */ 158 | public function load_textdomain() 159 | { 160 | 161 | // Get version of WordPress in use. 162 | $wp_version = get_bloginfo('version'); 163 | 164 | // Textdomain. 165 | $domain = 'church-theme-content'; 166 | 167 | // WordPress core locale filter. 168 | $locale = apply_filters('plugin_locale', get_locale(), $domain); 169 | 170 | // WordPress 3.6 and earlier don't auto-load from wp-content/languages, so check and load manually: http://core.trac.wordpress.org/changeset/22346. 171 | $external_mofile = WP_LANG_DIR . '/plugins/' . $domain . '-' . $locale . '.mo'; 172 | if (version_compare($wp_version, '3.6', '<=') && file_exists($external_mofile)) { // external translation exists. 173 | load_textdomain($domain, $external_mofile); 174 | } 175 | 176 | // Load normally. 177 | // Either using WordPress 3.7+ or older version with external translation. 178 | else { 179 | $languages_dir = CTC_DIR . '/' . trailingslashit(CTC_LANG_DIR); // ensure trailing slash. 180 | load_plugin_textdomain($domain, false, $languages_dir); 181 | } 182 | } 183 | 184 | /** 185 | * Set includes 186 | * 187 | * @since 0.9 188 | * @access public 189 | */ 190 | public function set_includes() 191 | { 192 | 193 | $this->includes = apply_filters('ctc_includes', array( 194 | 195 | // Frontend or admin 196 | 'always' => array( 197 | 198 | // Functions. 199 | CTC_INC_DIR . '/add-ons.php', 200 | CTC_INC_DIR . '/event-fields.php', 201 | CTC_INC_DIR . '/helpers.php', 202 | CTC_INC_DIR . '/mime-types.php', 203 | CTC_INC_DIR . '/podcast.php', 204 | CTC_INC_DIR . '/post-types.php', 205 | CTC_INC_DIR . '/schedule.php', 206 | CTC_INC_DIR . '/settings.php', 207 | CTC_INC_DIR . '/support.php', 208 | CTC_INC_DIR . '/taxonomies.php', 209 | 210 | // Libraries. 211 | CTC_LIB_DIR . '/ct-plugin-settings/ct-plugin-settings.php', // see CTPS_URL constant defined above. 212 | CTC_LIB_DIR . '/ct-recurrence/ct-recurrence-load.php', // don't load ct-recurrence.php directly. 213 | 214 | // Classes. 215 | 216 | ), 217 | 218 | // Admin only. 219 | 'admin' => array( 220 | 221 | // Functions 222 | CTC_ADMIN_DIR . '/admin-add-ons.php', 223 | CTC_ADMIN_DIR . '/admin-enqueue-scripts.php', 224 | CTC_ADMIN_DIR . '/admin-enqueue-styles.php', 225 | CTC_ADMIN_DIR . '/admin-event-fields.php', 226 | CTC_ADMIN_DIR . '/admin-helpers.php', 227 | CTC_ADMIN_DIR . '/admin-location-fields.php', 228 | CTC_ADMIN_DIR . '/admin-maps.php', 229 | CTC_ADMIN_DIR . '/admin-menu.php', 230 | CTC_ADMIN_DIR . '/admin-person-fields.php', 231 | CTC_ADMIN_DIR . '/admin-posts.php', 232 | CTC_ADMIN_DIR . '/admin-sermon-fields.php', 233 | CTC_ADMIN_DIR . '/admin-support.php', 234 | CTC_ADMIN_DIR . '/dashboard.php', 235 | CTC_ADMIN_DIR . '/edd-license.php', 236 | CTC_ADMIN_DIR . '/editor.php', 237 | CTC_ADMIN_DIR . '/import.php', 238 | CTC_ADMIN_DIR . '/migrate-risen.php', 239 | CTC_ADMIN_DIR . '/notices.php', 240 | CTC_ADMIN_DIR . '/upgrade.php', 241 | 242 | // Libraries. 243 | CTC_LIB_DIR . '/ct-meta-box/ct-meta-box.php', // see CTMB_URL constant defined above 244 | 245 | // Classes. 246 | CTC_CLASS_DIR . '/CTC_Dashboard_News.php', // see CTMB_URL constant defined above 247 | 248 | ), 249 | 250 | // Frontend only 251 | /* 252 | 'frontend' => array ( 253 | 254 | ), 255 | */ 256 | 257 | )); 258 | } 259 | 260 | /** 261 | * Load includes 262 | * 263 | * Include files based on whether or not condition is met. 264 | * 265 | * @since 0.9 266 | * @access public 267 | */ 268 | public function load_includes() 269 | { 270 | 271 | // Get includes 272 | $includes = $this->includes; 273 | 274 | // Loop conditions 275 | foreach ($includes as $condition => $files) { 276 | 277 | $do_includes = false; 278 | 279 | // Check condition 280 | switch ($condition) { 281 | 282 | // Admin Only 283 | case 'admin': 284 | 285 | if (is_admin()) { 286 | $do_includes = true; 287 | } 288 | 289 | break; 290 | 291 | // Frontend Only 292 | case 'frontend': 293 | 294 | if (! is_admin()) { 295 | $do_includes = true; 296 | } 297 | 298 | break; 299 | 300 | // Admin or Frontend (always) 301 | default: 302 | 303 | $do_includes = true; 304 | 305 | break; 306 | } 307 | 308 | // Loop files if condition met 309 | if ($do_includes) { 310 | 311 | foreach ($files as $file) { 312 | require_once trailingslashit(CTC_PATH) . $file; 313 | } 314 | } 315 | } 316 | } 317 | 318 | /** 319 | * Trigger flushing of rewrite rules. 320 | * 321 | * Doing this on activation makes post type rewrite slugs take effect. 322 | * 323 | * @since 1.0 324 | * @access public 325 | */ 326 | public static function trigger_flush_rewrite_rules() 327 | { 328 | 329 | // Tell to flush rules after post types registered. 330 | update_option('ctc_flush_rewrite_rules', '1'); 331 | } 332 | 333 | /** 334 | * Check if rewrite rules should be flushed. 335 | * 336 | * This checks if ctc_flush_rewrite_rules option has been set earlier 337 | * so that the rewrite rules can be flushed later. 338 | */ 339 | public function ctc_check_flush_rewrite_rules() 340 | { 341 | 342 | // Check if option was set. 343 | if (get_option('ctc_flush_rewrite_rules')) { 344 | 345 | // Flush rewrite rules. 346 | flush_rewrite_rules(); 347 | 348 | // Delete option so this doesn't run again. 349 | delete_option('ctc_flush_rewrite_rules'); 350 | } 351 | } 352 | } 353 | 354 | // Instantiate the main class. 355 | new Church_Theme_Content(); 356 | -------------------------------------------------------------------------------- /includes/admin/admin-maps.php: -------------------------------------------------------------------------------- 1 | esc_html_x( 'Road', 'map', 'church-theme-content' ), 30 | 'SATELLITE' => esc_html_x( 'Satellite', 'map', 'church-theme-content' ), 31 | 'HYBRID' => esc_html_x( 'Hybrid', 'map', 'church-theme-content' ), 32 | 'TERRAIN' => esc_html_x( 'Terrain', 'map', 'church-theme-content' ), 33 | ); 34 | 35 | return apply_filters( 'ctc_gmaps_types', $types ); 36 | 37 | } 38 | 39 | /** 40 | * Google Map type default 41 | * 42 | * @since 0.9 43 | * @return string Default map type 44 | */ 45 | function ctc_gmaps_type_default() { 46 | return apply_filters( 'ctc_gmaps_type_default', 'HYBRID' ); 47 | } 48 | 49 | /** 50 | * Zoom levels array 51 | * 52 | * @since 0.9 53 | * @return array Valid Google Maps zoom levels 54 | */ 55 | function ctc_gmaps_zoom_levels() { 56 | 57 | $zoom_levels = array(); 58 | 59 | $zoom_min = ctc_gmaps_zoom_min(); // 0 is actually lowest but then it's detected as not set and reverts to default 60 | $zoom_max = ctc_gmaps_zoom_max(); 61 | 62 | for ( $z = $zoom_min; $z <= $zoom_max; $z++ ) { 63 | $zoom_levels[$z] = $z; 64 | } 65 | 66 | return apply_filters( 'ctc_gmaps_zoom_levels', $zoom_levels ); 67 | 68 | } 69 | 70 | /** 71 | * Zoom maximum 72 | * 73 | * @since 1.7.1 74 | * @return int Google Maps zoom max 75 | */ 76 | function ctc_gmaps_zoom_max() { 77 | return apply_filters( 'ctc_gmaps_zoom_max', 21 ); 78 | } 79 | 80 | /** 81 | * Zoom minimum 82 | * 83 | * @since 0.9 84 | * @return int Default Google Maps zoom level 85 | */ 86 | function ctc_gmaps_zoom_min() { 87 | return apply_filters( 'ctc_gmaps_zoom_min', 1 ); 88 | } 89 | 90 | /** 91 | * Zoom level default 92 | * 93 | * @since 0.9 94 | * @return int Default Google Maps zoom level 95 | */ 96 | function ctc_gmaps_zoom_level_default() { 97 | return apply_filters( 'ctc_gmaps_zoom_level_default', 14 ); 98 | } 99 | 100 | /********************************** 101 | * CONDITIONS 102 | **********************************/ 103 | 104 | /** 105 | * Adding/editing event/location with longitude and latitude fields support? 106 | * 107 | * @since 1.7.1 108 | * @return bool True if conditions met 109 | */ 110 | function ctc_has_lat_lng_fields() { 111 | 112 | // Get current screen 113 | $screen = get_current_screen(); 114 | 115 | // Only on Add/Edit Location or Event 116 | if ( ! ( $screen->base == 'post' && in_array( $screen->post_type, array( 'ctc_event', 'ctc_location' ) ) ) ) { 117 | return false; 118 | } 119 | 120 | // Only if has latitude and longitude fields supported 121 | if ( 122 | ( 'ctc_event' == $screen->post_type && ( ! ctc_field_supported( 'events', '_ctc_event_map_lat' ) || ! ctc_field_supported( 'events', '_ctc_event_map_lng' ) ) ) 123 | || ( 'ctc_location' == $screen->post_type && ( ! ctc_field_supported( 'locations', '_ctc_location_map_lat' ) || ! ctc_field_supported( 'locations', '_ctc_location_map_lng' ) ) ) 124 | ) { 125 | return false; 126 | } 127 | 128 | return true; 129 | 130 | } 131 | 132 | /********************************** 133 | * API KEY NOTICE 134 | **********************************/ 135 | 136 | /** 137 | * Show missing Google Maps API Key notice 138 | * 139 | * The notice should only be shown if certain conditions are met. 140 | * 141 | * @since 1.7 142 | * @return bool True if notice should be shown 143 | */ 144 | function ctc_gmaps_api_key_show_notice() { 145 | 146 | $show = true; 147 | 148 | // Get current screen 149 | $screen = get_current_screen(); 150 | 151 | // Only on List or Add/Edit Location or Event 152 | if ( ! ( in_array( $screen->base, array( 'edit', 'post' ) ) && in_array( $screen->post_type, array( 'ctc_event', 'ctc_location' ) ) ) ) { 153 | $show = false; 154 | } 155 | 156 | // Only if user can edit plugin settings 157 | if ( ! current_user_can( 'manage_options' ) ) { 158 | return; 159 | } 160 | 161 | // Only if latitude and longitude fields supported. 162 | // Skip this test on list of events/locations, since ctc_has_lat_lng_fields() checks that screen is add/edit. 163 | if ( 'edit' !== $screen->base && ! ctc_has_lat_lng_fields() ) { 164 | return; 165 | } 166 | 167 | // Only if key not set 168 | if ( ctc_setting( 'google_maps_api_key' ) ) { 169 | $show = false; 170 | } 171 | 172 | // Only if not already dismissed 173 | if ( get_option( 'ctc_gmaps_api_key_notice_dismissed' ) ) { 174 | $show = false; 175 | } 176 | 177 | return $show; 178 | 179 | } 180 | 181 | /** 182 | * Show notice if Google Maps API Key missing 183 | * 184 | * This will show only on Add/Edit event or location screen if key is not set and user has permission to set it. 185 | * 186 | * @since 1.7 187 | */ 188 | function ctc_gmaps_api_key_notice() { 189 | 190 | // Only on Add/Edit Location or Event, key is not set, not already dismissed and latitude/longitude fields supported 191 | if ( ! ctc_gmaps_api_key_show_notice() ) { 192 | return; 193 | } 194 | 195 | // Show notice 196 | ?> 197 | 198 |
199 |

200 | Google Maps API Key Not Set. You must set it in Church Content Settings for maps to work.', 'church-theme-content' ), 205 | array( 206 | 'strong' => array(), 207 | 'a' => array( 208 | 'href' => array(), 209 | 'target' => array(), 210 | ), 211 | ) 212 | ), 213 | esc_url( admin_url( 'options-general.php?page=' . CTC_DIR . '#locations' ) ) 214 | ); 215 | ?> 216 |

217 |
218 | 219 | 245 | 246 | 263 | 264 | meta_box['id'], array( 'ctc_event_location', 'ctc_location' ) ) ) { 318 | return; 319 | } 320 | 321 | // Only if latitude and longitude fields supported 322 | if ( ! ctc_has_lat_lng_fields() ) { 323 | return; 324 | } 325 | 326 | // Only if entered API Key 327 | // This will prevent confusion for new users (which require a key) 328 | // It will help old users (not requiring a key) set one up to ensure things remain smooth 329 | if ( ctc_setting( 'google_maps_api_key' ) ) { 330 | 331 | // Output map elements 332 | ?> 333 | 334 |
335 | 336 |
337 | 338 |

339 | 340 |

341 | 342 |
343 | 344 | 350 | 351 |

352 | 353 | Important: You must set your Google Maps API Key in Church Content Settings for maps to work.', 'church-theme-content' ), 358 | array( 359 | 'strong' => array(), 360 | 'a' => array( 361 | 'href' => array(), 362 | 'target' => array(), 363 | ), 364 | ) 365 | ), 366 | esc_url( admin_url( 'options-general.php?page=' . CTC_DIR . '#locations' ) ) 367 | ); 368 | ?> 369 | 370 | 371 | 372 |

373 | 374 | is same but with the button on end. 386 | * 387 | * @since 1.6 388 | */ 389 | function ctc_coordinate_field( $data ) { 390 | 391 | // Get current screen 392 | $screen = get_current_screen(); 393 | 394 | // Text input from CT Meta Box 395 | $input = ''; 396 | 397 | // Only if address field supported 398 | if ( 399 | ( 'ctc_event' == $screen->post_type && ! ctc_field_supported( 'events', '_ctc_event_address' ) ) 400 | || ( 'ctc_location' == $screen->post_type && ! ctc_field_supported( 'locations', '_ctc_location_address' ) ) 401 | ) { 402 | return $input; 403 | } 404 | 405 | // Only if latitude and longitude fields supported 406 | if ( ! ctc_has_lat_lng_fields() ) { 407 | return $input; 408 | } 409 | 410 | // Append aufofill button 411 | $input .= ' '; 412 | 413 | // Return input with button 414 | return $input; 415 | 416 | } 417 | -------------------------------------------------------------------------------- /includes/helpers.php: -------------------------------------------------------------------------------- 1 | '/' . $utm_base_query . '&utm_campaign=church-theme-content', 88 | 89 | // How to get a Google Maps API key. 90 | 'google-maps-api-key' => '/go/google-maps-api-key/', // no utm, messes up redirect. 91 | 92 | // Church Content product page. 93 | 'church-content' => '/plugins/church-content/' . $utm_base_query . '&utm_campaign=church-theme-content', 94 | 95 | // Church Content Pro product page. 96 | // Pro details are on same page as Church Content plugin. 97 | 'church-content-pro' => '/plugins/church-content-pro/' . $utm_base_query . '&utm_campaign=church_content_pro', 98 | 99 | // How to upgrade from Custom Recurring Events to Pro. 100 | 'cre-to-pro' => '/go/cre-to-pro/' . $utm_base_query . '&utm_campaign=church_content_pro', 101 | 102 | // What SEO Structured Data setting does. 103 | 'seo-setting' => '/go/seo-setting/' . $utm_base_query . '&utm_campaign=church_content_pro&utm_content=settings', 104 | 105 | // How to handle event registration. 106 | 'event-registration' => '/go/ctc-event-registration/' . $utm_base_query . '&utm_campaign=church-theme-content', 107 | 108 | // How to update PHP. 109 | 'update-php' => '/go/update-php/' . $utm_base_query . '&utm_campaign=church-theme-content', 110 | 111 | // URLs for sermon media field descriptions. 112 | 'sermon-video-sites' => '/go/ctc-sermon-video-sites/', // external. 113 | 'sermon-video-help' => '/go/ctc-sermon-video-help/' . $utm_base_query . '&utm_campaign=church-theme-content&utm_content=sermon', 114 | 'sermon-audio-sites' => '/go/ctc-sermon-audio-sites/', // external. 115 | 'sermon-audio-help' => '/go/ctc-sermon-audio-help/' . $utm_base_query . '&utm_campaign=church-theme-content&utm_content=sermon', 116 | 117 | // Podcasting guide. 118 | 'podcast-guide' => '/go/podcast-guide/' . $utm_base_query . '&utm_campaign=church-theme-content', 119 | 120 | // Podcasting submission. 121 | 'podcast-submit-apple' => '/go/apple-podcast-submit/', // no UTM, redirects. add ?url=https://feedurl argument 122 | 'podcast-submit-google' => '/go/google-podcast-submit/', // no UTM, redirects. 123 | 124 | // Agency mode guide. 125 | 'agency-mode' => '/go/agency-mode/', // no UTM, used by theme framework too. 126 | 127 | // Newsletter sign up. 128 | 'newsletter' => '/newsletter/', // no UTM, redirects to Mailchimp page. 129 | 130 | // Blog. 131 | 'blog' => '/blog/' . $utm_base_query . '&utm_campaign=church-theme-content&utm_content=settings', 132 | 133 | // Risen migration guide. 134 | 'migrate-risen' => '/go/switch-from-risen/' . $utm_base_query . '&utm_campaign=migrate-risen', 135 | 'migrate-risen-backup' => '/go/backups/', 136 | 137 | ); 138 | 139 | // Make URL. 140 | if ( isset( $paths[ $path_key ] ) ) { 141 | $url .= $paths[ $path_key ]; 142 | } 143 | 144 | // Add or replace params. 145 | if ( isset( $query_args ) && is_array( $query_args ) ) { 146 | $url = add_query_arg( $query_args, $url ); 147 | } 148 | 149 | // Return filtered. 150 | return apply_filters( 'ctc_ctcom_url', $url, $path_key, $query_args ); 151 | 152 | } 153 | 154 | /** 155 | * Make slug in URL bold. 156 | * 157 | * This is used by settings descriptions for URL slug change examples. 158 | * 159 | * Make https://yourname.com/slug/ into https://yourname.com/slug/ 160 | * 161 | * @since 2.0 162 | * @param string $slug Slug to append to home_url(). 163 | * @return string URL with slug made bold. 164 | */ 165 | function ctc_make_url_slug_bold( $slug ) { 166 | return preg_replace( '/(.*)(\/(.*)\/)$/', '$1/' . $slug . '/', trailingslashit( home_url( $slug ) ) ); 167 | } 168 | 169 | 170 | /** 171 | * Check if string is a URL 172 | * 173 | * @since 2.0 174 | * @param string $string String to check for URL format 175 | * @return bool True if string i=s URL 176 | */ 177 | function ctc_is_url( $string ) { 178 | 179 | $bool = false; 180 | 181 | $url_pattern = '/^(http(s*)):\/\//i'; 182 | 183 | if ( preg_match( $url_pattern, $string ) ) { // URL 184 | $bool = true; 185 | } 186 | 187 | return apply_filters( 'ctc_is_url', $bool, $string ); 188 | 189 | } 190 | 191 | /************************************************* 192 | * STRINGS 193 | *************************************************/ 194 | 195 | /** 196 | * Shorten a string 197 | * 198 | * @since 2.0 199 | * @param string $string String to shorten. 200 | * @param int $length Max length of string. 201 | * @return string Shortened string. 202 | */ 203 | function ctc_shorten( $string, $length ) { 204 | 205 | // Trim. 206 | $string = trim( $string ); 207 | 208 | // Shorten. 209 | $string = mb_substr( $string, 0, $length ); 210 | 211 | // Trim. 212 | $string = trim( $string ); 213 | 214 | // Return. 215 | return apply_filters( 'ctc_shorten', $string, $length ); 216 | 217 | } 218 | 219 | 220 | /** 221 | * Convert to one line 222 | * 223 | * It replaces line breaks with commas. 224 | * 225 | * @since 2.0 226 | * @param string $string Multi-line string. 227 | * @return string Single line string. 228 | */ 229 | function ctc_one_line( $string ) { 230 | 231 | $one_line = $string; 232 | 233 | if ( $string ) { 234 | $one_line = strip_tags( $string ); // remove HTML 235 | $one_line = preg_replace( '/\r\n|\n|\r/', ', ', $one_line ); // replace line breaks with commas 236 | $one_line = trim( $one_line ); // remove whitespace 237 | } 238 | 239 | return apply_filters( 'ctc_one_line', $one_line, $string ); 240 | 241 | } 242 | 243 | /************************************************* 244 | * ARRAYS 245 | *************************************************/ 246 | 247 | /** 248 | * Merge an array into another array after a specific key 249 | * 250 | * Meant for one dimensional associative arrays. 251 | * Used to insert post type overview columns. 252 | * 253 | * @since 0.9 254 | * @param array $original_array Array to merge another into 255 | * @param array $insert_array Array to merge into original 256 | * @param mixed $after_key Key in original array to merge second array after 257 | * @return array Modified array 258 | */ 259 | function ctc_array_merge_after_key( $original_array, $insert_array, $after_key ) { 260 | 261 | $modified_array = array(); 262 | 263 | // loop original array items 264 | foreach ( $original_array as $item_key => $item_value ) { 265 | 266 | // rebuild the array one item at a time 267 | $modified_array[$item_key] = $item_value; 268 | 269 | // insert array after specific key 270 | if ( $item_key == $after_key ) { 271 | $modified_array = array_merge( $modified_array, $insert_array ); 272 | } 273 | 274 | } 275 | 276 | return apply_filters( 'ctc_array_merge_after_key', $modified_array, $original_array, $insert_array, $after_key ); 277 | 278 | } 279 | 280 | /** 281 | * Show array as HTML 282 | * 283 | * This is helpful for development / debugging 284 | * 285 | * @since 1.2 286 | * @param array $array Array to format 287 | * @param bool $return Return or echo output 288 | */ 289 | function ctc_print_array( $array, $return = false ) { 290 | 291 | $result = '
' . print_r( $array, true ) . '
'; 292 | 293 | if ( empty($return) ) { 294 | echo $result; 295 | } else { 296 | return $result; 297 | } 298 | 299 | } 300 | 301 | /************************************************* 302 | * DATES 303 | *************************************************/ 304 | 305 | /** 306 | * Convert date and time to MySQL DATETIME format 307 | * 308 | * If no date, value will be 0000-00-00 00:00:00 309 | * If no time, value will be 2014-10-28 00:00:00 310 | * 311 | * @since 1.2 312 | * @param string $date Date in YYYY-mm-dd format (e.g. 2014-05-10 for May 5th, 2014) 313 | * @param string $time Time in 24-hour hh-mm format (e.g. 08:00 for 8 AM or 13:12 for 1:12 PM) 314 | * @return string Date and time in DATETIME format (e.g. 2014-05-10 13:12:00) 315 | */ 316 | function ctc_convert_to_datetime( $date, $time ) { 317 | 318 | if ( empty( $date ) ) { 319 | $date = '0000-00-00'; 320 | } 321 | if ( empty( $time ) ) { 322 | $time = '00:00'; 323 | } 324 | 325 | $datetime = $date . ' ' . $time . ':00'; 326 | 327 | return apply_filters( 'ctc_convert_to_datetime', $datetime, $date, $time ); 328 | 329 | } 330 | 331 | /** 332 | * Convert address to one line 333 | * 334 | * It replaces line breaks with commas. 335 | * 336 | * @since 2.0 337 | * @param string $address Multi-line address 338 | * @return string Single line address 339 | */ 340 | function ctc_address_one_line( $address ) { 341 | 342 | $address_one_line = ctc_one_line( $address ); 343 | 344 | return apply_filters( 'ctc_address_one_line', $address_one_line, $address ); 345 | 346 | } 347 | 348 | /************************************************* 349 | * FUNCTIONS 350 | *************************************************/ 351 | 352 | /** 353 | * Check if a function is available 354 | * 355 | * This is helpful: http://bit.ly/100BpPJ 356 | * 357 | * @since 1.2 358 | * @param string $function Name of function to check 359 | * @return bool True if function exists and is not disabled 360 | */ 361 | function ctc_function_available( $function ) { 362 | 363 | $available = false; 364 | 365 | // Function exists? 366 | if ( function_exists( $function ) ) { 367 | 368 | // Is it not disabled in php.ini? 369 | $disabled_functions = explode( ',', ini_get( 'disable_functions' ) ); 370 | if ( ! in_array( $function, $disabled_functions ) ) { 371 | $available = true; 372 | } 373 | 374 | } 375 | 376 | return apply_filters( 'ctc_function_available', $available, $function ); 377 | 378 | } 379 | 380 | /************************************************* 381 | * PLUGINS 382 | *************************************************/ 383 | 384 | /** 385 | * Check if Church Content Pro plugin active. 386 | * 387 | * @since 2.0 388 | * @return bool True if plugin installed and active. 389 | */ 390 | function ctc_pro_is_active() { 391 | 392 | $active = false; 393 | 394 | if ( defined( 'CCP_VERSION' ) ) { 395 | $active = true; 396 | } 397 | 398 | return $active; 399 | 400 | } 401 | 402 | /** 403 | * Check if Custom Recurring Events plugin is active. 404 | * 405 | * @since 2.0 406 | * @return bool True if plugin installed and active. 407 | */ 408 | function ctc_cre_is_active() { 409 | 410 | $active = false; 411 | 412 | if ( defined( 'CTC_CRE_VERSION' ) ) { 413 | $active = true; 414 | } 415 | 416 | return $active; 417 | 418 | } 419 | -------------------------------------------------------------------------------- /includes/support.php: -------------------------------------------------------------------------------- 1 | array( 38 | 'theme_support' => 'ctc-sermons', // theme support feature name 39 | 'post_type' => 'ctc_sermon', // post type feature requires 40 | ), 41 | 42 | 'events' => array( 43 | 'theme_support' => 'ctc-events', 44 | 'post_type' => 'ctc_event', 45 | ), 46 | 47 | 'people' => array( 48 | 'theme_support' => 'ctc-people', 49 | 'post_type' => 'ctc_person', 50 | ), 51 | 52 | 'locations' => array( 53 | 'theme_support' => 'ctc-locations', 54 | 'post_type' => 'ctc_location', 55 | ), 56 | 57 | ); 58 | 59 | // Add feature to array values for ease of use 60 | foreach ( $features as $feature_key => $feature_data ) { 61 | $features[$feature_key]['feature'] = $feature_key; 62 | } 63 | 64 | // Return specific feature 65 | if ( ! empty( $feature ) ) { 66 | if ( isset( $features[$feature] ) ) { // feature data exists 67 | return apply_filters( 'ctc_get_feature_data-' . $feature, $features[$feature] ); 68 | } 69 | } 70 | 71 | // Return all features 72 | else { 73 | return apply_filters( 'ctc_get_feature_data', $features ); 74 | } 75 | 76 | // In case feature given but not valid 77 | return false; 78 | 79 | } 80 | 81 | /** 82 | * Get feature data by post type 83 | * 84 | * @since 0.9 85 | * @param string $post_type Post type to get feature data for 86 | * @return array Feature data 87 | */ 88 | function ctc_get_feature_data_by_post_type( $post_type ) { 89 | 90 | $data = false; 91 | 92 | // Get all features 93 | $features = ctc_get_feature_data(); 94 | 95 | // Loop features to find post type and get feature data 96 | foreach ( $features as $feature_key => $feature_data ) { 97 | 98 | // Post type given used by this feature 99 | if ( $post_type == $feature_data['post_type'] ) { 100 | 101 | $data = $feature_data; 102 | 103 | break; 104 | 105 | } 106 | } 107 | 108 | // Return filterable 109 | return apply_filters( 'ctc_get_feature_data_by_post_type', $data, $post_type ); 110 | 111 | } 112 | 113 | /**************************************** 114 | * THEME SUPPORT 115 | **************************************** 116 | 117 | /** 118 | * Default features for unsupported themes 119 | * 120 | * If no add_theme_support( 'church-theme-content' ), add support for all features with no arguments. 121 | * This causes all content to be revealed in case admin switched to unsupported theme. 122 | * They can then develop the theme for the plugin or retrieve their content. 123 | * 124 | * @since 0.9 125 | */ 126 | function ctc_set_default_theme_support() { 127 | 128 | // Theme does not support plugin 129 | if ( ! current_theme_supports( 'church-theme-content' ) ) { 130 | 131 | // Loop features 132 | $features = ctc_get_feature_data(); 133 | foreach ( $features as $feature_key => $feature_data ) { 134 | 135 | // Add support with no arguments so defaults are used (everything) 136 | add_theme_support( $feature_data['theme_support'] ); 137 | 138 | } 139 | 140 | } 141 | 142 | } 143 | 144 | add_action( 'init', 'ctc_set_default_theme_support', 1 ); // init 1 is right after after_setup_theme when theme add support but earlier than normal plugin init at 10 145 | 146 | /** 147 | * Get theme support data for a feature 148 | * 149 | * Optionally specify an argument to get that data 150 | * 151 | * @since 0.9 152 | * @param string $feature Feature to get theme support data for 153 | * @return mixed Feature data if found 154 | */ 155 | function ctc_get_theme_support( $feature, $argument = null ) { 156 | 157 | $data = false; 158 | 159 | // Theme has support 160 | $support = get_theme_support( $feature ); 161 | if ( $support ) { 162 | 163 | // Get theme support data 164 | $support = isset( $support[0] ) ? $support[0] : false; 165 | 166 | // Use data for specific argument 167 | if ( isset( $argument ) ) { // argument given 168 | if ( is_array( $support ) && isset( $support[$argument] ) ) { // argument is set (even if empty) 169 | $data = $support[$argument]; 170 | } else { 171 | $data = null; // so return value will return false for isset() 172 | } 173 | } 174 | 175 | // Use all arguments 176 | else { 177 | $data = $support; 178 | } 179 | 180 | } 181 | 182 | // Return data 183 | return apply_filters( 'ctc_get_theme_support', $data, $feature, $argument ); 184 | 185 | } 186 | 187 | /** 188 | * Get theme support data based on post type 189 | * 190 | * Optionally specify an argument to get that data. 191 | * 192 | * @since 0.9 193 | * @param string $post_type Post type to get theme support data for. 194 | * @param string $argument Optional feature argument to get specific data for. 195 | * @return mixed Array of all feature data or specific argument 196 | */ 197 | function ctc_get_theme_support_by_post_type( $post_type, $argument = null ) { 198 | 199 | $data = false; 200 | 201 | // Get feature based on post type 202 | $feature_data = ctc_get_feature_data_by_post_type( $post_type ); 203 | if ( $feature_data ) { 204 | 205 | // Get data for feature/argument 206 | $data = ctc_get_theme_support( $feature_data['theme_support'], $argument ); 207 | 208 | } 209 | 210 | // Return data 211 | return apply_filters( 'ctc_get_theme_support_by_post_type', $data, $post_type, $argument ); 212 | 213 | } 214 | 215 | /********************************************* 216 | * FEATURE CHECKING 217 | *********************************************/ 218 | 219 | /** 220 | * Check if feature is supported 221 | * 222 | * @since 0.9 223 | * @param string $feature Feature to check support for 224 | * @return bool True if supported by theme 225 | */ 226 | function ctc_feature_supported( $feature ) { 227 | 228 | $supported = false; 229 | 230 | // Get feature data 231 | $feature_data = ctc_get_feature_data( $feature ); 232 | if ( $feature_data ) { // valid feature returns data 233 | 234 | // Does theme support feature? 235 | if ( current_theme_supports( $feature_data['theme_support'] ) ) { 236 | 237 | $supported = true; 238 | 239 | // (in future could override support via plugin settings here) 240 | 241 | } 242 | 243 | } 244 | 245 | // Return filtered 246 | return apply_filters( 'ctc_feature_supported', $supported, $feature ); 247 | 248 | } 249 | 250 | /** 251 | * Check if taxonomy is supported 252 | * 253 | * @since 0.9 254 | * @param string $feature Feature taxonomy relates to 255 | * @param string $taxonomy Taxonomy to check support for 256 | * @return bool True if feature supported 257 | */ 258 | function ctc_taxonomy_supported( $feature, $taxonomy ) { 259 | 260 | $supported = false; 261 | 262 | // Get feature data 263 | $feature_data = ctc_get_feature_data( $feature ); 264 | if ( $feature_data ) { // valid feature returns data 265 | 266 | // Theme taxonomies are specified 267 | $theme_taxonomies = ctc_get_theme_support( $feature_data['theme_support'], 'taxonomies' ); 268 | if ( isset( $theme_taxonomies ) ) { 269 | 270 | // Taxonomy is explicitly supported 271 | if ( in_array( $taxonomy, (array) $theme_taxonomies ) ) { 272 | $supported = true; 273 | } 274 | 275 | } 276 | 277 | // Theme taxonomies are not specified 278 | // Default is to use all taxonomies when support not explicit, so anything returns true 279 | else { 280 | $supported = true; 281 | } 282 | 283 | // (if true, could override with false using plugin settings here) 284 | // (checking if show_ui is true is not enough since this is used during taxonomy registration) 285 | 286 | } 287 | 288 | // Return filtered 289 | return apply_filters( 'ctc_taxonomy_supported', $supported, $feature, $taxonomy ); 290 | 291 | } 292 | 293 | /** 294 | * Check if field is supported 295 | * 296 | * @since 0.9 297 | * @param string $feature Feature field relates to 298 | * @param string $field Field to check support for 299 | * @return bool True if field supported 300 | */ 301 | function ctc_field_supported( $feature, $field ) { 302 | 303 | $supported = false; 304 | 305 | // Get feature data 306 | $feature_data = ctc_get_feature_data( $feature ); 307 | if ( $feature_data ) { // valid feature returns data 308 | 309 | // Theme fields are specified 310 | $theme_fields = ctc_get_theme_support( $feature_data['theme_support'], 'fields' ); 311 | if ( isset( $theme_fields ) ) { 312 | 313 | // Field is explicitly supported 314 | if ( in_array( $field, (array) $theme_fields ) ) { 315 | $supported = true; 316 | } 317 | 318 | } 319 | 320 | // Theme fields are not specified 321 | // Default is to use all fields when support not explicit, so anything returns true 322 | else { 323 | $supported = true; 324 | } 325 | 326 | // (if true, can override with false using plugin settings here) 327 | 328 | } 329 | 330 | // Return filtered 331 | return apply_filters( 'ctc_field_supported', $supported, $feature, $field ); 332 | 333 | } 334 | 335 | /********************************************* 336 | * FIELD FILTERING 337 | *********************************************/ 338 | 339 | /** 340 | * Filter Meta Box Fields 341 | * 342 | * Add filters for CT_Meta_Box to set visibility and override data on fields 343 | * based on theme support and possibly in future plugin settings. 344 | * 345 | * @since 0.9 346 | */ 347 | function ctc_filter_fields() { 348 | 349 | // Loop features to filter their fields 350 | $features = ctc_get_feature_data(); 351 | foreach ( $features as $feature_key => $feature_data ) { 352 | 353 | // Has post type, filter CT_Meta_Box configs 354 | if ( isset( $feature_data['post_type'] ) ) { 355 | 356 | // Set Visible Fields 357 | add_filter( 'ctmb_visible_fields-' . $feature_data['post_type'], 'ctc_set_visible_fields', 10, 2 ); 358 | 359 | // Set Field Overrides 360 | add_filter( 'ctmb_field_overrides-' . $feature_data['post_type'], 'ctc_set_field_overrides', 10, 2 ); 361 | 362 | } 363 | 364 | } 365 | 366 | } 367 | 368 | add_action( 'init', 'ctc_filter_fields' ); 369 | 370 | /** 371 | * Set visible fields 372 | * 373 | * Show or hide CT_Meta_Box fields for a post type based on add_theme_support. 374 | * Door is open for plugin settings to override in future. 375 | * 376 | * @since 0.9 377 | * @param array $visible_fields Current field visibility 378 | * @param string $post_type Post type this relates to 379 | * @return array Modified $visible_fields 380 | */ 381 | function ctc_set_visible_fields( $visible_fields, $post_type ) { 382 | 383 | // All fields 384 | $original_visible_fields = $visible_fields; 385 | 386 | // Filter visible fields based on theme support 387 | // If not set, all fields are used by default 388 | // If set and empty, all fields will be hidden 389 | $theme_fields = ctc_get_theme_support_by_post_type( $post_type, 'fields' ); 390 | if ( isset( $theme_fields ) ) { 391 | 392 | // Make new array out of fields theme supports 393 | $visible_fields = $theme_fields; 394 | 395 | // Add support for fields that are not from Church Content 396 | // (otherwise they would need to be in add_theme_support arguments) 397 | foreach ( $original_visible_fields as $field ) { 398 | if ( ! preg_match( '/^_ctc_.+$/', $field ) ) { // CTC fields are prefixed by "_ctc_" 399 | $visible_fields[] = $field; 400 | } 401 | } 402 | 403 | } 404 | 405 | // (here plugin settings could disable fields supported by theme) 406 | 407 | // Return default or filtered field list 408 | return $visible_fields; 409 | 410 | } 411 | 412 | /** 413 | * Set field overrides 414 | * 415 | * Override CT_Meta_Box field data for a post type based on add_theme_support. 416 | * 417 | * @since 0.9 418 | * @param array $field_overrides Field overrides to set 419 | * @param string $post_type Post type to set overrides on 420 | * @return mixed Theme support data 421 | */ 422 | function ctc_set_field_overrides( $field_overrides, $post_type ) { 423 | 424 | // Return field overrides, if any 425 | return ctc_get_theme_support_by_post_type( $post_type, 'field_overrides' ); 426 | 427 | } 428 | -------------------------------------------------------------------------------- /includes/post-types.php: -------------------------------------------------------------------------------- 1 | array( 46 | 'name' => esc_html( $plural ), 47 | 'singular_name' => esc_html( $singular ), 48 | 'add_new' => esc_html_x( 'Add New', 'sermon', 'church-theme-content' ), 49 | 'add_new_item' => esc_html( sprintf( 50 | /* translators: %s is singular word for "Sermon", possibly changed via settings. Always use %s and not "Sermon" directly. */ 51 | _x( 'Add %s', 'sermon', 'church-theme-content' ), 52 | $singular 53 | ) ), 54 | 'edit_item' => esc_html( sprintf( 55 | /* translators: %s is singular word for "Sermon", possibly changed via settings. Always use %s and not "Sermon" directly. */ 56 | _x( 'Edit %s', 'sermon', 'church-theme-content' ), 57 | $singular 58 | ) ), 59 | 'new_item' => esc_html( sprintf( 60 | /* translators: %s is singular word for "Sermon", possibly changed via settings. Always use %s and not "Sermon" directly. */ 61 | _x( 'New %s', 'sermon', 'church-theme-content' ), 62 | $singular 63 | ) ), 64 | 'all_items' => esc_html( sprintf( 65 | /* translators: %s is plural word for "Sermons", possibly changed via settings. Always use %s and not "Sermons" directly. */ 66 | _x( 'All %s', 'sermons', 'church-theme-content' ), 67 | $plural 68 | ) ), 69 | 'view_item' => esc_html( sprintf( 70 | /* translators: %s is singular word for "Sermon", possibly changed via settings. Always use %s and not "Sermon" directly. */ 71 | _x( 'View %s', 'sermon', 'church-theme-content' ), 72 | $singular 73 | ) ), 74 | 'view_items' => esc_html( sprintf( 75 | /* translators: %s is plural word for "Sermons", possibly changed via settings. Always use %s and not "Sermons" directly. */ 76 | _x( 'View %s', 'sermons', 'church-theme-content' ), 77 | $plural 78 | ) ), 79 | 'search_items' => esc_html( sprintf( 80 | /* translators: %s is plural word for "Sermons", possibly changed via settings. Always use %s and not "Sermons" directly. */ 81 | _x( 'Search %s', 'sermons', 'church-theme-content' ), 82 | $plural 83 | ) ), 84 | 'not_found' => esc_html( sprintf( 85 | /* translators: %s is lowercase plural word for "sermons", possibly changed via settings. Always use %s and not "sermons" directly. */ 86 | _x( 'No %s found', 'sermons', 'church-theme-content' ), 87 | $plural_lowercase 88 | ) ), 89 | 'not_found_in_trash' => esc_html( sprintf( 90 | /* translators: %s is lowercase plural word for "sermons", possibly changed via settings. Always use %s and not "sermons" directly. */ 91 | _x( 'No %s found in Trash', 'sermons', 'church-theme-content' ), 92 | $plural_lowercase 93 | ) ), 94 | // Note: WordPress now offers additional labels that may be worth defining: https://codex.wordpress.org/Function_Reference/register_post_type#Arguments. 95 | ), 96 | 'public' => ctc_feature_supported( 'sermons' ), 97 | 'has_archive' => ctc_feature_supported( 'sermons' ), 98 | 'rewrite' => array( 99 | 'slug' => 'sermons', 100 | 'with_front' => false, 101 | 'feeds' => ctc_feature_supported( 'sermons' ), 102 | ), 103 | 'supports' => array( 'title', 'editor', 'excerpt', 'publicize', 'thumbnail', 'comments', 'author', 'revisions' ), // 'editor' required for media upload button (see Meta Boxes note below about hiding) 104 | 'taxonomies' => array( 'ctc_sermon_topic', 'ctc_sermon_book', 'ctc_sermon_series', 'ctc_sermon_speaker', 'ctc_sermon_tag' ), 105 | 'menu_icon' => 'dashicons-video-alt3', 106 | 'show_in_rest' => true, 107 | ); 108 | 109 | // Filter arguments. 110 | if ( ! $unfiltered ) { 111 | $args = apply_filters( 'ctc_post_type_sermon_args', $args ); 112 | } 113 | 114 | return $args; 115 | 116 | } 117 | 118 | /** 119 | * Register sermon post type. 120 | * 121 | * @since 0.9 122 | */ 123 | function ctc_register_post_type_sermon() { 124 | 125 | // Arguments. 126 | $args = ctc_post_type_sermon_args(); 127 | 128 | // Registration. 129 | register_post_type( 130 | 'ctc_sermon', 131 | $args 132 | ); 133 | 134 | } 135 | 136 | add_action( 'init', 'ctc_register_post_type_sermon' ); // register post type. 137 | 138 | /** 139 | * "Sermon" singular label from post type. 140 | * 141 | * @since 2.0 142 | * @return string Default, translated or what is set in settings. 143 | */ 144 | function ctc_sermon_word_singular() { 145 | 146 | // Get post type label, possibly changed by Pro settings. 147 | $word = ctc_post_type_label( 'ctc_sermon', 'singular' ); 148 | 149 | // Get default word in case post type not registered yet. 150 | if ( ! $word ) { 151 | $word = _x( 'Sermon', 'post type singular', 'church-theme-content' ); 152 | } 153 | 154 | return $word; 155 | 156 | } 157 | 158 | /** 159 | * "Sermons" plural label from post type. 160 | * 161 | * @since 2.0 162 | * @return string Default, translated or what is set in settings. 163 | */ 164 | function ctc_sermon_word_plural() { 165 | 166 | // Get post type label, possibly changed by Pro settings. 167 | $word = ctc_post_type_label( 'ctc_sermon', 'plural' ); 168 | 169 | // Get default word in case post type not registered yet. 170 | if ( ! $word ) { 171 | $word = _x( 'Sermons', 'post type plural', 'church-theme-content' ); 172 | } 173 | 174 | return $word; 175 | 176 | } 177 | 178 | /********************************** 179 | * EVENT POST TYPE 180 | **********************************/ 181 | 182 | /** 183 | * Event post type arguments. 184 | * 185 | * @since 2.0 186 | * @param bool $unfiltered Set true to return arguments without being filtered. 187 | * @return array Post type registration arguments. 188 | */ 189 | function ctc_post_type_event_args( $unfiltered = false ) { 190 | 191 | // Arguments 192 | $args = array( 193 | 'labels' => array( 194 | 'name' => esc_html_x( 'Events', 'post type general name', 'church-theme-content' ), 195 | 'singular_name' => esc_html_x( 'Event', 'post type singular name', 'church-theme-content' ), 196 | 'add_new' => esc_html_x( 'Add New', 'event', 'church-theme-content' ), 197 | 'add_new_item' => esc_html__( 'Add Event', 'church-theme-content' ), 198 | 'edit_item' => esc_html__( 'Edit Event', 'church-theme-content' ), 199 | 'new_item' => esc_html__( 'New Event', 'church-theme-content' ), 200 | 'all_items' => esc_html__( 'All Events', 'church-theme-content' ), 201 | 'view_item' => esc_html__( 'View Event', 'church-theme-content' ), 202 | 'view_items' => esc_html__( 'View Events', 'church-theme-content' ), 203 | 'search_items' => esc_html__( 'Search Events', 'church-theme-content' ), 204 | 'not_found' => esc_html__( 'No events found', 'church-theme-content' ), 205 | 'not_found_in_trash' => esc_html__( 'No events found in Trash', 'church-theme-content' ) 206 | ), 207 | 'public' => ctc_feature_supported( 'events' ), 208 | 'has_archive' => ctc_feature_supported( 'events' ), 209 | 'rewrite' => array( 210 | 'slug' => 'events', 211 | 'with_front' => false, 212 | 'feeds' => ctc_feature_supported( 'events' ), 213 | ), 214 | 'supports' => array( 'title', 'editor', 'excerpt', 'publicize', 'thumbnail', 'comments', 'author', 'revisions' ), 215 | 'taxonomies' => array( 'ctc_event_category' ), 216 | 'menu_icon' => 'dashicons-calendar', 217 | 'show_in_rest' => true, 218 | ); 219 | 220 | // Filter arguments. 221 | if ( ! $unfiltered ) { 222 | $args = apply_filters( 'ctc_post_type_event_args', $args ); 223 | } 224 | 225 | return $args; 226 | 227 | } 228 | 229 | /** 230 | * Register event post type. 231 | * 232 | * @since 0.9 233 | */ 234 | function ctc_register_post_type_event() { 235 | 236 | // Arguments. 237 | $args = ctc_post_type_event_args(); 238 | 239 | // Registration. 240 | register_post_type( 241 | 'ctc_event', 242 | $args 243 | ); 244 | 245 | } 246 | 247 | add_action( 'init', 'ctc_register_post_type_event' ); // register post type. 248 | 249 | /********************************** 250 | * LOCATION POST TYPE 251 | **********************************/ 252 | 253 | /** 254 | * Location post type arguments. 255 | * 256 | * @since 2.0 257 | * @param bool $unfiltered Set true to return arguments without being filtered. 258 | * @return array Post type registration arguments. 259 | */ 260 | function ctc_post_type_location_args( $unfiltered = false ) { 261 | 262 | // Arguments 263 | $args = array( 264 | 'labels' => array( 265 | 'name' => esc_html_x( 'Locations', 'post type general name', 'church-theme-content' ), 266 | 'singular_name' => esc_html_x( 'Location', 'post type singular name', 'church-theme-content' ), 267 | 'add_new' => esc_html_x( 'Add New', 'location', 'church-theme-content' ), 268 | 'add_new_item' => esc_html__( 'Add Location', 'church-theme-content' ), 269 | 'edit_item' => esc_html__( 'Edit Location', 'church-theme-content' ), 270 | 'new_item' => esc_html__( 'New Location', 'church-theme-content' ), 271 | 'all_items' => esc_html__( 'All Locations', 'church-theme-content' ), 272 | 'view_item' => esc_html__( 'View Location', 'church-theme-content' ), 273 | 'view_items' => esc_html__( 'View Locations', 'church-theme-content' ), 274 | 'search_items' => esc_html__( 'Search Locations', 'church-theme-content' ), 275 | 'not_found' => esc_html__( 'No location found', 'church-theme-content' ), 276 | 'not_found_in_trash' => esc_html__( 'No location found in Trash', 'church-theme-content' ) 277 | ), 278 | 'public' => ctc_feature_supported( 'locations' ), 279 | 'has_archive' => ctc_feature_supported( 'locations' ), 280 | 'rewrite' => array( 281 | 'slug' => 'locations', 282 | 'with_front' => false, 283 | 'feeds' => ctc_feature_supported( 'locations' ), 284 | ), 285 | 'supports' => array( 'title', 'editor', 'excerpt', 'thumbnail', 'page-attributes' ), 286 | 'menu_icon' => 'dashicons-location', 287 | 'show_in_rest' => true, 288 | ); 289 | 290 | // Filter arguments. 291 | if ( ! $unfiltered ) { 292 | $args = apply_filters( 'ctc_post_type_location_args', $args ); 293 | } 294 | 295 | return $args; 296 | 297 | } 298 | 299 | /** 300 | * Register location post type. 301 | * 302 | * @since 0.9 303 | */ 304 | function ctc_register_location_post_type() { 305 | 306 | // Arguments. 307 | $args = ctc_post_type_location_args(); 308 | 309 | // Registration. 310 | register_post_type( 311 | 'ctc_location', 312 | $args 313 | ); 314 | 315 | } 316 | 317 | add_action( 'init', 'ctc_register_location_post_type' ); // register post type. 318 | 319 | /********************************** 320 | * PERSON POST TYPE 321 | **********************************/ 322 | 323 | /** 324 | * Person post type arguments. 325 | * 326 | * @since 2.0 327 | * @param bool $unfiltered Set true to return arguments without being filtered. 328 | * @return array Post type registration arguments. 329 | */ 330 | function ctc_post_type_person_args( $unfiltered = false ) { 331 | 332 | // Arguments 333 | $args = array( 334 | 'labels' => array( 335 | 'name' => esc_html_x( 'People', 'post type general name', 'church-theme-content' ), 336 | 'singular_name' => esc_html_x( 'Person', 'post type singular name', 'church-theme-content' ), 337 | 'add_new' => esc_html_x( 'Add New', 'person', 'church-theme-content' ), 338 | 'add_new_item' => esc_html__( 'Add Person', 'church-theme-content' ), 339 | 'edit_item' => esc_html__( 'Edit Person', 'church-theme-content' ), 340 | 'new_item' => esc_html__( 'New Person', 'church-theme-content' ), 341 | 'all_items' => esc_html__( 'All People', 'church-theme-content' ), 342 | 'view_item' => esc_html__( 'View Person', 'church-theme-content' ), 343 | 'view_items' => esc_html__( 'View People', 'church-theme-content' ), 344 | 'search_items' => esc_html__( 'Search People', 'church-theme-content' ), 345 | 'not_found' => esc_html__( 'No people found', 'church-theme-content' ), 346 | 'not_found_in_trash' => esc_html__( 'No people found in Trash', 'church-theme-content' ) 347 | ), 348 | 'public' => ctc_feature_supported( 'people' ), 349 | 'has_archive' => ctc_feature_supported( 'people' ), 350 | 'rewrite' => array( 351 | 'slug' => 'people', 352 | 'with_front' => false, 353 | 'feeds' => ctc_feature_supported( 'people' ), 354 | ), 355 | 'supports' => array( 'title', 'editor', 'page-attributes', 'thumbnail', 'excerpt' ), 356 | 'taxonomies' => array( 'ctc_person_group' ), 357 | 'menu_icon' => 'dashicons-admin-users', 358 | 'show_in_rest' => true, 359 | ); 360 | 361 | // Filter arguments. 362 | if ( ! $unfiltered ) { 363 | $args = apply_filters( 'ctc_post_type_person_args', $args ); 364 | } 365 | 366 | return $args; 367 | 368 | } 369 | 370 | /** 371 | * Register person post type. 372 | * 373 | * @since 0.9 374 | */ 375 | function ctc_register_post_type_person() { 376 | 377 | // Arguments. 378 | $args = ctc_post_type_person_args(); 379 | 380 | // Registration. 381 | register_post_type( 382 | 'ctc_person', 383 | $args 384 | ); 385 | 386 | } 387 | 388 | add_action( 'init', 'ctc_register_post_type_person' ); // register post type. 389 | 390 | /********************************** 391 | * POST TYPE HELPERS 392 | **********************************/ 393 | 394 | /** 395 | * Get post type label (plural or singular). 396 | * 397 | * This will get the label used when registering post type. 398 | * The value may be the default or what Pro settings provide. 399 | * 400 | * Will return empty if post type not yet registered. 401 | * 402 | * @since 2.0 403 | * @param string $post_type Post type to get label for. 404 | * @param string $form 'singular' or 'plural' (can also leave empty to get plural). 405 | * @return string Singular or plural label for post type. 406 | */ 407 | function ctc_post_type_label( $post_type, $form = false ) { 408 | 409 | // Empty if cannot get name. 410 | $name = ''; 411 | 412 | // Get post type object. 413 | $obj = get_post_type_object( $post_type ); 414 | 415 | // Have object. 416 | if ( ! empty( $obj ) ) { 417 | 418 | // Singular form. 419 | if ( 'singular' === $form && isset( $obj->labels->singular_name ) ) { 420 | $name = $obj->labels->singular_name; 421 | } 422 | 423 | // Plural form. 424 | // If not singular, assume plural. 425 | elseif ( isset( $obj->labels->name ) ) { 426 | $name = $obj->labels->name; 427 | } 428 | 429 | } 430 | 431 | // Return filtered. 432 | return apply_filters( 'ctc_post_type_label', $name ); 433 | 434 | } 435 | -------------------------------------------------------------------------------- /includes/admin/admin-person-fields.php: -------------------------------------------------------------------------------- 1 | 'ctc_person_details', // unique ID 39 | 'title' => _x( 'Person Details', 'meta box', 'church-theme-content' ), 40 | 'post_type' => 'ctc_person', 41 | 'context' => 'normal', // where the meta box appear: normal (left above standard meta boxes), advanced (left below standard boxes), side 42 | 'priority' => 'high', // high, core, default or low (see this: http://www.wproots.com/ultimate-guide-to-meta-boxes-in-wordpress/) 43 | 'callback_args' => array( 44 | '__block_editor_compatible_meta_box' => true, // meta box works in Gutenberg editor. 45 | ), 46 | 47 | // Fields 48 | 'fields' => array( 49 | 50 | // Example 51 | /* 52 | 'option_key' => array( 53 | 'name' => __( 'Field Name', 'church-theme-content' ), 54 | 'after_name' => '', // (Optional), (Required), etc. 55 | 'after_input' => '', // text to show to right of input (fields: text, select, number, range, upload, url, date, time) 56 | 'desc' => __( 'This is the description below the field.', 'church-theme-content' ), 57 | 'type' => 'text', // text, textarea, checkbox, checkbox_multiple, radio, select, number, range, upload, upload_textarea, url, date, time 58 | 'checkbox_label' => '', //show text after checkbox 59 | 'options' => array(), // array of keys/values for radio or select 60 | 'upload_button' => '', // text for button that opens media frame 61 | 'upload_title' => '', // title appearing at top of media frame 62 | 'upload_type' => '', // optional type of media to filter by (image, audio, video, application/pdf) 63 | 'date_multiple' => false, // whether or not to allow date field type to select multiple dates, to be saved as comma-separated list. 64 | 'date_button' => '', // text for button user clicks to open datepicker calendar. 65 | 'default' => '', // value to pre-populate option with (before first save or on reset) 66 | 'no_empty' => false, // if user empties value, force default to be saved instead 67 | 'allow_html' => false, // allow HTML to be used in the value (text, textarea) 68 | 'attributes' => array(), // attr => value array (e.g. set min/max for number or range type) 69 | 'class' => '', // class(es) to add to input (try ctmb-medium, ctmb-small, ctmb-tiny) 70 | 'field_attributes' => array(), // attr => value array for field container 71 | 'field_class' => '', // class(es) to add to field container 72 | 'custom_sanitize' => '', // function to do additional sanitization 73 | 'custom_field' => '', // function for custom display of field input 74 | 'visibility' => array( // show/hide this field based on other fields' values 75 | 'field1' => 'value', // and... 76 | 'field2' => array( 'value', '!=' ), // not having this value 77 | ), 78 | */ 79 | 80 | // Position 81 | '_ctc_person_position' => array( 82 | 'name' => _x( 'Position', 'person meta box', 'church-theme-content' ), 83 | 'after_name' => '', // (Optional), (Required), etc. 84 | 'after_input' => '', // text to show to right of input (fields: text, select, number, range, upload, url, date, time) 85 | 'desc' => __( "Enter the person's position or title (e.g. Senior Pastor, Deacon, etc.)", 'church-theme-content' ), 86 | 'type' => 'text', // text, textarea, checkbox, checkbox_multiple, radio, select, number, range, upload, upload_textarea, url, date, time 87 | 'checkbox_label' => '', //show text after checkbox 88 | 'options' => array(), // array of keys/values for radio or select 89 | 'upload_button' => '', // text for button that opens media frame 90 | 'upload_title' => '', // title appearing at top of media frame 91 | 'upload_type' => '', // optional type of media to filter by (image, audio, video, application/pdf) 92 | 'date_multiple' => false, // whether or not to allow date field type to select multiple dates, to be saved as comma-separated list. 93 | 'date_button' => '', // text for button user clicks to open datepicker calendar. 94 | 'default' => '', // value to pre-populate option with (before first save or on reset) 95 | 'no_empty' => false, // if user empties value, force default to be saved instead 96 | 'allow_html' => false, // allow HTML to be used in the value (text, textarea) 97 | 'attributes' => array(), // attr => value array (e.g. set min/max for number or range type) 98 | 'class' => 'ctmb-medium', // class(es) to add to input (try ctmb-medium, ctmb-small, ctmb-tiny) 99 | 'field_attributes' => array(), // attr => value array for field container 100 | 'field_class' => '', // class(es) to add to field container 101 | 'custom_sanitize' => '', // function to do additional sanitization 102 | 'custom_field' => '', // function for custom display of field input 103 | 'visibility' => array(), // show/hide based on other fields' values: array( array( 'field1' => 'value' ), array( 'field2' => array( 'value', '!=' ) ) 104 | ), 105 | 106 | // Phone 107 | '_ctc_person_phone' => array( 108 | 'name' => __( 'Phone', 'church-theme-content' ), 109 | 'after_name' => '', // (Optional), (Required), etc. 110 | 'after_input' => '', // text to show to right of input (fields: text, select, number, range, upload, url, date, time) 111 | 'desc' => '', 112 | 'type' => 'text', // text, textarea, checkbox, checkbox_multiple, radio, select, number, range, upload, upload_textarea, url, date, time 113 | 'checkbox_label' => '', //show text after checkbox 114 | 'options' => array(), // array of keys/values for radio or select 115 | 'upload_button' => '', // text for button that opens media frame 116 | 'upload_title' => '', // title appearing at top of media frame 117 | 'upload_type' => '', // optional type of media to filter by (image, audio, video, application/pdf) 118 | 'date_multiple' => false, // whether or not to allow date field type to select multiple dates, to be saved as comma-separated list. 119 | 'date_button' => '', // text for button user clicks to open datepicker calendar. 120 | 'default' => '', // value to pre-populate option with (before first save or on reset) 121 | 'no_empty' => false, // if user empties value, force default to be saved instead 122 | 'allow_html' => false, // allow HTML to be used in the value (text, textarea) 123 | 'attributes' => array(), // attr => value array (e.g. set min/max for number or range type) 124 | 'class' => 'ctmb-medium', // class(es) to add to input (try ctmb-medium, ctmb-small, ctmb-tiny) 125 | 'field_attributes' => array(), // attr => value array for field container 126 | 'field_class' => '', // class(es) to add to field container 127 | 'custom_sanitize' => '', // function to do additional sanitization 128 | 'custom_field' => '', // function for custom display of field input 129 | 'visibility' => array(), // show/hide based on other fields' values: array( array( 'field1' => 'value' ), array( 'field2' => array( 'value', '!=' ) ) 130 | ), 131 | 132 | // Email 133 | '_ctc_person_email' => array( 134 | 'name' => __( 'Email', 'church-theme-content' ), 135 | 'after_name' => '', // (Optional), (Required), etc. 136 | 'after_input' => '', // text to show to right of input (fields: text, select, number, range, upload, url, date, time) 137 | 'desc' => '', 138 | 'type' => 'text', // text, textarea, checkbox, checkbox_multiple, radio, select, number, range, upload, upload_textarea, url, date, time 139 | 'checkbox_label' => '', //show text after checkbox 140 | 'options' => array(), // array of keys/values for radio or select 141 | 'upload_button' => '', // text for button that opens media frame 142 | 'upload_title' => '', // title appearing at top of media frame 143 | 'upload_type' => '', // optional type of media to filter by (image, audio, video, application/pdf) 144 | 'date_multiple' => false, // whether or not to allow date field type to select multiple dates, to be saved as comma-separated list. 145 | 'date_button' => '', // text for button user clicks to open datepicker calendar. 146 | 'default' => '', // value to pre-populate option with (before first save or on reset) 147 | 'no_empty' => false, // if user empties value, force default to be saved instead 148 | 'allow_html' => false, // allow HTML to be used in the value (text, textarea) 149 | 'attributes' => array(), // attr => value array (e.g. set min/max for number or range type) 150 | 'class' => 'ctmb-medium', // class(es) to add to input (try ctmb-medium, ctmb-small, ctmb-tiny) 151 | 'field_attributes' => array(), // attr => value array for field container 152 | 'field_class' => '', // class(es) to add to field container 153 | 'custom_sanitize' => 'sanitize_email', // function to do additional sanitization 154 | 'custom_field' => '', // function for custom display of field input 155 | 'visibility' => array(), // show/hide based on other fields' values: array( array( 'field1' => 'value' ), array( 'field2' => array( 'value', '!=' ) ) 156 | ), 157 | 158 | // URLs 159 | '_ctc_person_urls' => array( 160 | 'name' => __( 'URLs', 'church-theme-content' ), 161 | 'after_name' => '', // (Optional), (Required), etc. 162 | 'after_input' => '', // text to show to right of input (fields: text, select, number, range, upload, url, date, time) 163 | 'desc' => '', 164 | 'type' => 'textarea', // text, textarea, checkbox, checkbox_multiple, radio, select, number, range, upload, upload_textarea, url, date, time 165 | 'checkbox_label' => '', //show text after checkbox 166 | 'options' => array(), // array of keys/values for radio or select 167 | 'upload_button' => '', // text for button that opens media frame 168 | 'upload_title' => '', // title appearing at top of media frame 169 | 'upload_type' => '', // optional type of media to filter by (image, audio, video, application/pdf) 170 | 'date_multiple' => false, // whether or not to allow date field type to select multiple dates, to be saved as comma-separated list. 171 | 'date_button' => '', // text for button user clicks to open datepicker calendar. 172 | 'default' => '', // value to pre-populate option with (before first save or on reset) 173 | 'no_empty' => false, // if user empties value, force default to be saved instead 174 | 'allow_html' => false, // allow HTML to be used in the value (text, textarea) 175 | 'attributes' => array(), // attr => value array (e.g. set min/max for number or range type) 176 | 'class' => '', // class(es) to add to input (try ctmb-medium, ctmb-small, ctmb-tiny) 177 | 'field_attributes' => array(), // attr => value array for field container 178 | 'field_class' => '', // class(es) to add to field container 179 | 'custom_sanitize' => '', // function to do additional sanitization 180 | 'custom_field' => '', // function for custom display of field input 181 | 'visibility' => array(), // show/hide based on other fields' values: array( array( 'field1' => 'value' ), array( 'field2' => array( 'value', '!=' ) ) 182 | ), 183 | 184 | ), 185 | 186 | ); 187 | 188 | // Add Meta Box 189 | new CT_Meta_Box( $meta_box ); 190 | 191 | } 192 | 193 | add_action( 'admin_init', 'ctc_add_meta_box_person_details' ); 194 | 195 | /********************************** 196 | * ADMIN COLUMNS 197 | **********************************/ 198 | 199 | /** 200 | * Add/remove list columns 201 | * 202 | * @since 0.9 203 | * @param array $columns Columns to manipulate 204 | * @return array Modified columns 205 | */ 206 | function ctc_person_columns( $columns ) { 207 | 208 | // insert thumbnail after checkbox (before title) 209 | $insert_array = array(); 210 | $insert_array['ctc_person_thumbnail'] = esc_html__( 'Thumbnail', 'church-theme-content' ); 211 | $columns = ctc_array_merge_after_key( $columns, $insert_array, 'cb' ); 212 | 213 | // insert columns after title 214 | $insert_array = array(); 215 | if ( ctc_field_supported( 'people', '_ctc_person_position' ) ) $insert_array['ctc_person_position'] = esc_html__( 'Position', 'church-theme-content' ); 216 | if ( ctc_taxonomy_supported( 'people', 'ctc_person_group' ) ) $insert_array['ctc_person_group'] = esc_html_x( 'Groups', 'people column', 'church-theme-content' ); 217 | $insert_array['ctc_person_order'] = esc_html_x( 'Order', 'sorting', 'church-theme-content' ); 218 | $columns = ctc_array_merge_after_key( $columns, $insert_array, 'title' ); 219 | 220 | //change "title" to "name" 221 | $columns['title'] = esc_html_x( 'Name', 'person', 'church-theme-content' ); 222 | 223 | return $columns; 224 | 225 | } 226 | 227 | add_filter( 'manage_ctc_person_posts_columns' , 'ctc_person_columns' ); // add columns 228 | 229 | /** 230 | * Change person list column content 231 | * 232 | * @since 0.9 233 | * @param string $column Column being worked on 234 | */ 235 | function ctc_person_columns_content( $column ) { 236 | 237 | global $post; 238 | 239 | switch ( $column ) { 240 | 241 | // Thumbnail 242 | case 'ctc_person_thumbnail' : 243 | 244 | if ( has_post_thumbnail() ) { 245 | echo '' . get_the_post_thumbnail( $post->ID, array( 60, 60 ) ) . ''; 246 | } 247 | 248 | break; 249 | 250 | // Position 251 | case 'ctc_person_position' : 252 | 253 | echo strip_tags( get_post_meta( $post->ID , '_ctc_person_position' , true ) ); 254 | 255 | break; 256 | 257 | // Group 258 | case 'ctc_person_group' : 259 | 260 | echo ctc_admin_term_list( $post->ID, 'ctc_person_group' ); 261 | 262 | break; 263 | 264 | // Order 265 | case 'ctc_person_order' : 266 | 267 | echo isset( $post->menu_order ) ? $post->menu_order : ''; 268 | 269 | break; 270 | 271 | } 272 | 273 | } 274 | 275 | add_action( 'manage_posts_custom_column' , 'ctc_person_columns_content' ); // add content for columns 276 | 277 | /** 278 | * Enable sorting for new columns 279 | * 280 | * @since 0.9 281 | * @param array $columns Columns being worked on 282 | * @return array Modified columns 283 | */ 284 | function ctc_person_columns_sorting( $columns ) { 285 | 286 | $columns['ctc_person_position'] = '_ctc_person_position'; 287 | $columns['ctc_person_order'] = 'menu_order'; 288 | 289 | return $columns; 290 | 291 | } 292 | 293 | add_filter( 'manage_edit-ctc_person_sortable_columns', 'ctc_person_columns_sorting' ); // make columns sortable 294 | 295 | /** 296 | * Set how to sort columns (default sorting, custom fields) 297 | * 298 | * @since 0.9 299 | * @param array $args Sorting arguments 300 | * @return array Modified arguments 301 | */ 302 | function ctc_person_columns_sorting_request( $args ) { 303 | 304 | // admin area only 305 | if ( is_admin() ) { 306 | 307 | // Don't run if something causing filter to run when would not normally. 308 | if ( ! function_exists( 'get_current_screen' ) ) { 309 | return; 310 | } 311 | 312 | $screen = get_current_screen(); 313 | 314 | // only on this post type's list 315 | if ( 'ctc_person' == $screen->post_type && 'edit' == $screen->base ) { 316 | 317 | // orderby has been set, tell how to order 318 | if ( isset( $args['orderby'] ) ) { 319 | 320 | switch ( $args['orderby'] ) { 321 | 322 | // Under Name 323 | case '_ctc_person_position' : 324 | 325 | $args['meta_key'] = '_ctc_person_position'; 326 | $args['orderby'] = 'meta_value'; // alphabetically (meta_value_num for numeric) 327 | 328 | break; 329 | 330 | } 331 | 332 | } 333 | 334 | // orderby not set, tell which column to sort by default 335 | else { 336 | $args['orderby'] = 'menu_order'; // sort by Order column by default 337 | $args['order'] = 'ASC'; 338 | } 339 | 340 | } 341 | 342 | } 343 | 344 | return $args; 345 | 346 | } 347 | 348 | add_filter( 'request', 'ctc_person_columns_sorting_request' ); // set how to sort columns 349 | -------------------------------------------------------------------------------- /includes/classes/CTC_EDD_SL_Plugin_Updater.php: -------------------------------------------------------------------------------- 1 | api_url = trailingslashit($_api_url); 52 | $this->api_data = $_api_data; 53 | $this->name = plugin_basename($_plugin_file); 54 | $this->slug = basename($_plugin_file, '.php'); 55 | $this->version = $_api_data['version']; 56 | $this->wp_override = isset($_api_data['wp_override']) ? (bool) $_api_data['wp_override'] : false; 57 | $this->beta = ! empty($this->api_data['beta']) ? true : false; 58 | $this->cache_key = md5(serialize($this->slug . $this->api_data['license'] . $this->beta)); 59 | 60 | $edd_plugin_data[$this->slug] = $this->api_data; 61 | 62 | // Set up hooks. 63 | $this->init(); 64 | } 65 | 66 | /** 67 | * Set up WordPress filters to hook into WP's update process. 68 | * 69 | * @uses add_filter() 70 | * 71 | * @return void 72 | */ 73 | public function init() 74 | { 75 | 76 | add_filter('pre_set_site_transient_update_plugins', array($this, 'check_update')); 77 | add_filter('plugins_api', array($this, 'plugins_api_filter'), 10, 3); 78 | remove_action('after_plugin_row_' . $this->name, 'wp_plugin_update_row', 10); 79 | add_action('after_plugin_row_' . $this->name, array($this, 'show_update_notification'), 10, 2); 80 | add_action('admin_init', array($this, 'show_changelog')); 81 | } 82 | 83 | /** 84 | * Check for Updates at the defined API endpoint and modify the update array. 85 | * 86 | * This function dives into the update API just when WordPress creates its update array, 87 | * then adds a custom API call and injects the custom plugin data retrieved from the API. 88 | * It is reassembled from parts of the native WordPress plugin update code. 89 | * See wp-includes/update.php line 121 for the original wp_update_plugins() function. 90 | * 91 | * @uses api_request() 92 | * 93 | * @param array $_transient_data Update array build by WordPress. 94 | * @return array Modified update array with custom plugin data. 95 | */ 96 | public function check_update($_transient_data) 97 | { 98 | 99 | global $pagenow; 100 | 101 | if (! is_object($_transient_data)) { 102 | $_transient_data = new stdClass; 103 | } 104 | 105 | if ('plugins.php' == $pagenow && is_multisite()) { 106 | return $_transient_data; 107 | } 108 | 109 | if (! empty($_transient_data->response) && ! empty($_transient_data->response[$this->name]) && false === $this->wp_override) { 110 | return $_transient_data; 111 | } 112 | 113 | $version_info = $this->get_cached_version_info(); 114 | 115 | if (false === $version_info) { 116 | $version_info = $this->api_request('plugin_latest_version', array('slug' => $this->slug, 'beta' => $this->beta)); 117 | 118 | $this->set_version_info_cache($version_info); 119 | } 120 | 121 | if (false !== $version_info && is_object($version_info) && isset($version_info->new_version)) { 122 | 123 | if (version_compare($this->version, $version_info->new_version, '<')) { 124 | 125 | $_transient_data->response[$this->name] = $version_info; 126 | } 127 | 128 | $_transient_data->last_checked = current_time('timestamp'); 129 | $_transient_data->checked[$this->name] = $this->version; 130 | } 131 | 132 | return $_transient_data; 133 | } 134 | 135 | /** 136 | * show update notification row -- needed for multisite subsites, because WP won't tell you otherwise! 137 | * 138 | * @param string $file 139 | * @param array $plugin 140 | */ 141 | public function show_update_notification($file, $plugin) 142 | { 143 | 144 | if (is_network_admin()) { 145 | return; 146 | } 147 | 148 | if (! current_user_can('update_plugins')) { 149 | return; 150 | } 151 | 152 | if (! is_multisite()) { 153 | return; 154 | } 155 | 156 | if ($this->name != $file) { 157 | return; 158 | } 159 | 160 | // Remove our filter on the site transient 161 | remove_filter('pre_set_site_transient_update_plugins', array($this, 'check_update'), 10); 162 | 163 | $update_cache = get_site_transient('update_plugins'); 164 | 165 | $update_cache = is_object($update_cache) ? $update_cache : new stdClass(); 166 | 167 | if (empty($update_cache->response) || empty($update_cache->response[$this->name])) { 168 | 169 | $version_info = $this->get_cached_version_info(); 170 | 171 | if (false === $version_info) { 172 | $version_info = $this->api_request('plugin_latest_version', array('slug' => $this->slug, 'beta' => $this->beta)); 173 | 174 | $this->set_version_info_cache($version_info); 175 | } 176 | 177 | if (! is_object($version_info)) { 178 | return; 179 | } 180 | 181 | if (version_compare($this->version, $version_info->new_version, '<')) { 182 | 183 | $update_cache->response[$this->name] = $version_info; 184 | } 185 | 186 | $update_cache->last_checked = current_time('timestamp'); 187 | $update_cache->checked[$this->name] = $this->version; 188 | 189 | set_site_transient('update_plugins', $update_cache); 190 | } else { 191 | 192 | $version_info = $update_cache->response[$this->name]; 193 | } 194 | 195 | // Restore our filter 196 | add_filter('pre_set_site_transient_update_plugins', array($this, 'check_update')); 197 | 198 | if (! empty($update_cache->response[$this->name]) && version_compare($this->version, $version_info->new_version, '<')) { 199 | 200 | // build a plugin list row, with update notification 201 | $wp_list_table = _get_list_table('WP_Plugins_List_Table'); 202 | # 203 | echo ''; 204 | echo ''; 205 | echo '
'; 206 | 207 | $changelog_link = self_admin_url('index.php?edd_sl_action=view_plugin_changelog&plugin=' . $this->name . '&slug=' . $this->slug . '&TB_iframe=true&width=772&height=911'); 208 | 209 | // CT Mod 210 | // Use external changelog URL instead of post-provided changelog embedded in modal 211 | $ctc_changelog_url = ctc_get_add_on($this->slug, 'changelog_url'); 212 | $ctc_changelog_link_class = 'thickbox'; 213 | if ($ctc_changelog_url) { 214 | $changelog_link = $ctc_changelog_url; 215 | $ctc_changelog_link_class = ''; 216 | } 217 | 218 | // CT Mod 219 | // 1. Added CTC textdomain 220 | // 2. Added class replacement. If $changelog_link provided, class is none; otherwise class for modal is used 221 | if (empty($version_info->download_link)) { 222 | printf( 223 | wp_kses( // CTC Mod - escape translation string 224 | __('There is a new version of %1$s available. View version %3$s details.', 'church-theme-content'), // CTC Mod textdomain 225 | array( 226 | 'a' => array( 227 | 'href' => array(), 228 | 'target' => array(), 229 | 'class' => array(), 230 | ) 231 | ) 232 | ), 233 | esc_html($version_info->name), 234 | esc_url($changelog_link), 235 | esc_html($version_info->new_version), 236 | $ctc_changelog_link_class 237 | ); 238 | } else { 239 | printf( 240 | wp_kses( // CTC Mod - escape translation string 241 | __('There is a new version of %1$s available. View version %3$s details or update now.', 'church-theme-content'), // CTC Mod textdomain 242 | array( 243 | 'a' => array( 244 | 'href' => array(), 245 | 'target' => array(), 246 | 'class' => array(), 247 | ) 248 | ) 249 | ), 250 | esc_html($version_info->name), 251 | esc_url($changelog_link), 252 | esc_html($version_info->new_version), 253 | esc_url(wp_nonce_url(self_admin_url('update.php?action=upgrade-plugin&plugin=') . $this->name, 'upgrade-plugin_' . $this->name)), 254 | $ctc_changelog_link_class 255 | ); 256 | } 257 | 258 | do_action("in_plugin_update_message-{$file}", $plugin, $version_info); 259 | 260 | echo '
'; 261 | } 262 | } 263 | 264 | /** 265 | * Updates information on the "View version x.x details" page with custom data. 266 | * 267 | * @uses api_request() 268 | * 269 | * @param mixed $_data 270 | * @param string $_action 271 | * @param object $_args 272 | * @return object $_data 273 | */ 274 | public function plugins_api_filter($_data, $_action = '', $_args = null) 275 | { 276 | 277 | if ($_action != 'plugin_information') { 278 | 279 | return $_data; 280 | } 281 | 282 | if (! isset($_args->slug) || ($_args->slug != $this->slug)) { 283 | 284 | return $_data; 285 | } 286 | 287 | $to_send = array( 288 | 'slug' => $this->slug, 289 | 'is_ssl' => is_ssl(), 290 | 'fields' => array( 291 | 'banners' => array(), 292 | 'reviews' => false 293 | ) 294 | ); 295 | 296 | $cache_key = 'edd_api_request_' . md5(serialize($this->slug . $this->api_data['license'] . $this->beta)); 297 | 298 | // Get the transient where we store the api request for this plugin for 24 hours 299 | $edd_api_request_transient = $this->get_cached_version_info($cache_key); 300 | 301 | //If we have no transient-saved value, run the API, set a fresh transient with the API value, and return that value too right now. 302 | if (empty($edd_api_request_transient)) { 303 | 304 | $api_response = $this->api_request('plugin_information', $to_send); 305 | 306 | // Expires in 3 hours 307 | $this->set_version_info_cache($api_response, $cache_key); 308 | 309 | if (false !== $api_response) { 310 | $_data = $api_response; 311 | } 312 | } else { 313 | $_data = $edd_api_request_transient; 314 | } 315 | 316 | // Convert sections into an associative array, since we're getting an object, but Core expects an array. 317 | if (isset($_data->sections) && ! is_array($_data->sections)) { 318 | $new_sections = array(); 319 | foreach ($_data->sections as $key => $value) { 320 | $new_sections[$key] = $value; 321 | } 322 | 323 | $_data->sections = $new_sections; 324 | } 325 | 326 | // Convert banners into an associative array, since we're getting an object, but Core expects an array. 327 | if (isset($_data->banners) && ! is_array($_data->banners)) { 328 | $new_banners = array(); 329 | foreach ($_data->banners as $key => $value) { 330 | $new_banners[$key] = $value; 331 | } 332 | 333 | $_data->banners = $new_banners; 334 | } 335 | 336 | return $_data; 337 | } 338 | 339 | /** 340 | * Disable SSL verification in order to prevent download update failures 341 | * 342 | * @param array $args 343 | * @param string $url 344 | * @return object $array 345 | */ 346 | public function http_request_args($args, $url) 347 | { 348 | 349 | $verify_ssl = $this->verify_ssl(); 350 | if (strpos($url, 'https://') !== false && strpos($url, 'edd_action=package_download')) { 351 | $args['sslverify'] = $verify_ssl; 352 | } 353 | return $args; 354 | } 355 | 356 | /** 357 | * Calls the API and, if successful, returns the object delivered by the API. 358 | * 359 | * @uses get_bloginfo() 360 | * @uses wp_remote_post() 361 | * @uses is_wp_error() 362 | * 363 | * @param string $_action The requested action. 364 | * @param array $_data Parameters for the API action. 365 | * @return false|object 366 | */ 367 | private function api_request($_action, $_data) 368 | { 369 | 370 | global $wp_version; 371 | 372 | $data = array_merge($this->api_data, $_data); 373 | 374 | if ($data['slug'] != $this->slug) { 375 | return; 376 | } 377 | 378 | if ($this->api_url == trailingslashit(home_url())) { 379 | return false; // Don't allow a plugin to ping itself 380 | } 381 | 382 | $request = wp_remote_post($this->api_url, [ 383 | 'body' => [ 384 | 'edd_action' => 'get_version', 385 | 'license' => ! empty($data['license']) ? $data['license'] : '', 386 | 'item_name' => isset($data['item_name']) ? $data['item_name'] : false, 387 | 'item_id' => isset($data['item_id']) ? $data['item_id'] : false, 388 | 'version' => isset($data['version']) ? $data['version'] : false, 389 | 'slug' => $data['slug'], 390 | 'author' => $data['author'], 391 | 'url' => home_url(), 392 | 'beta' => ! empty($data['beta']), 393 | 'no_cache' => md5(microtime(true)), 394 | ], 395 | 'timeout' => 15, 396 | 'sslverify' => $this->verify_ssl(), 397 | ]); 398 | 399 | if (! is_wp_error($request)) { 400 | $request = json_decode(wp_remote_retrieve_body($request)); 401 | } 402 | 403 | if ($request && isset($request->sections)) { 404 | $request->sections = maybe_unserialize($request->sections); 405 | } else { 406 | $request = false; 407 | } 408 | 409 | if ($request && isset($request->banners)) { 410 | $request->banners = maybe_unserialize($request->banners); 411 | } 412 | 413 | if (! empty($request->sections)) { 414 | foreach ($request->sections as $key => $section) { 415 | $request->$key = (array) $section; 416 | } 417 | } 418 | 419 | return $request; 420 | } 421 | 422 | public function show_changelog() 423 | { 424 | 425 | global $edd_plugin_data; 426 | 427 | if (empty($_REQUEST['edd_sl_action']) || 'view_plugin_changelog' != $_REQUEST['edd_sl_action']) { 428 | return; 429 | } 430 | 431 | if (empty($_REQUEST['plugin'])) { 432 | return; 433 | } 434 | 435 | if (empty($_REQUEST['slug'])) { 436 | return; 437 | } 438 | 439 | if (! current_user_can('update_plugins')) { 440 | wp_die(__('You do not have permission to install plugin updates', 'church-theme-content'), __('Error', 'church-theme-content'), array('response' => 403)); 441 | } 442 | 443 | $data = $edd_plugin_data[$_REQUEST['slug']]; 444 | $beta = ! empty($data['beta']) ? true : false; 445 | $cache_key = md5('edd_plugin_' . sanitize_key($_REQUEST['plugin']) . '_' . $beta . '_version_info'); 446 | $version_info = $this->get_cached_version_info($cache_key); 447 | 448 | if (false === $version_info) { 449 | 450 | $request = wp_remote_post($this->api_url, [ 451 | 'body' => [ 452 | 'edd_action' => 'get_version', 453 | 'item_name' => isset($data['item_name']) ? $data['item_name'] : false, 454 | 'item_id' => isset($data['item_id']) ? $data['item_id'] : false, 455 | 'slug' => $_REQUEST['slug'], 456 | 'author' => $data['author'], 457 | 'url' => home_url(), 458 | 'beta' => ! empty($data['beta']), 459 | 'no_cache' => md5(microtime(true)), 460 | ], 461 | 'timeout' => 15, 462 | 'sslverify' => $this->verify_ssl(), 463 | ]); 464 | 465 | if (! is_wp_error($request)) { 466 | $version_info = json_decode(wp_remote_retrieve_body($request)); 467 | } 468 | 469 | 470 | if (! empty($version_info) && isset($version_info->sections)) { 471 | $version_info->sections = maybe_unserialize($version_info->sections); 472 | } else { 473 | $version_info = false; 474 | } 475 | 476 | if (! empty($version_info)) { 477 | foreach ($version_info->sections as $key => $section) { 478 | $version_info->$key = (array) $section; 479 | } 480 | } 481 | 482 | $this->set_version_info_cache($version_info, $cache_key); 483 | } 484 | 485 | if (! empty($version_info) && isset($version_info->sections['changelog'])) { 486 | echo '
' . $version_info->sections['changelog'] . '
'; 487 | } 488 | 489 | exit; 490 | } 491 | 492 | public function get_cached_version_info($cache_key = '') 493 | { 494 | 495 | if (empty($cache_key)) { 496 | $cache_key = $this->cache_key; 497 | } 498 | 499 | $cache = get_option($cache_key); 500 | 501 | if (empty($cache['timeout']) || current_time('timestamp') > $cache['timeout']) { 502 | return false; // Cache is expired 503 | } 504 | 505 | return json_decode($cache['value']); 506 | } 507 | 508 | public function set_version_info_cache($value = '', $cache_key = '') 509 | { 510 | 511 | if (empty($cache_key)) { 512 | $cache_key = $this->cache_key; 513 | } 514 | 515 | $data = array( 516 | 'timeout' => strtotime('+3 hours', current_time('timestamp')), 517 | 'value' => json_encode($value) 518 | ); 519 | 520 | update_option($cache_key, $data, 'no'); 521 | } 522 | 523 | /** 524 | * Returns if the SSL of the store should be verified. 525 | * 526 | * @since 1.6.13 527 | * @return bool 528 | */ 529 | private function verify_ssl() 530 | { 531 | return (bool) apply_filters('edd_sl_api_request_verify_ssl', true, $this); // could be made false for local self-signed SSL 532 | } 533 | } 534 | --------------------------------------------------------------------------------