+ echo '
';
}
diff --git a/includes/class-ccb-core-settings-page.php b/includes/class-ccb-core-settings-page.php
index 3d7c01b..a3994ad 100644
--- a/includes/class-ccb-core-settings-page.php
+++ b/includes/class-ccb-core-settings-page.php
@@ -57,6 +57,7 @@ public function render_page() {
page_id ); ?>
page_id ); ?>
+
page_id ) {
diff --git a/includes/class-ccb-core-settings.php b/includes/class-ccb-core-settings.php
index af455a2..27e1b9b 100644
--- a/includes/class-ccb-core-settings.php
+++ b/includes/class-ccb-core-settings.php
@@ -142,7 +142,9 @@ public function validate_settings( $input ) {
* @return array
*/
public function get_settings_definitions() {
- return array(
+
+ // Initialize a settings array with an About page and Credentials page.
+ $settings = [
'ccb_core_settings' => array(
'page_title' => esc_html__( 'About', 'ccb-core' ),
'sections' => array(
@@ -163,13 +165,13 @@ public function get_settings_definitions() {
'field_render_function' => 'render_text',
'field_placeholder' => 'subdomain',
'field_validation' => 'alphanumeric',
- 'field_tooltip' => 'We just need the first part of your software URL (
without "http://" and
without ".ccbchurch.com").',
+ 'field_tooltip' => esc_html__( 'We just need the first part of your software URL (without "http://" and without ".ccbchurch.com").', 'ccb-core' ),
),
'credentials' => array(
'field_title' => esc_html__( 'API Credentials', 'ccb-core' ),
'field_render_function' => 'render_credentials',
'field_validation' => 'encrypt',
- 'field_tooltip' => 'This is the username and password for the API user in your Church Community Builder software.',
+ 'field_tooltip' => esc_html__( 'This is the username and password for the API user in your Church Community Builder software.', 'ccb-core' ),
),
'test_credentials' => array(
'field_title' => esc_html__( 'Test Credentials', 'ccb-core' ),
@@ -179,279 +181,89 @@ public function get_settings_definitions() {
),
),
),
- 'ccb_core_settings_groups' => array(
- 'page_title' => esc_html__( 'Groups', 'ccb-core' ),
- 'sections' => array(
- 'groups' => array(
- 'section_title' => esc_html__( 'Groups', 'ccb-core' ),
- 'fields' => array(
- 'groups-enabled' => array(
- 'field_title' => esc_html__( 'Enable Groups', 'ccb-core' ),
- 'field_render_function' => 'render_switch',
- 'field_validation' => 'switch',
- ),
- 'groups-name' => array(
- 'field_title' => esc_html__( 'Groups Display Name', 'ccb-core' ),
- 'field_render_function' => 'render_text',
- 'field_placeholder' => esc_html__( 'Groups', 'ccb-core' ),
- 'field_validation' => 'alphanumeric_extended',
- 'field_attributes' => array( 'data-requires' => '{"groups-enabled":1}' ),
- 'field_tooltip' => 'This is what you call the groups in your church (i.e.
Home Groups, Connections, Life Groups, etc.).',
- ),
- 'groups-slug' => array(
- 'field_title' => esc_html__( 'Groups URL Name', 'ccb-core' ),
- 'field_render_function' => 'render_text',
- 'field_placeholder' => 'groups',
- 'field_validation' => 'slug',
- 'field_attributes' => array( 'data-requires' => '{"groups-enabled":1}' ),
- 'field_tooltip' => 'This is typically where your theme will display
all the groups. WordPress calls this a "slug".',
- ),
- 'groups-import-images' => array(
- 'field_title' => esc_html__( 'Also Import Group Images?', 'ccb-core' ),
- 'field_render_function' => 'render_radio',
- 'field_options' => array(
- 'yes' => esc_html__( 'Yes', 'ccb-core' ),
- 'no' => esc_html__( 'No', 'ccb-core' ),
- ),
- 'field_validation' => '',
- 'field_default' => 'no',
- 'field_attributes' => array( 'data-requires' => '{"groups-enabled":1}' ),
- 'field_tooltip' => 'This will download the CCB Group Image and attach it as a Featured Image.
If you don\'t need group images, then disabling this feature will speed up the synchronization.',
- ),
- 'groups-advanced' => array(
- 'field_title' => esc_html__( 'Enable Advanced Settings (Optional)', 'ccb-core' ),
- 'field_render_function' => 'render_switch',
- 'field_validation' => 'switch',
- 'field_attributes' => array( 'data-requires' => '{"groups-enabled":1}' ),
- ),
- 'groups-exclude-from-search' => array(
- 'field_title' => esc_html__( 'Exclude From Search?', 'ccb-core' ),
- 'field_render_function' => 'render_radio',
- 'field_options' => array(
- 'yes' => esc_html__( 'Yes', 'ccb-core' ),
- 'no' => esc_html__( 'No', 'ccb-core' ),
- ),
- 'field_validation' => '',
- 'field_default' => 'no',
- 'field_attributes' => array( 'data-requires' => '{"groups-enabled":1,"groups-advanced":1}' ),
- ),
- 'groups-publicly-queryable' => array(
- 'field_title' => esc_html__( 'Publicly Queryable?', 'ccb-core' ),
- 'field_render_function' => 'render_radio',
- 'field_options' => array(
- 'yes' => esc_html__( 'Yes', 'ccb-core' ),
- 'no' => esc_html__( 'No', 'ccb-core' ),
- ),
- 'field_validation' => '',
- 'field_default' => 'yes',
- 'field_attributes' => array( 'data-requires' => '{"groups-enabled":1,"groups-advanced":1}' ),
- ),
- 'groups-show-ui' => array(
- 'field_title' => esc_html__( 'Show In Admin UI?', 'ccb-core' ),
- 'field_render_function' => 'render_radio',
- 'field_options' => array(
- 'yes' => esc_html__( 'Yes', 'ccb-core' ),
- 'no' => esc_html__( 'No', 'ccb-core' ),
- ),
- 'field_validation' => '',
- 'field_default' => 'yes',
- 'field_attributes' => array( 'data-requires' => '{"groups-enabled":1,"groups-advanced":1}' ),
- ),
- 'groups-show-in-nav-menus' => array(
- 'field_title' => esc_html__( 'Show In Navigation Menus?', 'ccb-core' ),
- 'field_render_function' => 'render_radio',
- 'field_options' => array(
- 'yes' => esc_html__( 'Yes', 'ccb-core' ),
- 'no' => esc_html__( 'No', 'ccb-core' ),
- ),
- 'field_validation' => '',
- 'field_default' => 'no',
- 'field_attributes' => array( 'data-requires' => '{"groups-enabled":1,"groups-advanced":1}' ),
- ),
+ ];
+
+ /**
+ * Allow custom post types to have their own settings pages.
+ *
+ * If you implement a custom post type and do not want to
+ * expose a settings page, simply `return $settings` from
+ * within your implementation of `get_post_settings_definitions()`
+ *
+ * @since 1.0.0
+ *
+ * @param array $settings The current array of settings definitions.
+ */
+ $settings = apply_filters( 'ccb_core_post_type_settings_definitions', $settings );
+
+ // Add a syncronization settings page.
+ $settings['ccb_core_settings_sync'] = array(
+ 'page_title' => esc_html__( 'Synchronize', 'ccb-core' ),
+ 'sections' => array(
+ 'synchronize' => array(
+ 'section_title' => esc_html__( 'Synchronize', 'ccb-core' ),
+ 'fields' => array(
+ 'auto_sync' => array(
+ 'field_title' => esc_html__( 'Enable Auto Sync', 'ccb-core' ),
+ 'field_render_function' => 'render_switch',
+ 'field_validation' => 'switch',
),
- ),
- ),
- ),
- 'ccb_core_settings_calendar' => array(
- 'page_title' => esc_html__( 'Public Events', 'ccb-core' ),
- 'sections' => array(
- 'calendar' => array(
- 'section_title' => esc_html__( 'Public Events', 'ccb-core' ),
- 'fields' => array(
- 'calendar-enabled' => array(
- 'field_title' => esc_html__( 'Enable Events', 'ccb-core' ),
- 'field_render_function' => 'render_switch',
- 'field_validation' => 'switch',
+ 'auto_sync_timeout' => array(
+ 'field_title' => esc_html__( 'Cache Expiration', 'ccb-core' ),
+ 'field_render_function' => 'render_slider',
+ 'field_options' => array(
+ 'min' => '10',
+ 'max' => '180',
+ 'units' => 'minutes',
),
- 'calendar-name' => array(
- 'field_title' => esc_html__( 'Event Display Name', 'ccb-core' ),
- 'field_render_function' => 'render_text',
- 'field_placeholder' => esc_html__( 'Events', 'ccb-core' ),
- 'field_validation' => 'alphanumeric_extended',
- 'field_attributes' => array( 'data-requires' => '{"calendar-enabled":1}' ),
- 'field_tooltip' => 'This is what you call the events in your church (i.e.
Meetups, Hangouts, etc.).',
- ),
- 'calendar-slug' => array(
- 'field_title' => esc_html__( 'Events URL Name', 'ccb-core' ),
- 'field_render_function' => 'render_text',
- 'field_placeholder' => 'events',
- 'field_validation' => 'slug',
- 'field_attributes' => array( 'data-requires' => '{"calendar-enabled":1}' ),
- 'field_tooltip' => 'This is typically where your theme will display
all the events. WordPress calls this a "slug".',
- ),
- 'calendar-advanced' => array(
- 'field_title' => esc_html__( 'Enable Advanced Settings (Optional)', 'ccb-core' ),
- 'field_render_function' => 'render_switch',
- 'field_validation' => 'switch',
- 'field_attributes' => array( 'data-requires' => '{"calendar-enabled":1}' ),
- ),
- 'calendar-date-range-type' => array(
- 'field_title' => esc_html__( 'Date Range Type', 'ccb-core' ),
- 'field_render_function' => 'render_radio',
- 'field_options' => array(
- 'relative' => esc_html__( 'Relative Range', 'ccb-core' ),
- 'specific' => esc_html__( 'Specific Range', 'ccb-core' ),
- ),
- 'field_validation' => '',
- 'field_default' => 'relative',
- 'field_attributes' => array( 'class' => 'date-range-type', 'data-requires' => '{"calendar-enabled":1,"calendar-advanced":1}' ),
- 'field_tooltip' => '
Relative: For example, always get the events from
\'One week ago\', up to
\'Eight weeks from now\'.
This is the best setting for most churches.
Specific: For example, only get events from
\'6/1/2015\' to
\'12/1/2015\'.
This setting is best if you want to tightly manage the events that get published.',
- ),
- 'calendar-relative-weeks-past' => array(
- 'field_title' => esc_html__( 'How Far Back?', 'ccb-core' ),
- 'field_render_function' => 'render_slider',
- 'field_options' => array(
- 'min' => '0',
- 'max' => '26',
- 'units' => 'weeks',
- ),
- 'field_default' => 1,
- 'field_validation' => '',
- 'field_attributes' => array( 'data-requires' => '{"calendar-enabled":1,"calendar-advanced":1,"calendar-date-range-type":"relative"}' ),
- 'field_tooltip' => 'Every time we synchronize, how many
weeks in the past should we look?
(0 would be "today")',
- ),
- 'calendar-relative-weeks-future' => array(
- 'field_title' => esc_html__( 'How Into The Future?', 'ccb-core' ),
- 'field_render_function' => 'render_slider',
- 'field_options' => array(
- 'min' => '1',
- 'max' => '52',
- 'units' => 'weeks',
- ),
- 'field_default' => 16,
- 'field_validation' => '',
- 'field_attributes' => array( 'data-requires' => '{"calendar-enabled":1,"calendar-advanced":1,"calendar-date-range-type":"relative"}' ),
- 'field_tooltip' => 'Every time we synchronize, how many
weeks in the future should we look?',
- ),
- 'calendar-specific-start' => array(
- 'field_title' => esc_html__( 'Specific Start Date', 'ccb-core' ),
- 'field_render_function' => 'render_date_picker',
- 'field_validation' => '',
- 'field_attributes' => array( 'data-requires' => '{"calendar-enabled":1,"calendar-advanced":1,"calendar-date-range-type":"specific"}' ),
- 'field_tooltip' => 'When synchronizing, we should get events that start
after this date.
(Leave empty to always start "today")',
- ),
- 'calendar-specific-end' => array(
- 'field_title' => esc_html__( 'Specific End Date', 'ccb-core' ),
- 'field_render_function' => 'render_date_picker',
- 'field_validation' => '',
- 'field_attributes' => array( 'data-requires' => '{"calendar-enabled":1,"calendar-advanced":1,"calendar-date-range-type":"specific"}' ),
- 'field_tooltip' => 'When synchronizing, we should get events that start
before this date.
(Setting this too far into the future may cause the API to timeout)',
- ),
- 'calendar-exclude-from-search' => array(
- 'field_title' => esc_html__( 'Exclude From Search?', 'ccb-core' ),
- 'field_render_function' => 'render_radio',
- 'field_options' => array(
- 'yes' => esc_html__( 'Yes', 'ccb-core' ),
- 'no' => esc_html__( 'No', 'ccb-core' ),
- ),
- 'field_validation' => '',
- 'field_default' => 'no',
- 'field_attributes' => array( 'data-requires' => '{"calendar-enabled":1,"calendar-advanced":1}' ),
- ),
- 'calendar-publicly-queryable' => array(
- 'field_title' => esc_html__( 'Publicly Queryable?', 'ccb-core' ),
- 'field_render_function' => 'render_radio',
- 'field_options' => array(
- 'yes' => esc_html__( 'Yes', 'ccb-core' ),
- 'no' => esc_html__( 'No', 'ccb-core' ),
- ),
- 'field_validation' => '',
- 'field_default' => 'yes',
- 'field_attributes' => array( 'data-requires' => '{"calendar-enabled":1,"calendar-advanced":1}' ),
- ),
- 'calendar-show-ui' => array(
- 'field_title' => esc_html__( 'Show In Admin UI?', 'ccb-core' ),
- 'field_render_function' => 'render_radio',
- 'field_options' => array(
- 'yes' => esc_html__( 'Yes', 'ccb-core' ),
- 'no' => esc_html__( 'No', 'ccb-core' ),
- ),
- 'field_validation' => '',
- 'field_default' => 'yes',
- 'field_attributes' => array( 'data-requires' => '{"calendar-enabled":1,"calendar-advanced":1}' ),
- ),
- 'calendar-show-in-nav-menus' => array(
- 'field_title' => esc_html__( 'Show In Navigation Menus?', 'ccb-core' ),
- 'field_render_function' => 'render_radio',
- 'field_options' => array(
- 'yes' => esc_html__( 'Yes', 'ccb-core' ),
- 'no' => esc_html__( 'No', 'ccb-core' ),
- ),
- 'field_validation' => '',
- 'field_default' => 'no',
- 'field_attributes' => array( 'data-requires' => '{"calendar-enabled":1,"calendar-advanced":1}' ),
+ 'field_default' => 90,
+ 'field_validation' => '',
+ 'field_attributes' => array( 'data-requires' => '{"auto_sync":1}' ),
+ 'field_tooltip' => sprintf(
+ esc_html__(
+ 'We keep a local copy (cache) of your Church Community Builder data for the best performance.%1$s
+ How often (in minutes) should we check for new data?%2$s
+ (90 minutes is recommended).',
+ 'ccb-core' ),
+ '
',
+ '
'
),
),
- ),
- ),
- ),
- 'ccb_core_settings_sync' => array(
- 'page_title' => esc_html__( 'Synchronize', 'ccb-core' ),
- 'sections' => array(
- 'synchronize' => array(
- 'section_title' => esc_html__( 'Synchronize', 'ccb-core' ),
- 'fields' => array(
- 'auto-sync' => array(
- 'field_title' => esc_html__( 'Enable Auto Sync', 'ccb-core' ),
- 'field_render_function' => 'render_switch',
- 'field_validation' => 'switch',
- ),
- 'auto-sync-timeout' => array(
- 'field_title' => esc_html__( 'Cache Expiration', 'ccb-core' ),
- 'field_render_function' => 'render_slider',
- 'field_options' => array(
- 'min' => '10',
- 'max' => '180',
- 'units' => 'minutes',
- ),
- 'field_default' => 90,
- 'field_validation' => '',
- 'field_attributes' => array( 'data-requires' => '{"auto-sync":1}' ),
- 'field_tooltip' => 'We keep a local copy (cache) of your Church Community Builder data for the best performance.
How often (in minutes) should we check for new data?
90 minutes is recommended.',
- ),
- 'manual-sync' => array(
- 'field_title' => esc_html__( 'Manual Sync', 'ccb-core' ),
- 'field_render_function' => 'render_manual_sync',
- ),
- 'latest-results' => array(
- 'field_title' => esc_html__( 'Latest Sync Results', 'ccb-core' ),
- 'field_render_function' => 'render_latest_results',
- ),
+ 'manual_sync' => array(
+ 'field_title' => esc_html__( 'Manual Sync', 'ccb-core' ),
+ 'field_render_function' => 'render_manual_sync',
+ ),
+ 'latest_results' => array(
+ 'field_title' => esc_html__( 'Latest Sync Results', 'ccb-core' ),
+ 'field_render_function' => 'render_latest_results',
),
),
),
),
);
+
+ /**
+ * Allow filtering of the entire settings array.
+ *
+ * @since 1.0.0
+ *
+ * @param array $settings The current array of settings definitions.
+ */
+ return apply_filters( 'ccb_core_settings_definitions', $settings );
+
}
/**
* Helper function to create a name/value hash for quick validation
*
- * @access protected
+ * @access private
* @since 0.9.0
* @return array $mapping
*/
- protected function generate_validation_hash() {
+ private function generate_validation_hash() {
+ // Verify the nonce before processing field data.
+ check_admin_referer( 'update_settings', 'ccb_core_nonce' );
+
$mapping = array();
$page_id = isset( $_POST['option_page'] ) ? sanitize_text_field( wp_unslash( $_POST['option_page'] ) ) : false; // Input var okay.
$settings_definitions = $this->get_settings_definitions();
diff --git a/includes/class-ccb-core-synchronizer.php b/includes/class-ccb-core-synchronizer.php
new file mode 100644
index 0000000..180d9dd
--- /dev/null
+++ b/includes/class-ccb-core-synchronizer.php
@@ -0,0 +1,499 @@
+
+ */
+class CCB_Core_Synchronizer {
+
+ /**
+ * A complete mapping of CCB API data to post types and taxonomies
+ *
+ * @var array
+ */
+ private $map;
+
+ /**
+ * An instance of the CCB_Core_API class
+ *
+ * @var CCB_Core_API
+ */
+ private $api;
+
+ /**
+ * Initialize the class and set its properties.
+ *
+ * @since 1.0.0
+ */
+ public function __construct() {
+ // Wait to initialize the map until after the plugins / themes are fully
+ // loaded so that all post types and taxonomies have been registered.
+ add_action( 'init', [ $this, 'initialize_map' ] );
+
+ $this->api = new CCB_Core_API();
+ }
+
+ /**
+ * Setup the registered post type maps and taxonomy maps
+ * from the custom post type and taxonomy classes.
+ *
+ * @return void
+ */
+ public function initialize_map() {
+ $post_type_maps = [];
+ $taxonomy_maps = [];
+ $this->map = array_merge_recursive(
+ apply_filters( 'ccb_core_post_type_map', $post_type_maps ),
+ apply_filters( 'ccb_core_taxonomy_map', $taxonomy_maps )
+ );
+ }
+
+ /**
+ * Calls the CCB API and synchronizes post objects and
+ * taxonomies based on the mapping definitions from the
+ * custom post type and custom taxonomy classes.
+ *
+ * @return bool True on success
+ */
+ public function synchronize() {
+
+ // For each registered custom post type, call the API and
+ // get a response object.
+ foreach ( $this->map as $post_type => $settings ) {
+ if ( ! empty( $settings['service'] ) ) {
+ $data = ! empty( $settings['data'] ) ? $settings['data'] : [];
+ $response = $this->api->get( $settings['service'], $data );
+ if ( 'SUCCESS' === $response['status'] ) {
+ $update_successful = $this->update_content( $post_type, $settings, $response );
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Takes an API response and will either Insert, Update, or Delete
+ * content based on the settings and any applicable existing content.
+ *
+ * @param string $post_type The post type being updated.
+ * @param array $settings The settings for the mapping.
+ * @param array $response An API response.
+ * @return void
+ */
+ private function update_content( $post_type, $settings, $response ) {
+
+ // The nodes are mapped down from the parent(s) to the
+ // single child object. Get a collection of entities
+ // that will map to a single post type.
+ $entities = $this->get_entities( $response, $settings['nodes'] );
+
+ // Get a collection of existing posts (previously imported) from WordPress.
+ // This is organized by entitiy id (from CCB) and contains the WordPress
+ // post_id and optional modified date.
+ $post_data = $this->get_existing_post_data( $post_type );
+
+ // Organize the entities and existing post data into their
+ // respective CRUD operations. This will return an array
+ // with entities to insert for the first time, entities
+ // to update (that already exist and have changed), and
+ // posts that no longer exist in CCB and should be deleted.
+ $organized_entities = $this->organize_entities( $entities, $post_data, $post_type );
+
+ error_log( 'before insert ' . $post_type . ': ' . size_format( memory_get_usage() ) );
+ if ( ! empty( $organized_entities['insert_update'] ) ) {
+ $insert_result = $this->insert_update_entities( $organized_entities['insert_update'], $post_type, $settings );
+ }
+ error_log( 'after insert ' . $post_type . ': ' . size_format( memory_get_usage() ) );
+ error_log( 'before delete ' . $post_type . ': ' . size_format( memory_get_usage() ) );
+ if ( ! empty( $organized_entities['delete'] ) ) {
+ $delete_result = $this->delete_posts( $organized_entities['delete'] );
+ }
+ error_log( 'after delete ' . $post_type . ': ' . size_format( memory_get_usage() ) );
+
+ $this->delete_empty_terms( $settings );
+
+ }
+
+ private function get_entities( $response, $nodes ) {
+ if ( ! empty( $nodes ) ) {
+ $depth = count( $nodes ) - 1;
+ $collection = $response['body']->response;
+ for ( $i = 0; $i < $depth; $i++ ) {
+ $collection = $collection->{$nodes[ $i ]};
+ }
+ return $collection;
+ }
+ return false;
+ }
+
+ private function get_existing_post_data( $post_type ) {
+ // Batch the WP_Query for performance.
+ $posts_per_page = 100;
+ $offset = 0;
+ $collection = [];
+
+ do {
+ $args = [
+ 'post_type' => $post_type,
+ 'post_status' => 'any',
+ 'posts_per_page' => $posts_per_page,
+ 'offset' => $offset,
+ 'no_rows_found' => true,
+ 'fields' => 'ids',
+ ];
+
+ $posts = new WP_Query( $args );
+
+ if ( ! empty( $posts->posts ) ) {
+ foreach ( $posts->posts as $post_id ) {
+
+ // These are saved during the insert / update process (of possible)
+ // in order to attempt future updates.
+ $entity_id = get_post_meta( $post_id, 'entity_id', true );
+ $ccb_modified_date = get_post_meta( $post_id, 'ccb_modified_date', true );
+
+ if ( ! empty( $entity_id ) ) {
+ $collection[ $entity_id ] = [
+ 'post_id' => $post_id,
+ 'ccb_modified_date' => $ccb_modified_date,
+ ];
+ }
+ }
+ }
+
+ $offset = $offset + $posts_per_page;
+
+ } while ( count( $posts->posts ) );
+
+ return $collection;
+ }
+
+ private function organize_entities( $entities, $post_data, $post_type ) {
+
+ $collection = [
+ 'insert_update' => [],
+ 'delete' => [],
+ ];
+
+ // Create a master collection of new entity ids
+ // that were either inserted or updated
+ // for quick filtering of existing posts
+ // so that we can find posts to delete.
+ $synced_entity_ids = [];
+
+ error_log( 'before organize ' . $post_type . ': ' . size_format( memory_get_usage() ) );
+ foreach ( $entities->children() as $entity ) {
+
+ $entity_id = $this->get_entity_id( $entity );
+
+ // If the entity_id is a 32 character hash it means we cannot
+ // map it to some known older post data because it either
+ // doesn't have a CCB ID or else it doesn't give us a modified
+ // date. So we hash its string value in order to get a unique
+ // signature. We use this to determine if we "already have this
+ // thing" so we don't need to insert / update it.
+ if ( 32 === strlen( $entity_id ) ) {
+ if ( apply_filters( 'ccb_core_entity_insert_allowed', true, $entity_id, $entity, $post_type ) ) {
+ // If the unique id for the new entity couldn't be found
+ // in the existing collection, it is new. Add it
+ // to the insert collection.
+ if ( ! array_key_exists( $entity_id, $post_data ) ) {
+ $entity->addChild( 'post_id', 0 ); // This is a WordPress post ID, so this will be an insert.
+ $collection['insert_update'][] = $entity;
+ }
+ // Regardless of whether or not this unique entity exists
+ // (i.e. regardless of an insert or update) we still record
+ // that it was synced so that it doesn't get deleted.
+ $synced_entity_ids[] = $entity_id;
+ }
+ } else {
+ if ( array_key_exists( $entity_id, $post_data ) && ! empty( $entity->modified ) ) {
+ if ( apply_filters( 'ccb_core_entity_update_allowed', true, $entity_id, $entity, $post_type ) ) {
+ // If an existing post has a newer modified date from the API
+ // add it to the insert_update collection with its existing post id.
+ if ( strtotime( $entity->modified ) > strtotime( $post_data[ $entity_id ]['ccb_modified_date'] ) ) {
+ $entity->addChild( 'post_id', $post_data[ $entity_id ]['post_id'] ); // This is a WordPress post ID, so this will be an update.
+ $collection['insert_update'][] = $entity;
+ }
+ // Even though we may not have made an update
+ // we still add this as a "synced" id because it exists.
+ $synced_entity_ids[] = $entity_id;
+ }
+ } else {
+ // The unique id for the new entity couldn't be found
+ // in the existing collection, so it is new. Add it
+ // to the insert_update collection.
+ if ( apply_filters( 'ccb_core_entity_insert_allowed', true, $entity_id, $entity, $post_type ) ) {
+ $entity->addChild( 'post_id', 0 ); // This is a WordPress post ID, so this will be an insert.
+ $collection['insert_update'][] = $entity;
+ $synced_entity_ids[] = $entity_id;
+ }
+ }
+ }
+
+ }
+
+ error_log( 'after organize ' . $post_type . ': ' . size_format( memory_get_usage() ) );
+ // For each existing post, check to see if it was included
+ // in the new entity id collection from this most recent
+ // API response. If it doesn't exist, it was deleted.
+ foreach ( $post_data as $entity_id => $data ) {
+ if ( ! in_array( $entity_id, $synced_entity_ids, true ) ) {
+ if ( apply_filters( 'ccb_core_entity_delete_allowed', true, $entity_id, $data, $post_type ) ) {
+ $collection['delete'][] = $data['post_id'];
+ }
+ }
+ }
+
+ return $collection;
+ }
+
+ private function get_entity_id( $entity ) {
+ // If an entity doesn't have a CCB ID, we cannot match it during
+ // the sync process in order to perform an update. So instead,
+ // create a hash of the entity and store it as a unique identifier.
+ //
+ // If an entity has an actual CCB ID, but *doesn't* have a modified
+ // date, there's no point in storing the CCB ID. This is because
+ // without a modified date we cannot determine if the entity has
+ // changed since the previous sync. Instead, just create a has of the entity.
+ if ( ! empty( $entity->attributes() ) && ! empty( $entity->modified ) ) {
+ foreach ( $entity->attributes() as $key => $value ) {
+ if ( false !== stristr( $key, 'id' ) ) {
+ return (int) $value;
+ }
+ }
+ }
+ if ( isset( $entity->post_id ) ) {
+ unset( $entity->post_id );
+ }
+ return md5( $entity->asXML() );
+ }
+
+ private function insert_update_entities( $entities, $post_type, $settings ) {
+ global $wpdb;
+ // Temporarily disable counting for performance.
+ wp_defer_term_counting( true );
+ wp_defer_comment_counting( true );
+ wp_suspend_cache_addition( true );
+ remove_action( 'do_pings', 'do_all_pings', 10, 1 );
+
+ error_log( 'after setting optimizations ' . $post_type . ': ' . size_format( memory_get_usage() ) );
+
+ // Temporarily disable autocommit.
+ $wpdb->query( 'SET autocommit = 0;' ); // db call ok; no cache ok.
+ set_time_limit( 600 );
+
+ foreach ( $entities as $entity ) {
+ // Build a new $args array for each post insert
+ // based on the settings config.
+ $args = [
+ 'ID' => (int) $entity->post_id, // Will be set to 0 if this is an insert.
+ 'post_title' => '', // Default to empty, expected to be overriden by the settings.
+ 'post_content' => '', // Default to empty, expected to be overriden by the settings.
+ 'post_status' => 'publish',
+ 'post_type' => $post_type,
+ 'meta_input' => [],
+ 'tax_input' => [],
+ ];
+
+ // Inspect each field defined in the settings. If it's a
+ // post_meta element, add it to the meta_input array, otherwise
+ // assume it's part of the parent post object.
+ foreach ( $settings['fields'] as $element => $field ) {
+ if ( 'post_meta' === $field ) {
+ $args['meta_input'][ $element ] = $this->auto_cast( $entity->{$element} );
+ } else {
+ $args[ $field ] = $this->auto_cast( $entity->{$element} );
+ }
+ }
+
+ // Attempt to store additional meta related to synchronization.
+ // If CCB provided a CCB ID for this entitiy, save it. If CCB
+ // provided a `modified` node, save it. We use them to attempt
+ // updates to the entity when needed.
+ $args['meta_input']['entity_id'] = $this->get_entity_id( $entity );
+ if ( isset( $entity->modified ) ) {
+ $args['meta_input']['ccb_modified_date'] = $this->auto_cast( $entity->modified );
+ }
+
+ // Prepare hierarchial taxonomies by ensuring the term id
+ // already exists and set terms ids.
+ $prepared_terms = $this->prepare_terms( $entity, $settings );
+ if ( ! empty( $prepared_terms ) ) {
+ $args['tax_input'] = $prepared_terms;
+ }
+
+ // If this is an update, we need to remove all term
+ // relationships in order to ensure we are in
+ // sync with the most recent entity. Otherwise if a
+ // relationship was removed by CCB, it'll persist in WordPress.
+ if ( $args['ID'] ) {
+ if ( ! empty( $settings['taxonomies']['hierarchical'] ) ) {
+ foreach ( $settings['taxonomies']['hierarchical'] as $taxonomy => $node ) {
+ wp_delete_object_term_relationships( $args['ID'], $taxonomy );
+ }
+ }
+ if ( ! empty( $settings['taxonomies']['nonhierarchical'] ) ) {
+ foreach ( $settings['taxonomies']['nonhierarchical'] as $taxonomy => $node ) {
+ wp_delete_object_term_relationships( $args['ID'], $taxonomy );
+ }
+ }
+ }
+
+ do_action( 'ccb_core_before_insert_update_post', $entity, $settings, $args, $post_type );
+
+ // Perform an insert (or update if we included a post id).
+ $post_id = wp_insert_post( $args, true );
+
+ do_action( 'ccb_core_after_insert_update_post', $entity, $settings, $args, $post_type, $post_id );
+ }
+
+ error_log( 'after loop, before commit ' . $post_type . ': ' . size_format( memory_get_usage() ) );
+
+ // Commit the database operations now.
+ $wpdb->query( 'COMMIT;' ); // db call ok; no cache ok.
+ // Re-enable autocommit.
+ $wpdb->query( 'SET autocommit = 1;' ); // db call ok; no cache ok.
+
+ error_log( 'after loop, before stop_the_insanity ' . $post_type . ': ' . size_format( memory_get_usage() ) );
+
+ // Re-enable counting.
+ wp_suspend_cache_addition( false );
+ wp_defer_term_counting( false );
+ wp_defer_comment_counting( false );
+ $this->stop_the_insanity();
+ }
+
+ private function delete_posts( $post_ids ) {
+ foreach ( $post_ids as $post_id ) {
+ wp_delete_post( $post_id, true );
+ }
+ return true;
+ }
+
+ private function delete_empty_terms( $settings ) {
+ $args = [
+ 'hide_empty' => false,
+ ];
+
+ if ( ! empty( $settings['taxonomies']['hierarchical'] ) ) {
+ foreach ( $settings['taxonomies']['hierarchical'] as $taxonomy => $node ) {
+ $args['taxonomy'][] = $taxonomy;
+ }
+ }
+ if ( ! empty( $settings['taxonomies']['nonhierarchical'] ) ) {
+ foreach ( $settings['taxonomies']['nonhierarchical'] as $taxonomy => $node ) {
+ $args['taxonomy'][] = $taxonomy;
+ }
+ }
+
+ $terms_query = new WP_Term_Query( $args );
+ foreach ( $terms_query->get_terms() as $term ) {
+ if ( 0 === $term->count ) {
+ wp_delete_term( $term->term_id, $term->taxonomy );
+ }
+ }
+
+ return true;
+ }
+
+ private function prepare_terms( $entity, $settings ) {
+ $categories = [];
+ $tags = [];
+
+ if ( ! empty( $settings['taxonomies']['hierarchical'] ) ) {
+ foreach ( $settings['taxonomies']['hierarchical'] as $taxonomy => $node ) {
+ $term_name = $this->auto_cast( $entity->{$node} );
+ if ( $term_name ) {
+ $term = term_exists( $term_name, $taxonomy );
+ if ( ! $term ) {
+ $term = wp_insert_term( $term_name, $taxonomy );
+ }
+ if ( $term && ! is_wp_error( $term ) ) {
+ $categories[ $taxonomy ][] = $term['term_taxonomy_id'];
+ }
+ }
+ }
+ }
+
+ if ( ! empty( $settings['taxonomies']['nonhierarchical'] ) ) {
+ foreach ( $settings['taxonomies']['nonhierarchical'] as $taxonomy => $node_array ) {
+ foreach ( $node_array as $node => $tag ) {
+ $tag_is_set = $this->auto_cast( $entity->{$node} );
+ if ( $tag_is_set ) {
+ $tags[ $taxonomy ][] = $tag;
+ }
+ }
+ }
+ }
+
+ return array_merge( $categories, $tags );
+ }
+
+ private function auto_cast( $element ) {
+ if ( $element->children()->count() ) {
+ $array = [];
+ // If the node happens to have an id attribute,
+ // transform it into a property so that it's not lost.
+ $id = false;
+ if ( ! empty( $element->attributes() ) ) {
+ foreach ( $element->attributes() as $key => $value ) {
+ if ( false !== stristr( $key, 'id' ) ) {
+ $id = (int) $value;
+ break;
+ }
+ }
+ }
+ if ( $id ) {
+ $array['id'] = $id;
+ }
+ foreach ( $element->children() as $child ) {
+ $array[ $child->getName() ] = $this->auto_cast( $child );
+ }
+ return $array;
+ } elseif ( 'true' === (string) $element ) {
+ return true;
+ } elseif ( 'false' === (string) $element ) {
+ return false;
+ } elseif ( strlen( (int) $element ) === strlen( (string) $element ) ) {
+ return (int) $element;
+ } else {
+ return (string) $element;
+ }
+ }
+
+ /**
+ * Clear all of the caches for memory management
+ */
+ private function stop_the_insanity() {
+ global $wpdb, $wp_object_cache;
+
+ $wpdb->queries = array();
+
+ if ( is_object( $wp_object_cache ) ) {
+ $wp_object_cache->group_ops = array();
+ $wp_object_cache->stats = array();
+ $wp_object_cache->memcache_debug = array();
+ $wp_object_cache->cache = array();
+
+ if ( method_exists( $wp_object_cache, '__remoteset' ) ) {
+ $wp_object_cache->__remoteset();
+ }
+ }
+ }
+}
diff --git a/includes/class-ccb-core.php b/includes/class-ccb-core.php
index d52fa45..5248e9f 100644
--- a/includes/class-ccb-core.php
+++ b/includes/class-ccb-core.php
@@ -59,8 +59,11 @@ private function load_dependencies() {
require_once CCB_CORE_PATH . 'includes/class-ccb-core-settings-section.php';
require_once CCB_CORE_PATH . 'includes/class-ccb-core-settings-field.php';
- // The class that handles data synchronization between CCB and the local cache.
- require_once CCB_CORE_PATH . 'includes/class-ccb-core-sync.php';
+ // The class that handles communication with the CCB API.
+ require_once CCB_CORE_PATH . 'includes/class-ccb-core-api.php';
+
+ // The class that handles synchronization logic.
+ require_once CCB_CORE_PATH . 'includes/class-ccb-core-synchronizer.php';
// Custom Post Type classes.
require_once CCB_CORE_PATH . 'includes/post-types/class-ccb-core-cpt.php';
@@ -79,6 +82,9 @@ private function load_dependencies() {
require_once CCB_CORE_PATH . 'includes/taxonomies/class-ccb-core-group-time.php';
require_once CCB_CORE_PATH . 'includes/taxonomies/class-ccb-core-group-type.php';
+ // Admin AJAX methods.
+ require_once CCB_CORE_PATH . 'includes/class-ccb-core-admin-ajax.php';
+
}
/**
@@ -100,23 +106,12 @@ private function define_hooks() {
add_action( 'admin_menu', array( $this, 'initialize_settings_menu' ) );
add_action( 'admin_init', array( $this, 'initialize_settings' ) );
+ // Callback for after the options are saved.
+ add_action( 'update_option_ccb_core_settings', array( $this, 'updated_options' ), 10, 2 );
+
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
- /*// Cron related hooks.
- $this->loader->add_action( 'schedule_auto_refresh', $plugin_admin, 'auto_sync' );
- $this->loader->add_action( 'wp_loaded', $plugin_admin, 'check_auto_refresh' );
-
- // User initiated actions.
- $this->loader->add_action( 'pre_update_option_' . $this->plugin_settings_name, $plugin_admin, 'update_settings_callback', 10, 2 );
- $this->loader->add_action( 'schedule_flush_rewrite_rules', $plugin_admin, 'flush_rewrite_rules_event' );
-
- // All backend ajax hooks.
- $this->loader->add_action( 'wp_ajax_sync', $plugin_admin, 'ajax_sync' );
- $this->loader->add_action( 'wp_ajax_poll_sync', $plugin_admin, 'ajax_poll_sync' );
- $this->loader->add_action( 'wp_ajax_test_credentials', $plugin_admin, 'ajax_test_credentials' );
- $this->loader->add_action( 'wp_ajax_get_latest_sync', $plugin_admin, 'ajax_get_latest_sync' );*/
-
}
/**
@@ -237,6 +232,39 @@ public function initialize_settings() {
}
+ /**
+ * After the options are saved, check to see if we
+ * should flush the rewrite rules.
+ *
+ * @param array $old_value The previous option value.
+ * @param array $value The new option value.
+ * @access public
+ * @since 1.0.0
+ * @return void
+ */
+ public function updated_options( $old_value, $value ) {
+
+ // Create a collection of settings that, if they change, should
+ // trigger a flush_rewrite_rules event.
+ $setting_array = array(
+ 'groups_enabled',
+ 'groups_slug',
+ 'calendar_enabled',
+ 'calendar_slug',
+ );
+
+ foreach ( $setting_array as $setting ) {
+ if ( isset( $value[ $setting ] ) ) {
+ if ( ! isset( $old_value[ $setting ] ) || $value[ $setting ] !== $old_value[ $setting ] ) {
+ // At least one option requires a flush, so do it once and return.
+ flush_rewrite_rules();
+ return;
+ }
+ }
+ }
+
+ }
+
/**
* Register the stylesheets for the dashboard.
*
@@ -271,8 +299,11 @@ public function enqueue_scripts( $hook ) {
wp_enqueue_script( 'picker', CCB_CORE_URL . 'js/vendor/picker.js', array( 'jquery' ), CCB_CORE_VERSION, false );
wp_enqueue_script( 'picker-date', CCB_CORE_URL . 'js/vendor/picker.date.js', array( 'picker' ), CCB_CORE_VERSION, false );
wp_enqueue_script( 'tipr', CCB_CORE_URL . 'js/vendor/tipr.min.js', array( 'jquery' ), CCB_CORE_VERSION, false );
- wp_localize_script( 'ccb-core', 'CCB_CORE_SETTINGS', array(
- 'nextNonce' => wp_create_nonce( 'ccb-core-nonce' ),
+ wp_localize_script(
+ 'ccb-core',
+ 'CCB_CORE_SETTINGS',
+ array(
+ 'nonce' => wp_create_nonce( 'ccb_core_nonce' ),
)
);
}
diff --git a/includes/post-types/class-ccb-core-calendar.php b/includes/post-types/class-ccb-core-calendar.php
index ece256a..895b0ca 100644
--- a/includes/post-types/class-ccb-core-calendar.php
+++ b/includes/post-types/class-ccb-core-calendar.php
@@ -23,46 +23,324 @@ class CCB_Core_Calendar extends CCB_Core_CPT {
*
* @var string
*/
- public $name = 'ccb-core-calendar';
+ public $name = 'ccb_core_calendar';
/**
- * Setup the default CPT options
+ * Initialize the class
+ */
+ public function __construct() {
+ $options = CCB_Core_Helpers::instance()->get_options();
+ $this->enabled = ! empty( $options['calendar_enabled'] ) ? true : false;
+ parent::__construct();
+ }
+
+ /**
+ * Setup the custom post type args
*
* @since 1.0.0
- * @return array Default options for register_post_type
+ * @return array $args for register_post_type
*/
- public function get_cpt_defaults() {
+ public function get_post_args() {
+
+ $options = CCB_Core_Helpers::instance()->get_options();
+ $plural = ! empty( $options['calendar_name'] ) ? $options['calendar_name'] : __( 'Events', 'ccb-core' );
+ $singular = ! empty( $options['calendar_name_singular'] ) ? $options['calendar_name_singular'] : __( 'Event', 'ccb-core' );
+ $rewrite = ! empty( $options['calendar_slug'] ) ? [ 'slug' => sanitize_title( $options['calendar_slug'] ) ] : [ 'slug' => 'events' ];
+ $has_archive = ! empty( $options['calendar_slug'] ) ? sanitize_title( $options['calendar_slug'] ) : 'events';
+ $exclude_from_search = ! empty( $options['calendar_exclude_from_search'] ) && 'yes' === $options['calendar_exclude_from_search'] ? true : false;
+ $publicly_queryable = ! empty( $options['calendar_publicly_queryable'] ) && 'no' === $options['calendar_publicly_queryable'] ? false : true;
+ $show_ui = ! empty( $options['calendar_show_ui'] ) && 'no' === $options['calendar_show_ui'] ? false : true;
+ $show_in_nav_menus = ! empty( $options['calendar_show_in_nav_menus'] ) && 'yes' === $options['calendar_show_in_nav_menus'] ? true : false;
+
return array(
'labels' => array(
- 'name' => __( 'Events', 'ccb-core' ),
- 'singular_name' => __( 'Event', 'ccb-core' ),
- 'all_items' => __( 'All Events', 'ccb-core' ),
+ 'name' => $plural,
+ 'singular_name' => $singular,
+ 'all_items' => sprintf( __( 'All %s', 'ccb-core' ), $plural ),
'add_new' => __( 'Add New', 'ccb-core' ),
- 'add_new_item' => __( 'Add New Event', 'ccb-core' ),
+ 'add_new_item' => sprintf( __( 'Add New %s', 'ccb-core' ), $singular ),
'edit' => __( 'Edit', 'ccb-core' ),
- 'edit_item' => __( 'Edit Event', 'ccb-core' ),
- 'new_item' => __( 'New Event', 'ccb-core' ),
- 'view_item' => __( 'View Event', 'ccb-core' ),
- 'search_items' => __( 'Search Events', 'ccb-core' ),
+ 'edit_item' => sprintf( __( 'Edit %s', 'ccb-core' ), $singular ),
+ 'new_item' => sprintf( __( 'New %s', 'ccb-core' ), $singular ),
+ 'view_item' => sprintf( __( 'View %s', 'ccb-core' ), $singular ),
+ 'search_items' => sprintf( __( 'Search %s', 'ccb-core' ), $plural ),
'not_found' => __( 'Nothing found in the Database.', 'ccb-core' ),
'not_found_in_trash' => __( 'Nothing found in Trash', 'ccb-core' ),
'parent_item_colon' => '',
),
- 'description' => __( 'These are the events that are synchronized with your Church Community Builder software.', 'ccb-core' ),
+ 'description' => sprintf( __( 'These are the %s that are synchronized with your Church Community Builder software.', 'ccb-core' ), $plural ),
'public' => true,
- 'publicly_queryable' => true,
- 'exclude_from_search' => false,
- 'show_ui' => true,
- 'show_in_nav_menus' => true,
+ 'publicly_queryable' => $publicly_queryable,
+ 'exclude_from_search' => $exclude_from_search,
+ 'show_ui' => $show_ui,
+ 'show_in_nav_menus' => $show_in_nav_menus,
'query_var' => true,
'menu_position' => 8,
'menu_icon' => 'dashicons-calendar',
- 'rewrite' => array( 'slug' => 'events' ),
- 'has_archive' => 'events',
+ 'rewrite' => $rewrite,
+ 'has_archive' => $has_archive,
'capability_type' => 'post',
'hierarchical' => false,
'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'custom-fields', 'sticky' ),
);
+
+ }
+
+ /**
+ * Configure the options that users are allowed to set
+ *
+ * @since 1.0.0
+ * @param array $settings The settings definitions.
+ * @return array
+ */
+ public function get_post_settings_definitions( $settings ) {
+
+ $settings['ccb_core_settings_calendar'] = array(
+ 'page_title' => esc_html__( 'Public Events', 'ccb-core' ),
+ 'sections' => array(
+ 'calendar' => array(
+ 'section_title' => esc_html__( 'Public Events', 'ccb-core' ),
+ 'fields' => array(
+ 'calendar_enabled' => array(
+ 'field_title' => esc_html__( 'Enable Events', 'ccb-core' ),
+ 'field_render_function' => 'render_switch',
+ 'field_validation' => 'switch',
+ ),
+ 'calendar_name' => array(
+ 'field_title' => esc_html__( 'Event Display Name (Plural)', 'ccb-core' ),
+ 'field_render_function' => 'render_text',
+ 'field_placeholder' => esc_html__( 'Events', 'ccb-core' ),
+ 'field_validation' => 'alphanumeric_extended',
+ 'field_attributes' => array( 'data-requires' => '{"calendar_enabled":1}' ),
+ 'field_tooltip' => esc_html__( 'This is what you call the events in your church (i.e. Meetups, Hangouts, etc.).', 'ccb-core' ),
+ ),
+ 'calendar_name_singular' => array(
+ 'field_title' => esc_html__( 'Event Display Name (Singular)', 'ccb-core' ),
+ 'field_render_function' => 'render_text',
+ 'field_placeholder' => esc_html__( 'Event', 'ccb-core' ),
+ 'field_validation' => 'alphanumeric_extended',
+ 'field_attributes' => array( 'data-requires' => '{"calendar_enabled":1}' ),
+ 'field_tooltip' => esc_html__( 'This is the singular name of what you call the events in your church (i.e. Meetup, Hangout, etc.).', 'ccb-core' ),
+ ),
+ 'calendar_slug' => array(
+ 'field_title' => esc_html__( 'Events URL Name', 'ccb-core' ),
+ 'field_render_function' => 'render_text',
+ 'field_placeholder' => 'events',
+ 'field_validation' => 'slug',
+ 'field_attributes' => array( 'data-requires' => '{"calendar_enabled":1}' ),
+ 'field_tooltip' => esc_html__( 'This is typically where your theme will display all the events. WordPress calls this a "slug".', 'ccb-core' ),
+ ),
+ 'calendar_advanced' => array(
+ 'field_title' => esc_html__( 'Enable Advanced Settings (Optional)', 'ccb-core' ),
+ 'field_render_function' => 'render_switch',
+ 'field_validation' => 'switch',
+ 'field_attributes' => array( 'data-requires' => '{"calendar_enabled":1}' ),
+ ),
+ 'calendar_date_range_type' => array(
+ 'field_title' => esc_html__( 'Date Range Type', 'ccb-core' ),
+ 'field_render_function' => 'render_radio',
+ 'field_options' => array(
+ 'relative' => esc_html__( 'Relative Range', 'ccb-core' ),
+ 'specific' => esc_html__( 'Specific Range', 'ccb-core' ),
+ ),
+ 'field_validation' => '',
+ 'field_default' => 'relative',
+ 'field_attributes' => array( 'class' => 'date-range-type', 'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1}' ),
+ 'field_tooltip' => sprintf(
+ esc_html__(
+ 'Relative: For example, always get the events from "One week ago", up to "Eight weeks from now".%1$s
+ This is the best setting for most churches.%2$s
+ Specific: For example, only get events from "6/1/2018" to "12/1/2018".%3$s
+ This setting is best if you want to tightly manage the events that get published.',
+ 'ccb-core' ),
+ '
',
+ '
',
+ '
'
+ ),
+ ),
+ 'calendar_relative_weeks_past' => array(
+ 'field_title' => esc_html__( 'How Far Back?', 'ccb-core' ),
+ 'field_render_function' => 'render_slider',
+ 'field_options' => array(
+ 'min' => '0',
+ 'max' => '26',
+ 'units' => 'weeks',
+ ),
+ 'field_default' => 1,
+ 'field_validation' => '',
+ 'field_attributes' => array( 'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1,"calendar_date_range_type":"relative"}' ),
+ 'field_tooltip' => esc_html__( 'Every time we synchronize, how many weeks in the past should we look? (0 would be "today")', 'ccb-core' ),
+ ),
+ 'calendar_relative_weeks_future' => array(
+ 'field_title' => esc_html__( 'How Into The Future?', 'ccb-core' ),
+ 'field_render_function' => 'render_slider',
+ 'field_options' => array(
+ 'min' => '1',
+ 'max' => '52',
+ 'units' => 'weeks',
+ ),
+ 'field_default' => 16,
+ 'field_validation' => '',
+ 'field_attributes' => array( 'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1,"calendar_date_range_type":"relative"}' ),
+ 'field_tooltip' => esc_html__( 'Every time we synchronize, how many weeks in the future should we look?', 'ccb-core' ),
+ ),
+ 'calendar_specific_start' => array(
+ 'field_title' => esc_html__( 'Specific Start Date', 'ccb-core' ),
+ 'field_render_function' => 'render_date_picker',
+ 'field_validation' => '',
+ 'field_attributes' => array( 'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1,"calendar_date_range_type":"specific"}' ),
+ 'field_tooltip' => sprintf(
+ esc_html__(
+ 'When synchronizing, we should get events that start after this date.%s
+ (Leave empty to always start "today")',
+ 'ccb-core' ),
+ '
'
+ ),
+ ),
+ 'calendar_specific_end' => array(
+ 'field_title' => esc_html__( 'Specific End Date', 'ccb-core' ),
+ 'field_render_function' => 'render_date_picker',
+ 'field_validation' => '',
+ 'field_attributes' => array( 'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1,"calendar_date_range_type":"specific"}' ),
+ 'field_tooltip' => sprintf(
+ esc_html__(
+ 'When synchronizing, we should get events that start before this date.%s
+ (Setting this too far into the future may cause the API to timeout)',
+ 'ccb-core' ),
+ '
'
+ ),
+ ),
+ 'calendar_exclude_from_search' => array(
+ 'field_title' => esc_html__( 'Exclude From Search?', 'ccb-core' ),
+ 'field_render_function' => 'render_radio',
+ 'field_options' => array(
+ 'yes' => esc_html__( 'Yes', 'ccb-core' ),
+ 'no' => esc_html__( 'No', 'ccb-core' ),
+ ),
+ 'field_validation' => '',
+ 'field_default' => 'no',
+ 'field_attributes' => array( 'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1}' ),
+ ),
+ 'calendar_publicly_queryable' => array(
+ 'field_title' => esc_html__( 'Publicly Queryable?', 'ccb-core' ),
+ 'field_render_function' => 'render_radio',
+ 'field_options' => array(
+ 'yes' => esc_html__( 'Yes', 'ccb-core' ),
+ 'no' => esc_html__( 'No', 'ccb-core' ),
+ ),
+ 'field_validation' => '',
+ 'field_default' => 'yes',
+ 'field_attributes' => array( 'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1}' ),
+ ),
+ 'calendar_show_ui' => array(
+ 'field_title' => esc_html__( 'Show In Admin UI?', 'ccb-core' ),
+ 'field_render_function' => 'render_radio',
+ 'field_options' => array(
+ 'yes' => esc_html__( 'Yes', 'ccb-core' ),
+ 'no' => esc_html__( 'No', 'ccb-core' ),
+ ),
+ 'field_validation' => '',
+ 'field_default' => 'yes',
+ 'field_attributes' => array( 'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1}' ),
+ ),
+ 'calendar_show_in_nav_menus' => array(
+ 'field_title' => esc_html__( 'Show In Navigation Menus?', 'ccb-core' ),
+ 'field_render_function' => 'render_radio',
+ 'field_options' => array(
+ 'yes' => esc_html__( 'Yes', 'ccb-core' ),
+ 'no' => esc_html__( 'No', 'ccb-core' ),
+ ),
+ 'field_validation' => '',
+ 'field_default' => 'no',
+ 'field_attributes' => array( 'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1}' ),
+ ),
+ ),
+ ),
+ ),
+ );
+
+ return $settings;
+ }
+
+ /**
+ * Define the mapping of CCB API fields to the Post fields
+ *
+ * @since 1.0.0
+ * @param array $map A collection of mappings from the API to WordPress.
+ * @return array
+ */
+ public function get_post_type_map( $map ) {
+ if ( $this->enabled ) {
+ $options = CCB_Core_Helpers::instance()->get_options();
+ $calendar_options = $this->get_calendar_options( $options );
+
+ $map[ $this->name ] = [
+ 'service' => 'public_calendar_listing',
+ 'data' => [
+ 'date_start' => $calendar_options['date_start'],
+ 'date_end' => $calendar_options['date_end'],
+ ],
+ 'nodes' => [ 'items', 'item' ],
+ 'fields' => [
+ 'event_name' => 'post_title',
+ 'event_description' => 'post_content',
+ 'date' => 'post_meta',
+ 'start_time' => 'post_meta',
+ 'end_time' => 'post_meta',
+ 'event_duration' => 'post_meta',
+ 'location' => 'post_meta',
+ ],
+ ];
+ }
+ return $map;
+ }
+
+ private function get_calendar_options( $options ) {
+ // By default, set some sane limits.
+ $calendar_options = [
+ 'date_start' => date( 'Y-m-d', strtotime( '1 weeks ago' ) ),
+ 'date_end' => date( 'Y-m-d', strtotime( '+16 weeks' ) ),
+ ];
+
+ // If the user has set a preferred date range type.
+ if ( ! empty( $options['calendar_date_range_type'] ) ) {
+
+ if ( 'relative' === $options['calendar_date_range_type'] ) {
+
+ $calendar_options['date_start'] = date( 'Y-m-d', strtotime( $options['calendar_relative_weeks_past'] . ' weeks ago' ) );
+ $calendar_options['date_end'] = date( 'Y-m-d', strtotime( '+' . $options['calendar_relative_weeks_future'] . ' weeks' ) );
+
+ } elseif ( 'specific' === $options['calendar_date_range_type'] ) {
+
+ // For each date range, do not let the user go further than
+ // 1 year in the past or 1 year into the future to prevent
+ // them from blowing up their server.
+ if ( ! empty( $options['calendar_specific_start'] ) ) {
+ $last_year = strtotime( '1 year ago' );
+ $start_timestamp = strtotime( $options['calendar_specific_start'] );
+
+ if ( $last_year < $start_timestamp ) {
+ $calendar_options['date_start'] = date( 'Y-m-d', $start_timestamp );
+ } else {
+ $calendar_options['date_start'] = date( 'Y-m-d', $last_year );
+ }
+ }
+
+ if ( ! empty( $options['calendar_specific_end'] ) ) {
+ $next_year = strtotime( '+1 year' );
+ $end_timestamp = strtotime( $options['calendar_specific_end'] );
+
+ if ( $next_year > $end_timestamp ) {
+ $calendar_options['date_end'] = date( 'Y-m-d', $end_timestamp );
+ } else {
+ $calendar_options['date_end'] = date( 'Y-m-d', $next_year );
+ }
+ }
+
+ }
+ }
+
+ return $calendar_options;
}
}
diff --git a/includes/post-types/class-ccb-core-cpt.php b/includes/post-types/class-ccb-core-cpt.php
index f55fd73..227f879 100644
--- a/includes/post-types/class-ccb-core-cpt.php
+++ b/includes/post-types/class-ccb-core-cpt.php
@@ -26,78 +26,27 @@ abstract class CCB_Core_CPT {
public $name;
/**
- * The specific custom post type options
+ * Whether or not this post type is enabled. (Set by child class).
*
- * @var array
+ * @var bool
*/
- protected $cpt_options = array();
+ public $enabled;
/**
* Initialize the class
*/
public function __construct() {
- $plugin_options = CCB_Core_Helpers::instance()->get_options();
+ add_filter( 'ccb_core_post_type_settings_definitions', array( $this, 'get_post_settings_definitions' ) );
- // If this CPT is enabled, merge the defaults and set the registration hook.
- if ( ! empty( $plugin_options[ $this->name ]['enabled'] ) ) {
- $this->cpt_options = wp_parse_args( $this->get_user_cpt_options( $plugin_options ), $this->get_cpt_defaults() );
+ // If this custom post type is enabled, merge the defaults and set the registration hook.
+ if ( $this->enabled ) {
add_action( 'init', array( $this, 'register_post_type' ) );
+ add_filter( 'ccb_core_post_type_map', array( $this, 'get_post_type_map' ) );
}
}
- /**
- * Get the dynamic CPT options based on
- * what the user may have set
- *
- * @param array $plugin_options The entire options array.
- * @return array
- */
- protected function get_user_cpt_options( $plugin_options ) {
-
- $user_cpt_options = array();
-
- if ( ! empty( $plugin_options[ $this->name ]['cpt_options']['name'] ) ) {
- $user_cpt_options['labels']['name'] = $plugin_options[ $this->name ]['cpt_options']['name'];
- $user_cpt_options['labels']['all_items'] = sprintf( __( 'All %s', 'ccb-core' ), $plugin_options[ $this->name ]['cpt_options']['name'] );
- $user_cpt_options['labels']['search_items'] = sprintf( __( 'Search %s', 'ccb-core' ), $plugin_options[ $this->name ]['cpt_options']['name'] );
- }
-
- if ( ! empty( $plugin_options[ $this->name ]['cpt_options']['singular_name'] ) ) {
- $user_cpt_options['labels']['singular_name'] = $plugin_options[ $this->name ]['cpt_options']['singular_name'];
- $user_cpt_options['labels']['add_new_item'] = sprintf( __( 'Add New %s', 'ccb-core' ), $plugin_options[ $this->name ]['cpt_options']['singular_name'] );
- $user_cpt_options['labels']['edit_item'] = sprintf( __( 'Edit %s', 'ccb-core' ), $plugin_options[ $this->name ]['cpt_options']['singular_name'] );
- $user_cpt_options['labels']['new_item'] = sprintf( __( 'New %s', 'ccb-core' ), $plugin_options[ $this->name ]['cpt_options']['singular_name'] );
- $user_cpt_options['labels']['view_item'] = sprintf( __( 'View %s', 'ccb-core' ), $plugin_options[ $this->name ]['cpt_options']['singular_name'] );
- }
-
- if ( ! empty( $plugin_options[ $this->name ]['cpt_options']['slug'] ) ) {
- $user_cpt_options['rewrite'] = array( 'slug' => $plugin_options[ $this->name ]['cpt_options']['slug'] );
- $user_cpt_options['has_archive'] = $plugin_options[ $this->name ]['cpt_options']['slug'];
- }
-
- // The remaining options are boolean, so directly set their values if the option is set.
- if ( isset( $plugin_options[ $this->name ]['cpt_options']['publicly_queryable'] ) ) {
- $user_cpt_options['publicly_queryable'] = $plugin_options[ $this->name ]['cpt_options']['publicly_queryable'];
- }
-
- if ( isset( $plugin_options[ $this->name ]['cpt_options']['exclude_from_search'] ) ) {
- $user_cpt_options['exclude_from_search'] = $plugin_options[ $this->name ]['cpt_options']['exclude_from_search'];
- }
-
- if ( isset( $plugin_options[ $this->name ]['cpt_options']['show_ui'] ) ) {
- $user_cpt_options['show_ui'] = $plugin_options[ $this->name ]['cpt_options']['show_ui'];
- }
-
- if ( isset( $plugin_options[ $this->name ]['cpt_options']['show_in_nav_menus'] ) ) {
- $user_cpt_options['show_in_nav_menus'] = $plugin_options[ $this->name ]['cpt_options']['show_in_nav_menus'];
- }
-
- return $user_cpt_options;
-
- }
-
/**
* Register the custom post type
*
@@ -106,7 +55,7 @@ protected function get_user_cpt_options( $plugin_options ) {
* @return void
*/
public function register_post_type() {
- register_post_type( $this->name, $this->cpt_options );
+ register_post_type( $this->name, $this->get_post_args() );
}
/**
@@ -118,12 +67,30 @@ public function get_post_type_object() {
return get_post_type_object( $this->name );
}
+ /**
+ * Setup the custom post type $args
+ *
+ * @since 1.0.0
+ * @return array $args for register_post_type
+ */
+ abstract public function get_post_args();
+
/**
* Setup the default CPT options
*
* @since 1.0.0
- * @return array Default options for register_post_type
+ * @param array $settings The settings definitions.
+ * @return array The configuration for the options settable by the user
+ */
+ abstract public function get_post_settings_definitions( $settings );
+
+ /**
+ * Define the mapping of CCB API fields to the Post fields
+ *
+ * @since 1.0.0
+ * @param array $map A collection of mappings from the API to WordPress.
+ * @return array
*/
- abstract public function get_cpt_defaults();
+ abstract public function get_post_type_map( $map );
}
diff --git a/includes/post-types/class-ccb-core-cpts.php b/includes/post-types/class-ccb-core-cpts.php
deleted file mode 100644
index fbee973..0000000
--- a/includes/post-types/class-ccb-core-cpts.php
+++ /dev/null
@@ -1,400 +0,0 @@
-
- */
-class CCB_Core_CPT {
-
- /**
- * The options we should use to register the groups CPT
- *
- * @since 0.9.0
- * @access protected
- * @var array $groups_cpt_options
- */
- protected $groups_cpt_options = array();
-
- /**
- * The options we should use to register the calendar CPT
- *
- * @since 0.9.0
- * @access protected
- * @var array $calendar_cpt_options
- */
- protected $calendar_cpt_options = array();
-
- /**
- * Initialize the class and set its properties.
- *
- * @since 0.9.0
- */
- public function __construct() {
-
- parent::__construct();
-
- }
-
- /**
- * Determine which CCB custom post types should be registered
- *
- * @access public
- * @since 0.9.0
- * @return void
- */
- public function initialize() {
-
- $settings = get_option( $this->plugin_settings_name );
-
- if ( isset( $settings['groups-enabled'] ) && $settings['groups-enabled'] == 1 ) {
-
- $this->groups_cpt_options['name'] = ( empty( $settings['groups-name'] ) ? 'Groups' : $settings['groups-name'] );
- $this->groups_cpt_options['slug'] = ( empty( $settings['groups-slug'] ) ? 'groups' : $settings['groups-slug'] );
- $this->groups_cpt_options['singular_name'] = rtrim( $this->groups_cpt_options['name'], 's' ); // this is ghetto
- $this->groups_cpt_options['exclude_from_search'] = ( $settings['groups-exclude-from-search'] == 'yes' ? true : false );
- $this->groups_cpt_options['publicly_queryable'] = ( $settings['groups-publicly-queryable'] == 'yes' ? true : false );
- $this->groups_cpt_options['show_ui'] = ( $settings['groups-show-ui'] == 'yes' ? true : false );
- $this->groups_cpt_options['show_in_nav_menus'] = ( $settings['groups-show-in-nav-menus'] == 'yes' ? true : false );
-
- $this->register_groups();
-
- }
-
- if ( isset( $settings['calendar-enabled'] ) && $settings['calendar-enabled'] == 1 ) {
-
- $this->calendar_cpt_options['name'] = ( empty( $settings['calendar-name'] ) ? 'Events' : $settings['calendar-name'] );
- $this->calendar_cpt_options['slug'] = ( empty( $settings['calendar-slug'] ) ? 'events' : $settings['calendar-slug'] );
- $this->calendar_cpt_options['singular_name'] = rtrim( $this->calendar_cpt_options['name'], 's' ); // this is ghetto
- $this->calendar_cpt_options['exclude_from_search'] = ( $settings['calendar-exclude-from-search'] == 'yes' ? true : false );
- $this->calendar_cpt_options['publicly_queryable'] = ( $settings['calendar-publicly-queryable'] == 'yes' ? true : false );
- $this->calendar_cpt_options['show_ui'] = ( $settings['calendar-show-ui'] == 'yes' ? true : false );
- $this->calendar_cpt_options['show_in_nav_menus'] = ( $settings['calendar-show-in-nav-menus'] == 'yes' ? true : false );
-
- $this->register_calendar();
-
- }
- }
-
- /**
- * Setup the CCB Groups custom post type and its taxonomies
- *
- * @access protected
- * @since 0.9.0
- * @return void
- */
- protected function register_groups() {
-
- register_post_type( $this->plugin_name . '-groups',
- array( 'labels' =>
- array(
- 'name' => $this->groups_cpt_options['name'],
- 'singular_name' => $this->groups_cpt_options['singular_name'],
- 'all_items' => __( 'All ' . $this->groups_cpt_options['name'], $this->plugin_name ),
- 'add_new' => __( 'Add New', $this->plugin_name ),
- 'add_new_item' => __( 'Add New ' . $this->groups_cpt_options['singular_name'], $this->plugin_name ),
- 'edit' => __( 'Edit', $this->plugin_name ),
- 'edit_item' => __( 'Edit ' . $this->groups_cpt_options['name'], $this->plugin_name ),
- 'new_item' => __( 'New ' . $this->groups_cpt_options['singular_name'], $this->plugin_name ),
- 'view_item' => __( 'View ' . $this->groups_cpt_options['singular_name'], $this->plugin_name ),
- 'search_items' => __( 'Search ' . $this->groups_cpt_options['singular_name'], $this->plugin_name ),
- 'not_found' => __( 'Nothing found in the Database.', $this->plugin_name ),
- 'not_found_in_trash' => __( 'Nothing found in Trash', $this->plugin_name ),
- 'parent_item_colon' => ''
- ),
- 'description' => __( 'These are the groups that are synchronized with your Church Community Builder software.', $this->plugin_name ),
- 'public' => true,
- 'publicly_queryable' => $this->groups_cpt_options['publicly_queryable'],
- 'exclude_from_search' => $this->groups_cpt_options['exclude_from_search'],
- 'show_ui' => $this->groups_cpt_options['show_ui'],
- 'show_in_nav_menus' => $this->groups_cpt_options['show_in_nav_menus'],
- 'query_var' => true,
- 'menu_position' => 8,
- 'menu_icon' => 'dashicons-groups',
- 'rewrite' => array( 'slug' => $this->groups_cpt_options['slug'] ),
- 'has_archive' => $this->groups_cpt_options['slug'],
- 'capability_type' => 'post',
- 'hierarchical' => false,
- 'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'custom-fields', 'sticky' ),
- )
- );
-
- $groups_taxonomies = self::get_groups_taxonomy_map();
- foreach ( $groups_taxonomies as $taxonomy_name=>$taxonomy ) {
- $taxonomy_options = array(
- 'hierarchical' => $taxonomy['hierarchical'],
- 'labels' => array(
- 'name' => $taxonomy['name_plural'],
- 'singular_name' => $taxonomy['name'],
- 'search_items' => "Search {$taxonomy['name_plural']}",
- 'all_items' => "All {$taxonomy['name_plural']}",
- 'parent_item' => "Parent {$taxonomy['name']}",
- 'parent_item_colon' => "Parent {$taxonomy['name']}:",
- 'edit_item' => "Edit {$taxonomy['name']}",
- 'update_item' => "Update {$taxonomy['name']}",
- 'add_new_item' => "Add New {$taxonomy['name']}",
- 'new_item_name' => "New {$taxonomy['name']}"
- ),
- 'show_admin_column' => true,
- 'show_ui' => true,
- 'query_var' => true,
- );
-
- register_taxonomy( $taxonomy_name, "{$this->plugin_name}-groups", $taxonomy_options );
- }
-
- }
-
- /**
- * Setup the CCB Events custom post type and its taxonomies
- *
- * @access protected
- * @since 0.9.0
- * @return void
- */
- protected function register_calendar() {
-
- register_post_type( $this->plugin_name . '-calendar',
- array( 'labels' =>
- array(
- 'name' => $this->calendar_cpt_options['name'],
- 'singular_name' => $this->calendar_cpt_options['singular_name'],
- 'all_items' => __( 'All ' . $this->calendar_cpt_options['name'], $this->plugin_name ),
- 'add_new' => __( 'Add New', $this->plugin_name ),
- 'add_new_item' => __( 'Add New ' . $this->calendar_cpt_options['singular_name'], $this->plugin_name ),
- 'edit' => __( 'Edit', $this->plugin_name ),
- 'edit_item' => __( 'Edit ' . $this->calendar_cpt_options['name'], $this->plugin_name ),
- 'new_item' => __( 'New ' . $this->calendar_cpt_options['singular_name'], $this->plugin_name ),
- 'view_item' => __( 'View ' . $this->calendar_cpt_options['singular_name'], $this->plugin_name ),
- 'search_items' => __( 'Search ' . $this->calendar_cpt_options['singular_name'], $this->plugin_name ),
- 'not_found' => __( 'Nothing found in the Database.', $this->plugin_name ),
- 'not_found_in_trash' => __( 'Nothing found in Trash', $this->plugin_name ),
- 'parent_item_colon' => ''
- ),
- 'description' => __( 'These are the calendar that are synchronized with your Church Community Builder software.', $this->plugin_name ),
- 'public' => true,
- 'publicly_queryable' => $this->calendar_cpt_options['publicly_queryable'],
- 'exclude_from_search' => $this->calendar_cpt_options['exclude_from_search'],
- 'show_ui' => $this->calendar_cpt_options['show_ui'],
- 'show_in_nav_menus' => $this->calendar_cpt_options['show_in_nav_menus'],
- 'query_var' => true,
- 'menu_position' => 8,
- 'menu_icon' => 'dashicons-calendar',
- 'rewrite' => array( 'slug' => $this->calendar_cpt_options['slug'] ),
- 'has_archive' => $this->calendar_cpt_options['slug'],
- 'capability_type' => 'post',
- 'hierarchical' => false,
- 'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'custom-fields', 'sticky' ),
- )
- );
-
- $calendar_taxonomies = self::get_calendar_taxonomy_map();
- foreach ( $calendar_taxonomies as $taxonomy_name=>$taxonomy ) {
- $taxonomy_options = array(
- 'hierarchical' => $taxonomy['hierarchical'],
- 'labels' => array(
- 'name' => $taxonomy['name_plural'],
- 'singular_name' => $taxonomy['name'],
- 'search_items' => "Search {$taxonomy['name_plural']}",
- 'all_items' => "All {$taxonomy['name_plural']}",
- 'parent_item' => "Parent {$taxonomy['name']}",
- 'parent_item_colon' => "Parent {$taxonomy['name']}:",
- 'edit_item' => "Edit {$taxonomy['name']}",
- 'update_item' => "Update {$taxonomy['name']}",
- 'add_new_item' => "Add New {$taxonomy['name']}",
- 'new_item_name' => "New {$taxonomy['name']}"
- ),
- 'show_admin_column' => true,
- 'show_ui' => true,
- 'query_var' => true,
- );
-
- register_taxonomy( $taxonomy_name, "{$this->plugin_name}-calendar", $taxonomy_options );
- }
-
- }
-
- /**
- * Helper method to hold a map of structure from the groups custom post
- * type custom fields to the API schema
- *
- * @static
- * @access public
- * @return array
- */
- public static function get_groups_custom_fields_map() {
- return array(
- 'group_main_leader' => array(
- 'api_mapping' => 'main_leader',
- 'data_type' => 'object',
- 'child_object' => array(
- 'leader_full_name' => array(
- 'api_mapping' => 'full_name',
- 'data_type' => 'string'
- ),
- 'leader_email' => array(
- 'api_mapping' => 'email',
- 'data_type' => 'string'
- )
- )
- ),
- 'group_calendar_feed' => array(
- 'api_mapping' => 'calendar_feed',
- 'data_type' => 'string',
- ),
- 'addresses' => array(
- 'api_mapping' => 'addresses',
- 'data_type' => 'object',
- 'child_object' => array(
- 'address' => array(
- 'api_mapping' => 'address',
- 'data_type' => 'object',
- 'child_object' => array(
- 'longitude' => array(
- 'api_mapping' => 'longitude',
- 'data_type' => 'string'
- ),
- 'latitude' => array(
- 'api_mapping' => 'latitude',
- 'data_type' => 'string'
- ),
- 'address_line_1' => array(
- 'api_mapping' => 'line_1',
- 'data_type' => 'string'
- ),
- 'address_line_2' => array(
- 'api_mapping' => 'line_2',
- 'data_type' => 'string'
- ),
- )
- ),
- ),
- ),
- );
- }
-
- /**
- * Helper method to hold a map of structure from the calendar custom post
- * type custom fields to the API schema
- *
- * @static
- * @access public
- * @return array
- */
- public static function get_calendar_custom_fields_map() {
- return array(
- 'calendar_date' => array(
- 'api_mapping' => 'date',
- 'data_type' => 'string',
- ),
- 'calendar_start_time' => array(
- 'api_mapping' => 'start_time',
- 'data_type' => 'string',
- ),
- 'calendar_end_time' => array(
- 'api_mapping' => 'end_time',
- 'data_type' => 'string',
- ),
- 'calendar_duration' => array(
- 'api_mapping' => 'event_duration',
- 'data_type' => 'int',
- ),
- );
- }
-
- /**
- * Helper method to hold a map of structure from the groups custom post
- * type taxonomies to the API schema
- *
- * @static
- * @access public
- * @return array
- */
- public static function get_groups_taxonomy_map() {
-
- return array(
- 'group_areas' => array(
- 'name' => 'Area',
- 'name_plural' => 'Areas',
- 'hierarchical' => true,
- 'api_mapping' => 'area'
- ),
- 'group_days' => array(
- 'name' => 'Day',
- 'name_plural' => 'Days',
- 'hierarchical' => true,
- 'api_mapping' => 'meeting_day'
- ),
- 'group_types' => array(
- 'name' => 'Type',
- 'name_plural' => 'Types',
- 'hierarchical' => true,
- 'api_mapping' => 'group_type'
- ),
- 'group_times' => array(
- 'name' => 'Time',
- 'name_plural' => 'Times',
- 'hierarchical' => true,
- 'api_mapping' => 'meeting_time'
- ),
- 'group_departments' => array(
- 'name' => 'Department',
- 'name_plural' => 'Departments',
- 'hierarchical' => true,
- 'api_mapping' => 'department'
- ),
- 'group_tags' => array(
- 'name' => 'Group Tag',
- 'name_plural' => 'Group Tags',
- 'hierarchical' => false,
- 'api_mapping' => array(
- 'childcare_provided' => 'Childcare Provided'
- )
- ),
- );
- }
-
- /**
- * Helper method to hold a map of structure from the events custom post
- * type taxonomies to the API schema
- *
- * @static
- * @access public
- * @return array
- */
- public static function get_calendar_taxonomy_map() {
-
- return array(
- 'calendar_event_type' => array(
- 'name' => 'Type',
- 'name_plural' => 'Types',
- 'hierarchical' => true,
- 'api_mapping' => 'event_type'
- ),
- 'calendar_group_name' => array(
- 'name' => 'Group Name',
- 'name_plural' => 'Group Names',
- 'hierarchical' => true,
- 'api_mapping' => 'group_name'
- ),
- 'calendar_grouping_name' => array(
- 'name' => 'Grouping Name',
- 'name_plural' => 'Grouping Names',
- 'hierarchical' => true,
- 'api_mapping' => 'grouping_name'
- ),
- );
- }
-
-}
diff --git a/includes/post-types/class-ccb-core-group.php b/includes/post-types/class-ccb-core-group.php
index 964b8be..2e5723b 100644
--- a/includes/post-types/class-ccb-core-group.php
+++ b/includes/post-types/class-ccb-core-group.php
@@ -23,48 +23,245 @@ class CCB_Core_Group extends CCB_Core_CPT {
*
* @var string
*/
- public $name = 'ccb-core-groups';
+ public $name = 'ccb_core_groups';
/**
- * Setup the default CPT options
+ * Initialize the class
+ */
+ public function __construct() {
+ add_filter( 'ccb_core_entity_insert_allowed', [ $this, 'entity_insert_update_allowed' ], 10, 4 );
+ add_filter( 'ccb_core_entity_update_allowed', [ $this, 'entity_insert_update_allowed' ], 10, 4 );
+ add_filter( 'ccb_core_after_insert_update_post', [ $this, 'attach_group_image' ], 10, 5 );
+
+ $options = CCB_Core_Helpers::instance()->get_options();
+ $this->enabled = ! empty( $options['groups_enabled'] ) ? true : false;
+ parent::__construct();
+ }
+
+ /**
+ * Setup the custom post type args
*
* @since 1.0.0
- * @return array Default options for register_post_type
+ * @return array $args for register_post_type
*/
- public function get_cpt_defaults() {
+ public function get_post_args() {
+
+ $options = CCB_Core_Helpers::instance()->get_options();
+ $plural = ! empty( $options['groups_name'] ) ? $options['groups_name'] : __( 'Groups', 'ccb-core' );
+ $singular = ! empty( $options['groups_name_singular'] ) ? $options['groups_name_singular'] : __( 'Group', 'ccb-core' );
+ $rewrite = ! empty( $options['groups_slug'] ) ? [ 'slug' => sanitize_title( $options['groups_slug'] ) ] : [ 'slug' => 'groups' ];
+ $has_archive = ! empty( $options['groups_slug'] ) ? sanitize_title( $options['groups_slug'] ) : 'groups';
+ $exclude_from_search = ! empty( $options['groups_exclude_from_search'] ) && 'yes' === $options['groups_exclude_from_search'] ? true : false;
+ $publicly_queryable = ! empty( $options['groups_publicly_queryable'] ) && 'no' === $options['groups_publicly_queryable'] ? false : true;
+ $show_ui = ! empty( $options['groups_show_ui'] ) && 'no' === $options['groups_show_ui'] ? false : true;
+ $show_in_nav_menus = ! empty( $options['groups_show_in_nav_menus'] ) && 'yes' === $options['groups_show_in_nav_menus'] ? true : false;
+
return array(
'labels' => array(
- 'name' => __( 'Groups', 'ccb-core' ),
- 'singular_name' => __( 'Group', 'ccb-core' ),
- 'all_items' => __( 'All Groups', 'ccb-core' ),
+ 'name' => $plural,
+ 'singular_name' => $singular,
+ 'all_items' => sprintf( __( 'All %s', 'ccb-core' ), $plural ),
'add_new' => __( 'Add New', 'ccb-core' ),
- 'add_new_item' => __( 'Add New Group', 'ccb-core' ),
+ 'add_new_item' => sprintf( __( 'Add New %s', 'ccb-core' ), $singular ),
'edit' => __( 'Edit', 'ccb-core' ),
- 'edit_item' => __( 'Edit Group', 'ccb-core' ),
- 'new_item' => __( 'New Group', 'ccb-core' ),
- 'view_item' => __( 'View Group', 'ccb-core' ),
- 'search_items' => __( 'Search Groups', 'ccb-core' ),
+ 'edit_item' => sprintf( __( 'Edit %s', 'ccb-core' ), $singular ),
+ 'new_item' => sprintf( __( 'New %s', 'ccb-core' ), $singular ),
+ 'view_item' => sprintf( __( 'View %s', 'ccb-core' ), $singular ),
+ 'search_items' => sprintf( __( 'Search %s', 'ccb-core' ), $plural ),
'not_found' => __( 'Nothing found in the Database.', 'ccb-core' ),
'not_found_in_trash' => __( 'Nothing found in Trash', 'ccb-core' ),
'parent_item_colon' => '',
),
- 'description' => __( 'These are the groups that are synchronized with your Church Community Builder software.', 'ccb-core' ),
+ 'description' => sprintf( __( 'These are the %s that are synchronized with your Church Community Builder software.', 'ccb-core' ), $plural ),
'public' => true,
- 'publicly_queryable' => true,
- 'exclude_from_search' => false,
- 'show_ui' => true,
- 'show_in_nav_menus' => true,
+ 'publicly_queryable' => $publicly_queryable,
+ 'exclude_from_search' => $exclude_from_search,
+ 'show_ui' => $show_ui,
+ 'show_in_nav_menus' => $show_in_nav_menus,
'query_var' => true,
'menu_position' => 8,
'menu_icon' => 'dashicons-groups',
- 'rewrite' => array( 'slug' => 'groups' ),
- 'has_archive' => 'groups',
+ 'rewrite' => $rewrite,
+ 'has_archive' => $has_archive,
'capability_type' => 'post',
'hierarchical' => false,
'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'custom-fields', 'sticky' ),
);
+
}
+ /**
+ * Configure the options that users are allowed to set
+ *
+ * @since 1.0.0
+ * @param array $settings The settings definitions.
+ * @return array
+ */
+ public function get_post_settings_definitions( $settings ) {
+
+ $settings['ccb_core_settings_groups'] = array(
+ 'page_title' => esc_html__( 'Groups', 'ccb-core' ),
+ 'sections' => array(
+ 'groups' => array(
+ 'section_title' => esc_html__( 'Groups', 'ccb-core' ),
+ 'fields' => array(
+ 'groups_enabled' => array(
+ 'field_title' => esc_html__( 'Enable Groups', 'ccb-core' ),
+ 'field_render_function' => 'render_switch',
+ 'field_validation' => 'switch',
+ ),
+ 'groups_name' => array(
+ 'field_title' => esc_html__( 'Groups Display Name (Plural)', 'ccb-core' ),
+ 'field_render_function' => 'render_text',
+ 'field_placeholder' => esc_html__( 'Groups', 'ccb-core' ),
+ 'field_validation' => 'alphanumeric_extended',
+ 'field_attributes' => array( 'data-requires' => '{"groups_enabled":1}' ),
+ 'field_tooltip' => esc_html__( 'This is what you call the groups in your church (i.e. Home Groups, Connections, Life Groups, etc.).', 'ccb-core' ),
+ ),
+ 'groups_name_singular' => array(
+ 'field_title' => esc_html__( 'Groups Display Name (Singular)', 'ccb-core' ),
+ 'field_render_function' => 'render_text',
+ 'field_placeholder' => esc_html__( 'Group', 'ccb-core' ),
+ 'field_validation' => 'alphanumeric_extended',
+ 'field_attributes' => array( 'data-requires' => '{"groups_enabled":1}' ),
+ 'field_tooltip' => esc_html__( 'This is the singular name of what you call the groups in your church (i.e. Home Group, Connection, Life Group, etc.).', 'ccb-core' ),
+ ),
+ 'groups_slug' => array(
+ 'field_title' => esc_html__( 'Groups URL Name', 'ccb-core' ),
+ 'field_render_function' => 'render_text',
+ 'field_placeholder' => 'groups',
+ 'field_validation' => 'slug',
+ 'field_attributes' => array( 'data-requires' => '{"groups_enabled":1}' ),
+ 'field_tooltip' => esc_html__( 'This is typically where your theme will display all the groups. WordPress calls this a "slug".', 'ccb-core' ),
+ ),
+ 'groups_import_images' => array(
+ 'field_title' => esc_html__( 'Also Import Group Images?', 'ccb-core' ),
+ 'field_render_function' => 'render_radio',
+ 'field_options' => array(
+ 'yes' => esc_html__( 'Yes', 'ccb-core' ),
+ 'no' => esc_html__( 'No', 'ccb-core' ),
+ ),
+ 'field_validation' => '',
+ 'field_default' => 'no',
+ 'field_attributes' => array( 'data-requires' => '{"groups_enabled":1}' ),
+ 'field_tooltip' => sprintf(
+ esc_html__(
+ 'This will download the CCB Group Image and attach it as a Featured Image.%s
+ If you don\'t need group images, then disabling this feature will speed up the synchronization.',
+ 'ccb-core' ),
+ '
'
+ ),
+ ),
+ 'groups_advanced' => array(
+ 'field_title' => esc_html__( 'Enable Advanced Settings (Optional)', 'ccb-core' ),
+ 'field_render_function' => 'render_switch',
+ 'field_validation' => 'switch',
+ 'field_attributes' => array( 'data-requires' => '{"groups_enabled":1}' ),
+ ),
+ 'groups_exclude_from_search' => array(
+ 'field_title' => esc_html__( 'Exclude From Search?', 'ccb-core' ),
+ 'field_render_function' => 'render_radio',
+ 'field_options' => array(
+ 'yes' => esc_html__( 'Yes', 'ccb-core' ),
+ 'no' => esc_html__( 'No', 'ccb-core' ),
+ ),
+ 'field_validation' => '',
+ 'field_default' => 'no',
+ 'field_attributes' => array( 'data-requires' => '{"groups_enabled":1,"groups_advanced":1}' ),
+ ),
+ 'groups_publicly_queryable' => array(
+ 'field_title' => esc_html__( 'Publicly Queryable?', 'ccb-core' ),
+ 'field_render_function' => 'render_radio',
+ 'field_options' => array(
+ 'yes' => esc_html__( 'Yes', 'ccb-core' ),
+ 'no' => esc_html__( 'No', 'ccb-core' ),
+ ),
+ 'field_validation' => '',
+ 'field_default' => 'yes',
+ 'field_attributes' => array( 'data-requires' => '{"groups_enabled":1,"groups_advanced":1}' ),
+ ),
+ 'groups_show_ui' => array(
+ 'field_title' => esc_html__( 'Show In Admin UI?', 'ccb-core' ),
+ 'field_render_function' => 'render_radio',
+ 'field_options' => array(
+ 'yes' => esc_html__( 'Yes', 'ccb-core' ),
+ 'no' => esc_html__( 'No', 'ccb-core' ),
+ ),
+ 'field_validation' => '',
+ 'field_default' => 'yes',
+ 'field_attributes' => array( 'data-requires' => '{"groups_enabled":1,"groups_advanced":1}' ),
+ ),
+ 'groups_show_in_nav_menus' => array(
+ 'field_title' => esc_html__( 'Show In Navigation Menus?', 'ccb-core' ),
+ 'field_render_function' => 'render_radio',
+ 'field_options' => array(
+ 'yes' => esc_html__( 'Yes', 'ccb-core' ),
+ 'no' => esc_html__( 'No', 'ccb-core' ),
+ ),
+ 'field_validation' => '',
+ 'field_default' => 'no',
+ 'field_attributes' => array( 'data-requires' => '{"groups_enabled":1,"groups_advanced":1}' ),
+ ),
+ ),
+ ),
+ ),
+ );
+
+ return $settings;
+
+ }
+
+ /**
+ * Define the mapping of CCB API fields to the Post fields
+ *
+ * @since 1.0.0
+ * @param array $map A collection of mappings from the API to WordPress.
+ * @return array
+ */
+ public function get_post_type_map( $map ) {
+ if ( $this->enabled ) {
+ $options = CCB_Core_Helpers::instance()->get_options();
+ $include_image_link = ! empty( $options['groups_import_images'] ) && 'yes' === $options['groups_import_images'] ? true : false;
+
+ $map[ $this->name ] = [
+ 'service' => 'group_profiles',
+ 'data' => [
+ 'include_participants' => false,
+ 'include_image_link' => $include_image_link,
+ ],
+ 'nodes' => [ 'groups', 'group' ],
+ 'fields' => [
+ 'name' => 'post_title',
+ 'description' => 'post_content',
+ 'main_leader' => 'post_meta',
+ 'calendar_feed' => 'post_meta',
+ 'current_members' => 'post_meta',
+ 'group_capacity' => 'post_meta',
+ 'addresses' => 'post_meta',
+ ],
+ ];
+ }
+ return $map;
+ }
+
+ public function entity_insert_update_allowed( $allowed, $entity_id, $entity, $post_type ) {
+ if ( $this->name === $post_type ) {
+ // Only allow active, publicly listed groups to be imported.
+ if ( 'true' === (string) $entity->inactive || 'false' === (string) $entity->public_search_listed ) {
+ $allowed = false;
+ }
+ }
+ return $allowed;
+ }
+
+ public function attach_group_image( $entity, $settings, $args, $post_type, $post_id ) {
+ if ( $this->name === $post_type && $settings['data']['include_image_link'] ) {
+ $image_url = (string) $entity->image;
+ if ( $image_url ) {
+ CCB_Core_Helpers::instance()->download_image( $image_url, $args['post_title'], $post_id );
+ }
+ }
+ }
}
new CCB_Core_Group();
diff --git a/includes/taxonomies/class-ccb-core-calendar-event-type.php b/includes/taxonomies/class-ccb-core-calendar-event-type.php
index 7ba1615..62a71a2 100644
--- a/includes/taxonomies/class-ccb-core-calendar-event-type.php
+++ b/includes/taxonomies/class-ccb-core-calendar-event-type.php
@@ -30,7 +30,7 @@ class CCB_Core_Calendar_Event_Type extends CCB_Core_Taxonomy {
*
* @var array
*/
- public $object_types = array( 'ccb-core-calendar' );
+ public $object_types = array( 'ccb_core_calendar' );
/**
* Setup the default taxonomy mappings
@@ -38,7 +38,7 @@ class CCB_Core_Calendar_Event_Type extends CCB_Core_Taxonomy {
* @since 1.0.0
* @return array Default options for register_taxonomy
*/
- public static function get_taxonomy_mapping() {
+ public static function get_taxonomy_args() {
return array(
'labels' => array(
'name' => __( 'Types', 'ccb-core' ),
diff --git a/includes/taxonomies/class-ccb-core-calendar-group-name.php b/includes/taxonomies/class-ccb-core-calendar-group-name.php
index cfaa411..9e6e02e 100644
--- a/includes/taxonomies/class-ccb-core-calendar-group-name.php
+++ b/includes/taxonomies/class-ccb-core-calendar-group-name.php
@@ -30,7 +30,7 @@ class CCB_Core_Calendar_Group_Name extends CCB_Core_Taxonomy {
*
* @var array
*/
- public $object_types = array( 'ccb-core-calendar' );
+ public $object_types = array( 'ccb_core_calendar' );
/**
* Setup the default taxonomy mappings
@@ -38,7 +38,7 @@ class CCB_Core_Calendar_Group_Name extends CCB_Core_Taxonomy {
* @since 1.0.0
* @return array Default options for register_taxonomy
*/
- public static function get_taxonomy_mapping() {
+ public static function get_taxonomy_args() {
return array(
'labels' => array(
'name' => __( 'Group Names', 'ccb-core' ),
diff --git a/includes/taxonomies/class-ccb-core-calendar-grouping-name.php b/includes/taxonomies/class-ccb-core-calendar-grouping-name.php
index 01fe316..7547fb3 100644
--- a/includes/taxonomies/class-ccb-core-calendar-grouping-name.php
+++ b/includes/taxonomies/class-ccb-core-calendar-grouping-name.php
@@ -30,7 +30,7 @@ class CCB_Core_Calendar_Grouping_Name extends CCB_Core_Taxonomy {
*
* @var array
*/
- public $object_types = array( 'ccb-core-calendar' );
+ public $object_types = array( 'ccb_core_calendar' );
/**
* Setup the default taxonomy mappings
@@ -38,7 +38,7 @@ class CCB_Core_Calendar_Grouping_Name extends CCB_Core_Taxonomy {
* @since 1.0.0
* @return array Default options for register_taxonomy
*/
- public static function get_taxonomy_mapping() {
+ public static function get_taxonomy_args() {
return array(
'labels' => array(
'name' => __( 'Grouping Names', 'ccb-core' ),
diff --git a/includes/taxonomies/class-ccb-core-group-area.php b/includes/taxonomies/class-ccb-core-group-area.php
index f330957..87c57f9 100644
--- a/includes/taxonomies/class-ccb-core-group-area.php
+++ b/includes/taxonomies/class-ccb-core-group-area.php
@@ -30,7 +30,7 @@ class CCB_Core_Group_Area extends CCB_Core_Taxonomy {
*
* @var array
*/
- public $object_types = array( 'ccb-core-groups' );
+ public $object_types = array( 'ccb_core_groups' );
/**
* Setup the default taxonomy mappings
@@ -38,7 +38,7 @@ class CCB_Core_Group_Area extends CCB_Core_Taxonomy {
* @since 1.0.0
* @return array Default options for register_taxonomy
*/
- public static function get_taxonomy_mapping() {
+ public static function get_taxonomy_args() {
return array(
'labels' => array(
'name' => __( 'Areas', 'ccb-core' ),
diff --git a/includes/taxonomies/class-ccb-core-group-day.php b/includes/taxonomies/class-ccb-core-group-day.php
index 819474f..f17891f 100644
--- a/includes/taxonomies/class-ccb-core-group-day.php
+++ b/includes/taxonomies/class-ccb-core-group-day.php
@@ -30,7 +30,7 @@ class CCB_Core_Group_Day extends CCB_Core_Taxonomy {
*
* @var array
*/
- public $object_types = array( 'ccb-core-groups' );
+ public $object_types = array( 'ccb_core_groups' );
/**
* Setup the default taxonomy mappings
@@ -38,7 +38,7 @@ class CCB_Core_Group_Day extends CCB_Core_Taxonomy {
* @since 1.0.0
* @return array Default options for register_taxonomy
*/
- public static function get_taxonomy_mapping() {
+ public static function get_taxonomy_args() {
return array(
'labels' => array(
'name' => __( 'Days', 'ccb-core' ),
diff --git a/includes/taxonomies/class-ccb-core-group-department.php b/includes/taxonomies/class-ccb-core-group-department.php
index e051967..d46f386 100644
--- a/includes/taxonomies/class-ccb-core-group-department.php
+++ b/includes/taxonomies/class-ccb-core-group-department.php
@@ -30,7 +30,7 @@ class CCB_Core_Group_Department extends CCB_Core_Taxonomy {
*
* @var array
*/
- public $object_types = array( 'ccb-core-groups' );
+ public $object_types = array( 'ccb_core_groups' );
/**
* Setup the default taxonomy mappings
@@ -38,7 +38,7 @@ class CCB_Core_Group_Department extends CCB_Core_Taxonomy {
* @since 1.0.0
* @return array Default options for register_taxonomy
*/
- public static function get_taxonomy_mapping() {
+ public static function get_taxonomy_args() {
return array(
'labels' => array(
'name' => __( 'Departments', 'ccb-core' ),
diff --git a/includes/taxonomies/class-ccb-core-group-tag.php b/includes/taxonomies/class-ccb-core-group-tag.php
index 7734efb..7d5aaa2 100644
--- a/includes/taxonomies/class-ccb-core-group-tag.php
+++ b/includes/taxonomies/class-ccb-core-group-tag.php
@@ -30,7 +30,7 @@ class CCB_Core_Group_Tag extends CCB_Core_Taxonomy {
*
* @var array
*/
- public $object_types = array( 'ccb-core-groups' );
+ public $object_types = array( 'ccb_core_groups' );
/**
* Setup the default taxonomy mappings
@@ -38,7 +38,7 @@ class CCB_Core_Group_Tag extends CCB_Core_Taxonomy {
* @since 1.0.0
* @return array Default options for register_taxonomy
*/
- public static function get_taxonomy_mapping() {
+ public static function get_taxonomy_args() {
return array(
'labels' => array(
'name' => __( 'Group Tags', 'ccb-core' ),
@@ -56,7 +56,9 @@ public static function get_taxonomy_mapping() {
'show_admin_column' => true,
'show_ui' => true,
'query_var' => true,
- 'api_mapping' => array( 'childcare_provided' => __( 'Childcare Provided' ) ), // The field key from the CCB API.
+ 'api_mapping' => array(
+ 'childcare_provided' => __( 'Childcare Provided', 'ccb-core' ), // The field key from the CCB API.
+ ),
);
}
diff --git a/includes/taxonomies/class-ccb-core-group-time.php b/includes/taxonomies/class-ccb-core-group-time.php
index 29bc51c..916f281 100644
--- a/includes/taxonomies/class-ccb-core-group-time.php
+++ b/includes/taxonomies/class-ccb-core-group-time.php
@@ -30,7 +30,7 @@ class CCB_Core_Group_Time extends CCB_Core_Taxonomy {
*
* @var array
*/
- public $object_types = array( 'ccb-core-groups' );
+ public $object_types = array( 'ccb_core_groups' );
/**
* Setup the default taxonomy mappings
@@ -38,7 +38,7 @@ class CCB_Core_Group_Time extends CCB_Core_Taxonomy {
* @since 1.0.0
* @return array Default options for register_taxonomy
*/
- public static function get_taxonomy_mapping() {
+ public static function get_taxonomy_args() {
return array(
'labels' => array(
'name' => __( 'Times', 'ccb-core' ),
diff --git a/includes/taxonomies/class-ccb-core-group-type.php b/includes/taxonomies/class-ccb-core-group-type.php
index 156036d..ab104a1 100644
--- a/includes/taxonomies/class-ccb-core-group-type.php
+++ b/includes/taxonomies/class-ccb-core-group-type.php
@@ -30,7 +30,7 @@ class CCB_Core_Group_Type extends CCB_Core_Taxonomy {
*
* @var array
*/
- public $object_types = array( 'ccb-core-groups' );
+ public $object_types = array( 'ccb_core_groups' );
/**
* Setup the default taxonomy mappings
@@ -38,7 +38,7 @@ class CCB_Core_Group_Type extends CCB_Core_Taxonomy {
* @since 1.0.0
* @return array Default options for register_taxonomy
*/
- public static function get_taxonomy_mapping() {
+ public static function get_taxonomy_args() {
return array(
'labels' => array(
'name' => __( 'Types', 'ccb-core' ),
diff --git a/includes/taxonomies/class-ccb-core-taxonomy.php b/includes/taxonomies/class-ccb-core-taxonomy.php
index aff5458..58ffeaa 100644
--- a/includes/taxonomies/class-ccb-core-taxonomy.php
+++ b/includes/taxonomies/class-ccb-core-taxonomy.php
@@ -37,6 +37,7 @@ abstract class CCB_Core_Taxonomy {
*/
public function __construct() {
add_action( 'init', array( $this, 'register_taxonomy' ) );
+ add_filter( 'ccb_core_taxonomy_map', [ $this, 'get_taxonomy_map' ] );
}
/**
@@ -45,12 +46,30 @@ public function __construct() {
* @return void
*/
public function register_taxonomy() {
- register_taxonomy( $this->name, $this->object_types, static::get_taxonomy_mapping() );
+ register_taxonomy( $this->name, $this->object_types, static::get_taxonomy_args() );
+ }
+
+ /**
+ * Define the mapping of CCB API fields to this taxonomy
+ *
+ * @since 1.0.0
+ * @param array $map A collection of mappings from the API to WordPress.
+ * @return array
+ */
+ public function get_taxonomy_map( $map ) {
+ if ( ! empty( $this->object_types ) ) {
+ foreach ( $this->object_types as $object_type ) {
+ $taxonomy_args = static::get_taxonomy_args();
+ $hierarchical = ! empty( $taxonomy_args['hierarchical'] ) ? 'hierarchical' : 'nonhierarchical';
+ $map[ $object_type ]['taxonomies'][ $hierarchical ][ $this->name ] = $taxonomy_args['api_mapping'];
+ }
+ }
+ return $map;
}
/**
* Register the taxonomy.
*/
- abstract public static function get_taxonomy_mapping();
+ abstract public static function get_taxonomy_args();
}
diff --git a/js/ccb-core-admin.js b/js/ccb-core-admin.js
index a83ee38..8e62f9b 100644
--- a/js/ccb-core-admin.js
+++ b/js/ccb-core-admin.js
@@ -89,7 +89,7 @@
var data = {
'action': 'get_latest_sync',
- 'nextNonce': CCB_CORE_SETTINGS.nextNonce
+ 'nonce': CCB_CORE_SETTINGS.nonce
};
$.post(ajaxurl, data, function(response) {
@@ -108,7 +108,7 @@
var data = {
'action': 'poll_sync',
- 'nextNonce': CCB_CORE_SETTINGS.nextNonce
+ 'nonce': CCB_CORE_SETTINGS.nonce
};
$.post(ajaxurl, data, function(response) {
@@ -145,7 +145,7 @@
var data = {
'action': 'test_credentials',
- 'nextNonce': CCB_CORE_SETTINGS.nextNonce
+ 'nonce': CCB_CORE_SETTINGS.nonce
};
$.post(ajaxurl, data, function(response) {
@@ -154,7 +154,7 @@
$spinner.removeClass('is-active');
if (response.success === false) {
- $testLoginWrapper.append('
' + response.message + '
');
+ $testLoginWrapper.append('
' + response.data + '
');
}
else if (typeof response.services !== 'undefined' && response.services.length > 0) {
@@ -187,7 +187,7 @@
var data = {
'action': 'sync',
- 'nextNonce': CCB_CORE_SETTINGS.nextNonce
+ 'nonce': CCB_CORE_SETTINGS.nonce
};
$.post(ajaxurl, data, function(response) {
diff --git a/lib/class-ccb-core-vendor-encryption.php b/lib/class-ccb-core-vendor-encryption.php
index eb82917..f4d36bb 100644
--- a/lib/class-ccb-core-vendor-encryption.php
+++ b/lib/class-ccb-core-vendor-encryption.php
@@ -114,16 +114,20 @@ public function encrypt( $data, $key ) {
* @returns array An array of keys ( a cipher key, a mac key, and a IV )
*/
protected function get_keys( $salt, $key ) {
- $iv_size = mcrypt_get_iv_size( $this->cipher, $this->mode );
- $key_size = mcrypt_get_key_size( $this->cipher, $this->mode );
- $length = 2 * $key_size + $iv_size;
-
- $key = $this->pbkdf2( 'sha512', $key, $salt, $this->rounds, $length );
-
- $cipher_key = substr( $key, 0, $key_size );
- $mac_key = substr( $key, $key_size, $key_size );
- $iv = substr( $key, 2 * $key_size );
- return array( $cipher_key, $mac_key, $iv );
+ if ( function_exists( 'mcrypt_get_iv_size' ) ) {
+ $iv_size = mcrypt_get_iv_size( $this->cipher, $this->mode );
+ $key_size = mcrypt_get_key_size( $this->cipher, $this->mode );
+ $length = 2 * $key_size + $iv_size;
+
+ $key = $this->pbkdf2( 'sha512', $key, $salt, $this->rounds, $length );
+
+ $cipher_key = substr( $key, 0, $key_size );
+ $mac_key = substr( $key, $key_size, $key_size );
+ $iv = substr( $key, 2 * $key_size );
+ return array( $cipher_key, $mac_key, $iv );
+ } else {
+ return false;
+ }
}
/**
From 52afd02204ca408b91a877dd02adbfd852396915 Mon Sep 17 00:00:00 2001
From: Jared Cobb
Date: Mon, 1 Jan 2018 11:18:07 -0700
Subject: [PATCH 03/16] Final commit for main refactor
---
README.txt | 2 +-
ccb-core.php | 7 +-
css/ccb-core-admin.css | 21 +-
includes/class-ccb-core-activator.php | 13 +
includes/class-ccb-core-admin-ajax.php | 158 ++-
includes/class-ccb-core-api-old.php | 961 ------------------
includes/class-ccb-core-api.php | 54 +-
includes/class-ccb-core-cron.php | 113 ++
includes/class-ccb-core-helpers.php | 41 +-
includes/class-ccb-core-plugin.php | 146 ---
includes/class-ccb-core-settings-field.php | 28 +-
includes/class-ccb-core-settings-page.php | 4 +-
includes/class-ccb-core-settings-section.php | 12 +-
includes/class-ccb-core-settings.php | 15 +-
includes/class-ccb-core-synchronizer.php | 609 +++++++++--
includes/class-ccb-core.php | 8 +
.../post-types/class-ccb-core-calendar.php | 26 +-
includes/post-types/class-ccb-core-cpt.php | 14 +-
includes/post-types/class-ccb-core-group.php | 37 +-
.../taxonomies/class-ccb-core-group-day.php | 2 +
.../taxonomies/class-ccb-core-taxonomy.php | 2 +-
js/ccb-core-admin.js | 139 +--
22 files changed, 986 insertions(+), 1426 deletions(-)
delete mode 100644 includes/class-ccb-core-api-old.php
create mode 100644 includes/class-ccb-core-cron.php
delete mode 100644 includes/class-ccb-core-plugin.php
diff --git a/README.txt b/README.txt
index 0af1275..bf04ee2 100644
--- a/README.txt
+++ b/README.txt
@@ -2,7 +2,7 @@
Contributors: jaredcobb
Tags: ccb, church, api, chms
Requires at least: 4.4.0
-Tested up to: 4.7.2
+Tested up to: 4.9.1
Stable tag: 1.0.0
License: GPLv2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
diff --git a/ccb-core.php b/ccb-core.php
index 9cc25c6..5ed250e 100644
--- a/ccb-core.php
+++ b/ccb-core.php
@@ -19,7 +19,7 @@
* Domain Path: /languages
*/
-// do not allow direct access to this file.
+// Do not allow direct access to this file.
if ( ! defined( 'WPINC' ) ) {
die;
}
@@ -29,11 +29,12 @@
define( 'CCB_CORE_BASENAME', plugin_basename( __FILE__ ) );
define( 'CCB_CORE_VERSION', '1.0.0' );
-// code that runs during plugin activation.
+// Code that runs during plugin activation and deactivation.
require_once CCB_CORE_PATH . 'includes/class-ccb-core-activator.php';
register_activation_hook( __FILE__, array( 'CCB_Core_Activator', 'activate' ) );
+register_deactivation_hook( __FILE__, array( 'CCB_Core_Activator', 'deactivate' ) );
-// internationalization, dashboard-specific hooks, and public-facing site hooks.
+// Internationalization, dashboard-specific hooks, and public-facing site hooks.
require_once CCB_CORE_PATH . 'includes/class-ccb-core.php';
/**
diff --git a/css/ccb-core-admin.css b/css/ccb-core-admin.css
index 1a7cdb3..ba93755 100644
--- a/css/ccb-core-admin.css
+++ b/css/ccb-core-admin.css
@@ -1,21 +1,6 @@
-.test-login-wrapper .button, .test-login-wrapper .spinner, .sync-wrapper .button, .sync-wrapper .spinner {
- float: left;
- margin-bottom: 20px;
-}
-
-.ccb_core_settings-wrapper div.ajax-message {
- width: 50%;
- padding: 10px;
- clear:both;
- font-size: 13px;
-}
-
-.ccb_core_settings-wrapper code {
- background: none;
- padding: 0;
- font-size: 12px;
- font-weight: bold;
- color: #666666;
+.ccb_core_settings-wrapper .spinner {
+ float: left;
+ margin-top: 5px;
}
.ccb_core_settings ul {
diff --git a/includes/class-ccb-core-activator.php b/includes/class-ccb-core-activator.php
index 97e4607..42462bf 100644
--- a/includes/class-ccb-core-activator.php
+++ b/includes/class-ccb-core-activator.php
@@ -30,4 +30,17 @@ public static function activate() {
// TODO: check dependencies like mcrypt and memory limits.
}
+ /**
+ * Deactivation code
+ *
+ * @since 1.0.0
+ */
+ public static function deactivate() {
+ // Ensure we do not have a scheduled hook.
+ $timestamp = wp_next_scheduled( 'ccb_core_auto_sync_hook' );
+ if ( $timestamp ) {
+ wp_unschedule_event( $timestamp, 'ccb_core_auto_sync_hook' );
+ }
+ }
+
}
diff --git a/includes/class-ccb-core-admin-ajax.php b/includes/class-ccb-core-admin-ajax.php
index 0bcb469..52ae986 100644
--- a/includes/class-ccb-core-admin-ajax.php
+++ b/includes/class-ccb-core-admin-ajax.php
@@ -6,7 +6,7 @@
* @since 1.0.0
*
* @package CCB_Core
- * @subpackage CCB_Core/admin
+ * @subpackage CCB_Core/includes
*/
/**
@@ -40,11 +40,11 @@ class CCB_Core_Admin_AJAX {
public function __construct() {
add_action( 'wp_ajax_sync', array( $this, 'ajax_sync' ) );
add_action( 'wp_ajax_poll_sync', array( $this, 'ajax_poll_sync' ) );
- add_action( 'wp_ajax_test_credentials', array( $this, 'ajax_test_credentials' ) );
add_action( 'wp_ajax_get_latest_sync', array( $this, 'ajax_get_latest_sync' ) );
+ add_action( 'wp_ajax_test_credentials', array( $this, 'ajax_test_credentials' ) );
- $this->api = new CCB_Core_API();
- $this->synchronizer = new CCB_Core_Synchronizer();
+ $this->api = CCB_Core_API::instance();
+ $this->synchronizer = CCB_Core_Synchronizer::instance();
}
/**
@@ -58,10 +58,10 @@ public function __construct() {
public function ajax_sync() {
check_ajax_referer( 'ccb_core_nonce', 'nonce' );
- $this->synchronizer->synchronize();
// Tell the user to move along and go about their business...
CCB_Core_Helpers::instance()->send_non_blocking_json_success();
+ $result = $this->synchronizer->synchronize();
}
@@ -76,14 +76,14 @@ public function ajax_sync() {
public function ajax_poll_sync() {
check_ajax_referer( 'ccb_core_nonce', 'nonce' );
-
- //$sync_in_progress = get_transient( $this->plugin_name . '-sync-in-progress' );
- //wp_send_json( array( 'syncInProgress' => $sync_in_progress ) );
+ $sync_in_progress = get_transient( CCB_Core_Helpers::SYNC_STATUS_KEY );
+ wp_send_json_success( $sync_in_progress );
}
/**
* Gets the latest synchronization results from an ajax hook
+ * and encodes / echoes a standardized result array.
*
* @access public
* @since 1.0.0
@@ -93,13 +93,107 @@ public function ajax_get_latest_sync() {
check_ajax_referer( 'ccb_core_nonce', 'nonce' );
- //$latest_sync = $this->get_latest_sync_results();
- //wp_send_json( $latest_sync );
+ $message = '';
+ $result = [];
+
+ // Latest sync results are always stored as an option after a sync takes place.
+ $latest_sync = get_option( 'ccb_core_latest_sync_result' );
+
+ if ( ! empty( $latest_sync ) ) {
+ // Set the success result to the same result as the latest sync.
+ $result['success'] = $latest_sync['success'];
+
+ if ( true === $latest_sync['success'] ) {
+
+ $message .= esc_html(
+ sprintf(
+ // Translators: A formatted date/time.
+ __( 'The latest synchronization was successful on %s.', 'ccb-core' ),
+ get_date_from_gmt(
+ date( 'Y-m-d H:i:s', $latest_sync['timestamp'] ),
+ get_option( 'date_format' ) . ' \a\t ' . get_option( 'time_format' )
+ )
+ )
+ ) . '
';
+
+ // Send detailed results for each service that has information.
+ if ( ! empty( $latest_sync['services'] ) ) {
+ foreach ( $latest_sync['services'] as $service => $service_result ) {
+
+ $message .= esc_html(
+ sprintf(
+ // Translators: The service name.
+ __( 'Results from the %s service: ', 'ccb-core' ),
+ $service
+ )
+ );
+
+ if ( isset( $service_result['insert_update']['processed'] ) ) {
+ $message .= esc_html(
+ sprintf(
+ // Translators: The number of records processed.
+ __( '%s records inserted / updated. ', 'ccb-core' ),
+ absint( $service_result['insert_update']['processed'] )
+ )
+ );
+ }
+
+ if ( isset( $service_result['delete']['processed'] ) ) {
+ $message .= esc_html(
+ sprintf(
+ // Translators: The number of records processed.
+ __( '%s records deleted. ', 'ccb-core' ),
+ absint( $service_result['delete']['processed'] )
+ )
+ );
+ }
+
+ $message .= '
';
+ }
+ }
+
+ } else {
+ $message .= esc_html(
+ sprintf(
+ __( '%1$s on %2$s', 'ccb-core' ),
+ $latest_sync['message'],
+ get_date_from_gmt(
+ date( 'Y-m-d H:i:s', $latest_sync['timestamp'] ),
+ get_option( 'date_format' ) . ' \a\t ' . get_option( 'time_format' )
+ )
+ )
+ ) . '
';
+ if ( ! empty( $latest_sync['services'] ) ) {
+ foreach ( $latest_sync['services'] as $service => $service_result ) {
+ if ( ! empty( $service_result['insert_update']['message'] ) ) {
+ $message .= $service_result['insert_update']['message'] . '
';
+ }
+ if ( ! empty( $service_result['delete']['message'] ) ) {
+ $message .= $service_result['delete']['message'] . '
';
+ }
+ }
+ }
+ }
+ } else {
+ $message .= esc_html__( 'We do not have any recent synchronizations', 'ccb-core' );
+ }
+
+ /**
+ * Filters the message that gets output to the user
+ * after a synchonrization is finished.
+ *
+ * @since 1.0.0
+ *
+ * @param string $message The message with the results.
+ * @param array $latest_sync The latest synchronization results.
+ */
+ $result['message'] = apply_filters( 'ccb_core_ajax_results_message', $message, $latest_sync );
+ wp_send_json_success( $result );
}
/**
- * Checks the credentials for a user from an ajax hook
+ * Checks the CCB API credentials for a user from an ajax hook
*
* @access public
* @since 1.0.0
@@ -116,48 +210,6 @@ public function ajax_test_credentials() {
}
}
- /**
- * Check if we should schedule a synchronization based on
- * the options set by the user
- *
- * @access public
- * @since 1.0.0
- * @return void
- */
- public function check_auto_refresh() {
-
- $settings = get_option( $this->plugin_settings_name );
-
- if ( isset( $settings['auto_sync'] ) && 1 === $settings['auto_sync'] ) {
- $latest_sync = get_option( $this->plugin_name . '-latest-sync' );
-
- if ( ! empty( $latest_sync ) ) {
- $auto_sync_timeout = $settings['auto_sync_timeout'];
- $now = time();
- $diff = $now - $latest_sync['timestamp'];
-
- if ( $diff > $auto_sync_timeout * 60 ) {
- wp_schedule_single_event( time(), 'schedule_auto_refresh' );
- }
-
- } else {
- wp_schedule_single_event( time(), 'schedule_auto_refresh' );
- }
- }
- }
-
- /**
- * Callback function to kick off a synchronization
- *
- * @access public
- * @since 1.0.0
- * @return void
- */
- public function auto_sync() {
- $sync = new CCB_Core_Sync();
- $sync->sync();
- }
-
}
new CCB_Core_Admin_AJAX();
diff --git a/includes/class-ccb-core-api-old.php b/includes/class-ccb-core-api-old.php
deleted file mode 100644
index 9b880b5..0000000
--- a/includes/class-ccb-core-api-old.php
+++ /dev/null
@@ -1,961 +0,0 @@
-
- */
-class CCB_Core_API {
-
- /**
- * The subdomain of the ccb church installation
- *
- * @since 1.0.0
- * @access protected
- * @var string $subdomain
- */
- protected $subdomain;
-
- /**
- * The ccb api username
- *
- * @since 1.0.0
- * @access protected
- * @var string $username
- */
- protected $username;
-
- /**
- * The ccb api password
- *
- * @since 1.0.0
- * @access protected
- * @var string $password
- */
- protected $password;
-
- /**
- * The CCB APIs we want to sync with
- *
- * @since 1.0.0
- * @access protected
- * @var array $enabled_apis
- */
- protected $enabled_apis = array();
-
- /**
- * The start date range for calendar events
- *
- * @since 1.0.0
- * @access protected
- * @var string $calendar_start_date
- */
- protected $calendar_start_date;
-
- /**
- * The end date range for calendar events
- *
- * @since 1.0.0
- * @access protected
- * @var string $calendar_end_date
- */
- protected $calendar_end_date;
-
- /**
- * Any valid service that the core API might integrate with
- *
- * @since 1.0.0
- * @access protected
- * @var array $valid_services
- */
- protected $valid_services;
-
- /**
- * Whether or not to additionally import group images
- *
- * @since 1.0.0
- * @access protected
- * @var array $valid_services
- */
- protected $import_group_images;
-
- /**
- * Initialize the class and set its properties.
- *
- * @since 1.0.0
- */
- public function __construct() {
-
- $settings = get_option( $this->plugin_settings_name );
-
- $this->subdomain = $settings['subdomain'];
- $this->username = $settings['credentials']['username'];
- $this->password = $this->decrypt( $settings['credentials']['password'] );
-
- if ( isset( $settings['groups_enabled'] ) && $settings['groups_enabled'] == 1 ) {
-
- $this->enabled_apis['group_profiles'] = true;
-
- if ( isset( $settings['groups_import_images'] ) && $settings['groups_import_images'] == 'yes' ) {
- $this->import_group_images = true;
- }
- else {
- $this->import_group_images = false;
- }
-
- }
- if ( isset( $settings['calendar_enabled'] ) && $settings['calendar_enabled'] == 1 ) {
-
- $this->enabled_apis['public_calendar_listing'] = true;
-
- // use sane defaults if this advanced setting isn't set
- if ( ! isset( $settings['calendar_date_range_type'] ) ) {
-
- $this->calendar_start_date = date( 'Y-m-d', strtotime( '1 weeks ago') );
- $this->calendar_end_date = date( 'Y-m-d', strtotime( '+16 weeks' ) );
-
- }
- elseif ( $settings['calendar_date_range_type'] == 'relative' ) {
-
- $this->calendar_start_date = date( 'Y-m-d', strtotime( $settings['calendar_relative_weeks_past'] . ' weeks ago') );
- $this->calendar_end_date = date( 'Y-m-d', strtotime( '+' . $settings['calendar_relative_weeks_future'] . ' weeks' ) );
-
- }
- elseif ( $settings['calendar_date_range_type'] == 'specific' ) {
-
- // TODO: Use localization for date formats other than U.S.
-
- if ( $settings['calendar_specific_start'] ) {
-
- $last_year = strtotime( '1 year ago' );
- $start_timestamp = strtotime( $settings['calendar_specific_start'] );
-
- if ( abs( $start_timestamp - $last_year ) > 0 ) {
- $this->calendar_start_date = date( 'Y-m-d', $start_timestamp );
- }
- else {
- $this->calendar_start_date = date( 'Y-m-d', $last_year );
- }
-
- }
- else {
- $this->calendar_start_date = date( 'Y-m-d' );
- }
-
- if ( $settings['calendar_specific_end'] ) {
-
- $next_year = strtotime( '+1 year' );
- $end_timestamp = strtotime( $settings['calendar_specific_end'] );
-
- if ( abs( $next_year - $end_timestamp ) > 0 ) {
- $this->calendar_end_date = date( 'Y-m-d', $end_timestamp );
- }
- else {
- $this->calendar_end_date = date( 'Y-m-d', $next_year );
- }
-
- }
- else {
- $this->calendar_end_date = date( 'Y-m-d', strtotime( '+1 year' ) );
- }
- }
-
- }
-
- $this->valid_services = array(
- array(
- 'service_name' => 'api_status',
- 'service_friendly_name' => 'Credentials',
- ),
- array(
- 'service_name' => 'group_profiles',
- 'params' => array(
- 'describe_api' => '1'
- ),
- 'service_friendly_name' => 'Group Profiles API',
- ),
- array(
- 'service_name' => 'public_calendar_listing',
- 'params' => array(
- 'describe_api' => '1'
- ),
- 'service_friendly_name' => 'Public Calendar Listing API',
- ),
- );
-
- }
-
- /**
- * Make a service call to the CCB API
- *
- * The $services array is in the format:
- * $services = array(
- * array (
- * 'service_name' => 'group_profiles',
- * 'params' => array(
- * 'modified_since' => '2015-06-01',
- * 'include_participants' => 'false'
- * )
- * ),
- * array (
- * 'service_name' => 'public_calendar_listing',
- * 'params' => array(
- * 'date_start' => '2015-06-01',
- * )
- * ),
- * )
- *
- * @since 1.0.0
- * @param array $services An array of services and parameters to call
- * @access protected
- * @return void
- */
- protected function call_ccb_api( $services = array() ) {
-
- set_time_limit(600);
- $full_response = array();
-
- // for debugging purposes, set a constant and serialize an array like so:
- // define( 'RESPONSE_FILE', serialize( array( 'filename' => 'some_file.xml', 'service_name' => 'group_profiles' ) ) );
- // file must be located in the /uploads/ccb-core/ folder
- // this will prevent a real api call and will use an xml file
- if ( WP_DEBUG == true && defined( 'RESPONSE_FILE' ) ) {
-
- $service = unserialize( RESPONSE_FILE );
- $upload_dir = wp_upload_dir();
- $filepath = trailingslashit( trailingslashit( $upload_dir['basedir'] ) . $this->plugin_name ) . $service['filename'];
-
- if ( file_exists( $filepath ) ) {
- $response_body = file_get_contents( $filepath );
- libxml_use_internal_errors(true);
- $response_xml = simplexml_load_string( $response_body );
-
- if ( is_object( $response_xml ) ) {
- $full_response['success'] = true;
- $full_response[ $service['service_name'] ] = $response_xml;
- }
-
- return $full_response;
- }
-
- }
-
- if ( ! empty( $services ) && is_array( $services ) ) {
-
- foreach ( $services as $service ) {
-
- $params = '';
- if ( isset( $service['params'] ) && ! empty( $service['params'] ) ) {
- $params = http_build_query($service['params']);
- }
-
- $api_url = "https://{$this->subdomain}.ccbchurch.com/api.php?srv={$service['service_name']}&{$params}";
- $post_args = array(
- 'body' => array(),
- 'timeout' => '600',
- 'redirection' => '15',
- 'httpversion' => '1.0',
- 'blocking' => true,
- 'headers' => array(
- 'Authorization' => 'Basic ' . base64_encode( "{$this->username}:{$this->password}" )
- ),
- 'cookies' => array()
- );
-
- $response = wp_remote_post( $api_url, $post_args );
- $response_code = wp_remote_retrieve_response_code( $response );
-
- if ( $response_code != 200 ) {
- $full_response['success'] = false;
- $full_response['message'] = "There was a problem connecting with the Church Community Builder API - Response Code: {$response_code}";
- break;
- }
- else {
- try {
- libxml_use_internal_errors(true);
- $response_body = wp_remote_retrieve_body( $response );
- $response_xml = simplexml_load_string( $response_body );
-
- if ( is_object( $response_xml ) ) {
- $full_response['success'] = true;
- $full_response[ $service['service_name'] ] = $response_xml;
- }
- else {
- $full_response['success'] = false;
- $full_response['message'] = 'Oops, something went wrong while trying to read the API response. Is your subdomain correct?';
- break;
- }
- }
- catch ( Exception $ex ) {
- $full_response['success'] = false;
- $full_response['message'] = 'Oops, something went wrong while trying to read the API response. Is your subdomain correct?';
- break;
- }
- }
-
- // cache the xml response to the uploads folder if debug mode is on (testing purposes)
- if ( WP_DEBUG == true ) {
-
- $now = new DateTime();
- $cache_filename = $service['service_name'] . '_' . $now->format( 'Y-m-d_His' ) . '.xml';
- $upload_dir = wp_upload_dir();
-
- if ( wp_mkdir_p( trailingslashit( $upload_dir['basedir'] ) . $this->plugin_name ) ) {
- // first delete any files that weren't created "today" so we don't spam the server over time
- $files = preg_grep( '/' . $now->format( 'Y-m-d' ) . '/', glob( trailingslashit( trailingslashit( $upload_dir['basedir'] ) . $this->plugin_name ) . '*' ), PREG_GREP_INVERT );
- foreach ( $files as $file ) {
- if ( is_file( $file ) ) {
- @unlink( $file );
- }
- }
-
- $upload_file_path = trailingslashit( $upload_dir['basedir'] ) . trailingslashit( $this->plugin_name ) . $cache_filename;
- file_put_contents( $upload_file_path, $response_body );
- }
-
- }
- }
- }
- else {
- $full_response['success'] = false;
- $full_response['message'] = 'You tried to kick off a syncronization on ' . date( 'F j, Y @ h:i:s a (e)' ) . " but didn't have any integrations enabled (see each service tab).";
- }
-
- return $full_response;
- }
-
- /**
- * Perform a synchronization
- *
- * @access public
- * @since 1.0.0
- * @return void
- */
- public function sync() {
-
- // check for a transient that assumes a sync in in progress
- if ( get_transient( $this->plugin_name . '-sync-in-progress' ) ) {
- return;
- }
- else {
- set_transient( $this->plugin_name . '-sync-in-progress', true, 60*20 );
- }
-
- $services = array();
- $current_time = time();
-
- // GROUP PROFILES
- if ( $this->enabled_apis['group_profiles'] ) {
-
- $include_participants = false;
- $include_participants = apply_filters( 'ccb_include_group_participants', $include_participants );
-
- $services[] = array(
- 'service_name' => 'group_profiles',
- 'params' => array(
- 'include_participants' => $include_participants,
- ),
- );
-
- }
-
- // PUBLIC CALENDAR LISTING
- if ( $this->enabled_apis['public_calendar_listing'] ) {
-
- $services[] = array(
- 'service_name' => 'public_calendar_listing',
- 'params' => array( 'date_start' => $this->calendar_start_date, 'date_end' => $this->calendar_end_date ),
- );
-
- }
-
- $full_response = $this->call_ccb_api( $services );
- $validation_results = $this->validate_response( $full_response );
- // a data structure to hold a unique status of the latest sync results, stored in the db
- $latest_sync = array();
-
- if ( $validation_results['success'] == true ) {
-
- // check if any services failed so we can abort the sync and show different messaging
- $service_failure = false;
- foreach ( $validation_results['services'] as $service ) {
- if ( $service['success'] == false ) {
- $service_failure = true;
- break;
- }
- }
-
- if ( $service_failure ) {
-
- $messages = array();
-
- foreach ( $validation_results['services'] as $service ) {
-
- if ( $service['success'] == false ) {
- $messages[] = 'We were not able to successfully synchronize with the ' . $service['service_name'] . ' service on ' . date( 'F j, Y @ h:i:s a (e)', $current_time ) . '. ' . $service['message'];
- }
- else {
- $messages[] = 'We were able to successfully contact the ' . $service['service_name'] . ' service on ' . date( 'F j, Y @ h:i:s a (e)', $current_time ) . ', however we cancelled the synchronization because of other service errors.';
- }
- }
-
- $message = implode( '
', $messages );
- $latest_sync = array(
- 'success' => false,
- 'message' => $message,
- );
- }
- else {
-
- $this->import_cpts( $full_response );
-
- $latest_sync = array(
- 'success' => true,
- 'message' => 'We last successfully synchronized with the Church Community Builder API on ' . date( 'F j, Y @ h:i:s a (e)', $current_time ),
- );
- }
- }
- else {
- $latest_sync = array(
- 'success' => false,
- 'message' => $validation_results['message'] . ' We made the last attempt on ' . date( 'F j, Y @ h:i:s a (e)', $current_time ),
- );
- }
-
- $latest_sync['timestamp'] = $current_time;
- delete_transient( $this->plugin_name . '-sync-in-progress' );
- update_option( $this->plugin_name . '-latest-sync', $latest_sync );
-
- }
-
- /**
- * Tests API connection, credentials, and specific services
- * as defined in the constructor
- *
- * @access public
- * @since 1.0.0
- * @return string
- */
- public function test_api_credentials() {
-
- $full_response = $this->call_ccb_api( $this->valid_services );
- delete_transient( $this->plugin_name . '-sync-in-progress' );
- return $this->validate_response( $full_response );
-
- }
-
- /**
- * Takes a CCB API response and parses it for basic business rules.
- * Returns an array of successes, failures, and messages
- *
- * @param mixed $full_response
- * @access protected
- * @since 1.0.0
- * @return array
- */
- protected function validate_response( $full_response ) {
-
- $validation_results = array();
-
- if ( $full_response['success'] ) {
-
- $validation_results['success'] = true;
- $validation_results['services'] = array();
-
- foreach ( $this->valid_services as $service ) {
-
- if ( ! empty ( $full_response[ $service['service_name'] ] ) ) {
-
- $result_array = array();
-
- if ( isset( $full_response[ $service['service_name'] ]->response->errors ) ) {
- if ( isset( $full_response[ $service['service_name'] ]->response->errors->error ) && ! empty( $full_response[ $service['service_name'] ]->response->errors->error ) ) {
- $result_array = array(
- 'success' => false,
- 'label' => $service['service_friendly_name'],
- 'service_name' => $service['service_name'],
- 'message' => 'The API responded with the message:
"' . $full_response[ $service['service_name'] ]->response->errors->error . '"
',
- );
- }
- else {
- $result_array = array(
- 'success' => false,
- 'label' => $service['service_friendly_name'],
- 'service_name' => $service['service_name'],
- 'message' => 'The API did not provide any other information',
- );
- }
- }
- else {
- $result_array = array(
- 'success' => true,
- 'label' => $service['service_friendly_name'],
- 'service_name' => $service['service_name'],
- 'message' => 'Success',
- );
- }
-
- $validation_results['services'][] = $result_array;
- }
-
- }
-
- }
- else {
- // the entire call was a failure. a sad sad failure.
- $validation_results['success'] = false;
- $validation_results['message'] = $full_response['message'];
- }
-
- return $validation_results;
- }
-
- /**
- * Parses the XML response, deletes existing CPTs, and imports CCB data
- *
- * @param mixed $full_response
- * @since 1.0.0
- * @access protected
- * @return void
- */
- protected function import_cpts( $full_response ) {
-
- global $wpdb;
- // temporarily disable counting for performance
- wp_defer_term_counting( true );
- wp_defer_comment_counting( true );
- // temporarily disable autocommit
- $wpdb->query( 'SET autocommit = 0;' );
-
-
- // GROUP PROFILES
- if ( $this->enabled_apis['group_profiles'] == true && isset( $full_response['group_profiles']->response->groups->group ) && ! empty( $full_response['group_profiles']->response->groups->group ) ) {
-
- $groups_taxonomy_map = CCB_Core_CPTs::get_groups_taxonomy_map();
- $groups_taxonomy_map = apply_filters( 'ccb_get_groups_taxonomy_map', $groups_taxonomy_map );
-
- $groups_custom_fields_map = CCB_Core_CPTs::get_groups_custom_fields_map();
- $groups_custom_fields_map = apply_filters( 'ccb_get_groups_custom_fields_map', $groups_custom_fields_map );
-
- // delete the existing taxonomy terms
- foreach ( $groups_taxonomy_map as $taxonomy_name => $taxonomy ) {
- $terms = get_terms( $taxonomy_name, array( 'fields' => 'ids', 'hide_empty' => false ) );
- if ( ! empty( $terms ) ) {
- foreach ( $terms as $term_value ) {
- wp_delete_term( $term_value, $taxonomy_name );
- }
- }
- }
-
- // delete existing custom posts
- $custom_posts = get_posts( array( 'post_type' => $this->plugin_name . '-groups', 'posts_per_page' => -1 ) );
- foreach( $custom_posts as $custom_post ) {
-
- // delete the post thumbnail if it exists before deleting the post
- $thumbnail_id = get_post_thumbnail_id( $custom_post->ID );
- if ( $thumbnail_id ) {
- wp_delete_attachment( $thumbnail_id, true );
- }
-
- wp_delete_post( $custom_post->ID, true);
- }
-
- // commit the deletes now
- $wpdb->query( 'COMMIT;' );
-
- // keep track of whether or not a default image has already been imported
- $default_attachment = 0;
-
- foreach ( $full_response['group_profiles']->response->groups->group as $group ) {
-
- // only allow publicly listed and active groups to be imported
- if ( $group->inactive == 'false' && $group->public_search_listed == 'true' ) {
-
- $group_id = 0;
- foreach( $group->attributes() as $key => $value ) {
- if ( $key == 'id' ) {
- $group_id = (int) $value;
- break;
- }
- }
-
- // insert group post
- $group_post_atts = array(
- 'post_title' => $group->name,
- 'post_name' => $group->name,
- 'post_content' => $group->description,
- 'post_status' => 'publish',
- 'post_type' => $this->plugin_name . '-groups',
- );
- $post_id = wp_insert_post( $group_post_atts );
-
- // insert hierarchial taxonomy values (categories) and non-hierarchial taxonomy values (tags)
- $taxonomy_atts = $this->get_taxonomy_atts( $group, $groups_taxonomy_map );
- if ( ! empty( $taxonomy_atts ) ) {
- foreach ( $taxonomy_atts as $taxonomy_attribute ) {
- wp_set_post_terms( $post_id, $taxonomy_attribute['terms'], $taxonomy_attribute['taxonomy'], true );
- }
- }
-
- // insert custom fields
- $custom_fields_atts = $this->get_custom_fields_atts( $group, $groups_custom_fields_map );
- if ( ! empty( $custom_fields_atts ) ) {
- foreach ( $custom_fields_atts as $field_key => $custom_fields_attribute ) {
- add_post_meta( $post_id, $custom_fields_attribute['field_name'], $custom_fields_attribute['field_value'] );
- }
- }
-
- // download and attach the group image as the featured image
- if ( isset( $group->image ) && $this->import_group_images == true ) {
-
- $group_image_url = esc_url_raw( $group->image );
-
- if ( ! empty( $group_image_url ) ) {
-
- // handle default images
- if ( strpos( $group_image_url, 'default' ) ) {
- if ( ! $default_attachment ) {
- $attachment_result = $this->create_media_image( 'default', 0, $group_image_url );
- if ( $attachment_result ) {
- $default_attachment = $attachment_result;
- set_post_thumbnail( $post_id, $default_attachment );
- }
- }
- else {
- set_post_thumbnail( $post_id, $default_attachment );
- }
- }
- else {
- $attachment_result = $this->create_media_image( $group->name, $post_id, $group_image_url );
- if ( $attachment_result ) {
- set_post_thumbnail( $post_id, $attachment_result );
- }
- }
- }
-
- }
-
- }
-
- }
-
- // commit the inserts now
- $wpdb->query( 'COMMIT;' );
-
- }
-
- // PUBLIC CALENDAR LISTING
- if ( $this->enabled_apis['public_calendar_listing'] == true && isset( $full_response['public_calendar_listing']->response->items->item ) && ! empty( $full_response['public_calendar_listing']->response->items->item ) ) {
-
- $calendar_taxonomy_map = CCB_Core_CPTs::get_calendar_taxonomy_map();
- $calendar_taxonomy_map = apply_filters( 'ccb_get_calendar_taxonomy_map', $calendar_taxonomy_map );
-
- $calendar_custom_fields_map = CCB_Core_CPTs::get_calendar_custom_fields_map();
- $calendar_custom_fields_map = apply_filters( 'ccb_get_calendar_custom_fields_map', $calendar_custom_fields_map );
-
- // delete the existing taxonomy terms
- foreach ( $calendar_taxonomy_map as $taxonomy_name => $taxonomy ) {
- $terms = get_terms( $taxonomy_name, array( 'fields' => 'ids', 'hide_empty' => false ) );
- if ( ! empty( $terms ) ) {
- foreach ( $terms as $term_value ) {
- wp_delete_term( $term_value, $taxonomy_name );
- }
- }
- }
-
- // delete existing custom posts
- $custom_posts = get_posts( array( 'post_type' => $this->plugin_name . '-calendar', 'posts_per_page' => -1 ) );
- foreach( $custom_posts as $custom_post ) {
- wp_delete_post( $custom_post->ID, true);
- }
-
- // commit the deletes now
- $wpdb->query( 'COMMIT;' );
-
- foreach ( $full_response['public_calendar_listing']->response->items->item as $event ) {
-
- // insert event post
- $event_post_atts = array(
- 'post_title' => $event->event_name,
- 'post_name' => $event->event_name,
- 'post_content' => $event->event_description,
- 'post_status' => 'publish',
- 'post_type' => $this->plugin_name . '-calendar',
- );
- $post_id = wp_insert_post( $event_post_atts );
-
- // insert hierarchial taxonomy values (categories) and non-hierarchial taxonomy values (tags)
- $taxonomy_atts = $this->get_taxonomy_atts( $event, $calendar_taxonomy_map );
- if ( ! empty( $taxonomy_atts ) ) {
- foreach ( $taxonomy_atts as $taxonomy_attribute ) {
- wp_set_post_terms( $post_id, $taxonomy_attribute['terms'], $taxonomy_attribute['taxonomy'], true );
- }
- }
-
- // insert custom fields
- $custom_fields_atts = $this->get_custom_fields_atts( $event, $calendar_custom_fields_map );
- if ( ! empty( $custom_fields_atts ) ) {
- foreach ( $custom_fields_atts as $field_key => $custom_fields_attribute ) {
- add_post_meta( $post_id, $custom_fields_attribute['field_name'], $custom_fields_attribute['field_value'] );
- }
- }
-
- }
-
- // commit the inserts now
- $wpdb->query( 'COMMIT;' );
-
- }
-
- // re-enable autocommit
- $wpdb->query( 'SET autocommit = 1;' );
- // re-enable counting
- wp_defer_term_counting( false );
- wp_defer_comment_counting( false );
-
- }
-
- /**
- * Uses a taxonomy map to build out the categories and tags
- * for a CCB custom post type
- *
- * @param mixed $post_data
- * @param array $taxonomy_map
- * @access protected
- * @since 1.0.0
- * @return void
- */
- protected function get_taxonomy_atts( $post_data, $taxonomy_map ) {
-
- foreach( $taxonomy_map as $taxonomy_name => $taxonomy ) {
-
- if ( $taxonomy['hierarchical'] ) {
-
- $taxonomy_value = (string) $post_data->$taxonomy['api_mapping'];
-
- if ( ! empty( $taxonomy_value ) ) {
-
- $term_id = term_exists( $taxonomy_value, $taxonomy_name );
- if ( $term_id ) {
- $terms_collection[] = array(
- 'terms' => $term_id['term_id'],
- 'taxonomy' => $taxonomy_name,
- );
- }
- else {
- $new_term = wp_insert_term( $taxonomy_value, $taxonomy_name );
- $terms_collection[] = array(
- 'terms' => $new_term['term_id'],
- 'taxonomy' => $taxonomy_name,
- );
- }
-
- }
-
- }
- else {
-
- if ( isset( $taxonomy['api_mapping'] ) && ! empty( $taxonomy['api_mapping'] ) ) {
-
- foreach ( $taxonomy['api_mapping'] as $api_mapping => $tag_name ) {
-
- $tag_value = ( $post_data->$api_mapping == 'true' ? $tag_name : false );
- if ( $tag_value ) {
- $terms_collection[] = array(
- 'terms' => $tag_value,
- 'taxonomy' => $taxonomy_name,
- );
- }
-
- }
-
- }
- }
-
- }
-
- return $terms_collection;
-
- }
-
- /**
- * Uses a custom fields map to build out the custom fields
- * for a CCB custom post type
- *
- * @param mixed $post_data
- * @param array $custom_fields_map
- * @param string $parent_field_name
- * @access protected
- * @since 1.0.0
- * @return void
- */
- protected function get_custom_fields_atts( $post_data, $custom_fields_map, $parent_field_name = '' ) {
-
- $custom_fields_collection = array();
-
- foreach( $custom_fields_map as $field_name => $field_data ) {
-
- switch ( $field_data['data_type'] ) {
- case 'string':
- $field_value = (string) $post_data->$field_data['api_mapping'];
- $custom_fields_collection[] = array(
- 'field_name' => $field_name,
- 'field_value' => $field_value,
- );
- break;
- case 'int':
- $field_value = (int) $post_data->$field_data['api_mapping'];
- $custom_fields_collection[] = array(
- 'field_name' => $field_name,
- 'field_value' => $field_value,
- );
- break;
- case 'object':
- if ( isset( $field_data['child_object'] ) && ! empty( $field_data['child_object'] ) ) {
- // some child objects may be collections of objects
- if ( count( $post_data->$field_data['api_mapping'] ) > 1 ) {
- $collection_grouping = array();
- foreach ( $post_data->$field_data['api_mapping'] as $key => $child_field_data ) {
- if ( is_object ( $child_field_data ) ) {
- $collection_grouping[] = $this->get_custom_fields_atts( $child_field_data, $field_data['child_object'], $field_name );
- }
- }
- $prepared_field_collection = $this->prepare_field_collection( $parent_field_name, $collection_grouping );
- $custom_fields_collection[] = $prepared_field_collection;
- }
- else {
- $child_custom_fields_collection = $this->get_custom_fields_atts( $post_data->$field_data['api_mapping'], $field_data['child_object'], $field_name );
- $custom_fields_collection = array_merge( $custom_fields_collection, $child_custom_fields_collection );
- }
- }
- break;
- }
-
- }
-
- return $custom_fields_collection;
-
- }
-
- /**
- * Takes a multidimensional array of field collections and formats
- * them into groups that are compatible with storage in a
- * single custom field
- *
- * @param string $field_name
- * @param array $collection_grouping
- * @access protected
- * @since 0.9.4
- * @return array
- */
- protected function prepare_field_collection( $field_name, $collection_grouping ) {
-
- $flat_collection = array(
- 'field_name' => $field_name,
- 'field_value' => array(),
- );
-
- foreach( $collection_grouping as $grouping_value ) {
-
- $grouping_array = array();
-
- foreach ( $grouping_value as $value_pair ) {
-
- $grouping_array[] = array(
- $value_pair['field_name'] => $value_pair['field_value']
- );
-
- }
-
- $flat_collection['field_value'][] = $grouping_array;
-
- }
-
- return $flat_collection;
- }
-
- /**
- * Downloads an image from a URL, uploads it to the Media Library,
- * and then optionally attaches it to a post
- *
- * @param string $group_name
- * @param int $post_id
- * @param string $image_url
- * @access protected
- * @since 0.9.5
- * @return mixed Returns a media id or false on failure
- */
- protected function create_media_image( $group_name, $post_id, $image_url ) {
-
- // fetch the image from the cdn and store temporarily
- $temp_file = download_url( $image_url );
-
- if ( is_wp_error( $temp_file ) ) {
- return false;
- }
-
- // attempt to detect the mimetype based on the available functions
- $extension = false;
- if ( function_exists( 'exif_imagetype' ) && function_exists( 'image_type_to_extension' ) ) {
- // open with exif
- $image_type = exif_imagetype( $temp_file );
- if ( $image_type ) {
- $extension = image_type_to_extension( $image_type );
- }
- }
- elseif ( function_exists( 'getimagesize' ) && function_exists( 'image_type_to_extension' ) ) {
- // open with gd
- $file_size = getimagesize( $temp_file );
- if ( isset( $file_size[2] ) ) {
- $extension = image_type_to_extension( $file_size[2] );
- }
- }
- elseif ( function_exists( 'finfo_open' ) ) {
- // open with fileinfo
- $resource = finfo_open( FILEINFO_MIME_TYPE );
- $mimetype = finfo_file( $resource, $temp_file );
- finfo_close( $resource );
- if ( $mimetype ) {
- $mimetype_array = explode( '/', $mimetype );
- $extension = '.' . $mimetype_array[1];
- }
- }
-
- if ( $extension ) {
-
- $filename = 'ccb-' . sanitize_file_name( strtolower( $group_name ) ) . $extension;
-
- $file_array = array(
- 'name' => $filename,
- 'tmp_name' => $temp_file,
- );
-
- $media_id = media_handle_sideload( $file_array, $post_id );
- @unlink( $temp_file );
-
- if ( is_wp_error( $media_id ) ) {
- return false;
- }
-
- return $media_id;
-
- }
- else {
- return false;
- }
- }
-
-}
diff --git a/includes/class-ccb-core-api.php b/includes/class-ccb-core-api.php
index 18e3261..722197b 100644
--- a/includes/class-ccb-core-api.php
+++ b/includes/class-ccb-core-api.php
@@ -28,39 +28,74 @@ class CCB_Core_API {
*/
public $initialized = false;
+ /**
+ * Instance of the class
+ *
+ * @var CCB_Core_API
+ * @access private
+ * @static
+ */
+ private static $instance;
+
/**
* The subdomain of the ccb church installation
*
* @since 1.0.0
- * @access protected
+ * @access private
* @var string $subdomain
*/
- protected $subdomain;
+ private $subdomain;
/**
* The ccb api username
*
* @since 1.0.0
- * @access protected
+ * @access private
* @var string $username
*/
- protected $username;
+ private $username;
/**
* The ccb api password
*
* @since 1.0.0
- * @access protected
+ * @access private
* @var string $password
*/
- protected $password;
+ private $password;
/**
- * Initialize the class and set its properties.
+ * Unused constructor in the singleton pattern
*
- * @since 1.0.0
+ * @access public
+ * @return void
*/
public function __construct() {
+ // Initialize this class with the instance() method.
+ }
+
+ /**
+ * Returns the instance of the class
+ *
+ * @access public
+ * @static
+ * @return CCB_Core_API
+ */
+ public static function instance() {
+ if ( ! isset( static::$instance ) ) {
+ static::$instance = new CCB_Core_API();
+ static::$instance->setup();
+ }
+ return static::$instance;
+ }
+
+ /**
+ * Initial setup of the singleton
+ *
+ * @access private
+ * @return void
+ */
+ private function setup() {
// Wait to initialize the API credentials until after WordPress
// has loaded pluggable.php because we are using some WordPress helper functions.
add_action( 'plugins_loaded', [ $this, 'initialize_credentials' ] );
@@ -209,6 +244,7 @@ private function request( $method, $service, $data = array() ) {
return $result;
}
+ // Serialize the response XML into a SimpleXML object.
try {
libxml_use_internal_errors( true );
$parsed_response = simplexml_load_string( $result['xml'] );
@@ -241,3 +277,5 @@ private function request( $method, $service, $data = array() ) {
}
}
+
+CCB_Core_API::instance();
diff --git a/includes/class-ccb-core-cron.php b/includes/class-ccb-core-cron.php
new file mode 100644
index 0000000..24222e6
--- /dev/null
+++ b/includes/class-ccb-core-cron.php
@@ -0,0 +1,113 @@
+
+ */
+class CCB_Core_Cron {
+
+ /**
+ * An instance of the CCB_Core_Synchronizer class
+ *
+ * @var CCB_Core_Synchronizer
+ */
+ private $synchronizer;
+
+ /**
+ * Initialize the class and set its properties.
+ *
+ * @since 1.0.0
+ */
+ public function __construct() {
+ // Setup a custom cron schedule based on the user preferences.
+ // phpcs:ignore
+ add_filter( 'cron_schedules', [ $this, 'custom_cron_schedule' ] );
+ // Setup the action and callback for the hook.
+ add_action( 'ccb_core_auto_sync_hook', [ $this, 'auto_sync_callback' ] );
+ // When the cron settings are changed, configure the events.
+ add_action( 'update_option_ccb_core_settings', [ $this, 'cron_settings_changed' ], 10, 2 );
+
+ $this->synchronizer = CCB_Core_Synchronizer::instance();
+ }
+
+ /**
+ * Create a custom cron schedule based on the
+ * timeout interval set by the user.
+ *
+ * @param array $schedules An array of cron schedules.
+ * @return array
+ */
+ public function custom_cron_schedule( $schedules ) {
+ $settings = CCB_Core_Helpers::instance()->get_options();
+ if ( ! empty( $settings['auto_sync_timeout'] ) ) {
+ $schedules['ccb_core_schedule'] = [
+ 'interval' => MINUTE_IN_SECONDS * absint( $settings['auto_sync_timeout'] ),
+ 'display' => esc_html__( sprintf(
+ __( 'Every %s Minutes' ),
+ absint( $settings['auto_sync_timeout'] )
+ ) ),
+ ];
+ }
+ return $schedules;
+ }
+
+ /**
+ * Callback method to detect when the settings have changed.
+ *
+ * We check for whether or not the auto sync was turned on / off and
+ * whether or not the user changed the timeout interval. This is
+ * how we register cron events and clean up invalid events.
+ *
+ * @param array $old_value The old settings array.
+ * @param array $new_value The new settings array.
+ * @return void
+ */
+ public function cron_settings_changed( $old_value, $new_value ) {
+ // If the cron was enabled OR the timeout was changed.
+ if (
+ ( '' === $old_value['auto_sync'] && '1' === $new_value['auto_sync'] )
+ || ( $old_value['auto_sync_timeout'] !== $new_value['auto_sync_timeout'] )
+ ) {
+ $this->remove_existing_cron_events();
+ wp_schedule_event( time(), 'ccb_core_schedule', 'ccb_core_auto_sync_hook' );
+ } elseif ( '1' === $old_value['auto_sync'] && '' === $new_value['auto_sync'] ) {
+ $this->remove_existing_cron_events();
+ }
+ }
+
+ /**
+ * Removes all CCB Core cron events.
+ *
+ * @return void
+ */
+ private function remove_existing_cron_events() {
+ $timestamp = wp_next_scheduled( 'ccb_core_auto_sync_hook' );
+ if ( $timestamp ) {
+ wp_unschedule_event( $timestamp, 'ccb_core_auto_sync_hook' );
+ }
+ }
+
+ /**
+ * The callback method of the cron event that kicks off a synchronization
+ *
+ * @return void
+ */
+ public function auto_sync_callback() {
+ $this->synchronizer->synchronize();
+ }
+
+}
+
+new CCB_Core_Cron();
diff --git a/includes/class-ccb-core-helpers.php b/includes/class-ccb-core-helpers.php
index 1dda450..c46e372 100644
--- a/includes/class-ccb-core-helpers.php
+++ b/includes/class-ccb-core-helpers.php
@@ -20,13 +20,13 @@
*/
class CCB_Core_Helpers {
- const SYNC_STATUS_KEY = 'ccb-core-sync-in-progress';
+ const SYNC_STATUS_KEY = 'ccb_core_sync_in_progress';
/**
* Instance of the Helper class
*
- * @var CCB_Core_Helpers
- * @access protected
+ * @var CCB_Core_Helpers
+ * @access private
* @static
*/
private static $instance;
@@ -34,15 +34,15 @@ class CCB_Core_Helpers {
/**
* The options set by the user
*
- * @var array
+ * @var array
*/
private $plugin_options = array();
/**
* Unused constructor in the singleton pattern
*
- * @access public
- * @return void
+ * @access public
+ * @return void
*/
public function __construct() {
// Initialize this class with the instance() method.
@@ -51,9 +51,9 @@ public function __construct() {
/**
* Returns the instance of the class
*
- * @access public
+ * @access public
* @static
- * @return CCB_Core_Helpers
+ * @return CCB_Core_Helpers
*/
public static function instance() {
if ( ! isset( static::$instance ) ) {
@@ -66,8 +66,8 @@ public static function instance() {
/**
* Initial setup of the singleton
*
- * @access private
- * @return void
+ * @access private
+ * @return void
*/
private function setup() {
// Get any options the user may have set.
@@ -77,7 +77,7 @@ private function setup() {
/**
* Get any options stored by the user
*
- * @return array
+ * @return array
*/
public function get_options() {
return $this->plugin_options;
@@ -141,9 +141,10 @@ public function decrypt( $data ) {
*
* @access public
* @since 1.0.0
+ * @param array $data Optional data to send back.
* @return bool
*/
- public function send_non_blocking_json_success() {
+ public function send_non_blocking_json_success( $data = array() ) {
ignore_user_abort( true );
ob_start();
@@ -151,7 +152,10 @@ public function send_non_blocking_json_success() {
header( 'Content-Type: application/json' );
header( 'Content-Encoding: none' );
- echo wp_json_encode( [ 'success' => true ] );
+ echo wp_json_encode( [
+ 'success' => true,
+ 'data' => $data,
+ ] );
header( 'Connection: close' );
header( 'Content-Length: ' . ob_get_length() );
@@ -237,6 +241,7 @@ public function download_image( $image_url, $filename = '', $post_id = 0 ) {
set_post_thumbnail( $post_id, $media_id );
}
+ // phpcs:ignore
@unlink( $temp_file );
if ( ! is_wp_error( $media_id ) ) {
@@ -257,8 +262,14 @@ public function download_image( $image_url, $filename = '', $post_id = 0 ) {
* @return array
*/
public function custom_uploads_directory( $upload ) {
- // Allow for the ability to enable / disable custom upload path.
- if ( apply_filters( 'ccb_core_allow_custom_upload_directory', true ) ) {
+ /**
+ * Allow for the ability to enable / disable custom upload path.
+ *
+ * @since 1.0.0
+ *
+ * @param bool $allowed Whether this plugin is allowed to use custom upload paths.
+ */
+ if ( apply_filters( 'ccb_core_allow_custom_uploads_directory', true ) ) {
$upload['path'] = trailingslashit( $upload['basedir'] ) . 'ccb';
$upload['url'] = $upload['baseurl'] . '/ccb';
$upload['subdir'] = '/ccb';
diff --git a/includes/class-ccb-core-plugin.php b/includes/class-ccb-core-plugin.php
deleted file mode 100644
index dbc1254..0000000
--- a/includes/class-ccb-core-plugin.php
+++ /dev/null
@@ -1,146 +0,0 @@
-
- */
-class CCB_Core_Plugin {
-
- /**
- * The unique identifier of this plugin.
- *
- * @since 0.9.0
- * @access protected
- * @var string $plugin_name The string used to uniquely identify this plugin.
- */
- protected $plugin_name;
-
- /**
- * The settings variable name used to access the plugin settings
- *
- * @since 0.9.0
- * @access protected
- * @var string $plugin_settings_name
- */
- protected $plugin_settings_name;
-
- /**
- * The display name of this plugin.
- *
- * @since 0.9.0
- * @access protected
- * @var string $plugin_display_name The display name of this plugin.
- */
- protected $plugin_display_name;
-
- /**
- * The short display name of this plugin.
- *
- * @since 0.9.0
- * @access protected
- * @var string $plugin_short_display_name The short display name of this plugin.
- */
- protected $plugin_short_display_name;
-
- /**
- * The current version of the plugin.
- *
- * @since 0.9.0
- * @access protected
- * @var string $version The current version of the plugin.
- */
- protected $version;
-
- /**
- * Define the core properties of the plugin
- *
- * Set the plugin name and the plugin version that can be used throughout the plugin.
- *
- * @since 0.9.0
- */
- public function __construct() {
-
- $this->plugin_name = 'ccb-core';
- $this->plugin_settings_name = 'ccb_core_settings';
- $this->plugin_display_name = __( 'Church Community Builder Core API', $this->plugin_name );
- $this->plugin_short_display_name = __( 'CCB Core API', $this->plugin_name );
- $this->version = '0.9.6';
- add_theme_support( 'post-thumbnails' );
-
- }
-
- /**
- * Helper function to check if a date is valid
- *
- * @param string $date The date.
- * @param string $format
- * @access protected
- * @since 0.9.0
- * @return bool
- */
- protected function valid_date( $date, $format = 'Y-m-d H:i:s' ) {
- $version = explode('.', phpversion());
- if ( (int) $version[0] >= 5 && (int) $version[1] >= 2 && (int) $version[2] > 17 ) {
- $d = DateTime::createFromFormat( $format, $date );
- } else {
- $d = new DateTime( date( $format, strtotime( $date ) ) );
- }
- return $d && $d->format( $format ) == $date;
- }
-
- /**
- * Gets the most recent synchronization results in the form
- * of an array with a style class and message
- *
- * @access protected
- * @since 0.9.0
- * @return array
- */
- protected function get_latest_sync_results() {
-
- $latest_sync = get_option( $this->plugin_name . '-latest-sync' );
-
- if ( is_array( $latest_sync ) && ! empty( $latest_sync ) ) {
-
- if ( $latest_sync['success'] == true ) {
-
- $latest_sync_message = array(
- 'style' => 'updated',
- 'description' => $latest_sync['message'],
- );
-
- }
- else {
- $latest_sync_message = array(
- 'style' => 'error',
- 'description' => $latest_sync['message'],
- );
- }
- }
- else {
- $latest_sync_message = array(
- 'style' => 'notice',
- 'description' => "It looks like you haven't synchronized anything yet."
- );
- }
-
- return $latest_sync_message;
-
- }
-
-}
-
diff --git a/includes/class-ccb-core-settings-field.php b/includes/class-ccb-core-settings-field.php
index bd6ca7b..a3587de 100644
--- a/includes/class-ccb-core-settings-field.php
+++ b/includes/class-ccb-core-settings-field.php
@@ -6,14 +6,14 @@
* @since 0.9.0
*
* @package CCB_Core
- * @subpackage CCB_Core/admin
+ * @subpackage CCB_Core/includes
*/
/**
* Object to manage the plugin settings fields
*
* @package CCB_Core
- * @subpackage CCB_Core/admin
+ * @subpackage CCB_Core/includes
* @author Jared Cobb
*/
class CCB_Core_Settings_Field {
@@ -276,9 +276,8 @@ protected function render_test_credentials() {
);
} else {
echo sprintf(
- '
-
-
+ '
+
',
esc_attr__( 'Test Credentials', 'ccb-core' )
);
@@ -293,22 +292,11 @@ protected function render_test_credentials() {
* @return void
*/
protected function render_manual_sync() {
- $sync_in_progress = get_transient( CCB_Core_Helpers::SYNC_STATUS_KEY );
-
- $sync_message = $sync_in_progress ? __( 'Syncronization in progress... You can safely navigate away from this page while we work hard in the background. (It should be just a moment).' ) : '';
- $button_disabled = $sync_in_progress ? 'disabled' : '';
- $spinner_active = $sync_in_progress ? 'is-active' :'';
-
echo sprintf(
'
',
- esc_attr( $button_disabled ),
- esc_attr__( 'Synchronize', 'ccb-core' ),
- esc_attr( $spinner_active ),
- esc_html( $sync_message )
+ esc_attr__( 'Synchronize', 'ccb-core' )
);
}
@@ -320,8 +308,8 @@ protected function render_manual_sync() {
* @return void
*/
protected function render_latest_results() {
- echo '
-
+ echo '
+
';
}
diff --git a/includes/class-ccb-core-settings-page.php b/includes/class-ccb-core-settings-page.php
index a3994ad..1b16a8f 100644
--- a/includes/class-ccb-core-settings-page.php
+++ b/includes/class-ccb-core-settings-page.php
@@ -6,14 +6,14 @@
* @since 0.9.0
*
* @package CCB_Core
- * @subpackage CCB_Core/admin
+ * @subpackage CCB_Core/includes
*/
/**
* Object to manage the plugin settings pages
*
* @package CCB_Core
- * @subpackage CCB_Core/admin
+ * @subpackage CCB_Core/includes
* @author Jared Cobb
*/
class CCB_Core_Settings_Page {
diff --git a/includes/class-ccb-core-settings-section.php b/includes/class-ccb-core-settings-section.php
index 3057a19..6674663 100644
--- a/includes/class-ccb-core-settings-section.php
+++ b/includes/class-ccb-core-settings-section.php
@@ -6,14 +6,14 @@
* @since 0.9.0
*
* @package CCB_Core
- * @subpackage CCB_Core/admin
+ * @subpackage CCB_Core/includes
*/
/**
* Object to manage the plugin settings sections
*
* @package CCB_Core
- * @subpackage CCB_Core/admin
+ * @subpackage CCB_Core/includes
* @author Jared Cobb
*/
class CCB_Core_Settings_Section {
@@ -125,17 +125,17 @@ public function render_section_about() {
echo '
- You\'ll need to ensure your group settings
- allow the group to be publicly listed. A great way to cross reference if your group is publicly visible is to visit ';
+ You\'ll need to ensure your group settings
+ have Public Search enabled (see the Options tab). A great way to cross reference if your group is publicly visible is to visit ';
// If the user has set their subdomain, use it for the url to w_group_list.php.
$options = CCB_Core_Helpers::instance()->get_options();
if ( ! empty( $options['subdomain'] ) ) {
echo sprintf(
'
- %1$s
+ your public search page
',
- esc_url( $options['subdomain'] )
+ esc_url( "https://{$options['subdomain']}.ccbchurch.com/w_group_list.php" )
);
} else {
echo 'https://[yoursite].ccbchurch.com/w_group_list.php';
diff --git a/includes/class-ccb-core-settings.php b/includes/class-ccb-core-settings.php
index 27e1b9b..42f3cba 100644
--- a/includes/class-ccb-core-settings.php
+++ b/includes/class-ccb-core-settings.php
@@ -6,14 +6,14 @@
* @since 0.9.0
*
* @package CCB_Core
- * @subpackage CCB_Core/admin
+ * @subpackage CCB_Core/includes
*/
/**
* Object to manage the plugin settings
*
* @package CCB_Core
- * @subpackage CCB_Core/admin
+ * @subpackage CCB_Core/includes
* @author Jared Cobb
*/
class CCB_Core_Settings {
@@ -38,7 +38,6 @@ public function validate_settings( $input ) {
switch ( $validation['field_validation'] ) {
case 'alphanumeric':
-
if ( empty( $input[ $field_id ] ) || ctype_alnum( $input[ $field_id ] ) ) {
$current_options[ $field_id ] = $input[ $field_id ];
} else {
@@ -57,7 +56,6 @@ public function validate_settings( $input ) {
break;
case 'numeric':
-
if ( empty( $input[ $field_id ] ) || ctype_digit( $input[ $field_id ] ) ) {
$current_options[ $field_id ] = $input[ $field_id ];
} else {
@@ -76,12 +74,9 @@ public function validate_settings( $input ) {
break;
case 'slug':
-
- // Continue onto alphanumeric_extended validation.
$input[ $field_id ] = sanitize_key( $input[ $field_id ] );
-
+ // Continue onto alphanumeric_extended validation because these are essentially the same.
case 'alphanumeric_extended':
-
if ( empty( $input[ $field_id ] ) || ! preg_match( '/[^\w\s-_]/', $input[ $field_id ] ) ) {
$current_options[ $field_id ] = $input[ $field_id ];
} else {
@@ -100,7 +95,6 @@ public function validate_settings( $input ) {
break;
case 'encrypt':
-
if ( ! empty( $input[ $field_id ]['password'] ) ) {
$encrypted_password = CCB_Core_Helpers::instance()->encrypt( $input[ $field_id ]['password'] );
if ( $encrypted_password ) {
@@ -118,7 +112,6 @@ public function validate_settings( $input ) {
break;
case 'switch':
-
$current_options[ $field_id ] = ( isset( $input[ $field_id ] ) && '1' === $input[ $field_id ] ? '1' : '' );
break;
@@ -194,7 +187,7 @@ public function get_settings_definitions() {
*
* @param array $settings The current array of settings definitions.
*/
- $settings = apply_filters( 'ccb_core_post_type_settings_definitions', $settings );
+ $settings = apply_filters( 'ccb_core_settings_post_definitions', $settings );
// Add a syncronization settings page.
$settings['ccb_core_settings_sync'] = array(
diff --git a/includes/class-ccb-core-synchronizer.php b/includes/class-ccb-core-synchronizer.php
index 180d9dd..4b16d92 100644
--- a/includes/class-ccb-core-synchronizer.php
+++ b/includes/class-ccb-core-synchronizer.php
@@ -19,31 +19,64 @@
*/
class CCB_Core_Synchronizer {
+ /**
+ * Instance of the class
+ *
+ * @var CCB_Core_Synchronizer
+ * @access private
+ * @static
+ */
+ private static $instance;
+
/**
* A complete mapping of CCB API data to post types and taxonomies
*
- * @var array
+ * @var array
*/
private $map;
/**
* An instance of the CCB_Core_API class
*
- * @var CCB_Core_API
+ * @var CCB_Core_API
*/
private $api;
/**
- * Initialize the class and set its properties.
+ * Unused constructor in the singleton pattern
*
- * @since 1.0.0
+ * @access public
+ * @return void
*/
public function __construct() {
- // Wait to initialize the map until after the plugins / themes are fully
- // loaded so that all post types and taxonomies have been registered.
- add_action( 'init', [ $this, 'initialize_map' ] );
+ // Initialize this class with the instance() method.
+ }
- $this->api = new CCB_Core_API();
+ /**
+ * Returns the instance of the class
+ *
+ * @access public
+ * @static
+ * @return CCB_Core_Synchronizer
+ */
+ public static function instance() {
+ if ( ! isset( static::$instance ) ) {
+ static::$instance = new CCB_Core_Synchronizer();
+ static::$instance->setup();
+ }
+ return static::$instance;
+ }
+
+ /**
+ * Initial setup of the singleton
+ *
+ * @since 1.0.0
+ */
+ private function setup() {
+ // Wait to initialize the map until after the plugins / themes are fully
+ // loaded so that all post types, taxonomies, and theme hooks have been registered.
+ add_action( 'init', [ $this, 'initialize_map' ], 9, 1 ); // Cron runs on `init` priority `10`.
+ $this->api = CCB_Core_API::instance();
}
/**
@@ -56,8 +89,55 @@ public function initialize_map() {
$post_type_maps = [];
$taxonomy_maps = [];
$this->map = array_merge_recursive(
- apply_filters( 'ccb_core_post_type_map', $post_type_maps ),
- apply_filters( 'ccb_core_taxonomy_map', $taxonomy_maps )
+ /**
+ * Get a collection of all post type / API mappings.
+ *
+ * This is the main configuration for how the CCB API
+ * maps to a custom post type.
+ *
+ * @since 1.0.0
+ *
+ * @param array $post_type_maps {
+ * A single map that defines the relationship between
+ * the CCB API entity node and the custom post type.
+ *
+ * $map[ {post_type} ] = [
+ * 'service' => {ccb_service_name},
+ * 'data' => [ {ccb_query_string_parameters} ],
+ * 'nodes' => [ {node list that maps to a single entity} ],
+ * 'fields' => [
+ * {entity_name_node} => 'post_title',
+ * {entity_description_node} => 'post_content',
+ * {any_other_node} => 'post_meta',
+ * {any_other_node} => 'post_meta',
+ * {any_other_node} => 'post_meta',
+ * ],
+ * ];
+ * }
+ */
+ apply_filters( 'ccb_core_synchronizer_post_api_map', $post_type_maps ),
+ /**
+ * Get a collection of all taxonomy / API mappings.
+ *
+ * This is the main configuration for how the CCB API
+ * maps some of the nodes on an entity to custom taxonomies.
+ *
+ * @since 1.0.0
+ *
+ * @param array $taxonomy_maps {
+ * A single map that defines the relationship between
+ * some of the nodes on a CCB entity to custom taxonomies.
+ *
+ * $map[ {post_type} ]['taxonomies']['hierarchical'][ {taxonomy} ] = [
+ * 'api_mapping' => {node},
+ * ];
+ *
+ * $map[ {post_type} ]['taxonomies']['nonhierarchical'][ {taxonomy} ] = [
+ * 'api_mapping' => [ {node} => {tag_name} ],
+ * ];
+ * }
+ */
+ apply_filters( 'ccb_core_synchronizer_taxonomy_api_map', $taxonomy_maps )
);
}
@@ -66,35 +146,100 @@ public function initialize_map() {
* taxonomies based on the mapping definitions from the
* custom post type and custom taxonomy classes.
*
- * @return bool True on success
+ * @return array
*/
public function synchronize() {
- // For each registered custom post type, call the API and
- // get a response object.
+ $result = [
+ 'success' => true,
+ ];
+
+ // Set a flag to globally signal that a sync is in progress.
+ set_transient( CCB_Core_Helpers::SYNC_STATUS_KEY, true, MINUTE_IN_SECONDS * 10 );
+
+ // For each registered custom post type, call the
+ // API and get a response object.
foreach ( $this->map as $post_type => $settings ) {
if ( ! empty( $settings['service'] ) ) {
+
$data = ! empty( $settings['data'] ) ? $settings['data'] : [];
- $response = $this->api->get( $settings['service'], $data );
+
+ /**
+ * Filters the API response for each service during the synchronization process.
+ *
+ * @since 1.0.0
+ *
+ * @param array $response The API response for a specific service call.
+ * @param array $settings The settings used for the API call.
+ * @param string $post_type The current post type being synchronized.
+ */
+ $response = apply_filters(
+ 'ccb_core_synchronizer_api_response',
+ $this->api->get( $settings['service'], $data ),
+ $settings,
+ $post_type
+ );
+
if ( 'SUCCESS' === $response['status'] ) {
- $update_successful = $this->update_content( $post_type, $settings, $response );
+
+ // A successful API request was made, update the WordPress content.
+ $update_result = $this->update_content( $response, $settings, $post_type );
+
+ if ( false === $update_result['success'] ) {
+ $result['success'] = false;
+ $result['message'] = esc_html__( 'At least one API synchronization failed', 'ccb-core' );
+ }
+ $result['services'][ $settings['service'] ] = $update_result;
+
+ } else {
+
+ $result['success'] = false;
+ $result['message'] = esc_html(
+ sprintf(
+ // Translators: Error message and error code.
+ __( 'There was an API error: %1$s. Error code: %2$s', 'ccb-core' ),
+ $response['error'],
+ $response['code']
+ )
+ );
+ break;
+
}
+
}
}
- return true;
+ $result['timestamp'] = time();
+
+ update_option( 'ccb_core_latest_sync_result', $result );
+ // Delete the sync in progress flag.
+ delete_transient( CCB_Core_Helpers::SYNC_STATUS_KEY );
+
+ return $result;
}
/**
* Takes an API response and will either Insert, Update, or Delete
* content based on the settings and any applicable existing content.
*
- * @param string $post_type The post type being updated.
- * @param array $settings The settings for the mapping.
* @param array $response An API response.
- * @return void
+ * @param array $settings The settings for the mapping.
+ * @param string $post_type The post type being updated.
+ * @return array
*/
- private function update_content( $post_type, $settings, $response ) {
+ private function update_content( $response, $settings, $post_type ) {
+
+ $result = [
+ 'success' => true,
+ 'insert_update' => [
+ 'success' => true,
+ 'processed' => 0,
+ ],
+ 'delete' => [
+ 'success' => true,
+ 'processed' => 0,
+ ],
+ ];
// The nodes are mapped down from the parent(s) to the
// single child object. Get a collection of entities
@@ -102,8 +247,8 @@ private function update_content( $post_type, $settings, $response ) {
$entities = $this->get_entities( $response, $settings['nodes'] );
// Get a collection of existing posts (previously imported) from WordPress.
- // This is organized by entitiy id (from CCB) and contains the WordPress
- // post_id and optional modified date.
+ // This is organized by a key of an entitiy id (from CCB) and contains
+ // the WordPress post_id and optional ccb_modified_date.
$post_data = $this->get_existing_post_data( $post_type );
// Organize the entities and existing post data into their
@@ -113,21 +258,62 @@ private function update_content( $post_type, $settings, $response ) {
// posts that no longer exist in CCB and should be deleted.
$organized_entities = $this->organize_entities( $entities, $post_data, $post_type );
- error_log( 'before insert ' . $post_type . ': ' . size_format( memory_get_usage() ) );
+ $insert_update_result = false;
if ( ! empty( $organized_entities['insert_update'] ) ) {
- $insert_result = $this->insert_update_entities( $organized_entities['insert_update'], $post_type, $settings );
+ $insert_update_result = $this->insert_update_entities( $organized_entities['insert_update'], $settings, $post_type );
}
- error_log( 'after insert ' . $post_type . ': ' . size_format( memory_get_usage() ) );
- error_log( 'before delete ' . $post_type . ': ' . size_format( memory_get_usage() ) );
+ $delete_result = false;
if ( ! empty( $organized_entities['delete'] ) ) {
$delete_result = $this->delete_posts( $organized_entities['delete'] );
}
- error_log( 'after delete ' . $post_type . ': ' . size_format( memory_get_usage() ) );
- $this->delete_empty_terms( $settings );
+ /**
+ * Whether or not we should clean up (delete) empty terms
+ * after a synchronization. Recommended to be true.
+ *
+ * @since 1.0.0
+ *
+ * @param bool $delete_terms Whether or not to delete empty terms.
+ * @param array $settings The settings for the current sync.
+ * @param string $post_type The current post type.
+ */
+ if ( apply_filters( 'ccb_core_synchronizer_delete_empty_terms', true, $settings, $post_type ) ) {
+ $this->delete_empty_terms( $settings );
+ }
+
+ // Setup the result array.
+ if ( ! empty( $insert_update_result ) ) {
+ $result['insert_update'] = $insert_update_result;
+ if ( false === $insert_update_result['success'] ) {
+ // Set the overall result as a failure also, then
+ // immediately return it.
+ $result['success'] = false;
+ return $result;
+ }
+ }
+
+ if ( ! empty( $delete_result ) ) {
+ $result['delete'] = $delete_result;
+ if ( false === $delete_result['success'] ) {
+ // Set the overall result as a failure also, then
+ // immediately return it.
+ $result['success'] = false;
+ return $result;
+ }
+ }
+
+ return $result;
}
+ /**
+ * Returns a collection of the CCB entities that will be processed.
+ *
+ * @param array $response The standardized response array.
+ * @param array $nodes A path to the single entity.
+ *
+ * @return SimpleXML A collection of entities.
+ */
private function get_entities( $response, $nodes ) {
if ( ! empty( $nodes ) ) {
$depth = count( $nodes ) - 1;
@@ -140,6 +326,20 @@ private function get_entities( $response, $nodes ) {
return false;
}
+ /**
+ * Returns a collection of post data representing
+ * the existing (already imported) CCB entities.
+ *
+ * Return structure:
+ *
+ * array[ $entity_id ][
+ * 'post_id' => $post_id,
+ * 'ccb_modified_date' => $ccb_modified_date,
+ * ]
+ *
+ * @param string $post_type The post type mapped to the CCB entitiy.
+ * @return array
+ */
private function get_existing_post_data( $post_type ) {
// Batch the WP_Query for performance.
$posts_per_page = 100;
@@ -157,11 +357,11 @@ private function get_existing_post_data( $post_type ) {
];
$posts = new WP_Query( $args );
+ $have_posts = ! empty( $posts->posts );
- if ( ! empty( $posts->posts ) ) {
+ if ( $have_posts ) {
foreach ( $posts->posts as $post_id ) {
-
- // These are saved during the insert / update process (of possible)
+ // These are saved during the insert / update process (if possible)
// in order to attempt future updates.
$entity_id = get_post_meta( $post_id, 'entity_id', true );
$ccb_modified_date = get_post_meta( $post_id, 'ccb_modified_date', true );
@@ -177,11 +377,28 @@ private function get_existing_post_data( $post_type ) {
$offset = $offset + $posts_per_page;
- } while ( count( $posts->posts ) );
+ } while ( $have_posts );
return $collection;
}
+ /**
+ * Returns an array where entities are seperated into
+ * different database operations. Currently organized into
+ * `insert_update` and `delete` collections.
+ *
+ * Return structure:
+ *
+ * array[
+ * 'insert_update' => array[$entities],
+ * 'delete' => array[$entities],
+ * ]
+ *
+ * @param SimpleXML $entities A parent collection of entities.
+ * @param array $post_data Existing posts (may be empty).
+ * @param string $post_type The post type to map to the entities.
+ * @return array
+ */
private function organize_entities( $entities, $post_data, $post_type ) {
$collection = [
@@ -195,7 +412,6 @@ private function organize_entities( $entities, $post_data, $post_type ) {
// so that we can find posts to delete.
$synced_entity_ids = [];
- error_log( 'before organize ' . $post_type . ': ' . size_format( memory_get_usage() ) );
foreach ( $entities->children() as $entity ) {
$entity_id = $this->get_entity_id( $entity );
@@ -207,7 +423,22 @@ private function organize_entities( $entities, $post_data, $post_type ) {
// signature. We use this to determine if we "already have this
// thing" so we don't need to insert / update it.
if ( 32 === strlen( $entity_id ) ) {
- if ( apply_filters( 'ccb_core_entity_insert_allowed', true, $entity_id, $entity, $post_type ) ) {
+
+ /**
+ * Whether or not this specific entity is allowed to insert.
+ *
+ * Helpful if you need to inspect an entity for custom business
+ * rules before allowing an insert to happen. For example, `group_profiles`
+ * will send us inactive groups. We do not want to insert inactive groups.
+ *
+ * @since 1.0.0
+ *
+ * @param bool $allowed Whether or not the entity is allowed.
+ * @param SimpleXML $entity The entity to insert.
+ * @param mixed $entity_id The unique identifier from CCB.
+ * @param string $post_type The current post type.
+ */
+ if ( apply_filters( 'ccb_core_synchronizer_entity_insert_allowed', true, $entity, $entity_id, $post_type ) ) {
// If the unique id for the new entity couldn't be found
// in the existing collection, it is new. Add it
// to the insert collection.
@@ -215,29 +446,43 @@ private function organize_entities( $entities, $post_data, $post_type ) {
$entity->addChild( 'post_id', 0 ); // This is a WordPress post ID, so this will be an insert.
$collection['insert_update'][] = $entity;
}
- // Regardless of whether or not this unique entity exists
+ // Regardless of whether or not this unique entity already exists
// (i.e. regardless of an insert or update) we still record
// that it was synced so that it doesn't get deleted.
$synced_entity_ids[] = $entity_id;
}
} else {
if ( array_key_exists( $entity_id, $post_data ) && ! empty( $entity->modified ) ) {
- if ( apply_filters( 'ccb_core_entity_update_allowed', true, $entity_id, $entity, $post_type ) ) {
+ /**
+ * Whether or not this specific entity is allowed to update.
+ *
+ * Helpful if you need to inspect an entity for custom business
+ * rules before allowing an update to happen.
+ *
+ * @since 1.0.0
+ *
+ * @param bool $allowed Whether or not the entity is allowed.
+ * @param SimpleXML $entity The entity to insert.
+ * @param mixed $entity_id The unique identifier from CCB.
+ * @param string $post_type The current post type.
+ */
+ if ( apply_filters( 'ccb_core_synchronizer_entity_update_allowed', true, $entity, $entity_id, $post_type ) ) {
// If an existing post has a newer modified date from the API
// add it to the insert_update collection with its existing post id.
if ( strtotime( $entity->modified ) > strtotime( $post_data[ $entity_id ]['ccb_modified_date'] ) ) {
$entity->addChild( 'post_id', $post_data[ $entity_id ]['post_id'] ); // This is a WordPress post ID, so this will be an update.
$collection['insert_update'][] = $entity;
}
- // Even though we may not have made an update
- // we still add this as a "synced" id because it exists.
+ // Even though we may not have made an update (i.e. it currently
+ // exists but hasn't changed), we still add this as a "synced" id
+ // so that it doesn't get deleted.
$synced_entity_ids[] = $entity_id;
}
} else {
// The unique id for the new entity couldn't be found
// in the existing collection, so it is new. Add it
// to the insert_update collection.
- if ( apply_filters( 'ccb_core_entity_insert_allowed', true, $entity_id, $entity, $post_type ) ) {
+ if ( apply_filters( 'ccb_core_synchronizer_entity_insert_allowed', true, $entity, $entity_id, $post_type ) ) {
$entity->addChild( 'post_id', 0 ); // This is a WordPress post ID, so this will be an insert.
$collection['insert_update'][] = $entity;
$synced_entity_ids[] = $entity_id;
@@ -247,13 +492,25 @@ private function organize_entities( $entities, $post_data, $post_type ) {
}
- error_log( 'after organize ' . $post_type . ': ' . size_format( memory_get_usage() ) );
// For each existing post, check to see if it was included
- // in the new entity id collection from this most recent
- // API response. If it doesn't exist, it was deleted.
+ // in this entity id collection from this most recent
+ // API response. If it doesn't exist, it was deleted in CCB.
foreach ( $post_data as $entity_id => $data ) {
if ( ! in_array( $entity_id, $synced_entity_ids, true ) ) {
- if ( apply_filters( 'ccb_core_entity_delete_allowed', true, $entity_id, $data, $post_type ) ) {
+ /**
+ * Whether or not this specific entity is allowed to delete.
+ *
+ * Helpful if you need to inspect an entity for custom business
+ * rules before allowing a delete to happen.
+ *
+ * @since 1.0.0
+ *
+ * @param bool $allowed Whether or not the entity is allowed.
+ * @param array $data The WordPress post data.
+ * @param mixed $entity_id The unique identifier from CCB.
+ * @param string $post_type The current post type.
+ */
+ if ( apply_filters( 'ccb_core_synchronizer_entity_delete_allowed', true, $data, $entity_id, $post_type ) ) {
$collection['delete'][] = $data['post_id'];
}
}
@@ -262,7 +519,24 @@ private function organize_entities( $entities, $post_data, $post_type ) {
return $collection;
}
+ /**
+ * Returns a unique identifier for the CCB entitiy.
+ *
+ * If CCB included their own id (for example a group id) we
+ * use that. Otherwise we hash the string value of the entity.
+ *
+ * @param SimpleXML $entity A single entity object.
+ * @return mixed An integer id or string hash.
+ */
private function get_entity_id( $entity ) {
+ $entity_id = '';
+ // As part of the insert / update process we sometimes append
+ // a post_id to the entitiy. Ensure we remove it before hashing
+ // the string value so that it represents what we received from CCB.
+ if ( isset( $entity->post_id ) ) {
+ unset( $entity->post_id );
+ }
+
// If an entity doesn't have a CCB ID, we cannot match it during
// the sync process in order to perform an update. So instead,
// create a hash of the entity and store it as a unique identifier.
@@ -270,33 +544,51 @@ private function get_entity_id( $entity ) {
// If an entity has an actual CCB ID, but *doesn't* have a modified
// date, there's no point in storing the CCB ID. This is because
// without a modified date we cannot determine if the entity has
- // changed since the previous sync. Instead, just create a has of the entity.
+ // changed since the previous sync. Instead, just create a hash of the entity.
if ( ! empty( $entity->attributes() ) && ! empty( $entity->modified ) ) {
foreach ( $entity->attributes() as $key => $value ) {
if ( false !== stristr( $key, 'id' ) ) {
- return (int) $value;
+ $entity_id = (int) $value;
+ break;
}
}
}
- if ( isset( $entity->post_id ) ) {
- unset( $entity->post_id );
+
+ if ( ! $entity_id ) {
+ $entity_id = md5( $entity->asXML() );
}
- return md5( $entity->asXML() );
+
+ /**
+ * Filter the unique entity_id that we attempt to
+ * auto detect from the entity object
+ *
+ * @since 1.0.0
+ *
+ * @param mixed $entity_id Either an integer id or hash.
+ * @param SimpleXML $entity The current entity object.
+ */
+ return apply_filters( 'ccb_core_synchronizer_get_entity_id', $entity_id, $entity );
}
- private function insert_update_entities( $entities, $post_type, $settings ) {
- global $wpdb;
- // Temporarily disable counting for performance.
- wp_defer_term_counting( true );
- wp_defer_comment_counting( true );
- wp_suspend_cache_addition( true );
- remove_action( 'do_pings', 'do_all_pings', 10, 1 );
+ /**
+ * Inserts / Updates entities into WordPress.
+ *
+ * @param array $entities A collection of SimpleXML entity objects.
+ * @param array $settings The settings for the import.
+ * @param string $post_type The current post type.
+ * @return array
+ */
+ private function insert_update_entities( $entities, $settings, $post_type ) {
- error_log( 'after setting optimizations ' . $post_type . ': ' . size_format( memory_get_usage() ) );
+ // Allow this script to run longer.
+ set_time_limit( MINUTE_IN_SECONDS * 10 );
+ $this->enable_optimizations();
- // Temporarily disable autocommit.
- $wpdb->query( 'SET autocommit = 0;' ); // db call ok; no cache ok.
- set_time_limit( 600 );
+ $result = [
+ 'success' => true,
+ 'processed' => 0,
+ 'message' => '',
+ ];
foreach ( $entities as $entity ) {
// Build a new $args array for each post insert
@@ -312,13 +604,13 @@ private function insert_update_entities( $entities, $post_type, $settings ) {
];
// Inspect each field defined in the settings. If it's a
- // post_meta element, add it to the meta_input array, otherwise
+ // post_meta node, add it to the meta_input array, otherwise
// assume it's part of the parent post object.
- foreach ( $settings['fields'] as $element => $field ) {
+ foreach ( $settings['fields'] as $node => $field ) {
if ( 'post_meta' === $field ) {
- $args['meta_input'][ $element ] = $this->auto_cast( $entity->{$element} );
+ $args['meta_input'][ $node ] = $this->auto_cast( $entity->{$node} );
} else {
- $args[ $field ] = $this->auto_cast( $entity->{$element} );
+ $args[ $field ] = $this->auto_cast( $entity->{$node} );
}
}
@@ -341,7 +633,7 @@ private function insert_update_entities( $entities, $post_type, $settings ) {
// If this is an update, we need to remove all term
// relationships in order to ensure we are in
// sync with the most recent entity. Otherwise if a
- // relationship was removed by CCB, it'll persist in WordPress.
+ // relationship was removed by CCB, it'll be orphaned in WordPress.
if ( $args['ID'] ) {
if ( ! empty( $settings['taxonomies']['hierarchical'] ) ) {
foreach ( $settings['taxonomies']['hierarchical'] as $taxonomy => $node ) {
@@ -355,42 +647,110 @@ private function insert_update_entities( $entities, $post_type, $settings ) {
}
}
+ /**
+ * Filters the `wp_insert_post` $args for each entity.
+ *
+ * @since 1.0.0
+ *
+ * @param array $args The `wp_insert_post` args.
+ * @param SimpleXML $entity The entity object.
+ * @param array $settings The current settings for the sync.
+ * @param string $post_type The current post type.
+ */
+ $args = apply_filters( 'ccb_core_synchronizer_insert_post_args', $args, $entity, $settings, $post_type );
+
+ /**
+ * Before the insert / update is processed.
+ *
+ * @since 1.0.0
+ *
+ * @param SimpleXML $entity The entity object to be inserted / updated.
+ * @param array $settings The current settings for the import.
+ * @param array $args The args that will be used for wp_insert_post.
+ * @param string $post_type The current post type.
+ */
do_action( 'ccb_core_before_insert_update_post', $entity, $settings, $args, $post_type );
// Perform an insert (or update if we included a post id).
$post_id = wp_insert_post( $args, true );
+ if ( is_wp_error( $post_id ) ) {
+ $result['success'] = false;
+ $result['message'] = esc_html(
+ sprintf(
+ __( 'Inserting / Updating a post failed for the %1$s post type. Error: %2$s', 'ccb-core' ),
+ $post_type,
+ $post_id->get_error_message()
+ )
+ );
+ $this->disable_optimizations();
+ return $result;
+ }
+
+ /**
+ * After the insert / update is processed.
+ *
+ * Useful if you need to run custom business logic after a post is
+ * inserted / updated. For example, this is how we import and attach
+ * featured images for Groups.
+ *
+ * @since 1.0.0
+ *
+ * @param SimpleXML $entity The entity object to be inserted / updated.
+ * @param array $settings The current settings for the import.
+ * @param array $args The args that will be used for wp_insert_post.
+ * @param string $post_type The current post type.
+ * @param int $post_id The new post id (or existing post id if update).
+ */
do_action( 'ccb_core_after_insert_update_post', $entity, $settings, $args, $post_type, $post_id );
- }
- error_log( 'after loop, before commit ' . $post_type . ': ' . size_format( memory_get_usage() ) );
+ $result['processed'] += 1;
- // Commit the database operations now.
- $wpdb->query( 'COMMIT;' ); // db call ok; no cache ok.
- // Re-enable autocommit.
- $wpdb->query( 'SET autocommit = 1;' ); // db call ok; no cache ok.
+ }
- error_log( 'after loop, before stop_the_insanity ' . $post_type . ': ' . size_format( memory_get_usage() ) );
+ $this->disable_optimizations();
- // Re-enable counting.
- wp_suspend_cache_addition( false );
- wp_defer_term_counting( false );
- wp_defer_comment_counting( false );
- $this->stop_the_insanity();
+ return $result;
}
+ /**
+ * Deletes any posts that no longer exist in CCB.
+ *
+ * @param array $post_ids A collection of post ids to delete.
+ * @return array
+ */
private function delete_posts( $post_ids ) {
+ $result = [
+ 'success' => true,
+ 'processed' => 0,
+ 'message' => '',
+ ];
+
foreach ( $post_ids as $post_id ) {
- wp_delete_post( $post_id, true );
+ $deleted = wp_delete_post( $post_id, true );
+ if ( ! $deleted ) {
+ $result['success'] = false;
+ $result['message'] = esc_html__( 'There was an error while attempting to delete orhpaned posts', 'ccb-core' );
+ return $result;
+ }
+ $result['processed'] += 1;
}
- return true;
+ return $result;
}
+ /**
+ * Cleans up any orphaned terms after a sync.
+ *
+ * @param array $settings Definitions of taxonomies.
+ * @return void
+ */
private function delete_empty_terms( $settings ) {
+ // Ensure we get empty terms.
$args = [
'hide_empty' => false,
];
+ // Build a collection of all taxonomies configured.
if ( ! empty( $settings['taxonomies']['hierarchical'] ) ) {
foreach ( $settings['taxonomies']['hierarchical'] as $taxonomy => $node ) {
$args['taxonomy'][] = $taxonomy;
@@ -402,16 +762,23 @@ private function delete_empty_terms( $settings ) {
}
}
+ // Delete any terms with a 0 count.
$terms_query = new WP_Term_Query( $args );
foreach ( $terms_query->get_terms() as $term ) {
if ( 0 === $term->count ) {
wp_delete_term( $term->term_id, $term->taxonomy );
}
}
-
- return true;
}
+ /**
+ * Returns an array of hierarchical and nonhierarchical terms
+ * that are ready for use by wp_insert_post.
+ *
+ * @param SimpleXML $entity An entity object.
+ * @param array $settings The taxonomy settings.
+ * @return array
+ */
private function prepare_terms( $entity, $settings ) {
$categories = [];
$tags = [];
@@ -420,6 +787,7 @@ private function prepare_terms( $entity, $settings ) {
foreach ( $settings['taxonomies']['hierarchical'] as $taxonomy => $node ) {
$term_name = $this->auto_cast( $entity->{$node} );
if ( $term_name ) {
+ // phpcs:ignore
$term = term_exists( $term_name, $taxonomy );
if ( ! $term ) {
$term = wp_insert_term( $term_name, $taxonomy );
@@ -445,14 +813,23 @@ private function prepare_terms( $entity, $settings ) {
return array_merge( $categories, $tags );
}
- private function auto_cast( $element ) {
- if ( $element->children()->count() ) {
+ /**
+ * Attempt to cast a SimpleXML node to a strong type.
+ * Will recursively process arrays.
+ *
+ * @param SimpleXML $node A single node on the entitiy.
+ * @return mixed
+ */
+ private function auto_cast( $node ) {
+ // If the node has children, convert it to an array
+ // and recursively process the child nodes.
+ if ( $node->children()->count() ) {
$array = [];
// If the node happens to have an id attribute,
// transform it into a property so that it's not lost.
$id = false;
- if ( ! empty( $element->attributes() ) ) {
- foreach ( $element->attributes() as $key => $value ) {
+ if ( ! empty( $node->attributes() ) ) {
+ foreach ( $node->attributes() as $key => $value ) {
if ( false !== stristr( $key, 'id' ) ) {
$id = (int) $value;
break;
@@ -462,38 +839,64 @@ private function auto_cast( $element ) {
if ( $id ) {
$array['id'] = $id;
}
- foreach ( $element->children() as $child ) {
+ foreach ( $node->children() as $child ) {
$array[ $child->getName() ] = $this->auto_cast( $child );
}
return $array;
- } elseif ( 'true' === (string) $element ) {
+ } elseif ( 'true' === (string) $node ) {
return true;
- } elseif ( 'false' === (string) $element ) {
+ } elseif ( 'false' === (string) $node ) {
return false;
- } elseif ( strlen( (int) $element ) === strlen( (string) $element ) ) {
- return (int) $element;
+ } elseif ( strlen( (int) $node ) === strlen( (string) $node ) ) {
+ return (int) $node;
} else {
- return (string) $element;
+ return (string) $node;
}
}
/**
- * Clear all of the caches for memory management
+ * Configure some common optimizations for bulk
+ * insert / update processing.
+ *
+ * @return void
*/
- private function stop_the_insanity() {
- global $wpdb, $wp_object_cache;
+ private function enable_optimizations() {
+ global $wpdb;
- $wpdb->queries = array();
+ // Remove expensive unneeded actions.
+ remove_action( 'do_pings', 'do_all_pings', 10, 1 );
- if ( is_object( $wp_object_cache ) ) {
- $wp_object_cache->group_ops = array();
- $wp_object_cache->stats = array();
- $wp_object_cache->memcache_debug = array();
- $wp_object_cache->cache = array();
+ // Temporarily disable counting for performance.
+ wp_defer_term_counting( true );
+ wp_defer_comment_counting( true );
+ wp_suspend_cache_addition( true );
- if ( method_exists( $wp_object_cache, '__remoteset' ) ) {
- $wp_object_cache->__remoteset();
- }
- }
+ // Temporarily disable autocommit.
+ $wpdb->query( 'SET autocommit = 0;' ); // db call ok; no cache ok.
}
+
+ /**
+ * Remove the optimizations after processing is complete.
+ *
+ * @return void
+ */
+ private function disable_optimizations() {
+ global $wpdb;
+
+ // Commit the database operations now.
+ $wpdb->query( 'COMMIT;' ); // db call ok; no cache ok.
+ // Re-enable autocommit.
+ $wpdb->query( 'SET autocommit = 1;' ); // db call ok; no cache ok.
+
+ // Re-enable counting.
+ wp_suspend_cache_addition( false );
+ wp_defer_term_counting( false );
+ wp_defer_comment_counting( false );
+
+ // Re-attach expensive actions.
+ add_action( 'do_pings', 'do_all_pings', 10, 1 );
+ }
+
}
+
+CCB_Core_Synchronizer::instance();
diff --git a/includes/class-ccb-core.php b/includes/class-ccb-core.php
index 5248e9f..b53bb68 100644
--- a/includes/class-ccb-core.php
+++ b/includes/class-ccb-core.php
@@ -85,6 +85,9 @@ private function load_dependencies() {
// Admin AJAX methods.
require_once CCB_CORE_PATH . 'includes/class-ccb-core-admin-ajax.php';
+ // Cron Management.
+ require_once CCB_CORE_PATH . 'includes/class-ccb-core-cron.php';
+
}
/**
@@ -304,6 +307,11 @@ public function enqueue_scripts( $hook ) {
'CCB_CORE_SETTINGS',
array(
'nonce' => wp_create_nonce( 'ccb_core_nonce' ),
+ 'translations' => [
+ 'credentialsSuccessful' => esc_html__( 'The credentials were successfully authenticated.', 'ccb-core' ),
+ 'credentialsFailed' => esc_html__( 'The credentials failed authentication', 'ccb-core' ),
+ 'syncInProgress' => esc_html__( 'Syncronization in progress... You can safely navigate away from this page while we work in the background.', 'ccb-core' ),
+ ],
)
);
}
diff --git a/includes/post-types/class-ccb-core-calendar.php b/includes/post-types/class-ccb-core-calendar.php
index 895b0ca..7894870 100644
--- a/includes/post-types/class-ccb-core-calendar.php
+++ b/includes/post-types/class-ccb-core-calendar.php
@@ -1,6 +1,6 @@
'',
'field_default' => 'relative',
- 'field_attributes' => array( 'class' => 'date-range-type', 'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1}' ),
+ 'field_attributes' => array(
+ 'class' => 'date-range-type',
+ 'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1}',
+ ),
'field_tooltip' => sprintf(
esc_html__(
'Relative: For example, always get the events from "One week ago", up to "Eight weeks from now".%1$s
@@ -269,10 +272,9 @@ public function get_post_settings_definitions( $settings ) {
* @param array $map A collection of mappings from the API to WordPress.
* @return array
*/
- public function get_post_type_map( $map ) {
+ public function get_post_api_map( $map ) {
if ( $this->enabled ) {
- $options = CCB_Core_Helpers::instance()->get_options();
- $calendar_options = $this->get_calendar_options( $options );
+ $calendar_options = $this->get_calendar_options();
$map[ $this->name ] = [
'service' => 'public_calendar_listing',
@@ -295,11 +297,19 @@ public function get_post_type_map( $map ) {
return $map;
}
- private function get_calendar_options( $options ) {
+ /**
+ * Returns a standardized configuration array of
+ * start and end dates to be used by the API call.
+ *
+ * @return array
+ */
+ private function get_calendar_options() {
+ $options = CCB_Core_Helpers::instance()->get_options();
+
// By default, set some sane limits.
$calendar_options = [
'date_start' => date( 'Y-m-d', strtotime( '1 weeks ago' ) ),
- 'date_end' => date( 'Y-m-d', strtotime( '+16 weeks' ) ),
+ 'date_end' => date( 'Y-m-d', strtotime( '+8 weeks' ) ),
];
// If the user has set a preferred date range type.
diff --git a/includes/post-types/class-ccb-core-cpt.php b/includes/post-types/class-ccb-core-cpt.php
index 227f879..76363b0 100644
--- a/includes/post-types/class-ccb-core-cpt.php
+++ b/includes/post-types/class-ccb-core-cpt.php
@@ -6,14 +6,14 @@
* @since 1.0.0
*
* @package CCB_Core
- * @subpackage CCB_Core/admin
+ * @subpackage CCB_Core/includes/post-types
*/
/**
* Abstract custom post type used help define all CPTs
*
* @package CCB_Core
- * @subpackage CCB_Core/admin
+ * @subpackage CCB_Core/includes/post-types
* @author Jared Cobb
*/
abstract class CCB_Core_CPT {
@@ -26,23 +26,23 @@ abstract class CCB_Core_CPT {
public $name;
/**
- * Whether or not this post type is enabled. (Set by child class).
+ * Whether or not this post type is enabled. (Overriden by child class).
*
* @var bool
*/
- public $enabled;
+ public $enabled = false;
/**
* Initialize the class
*/
public function __construct() {
- add_filter( 'ccb_core_post_type_settings_definitions', array( $this, 'get_post_settings_definitions' ) );
+ add_filter( 'ccb_core_settings_post_definitions', array( $this, 'get_post_settings_definitions' ) );
// If this custom post type is enabled, merge the defaults and set the registration hook.
if ( $this->enabled ) {
add_action( 'init', array( $this, 'register_post_type' ) );
- add_filter( 'ccb_core_post_type_map', array( $this, 'get_post_type_map' ) );
+ add_filter( 'ccb_core_synchronizer_post_api_map', array( $this, 'get_post_api_map' ) );
}
}
@@ -91,6 +91,6 @@ abstract public function get_post_settings_definitions( $settings );
* @param array $map A collection of mappings from the API to WordPress.
* @return array
*/
- abstract public function get_post_type_map( $map );
+ abstract public function get_post_api_map( $map );
}
diff --git a/includes/post-types/class-ccb-core-group.php b/includes/post-types/class-ccb-core-group.php
index 2e5723b..b681799 100644
--- a/includes/post-types/class-ccb-core-group.php
+++ b/includes/post-types/class-ccb-core-group.php
@@ -29,8 +29,8 @@ class CCB_Core_Group extends CCB_Core_CPT {
* Initialize the class
*/
public function __construct() {
- add_filter( 'ccb_core_entity_insert_allowed', [ $this, 'entity_insert_update_allowed' ], 10, 4 );
- add_filter( 'ccb_core_entity_update_allowed', [ $this, 'entity_insert_update_allowed' ], 10, 4 );
+ add_filter( 'ccb_core_synchronizer_entity_insert_allowed', [ $this, 'entity_insert_update_allowed' ], 10, 4 );
+ add_filter( 'ccb_core_synchronizer_entity_update_allowed', [ $this, 'entity_insert_update_allowed' ], 10, 4 );
add_filter( 'ccb_core_after_insert_update_post', [ $this, 'attach_group_image' ], 10, 5 );
$options = CCB_Core_Helpers::instance()->get_options();
@@ -42,7 +42,7 @@ public function __construct() {
* Setup the custom post type args
*
* @since 1.0.0
- * @return array $args for register_post_type
+ * @return array $args for register_post_type
*/
public function get_post_args() {
@@ -218,7 +218,7 @@ public function get_post_settings_definitions( $settings ) {
* @param array $map A collection of mappings from the API to WordPress.
* @return array
*/
- public function get_post_type_map( $map ) {
+ public function get_post_api_map( $map ) {
if ( $this->enabled ) {
$options = CCB_Core_Helpers::instance()->get_options();
$include_image_link = ! empty( $options['groups_import_images'] ) && 'yes' === $options['groups_import_images'] ? true : false;
@@ -244,7 +244,20 @@ public function get_post_type_map( $map ) {
return $map;
}
- public function entity_insert_update_allowed( $allowed, $entity_id, $entity, $post_type ) {
+ /**
+ * Callback function for `ccb_core_synchronizer_entity_insert_allowed` and
+ * `ccb_core_synchronizer_entity_update_allowed` so that we can filter OUT
+ * inactive and non-public groups from an import.
+ *
+ * @since 1.0.0
+ *
+ * @param bool $allowed Whether an insert/update is allowed.
+ * @param SimpleXML $entity The specific entity object.
+ * @param mixed $entity_id A unique entity id.
+ * @param string $post_type The current post type.
+ * @return bool
+ */
+ public function entity_insert_update_allowed( $allowed, $entity, $entity_id, $post_type ) {
if ( $this->name === $post_type ) {
// Only allow active, publicly listed groups to be imported.
if ( 'true' === (string) $entity->inactive || 'false' === (string) $entity->public_search_listed ) {
@@ -254,6 +267,20 @@ public function entity_insert_update_allowed( $allowed, $entity_id, $entity, $po
return $allowed;
}
+ /**
+ * Checks whether downloading group images is enabled
+ * and an entity has an image attachment, then attaches
+ * the image as a featured image.
+ *
+ * @since 1.0.0
+ *
+ * @param SimpleXML $entity The entity object.
+ * @param array $settings The settings array for the import.
+ * @param array $args The `wp_insert_post` args.
+ * @param string $post_type The current post type.
+ * @param int $post_id The WordPress post id of this post.
+ * @return void
+ */
public function attach_group_image( $entity, $settings, $args, $post_type, $post_id ) {
if ( $this->name === $post_type && $settings['data']['include_image_link'] ) {
$image_url = (string) $entity->image;
diff --git a/includes/taxonomies/class-ccb-core-group-day.php b/includes/taxonomies/class-ccb-core-group-day.php
index f17891f..d838c20 100644
--- a/includes/taxonomies/class-ccb-core-group-day.php
+++ b/includes/taxonomies/class-ccb-core-group-day.php
@@ -61,3 +61,5 @@ public static function get_taxonomy_args() {
}
}
+
+new CCB_Core_Group_Day();
diff --git a/includes/taxonomies/class-ccb-core-taxonomy.php b/includes/taxonomies/class-ccb-core-taxonomy.php
index 58ffeaa..3849cf7 100644
--- a/includes/taxonomies/class-ccb-core-taxonomy.php
+++ b/includes/taxonomies/class-ccb-core-taxonomy.php
@@ -37,7 +37,7 @@ abstract class CCB_Core_Taxonomy {
*/
public function __construct() {
add_action( 'init', array( $this, 'register_taxonomy' ) );
- add_filter( 'ccb_core_taxonomy_map', [ $this, 'get_taxonomy_map' ] );
+ add_filter( 'ccb_core_synchronizer_taxonomy_api_map', [ $this, 'get_taxonomy_map' ] );
}
/**
diff --git a/js/ccb-core-admin.js b/js/ccb-core-admin.js
index 8e62f9b..970abaa 100644
--- a/js/ccb-core-admin.js
+++ b/js/ccb-core-admin.js
@@ -2,16 +2,17 @@
'use strict';
- var ccbCoreAdmin = {
+ var CCBCoreAdmin = {
- syncPollId : '',
+ syncPollId : null,
+ spinner : '',
initialize : function() {
this.syncPollId = setInterval(this.pollForActiveSync, 10000);
this.pollForActiveSync();
- $('.test-login-wrapper .button').on('click', window.event, this.testCredentials);
+ $('.test-credentials-wrapper .button').on('click', window.event, this.testCredentials);
$('.sync-wrapper .button').on('click', window.event, this.syncData);
@@ -93,13 +94,18 @@
};
$.post(ajaxurl, data, function(response) {
+ if (true === response.success) {
+ var $resultsWrapper = $('.ccb-core-latest-results');
+ var $syncButton = $('.sync-wrapper .button');
+ var className = response.data.success ? 'notice-info' : 'notice-error';
+ var $content = $('');
- var $latestSyncMessageWrapper = $('.ccb-core-latest-results');
- var labelContent = 'Latest Sync Results
' + response.description;
-
- $latestSyncMessageWrapper.removeClass('error notice updated').addClass(response.style);
- $latestSyncMessageWrapper.empty().append(labelContent);
+ $content.append('' + response.data.message + '
');
+ $resultsWrapper.append($content);
+ $resultsWrapper.find('.spinner').remove();
+ $syncButton.removeClass('disabled');
+ }
});
},
@@ -111,97 +117,114 @@
'nonce': CCB_CORE_SETTINGS.nonce
};
- $.post(ajaxurl, data, function(response) {
- if ( response.syncInProgress == false ) {
- clearInterval(ccbCoreAdmin.syncPollId);
- ccbCoreAdmin.updateLatestSync();
-
- var $spinner = $('.sync-wrapper .spinner');
- var $syncButtons = $('.sync-wrapper .button');
- var $syncMessages = $('div.in-progress-message');
-
- $spinner.removeClass('is-active');
- $syncButtons.removeClass('disabled');
- $syncMessages.remove();
+ CCBCoreAdmin.disableUI();
+ $.post(ajaxurl, data, function(response) {
+ if (true === response.success) {
+ if (false === response.data) {
+ // A sync is not currently in progress.
+ clearInterval(CCBCoreAdmin.syncPollId);
+ CCBCoreAdmin.updateLatestSync();
+ } else {
+ CCBCoreAdmin.adminNotice( CCB_CORE_SETTINGS.translations.syncInProgress, 'warning' );
+ }
}
});
},
- testCredentials : function(event) {
+ syncData : function(event) {
+
event.preventDefault();
- var $clickedButton = $(this);
- if ( $clickedButton.hasClass('disabled') ) {
+ var $syncButton = $('.sync-wrapper .button');
+
+ if ( $syncButton.hasClass('disabled') ) {
return false;
}
- var $spinner = $('.test-login-wrapper .spinner');
- var $testLoginWrapper = $('.test-login-wrapper');
-
- $clickedButton.addClass('disabled');
- $spinner.addClass('is-active');
- $testLoginWrapper.find('.ajax-message').remove();
+ CCBCoreAdmin.disableUI();
var data = {
- 'action': 'test_credentials',
+ 'action': 'sync',
'nonce': CCB_CORE_SETTINGS.nonce
};
$.post(ajaxurl, data, function(response) {
-
- $clickedButton.removeClass('disabled');
- $spinner.removeClass('is-active');
-
- if (response.success === false) {
- $testLoginWrapper.append('' + response.data + '
');
- }
- else if (typeof response.services !== 'undefined' && response.services.length > 0) {
-
- $.each(response.services, function( index, value ){
-
- var divClass = (value.success === true ? 'updated' : 'error');
- $testLoginWrapper.append('' + value.label + ': ' + value.message + '
');
-
- });
-
- }
-
+ CCBCoreAdmin.adminNotice( CCB_CORE_SETTINGS.translations.syncInProgress, 'warning' );
+ CCBCoreAdmin.syncPollId = setInterval(CCBCoreAdmin.pollForActiveSync, 10000);
});
- },
- syncData : function(event) {
+ },
+ testCredentials : function(event) {
event.preventDefault();
var $clickedButton = $(this);
-
if ( $clickedButton.hasClass('disabled') ) {
return false;
}
- var $syncWrapper = $clickedButton.parents('.sync-wrapper');
- var $spinner = $syncWrapper.find('.spinner');
+ var $testCredentialsWrapper = $('.test-credentials-wrapper');
$clickedButton.addClass('disabled');
- $spinner.addClass('is-active');
+ CCBCoreAdmin.removeNotice();
var data = {
- 'action': 'sync',
+ 'action': 'test_credentials',
'nonce': CCB_CORE_SETTINGS.nonce
};
$.post(ajaxurl, data, function(response) {
- $syncWrapper.append('Syncronization in progress... You can safely navigate away from this page while we work hard in the background. (It should be just a moment).
');
- ccbCoreAdmin.syncPollId = setInterval(ccbCoreAdmin.pollForActiveSync, 10000);
+
+ if (true === response.success) {
+ CCBCoreAdmin.adminNotice( CCB_CORE_SETTINGS.translations.credentialsSuccessful );
+ } else {
+ CCBCoreAdmin.adminNotice( CCB_CORE_SETTINGS.translations.credentialsFailed + ': ' + response.data, 'error' );
+ }
+
+ $clickedButton.removeClass('disabled');
+
});
+ },
+
+ disableUI : function() {
+ var $resultsWrapper = $('.ccb-core-latest-results');
+ var $syncButton = $('.sync-wrapper .button');
+ CCBCoreAdmin.removeNotice();
+ $syncButton.addClass('disabled');
+ $resultsWrapper.empty().append(CCBCoreAdmin.spinner);
+ },
+
+ /**
+ * Helper method to insert an admin notice on the page
+ */
+ adminNotice : function(message, type = 'info', className = '') {
+ var $notice = $('');
+ var $content = $('');
+ $notice.addClass('notice-' + type);
+ $content.text(message);
+ $notice.append($content);
+ $('.ccb_core_settings-wrapper > h2 ').after( $notice );
+ },
+
+ /**
+ * Helper method to remove all or some notices
+ */
+ removeNotice : function(className = '') {
+ var noticeSelector;
+ if (className.length) {
+ noticeSelector = '.notice.' + className;
+ } else {
+ noticeSelector = '.notice';
+ }
+ $(noticeSelector).remove();
}
};
$(function() {
- ccbCoreAdmin.initialize();
+ CCBCoreAdmin.initialize();
});
From 4a495f0e14f007224a1de6cbd459a538f28d778f Mon Sep 17 00:00:00 2001
From: Jared Cobb
Date: Mon, 1 Jan 2018 11:38:07 -0700
Subject: [PATCH 04/16] Travis and codesniffer rulesets
---
.travis.yml | 157 ++++++++++++++++++++++++++++++++++++++++
codesniffer.ruleset.xml | 42 +++++++++++
2 files changed, 199 insertions(+)
create mode 100644 .travis.yml
create mode 100644 codesniffer.ruleset.xml
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..4d5d610
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,157 @@
+# Travis CI (MIT License) configuration file for CCB Core
+# @link https://travis-ci.org/
+
+# Tell Travis to use new container-based infrastructure
+sudo: false
+
+# Declare project language.
+# @link http://about.travis-ci.org/docs/user/languages/php/
+language: php
+
+# Specify when Travis should build.
+branches:
+ only:
+ - master
+
+# Git clone depth.
+git:
+ depth: 1
+
+matrix:
+ fast_finish: true
+
+ include:
+ - php: '5.6'
+ env: WP_VERSION=master PHP_LINT=1
+ - php: '5.6'
+ env: WP_VERSION=master
+ - php: '5.6'
+ env: WP_VERSION=4.9 WP_PHPCS=1 WP_TRAVIS_OBJECT_CACHE=1
+
+ - php: '7.0'
+ env: WP_VERSION=master PHP_LINT=1
+ - php: '7.0'
+ env: WP_VERSION=4.9
+
+ - php: '7.2'
+ env: WP_VERSION=master PHP_LINT=1
+ - php: '7.2'
+ env: WP_VERSION=4.9
+
+ - php: 'nightly'
+ env: WP_VERSION=master PHP_LINT=1
+
+ allow_failures:
+ - php: 'nightly'
+
+# Use this to prepare the system to install prerequisites or dependencies.
+# e.g. sudo apt-get update.
+# Failures in this section will result in build status 'errored'.
+before_install:
+
+# Use this to prepare your build for testing.
+# e.g. copy database configurations, environment variables, etc.
+# Failures in this section will result in build status 'errored'.
+before_script:
+ # Turn off Xdebug. See https://core.trac.wordpress.org/changeset/40138.
+ - phpenv config-rm xdebug.ini || echo "Xdebug not available"
+
+ - export PATH="$HOME/.composer/vendor/bin:$PATH"
+
+ # Couple the PHPUnit version to the PHP version.
+ - |
+ case "$TRAVIS_PHP_VERSION" in
+ 7.2|7.0|nightly)
+ echo "Using PHPUnit 6.1"
+ composer global require "phpunit/phpunit=6.1.*"
+ ;;
+ 5.6)
+ echo "Using PHPUnit 4.8"
+ composer global require "phpunit/phpunit=4.8.*"
+ ;;
+ *)
+ echo "No PHPUnit version handling for PHP version $TRAVIS_PHP_VERSION"
+ exit 1
+ ;;
+ esac
+
+ - og_dir="$(pwd)"
+ - plugin_slug="$(basename $(pwd))"
+
+ # Set up WordPress installation.
+ - export WP_DEVELOP_DIR=/tmp/wordpress
+ - export WP_TESTS_DIR=${WP_DEVELOP_DIR}/tests/phpunit
+ - export WP_CORE_DIR=${WP_DEVELOP_DIR}/src/
+ - mkdir -p $WP_DEVELOP_DIR
+
+ # Use the Git mirror of WordPress.
+ - git clone --depth=1 --branch="$WP_VERSION" git://develop.git.wordpress.org/ $WP_DEVELOP_DIR
+
+ # Set up WordPress configuration.
+ - cd $WP_DEVELOP_DIR
+ - echo $WP_DEVELOP_DIR
+ - cp wp-tests-config-sample.php wp-tests-config.php
+ - sed -i "s/youremptytestdbnamehere/wordpress_test/" wp-tests-config.php
+ - sed -i "s/yourusernamehere/root/" wp-tests-config.php
+ - sed -i "s/yourpasswordhere//" wp-tests-config.php
+ - echo "define( 'JETPACK_DEV_DEBUG', true );" >> wp-tests-config.php
+ - cat wp-tests-config.php
+
+ # Create WordPress database.
+ - mysql -e 'CREATE DATABASE wordpress_test;' -uroot
+
+ # Maybe install memcached
+ - |
+ if [[ "$WP_TRAVIS_OBJECT_CACHE" == "1" ]]; then
+ curl https://raw.githubusercontent.com/tollmanz/wordpress-pecl-memcached-object-cache/584392b56dc4adbe52bd2c7b86f875e23a3e5f75/object-cache.php > $WP_CORE_DIR/wp-content/object-cache.php
+ echo "extension = memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
+ fi
+
+ # Set up phpcs
+ - |
+ if [[ "$WP_PHPCS" == "1" ]]; then
+ cd $og_dir
+ composer install
+ export PATH=$PATH:${og_dir}/vendor/bin/
+ # After CodeSniffer install you should refresh your path.
+ phpenv rehash
+ fi
+
+ # Set up the plugin. This assumes that this repo name matches the theme name.
+ - cd $og_dir
+ - mkdir -p "${WP_CORE_DIR}wp-content/plugins/$plugin_slug"
+ - cp -R . "${WP_CORE_DIR}wp-content/plugins/$plugin_slug/"
+
+ # Hop into theme's directory.
+ - cd ${WP_CORE_DIR}wp-content/themes/$plugin_slug/
+
+ # For debugging.
+ - which phpunit
+ - phpunit --version
+ - pwd
+
+# Run test script commands.
+# Default is specific to project language.
+# All commands must exit with code 0 on success. Anything else is considered failure.
+script:
+ # Search for PHP syntax errors.
+ #
+ # Only need to run this once per PHP version.
+ - if [[ "$PHP_LINT" == "1" ]]; then find . -type "f" -iname "*.php" | xargs -L "1" php -l; fi
+
+ # WordPress Coding Standards.
+ #
+ # These are the same across PHP and WordPress, so we need to run them only once.
+ #
+ # @link https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards
+ # @link http://pear.php.net/package/PHP_CodeSniffer/
+ - if [[ "$WP_PHPCS" == "1" ]]; then phpcs -v; fi
+
+ # Test the theme's unit tests
+ - phpunit
+ - phpunit -c multisite.xml
+
+# Receive notifications for build results.
+# @link http://docs.travis-ci.com/user/notifications/#Email-notifications
+notifications:
+ email: false
diff --git a/codesniffer.ruleset.xml b/codesniffer.ruleset.xml
new file mode 100644
index 0000000..a4c04d9
--- /dev/null
+++ b/codesniffer.ruleset.xml
@@ -0,0 +1,42 @@
+
+
+
+ Code standard rules to check against a WordPress Plugin.
+
+ tests/*
+ lib/*
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ warning
+
+
+
+ warning
+
+
+
+
+ warning
+
+
+ warning
+
+
+ warning
+
+
+ warning
+
+
From 54f83a97bb2635d34007d6b46b870eef69b5c006 Mon Sep 17 00:00:00 2001
From: Jared Cobb
Date: Mon, 1 Jan 2018 13:48:51 -0700
Subject: [PATCH 05/16] Travis
---
.travis.yml | 195 ++++++----------------
bin/install-wp-tests.sh | 152 +++++++++++++++++
includes/class-ccb-core-cron.php | 2 +-
includes/index.php | 1 -
index.php | 2 +-
codesniffer.ruleset.xml => phpcs.xml.dist | 18 +-
phpunit.xml.dist | 14 ++
tests/bootstrap.php | 31 ++++
tests/test-sample.php | 20 +++
9 files changed, 283 insertions(+), 152 deletions(-)
create mode 100755 bin/install-wp-tests.sh
delete mode 100644 includes/index.php
rename codesniffer.ruleset.xml => phpcs.xml.dist (75%)
create mode 100644 phpunit.xml.dist
create mode 100644 tests/bootstrap.php
create mode 100644 tests/test-sample.php
diff --git a/.travis.yml b/.travis.yml
index 4d5d610..07d63a1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,157 +1,66 @@
-# Travis CI (MIT License) configuration file for CCB Core
-# @link https://travis-ci.org/
-
-# Tell Travis to use new container-based infrastructure
sudo: false
+dist: trusty
-# Declare project language.
-# @link http://about.travis-ci.org/docs/user/languages/php/
language: php
-# Specify when Travis should build.
+notifications:
+ email:
+ on_success: never
+ on_failure: change
+
branches:
only:
- master
-# Git clone depth.
-git:
- depth: 1
+cache:
+ directories:
+ - vendor
+ - $HOME/.composer/cache
matrix:
- fast_finish: true
-
- include:
- - php: '5.6'
- env: WP_VERSION=master PHP_LINT=1
- - php: '5.6'
- env: WP_VERSION=master
- - php: '5.6'
- env: WP_VERSION=4.9 WP_PHPCS=1 WP_TRAVIS_OBJECT_CACHE=1
-
- - php: '7.0'
- env: WP_VERSION=master PHP_LINT=1
- - php: '7.0'
- env: WP_VERSION=4.9
-
- - php: '7.2'
- env: WP_VERSION=master PHP_LINT=1
- - php: '7.2'
- env: WP_VERSION=4.9
-
- - php: 'nightly'
- env: WP_VERSION=master PHP_LINT=1
+ include:
+ - php: 7.1
+ env: WP_VERSION=latest
+ - php: 7.0
+ env: WP_VERSION=latest
+ - php: 5.6
+ env: WP_VERSION=4.4
+ - php: 5.6
+ env: WP_VERSION=latest
+ - php: 5.6
+ env: WP_VERSION=trunk
+ - php: 5.6
+ env: WP_TRAVISCI=phpcs
+ - php: 5.3
+ env: WP_VERSION=latest
+ dist: precise
- allow_failures:
- - php: 'nightly'
-
-# Use this to prepare the system to install prerequisites or dependencies.
-# e.g. sudo apt-get update.
-# Failures in this section will result in build status 'errored'.
-before_install:
-
-# Use this to prepare your build for testing.
-# e.g. copy database configurations, environment variables, etc.
-# Failures in this section will result in build status 'errored'.
before_script:
- # Turn off Xdebug. See https://core.trac.wordpress.org/changeset/40138.
- - phpenv config-rm xdebug.ini || echo "Xdebug not available"
-
- - export PATH="$HOME/.composer/vendor/bin:$PATH"
-
- # Couple the PHPUnit version to the PHP version.
- - |
- case "$TRAVIS_PHP_VERSION" in
- 7.2|7.0|nightly)
- echo "Using PHPUnit 6.1"
- composer global require "phpunit/phpunit=6.1.*"
- ;;
- 5.6)
- echo "Using PHPUnit 4.8"
- composer global require "phpunit/phpunit=4.8.*"
- ;;
- *)
- echo "No PHPUnit version handling for PHP version $TRAVIS_PHP_VERSION"
- exit 1
- ;;
- esac
-
- - og_dir="$(pwd)"
- - plugin_slug="$(basename $(pwd))"
-
- # Set up WordPress installation.
- - export WP_DEVELOP_DIR=/tmp/wordpress
- - export WP_TESTS_DIR=${WP_DEVELOP_DIR}/tests/phpunit
- - export WP_CORE_DIR=${WP_DEVELOP_DIR}/src/
- - mkdir -p $WP_DEVELOP_DIR
+ - export PATH="$HOME/.composer/vendor/bin:$PATH"
+ - |
+ if [ -f ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/xdebug.ini ]; then
+ phpenv config-rm xdebug.ini
+ else
+ echo "xdebug.ini does not exist"
+ fi
+ - |
+ if [[ ! -z "$WP_VERSION" ]] ; then
+ bash bin/install-wp-tests.sh wordpress_test root '' localhost $WP_VERSION
+ composer global require "phpunit/phpunit=4.8.*|5.7.*"
+ fi
+ - |
+ if [[ "$WP_TRAVISCI" == "phpcs" ]] ; then
+ composer global require wp-coding-standards/wpcs
+ phpcs --config-set installed_paths $HOME/.composer/vendor/wp-coding-standards/wpcs
+ fi
- # Use the Git mirror of WordPress.
- - git clone --depth=1 --branch="$WP_VERSION" git://develop.git.wordpress.org/ $WP_DEVELOP_DIR
-
- # Set up WordPress configuration.
- - cd $WP_DEVELOP_DIR
- - echo $WP_DEVELOP_DIR
- - cp wp-tests-config-sample.php wp-tests-config.php
- - sed -i "s/youremptytestdbnamehere/wordpress_test/" wp-tests-config.php
- - sed -i "s/yourusernamehere/root/" wp-tests-config.php
- - sed -i "s/yourpasswordhere//" wp-tests-config.php
- - echo "define( 'JETPACK_DEV_DEBUG', true );" >> wp-tests-config.php
- - cat wp-tests-config.php
-
- # Create WordPress database.
- - mysql -e 'CREATE DATABASE wordpress_test;' -uroot
-
- # Maybe install memcached
- - |
- if [[ "$WP_TRAVIS_OBJECT_CACHE" == "1" ]]; then
- curl https://raw.githubusercontent.com/tollmanz/wordpress-pecl-memcached-object-cache/584392b56dc4adbe52bd2c7b86f875e23a3e5f75/object-cache.php > $WP_CORE_DIR/wp-content/object-cache.php
- echo "extension = memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
- fi
-
- # Set up phpcs
- - |
- if [[ "$WP_PHPCS" == "1" ]]; then
- cd $og_dir
- composer install
- export PATH=$PATH:${og_dir}/vendor/bin/
- # After CodeSniffer install you should refresh your path.
- phpenv rehash
- fi
-
- # Set up the plugin. This assumes that this repo name matches the theme name.
- - cd $og_dir
- - mkdir -p "${WP_CORE_DIR}wp-content/plugins/$plugin_slug"
- - cp -R . "${WP_CORE_DIR}wp-content/plugins/$plugin_slug/"
-
- # Hop into theme's directory.
- - cd ${WP_CORE_DIR}wp-content/themes/$plugin_slug/
-
- # For debugging.
- - which phpunit
- - phpunit --version
- - pwd
-
-# Run test script commands.
-# Default is specific to project language.
-# All commands must exit with code 0 on success. Anything else is considered failure.
script:
- # Search for PHP syntax errors.
- #
- # Only need to run this once per PHP version.
- - if [[ "$PHP_LINT" == "1" ]]; then find . -type "f" -iname "*.php" | xargs -L "1" php -l; fi
-
- # WordPress Coding Standards.
- #
- # These are the same across PHP and WordPress, so we need to run them only once.
- #
- # @link https://github.com/WordPress-Coding-Standards/WordPress-Coding-Standards
- # @link http://pear.php.net/package/PHP_CodeSniffer/
- - if [[ "$WP_PHPCS" == "1" ]]; then phpcs -v; fi
-
- # Test the theme's unit tests
- - phpunit
- - phpunit -c multisite.xml
-
-# Receive notifications for build results.
-# @link http://docs.travis-ci.com/user/notifications/#Email-notifications
-notifications:
- email: false
+ - |
+ if [[ ! -z "$WP_VERSION" ]] ; then
+ phpunit
+ WP_MULTISITE=1 phpunit
+ fi
+ - |
+ if [[ "$WP_TRAVISCI" == "phpcs" ]] ; then
+ phpcs
+ fi
diff --git a/bin/install-wp-tests.sh b/bin/install-wp-tests.sh
new file mode 100755
index 0000000..878881f
--- /dev/null
+++ b/bin/install-wp-tests.sh
@@ -0,0 +1,152 @@
+#!/usr/bin/env bash
+
+if [ $# -lt 3 ]; then
+ echo "usage: $0 [db-host] [wp-version] [skip-database-creation]"
+ exit 1
+fi
+
+DB_NAME=$1
+DB_USER=$2
+DB_PASS=$3
+DB_HOST=${4-localhost}
+WP_VERSION=${5-latest}
+SKIP_DB_CREATE=${6-false}
+
+TMPDIR=${TMPDIR-/tmp}
+TMPDIR=$(echo $TMPDIR | sed -e "s/\/$//")
+WP_TESTS_DIR=${WP_TESTS_DIR-$TMPDIR/wordpress-tests-lib}
+WP_CORE_DIR=${WP_CORE_DIR-$TMPDIR/wordpress/}
+
+download() {
+ if [ `which curl` ]; then
+ curl -s "$1" > "$2";
+ elif [ `which wget` ]; then
+ wget -nv -O "$2" "$1"
+ fi
+}
+
+if [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+$ ]]; then
+ WP_TESTS_TAG="branches/$WP_VERSION"
+elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then
+ if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then
+ # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x
+ WP_TESTS_TAG="tags/${WP_VERSION%??}"
+ else
+ WP_TESTS_TAG="tags/$WP_VERSION"
+ fi
+elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
+ WP_TESTS_TAG="trunk"
+else
+ # http serves a single offer, whereas https serves multiple. we only want one
+ download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json
+ grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json
+ LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//')
+ if [[ -z "$LATEST_VERSION" ]]; then
+ echo "Latest WordPress version could not be found"
+ exit 1
+ fi
+ WP_TESTS_TAG="tags/$LATEST_VERSION"
+fi
+
+set -ex
+
+install_wp() {
+
+ if [ -d $WP_CORE_DIR ]; then
+ return;
+ fi
+
+ mkdir -p $WP_CORE_DIR
+
+ if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
+ mkdir -p $TMPDIR/wordpress-nightly
+ download https://wordpress.org/nightly-builds/wordpress-latest.zip $TMPDIR/wordpress-nightly/wordpress-nightly.zip
+ unzip -q $TMPDIR/wordpress-nightly/wordpress-nightly.zip -d $TMPDIR/wordpress-nightly/
+ mv $TMPDIR/wordpress-nightly/wordpress/* $WP_CORE_DIR
+ else
+ if [ $WP_VERSION == 'latest' ]; then
+ local ARCHIVE_NAME='latest'
+ elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+ ]]; then
+ # https serves multiple offers, whereas http serves single.
+ download https://api.wordpress.org/core/version-check/1.7/ $TMPDIR/wp-latest.json
+ if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then
+ # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x
+ LATEST_VERSION=${WP_VERSION%??}
+ else
+ # otherwise, scan the releases and get the most up to date minor version of the major release
+ local VERSION_ESCAPED=`echo $WP_VERSION | sed 's/\./\\\\./g'`
+ LATEST_VERSION=$(grep -o '"version":"'$VERSION_ESCAPED'[^"]*' $TMPDIR/wp-latest.json | sed 's/"version":"//' | head -1)
+ fi
+ if [[ -z "$LATEST_VERSION" ]]; then
+ local ARCHIVE_NAME="wordpress-$WP_VERSION"
+ else
+ local ARCHIVE_NAME="wordpress-$LATEST_VERSION"
+ fi
+ else
+ local ARCHIVE_NAME="wordpress-$WP_VERSION"
+ fi
+ download https://wordpress.org/${ARCHIVE_NAME}.tar.gz $TMPDIR/wordpress.tar.gz
+ tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WP_CORE_DIR
+ fi
+
+ download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php
+}
+
+install_test_suite() {
+ # portable in-place argument for both GNU sed and Mac OSX sed
+ if [[ $(uname -s) == 'Darwin' ]]; then
+ local ioption='-i .bak'
+ else
+ local ioption='-i'
+ fi
+
+ # set up testing suite if it doesn't yet exist
+ if [ ! -d $WP_TESTS_DIR ]; then
+ # set up testing suite
+ mkdir -p $WP_TESTS_DIR
+ svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes
+ svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data
+ fi
+
+ if [ ! -f wp-tests-config.php ]; then
+ download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php
+ # remove all forward slashes in the end
+ WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::")
+ sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php
+ sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php
+ sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php
+ sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php
+ sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php
+ fi
+
+}
+
+install_db() {
+
+ if [ ${SKIP_DB_CREATE} = "true" ]; then
+ return 0
+ fi
+
+ # parse DB_HOST for port or socket references
+ local PARTS=(${DB_HOST//\:/ })
+ local DB_HOSTNAME=${PARTS[0]};
+ local DB_SOCK_OR_PORT=${PARTS[1]};
+ local EXTRA=""
+
+ if ! [ -z $DB_HOSTNAME ] ; then
+ if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then
+ EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp"
+ elif ! [ -z $DB_SOCK_OR_PORT ] ; then
+ EXTRA=" --socket=$DB_SOCK_OR_PORT"
+ elif ! [ -z $DB_HOSTNAME ] ; then
+ EXTRA=" --host=$DB_HOSTNAME --protocol=tcp"
+ fi
+ fi
+
+ # create database
+ mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA
+}
+
+install_wp
+install_test_suite
+install_db
diff --git a/includes/class-ccb-core-cron.php b/includes/class-ccb-core-cron.php
index 24222e6..a8a2c39 100644
--- a/includes/class-ccb-core-cron.php
+++ b/includes/class-ccb-core-cron.php
@@ -54,7 +54,7 @@ public function custom_cron_schedule( $schedules ) {
if ( ! empty( $settings['auto_sync_timeout'] ) ) {
$schedules['ccb_core_schedule'] = [
'interval' => MINUTE_IN_SECONDS * absint( $settings['auto_sync_timeout'] ),
- 'display' => esc_html__( sprintf(
+ 'display' => esc_html( sprintf(
__( 'Every %s Minutes' ),
absint( $settings['auto_sync_timeout'] )
) ),
diff --git a/includes/index.php b/includes/index.php
deleted file mode 100644
index e71af0e..0000000
--- a/includes/index.php
+++ /dev/null
@@ -1 +0,0 @@
-
-
-
- Code standard rules to check against a WordPress Plugin.
-
- tests/*
- lib/*
+
+ Generally-applicable sniffs for WordPress plugins
@@ -39,4 +35,14 @@
warning
+
+
+
+ .
+
+
+
+
+ */tests/*
+ */lib/*
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
new file mode 100644
index 0000000..44f0fdb
--- /dev/null
+++ b/phpunit.xml.dist
@@ -0,0 +1,14 @@
+
+
+
+ ./tests/
+
+
+
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
new file mode 100644
index 0000000..483263d
--- /dev/null
+++ b/tests/bootstrap.php
@@ -0,0 +1,31 @@
+assertTrue( true );
+ }
+}
From b12925e1e9f896fe2461b9904033f9018eaf5a50 Mon Sep 17 00:00:00 2001
From: Jared Cobb
Date: Mon, 1 Jan 2018 14:57:20 -0700
Subject: [PATCH 06/16] Travis
---
tests/bootstrap.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
index 483263d..b337b11 100644
--- a/tests/bootstrap.php
+++ b/tests/bootstrap.php
@@ -23,7 +23,7 @@
* Manually load the plugin being tested.
*/
function _manually_load_plugin() {
- require dirname( dirname( __FILE__ ) ) . '/sample-plugin.php';
+ require dirname( dirname( __FILE__ ) ) . '/ccb-core.php';
}
tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' );
From 144d69e267456b78811c900e14b37410110ed94e Mon Sep 17 00:00:00 2001
From: Jared Cobb
Date: Wed, 3 Jan 2018 12:00:26 -0700
Subject: [PATCH 07/16] Initial unit tests, consistent slug names for terms and
post types.
---
.travis.yml | 3 -
ccb-core.php | 4 +-
includes/class-ccb-core-admin-ajax.php | 8 +-
includes/class-ccb-core-api.php | 32 +-
includes/class-ccb-core-helpers.php | 8 +-
includes/class-ccb-core-settings-field.php | 6 +-
includes/class-ccb-core-settings.php | 84 ++--
includes/class-ccb-core-synchronizer.php | 61 +--
includes/class-ccb-core.php | 60 +--
.../post-types/class-ccb-core-calendar.php | 138 +++---
includes/post-types/class-ccb-core-cpt.php | 6 +-
includes/post-types/class-ccb-core-group.php | 106 ++---
.../class-ccb-core-calendar-event-type.php | 12 +-
.../class-ccb-core-calendar-group-name.php | 12 +-
.../class-ccb-core-calendar-grouping-name.php | 12 +-
.../taxonomies/class-ccb-core-group-area.php | 12 +-
.../taxonomies/class-ccb-core-group-day.php | 12 +-
.../class-ccb-core-group-department.php | 12 +-
.../taxonomies/class-ccb-core-group-tag.php | 16 +-
.../taxonomies/class-ccb-core-group-time.php | 12 +-
.../taxonomies/class-ccb-core-group-type.php | 12 +-
.../taxonomies/class-ccb-core-taxonomy.php | 4 +-
lib/class-ccb-core-vendor-encryption.php | 2 +-
tests/bootstrap.php | 4 +-
tests/test-sample.php | 20 -
tests/test-synchronizer-calendar.php | 158 +++++++
tests/test-synchronizer-groups.php | 158 +++++++
tests/test-utils.php | 104 +++++
tests/xml/group_profiles_sample.xml | 405 ++++++++++++++++++
...group_profiles_some_inactivated_sample.xml | 405 ++++++++++++++++++
.../group_profiles_some_unlisted_sample.xml | 405 ++++++++++++++++++
.../group_profiles_some_updated_sample.xml | 405 ++++++++++++++++++
tests/xml/public_calendar_listing_sample.xml | 175 ++++++++
...c_calendar_listing_some_deleted_sample.xml | 127 ++++++
...ublic_calendar_listing_some_new_sample.xml | 223 ++++++++++
...c_calendar_listing_some_updated_sample.xml | 175 ++++++++
36 files changed, 3060 insertions(+), 338 deletions(-)
delete mode 100644 tests/test-sample.php
create mode 100644 tests/test-synchronizer-calendar.php
create mode 100644 tests/test-synchronizer-groups.php
create mode 100644 tests/test-utils.php
create mode 100644 tests/xml/group_profiles_sample.xml
create mode 100644 tests/xml/group_profiles_some_inactivated_sample.xml
create mode 100644 tests/xml/group_profiles_some_unlisted_sample.xml
create mode 100644 tests/xml/group_profiles_some_updated_sample.xml
create mode 100644 tests/xml/public_calendar_listing_sample.xml
create mode 100644 tests/xml/public_calendar_listing_some_deleted_sample.xml
create mode 100644 tests/xml/public_calendar_listing_some_new_sample.xml
create mode 100644 tests/xml/public_calendar_listing_some_updated_sample.xml
diff --git a/.travis.yml b/.travis.yml
index 07d63a1..bce2225 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -31,9 +31,6 @@ matrix:
env: WP_VERSION=trunk
- php: 5.6
env: WP_TRAVISCI=phpcs
- - php: 5.3
- env: WP_VERSION=latest
- dist: precise
before_script:
- export PATH="$HOME/.composer/vendor/bin:$PATH"
diff --git a/ccb-core.php b/ccb-core.php
index 5ed250e..1238846 100644
--- a/ccb-core.php
+++ b/ccb-core.php
@@ -31,8 +31,8 @@
// Code that runs during plugin activation and deactivation.
require_once CCB_CORE_PATH . 'includes/class-ccb-core-activator.php';
-register_activation_hook( __FILE__, array( 'CCB_Core_Activator', 'activate' ) );
-register_deactivation_hook( __FILE__, array( 'CCB_Core_Activator', 'deactivate' ) );
+register_activation_hook( __FILE__, [ 'CCB_Core_Activator', 'activate' ] );
+register_deactivation_hook( __FILE__, [ 'CCB_Core_Activator', 'deactivate' ] );
// Internationalization, dashboard-specific hooks, and public-facing site hooks.
require_once CCB_CORE_PATH . 'includes/class-ccb-core.php';
diff --git a/includes/class-ccb-core-admin-ajax.php b/includes/class-ccb-core-admin-ajax.php
index 52ae986..8cd10d6 100644
--- a/includes/class-ccb-core-admin-ajax.php
+++ b/includes/class-ccb-core-admin-ajax.php
@@ -38,10 +38,10 @@ class CCB_Core_Admin_AJAX {
* @since 1.0.0
*/
public function __construct() {
- add_action( 'wp_ajax_sync', array( $this, 'ajax_sync' ) );
- add_action( 'wp_ajax_poll_sync', array( $this, 'ajax_poll_sync' ) );
- add_action( 'wp_ajax_get_latest_sync', array( $this, 'ajax_get_latest_sync' ) );
- add_action( 'wp_ajax_test_credentials', array( $this, 'ajax_test_credentials' ) );
+ add_action( 'wp_ajax_sync', [ $this, 'ajax_sync' ] );
+ add_action( 'wp_ajax_poll_sync', [ $this, 'ajax_poll_sync' ] );
+ add_action( 'wp_ajax_get_latest_sync', [ $this, 'ajax_get_latest_sync' ] );
+ add_action( 'wp_ajax_test_credentials', [ $this, 'ajax_test_credentials' ] );
$this->api = CCB_Core_API::instance();
$this->synchronizer = CCB_Core_Synchronizer::instance();
diff --git a/includes/class-ccb-core-api.php b/includes/class-ccb-core-api.php
index 722197b..4b52bb5 100644
--- a/includes/class-ccb-core-api.php
+++ b/includes/class-ccb-core-api.php
@@ -133,7 +133,7 @@ public function initialize_credentials() {
* @access public
* @return array The API response data.
*/
- public function get( $service, $data = array() ) {
+ public function get( $service, $data = [] ) {
// Get the API response for the service.
return $this->request( 'GET', $service, $data );
}
@@ -148,7 +148,7 @@ public function get( $service, $data = array() ) {
* @access public
* @return array The API response data.
*/
- public function post( $service, $data = array() ) {
+ public function post( $service, $data = [] ) {
// Get the API response for the service.
return $this->request( 'POST', $service, $data );
}
@@ -164,29 +164,29 @@ public function post( $service, $data = array() ) {
* @access private
* @return array
*/
- private function request( $method, $service, $data = array() ) {
+ private function request( $method, $service, $data = [] ) {
if ( ! $this->initialized ) {
- return array(
+ return [
'code' => 401,
'error' => esc_html__( 'You are missing a subdomain, username, or password in the settings.', 'ccb-core' ),
'status' => 'ERROR',
- );
+ ];
}
$url = esc_url_raw( sprintf( 'https://%s.ccbchurch.com/api.php?srv=%s', $this->subdomain, $service ) );
if ( empty( $url ) ) {
- return array(
+ return [
'code' => 404,
'error' => esc_html__( 'Invalid API URL.', 'ccb-core' ),
'status' => 'ERROR',
- );
+ ];
}
// Add authentication header to the request object.
- $request = array(
+ $request = [
'timeout' => 60,
- 'headers' => array(
+ 'headers' => [
'Authorization' => 'Basic ' . base64_encode(
sprintf(
'%s:%s',
@@ -194,8 +194,8 @@ private function request( $method, $service, $data = array() ) {
sanitize_text_field( $this->password )
)
),
- ),
- );
+ ],
+ ];
if ( 'POST' === $method ) {
$request['body'] = $data;
@@ -211,11 +211,11 @@ private function request( $method, $service, $data = array() ) {
$response = wp_safe_remote_post( $url, $request );
break;
default:
- return array(
+ return [
'code' => 403,
'error' => esc_html__( 'Invalid request method.', 'ccb-core' ),
'status' => 'ERROR',
- );
+ ];
}
// Check for WordPress HTTP errors.
@@ -228,11 +228,11 @@ private function request( $method, $service, $data = array() ) {
}
// Build result object.
- $result = array(
+ $result = [
'xml' => wp_remote_retrieve_body( $response ),
'code' => absint( wp_remote_retrieve_response_code( $response ) ),
'headers' => wp_remote_retrieve_headers( $response ),
- );
+ ];
// Verify there are no HTTP errors.
if ( empty( $response )
@@ -277,5 +277,3 @@ private function request( $method, $service, $data = array() ) {
}
}
-
-CCB_Core_API::instance();
diff --git a/includes/class-ccb-core-helpers.php b/includes/class-ccb-core-helpers.php
index c46e372..59a3d10 100644
--- a/includes/class-ccb-core-helpers.php
+++ b/includes/class-ccb-core-helpers.php
@@ -36,7 +36,7 @@ class CCB_Core_Helpers {
*
* @var array
*/
- private $plugin_options = array();
+ private $plugin_options = [];
/**
* Unused constructor in the singleton pattern
@@ -144,7 +144,7 @@ public function decrypt( $data ) {
* @param array $data Optional data to send back.
* @return bool
*/
- public function send_non_blocking_json_success( $data = array() ) {
+ public function send_non_blocking_json_success( $data = [] ) {
ignore_user_abort( true );
ob_start();
@@ -228,10 +228,10 @@ public function download_image( $image_url, $filename = '', $post_id = 0 ) {
$filename = ! empty( $filename ) ? sanitize_file_name( $filename ) : 'ccb_' . crc32( $image_url );
- $file_array = array(
+ $file_array = [
'name' => $filename . $extension,
'tmp_name' => $temp_file,
- );
+ ];
add_filter( 'upload_dir', [ $this, 'custom_uploads_directory' ] );
$media_id = media_handle_sideload( $file_array, $post_id );
diff --git a/includes/class-ccb-core-settings-field.php b/includes/class-ccb-core-settings-field.php
index a3587de..9a5f3db 100644
--- a/includes/class-ccb-core-settings-field.php
+++ b/includes/class-ccb-core-settings-field.php
@@ -68,8 +68,8 @@ public function __construct( $field_id, $field ) {
* @return void
*/
public function render_field() {
- if ( isset( $this->field['field_render_function'] ) && is_callable( array( $this, $this->field['field_render_function'] ) ) ) {
- call_user_func( array( $this, $this->field['field_render_function'] ) );
+ if ( isset( $this->field['field_render_function'] ) && is_callable( [ $this, $this->field['field_render_function'] ] ) ) {
+ call_user_func( [ $this, $this->field['field_render_function'] ] );
if ( isset( $this->field['field_tooltip'] ) ) {
echo ' array(
+ ],
+ 'credentials' => [
'field_title' => esc_html__( 'API Credentials', 'ccb-core' ),
'field_render_function' => 'render_credentials',
'field_validation' => 'encrypt',
'field_tooltip' => esc_html__( 'This is the username and password for the API user in your Church Community Builder software.', 'ccb-core' ),
- ),
- 'test_credentials' => array(
+ ],
+ 'test_credentials' => [
'field_title' => esc_html__( 'Test Credentials', 'ccb-core' ),
'field_render_function' => 'render_test_credentials',
- ),
- ),
- ),
- ),
- ),
+ ],
+ ],
+ ],
+ ],
+ ],
];
/**
@@ -190,28 +190,28 @@ public function get_settings_definitions() {
$settings = apply_filters( 'ccb_core_settings_post_definitions', $settings );
// Add a syncronization settings page.
- $settings['ccb_core_settings_sync'] = array(
+ $settings['ccb_core_settings_sync'] = [
'page_title' => esc_html__( 'Synchronize', 'ccb-core' ),
- 'sections' => array(
- 'synchronize' => array(
+ 'sections' => [
+ 'synchronize' => [
'section_title' => esc_html__( 'Synchronize', 'ccb-core' ),
- 'fields' => array(
- 'auto_sync' => array(
+ 'fields' => [
+ 'auto_sync' => [
'field_title' => esc_html__( 'Enable Auto Sync', 'ccb-core' ),
'field_render_function' => 'render_switch',
'field_validation' => 'switch',
- ),
- 'auto_sync_timeout' => array(
+ ],
+ 'auto_sync_timeout' => [
'field_title' => esc_html__( 'Cache Expiration', 'ccb-core' ),
'field_render_function' => 'render_slider',
- 'field_options' => array(
+ 'field_options' => [
'min' => '10',
'max' => '180',
'units' => 'minutes',
- ),
+ ],
'field_default' => 90,
'field_validation' => '',
- 'field_attributes' => array( 'data-requires' => '{"auto_sync":1}' ),
+ 'field_attributes' => [ 'data-requires' => '{"auto_sync":1}' ],
'field_tooltip' => sprintf(
esc_html__(
'We keep a local copy (cache) of your Church Community Builder data for the best performance.%1$s
@@ -221,19 +221,19 @@ public function get_settings_definitions() {
'
',
'
'
),
- ),
- 'manual_sync' => array(
+ ],
+ 'manual_sync' => [
'field_title' => esc_html__( 'Manual Sync', 'ccb-core' ),
'field_render_function' => 'render_manual_sync',
- ),
- 'latest_results' => array(
+ ],
+ 'latest_results' => [
'field_title' => esc_html__( 'Latest Sync Results', 'ccb-core' ),
'field_render_function' => 'render_latest_results',
- ),
- ),
- ),
- ),
- );
+ ],
+ ],
+ ],
+ ],
+ ];
/**
* Allow filtering of the entire settings array.
@@ -257,7 +257,7 @@ private function generate_validation_hash() {
// Verify the nonce before processing field data.
check_admin_referer( 'update_settings', 'ccb_core_nonce' );
- $mapping = array();
+ $mapping = [];
$page_id = isset( $_POST['option_page'] ) ? sanitize_text_field( wp_unslash( $_POST['option_page'] ) ) : false; // Input var okay.
$settings_definitions = $this->get_settings_definitions();
@@ -265,10 +265,10 @@ private function generate_validation_hash() {
if ( ! empty( $section['fields'] ) ) {
foreach ( $section['fields'] as $field_id => $field ) {
if ( isset( $field['field_validation'] ) ) {
- $mapping[ $field_id ] = array(
+ $mapping[ $field_id ] = [
'field_title' => $field['field_title'],
'field_validation' => $field['field_validation'],
- );
+ ];
} else {
$mapping[ $field_id ] = false;
}
diff --git a/includes/class-ccb-core-synchronizer.php b/includes/class-ccb-core-synchronizer.php
index 4b16d92..8fdf5fd 100644
--- a/includes/class-ccb-core-synchronizer.php
+++ b/includes/class-ccb-core-synchronizer.php
@@ -19,6 +19,13 @@
*/
class CCB_Core_Synchronizer {
+ /**
+ * A complete mapping of CCB API data to post types and taxonomies
+ *
+ * @var array
+ */
+ public $map;
+
/**
* Instance of the class
*
@@ -28,13 +35,6 @@ class CCB_Core_Synchronizer {
*/
private static $instance;
- /**
- * A complete mapping of CCB API data to post types and taxonomies
- *
- * @var array
- */
- private $map;
-
/**
* An instance of the CCB_Core_API class
*
@@ -227,7 +227,7 @@ public function synchronize() {
* @param string $post_type The post type being updated.
* @return array
*/
- private function update_content( $response, $settings, $post_type ) {
+ public function update_content( $response, $settings, $post_type ) {
$result = [
'success' => true,
@@ -314,7 +314,7 @@ private function update_content( $response, $settings, $post_type ) {
*
* @return SimpleXML A collection of entities.
*/
- private function get_entities( $response, $nodes ) {
+ public function get_entities( $response, $nodes ) {
if ( ! empty( $nodes ) ) {
$depth = count( $nodes ) - 1;
$collection = $response['body']->response;
@@ -340,7 +340,7 @@ private function get_entities( $response, $nodes ) {
* @param string $post_type The post type mapped to the CCB entitiy.
* @return array
*/
- private function get_existing_post_data( $post_type ) {
+ public function get_existing_post_data( $post_type ) {
// Batch the WP_Query for performance.
$posts_per_page = 100;
$offset = 0;
@@ -399,7 +399,7 @@ private function get_existing_post_data( $post_type ) {
* @param string $post_type The post type to map to the entities.
* @return array
*/
- private function organize_entities( $entities, $post_data, $post_type ) {
+ public function organize_entities( $entities, $post_data, $post_type ) {
$collection = [
'insert_update' => [],
@@ -528,7 +528,7 @@ private function organize_entities( $entities, $post_data, $post_type ) {
* @param SimpleXML $entity A single entity object.
* @return mixed An integer id or string hash.
*/
- private function get_entity_id( $entity ) {
+ public function get_entity_id( $entity ) {
$entity_id = '';
// As part of the insert / update process we sometimes append
// a post_id to the entitiy. Ensure we remove it before hashing
@@ -578,7 +578,7 @@ private function get_entity_id( $entity ) {
* @param string $post_type The current post type.
* @return array
*/
- private function insert_update_entities( $entities, $settings, $post_type ) {
+ public function insert_update_entities( $entities, $settings, $post_type ) {
// Allow this script to run longer.
set_time_limit( MINUTE_IN_SECONDS * 10 );
@@ -719,7 +719,7 @@ private function insert_update_entities( $entities, $settings, $post_type ) {
* @param array $post_ids A collection of post ids to delete.
* @return array
*/
- private function delete_posts( $post_ids ) {
+ public function delete_posts( $post_ids ) {
$result = [
'success' => true,
'processed' => 0,
@@ -744,7 +744,7 @@ private function delete_posts( $post_ids ) {
* @param array $settings Definitions of taxonomies.
* @return void
*/
- private function delete_empty_terms( $settings ) {
+ public function delete_empty_terms( $settings ) {
// Ensure we get empty terms.
$args = [
'hide_empty' => false,
@@ -779,7 +779,7 @@ private function delete_empty_terms( $settings ) {
* @param array $settings The taxonomy settings.
* @return array
*/
- private function prepare_terms( $entity, $settings ) {
+ public function prepare_terms( $entity, $settings ) {
$categories = [];
$tags = [];
@@ -820,7 +820,7 @@ private function prepare_terms( $entity, $settings ) {
* @param SimpleXML $node A single node on the entitiy.
* @return mixed
*/
- private function auto_cast( $node ) {
+ public function auto_cast( $node ) {
// If the node has children, convert it to an array
// and recursively process the child nodes.
if ( $node->children()->count() ) {
@@ -861,8 +861,6 @@ private function auto_cast( $node ) {
* @return void
*/
private function enable_optimizations() {
- global $wpdb;
-
// Remove expensive unneeded actions.
remove_action( 'do_pings', 'do_all_pings', 10, 1 );
@@ -871,8 +869,13 @@ private function enable_optimizations() {
wp_defer_comment_counting( true );
wp_suspend_cache_addition( true );
- // Temporarily disable autocommit.
- $wpdb->query( 'SET autocommit = 0;' ); // db call ok; no cache ok.
+ // Unit tests rollback database transactions, do not
+ // alter commit settings for unit tests.
+ if ( ! defined( 'IS_UNIT_TEST' ) ) {
+ global $wpdb;
+ // Temporarily disable autocommit.
+ $wpdb->query( 'SET autocommit = 0;' ); // db call ok; no cache ok.
+ }
}
/**
@@ -881,12 +884,16 @@ private function enable_optimizations() {
* @return void
*/
private function disable_optimizations() {
- global $wpdb;
- // Commit the database operations now.
- $wpdb->query( 'COMMIT;' ); // db call ok; no cache ok.
- // Re-enable autocommit.
- $wpdb->query( 'SET autocommit = 1;' ); // db call ok; no cache ok.
+ // Unit tests rollback database transactions, do not
+ // alter commit settings for unit tests.
+ if ( ! defined( 'IS_UNIT_TEST' ) ) {
+ global $wpdb;
+ // Commit the database operations now.
+ $wpdb->query( 'COMMIT;' ); // db call ok; no cache ok.
+ // Re-enable autocommit.
+ $wpdb->query( 'SET autocommit = 1;' ); // db call ok; no cache ok.
+ }
// Re-enable counting.
wp_suspend_cache_addition( false );
@@ -898,5 +905,3 @@ private function disable_optimizations() {
}
}
-
-CCB_Core_Synchronizer::instance();
diff --git a/includes/class-ccb-core.php b/includes/class-ccb-core.php
index b53bb68..5aa3f79 100644
--- a/includes/class-ccb-core.php
+++ b/includes/class-ccb-core.php
@@ -100,20 +100,20 @@ private function load_dependencies() {
private function define_hooks() {
// Internationalization.
- add_action( 'plugins_loaded', array( $this, 'load_plugin_textdomain' ) );
+ add_action( 'plugins_loaded', [ $this, 'load_plugin_textdomain' ] );
// Plugin settings, menus, options.
- add_filter( 'plugin_action_links_' . CCB_CORE_BASENAME, array( $this, 'add_settings_link' ) );
+ add_filter( 'plugin_action_links_' . CCB_CORE_BASENAME, [ $this, 'add_settings_link' ] );
// Setup settings pages.
- add_action( 'admin_menu', array( $this, 'initialize_settings_menu' ) );
- add_action( 'admin_init', array( $this, 'initialize_settings' ) );
+ add_action( 'admin_menu', [ $this, 'initialize_settings_menu' ] );
+ add_action( 'admin_init', [ $this, 'initialize_settings' ] );
// Callback for after the options are saved.
- add_action( 'update_option_ccb_core_settings', array( $this, 'updated_options' ), 10, 2 );
+ add_action( 'update_option_ccb_core_settings', [ $this, 'updated_options' ], 10, 2 );
- add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_styles' ) );
- add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
+ add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_styles' ] );
+ add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
}
@@ -175,10 +175,10 @@ public function initialize_settings_menu() {
$page['page_title'],
'manage_options',
$page_id,
- array(
+ [
$settings_page,
'render_page',
- )
+ ]
);
}
}
@@ -196,7 +196,7 @@ public function initialize_settings() {
foreach ( $settings->get_settings_definitions() as $page_id => $page ) {
- register_setting( $page_id, 'ccb_core_settings', array( $settings, 'validate_settings' ) );
+ register_setting( $page_id, 'ccb_core_settings', [ $settings, 'validate_settings' ] );
foreach ( $page['sections'] as $section_id => $section ) {
@@ -204,10 +204,10 @@ public function initialize_settings() {
add_settings_section(
$section_id,
$section['section_title'],
- array(
+ [
$settings_section,
'render_section',
- ),
+ ],
$page_id
);
@@ -218,10 +218,10 @@ public function initialize_settings() {
add_settings_field(
$field_id,
$field['field_title'],
- array(
+ [
$settings_field,
'render_field',
- ),
+ ],
$page_id,
$section_id
);
@@ -249,12 +249,12 @@ public function updated_options( $old_value, $value ) {
// Create a collection of settings that, if they change, should
// trigger a flush_rewrite_rules event.
- $setting_array = array(
+ $setting_array = [
'groups_enabled',
'groups_slug',
'calendar_enabled',
'calendar_slug',
- );
+ ];
foreach ( $setting_array as $setting ) {
if ( isset( $value[ $setting ] ) ) {
@@ -277,12 +277,12 @@ public function updated_options( $old_value, $value ) {
public function enqueue_styles( $hook ) {
if ( false !== stristr( $hook, 'ccb_core_settings' ) ) {
- wp_enqueue_style( 'ccb-core', CCB_CORE_URL . 'css/ccb-core-admin.css', array(), CCB_CORE_VERSION, 'all' );
- wp_enqueue_style( 'switchery', CCB_CORE_URL . 'css/vendor/switchery.min.css', array(), CCB_CORE_VERSION, 'all' );
- wp_enqueue_style( 'powerange', CCB_CORE_URL . 'css/vendor/powerange.min.css', array(), CCB_CORE_VERSION, 'all' );
- wp_enqueue_style( 'picker', CCB_CORE_URL . 'css/vendor/default.css', array(), CCB_CORE_VERSION, 'all' );
- wp_enqueue_style( 'picker-date', CCB_CORE_URL . 'css/vendor/default.date.css', array(), CCB_CORE_VERSION, 'all' );
- wp_enqueue_style( 'tipr', CCB_CORE_URL . 'css/vendor/tipr.css', array(), CCB_CORE_VERSION, 'all' );
+ wp_enqueue_style( 'ccb-core', CCB_CORE_URL . 'css/ccb-core-admin.css', [], CCB_CORE_VERSION, 'all' );
+ wp_enqueue_style( 'switchery', CCB_CORE_URL . 'css/vendor/switchery.min.css', [], CCB_CORE_VERSION, 'all' );
+ wp_enqueue_style( 'powerange', CCB_CORE_URL . 'css/vendor/powerange.min.css', [], CCB_CORE_VERSION, 'all' );
+ wp_enqueue_style( 'picker', CCB_CORE_URL . 'css/vendor/default.css', [], CCB_CORE_VERSION, 'all' );
+ wp_enqueue_style( 'picker-date', CCB_CORE_URL . 'css/vendor/default.date.css', [], CCB_CORE_VERSION, 'all' );
+ wp_enqueue_style( 'tipr', CCB_CORE_URL . 'css/vendor/tipr.css', [], CCB_CORE_VERSION, 'all' );
}
}
@@ -296,23 +296,23 @@ public function enqueue_styles( $hook ) {
public function enqueue_scripts( $hook ) {
if ( false !== stristr( $hook, 'ccb_core_settings' ) ) {
- wp_enqueue_script( 'ccb-core', CCB_CORE_URL . 'js/ccb-core-admin.js', array( 'jquery' ), CCB_CORE_VERSION, false );
- wp_enqueue_script( 'switchery', CCB_CORE_URL . 'js/vendor/switchery.min.js', array( 'jquery' ), CCB_CORE_VERSION, false );
- wp_enqueue_script( 'powerange', CCB_CORE_URL . 'js/vendor/powerange.min.js', array( 'jquery' ), CCB_CORE_VERSION, false );
- wp_enqueue_script( 'picker', CCB_CORE_URL . 'js/vendor/picker.js', array( 'jquery' ), CCB_CORE_VERSION, false );
- wp_enqueue_script( 'picker-date', CCB_CORE_URL . 'js/vendor/picker.date.js', array( 'picker' ), CCB_CORE_VERSION, false );
- wp_enqueue_script( 'tipr', CCB_CORE_URL . 'js/vendor/tipr.min.js', array( 'jquery' ), CCB_CORE_VERSION, false );
+ wp_enqueue_script( 'ccb-core', CCB_CORE_URL . 'js/ccb-core-admin.js', [ 'jquery' ], CCB_CORE_VERSION, false );
+ wp_enqueue_script( 'switchery', CCB_CORE_URL . 'js/vendor/switchery.min.js', [ 'jquery' ], CCB_CORE_VERSION, false );
+ wp_enqueue_script( 'powerange', CCB_CORE_URL . 'js/vendor/powerange.min.js', [ 'jquery' ], CCB_CORE_VERSION, false );
+ wp_enqueue_script( 'picker', CCB_CORE_URL . 'js/vendor/picker.js', [ 'jquery' ], CCB_CORE_VERSION, false );
+ wp_enqueue_script( 'picker-date', CCB_CORE_URL . 'js/vendor/picker.date.js', [ 'picker' ], CCB_CORE_VERSION, false );
+ wp_enqueue_script( 'tipr', CCB_CORE_URL . 'js/vendor/tipr.min.js', [ 'jquery' ], CCB_CORE_VERSION, false );
wp_localize_script(
'ccb-core',
'CCB_CORE_SETTINGS',
- array(
+ [
'nonce' => wp_create_nonce( 'ccb_core_nonce' ),
'translations' => [
'credentialsSuccessful' => esc_html__( 'The credentials were successfully authenticated.', 'ccb-core' ),
'credentialsFailed' => esc_html__( 'The credentials failed authentication', 'ccb-core' ),
'syncInProgress' => esc_html__( 'Syncronization in progress... You can safely navigate away from this page while we work in the background.', 'ccb-core' ),
],
- )
+ ]
);
}
diff --git a/includes/post-types/class-ccb-core-calendar.php b/includes/post-types/class-ccb-core-calendar.php
index 7894870..3473dd0 100644
--- a/includes/post-types/class-ccb-core-calendar.php
+++ b/includes/post-types/class-ccb-core-calendar.php
@@ -52,8 +52,8 @@ public function get_post_args() {
$show_ui = ! empty( $options['calendar_show_ui'] ) && 'no' === $options['calendar_show_ui'] ? false : true;
$show_in_nav_menus = ! empty( $options['calendar_show_in_nav_menus'] ) && 'yes' === $options['calendar_show_in_nav_menus'] ? true : false;
- return array(
- 'labels' => array(
+ return [
+ 'labels' => [
'name' => $plural,
'singular_name' => $singular,
'all_items' => sprintf( __( 'All %s', 'ccb-core' ), $plural ),
@@ -67,7 +67,7 @@ public function get_post_args() {
'not_found' => __( 'Nothing found in the Database.', 'ccb-core' ),
'not_found_in_trash' => __( 'Nothing found in Trash', 'ccb-core' ),
'parent_item_colon' => '',
- ),
+ ],
'description' => sprintf( __( 'These are the %s that are synchronized with your Church Community Builder software.', 'ccb-core' ), $plural ),
'public' => true,
'publicly_queryable' => $publicly_queryable,
@@ -81,8 +81,8 @@ public function get_post_args() {
'has_archive' => $has_archive,
'capability_type' => 'post',
'hierarchical' => false,
- 'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'custom-fields', 'sticky' ),
- );
+ 'supports' => [ 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'custom-fields', 'sticky' ],
+ ];
}
@@ -95,60 +95,60 @@ public function get_post_args() {
*/
public function get_post_settings_definitions( $settings ) {
- $settings['ccb_core_settings_calendar'] = array(
+ $settings['ccb_core_settings_calendar'] = [
'page_title' => esc_html__( 'Public Events', 'ccb-core' ),
- 'sections' => array(
- 'calendar' => array(
+ 'sections' => [
+ 'calendar' => [
'section_title' => esc_html__( 'Public Events', 'ccb-core' ),
- 'fields' => array(
- 'calendar_enabled' => array(
+ 'fields' => [
+ 'calendar_enabled' => [
'field_title' => esc_html__( 'Enable Events', 'ccb-core' ),
'field_render_function' => 'render_switch',
'field_validation' => 'switch',
- ),
- 'calendar_name' => array(
+ ],
+ 'calendar_name' => [
'field_title' => esc_html__( 'Event Display Name (Plural)', 'ccb-core' ),
'field_render_function' => 'render_text',
'field_placeholder' => esc_html__( 'Events', 'ccb-core' ),
'field_validation' => 'alphanumeric_extended',
- 'field_attributes' => array( 'data-requires' => '{"calendar_enabled":1}' ),
+ 'field_attributes' => [ 'data-requires' => '{"calendar_enabled":1}' ],
'field_tooltip' => esc_html__( 'This is what you call the events in your church (i.e. Meetups, Hangouts, etc.).', 'ccb-core' ),
- ),
- 'calendar_name_singular' => array(
+ ],
+ 'calendar_name_singular' => [
'field_title' => esc_html__( 'Event Display Name (Singular)', 'ccb-core' ),
'field_render_function' => 'render_text',
'field_placeholder' => esc_html__( 'Event', 'ccb-core' ),
'field_validation' => 'alphanumeric_extended',
- 'field_attributes' => array( 'data-requires' => '{"calendar_enabled":1}' ),
+ 'field_attributes' => [ 'data-requires' => '{"calendar_enabled":1}' ],
'field_tooltip' => esc_html__( 'This is the singular name of what you call the events in your church (i.e. Meetup, Hangout, etc.).', 'ccb-core' ),
- ),
- 'calendar_slug' => array(
+ ],
+ 'calendar_slug' => [
'field_title' => esc_html__( 'Events URL Name', 'ccb-core' ),
'field_render_function' => 'render_text',
'field_placeholder' => 'events',
'field_validation' => 'slug',
- 'field_attributes' => array( 'data-requires' => '{"calendar_enabled":1}' ),
+ 'field_attributes' => [ 'data-requires' => '{"calendar_enabled":1}' ],
'field_tooltip' => esc_html__( 'This is typically where your theme will display all the events. WordPress calls this a "slug".', 'ccb-core' ),
- ),
- 'calendar_advanced' => array(
+ ],
+ 'calendar_advanced' => [
'field_title' => esc_html__( 'Enable Advanced Settings (Optional)', 'ccb-core' ),
'field_render_function' => 'render_switch',
'field_validation' => 'switch',
- 'field_attributes' => array( 'data-requires' => '{"calendar_enabled":1}' ),
- ),
- 'calendar_date_range_type' => array(
+ 'field_attributes' => [ 'data-requires' => '{"calendar_enabled":1}' ],
+ ],
+ 'calendar_date_range_type' => [
'field_title' => esc_html__( 'Date Range Type', 'ccb-core' ),
'field_render_function' => 'render_radio',
- 'field_options' => array(
+ 'field_options' => [
'relative' => esc_html__( 'Relative Range', 'ccb-core' ),
'specific' => esc_html__( 'Specific Range', 'ccb-core' ),
- ),
+ ],
'field_validation' => '',
'field_default' => 'relative',
- 'field_attributes' => array(
+ 'field_attributes' => [
'class' => 'date-range-type',
'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1}',
- ),
+ ],
'field_tooltip' => sprintf(
esc_html__(
'Relative: For example, always get the events from "One week ago", up to "Eight weeks from now".%1$s
@@ -160,38 +160,38 @@ public function get_post_settings_definitions( $settings ) {
'
',
'
'
),
- ),
- 'calendar_relative_weeks_past' => array(
+ ],
+ 'calendar_relative_weeks_past' => [
'field_title' => esc_html__( 'How Far Back?', 'ccb-core' ),
'field_render_function' => 'render_slider',
- 'field_options' => array(
+ 'field_options' => [
'min' => '0',
'max' => '26',
'units' => 'weeks',
- ),
+ ],
'field_default' => 1,
'field_validation' => '',
- 'field_attributes' => array( 'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1,"calendar_date_range_type":"relative"}' ),
+ 'field_attributes' => [ 'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1,"calendar_date_range_type":"relative"}' ],
'field_tooltip' => esc_html__( 'Every time we synchronize, how many weeks in the past should we look? (0 would be "today")', 'ccb-core' ),
- ),
- 'calendar_relative_weeks_future' => array(
+ ],
+ 'calendar_relative_weeks_future' => [
'field_title' => esc_html__( 'How Into The Future?', 'ccb-core' ),
'field_render_function' => 'render_slider',
- 'field_options' => array(
+ 'field_options' => [
'min' => '1',
'max' => '52',
'units' => 'weeks',
- ),
+ ],
'field_default' => 16,
'field_validation' => '',
- 'field_attributes' => array( 'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1,"calendar_date_range_type":"relative"}' ),
+ 'field_attributes' => [ 'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1,"calendar_date_range_type":"relative"}' ],
'field_tooltip' => esc_html__( 'Every time we synchronize, how many weeks in the future should we look?', 'ccb-core' ),
- ),
- 'calendar_specific_start' => array(
+ ],
+ 'calendar_specific_start' => [
'field_title' => esc_html__( 'Specific Start Date', 'ccb-core' ),
'field_render_function' => 'render_date_picker',
'field_validation' => '',
- 'field_attributes' => array( 'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1,"calendar_date_range_type":"specific"}' ),
+ 'field_attributes' => [ 'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1,"calendar_date_range_type":"specific"}' ],
'field_tooltip' => sprintf(
esc_html__(
'When synchronizing, we should get events that start after this date.%s
@@ -199,12 +199,12 @@ public function get_post_settings_definitions( $settings ) {
'ccb-core' ),
'
'
),
- ),
- 'calendar_specific_end' => array(
+ ],
+ 'calendar_specific_end' => [
'field_title' => esc_html__( 'Specific End Date', 'ccb-core' ),
'field_render_function' => 'render_date_picker',
'field_validation' => '',
- 'field_attributes' => array( 'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1,"calendar_date_range_type":"specific"}' ),
+ 'field_attributes' => [ 'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1,"calendar_date_range_type":"specific"}' ],
'field_tooltip' => sprintf(
esc_html__(
'When synchronizing, we should get events that start before this date.%s
@@ -212,55 +212,55 @@ public function get_post_settings_definitions( $settings ) {
'ccb-core' ),
'
'
),
- ),
- 'calendar_exclude_from_search' => array(
+ ],
+ 'calendar_exclude_from_search' => [
'field_title' => esc_html__( 'Exclude From Search?', 'ccb-core' ),
'field_render_function' => 'render_radio',
- 'field_options' => array(
+ 'field_options' => [
'yes' => esc_html__( 'Yes', 'ccb-core' ),
'no' => esc_html__( 'No', 'ccb-core' ),
- ),
+ ],
'field_validation' => '',
'field_default' => 'no',
- 'field_attributes' => array( 'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1}' ),
- ),
- 'calendar_publicly_queryable' => array(
+ 'field_attributes' => [ 'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1}' ],
+ ],
+ 'calendar_publicly_queryable' => [
'field_title' => esc_html__( 'Publicly Queryable?', 'ccb-core' ),
'field_render_function' => 'render_radio',
- 'field_options' => array(
+ 'field_options' => [
'yes' => esc_html__( 'Yes', 'ccb-core' ),
'no' => esc_html__( 'No', 'ccb-core' ),
- ),
+ ],
'field_validation' => '',
'field_default' => 'yes',
- 'field_attributes' => array( 'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1}' ),
- ),
- 'calendar_show_ui' => array(
+ 'field_attributes' => [ 'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1}' ],
+ ],
+ 'calendar_show_ui' => [
'field_title' => esc_html__( 'Show In Admin UI?', 'ccb-core' ),
'field_render_function' => 'render_radio',
- 'field_options' => array(
+ 'field_options' => [
'yes' => esc_html__( 'Yes', 'ccb-core' ),
'no' => esc_html__( 'No', 'ccb-core' ),
- ),
+ ],
'field_validation' => '',
'field_default' => 'yes',
- 'field_attributes' => array( 'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1}' ),
- ),
- 'calendar_show_in_nav_menus' => array(
+ 'field_attributes' => [ 'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1}' ],
+ ],
+ 'calendar_show_in_nav_menus' => [
'field_title' => esc_html__( 'Show In Navigation Menus?', 'ccb-core' ),
'field_render_function' => 'render_radio',
- 'field_options' => array(
+ 'field_options' => [
'yes' => esc_html__( 'Yes', 'ccb-core' ),
'no' => esc_html__( 'No', 'ccb-core' ),
- ),
+ ],
'field_validation' => '',
'field_default' => 'no',
- 'field_attributes' => array( 'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1}' ),
- ),
- ),
- ),
- ),
- );
+ 'field_attributes' => [ 'data-requires' => '{"calendar_enabled":1,"calendar_advanced":1}' ],
+ ],
+ ],
+ ],
+ ],
+ ];
return $settings;
}
diff --git a/includes/post-types/class-ccb-core-cpt.php b/includes/post-types/class-ccb-core-cpt.php
index 76363b0..3b7aa63 100644
--- a/includes/post-types/class-ccb-core-cpt.php
+++ b/includes/post-types/class-ccb-core-cpt.php
@@ -37,12 +37,12 @@ abstract class CCB_Core_CPT {
*/
public function __construct() {
- add_filter( 'ccb_core_settings_post_definitions', array( $this, 'get_post_settings_definitions' ) );
+ add_filter( 'ccb_core_settings_post_definitions', [ $this, 'get_post_settings_definitions' ] );
// If this custom post type is enabled, merge the defaults and set the registration hook.
if ( $this->enabled ) {
- add_action( 'init', array( $this, 'register_post_type' ) );
- add_filter( 'ccb_core_synchronizer_post_api_map', array( $this, 'get_post_api_map' ) );
+ add_action( 'init', [ $this, 'register_post_type' ] );
+ add_filter( 'ccb_core_synchronizer_post_api_map', [ $this, 'get_post_api_map' ] );
}
}
diff --git a/includes/post-types/class-ccb-core-group.php b/includes/post-types/class-ccb-core-group.php
index b681799..6166722 100644
--- a/includes/post-types/class-ccb-core-group.php
+++ b/includes/post-types/class-ccb-core-group.php
@@ -23,7 +23,7 @@ class CCB_Core_Group extends CCB_Core_CPT {
*
* @var string
*/
- public $name = 'ccb_core_groups';
+ public $name = 'ccb_core_group';
/**
* Initialize the class
@@ -56,8 +56,8 @@ public function get_post_args() {
$show_ui = ! empty( $options['groups_show_ui'] ) && 'no' === $options['groups_show_ui'] ? false : true;
$show_in_nav_menus = ! empty( $options['groups_show_in_nav_menus'] ) && 'yes' === $options['groups_show_in_nav_menus'] ? true : false;
- return array(
- 'labels' => array(
+ return [
+ 'labels' => [
'name' => $plural,
'singular_name' => $singular,
'all_items' => sprintf( __( 'All %s', 'ccb-core' ), $plural ),
@@ -71,7 +71,7 @@ public function get_post_args() {
'not_found' => __( 'Nothing found in the Database.', 'ccb-core' ),
'not_found_in_trash' => __( 'Nothing found in Trash', 'ccb-core' ),
'parent_item_colon' => '',
- ),
+ ],
'description' => sprintf( __( 'These are the %s that are synchronized with your Church Community Builder software.', 'ccb-core' ), $plural ),
'public' => true,
'publicly_queryable' => $publicly_queryable,
@@ -85,8 +85,8 @@ public function get_post_args() {
'has_archive' => $has_archive,
'capability_type' => 'post',
'hierarchical' => false,
- 'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'custom-fields', 'sticky' ),
- );
+ 'supports' => [ 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'custom-fields', 'sticky' ],
+ ];
}
@@ -99,51 +99,51 @@ public function get_post_args() {
*/
public function get_post_settings_definitions( $settings ) {
- $settings['ccb_core_settings_groups'] = array(
+ $settings['ccb_core_settings_groups'] = [
'page_title' => esc_html__( 'Groups', 'ccb-core' ),
- 'sections' => array(
- 'groups' => array(
+ 'sections' => [
+ 'groups' => [
'section_title' => esc_html__( 'Groups', 'ccb-core' ),
- 'fields' => array(
- 'groups_enabled' => array(
+ 'fields' => [
+ 'groups_enabled' => [
'field_title' => esc_html__( 'Enable Groups', 'ccb-core' ),
'field_render_function' => 'render_switch',
'field_validation' => 'switch',
- ),
- 'groups_name' => array(
+ ],
+ 'groups_name' => [
'field_title' => esc_html__( 'Groups Display Name (Plural)', 'ccb-core' ),
'field_render_function' => 'render_text',
'field_placeholder' => esc_html__( 'Groups', 'ccb-core' ),
'field_validation' => 'alphanumeric_extended',
- 'field_attributes' => array( 'data-requires' => '{"groups_enabled":1}' ),
+ 'field_attributes' => [ 'data-requires' => '{"groups_enabled":1}' ],
'field_tooltip' => esc_html__( 'This is what you call the groups in your church (i.e. Home Groups, Connections, Life Groups, etc.).', 'ccb-core' ),
- ),
- 'groups_name_singular' => array(
+ ],
+ 'groups_name_singular' => [
'field_title' => esc_html__( 'Groups Display Name (Singular)', 'ccb-core' ),
'field_render_function' => 'render_text',
'field_placeholder' => esc_html__( 'Group', 'ccb-core' ),
'field_validation' => 'alphanumeric_extended',
- 'field_attributes' => array( 'data-requires' => '{"groups_enabled":1}' ),
+ 'field_attributes' => [ 'data-requires' => '{"groups_enabled":1}' ],
'field_tooltip' => esc_html__( 'This is the singular name of what you call the groups in your church (i.e. Home Group, Connection, Life Group, etc.).', 'ccb-core' ),
- ),
- 'groups_slug' => array(
+ ],
+ 'groups_slug' => [
'field_title' => esc_html__( 'Groups URL Name', 'ccb-core' ),
'field_render_function' => 'render_text',
'field_placeholder' => 'groups',
'field_validation' => 'slug',
- 'field_attributes' => array( 'data-requires' => '{"groups_enabled":1}' ),
+ 'field_attributes' => [ 'data-requires' => '{"groups_enabled":1}' ],
'field_tooltip' => esc_html__( 'This is typically where your theme will display all the groups. WordPress calls this a "slug".', 'ccb-core' ),
- ),
- 'groups_import_images' => array(
+ ],
+ 'groups_import_images' => [
'field_title' => esc_html__( 'Also Import Group Images?', 'ccb-core' ),
'field_render_function' => 'render_radio',
- 'field_options' => array(
+ 'field_options' => [
'yes' => esc_html__( 'Yes', 'ccb-core' ),
'no' => esc_html__( 'No', 'ccb-core' ),
- ),
+ ],
'field_validation' => '',
'field_default' => 'no',
- 'field_attributes' => array( 'data-requires' => '{"groups_enabled":1}' ),
+ 'field_attributes' => [ 'data-requires' => '{"groups_enabled":1}' ],
'field_tooltip' => sprintf(
esc_html__(
'This will download the CCB Group Image and attach it as a Featured Image.%s
@@ -151,61 +151,61 @@ public function get_post_settings_definitions( $settings ) {
'ccb-core' ),
'
'
),
- ),
- 'groups_advanced' => array(
+ ],
+ 'groups_advanced' => [
'field_title' => esc_html__( 'Enable Advanced Settings (Optional)', 'ccb-core' ),
'field_render_function' => 'render_switch',
'field_validation' => 'switch',
- 'field_attributes' => array( 'data-requires' => '{"groups_enabled":1}' ),
- ),
- 'groups_exclude_from_search' => array(
+ 'field_attributes' => [ 'data-requires' => '{"groups_enabled":1}' ],
+ ],
+ 'groups_exclude_from_search' => [
'field_title' => esc_html__( 'Exclude From Search?', 'ccb-core' ),
'field_render_function' => 'render_radio',
- 'field_options' => array(
+ 'field_options' => [
'yes' => esc_html__( 'Yes', 'ccb-core' ),
'no' => esc_html__( 'No', 'ccb-core' ),
- ),
+ ],
'field_validation' => '',
'field_default' => 'no',
- 'field_attributes' => array( 'data-requires' => '{"groups_enabled":1,"groups_advanced":1}' ),
- ),
- 'groups_publicly_queryable' => array(
+ 'field_attributes' => [ 'data-requires' => '{"groups_enabled":1,"groups_advanced":1}' ],
+ ],
+ 'groups_publicly_queryable' => [
'field_title' => esc_html__( 'Publicly Queryable?', 'ccb-core' ),
'field_render_function' => 'render_radio',
- 'field_options' => array(
+ 'field_options' => [
'yes' => esc_html__( 'Yes', 'ccb-core' ),
'no' => esc_html__( 'No', 'ccb-core' ),
- ),
+ ],
'field_validation' => '',
'field_default' => 'yes',
- 'field_attributes' => array( 'data-requires' => '{"groups_enabled":1,"groups_advanced":1}' ),
- ),
- 'groups_show_ui' => array(
+ 'field_attributes' => [ 'data-requires' => '{"groups_enabled":1,"groups_advanced":1}' ],
+ ],
+ 'groups_show_ui' => [
'field_title' => esc_html__( 'Show In Admin UI?', 'ccb-core' ),
'field_render_function' => 'render_radio',
- 'field_options' => array(
+ 'field_options' => [
'yes' => esc_html__( 'Yes', 'ccb-core' ),
'no' => esc_html__( 'No', 'ccb-core' ),
- ),
+ ],
'field_validation' => '',
'field_default' => 'yes',
- 'field_attributes' => array( 'data-requires' => '{"groups_enabled":1,"groups_advanced":1}' ),
- ),
- 'groups_show_in_nav_menus' => array(
+ 'field_attributes' => [ 'data-requires' => '{"groups_enabled":1,"groups_advanced":1}' ],
+ ],
+ 'groups_show_in_nav_menus' => [
'field_title' => esc_html__( 'Show In Navigation Menus?', 'ccb-core' ),
'field_render_function' => 'render_radio',
- 'field_options' => array(
+ 'field_options' => [
'yes' => esc_html__( 'Yes', 'ccb-core' ),
'no' => esc_html__( 'No', 'ccb-core' ),
- ),
+ ],
'field_validation' => '',
'field_default' => 'no',
- 'field_attributes' => array( 'data-requires' => '{"groups_enabled":1,"groups_advanced":1}' ),
- ),
- ),
- ),
- ),
- );
+ 'field_attributes' => [ 'data-requires' => '{"groups_enabled":1,"groups_advanced":1}' ],
+ ],
+ ],
+ ],
+ ],
+ ];
return $settings;
diff --git a/includes/taxonomies/class-ccb-core-calendar-event-type.php b/includes/taxonomies/class-ccb-core-calendar-event-type.php
index 62a71a2..b006a86 100644
--- a/includes/taxonomies/class-ccb-core-calendar-event-type.php
+++ b/includes/taxonomies/class-ccb-core-calendar-event-type.php
@@ -23,14 +23,14 @@ class CCB_Core_Calendar_Event_Type extends CCB_Core_Taxonomy {
*
* @var string
*/
- public $name = 'calendar_event_type';
+ public $name = 'ccb_core_calendar_event_type';
/**
* Object types for this taxonomy
*
* @var array
*/
- public $object_types = array( 'ccb_core_calendar' );
+ public $object_types = [ 'ccb_core_calendar' ];
/**
* Setup the default taxonomy mappings
@@ -39,8 +39,8 @@ class CCB_Core_Calendar_Event_Type extends CCB_Core_Taxonomy {
* @return array Default options for register_taxonomy
*/
public static function get_taxonomy_args() {
- return array(
- 'labels' => array(
+ return [
+ 'labels' => [
'name' => __( 'Types', 'ccb-core' ),
'singular_name' => __( 'Type', 'ccb-core' ),
'search_items' => __( 'Search Types', 'ccb-core' ),
@@ -51,13 +51,13 @@ public static function get_taxonomy_args() {
'update_item' => __( 'Update Type', 'ccb-core' ),
'add_new_item' => __( 'Add New Type', 'ccb-core' ),
'new_item_name' => __( 'New Type', 'ccb-core' ),
- ),
+ ],
'hierarchical' => true,
'show_admin_column' => true,
'show_ui' => true,
'query_var' => true,
'api_mapping' => 'event_type', // The field key from the CCB API.
- );
+ ];
}
}
diff --git a/includes/taxonomies/class-ccb-core-calendar-group-name.php b/includes/taxonomies/class-ccb-core-calendar-group-name.php
index 9e6e02e..94280e9 100644
--- a/includes/taxonomies/class-ccb-core-calendar-group-name.php
+++ b/includes/taxonomies/class-ccb-core-calendar-group-name.php
@@ -23,14 +23,14 @@ class CCB_Core_Calendar_Group_Name extends CCB_Core_Taxonomy {
*
* @var string
*/
- public $name = 'calendar_group_name';
+ public $name = 'ccb_core_calendar_group_name';
/**
* Object types for this taxonomy
*
* @var array
*/
- public $object_types = array( 'ccb_core_calendar' );
+ public $object_types = [ 'ccb_core_calendar' ];
/**
* Setup the default taxonomy mappings
@@ -39,8 +39,8 @@ class CCB_Core_Calendar_Group_Name extends CCB_Core_Taxonomy {
* @return array Default options for register_taxonomy
*/
public static function get_taxonomy_args() {
- return array(
- 'labels' => array(
+ return [
+ 'labels' => [
'name' => __( 'Group Names', 'ccb-core' ),
'singular_name' => __( 'Group Name', 'ccb-core' ),
'search_items' => __( 'Search Group Names', 'ccb-core' ),
@@ -51,13 +51,13 @@ public static function get_taxonomy_args() {
'update_item' => __( 'Update Group Name', 'ccb-core' ),
'add_new_item' => __( 'Add New Group Name', 'ccb-core' ),
'new_item_name' => __( 'New Group Name', 'ccb-core' ),
- ),
+ ],
'hierarchical' => true,
'show_admin_column' => true,
'show_ui' => true,
'query_var' => true,
'api_mapping' => 'group_name', // The field key from the CCB API.
- );
+ ];
}
}
diff --git a/includes/taxonomies/class-ccb-core-calendar-grouping-name.php b/includes/taxonomies/class-ccb-core-calendar-grouping-name.php
index 7547fb3..05dd6ee 100644
--- a/includes/taxonomies/class-ccb-core-calendar-grouping-name.php
+++ b/includes/taxonomies/class-ccb-core-calendar-grouping-name.php
@@ -23,14 +23,14 @@ class CCB_Core_Calendar_Grouping_Name extends CCB_Core_Taxonomy {
*
* @var string
*/
- public $name = 'calendar_grouping_name';
+ public $name = 'ccb_core_calendar_grouping_name';
/**
* Object types for this taxonomy
*
* @var array
*/
- public $object_types = array( 'ccb_core_calendar' );
+ public $object_types = [ 'ccb_core_calendar' ];
/**
* Setup the default taxonomy mappings
@@ -39,8 +39,8 @@ class CCB_Core_Calendar_Grouping_Name extends CCB_Core_Taxonomy {
* @return array Default options for register_taxonomy
*/
public static function get_taxonomy_args() {
- return array(
- 'labels' => array(
+ return [
+ 'labels' => [
'name' => __( 'Grouping Names', 'ccb-core' ),
'singular_name' => __( 'Grouping Name', 'ccb-core' ),
'search_items' => __( 'Search Grouping Names', 'ccb-core' ),
@@ -51,13 +51,13 @@ public static function get_taxonomy_args() {
'update_item' => __( 'Update Grouping Name', 'ccb-core' ),
'add_new_item' => __( 'Add New Grouping Name', 'ccb-core' ),
'new_item_name' => __( 'New Grouping Name', 'ccb-core' ),
- ),
+ ],
'hierarchical' => true,
'show_admin_column' => true,
'show_ui' => true,
'query_var' => true,
'api_mapping' => 'grouping_name', // The field key from the CCB API.
- );
+ ];
}
}
diff --git a/includes/taxonomies/class-ccb-core-group-area.php b/includes/taxonomies/class-ccb-core-group-area.php
index 87c57f9..9215a07 100644
--- a/includes/taxonomies/class-ccb-core-group-area.php
+++ b/includes/taxonomies/class-ccb-core-group-area.php
@@ -23,14 +23,14 @@ class CCB_Core_Group_Area extends CCB_Core_Taxonomy {
*
* @var string
*/
- public $name = 'group_areas';
+ public $name = 'ccb_core_group_area';
/**
* Object types for this taxonomy
*
* @var array
*/
- public $object_types = array( 'ccb_core_groups' );
+ public $object_types = [ 'ccb_core_group' ];
/**
* Setup the default taxonomy mappings
@@ -39,8 +39,8 @@ class CCB_Core_Group_Area extends CCB_Core_Taxonomy {
* @return array Default options for register_taxonomy
*/
public static function get_taxonomy_args() {
- return array(
- 'labels' => array(
+ return [
+ 'labels' => [
'name' => __( 'Areas', 'ccb-core' ),
'singular_name' => __( 'Area', 'ccb-core' ),
'search_items' => __( 'Search Areas', 'ccb-core' ),
@@ -51,13 +51,13 @@ public static function get_taxonomy_args() {
'update_item' => __( 'Update Area', 'ccb-core' ),
'add_new_item' => __( 'Add New Area', 'ccb-core' ),
'new_item_name' => __( 'New Area', 'ccb-core' ),
- ),
+ ],
'hierarchical' => true,
'show_admin_column' => true,
'show_ui' => true,
'query_var' => true,
'api_mapping' => 'area', // The field key from the CCB API.
- );
+ ];
}
}
diff --git a/includes/taxonomies/class-ccb-core-group-day.php b/includes/taxonomies/class-ccb-core-group-day.php
index d838c20..da7bf7f 100644
--- a/includes/taxonomies/class-ccb-core-group-day.php
+++ b/includes/taxonomies/class-ccb-core-group-day.php
@@ -23,14 +23,14 @@ class CCB_Core_Group_Day extends CCB_Core_Taxonomy {
*
* @var string
*/
- public $name = 'group_days';
+ public $name = 'ccb_core_group_day';
/**
* Object types for this taxonomy
*
* @var array
*/
- public $object_types = array( 'ccb_core_groups' );
+ public $object_types = [ 'ccb_core_group' ];
/**
* Setup the default taxonomy mappings
@@ -39,8 +39,8 @@ class CCB_Core_Group_Day extends CCB_Core_Taxonomy {
* @return array Default options for register_taxonomy
*/
public static function get_taxonomy_args() {
- return array(
- 'labels' => array(
+ return [
+ 'labels' => [
'name' => __( 'Days', 'ccb-core' ),
'singular_name' => __( 'Day', 'ccb-core' ),
'search_items' => __( 'Search Days', 'ccb-core' ),
@@ -51,13 +51,13 @@ public static function get_taxonomy_args() {
'update_item' => __( 'Update Day', 'ccb-core' ),
'add_new_item' => __( 'Add New Day', 'ccb-core' ),
'new_item_name' => __( 'New Day', 'ccb-core' ),
- ),
+ ],
'hierarchical' => true,
'show_admin_column' => true,
'show_ui' => true,
'query_var' => true,
'api_mapping' => 'meeting_day', // The field key from the CCB API.
- );
+ ];
}
}
diff --git a/includes/taxonomies/class-ccb-core-group-department.php b/includes/taxonomies/class-ccb-core-group-department.php
index d46f386..d7c06fa 100644
--- a/includes/taxonomies/class-ccb-core-group-department.php
+++ b/includes/taxonomies/class-ccb-core-group-department.php
@@ -23,14 +23,14 @@ class CCB_Core_Group_Department extends CCB_Core_Taxonomy {
*
* @var string
*/
- public $name = 'group_departments';
+ public $name = 'ccb_core_group_department';
/**
* Object types for this taxonomy
*
* @var array
*/
- public $object_types = array( 'ccb_core_groups' );
+ public $object_types = [ 'ccb_core_group' ];
/**
* Setup the default taxonomy mappings
@@ -39,8 +39,8 @@ class CCB_Core_Group_Department extends CCB_Core_Taxonomy {
* @return array Default options for register_taxonomy
*/
public static function get_taxonomy_args() {
- return array(
- 'labels' => array(
+ return [
+ 'labels' => [
'name' => __( 'Departments', 'ccb-core' ),
'singular_name' => __( 'Department', 'ccb-core' ),
'search_items' => __( 'Search Departments', 'ccb-core' ),
@@ -51,13 +51,13 @@ public static function get_taxonomy_args() {
'update_item' => __( 'Update Department', 'ccb-core' ),
'add_new_item' => __( 'Add New Department', 'ccb-core' ),
'new_item_name' => __( 'New Department', 'ccb-core' ),
- ),
+ ],
'hierarchical' => true,
'show_admin_column' => true,
'show_ui' => true,
'query_var' => true,
'api_mapping' => 'department', // The field key from the CCB API.
- );
+ ];
}
}
diff --git a/includes/taxonomies/class-ccb-core-group-tag.php b/includes/taxonomies/class-ccb-core-group-tag.php
index 7d5aaa2..5430461 100644
--- a/includes/taxonomies/class-ccb-core-group-tag.php
+++ b/includes/taxonomies/class-ccb-core-group-tag.php
@@ -23,14 +23,14 @@ class CCB_Core_Group_Tag extends CCB_Core_Taxonomy {
*
* @var string
*/
- public $name = 'group_tags';
+ public $name = 'ccb_core_group_tag';
/**
* Object types for this taxonomy
*
* @var array
*/
- public $object_types = array( 'ccb_core_groups' );
+ public $object_types = [ 'ccb_core_group' ];
/**
* Setup the default taxonomy mappings
@@ -39,8 +39,8 @@ class CCB_Core_Group_Tag extends CCB_Core_Taxonomy {
* @return array Default options for register_taxonomy
*/
public static function get_taxonomy_args() {
- return array(
- 'labels' => array(
+ return [
+ 'labels' => [
'name' => __( 'Group Tags', 'ccb-core' ),
'singular_name' => __( 'Group Tag', 'ccb-core' ),
'search_items' => __( 'Search Group Tags', 'ccb-core' ),
@@ -51,15 +51,15 @@ public static function get_taxonomy_args() {
'update_item' => __( 'Update Group Tag', 'ccb-core' ),
'add_new_item' => __( 'Add New Group Tag', 'ccb-core' ),
'new_item_name' => __( 'New Group Tag', 'ccb-core' ),
- ),
+ ],
'hierarchical' => false,
'show_admin_column' => true,
'show_ui' => true,
'query_var' => true,
- 'api_mapping' => array(
+ 'api_mapping' => [
'childcare_provided' => __( 'Childcare Provided', 'ccb-core' ), // The field key from the CCB API.
- ),
- );
+ ],
+ ];
}
}
diff --git a/includes/taxonomies/class-ccb-core-group-time.php b/includes/taxonomies/class-ccb-core-group-time.php
index 916f281..16cebc7 100644
--- a/includes/taxonomies/class-ccb-core-group-time.php
+++ b/includes/taxonomies/class-ccb-core-group-time.php
@@ -23,14 +23,14 @@ class CCB_Core_Group_Time extends CCB_Core_Taxonomy {
*
* @var string
*/
- public $name = 'group_times';
+ public $name = 'ccb_core_group_time';
/**
* Object types for this taxonomy
*
* @var array
*/
- public $object_types = array( 'ccb_core_groups' );
+ public $object_types = [ 'ccb_core_group' ];
/**
* Setup the default taxonomy mappings
@@ -39,8 +39,8 @@ class CCB_Core_Group_Time extends CCB_Core_Taxonomy {
* @return array Default options for register_taxonomy
*/
public static function get_taxonomy_args() {
- return array(
- 'labels' => array(
+ return [
+ 'labels' => [
'name' => __( 'Times', 'ccb-core' ),
'singular_name' => __( 'Time', 'ccb-core' ),
'search_items' => __( 'Search Times', 'ccb-core' ),
@@ -51,13 +51,13 @@ public static function get_taxonomy_args() {
'update_item' => __( 'Update Time', 'ccb-core' ),
'add_new_item' => __( 'Add New Time', 'ccb-core' ),
'new_item_name' => __( 'New Time', 'ccb-core' ),
- ),
+ ],
'hierarchical' => true,
'show_admin_column' => true,
'show_ui' => true,
'query_var' => true,
'api_mapping' => 'meeting_time', // The field key from the CCB API.
- );
+ ];
}
}
diff --git a/includes/taxonomies/class-ccb-core-group-type.php b/includes/taxonomies/class-ccb-core-group-type.php
index ab104a1..c3fcafe 100644
--- a/includes/taxonomies/class-ccb-core-group-type.php
+++ b/includes/taxonomies/class-ccb-core-group-type.php
@@ -23,14 +23,14 @@ class CCB_Core_Group_Type extends CCB_Core_Taxonomy {
*
* @var string
*/
- public $name = 'group_types';
+ public $name = 'ccb_core_group_type';
/**
* Object types for this taxonomy
*
* @var array
*/
- public $object_types = array( 'ccb_core_groups' );
+ public $object_types = [ 'ccb_core_group' ];
/**
* Setup the default taxonomy mappings
@@ -39,8 +39,8 @@ class CCB_Core_Group_Type extends CCB_Core_Taxonomy {
* @return array Default options for register_taxonomy
*/
public static function get_taxonomy_args() {
- return array(
- 'labels' => array(
+ return [
+ 'labels' => [
'name' => __( 'Types', 'ccb-core' ),
'singular_name' => __( 'Type', 'ccb-core' ),
'search_items' => __( 'Search Types', 'ccb-core' ),
@@ -51,13 +51,13 @@ public static function get_taxonomy_args() {
'update_item' => __( 'Update Type', 'ccb-core' ),
'add_new_item' => __( 'Add New Type', 'ccb-core' ),
'new_item_name' => __( 'New Type', 'ccb-core' ),
- ),
+ ],
'hierarchical' => true,
'show_admin_column' => true,
'show_ui' => true,
'query_var' => true,
'api_mapping' => 'group_type', // The field key from the CCB API.
- );
+ ];
}
}
diff --git a/includes/taxonomies/class-ccb-core-taxonomy.php b/includes/taxonomies/class-ccb-core-taxonomy.php
index 3849cf7..8c0b388 100644
--- a/includes/taxonomies/class-ccb-core-taxonomy.php
+++ b/includes/taxonomies/class-ccb-core-taxonomy.php
@@ -30,13 +30,13 @@ abstract class CCB_Core_Taxonomy {
*
* @var array
*/
- public $object_types = array();
+ public $object_types = [];
/**
* Initialize the class
*/
public function __construct() {
- add_action( 'init', array( $this, 'register_taxonomy' ) );
+ add_action( 'init', [ $this, 'register_taxonomy' ] );
add_filter( 'ccb_core_synchronizer_taxonomy_api_map', [ $this, 'get_taxonomy_map' ] );
}
diff --git a/lib/class-ccb-core-vendor-encryption.php b/lib/class-ccb-core-vendor-encryption.php
index f4d36bb..c86e6fc 100644
--- a/lib/class-ccb-core-vendor-encryption.php
+++ b/lib/class-ccb-core-vendor-encryption.php
@@ -124,7 +124,7 @@ protected function get_keys( $salt, $key ) {
$cipher_key = substr( $key, 0, $key_size );
$mac_key = substr( $key, $key_size, $key_size );
$iv = substr( $key, 2 * $key_size );
- return array( $cipher_key, $mac_key, $iv );
+ return [ $cipher_key, $mac_key, $iv ];
} else {
return false;
}
diff --git a/tests/bootstrap.php b/tests/bootstrap.php
index b337b11..83eacf7 100644
--- a/tests/bootstrap.php
+++ b/tests/bootstrap.php
@@ -2,9 +2,11 @@
/**
* PHPUnit bootstrap file
*
- * @package Sample_Plugin
+ * @package CCB_Core
*/
+define( 'IS_UNIT_TEST', true );
+
$_tests_dir = getenv( 'WP_TESTS_DIR' );
if ( ! $_tests_dir ) {
diff --git a/tests/test-sample.php b/tests/test-sample.php
deleted file mode 100644
index 5dd94fb..0000000
--- a/tests/test-sample.php
+++ /dev/null
@@ -1,20 +0,0 @@
-assertTrue( true );
- }
-}
diff --git a/tests/test-synchronizer-calendar.php b/tests/test-synchronizer-calendar.php
new file mode 100644
index 0000000..f9d7b42
--- /dev/null
+++ b/tests/test-synchronizer-calendar.php
@@ -0,0 +1,158 @@
+utils = new Test_Utils();
+ $this->synchronizer = CCB_Core_Synchronizer::instance();
+ $this->synchronizer->map = $this->utils->synchronizer_get_calendar_map();
+ }
+
+ /**
+ * Test the insert of events when the database is empty.
+ */
+ public function test_update_content_insert_events() {
+ // Testing the $result is a successful insert.
+ $result = $this->synchronizer->update_content(
+ $this->utils->api_mock_response( 'public_calendar_listing_sample.xml' ),
+ $this->synchronizer->map['ccb_core_calendar'],
+ 'ccb_core_calendar'
+ );
+
+ $expected_result = [
+ 'success' => true,
+ 'insert_update' => [
+ 'success' => true,
+ 'processed' => 10,
+ 'message' => '',
+ ],
+ 'delete' => [
+ 'success' => true,
+ 'processed' => 0,
+ ],
+ ];
+
+ $this->assertEqualSets( $expected_result, $result );
+ }
+
+ /**
+ * Test when some events have been updated in CCB.
+ */
+ public function test_update_content_update_events() {
+ // Initial insert of events
+ $result = $this->synchronizer->update_content(
+ $this->utils->api_mock_response( 'public_calendar_listing_sample.xml' ),
+ $this->synchronizer->map['ccb_core_calendar'],
+ 'ccb_core_calendar'
+ );
+
+ // Testing whether updated events from CCB get updated in WordPress.
+ $result = $this->synchronizer->update_content(
+ $this->utils->api_mock_response( 'public_calendar_listing_some_updated_sample.xml' ),
+ $this->synchronizer->map['ccb_core_calendar'],
+ 'ccb_core_calendar'
+ );
+
+ // Events do not have a unique identifier from CCB, so updates
+ // are actually 3 inserts and 3 deletes.
+ $expected_result = [
+ 'success' => true,
+ 'insert_update' => [
+ 'success' => true,
+ 'processed' => 3,
+ 'message' => '',
+ ],
+ 'delete' => [
+ 'success' => true,
+ 'processed' => 3,
+ 'message' => '',
+ ],
+ ];
+
+ $this->assertEqualSets( $expected_result, $result );
+ }
+
+ /**
+ * Test when some events have been deleted in CCB.
+ */
+ public function test_update_content_deleted_events() {
+ // Initial insert of events
+ $result = $this->synchronizer->update_content(
+ $this->utils->api_mock_response( 'public_calendar_listing_sample.xml' ),
+ $this->synchronizer->map['ccb_core_calendar'],
+ 'ccb_core_calendar'
+ );
+
+ // Testing whether deleted events from CCB get deleted in WordPress.
+ $result = $this->synchronizer->update_content(
+ $this->utils->api_mock_response( 'public_calendar_listing_some_deleted_sample.xml' ),
+ $this->synchronizer->map['ccb_core_calendar'],
+ 'ccb_core_calendar'
+ );
+
+ $expected_result = [
+ 'success' => true,
+ 'insert_update' => [
+ 'success' => true,
+ 'processed' => 0,
+ ],
+ 'delete' => [
+ 'success' => true,
+ 'processed' => 3,
+ 'message' => '',
+ ],
+ ];
+
+ $this->assertEqualSets( $expected_result, $result );
+ }
+
+ /**
+ * Test when some events are new in CCB.
+ */
+ public function test_update_content_new_events() {
+ // Initial insert of events
+ $result = $this->synchronizer->update_content(
+ $this->utils->api_mock_response( 'public_calendar_listing_sample.xml' ),
+ $this->synchronizer->map['ccb_core_calendar'],
+ 'ccb_core_calendar'
+ );
+
+ // Testing whether new events from CCB get inserted in WordPress.
+ $result = $this->synchronizer->update_content(
+ $this->utils->api_mock_response( 'public_calendar_listing_some_new_sample.xml' ),
+ $this->synchronizer->map['ccb_core_calendar'],
+ 'ccb_core_calendar'
+ );
+
+ $expected_result = [
+ 'success' => true,
+ 'insert_update' => [
+ 'success' => true,
+ 'processed' => 3,
+ 'message' => '',
+ ],
+ 'delete' => [
+ 'success' => true,
+ 'processed' => 0,
+ ],
+ ];
+
+ $this->assertEqualSets( $expected_result, $result );
+ }
+
+ public function tearDown() {
+ parent::tearDown();
+ }
+
+}
diff --git a/tests/test-synchronizer-groups.php b/tests/test-synchronizer-groups.php
new file mode 100644
index 0000000..e8af5e8
--- /dev/null
+++ b/tests/test-synchronizer-groups.php
@@ -0,0 +1,158 @@
+utils = new Test_Utils();
+ $this->synchronizer = CCB_Core_Synchronizer::instance();
+ $this->synchronizer->map = $this->utils->synchronizer_get_groups_map();
+ }
+
+ /**
+ * Test the insert of groups when the database is empty.
+ */
+ public function test_update_content_insert_groups() {
+ // Testing the $result is a successful insert.
+ $result = $this->synchronizer->update_content(
+ $this->utils->api_mock_response( 'group_profiles_sample.xml' ),
+ $this->synchronizer->map['ccb_core_group'],
+ 'ccb_core_group'
+ );
+
+ $expected_result = [
+ 'success' => true,
+ 'insert_update' => [
+ 'success' => true,
+ 'processed' => 4,
+ 'message' => '',
+ ],
+ 'delete' => [
+ 'success' => true,
+ 'processed' => 0,
+ ],
+ ];
+
+ $this->assertEqualSets( $expected_result, $result );
+ }
+
+ /**
+ * Test when some groups have been updated in CCB.
+ */
+ public function test_update_content_update_groups() {
+ // Insert some posts.
+ $result = $this->synchronizer->update_content(
+ $this->utils->api_mock_response( 'group_profiles_sample.xml' ),
+ $this->synchronizer->map['ccb_core_group'],
+ 'ccb_core_group'
+ );
+
+ // Testing the $result has some successful updates.
+ $result = $this->synchronizer->update_content(
+ $this->utils->api_mock_response( 'group_profiles_some_updated_sample.xml' ),
+ $this->synchronizer->map['ccb_core_group'],
+ 'ccb_core_group'
+ );
+
+ $expected_result = [
+ 'success' => true,
+ 'insert_update' => [
+ 'success' => true,
+ 'processed' => 2,
+ 'message' => '',
+ ],
+ 'delete' => [
+ 'success' => true,
+ 'processed' => 0,
+ ],
+ ];
+
+ $this->assertEqualSets( $expected_result, $result );
+
+ }
+
+ /**
+ * Test when some groups have been unlisted in CCB.
+ */
+ public function test_update_content_unlisted_groups() {
+ // Insert some posts.
+ $result = $this->synchronizer->update_content(
+ $this->utils->api_mock_response( 'group_profiles_sample.xml' ),
+ $this->synchronizer->map['ccb_core_group'],
+ 'ccb_core_group'
+ );
+
+ // Testing the $result has deleted some unlisted posts.
+ $result = $this->synchronizer->update_content(
+ $this->utils->api_mock_response( 'group_profiles_some_unlisted_sample.xml' ),
+ $this->synchronizer->map['ccb_core_group'],
+ 'ccb_core_group'
+ );
+
+ $expected_result = [
+ 'success' => true,
+ 'insert_update' => [
+ 'success' => true,
+ 'processed' => 0,
+ ],
+ 'delete' => [
+ 'success' => true,
+ 'processed' => 2,
+ 'message' => '',
+ ],
+ ];
+
+ $this->assertEqualSets( $expected_result, $result );
+
+ }
+
+ /**
+ * Test when some groups have been inactivated in CCB.
+ */
+ public function test_update_content_inactivated_groups() {
+ // Insert some posts.
+ $result = $this->synchronizer->update_content(
+ $this->utils->api_mock_response( 'group_profiles_sample.xml' ),
+ $this->synchronizer->map['ccb_core_group'],
+ 'ccb_core_group'
+ );
+
+ // Testing the $result has deleted some unlisted posts.
+ $result = $this->synchronizer->update_content(
+ $this->utils->api_mock_response( 'group_profiles_some_inactivated_sample.xml' ),
+ $this->synchronizer->map['ccb_core_group'],
+ 'ccb_core_group'
+ );
+
+ $expected_result = [
+ 'success' => true,
+ 'insert_update' => [
+ 'success' => true,
+ 'processed' => 0,
+ ],
+ 'delete' => [
+ 'success' => true,
+ 'processed' => 2,
+ 'message' => '',
+ ],
+ ];
+
+ $this->assertEqualSets( $expected_result, $result );
+
+ }
+
+ public function tearDown() {
+ parent::tearDown();
+ }
+
+}
diff --git a/tests/test-utils.php b/tests/test-utils.php
new file mode 100644
index 0000000..fb23e38
--- /dev/null
+++ b/tests/test-utils.php
@@ -0,0 +1,104 @@
+plugin_path = plugin_dir_path( __FILE__ );
+ }
+
+ public function synchronizer_get_groups_map( $include_image_link = false ) {
+ return [
+ 'ccb_core_group' => [
+ 'service' => 'group_profiles',
+ 'data' => [
+ 'include_participants' => false,
+ 'include_image_link' => $include_image_link,
+ ],
+ 'nodes' => [ 'groups', 'group' ],
+ 'fields' => [
+ 'name' => 'post_title',
+ 'description' => 'post_content',
+ 'main_leader' => 'post_meta',
+ 'calendar_feed' => 'post_meta',
+ 'current_members' => 'post_meta',
+ 'group_capacity' => 'post_meta',
+ 'addresses' => 'post_meta',
+ ],
+ 'taxonomies' => [
+ 'hierarchical' => [
+ 'ccb_core_group_area' => 'area',
+ 'ccb_core_group_day' => 'meeting_day',
+ 'ccb_core_group_department' => 'department',
+ 'ccb_core_group_time' => 'meeting_time',
+ 'ccb_core_group_type' => 'group_type',
+ ],
+ 'nonhierarchical' => [
+ 'ccb_core_group_tag' => [ 'childcare_provided' => 'Childcare Provided' ],
+ ],
+ ],
+ ],
+ ];
+ }
+
+ public function synchronizer_get_calendar_map( $date_start = '', $date_end = '' ) {
+ $date_start = ! empty( $date_start ) ? $date_start : date( 'Y-m-d', strtotime( '1 weeks ago' ) );
+ $date_end = ! empty( $date_end ) ? $date_end : date( 'Y-m-d', strtotime( '+2 weeks' ) );
+ return [
+ 'ccb_core_calendar' => [
+ 'service' => 'public_calendar_listing',
+ 'data' => [
+ 'date_start' => $date_start,
+ 'date_end' => $date_end,
+ ],
+ 'nodes' => [ 'items', 'item' ],
+ 'fields' => [
+ 'event_name' => 'post_title',
+ 'event_description' => 'post_content',
+ 'date' => 'post_meta',
+ 'start_time' => 'post_meta',
+ 'end_time' => 'post_meta',
+ 'event_duration' => 'post_meta',
+ 'location' => 'post_meta',
+ ],
+ 'taxonomies' => [
+ 'hierarchical' => [
+ 'ccb_core_calendar_event_type' => 'event_type',
+ 'ccb_core_calendar_group_name' => 'group_name',
+ 'ccb_core_calendar_grouping_name' => 'grouping_name',
+ ],
+ ],
+ ],
+ ];
+
+ }
+
+ public function api_mock_response( $xml_file, $http_success = true ) {
+ $filepath = $this->plugin_path . 'xml/' . $xml_file;
+ $code = $http_success ? 200 : 500;
+ $status = ( false === strpos( $xml_file, 'fail' ) && $http_success ) ? 'SUCCESS' : 'ERROR';
+ $message = ( false === strpos( $xml_file, 'fail' ) && $http_success ) ? '' : 'Generic unit test error message.';
+
+ $result = [
+ 'code' => $code,
+ 'status' => $status,
+ 'message' => $message,
+ ];
+
+ if ( file_exists( $filepath ) ) {
+ $result['xml'] = file_get_contents( $filepath );
+ libxml_use_internal_errors( true );
+ $result['body'] = simplexml_load_string( $result['xml'] );
+ }
+ return $result;
+ }
+}
diff --git a/tests/xml/group_profiles_sample.xml b/tests/xml/group_profiles_sample.xml
new file mode 100644
index 0000000..591f860
--- /dev/null
+++ b/tests/xml/group_profiles_sample.xml
@@ -0,0 +1,405 @@
+
+
+
+
+
+
+
+
+
+
+ group_profiles
+ execute
+ public
+
+
+ Test Group One
+ This is a public_search_listed false example and should not be imported
+
+ Campus One
+
+ Jean Luc
+ Picard
+ Jean Luc Picard
+ jeanluc@enterprise.com
+
+
+
+
+
+ William
+ Riker
+ William Riker
+
+
+
+
+
+
+ Data
+
+ Data
+
+
+
+
+
+ Group Type One
+
+ Area One
+ webcal://starfleet.ccbchurch.com/group_calendar.ics?id=1&tk=57395EF2EECB102CAABD00B0D0E1CF3B
+
+ 3300
+ Unlimited
+
+
+
+ Meeting Address
+Street
+ City
+
+
+ -112.076814
+ 33.447549
+ Meeting Address
+Street
+ City
+
+
+
+
+ false
+ Announcement Only
+ Open to All
+ true
+
+ true
+ false
+ false
+ Jean Luc Picard
+ Jean Luc Picard
+ 2016-06-17 04:02:53
+ 2017-04-30 16:36:38
+
+
+ Test Group Two
+ This is an inactive example and should not be imported
+
+ Campus One
+
+ Jean Luc
+ Picard
+ Jean Luc Picard
+ jeanluc@enterprise.com
+
+
+
+
+
+ William
+ Riker
+ William Riker
+
+
+
+
+
+
+ Data
+
+ Data
+
+
+
+
+
+ Group Type One
+
+ Area One
+ webcal://starfleet.ccbchurch.com/group_calendar.ics?id=1&tk=57395EF2EECB102CAABD00B0D0E1CF3B
+
+ 3300
+ Unlimited
+
+
+ Ten Forward
+ Deck 10, Forward
+ Enterprise
+ C0
+ 80129
+ -117.322031
+ 33.229150
+ Deck 10, Forward
+ Enterprise, CO 80129
+
+
+
+
+ false
+ Announcement Only
+ Invitation or Request Required
+ false
+
+ true
+ true
+ true
+ Jean Luc Picard
+ Jean Luc Picard
+ 2016-07-17 04:02:53
+ 2017-05-30 16:36:38
+
+
+ Test Group Three
+ This is a group with all properties set
+ https://i.imgur.com/penLEu9.jpg
+ Campus Two
+
+ Geordi
+ La Forge
+ Geordi La Forge
+ geordi@enterprise.com
+
+
+
+
+
+ William
+ Riker
+ William Riker
+
+
+
+
+
+
+ Data
+
+ Data
+
+
+
+
+
+ Group Type One
+ Department One
+ Area One
+ webcal://starfleet.ccbchurch.com/group_calendar.ics?id=1&tk=57395EF2EECB102CAABD00B0D0E1CF3B
+
+ 100
+ Unlimited
+
+
+ Ten Forward
+ Deck 10, Forward
+ Enterprise
+ C0
+ 80129
+ -117.322031
+ 33.229150
+ Deck 10, Forward
+ Enterprise, CO 80129
+
+
+ Weekdays
+ Morning
+ true
+ Members Interact
+ Invitation or Request Required
+ false
+
+ true
+ true
+ false
+ Jean Luc Picard
+ Jean Luc Picard
+ 2016-07-17 04:02:53
+ 2017-05-30 16:36:38
+
+
+ Test Group Four
+ This is the same as Test Group Three but different name and ID
+ https://i.imgur.com/penLEu9.jpg
+ Campus Two
+
+ Geordi
+ La Forge
+ Geordi La Forge
+ geordi@enterprise.com
+
+
+
+
+
+ William
+ Riker
+ William Riker
+
+
+
+
+
+
+ Data
+
+ Data
+
+
+
+
+
+ Group Type One
+ Department One
+ Area One
+ webcal://starfleet.ccbchurch.com/group_calendar.ics?id=1&tk=57395EF2EECB102CAABD00B0D0E1CF3B
+
+ 100
+ Unlimited
+
+
+ Ten Forward
+ Deck 10, Forward
+ Enterprise
+ C0
+ 80129
+ -117.322031
+ 33.229150
+ Deck 10, Forward
+ Enterprise, CO 80129
+
+
+ Weekdays
+ Morning
+ true
+ Members Interact
+ Invitation or Request Required
+ false
+
+ true
+ true
+ false
+ Jean Luc Picard
+ Jean Luc Picard
+ 2016-07-17 04:02:53
+ 2017-05-30 16:36:38
+
+
+ Test Group Five
+ This has all properties but different than Test Group Three and Four
+ https://i.imgur.com/MZ9itik.png
+ Campus Three
+
+ Worf
+ Rozhenko
+ Worf Rozhenko
+ worf@enterprise.com
+
+ 123-456-7890
+
+
+
+ Beverly
+ Crusher
+ Beverly Crusher
+ beverly@enterprise.com
+
+ 123-123-1234
+
+
+
+ Deanna
+ Troi
+ Deanna Troi
+ deanna@enterprise.com
+
+ 246-810-1214
+
+
+ Group Type Two
+ Department Two
+ Area Two
+ webcal://starfleet.ccbchurch.com/group_calendar.ics?id=1&tk=57395EF2EECB102CAABD00B0D0E1CF3B
+
+ 10
+ Unlimited
+
+
+ Main Bridge
+ Deck 1, Forward
+ Enterprise
+ C0
+ 80129
+ -117.322031
+ 33.229150
+ Deck 1, Forward
+ Enterprise, CO 80129
+
+
+ Sundays
+ Nights
+ false
+ Members Interact
+ Invitation or Request Required
+ true
+
+ true
+ true
+ false
+ Jean Luc Picard
+ Jean Luc Picard
+ 2016-08-17 04:02:53
+ 2017-08-30 16:36:38
+
+
+ Test Group Six
+ Similar to Test Group Five but missing some properties
+ https://i.imgur.com/MZ9itik.png
+ Campus Three
+
+ Worf
+ Rozhenko
+ Worf Rozhenko
+ worf@enterprise.com
+
+ 123-456-7890
+
+
+
+
+ Group Type Two
+
+
+
+
+ 10
+ Unlimited
+
+
+ Main Bridge
+ Deck 1, Forward
+ Enterprise
+ C0
+ 80129
+ -117.322031
+ 33.229150
+ Deck 1, Forward
+ Enterprise, CO 80129
+
+
+ Mondays
+
+ true
+ Members Interact
+ Invitation or Request Required
+ true
+
+ true
+ true
+ false
+ Jean Luc Picard
+ Jean Luc Picard
+ 2016-08-17 04:02:53
+ 2017-08-30 16:36:38
+
+
+
+
diff --git a/tests/xml/group_profiles_some_inactivated_sample.xml b/tests/xml/group_profiles_some_inactivated_sample.xml
new file mode 100644
index 0000000..5bb4de8
--- /dev/null
+++ b/tests/xml/group_profiles_some_inactivated_sample.xml
@@ -0,0 +1,405 @@
+
+
+
+
+
+
+
+
+
+
+ group_profiles
+ execute
+ public
+
+
+ Test Group One
+ This is a public_search_listed false example and should not be imported
+
+ Campus One
+
+ Jean Luc
+ Picard
+ Jean Luc Picard
+ jeanluc@enterprise.com
+
+
+
+
+
+ William
+ Riker
+ William Riker
+
+
+
+
+
+
+ Data
+
+ Data
+
+
+
+
+
+ Group Type One
+
+ Area One
+ webcal://starfleet.ccbchurch.com/group_calendar.ics?id=1&tk=57395EF2EECB102CAABD00B0D0E1CF3B
+
+ 3300
+ Unlimited
+
+
+
+ Meeting Address
+Street
+ City
+
+
+ -112.076814
+ 33.447549
+ Meeting Address
+Street
+ City
+
+
+
+
+ false
+ Announcement Only
+ Open to All
+ true
+
+ true
+ false
+ false
+ Jean Luc Picard
+ Jean Luc Picard
+ 2016-06-17 04:02:53
+ 2017-04-30 16:36:38
+
+
+ Test Group Two
+ This is an inactive example and should not be imported
+
+ Campus One
+
+ Jean Luc
+ Picard
+ Jean Luc Picard
+ jeanluc@enterprise.com
+
+
+
+
+
+ William
+ Riker
+ William Riker
+
+
+
+
+
+
+ Data
+
+ Data
+
+
+
+
+
+ Group Type One
+
+ Area One
+ webcal://starfleet.ccbchurch.com/group_calendar.ics?id=1&tk=57395EF2EECB102CAABD00B0D0E1CF3B
+
+ 3300
+ Unlimited
+
+
+ Ten Forward
+ Deck 10, Forward
+ Enterprise
+ C0
+ 80129
+ -117.322031
+ 33.229150
+ Deck 10, Forward
+ Enterprise, CO 80129
+
+
+
+
+ false
+ Announcement Only
+ Invitation or Request Required
+ false
+
+ true
+ true
+ true
+ Jean Luc Picard
+ Jean Luc Picard
+ 2016-07-17 04:02:53
+ 2017-05-30 16:36:38
+
+
+ Test Group Three
+ This is a group with all properties set
+ https://i.imgur.com/penLEu9.jpg
+ Campus Two
+
+ Geordi
+ La Forge
+ Geordi La Forge
+ geordi@enterprise.com
+
+
+
+
+
+ William
+ Riker
+ William Riker
+
+
+
+
+
+
+ Data
+
+ Data
+
+
+
+
+
+ Group Type One
+ Department One
+ Area One
+ webcal://starfleet.ccbchurch.com/group_calendar.ics?id=1&tk=57395EF2EECB102CAABD00B0D0E1CF3B
+
+ 100
+ Unlimited
+
+
+ Ten Forward
+ Deck 10, Forward
+ Enterprise
+ C0
+ 80129
+ -117.322031
+ 33.229150
+ Deck 10, Forward
+ Enterprise, CO 80129
+
+
+ Weekdays
+ Morning
+ true
+ Members Interact
+ Invitation or Request Required
+ false
+
+ true
+ true
+ false
+ Jean Luc Picard
+ Jean Luc Picard
+ 2016-07-17 04:02:53
+ 2017-05-30 16:36:38
+
+
+ Test Group Four
+ This is the same as Test Group Three but different name and ID
+ https://i.imgur.com/penLEu9.jpg
+ Campus Two
+
+ Geordi
+ La Forge
+ Geordi La Forge
+ geordi@enterprise.com
+
+
+
+
+
+ William
+ Riker
+ William Riker
+
+
+
+
+
+
+ Data
+
+ Data
+
+
+
+
+
+ Group Type One
+ Department One
+ Area One
+ webcal://starfleet.ccbchurch.com/group_calendar.ics?id=1&tk=57395EF2EECB102CAABD00B0D0E1CF3B
+
+ 100
+ Unlimited
+
+
+ Ten Forward
+ Deck 10, Forward
+ Enterprise
+ C0
+ 80129
+ -117.322031
+ 33.229150
+ Deck 10, Forward
+ Enterprise, CO 80129
+
+
+ Weekdays
+ Morning
+ true
+ Members Interact
+ Invitation or Request Required
+ false
+
+ true
+ true
+ false
+ Jean Luc Picard
+ Jean Luc Picard
+ 2016-07-17 04:02:53
+ 2017-05-30 16:36:38
+
+
+ Test Group Five INACTIVATED
+ This has all properties but different than Test Group Three and Four
+ https://i.imgur.com/MZ9itik.png
+ Campus Three
+
+ Worf
+ Rozhenko
+ Worf Rozhenko
+ worf@enterprise.com
+
+ 123-456-7890
+
+
+
+ Beverly
+ Crusher
+ Beverly Crusher
+ beverly@enterprise.com
+
+ 123-123-1234
+
+
+
+ Deanna
+ Troi
+ Deanna Troi
+ deanna@enterprise.com
+
+ 246-810-1214
+
+
+ Group Type Two
+ Department Two
+ Area Two
+ webcal://starfleet.ccbchurch.com/group_calendar.ics?id=1&tk=57395EF2EECB102CAABD00B0D0E1CF3B
+
+ 10
+ Unlimited
+
+
+ Main Bridge
+ Deck 1, Forward
+ Enterprise
+ C0
+ 80129
+ -117.322031
+ 33.229150
+ Deck 1, Forward
+ Enterprise, CO 80129
+
+
+ Sundays
+ Nights
+ false
+ Members Interact
+ Invitation or Request Required
+ true
+
+ true
+ true
+ false
+ Jean Luc Picard
+ Jean Luc Picard
+ 2016-08-17 04:02:53
+ 2017-09-15 16:36:38
+
+
+ Test Group Six INACTIVATED
+ Similar to Test Group Five but missing some properties
+ https://i.imgur.com/MZ9itik.png
+ Campus Three
+
+ Worf
+ Rozhenko
+ Worf Rozhenko
+ worf@enterprise.com
+
+ 123-456-7890
+
+
+
+
+ Group Type Two
+
+
+
+
+ 10
+ Unlimited
+
+
+ Main Bridge
+ Deck 1, Forward
+ Enterprise
+ C0
+ 80129
+ -117.322031
+ 33.229150
+ Deck 1, Forward
+ Enterprise, CO 80129
+
+
+ Mondays
+
+ true
+ Members Interact
+ Invitation or Request Required
+ true
+
+ true
+ true
+ false
+ Jean Luc Picard
+ Jean Luc Picard
+ 2016-08-17 04:02:53
+ 2017-09-15 16:36:38
+
+
+
+
diff --git a/tests/xml/group_profiles_some_unlisted_sample.xml b/tests/xml/group_profiles_some_unlisted_sample.xml
new file mode 100644
index 0000000..6d33456
--- /dev/null
+++ b/tests/xml/group_profiles_some_unlisted_sample.xml
@@ -0,0 +1,405 @@
+
+
+
+
+
+
+
+
+
+
+ group_profiles
+ execute
+ public
+
+
+ Test Group One
+ This is a public_search_listed false example and should not be imported
+
+ Campus One
+
+ Jean Luc
+ Picard
+ Jean Luc Picard
+ jeanluc@enterprise.com
+
+
+
+
+
+ William
+ Riker
+ William Riker
+
+
+
+
+
+
+ Data
+
+ Data
+
+
+
+
+
+ Group Type One
+
+ Area One
+ webcal://starfleet.ccbchurch.com/group_calendar.ics?id=1&tk=57395EF2EECB102CAABD00B0D0E1CF3B
+
+ 3300
+ Unlimited
+
+
+
+ Meeting Address
+Street
+ City
+
+
+ -112.076814
+ 33.447549
+ Meeting Address
+Street
+ City
+
+
+
+
+ false
+ Announcement Only
+ Open to All
+ true
+
+ true
+ false
+ false
+ Jean Luc Picard
+ Jean Luc Picard
+ 2016-06-17 04:02:53
+ 2017-04-30 16:36:38
+
+
+ Test Group Two
+ This is an inactive example and should not be imported
+
+ Campus One
+
+ Jean Luc
+ Picard
+ Jean Luc Picard
+ jeanluc@enterprise.com
+
+
+
+
+
+ William
+ Riker
+ William Riker
+
+
+
+
+
+
+ Data
+
+ Data
+
+
+
+
+
+ Group Type One
+
+ Area One
+ webcal://starfleet.ccbchurch.com/group_calendar.ics?id=1&tk=57395EF2EECB102CAABD00B0D0E1CF3B
+
+ 3300
+ Unlimited
+
+
+ Ten Forward
+ Deck 10, Forward
+ Enterprise
+ C0
+ 80129
+ -117.322031
+ 33.229150
+ Deck 10, Forward
+ Enterprise, CO 80129
+
+
+
+
+ false
+ Announcement Only
+ Invitation or Request Required
+ false
+
+ true
+ true
+ true
+ Jean Luc Picard
+ Jean Luc Picard
+ 2016-07-17 04:02:53
+ 2017-05-30 16:36:38
+
+
+ Test Group Three
+ This is a group with all properties set
+ https://i.imgur.com/penLEu9.jpg
+ Campus Two
+
+ Geordi
+ La Forge
+ Geordi La Forge
+ geordi@enterprise.com
+
+
+
+
+
+ William
+ Riker
+ William Riker
+
+
+
+
+
+
+ Data
+
+ Data
+
+
+
+
+
+ Group Type One
+ Department One
+ Area One
+ webcal://starfleet.ccbchurch.com/group_calendar.ics?id=1&tk=57395EF2EECB102CAABD00B0D0E1CF3B
+
+ 100
+ Unlimited
+
+
+ Ten Forward
+ Deck 10, Forward
+ Enterprise
+ C0
+ 80129
+ -117.322031
+ 33.229150
+ Deck 10, Forward
+ Enterprise, CO 80129
+
+
+ Weekdays
+ Morning
+ true
+ Members Interact
+ Invitation or Request Required
+ false
+
+ true
+ true
+ false
+ Jean Luc Picard
+ Jean Luc Picard
+ 2016-07-17 04:02:53
+ 2017-05-30 16:36:38
+
+
+ Test Group Four
+ This is the same as Test Group Three but different name and ID
+ https://i.imgur.com/penLEu9.jpg
+ Campus Two
+
+ Geordi
+ La Forge
+ Geordi La Forge
+ geordi@enterprise.com
+
+
+
+
+
+ William
+ Riker
+ William Riker
+
+
+
+
+
+
+ Data
+
+ Data
+
+
+
+
+
+ Group Type One
+ Department One
+ Area One
+ webcal://starfleet.ccbchurch.com/group_calendar.ics?id=1&tk=57395EF2EECB102CAABD00B0D0E1CF3B
+
+ 100
+ Unlimited
+
+
+ Ten Forward
+ Deck 10, Forward
+ Enterprise
+ C0
+ 80129
+ -117.322031
+ 33.229150
+ Deck 10, Forward
+ Enterprise, CO 80129
+
+
+ Weekdays
+ Morning
+ true
+ Members Interact
+ Invitation or Request Required
+ false
+
+ true
+ true
+ false
+ Jean Luc Picard
+ Jean Luc Picard
+ 2016-07-17 04:02:53
+ 2017-05-30 16:36:38
+
+
+ Test Group Five UNLISTED
+ This has all properties but different than Test Group Three and Four
+ https://i.imgur.com/MZ9itik.png
+ Campus Three
+
+ Worf
+ Rozhenko
+ Worf Rozhenko
+ worf@enterprise.com
+
+ 123-456-7890
+
+
+
+ Beverly
+ Crusher
+ Beverly Crusher
+ beverly@enterprise.com
+
+ 123-123-1234
+
+
+
+ Deanna
+ Troi
+ Deanna Troi
+ deanna@enterprise.com
+
+ 246-810-1214
+
+
+ Group Type Two
+ Department Two
+ Area Two
+ webcal://starfleet.ccbchurch.com/group_calendar.ics?id=1&tk=57395EF2EECB102CAABD00B0D0E1CF3B
+
+ 10
+ Unlimited
+
+
+ Main Bridge
+ Deck 1, Forward
+ Enterprise
+ C0
+ 80129
+ -117.322031
+ 33.229150
+ Deck 1, Forward
+ Enterprise, CO 80129
+
+
+ Sundays
+ Nights
+ false
+ Members Interact
+ Invitation or Request Required
+ true
+
+ true
+ false
+ false
+ Jean Luc Picard
+ Jean Luc Picard
+ 2016-08-17 04:02:53
+ 2017-09-15 16:36:38
+
+
+ Test Group Six UNLISTED
+ Similar to Test Group Five but missing some properties
+ https://i.imgur.com/MZ9itik.png
+ Campus Three
+
+ Worf
+ Rozhenko
+ Worf Rozhenko
+ worf@enterprise.com
+
+ 123-456-7890
+
+
+
+
+ Group Type Two
+
+
+
+
+ 10
+ Unlimited
+
+
+ Main Bridge
+ Deck 1, Forward
+ Enterprise
+ C0
+ 80129
+ -117.322031
+ 33.229150
+ Deck 1, Forward
+ Enterprise, CO 80129
+
+
+ Mondays
+
+ true
+ Members Interact
+ Invitation or Request Required
+ true
+
+ true
+ false
+ false
+ Jean Luc Picard
+ Jean Luc Picard
+ 2016-08-17 04:02:53
+ 2017-08-30 16:36:38
+
+
+
+
diff --git a/tests/xml/group_profiles_some_updated_sample.xml b/tests/xml/group_profiles_some_updated_sample.xml
new file mode 100644
index 0000000..76271a7
--- /dev/null
+++ b/tests/xml/group_profiles_some_updated_sample.xml
@@ -0,0 +1,405 @@
+
+
+
+
+
+
+
+
+
+
+ group_profiles
+ execute
+ public
+
+
+ Test Group One UPDATED
+ This is a public_search_listed false example and should not be imported
+
+ Campus One
+
+ Jean Luc
+ Picard
+ Jean Luc Picard
+ jeanluc@enterprise.com
+
+
+
+
+
+ William
+ Riker
+ William Riker
+
+
+
+
+
+
+ Data
+
+ Data
+
+
+
+
+
+ Group Type One
+
+ Area One
+ webcal://starfleet.ccbchurch.com/group_calendar.ics?id=1&tk=57395EF2EECB102CAABD00B0D0E1CF3B
+
+ 3300
+ Unlimited
+
+
+
+ Meeting Address
+Street
+ City
+
+
+ -112.076814
+ 33.447549
+ Meeting Address
+Street
+ City
+
+
+
+
+ false
+ Announcement Only
+ Open to All
+ true
+
+ true
+ false
+ false
+ Jean Luc Picard
+ Jean Luc Picard
+ 2016-06-17 04:02:53
+ 2017-05-15 16:36:38
+
+
+ Test Group Two
+ This is an inactive example and should not be imported
+
+ Campus One
+
+ Jean Luc
+ Picard
+ Jean Luc Picard
+ jeanluc@enterprise.com
+
+
+
+
+
+ William
+ Riker
+ William Riker
+
+
+
+
+
+
+ Data
+
+ Data
+
+
+
+
+
+ Group Type One
+
+ Area One
+ webcal://starfleet.ccbchurch.com/group_calendar.ics?id=1&tk=57395EF2EECB102CAABD00B0D0E1CF3B
+
+ 3300
+ Unlimited
+
+
+ Ten Forward
+ Deck 10, Forward
+ Enterprise
+ C0
+ 80129
+ -117.322031
+ 33.229150
+ Deck 10, Forward
+ Enterprise, CO 80129
+
+
+
+
+ false
+ Announcement Only
+ Invitation or Request Required
+ false
+
+ true
+ true
+ true
+ Jean Luc Picard
+ Jean Luc Picard
+ 2016-07-17 04:02:53
+ 2017-05-30 16:36:38
+
+
+ Test Group Three UPDATED
+ This is a group with all properties set
+ https://i.imgur.com/penLEu9.jpg
+ Campus Two
+
+ Geordi
+ La Forge
+ Geordi La Forge
+ geordi@enterprise.com
+
+
+
+
+
+ William
+ Riker
+ William Riker
+
+
+
+
+
+
+ Data
+
+ Data
+
+
+
+
+
+ Group Type One
+ Department One
+ Area One
+ webcal://starfleet.ccbchurch.com/group_calendar.ics?id=1&tk=57395EF2EECB102CAABD00B0D0E1CF3B
+
+ 100
+ Unlimited
+
+
+ Ten Forward
+ Deck 10, Forward
+ Enterprise
+ C0
+ 80129
+ -117.322031
+ 33.229150
+ Deck 10, Forward
+ Enterprise, CO 80129
+
+
+ Weekdays
+ Morning
+ true
+ Members Interact
+ Invitation or Request Required
+ false
+
+ true
+ true
+ false
+ Jean Luc Picard
+ Jean Luc Picard
+ 2016-07-17 04:02:53
+ 2017-06-15 16:36:38
+
+
+ Test Group Four UPDATED
+ This is the same as Test Group Three but different name and ID
+ https://i.imgur.com/penLEu9.jpg
+ Campus Two
+
+ Geordi
+ La Forge
+ Geordi La Forge
+ geordi@enterprise.com
+
+
+
+
+
+ William
+ Riker
+ William Riker
+
+
+
+
+
+
+ Data
+
+ Data
+
+
+
+
+
+ Group Type One
+ Department One
+ Area One
+ webcal://starfleet.ccbchurch.com/group_calendar.ics?id=1&tk=57395EF2EECB102CAABD00B0D0E1CF3B
+
+ 100
+ Unlimited
+
+
+ Ten Forward
+ Deck 10, Forward
+ Enterprise
+ C0
+ 80129
+ -117.322031
+ 33.229150
+ Deck 10, Forward
+ Enterprise, CO 80129
+
+
+ Weekdays
+ Morning
+ true
+ Members Interact
+ Invitation or Request Required
+ false
+
+ true
+ true
+ false
+ Jean Luc Picard
+ Jean Luc Picard
+ 2016-07-17 04:02:53
+ 2017-06-15 16:36:38
+
+
+ Test Group Five
+ This has all properties but different than Test Group Three and Four
+ https://i.imgur.com/MZ9itik.png
+ Campus Three
+
+ Worf
+ Rozhenko
+ Worf Rozhenko
+ worf@enterprise.com
+
+ 123-456-7890
+
+
+
+ Beverly
+ Crusher
+ Beverly Crusher
+ beverly@enterprise.com
+
+ 123-123-1234
+
+
+
+ Deanna
+ Troi
+ Deanna Troi
+ deanna@enterprise.com
+
+ 246-810-1214
+
+
+ Group Type Two
+ Department Two
+ Area Two
+ webcal://starfleet.ccbchurch.com/group_calendar.ics?id=1&tk=57395EF2EECB102CAABD00B0D0E1CF3B
+
+ 10
+ Unlimited
+
+
+ Main Bridge
+ Deck 1, Forward
+ Enterprise
+ C0
+ 80129
+ -117.322031
+ 33.229150
+ Deck 1, Forward
+ Enterprise, CO 80129
+
+
+ Sundays
+ Nights
+ false
+ Members Interact
+ Invitation or Request Required
+ true
+
+ true
+ true
+ false
+ Jean Luc Picard
+ Jean Luc Picard
+ 2016-08-17 04:02:53
+ 2017-08-30 16:36:38
+
+
+ Test Group Six
+ Similar to Test Group Five but missing some properties
+ https://i.imgur.com/MZ9itik.png
+ Campus Three
+
+ Worf
+ Rozhenko
+ Worf Rozhenko
+ worf@enterprise.com
+
+ 123-456-7890
+
+
+
+
+ Group Type Two
+
+
+
+
+ 10
+ Unlimited
+
+
+ Main Bridge
+ Deck 1, Forward
+ Enterprise
+ C0
+ 80129
+ -117.322031
+ 33.229150
+ Deck 1, Forward
+ Enterprise, CO 80129
+
+
+ Mondays
+
+ true
+ Members Interact
+ Invitation or Request Required
+ true
+
+ true
+ true
+ false
+ Jean Luc Picard
+ Jean Luc Picard
+ 2016-08-17 04:02:53
+ 2017-08-30 16:36:38
+
+
+
+
diff --git a/tests/xml/public_calendar_listing_sample.xml b/tests/xml/public_calendar_listing_sample.xml
new file mode 100644
index 0000000..88b4123
--- /dev/null
+++ b/tests/xml/public_calendar_listing_sample.xml
@@ -0,0 +1,175 @@
+
+
+
+
+
+
+
+
+
+
+
+ -
+ 2018-01-03
+ Event One
+ Event Description One
+ 08:00:00
+ 08:15:00
+ 15
+ Open To All
+ Room One
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+ -
+ 2018-01-03
+ Event Two
+ Event Description Two
+ 09:00:00
+ 10:00:00
+ 60
+ Open To All
+
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+ -
+ 2018-01-03
+ Event Three
+
+ 15:00:00
+ 17:45:00
+ 160
+ Open To All
+ Room Three
+ Group Two
+ Interest
+ Department Two
+ William Riker
+ 123-123-1234
+ william@enterprise.com
+
+ -
+ 2018-01-03
+ Event Four
+ Event Description Four
+ 19:00:00
+ 21:00:00
+ 120
+ Open To All
+ Room Four
+ Group Three
+ Singles
+ Singles Department
+ Data
+
+
+
+ -
+ 2018-01-04
+ Event One
+ Event Description One
+ 08:00:00
+ 08:15:00
+ 15
+ Open To All
+ Room One
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+ -
+ 2018-01-04
+ Event Two
+ Event Description Two
+ 09:00:00
+ 10:00:00
+ 60
+ Open To All
+
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+ -
+ 2018-01-04
+ Event Five
+ Weekly Event Five
+ 19:00:00
+ 21:00:00
+ 120
+ Open To All
+ Main Bridge
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+ -
+ 2018-01-04
+ Event Four
+ Event Description Four
+ 19:00:00
+ 21:00:00
+ 120
+ Open To All
+ Room Four
+ Group Three
+ Singles
+ Singles Department
+ Data
+
+
+
+ -
+ 2018-01-05
+ Event One
+ Event Description One
+ 08:00:00
+ 08:15:00
+ 15
+ Open To All
+ Room One
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+ -
+ 2018-01-05
+ Event Two
+ Event Description Two
+ 09:00:00
+ 10:00:00
+ 60
+ Open To All
+
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+
+
+
+
diff --git a/tests/xml/public_calendar_listing_some_deleted_sample.xml b/tests/xml/public_calendar_listing_some_deleted_sample.xml
new file mode 100644
index 0000000..9c8737b
--- /dev/null
+++ b/tests/xml/public_calendar_listing_some_deleted_sample.xml
@@ -0,0 +1,127 @@
+
+
+
+
+
+
+
+
+
+
+
+ -
+ 2018-01-03
+ Event One
+ Event Description One
+ 08:00:00
+ 08:15:00
+ 15
+ Open To All
+ Room One
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+ -
+ 2018-01-03
+ Event Three
+
+ 15:00:00
+ 17:45:00
+ 160
+ Open To All
+ Room Three
+ Group Two
+ Interest
+ Department Two
+ William Riker
+ 123-123-1234
+ william@enterprise.com
+
+ -
+ 2018-01-03
+ Event Four
+ Event Description Four
+ 19:00:00
+ 21:00:00
+ 120
+ Open To All
+ Room Four
+ Group Three
+ Singles
+ Singles Department
+ Data
+
+
+
+ -
+ 2018-01-04
+ Event One
+ Event Description One
+ 08:00:00
+ 08:15:00
+ 15
+ Open To All
+ Room One
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+ -
+ 2018-01-04
+ Event Five
+ Weekly Event Five
+ 19:00:00
+ 21:00:00
+ 120
+ Open To All
+ Main Bridge
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+ -
+ 2018-01-04
+ Event Four
+ Event Description Four
+ 19:00:00
+ 21:00:00
+ 120
+ Open To All
+ Room Four
+ Group Three
+ Singles
+ Singles Department
+ Data
+
+
+
+ -
+ 2018-01-05
+ Event One
+ Event Description One
+ 08:00:00
+ 08:15:00
+ 15
+ Open To All
+ Room One
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+
+
+
+
diff --git a/tests/xml/public_calendar_listing_some_new_sample.xml b/tests/xml/public_calendar_listing_some_new_sample.xml
new file mode 100644
index 0000000..78e3fe8
--- /dev/null
+++ b/tests/xml/public_calendar_listing_some_new_sample.xml
@@ -0,0 +1,223 @@
+
+
+
+
+
+
+
+
+
+
+
+ -
+ 2018-01-03
+ Event One
+ Event Description One
+ 08:00:00
+ 08:15:00
+ 15
+ Open To All
+ Room One
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+ -
+ 2018-01-03
+ Event Two
+ Event Description Two
+ 09:00:00
+ 10:00:00
+ 60
+ Open To All
+
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+ -
+ 2018-01-03
+ Event NEW
+ New Event
+ 10:00:00
+ 11:00:00
+ 60
+ Open To All
+
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+ -
+ 2018-01-03
+ Event Three
+
+ 15:00:00
+ 17:45:00
+ 160
+ Open To All
+ Room Three
+ Group Two
+ Interest
+ Department Two
+ William Riker
+ 123-123-1234
+ william@enterprise.com
+
+ -
+ 2018-01-03
+ Event Four
+ Event Description Four
+ 19:00:00
+ 21:00:00
+ 120
+ Open To All
+ Room Four
+ Group Three
+ Singles
+ Singles Department
+ Data
+
+
+
+ -
+ 2018-01-04
+ Event One
+ Event Description One
+ 08:00:00
+ 08:15:00
+ 15
+ Open To All
+ Room One
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+ -
+ 2018-01-04
+ Event Two
+ Event Description Two
+ 09:00:00
+ 10:00:00
+ 60
+ Open To All
+
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+ -
+ 2018-01-04
+ Event NEW
+ New Event
+ 10:00:00
+ 11:00:00
+ 60
+ Open To All
+
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+ -
+ 2018-01-04
+ Event Five
+ Weekly Event Five
+ 19:00:00
+ 21:00:00
+ 120
+ Open To All
+ Main Bridge
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+ -
+ 2018-01-04
+ Event Four
+ Event Description Four
+ 19:00:00
+ 21:00:00
+ 120
+ Open To All
+ Room Four
+ Group Three
+ Singles
+ Singles Department
+ Data
+
+
+
+ -
+ 2018-01-05
+ Event One
+ Event Description One
+ 08:00:00
+ 08:15:00
+ 15
+ Open To All
+ Room One
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+ -
+ 2018-01-05
+ Event Two
+ Event Description Two
+ 09:00:00
+ 10:00:00
+ 60
+ Open To All
+
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+ -
+ 2018-01-05
+ Event NEW
+ New Event
+ 10:00:00
+ 11:00:00
+ 60
+ Open To All
+
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+
+
+
+
diff --git a/tests/xml/public_calendar_listing_some_updated_sample.xml b/tests/xml/public_calendar_listing_some_updated_sample.xml
new file mode 100644
index 0000000..d9f740e
--- /dev/null
+++ b/tests/xml/public_calendar_listing_some_updated_sample.xml
@@ -0,0 +1,175 @@
+
+
+
+
+
+
+
+
+
+
+
+ -
+ 2018-01-03
+ Event One UPDATED
+ Event Description One UPDATED
+ 08:00:00
+ 08:15:00
+ 15
+ Open To All
+ Room One
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+ -
+ 2018-01-03
+ Event Two
+ Event Description Two
+ 09:00:00
+ 10:00:00
+ 60
+ Open To All
+
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+ -
+ 2018-01-03
+ Event Three
+
+ 15:00:00
+ 17:45:00
+ 160
+ Open To All
+ Room Three
+ Group Two
+ Interest
+ Department Two
+ William Riker
+ 123-123-1234
+ william@enterprise.com
+
+ -
+ 2018-01-03
+ Event Four
+ Event Description Four
+ 19:00:00
+ 21:00:00
+ 120
+ Open To All
+ Room Four
+ Group Three
+ Singles
+ Singles Department
+ Data
+
+
+
+ -
+ 2018-01-04
+ Event One UPDATED
+ Event Description One UPDATED
+ 08:00:00
+ 08:15:00
+ 15
+ Open To All
+ Room One
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+ -
+ 2018-01-04
+ Event Two
+ Event Description Two
+ 09:00:00
+ 10:00:00
+ 60
+ Open To All
+
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+ -
+ 2018-01-04
+ Event Five
+ Weekly Event Five
+ 19:00:00
+ 21:00:00
+ 120
+ Open To All
+ Main Bridge
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+ -
+ 2018-01-04
+ Event Four
+ Event Description Four
+ 19:00:00
+ 21:00:00
+ 120
+ Open To All
+ Room Four
+ Group Three
+ Singles
+ Singles Department
+ Data
+
+
+
+ -
+ 2018-01-05
+ Event One UPDATED
+ Event Description One UPDATED
+ 08:00:00
+ 08:15:00
+ 15
+ Open To All
+ Room One
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+ -
+ 2018-01-05
+ Event Two
+ Event Description Two
+ 09:00:00
+ 10:00:00
+ 60
+ Open To All
+
+ Group One
+ Interest
+ Department One
+ Jean Luc Picard
+ 123-456-7890
+ jeanluc@enterprise.com
+
+
+
+
+
From ce44ea29922d2f4b3db91ab1e07db9988c2eba02 Mon Sep 17 00:00:00 2001
From: Jared Cobb
Date: Wed, 3 Jan 2018 12:26:45 -0700
Subject: [PATCH 08/16] Set min WP version to 4.6, remove abstract method (not
needed)
---
.travis.yml | 2 +-
includes/taxonomies/class-ccb-core-taxonomy.php | 5 -----
2 files changed, 1 insertion(+), 6 deletions(-)
diff --git a/.travis.yml b/.travis.yml
index bce2225..100725f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -24,7 +24,7 @@ matrix:
- php: 7.0
env: WP_VERSION=latest
- php: 5.6
- env: WP_VERSION=4.4
+ env: WP_VERSION=4.6
- php: 5.6
env: WP_VERSION=latest
- php: 5.6
diff --git a/includes/taxonomies/class-ccb-core-taxonomy.php b/includes/taxonomies/class-ccb-core-taxonomy.php
index 8c0b388..1709f7c 100644
--- a/includes/taxonomies/class-ccb-core-taxonomy.php
+++ b/includes/taxonomies/class-ccb-core-taxonomy.php
@@ -67,9 +67,4 @@ public function get_taxonomy_map( $map ) {
return $map;
}
- /**
- * Register the taxonomy.
- */
- abstract public static function get_taxonomy_args();
-
}
From a9e991b12df5f5f5395cbc1114b930f6eb582d95 Mon Sep 17 00:00:00 2001
From: Jared Cobb
Date: Wed, 3 Jan 2018 14:15:51 -0700
Subject: [PATCH 09/16] Use assertEqualSetsWithIndex for multidimensional
arrays
---
tests/test-synchronizer-calendar.php | 8 ++++----
tests/test-synchronizer-groups.php | 8 ++++----
tests/xml/group_profiles_some_inactivated_sample.xml | 4 ++--
3 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/tests/test-synchronizer-calendar.php b/tests/test-synchronizer-calendar.php
index f9d7b42..ec79b95 100644
--- a/tests/test-synchronizer-calendar.php
+++ b/tests/test-synchronizer-calendar.php
@@ -43,7 +43,7 @@ public function test_update_content_insert_events() {
],
];
- $this->assertEqualSets( $expected_result, $result );
+ $this->assertEqualSetsWithIndex( $expected_result, $result );
}
/**
@@ -80,7 +80,7 @@ public function test_update_content_update_events() {
],
];
- $this->assertEqualSets( $expected_result, $result );
+ $this->assertEqualSetsWithIndex( $expected_result, $result );
}
/**
@@ -114,7 +114,7 @@ public function test_update_content_deleted_events() {
],
];
- $this->assertEqualSets( $expected_result, $result );
+ $this->assertEqualSetsWithIndex( $expected_result, $result );
}
/**
@@ -148,7 +148,7 @@ public function test_update_content_new_events() {
],
];
- $this->assertEqualSets( $expected_result, $result );
+ $this->assertEqualSetsWithIndex( $expected_result, $result );
}
public function tearDown() {
diff --git a/tests/test-synchronizer-groups.php b/tests/test-synchronizer-groups.php
index e8af5e8..73d8e3c 100644
--- a/tests/test-synchronizer-groups.php
+++ b/tests/test-synchronizer-groups.php
@@ -43,7 +43,7 @@ public function test_update_content_insert_groups() {
],
];
- $this->assertEqualSets( $expected_result, $result );
+ $this->assertEqualSetsWithIndex( $expected_result, $result );
}
/**
@@ -77,7 +77,7 @@ public function test_update_content_update_groups() {
],
];
- $this->assertEqualSets( $expected_result, $result );
+ $this->assertEqualSetsWithIndex( $expected_result, $result );
}
@@ -112,7 +112,7 @@ public function test_update_content_unlisted_groups() {
],
];
- $this->assertEqualSets( $expected_result, $result );
+ $this->assertEqualSetsWithIndex( $expected_result, $result );
}
@@ -147,7 +147,7 @@ public function test_update_content_inactivated_groups() {
],
];
- $this->assertEqualSets( $expected_result, $result );
+ $this->assertEqualSetsWithIndex( $expected_result, $result );
}
diff --git a/tests/xml/group_profiles_some_inactivated_sample.xml b/tests/xml/group_profiles_some_inactivated_sample.xml
index 5bb4de8..750615f 100644
--- a/tests/xml/group_profiles_some_inactivated_sample.xml
+++ b/tests/xml/group_profiles_some_inactivated_sample.xml
@@ -343,7 +343,7 @@ Street
true
true
- false
+ true
Jean Luc Picard
Jean Luc Picard
2016-08-17 04:02:53
@@ -394,7 +394,7 @@ Street
true
true
- false
+ true
Jean Luc Picard
Jean Luc Picard
2016-08-17 04:02:53
From c6f7366e95f4029d5c72af3872c25c541fa91b6b Mon Sep 17 00:00:00 2001
From: Jared Cobb
Date: Fri, 5 Jan 2018 18:24:12 -0700
Subject: [PATCH 10/16] Fixed empty settings bug and flush rewrite rules bug
---
README.txt | 20 ++++++++------------
includes/class-ccb-core-settings.php | 15 ++++++++++++++-
includes/class-ccb-core.php | 20 ++++++++++++++++++--
3 files changed, 40 insertions(+), 15 deletions(-)
diff --git a/README.txt b/README.txt
index bf04ee2..9c2089b 100644
--- a/README.txt
+++ b/README.txt
@@ -1,7 +1,7 @@
=== Church Community Builder Core API ===
Contributors: jaredcobb
Tags: ccb, church, api, chms
-Requires at least: 4.4.0
+Requires at least: 4.6.0
Tested up to: 4.9.1
Stable tag: 1.0.0
License: GPLv2 or later
@@ -12,15 +12,14 @@ Provides a core integration to the Church Community Builder API.
== Description ==
Church Community Builder Core API *synchronizes* your church data to WordPress [custom post types](https://codex.wordpress.org/Custom_Post_Types).
+
This plugin is geared toward developers (or advanced WordPress users who aren't afraid to get into a little bit of code).
Find out more at [https://www.wpccb.com](https://www.wpccb.com).
= Why Use This Plugin? =
-One of the biggest challenges with getting your Church Community Builder data onto your site is the actual API integration.
-This plugin does all of the heavy lifting for you. Once your church data is securely synchronized you can use it freely in
-your theme, widgets, or even your own plugins!
+One of the biggest challenges with getting your Church Community Builder data onto your site is the actual API integration. This plugin does all of the heavy lifting for you. Once your church data is securely synchronized you can use it freely in your theme, widgets, or even your own plugins!
= Features =
@@ -34,7 +33,7 @@ your theme, widgets, or even your own plugins!
= Documentation =
-The [official documentation](https://www.wpccb.com/documentation) has more information, including code samples, hooks, filters, and links to tutorials.
+Extensive developer documentation is available on the [GitHub wiki](https://github.com/jaredcobb/ccb-core/wiki).
== Installation ==
@@ -49,15 +48,11 @@ The [official documentation](https://www.wpccb.com/documentation) has more infor
= I installed this plugin and my site doesn't look any different =
-This plugin has a very specific task: It gets some of your Church Community Builder data and imports it into your
-WordPress database (as custom post types). A developer (or advanced WordPress administrator) will need to
-alter your theme to *take advantage* of this data.
+This plugin has a very specific task: It gets some of your Church Community Builder data and imports it into your WordPress database (as custom post types). A developer (or advanced WordPress administrator) will need to alter your theme to *take advantage* of this data.
= Some of my groups in Church Community Builder aren't being synchronized =
-You'll need to ensure your [group settings](https://support.churchcommunitybuilder.com/customer/portal/articles/361764-editing-groups)
-allow the group to be publicly listed. A great way to cross reference if your group is publicly visible is to visit
-*yoursubdomain*.ccbchurch.com/w_group_list.php and see if the missing group shows up there.
+You'll need to ensure your [group settings](https://churchcommunitybuilder.force.com/s/article/2102903) allow the group to be publicly listed. A great way to cross reference if your group is publicly visible is to visit *yoursubdomain*.ccbchurch.com/w_group_list.php and see if the missing group shows up there.
== Screenshots ==
@@ -68,8 +63,9 @@ allow the group to be publicly listed. A great way to cross reference if your gr
= 1.0.0 =
* Official stable release
+* *Breaking Changes* - Please note that post type and custom taxonomy names have changed (see [release notes](https://github.com/jaredcobb/ccb-core/wiki/1.0.0-Stable-Release) )
* Fixed broken group images (CCB API query parameter `include_image_link=true`)
-* Refactored code to be faster, simpler, prettier, tastier
+* Refactored code to be faster, simpler, and easier to extend
= 0.9.6 =
* Added automatic flushing of rewrite rules when custom post type settings are changed
diff --git a/includes/class-ccb-core-settings.php b/includes/class-ccb-core-settings.php
index b98bd3b..01acbfb 100644
--- a/includes/class-ccb-core-settings.php
+++ b/includes/class-ccb-core-settings.php
@@ -29,6 +29,10 @@ class CCB_Core_Settings {
public function validate_settings( $input ) {
$current_options = CCB_Core_Helpers::instance()->get_options();
+ if ( empty( $current_options ) ) {
+ $current_options = [];
+ }
+
$validation_hash = $this->generate_validation_hash();
foreach ( $validation_hash as $field_id => $validation ) {
@@ -96,7 +100,16 @@ public function validate_settings( $input ) {
case 'encrypt':
if ( ! empty( $input[ $field_id ]['password'] ) ) {
- $encrypted_password = CCB_Core_Helpers::instance()->encrypt( $input[ $field_id ]['password'] );
+ // For a brand new installation, if the option doesn't yet
+ // exist, sanitize callback is called twice.
+ // See https://core.trac.wordpress.org/ticket/21989.
+ if ( 200 < strlen( $input[ $field_id ]['password'] ) && ! isset( $current_options[ $field_id ]['password'] ) ) {
+ // Password was already encrypted on the previous sanitization call.
+ $encrypted_password = $input[ $field_id ]['password'];
+ } else {
+ $encrypted_password = CCB_Core_Helpers::instance()->encrypt( $input[ $field_id ]['password'] );
+ }
+
if ( $encrypted_password ) {
$current_options[ $field_id ]['password'] = $encrypted_password;
} else {
diff --git a/includes/class-ccb-core.php b/includes/class-ccb-core.php
index 5aa3f79..cbcb0ad 100644
--- a/includes/class-ccb-core.php
+++ b/includes/class-ccb-core.php
@@ -112,6 +112,9 @@ private function define_hooks() {
// Callback for after the options are saved.
add_action( 'update_option_ccb_core_settings', [ $this, 'updated_options' ], 10, 2 );
+ // Determine if the rewrite rules need to be flushed.
+ add_action( 'init', [ $this, 'check_rewrite_rules' ] );
+
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_styles' ] );
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] );
@@ -259,8 +262,8 @@ public function updated_options( $old_value, $value ) {
foreach ( $setting_array as $setting ) {
if ( isset( $value[ $setting ] ) ) {
if ( ! isset( $old_value[ $setting ] ) || $value[ $setting ] !== $old_value[ $setting ] ) {
- // At least one option requires a flush, so do it once and return.
- flush_rewrite_rules();
+ // At least one option requires a flush, so set the transient and return.
+ set_transient( 'ccb_core_flush_rewrite_rules', true );
return;
}
}
@@ -268,6 +271,19 @@ public function updated_options( $old_value, $value ) {
}
+ /**
+ * Checks for a flag that may have been previously
+ * set in order to flush the rewrite rules.
+ *
+ * @return void
+ */
+ public function check_rewrite_rules() {
+ if ( get_transient( 'ccb_core_flush_rewrite_rules' ) ) {
+ delete_transient( 'ccb_core_flush_rewrite_rules' );
+ flush_rewrite_rules();
+ }
+ }
+
/**
* Register the stylesheets for the dashboard.
*
From effc8e8cb006343a2ea4c29fe7cc11de438e18d6 Mon Sep 17 00:00:00 2001
From: Jared Cobb
Date: Sat, 6 Jan 2018 09:42:13 -0700
Subject: [PATCH 11/16] Fixed undefined index errors, ensure options stay up to
date
---
includes/class-ccb-core-cron.php | 10 +++++++---
includes/class-ccb-core-helpers.php | 17 +++++++++++++++++
includes/class-ccb-core-settings-field.php | 16 ++++++++++------
3 files changed, 34 insertions(+), 9 deletions(-)
diff --git a/includes/class-ccb-core-cron.php b/includes/class-ccb-core-cron.php
index a8a2c39..4d254fc 100644
--- a/includes/class-ccb-core-cron.php
+++ b/includes/class-ccb-core-cron.php
@@ -77,12 +77,16 @@ public function custom_cron_schedule( $schedules ) {
public function cron_settings_changed( $old_value, $new_value ) {
// If the cron was enabled OR the timeout was changed.
if (
- ( '' === $old_value['auto_sync'] && '1' === $new_value['auto_sync'] )
- || ( $old_value['auto_sync_timeout'] !== $new_value['auto_sync_timeout'] )
+ ( empty( $old_value['auto_sync'] ) && ! empty( $new_value['auto_sync'] ) )
+ || (
+ ! empty( $old_value['auto_sync_timeout'] )
+ && ! empty( $new_value['auto_sync_timeout'] )
+ && $old_value['auto_sync_timeout'] !== $new_value['auto_sync_timeout']
+ )
) {
$this->remove_existing_cron_events();
wp_schedule_event( time(), 'ccb_core_schedule', 'ccb_core_auto_sync_hook' );
- } elseif ( '1' === $old_value['auto_sync'] && '' === $new_value['auto_sync'] ) {
+ } elseif ( ! empty( $old_value['auto_sync'] ) && empty( $new_value['auto_sync'] ) ) {
$this->remove_existing_cron_events();
}
}
diff --git a/includes/class-ccb-core-helpers.php b/includes/class-ccb-core-helpers.php
index 59a3d10..8669b87 100644
--- a/includes/class-ccb-core-helpers.php
+++ b/includes/class-ccb-core-helpers.php
@@ -72,6 +72,9 @@ public static function instance() {
private function setup() {
// Get any options the user may have set.
$this->plugin_options = get_option( 'ccb_core_settings' );
+ // Ensure we refresh this singleton's options whenever the options
+ // get updated (so that other callbacks have accurate values).
+ add_action( 'update_option_ccb_core_settings', [ $this, 'refresh_options' ], 5, 2 );
}
/**
@@ -83,6 +86,20 @@ public function get_options() {
return $this->plugin_options;
}
+ /**
+ * Callback method to detect when the settings have changed.
+ *
+ * Ensures that this singleton's `get_options()` method always
+ * returns accurate settings based on the latest changes.
+ *
+ * @param array $old_value The old settings array.
+ * @param array $new_value The new settings array.
+ * @return void
+ */
+ public function refresh_options( $old_value, $new_value ) {
+ $this->plugin_options = $new_value;
+ }
+
/**
* Encrypts and base64_encodes a string safe for serialization in WordPress
*
diff --git a/includes/class-ccb-core-settings-field.php b/includes/class-ccb-core-settings-field.php
index 9a5f3db..ecb3cd8 100644
--- a/includes/class-ccb-core-settings-field.php
+++ b/includes/class-ccb-core-settings-field.php
@@ -292,12 +292,16 @@ protected function render_test_credentials() {
* @return void
*/
protected function render_manual_sync() {
- echo sprintf(
- '
-
-
',
- esc_attr__( 'Synchronize', 'ccb-core' )
- );
+ if ( CCB_Core_API::instance()->initialized ) {
+ echo sprintf(
+ '
+
+
',
+ esc_attr__( 'Synchronize', 'ccb-core' )
+ );
+ } else {
+ echo '' . esc_html__( 'Please enter your credentials under the API Settings page', 'ccb-core' ) . '
';
+ }
}
/**
From 97c870b458564d6230ed32b8e83fe0cec05c7e6c Mon Sep 17 00:00:00 2001
From: Jared Cobb
Date: Sat, 6 Jan 2018 16:43:53 -0700
Subject: [PATCH 12/16] Update argument name, add some class examples
---
.../post-types/class-ccb-core-calendar.php | 8 +-
includes/post-types/class-ccb-core-cpt.php | 4 +-
includes/post-types/class-ccb-core-group.php | 10 +-
includes/post-types/class-example.php | 146 ++++++++++++++++++
includes/taxonomies/class-example.php | 69 +++++++++
5 files changed, 226 insertions(+), 11 deletions(-)
create mode 100644 includes/post-types/class-example.php
create mode 100644 includes/taxonomies/class-example.php
diff --git a/includes/post-types/class-ccb-core-calendar.php b/includes/post-types/class-ccb-core-calendar.php
index 3473dd0..21a23de 100644
--- a/includes/post-types/class-ccb-core-calendar.php
+++ b/includes/post-types/class-ccb-core-calendar.php
@@ -269,14 +269,14 @@ public function get_post_settings_definitions( $settings ) {
* Define the mapping of CCB API fields to the Post fields
*
* @since 1.0.0
- * @param array $map A collection of mappings from the API to WordPress.
+ * @param array $maps A collection of mappings from the API to WordPress.
* @return array
*/
- public function get_post_api_map( $map ) {
+ public function get_post_api_map( $maps ) {
if ( $this->enabled ) {
$calendar_options = $this->get_calendar_options();
- $map[ $this->name ] = [
+ $maps[ $this->name ] = [
'service' => 'public_calendar_listing',
'data' => [
'date_start' => $calendar_options['date_start'],
@@ -294,7 +294,7 @@ public function get_post_api_map( $map ) {
],
];
}
- return $map;
+ return $maps;
}
/**
diff --git a/includes/post-types/class-ccb-core-cpt.php b/includes/post-types/class-ccb-core-cpt.php
index 3b7aa63..cd9c447 100644
--- a/includes/post-types/class-ccb-core-cpt.php
+++ b/includes/post-types/class-ccb-core-cpt.php
@@ -88,9 +88,9 @@ abstract public function get_post_settings_definitions( $settings );
* Define the mapping of CCB API fields to the Post fields
*
* @since 1.0.0
- * @param array $map A collection of mappings from the API to WordPress.
+ * @param array $maps A collection of mappings from the API to WordPress.
* @return array
*/
- abstract public function get_post_api_map( $map );
+ abstract public function get_post_api_map( $maps );
}
diff --git a/includes/post-types/class-ccb-core-group.php b/includes/post-types/class-ccb-core-group.php
index 6166722..d0955fb 100644
--- a/includes/post-types/class-ccb-core-group.php
+++ b/includes/post-types/class-ccb-core-group.php
@@ -31,7 +31,7 @@ class CCB_Core_Group extends CCB_Core_CPT {
public function __construct() {
add_filter( 'ccb_core_synchronizer_entity_insert_allowed', [ $this, 'entity_insert_update_allowed' ], 10, 4 );
add_filter( 'ccb_core_synchronizer_entity_update_allowed', [ $this, 'entity_insert_update_allowed' ], 10, 4 );
- add_filter( 'ccb_core_after_insert_update_post', [ $this, 'attach_group_image' ], 10, 5 );
+ add_action( 'ccb_core_after_insert_update_post', [ $this, 'attach_group_image' ], 10, 5 );
$options = CCB_Core_Helpers::instance()->get_options();
$this->enabled = ! empty( $options['groups_enabled'] ) ? true : false;
@@ -215,15 +215,15 @@ public function get_post_settings_definitions( $settings ) {
* Define the mapping of CCB API fields to the Post fields
*
* @since 1.0.0
- * @param array $map A collection of mappings from the API to WordPress.
+ * @param array $maps A collection of mappings from the API to WordPress.
* @return array
*/
- public function get_post_api_map( $map ) {
+ public function get_post_api_map( $maps ) {
if ( $this->enabled ) {
$options = CCB_Core_Helpers::instance()->get_options();
$include_image_link = ! empty( $options['groups_import_images'] ) && 'yes' === $options['groups_import_images'] ? true : false;
- $map[ $this->name ] = [
+ $maps[ $this->name ] = [
'service' => 'group_profiles',
'data' => [
'include_participants' => false,
@@ -241,7 +241,7 @@ public function get_post_api_map( $map ) {
],
];
}
- return $map;
+ return $maps;
}
/**
diff --git a/includes/post-types/class-example.php b/includes/post-types/class-example.php
new file mode 100644
index 0000000..474838f
--- /dev/null
+++ b/includes/post-types/class-example.php
@@ -0,0 +1,146 @@
+
+ */
+class Example extends CCB_Core_CPT {
+
+ /**
+ * Name of the post type
+ *
+ * @var string
+ */
+ public $name = 'ccb_core_example_post';
+
+ /**
+ * Use the constructor to add any actions / filters.
+ */
+ public function __construct() {
+
+ // There are several actions and filters that allow you to run additional code during
+ // the synchronization process. If you need to hook into them, define them here.
+ add_action( 'ccb_core_after_insert_update_post', [ $this, 'my_callback_after_post_inserted' ], 10, 5 );
+
+ parent::__construct();
+ }
+
+ /**
+ * Setup the custom post type args
+ *
+ * @since 1.0.0
+ * @return array $args for register_post_type
+ */
+ public function get_post_args() {
+
+ return [
+ 'labels' => [
+ 'name' => 'Example Posts',
+ 'singular_name' => 'Example Post',
+ 'all_items' => 'All Example Posts',
+ 'add_new' => 'Add New',
+ 'add_new_item' => 'Add New Example Post',
+ 'edit' => 'Edit',
+ 'edit_item' => 'Edit Example Post',
+ 'new_item' => 'New Example Post',
+ 'view_item' => 'View Example Post',
+ 'search_items' => 'Search Example Posts',
+ 'not_found' => 'Nothing found in the Database.',
+ 'not_found_in_trash' => 'Nothing found in Trash',
+ ],
+ 'description' => 'These are Example Posts that came from CCB',
+ 'public' => true,
+ 'publicly_queryable' => true,
+ 'exclude_from_search' => false,
+ 'show_ui' => true,
+ 'show_in_nav_menus' => false,
+ 'query_var' => true,
+ 'menu_position' => 8,
+ 'rewrite' => [ 'slug' => 'examples' ],
+ 'has_archive' => 'examples',
+ 'capability_type' => 'post',
+ 'hierarchical' => false,
+ 'supports' => [ 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'custom-fields', 'sticky' ],
+ ];
+
+ }
+
+ /**
+ * Configure the options that users are allowed to set
+ *
+ * @since 1.0.0
+ * @param array $settings The settings definitions.
+ * @return array
+ */
+ public function get_post_settings_definitions( $settings ) {
+ // This method is required, but you do not need to actually create
+ // a settings page or settings fields if you don't need them. Just
+ // return the settings in that case.
+ return $settings;
+ }
+
+ /**
+ * Define the mapping of CCB API fields to the Post fields
+ *
+ * @since 1.0.0
+ * @param array $maps A collection of mappings from the API to WordPress.
+ * @return array
+ */
+ public function get_post_api_map( $maps ) {
+
+ $maps[ $this->name ] = [
+ 'service' => 'ccb_service_name', // This becomes the `srv` URL parameter in the API request.
+ 'data' => [
+ 'another_parameter' => true, // These are any additional URL parameters that need to be sent with the API request.
+ 'yet_another_parameter' => 'abc123',
+ ],
+ 'nodes' => [ 'elements', 'element' ], // The path from all the way to (and including) the CCB Entity.
+ 'fields' => [
+ 'some_property' => 'post_title', // Map to a Post Title from an entity's property.
+ 'another_property' => 'post_content', // Map to any other WP_Post property by name.
+ 'a_third_property' => 'post_meta', // Map to `post_meta` to have this saved as post meta.
+ ],
+ ];
+
+ return $maps;
+ }
+
+ /**
+ * Run additional logic after a post gets inserted
+ *
+ * @since 1.0.0
+ *
+ * @param SimpleXML $entity The entity object.
+ * @param array $settings The settings array for the import.
+ * @param array $args The `wp_insert_post` args.
+ * @param string $post_type The current post type.
+ * @param int $post_id The WordPress post id of this post.
+ * @return void
+ */
+ public function my_callback_after_post_inserted( $entity, $settings, $args, $post_type, $post_id ) {
+ // If this is a callback for this post type...
+ // phpcs:ignore
+ if ( $this->name === $post_type ) {
+ // Perhaps you want to inspect the new post after it gets inserted. You now have access
+ // to the original Entity (XML object from CCB), the post id, etc. You can now alter the post
+ // with any custom logic. For example, on the CCB_Core_Group post type we check whether the
+ // post should also have a featured image (and we download the group image and attach it).
+ }
+ }
+}
+
+new Example();
diff --git a/includes/taxonomies/class-example.php b/includes/taxonomies/class-example.php
new file mode 100644
index 0000000..e5279fb
--- /dev/null
+++ b/includes/taxonomies/class-example.php
@@ -0,0 +1,69 @@
+
+ */
+class Example extends CCB_Core_Taxonomy {
+
+ /**
+ * Name of the taxonomy
+ *
+ * @var string
+ */
+ public $name = 'ccb_core_example_taxonomy';
+
+ /**
+ * Object types for this taxonomy
+ *
+ * This may be attached to multiple post types, if needed.
+ *
+ * @var array
+ */
+ public $object_types = [ 'ccb_core_example_post' ];
+
+ /**
+ * Setup the default taxonomy mappings
+ *
+ * @since 1.0.0
+ * @return array Default options for register_taxonomy
+ */
+ public static function get_taxonomy_args() {
+ return [
+ 'labels' => [
+ 'name' => 'Example Categories',
+ 'singular_name' => 'Example Category',
+ 'search_items' => 'Search Example Categories',
+ 'all_items' => 'All Example Categories',
+ 'parent_item' => 'Parent Example Category',
+ 'parent_item_colon' => 'Parent Example Category:',
+ 'edit_item' => 'Edit Example Category',
+ 'update_item' => 'Update Example Category',
+ 'add_new_item' => 'Add New Example Category',
+ 'new_item_name' => 'New Example Category',
+ ],
+ 'hierarchical' => true,
+ 'show_admin_column' => true,
+ 'show_ui' => true,
+ 'query_var' => true,
+ 'api_mapping' => 'entity_property_name', // The field key from the CCB API.
+ ];
+ }
+
+}
+
+new Example();
From d70873f2450a9f24faaaeb3ce37abf31419a58bd Mon Sep 17 00:00:00 2001
From: Jared Cobb
Date: Sat, 6 Jan 2018 18:00:31 -0700
Subject: [PATCH 13/16] Rename example class names
---
.../{class-example.php => class-example-post-type.php} | 4 ++--
.../{class-example.php => class-example-taxonomy.php} | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
rename includes/post-types/{class-example.php => class-example-post-type.php} (98%)
rename includes/taxonomies/{class-example.php => class-example-taxonomy.php} (95%)
diff --git a/includes/post-types/class-example.php b/includes/post-types/class-example-post-type.php
similarity index 98%
rename from includes/post-types/class-example.php
rename to includes/post-types/class-example-post-type.php
index 474838f..12e784e 100644
--- a/includes/post-types/class-example.php
+++ b/includes/post-types/class-example-post-type.php
@@ -18,7 +18,7 @@
* @subpackage CCB_Core/includes/post-types
* @author Jared Cobb
*/
-class Example extends CCB_Core_CPT {
+class Example_Post_Type extends CCB_Core_CPT {
/**
* Name of the post type
@@ -143,4 +143,4 @@ public function my_callback_after_post_inserted( $entity, $settings, $args, $pos
}
}
-new Example();
+new Example_Post_Type();
diff --git a/includes/taxonomies/class-example.php b/includes/taxonomies/class-example-taxonomy.php
similarity index 95%
rename from includes/taxonomies/class-example.php
rename to includes/taxonomies/class-example-taxonomy.php
index e5279fb..37aa367 100644
--- a/includes/taxonomies/class-example.php
+++ b/includes/taxonomies/class-example-taxonomy.php
@@ -18,7 +18,7 @@
* @subpackage CCB_Core/includes/taxonomies
* @author Jared Cobb
*/
-class Example extends CCB_Core_Taxonomy {
+class Example_Taxonomy extends CCB_Core_Taxonomy {
/**
* Name of the taxonomy
@@ -66,4 +66,4 @@ public static function get_taxonomy_args() {
}
-new Example();
+new Example_Taxonomy();
From f214234a914239fb356fa0d67977171495c677d7 Mon Sep 17 00:00:00 2001
From: Jared Cobb
Date: Sun, 7 Jan 2018 13:05:26 -0700
Subject: [PATCH 14/16] Added new capabilities filter
---
README.md | 40 +++++++++++++++++++++++
README.txt | 4 +--
includes/class-ccb-core-settings-page.php | 11 ++++++-
includes/class-ccb-core.php | 12 +++++--
4 files changed, 62 insertions(+), 5 deletions(-)
create mode 100644 README.md
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f232ee1
--- /dev/null
+++ b/README.md
@@ -0,0 +1,40 @@
+# Church Community Builder Core API
+
+## A WordPress Plugin that syncs your church data
+
+CCB Core API is a WordPress plugin that has one simple job: It **synchronizes** your church data from [Church Community Builder](https://www.churchcommunitybuilder.com/) into your WordPress database as [Custom Post Types](https://codex.wordpress.org/Post_Types#Custom_Post_Types), [Custom Taxonomies](https://codex.wordpress.org/Taxonomies#Custom_Taxonomies), and [Post Meta](https://codex.wordpress.org/Custom_Fields).
+
+## Who should use this?
+
+This plugin is geared toward Developers, Designers, and Site Administrators who are familiar with customizing WordPress templates. While it does a great job of synchronizing the data, you'll still need to alter your theme in order to take *advantage* of the data.
+
+## What's included?
+
+Out of the box, there are two complete integrations:
+
+### Public Groups
+
+This integration will synchronize any groups that are both _publicly listed_ and _active_ from the Church Community Builder `group_profiles` service to a Custom Post Type named `ccb_core_groups`.
+
+### Public Calendar (Events)
+
+This integration will synchronize all events from the Church Community Builder `public_calendar_listing` service to a Custom Post Type named `ccb_core_calendar`.
+
+## Features
+
+* **Auto Synchronize** - Set it and forget it! The plugin works in the background, never interrupting you or your visitors.
+* **Secure** - Your credentials are encrypted, and so is the connection with the Church Community Builder API.
+* **WordPress Standards** - The plugin follows WordPress coding standards and best practices, so it's easy to extend and build upon.
+* **Free** - Free as in "speech" or free as in "beer"? Yes! It's [GPLv2 licensed](https://tldrlegal.com/license/gnu-general-public-license-v2). Don't you love open source?
+
+## Customizing & Extending
+
+* Setup additional integrations with other Church Community Builder API services.
+* Write your own plugin that builds upon this one.
+* Customize the existing integrations (Groups & Events).
+
+**[The Wiki](https://github.com/jaredcobb/ccb-core/wiki) has more information and code samples.**
+
+## General Usage
+
+General usage information (setting up the plugin and customizing your theme) can be found in the [usage docs](https://www.wpccb.com/documentation/).
\ No newline at end of file
diff --git a/README.txt b/README.txt
index 9c2089b..7cf0637 100644
--- a/README.txt
+++ b/README.txt
@@ -13,9 +13,9 @@ Provides a core integration to the Church Community Builder API.
Church Community Builder Core API *synchronizes* your church data to WordPress [custom post types](https://codex.wordpress.org/Custom_Post_Types).
-This plugin is geared toward developers (or advanced WordPress users who aren't afraid to get into a little bit of code).
+This plugin is geared toward Developers, Designers, and Site Administrators who aren't afraid to get into a little bit of code.
-Find out more at [https://www.wpccb.com](https://www.wpccb.com).
+Find out more at [https://www.wpccb.com](https://www.wpccb.com) and [https://github.com/jaredcobb/ccb-core](https://github.com/jaredcobb/ccb-core).
= Why Use This Plugin? =
diff --git a/includes/class-ccb-core-settings-page.php b/includes/class-ccb-core-settings-page.php
index 1b16a8f..8117278 100644
--- a/includes/class-ccb-core-settings-page.php
+++ b/includes/class-ccb-core-settings-page.php
@@ -45,7 +45,16 @@ public function __construct( $page_id ) {
* @return void
*/
public function render_page() {
- if ( ! current_user_can( 'manage_options' ) ) {
+
+ /**
+ * Defines the capability that is required for the user
+ * to access the settings page.
+ *
+ * @since 1.0.0
+ *
+ * @param string $capability The capability required to access the page.
+ */
+ if ( ! current_user_can( apply_filters( 'ccb_core_settings_capability', 'manage_options' ) ) ) {
wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'ccb-core' ) );
}
?>
diff --git a/includes/class-ccb-core.php b/includes/class-ccb-core.php
index cbcb0ad..fb2280e 100644
--- a/includes/class-ccb-core.php
+++ b/includes/class-ccb-core.php
@@ -163,7 +163,15 @@ public function initialize_settings_menu() {
add_menu_page(
__( 'Church Community Builder Core API', 'ccb-core' ),
__( 'CCB Core API', 'ccb-core' ),
- 'manage_options',
+ /**
+ * Defines the capability that is required for the user
+ * to access the settings page.
+ *
+ * @since 1.0.0
+ *
+ * @param string $capability The capability required to access the page.
+ */
+ apply_filters( 'ccb_core_settings_capability', 'manage_options' ),
'ccb_core_settings',
'__return_null',
'dashicons-update',
@@ -176,7 +184,7 @@ public function initialize_settings_menu() {
'ccb_core_settings',
$page['page_title'],
$page['page_title'],
- 'manage_options',
+ apply_filters( 'ccb_core_settings_capability', 'manage_options' ),
$page_id,
[
$settings_page,
From c34704d37c53407e9a723fbe6c53c44d71cc3ae4 Mon Sep 17 00:00:00 2001
From: Jared Cobb
Date: Sun, 7 Jan 2018 14:18:47 -0700
Subject: [PATCH 15/16] Fixed typo
---
includes/class-ccb-core-admin-ajax.php | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/includes/class-ccb-core-admin-ajax.php b/includes/class-ccb-core-admin-ajax.php
index 8cd10d6..268a316 100644
--- a/includes/class-ccb-core-admin-ajax.php
+++ b/includes/class-ccb-core-admin-ajax.php
@@ -180,7 +180,7 @@ public function ajax_get_latest_sync() {
/**
* Filters the message that gets output to the user
- * after a synchonrization is finished.
+ * after a synchronization is finished.
*
* @since 1.0.0
*
From 8d46270c76dd49b9a7ea7901367c74aecf009197 Mon Sep 17 00:00:00 2001
From: Jared Cobb
Date: Sun, 7 Jan 2018 15:43:12 -0700
Subject: [PATCH 16/16] Added migration script for settings when upgrading
---
includes/class-ccb-core.php | 69 +++++++++++++++++++++++++++++++++++++
1 file changed, 69 insertions(+)
diff --git a/includes/class-ccb-core.php b/includes/class-ccb-core.php
index fb2280e..26adb4b 100644
--- a/includes/class-ccb-core.php
+++ b/includes/class-ccb-core.php
@@ -102,6 +102,9 @@ private function define_hooks() {
// Internationalization.
add_action( 'plugins_loaded', [ $this, 'load_plugin_textdomain' ] );
+ // Check the plugin / database version and run any required upgrades.
+ add_action( 'plugins_loaded', [ $this, 'check_version' ] );
+
// Plugin settings, menus, options.
add_filter( 'plugin_action_links_' . CCB_CORE_BASENAME, [ $this, 'add_settings_link' ] );
@@ -135,6 +138,28 @@ public function load_plugin_textdomain() {
}
+ /**
+ * Check the current plugin version and kick off any applicable upgrades.
+ *
+ * @return void
+ */
+ public function check_version() {
+ $current_version = get_option( 'ccb_core_version' );
+
+ // We are currently up to date.
+ if ( version_compare( $current_version, CCB_CORE_VERSION, '>=' ) ) {
+ return;
+ }
+
+ // Upgrade to version 1.0.0.
+ if ( version_compare( $current_version, '1.0.0', '<' ) ) {
+ $this->upgrade_to_1_0_0();
+ }
+
+ // Update the DB version.
+ update_option( 'ccb_core_version', CCB_CORE_VERSION );
+ }
+
/**
* Create a helpful settings link on the plugin page
*
@@ -342,5 +367,49 @@ public function enqueue_scripts( $hook ) {
}
+ /**
+ * Converts any legacy options to the new format
+ *
+ * @return void
+ */
+ private function upgrade_to_1_0_0() {
+ $current_options = CCB_Core_Helpers::instance()->get_options();
+ $updated_options = [];
+ $options_hash = [
+ 'subdomain' => 'subdomain',
+ 'credentials' => 'credentials',
+ 'groups-enabled' => 'groups_enabled',
+ 'groups-name' => 'groups_name',
+ 'groups-slug' => 'groups_slug',
+ 'groups-import-images' => 'groups_import_images',
+ 'groups-advanced' => 'groups_advanced',
+ 'groups-exclude-from-search' => 'groups_exclude_from_search',
+ 'groups-publicly-queryable' => 'groups_publicly_queryable',
+ 'groups-show-ui' => 'groups_show_ui',
+ 'groups-show-in-nav-menus' => 'groups_show_in_nav_menus',
+ 'calendar-enabled' => 'calendar_enabled',
+ 'calendar-name' => 'calendar_name',
+ 'calendar-slug' => 'calendar_slug',
+ 'calendar-advanced' => 'calendar_advanced',
+ 'calendar-date-range-type' => 'calendar_date_range_type',
+ 'calendar-relative-weeks-past' => 'calendar_relative_weeks_past',
+ 'calendar-relative-weeks-future' => 'calendar_relative_weeks_future',
+ 'calendar-specific-start' => 'calendar_specific_start',
+ 'calendar-specific-end' => 'calendar_specific_end',
+ 'calendar-exclude-from-search' => 'calendar_exclude_from_search',
+ 'calendar-publicly-queryable' => 'calendar_publicly_queryable',
+ 'calendar-show-ui' => 'calendar_show_ui',
+ 'calendar-show-in-nav-menus' => 'calendar_show_in_nav_menus',
+ ];
+
+ if ( ! empty( $current_options ) ) {
+ foreach ( $options_hash as $old => $new ) {
+ if ( isset( $current_options[ $old ] ) ) {
+ $updated_options[ $new ] = $current_options[ $old ];
+ }
+ }
+ update_option( 'ccb_core_settings', $updated_options );
+ }
+ }
}