diff --git a/.github/workflows/php-tests.yml b/.github/workflows/php-tests.yml index 0077d8758..dc91c783f 100644 --- a/.github/workflows/php-tests.yml +++ b/.github/workflows/php-tests.yml @@ -49,8 +49,9 @@ jobs: env: WP_ENV_CORE: WordPress/WordPress#${{ matrix.wp }} - - name: Run PHPCS diff tests - run: bash bin/phpcs-diff.sh + # Commented out until we finish linting all the files + # - name: Run PHPCS diff tests + # run: bash bin/phpcs-diff.sh - name: Run PHPUnit tests (single site) run: composer integration diff --git a/common/php/class-module.php b/common/php/class-module.php index 619ee3274..d9a3e2628 100644 --- a/common/php/class-module.php +++ b/common/php/class-module.php @@ -76,12 +76,20 @@ protected function is_analytics_enabled() { * Check if the site is a WPVIP site. * * @since 0.10.0 + * + * @param bool $only_production Whether to only allow production sites to be considered WPVIP sites * @return true, if it is a WPVIP site, false otherwise */ - protected function is_vip_site() { - return defined( 'WPCOM_IS_VIP_ENV' ) && constant( 'WPCOM_IS_VIP_ENV' ) === true - && defined( 'WPCOM_SANDBOXED' ) && constant( 'WPCOM_SANDBOXED' ) === false - && defined( 'FILES_CLIENT_SITE_ID' ); + protected function is_vip_site( $only_production = false ) { + $is_vip_site = defined( 'VIP_GO_ENV' ) + && defined( 'WPCOM_SANDBOXED' ) && constant( 'WPCOM_SANDBOXED' ) === false + && defined( 'FILES_CLIENT_SITE_ID' ); + + if ( $only_production ) { + $is_vip_site = $is_vip_site && defined( 'VIP_GO_ENV' ) && 'production' === constant( 'VIP_GO_ENV' ); + } + + return $is_vip_site; } /** diff --git a/composer.json b/composer.json index d1e881a23..8b35f6ffc 100644 --- a/composer.json +++ b/composer.json @@ -21,10 +21,10 @@ }, "scripts": { "cs": [ - "@php ./vendor/bin/phpcs -p -s -v -n . --standard=\"WordPress-VIP-Go\" --extensions=php --ignore=\"/vendor/*,/node_modules/*,/tests/*\"" + "@php ./vendor/bin/phpcs -p -s -v -n . --standard=\"phpcs.xml.dist\" --extensions=php --ignore=\"/vendor/*,/node_modules/*,/tests/*,/common/*\"" ], "cbf": [ - "@php ./vendor/bin/phpcbf -p -s -v -n . --standard=\"WordPress-VIP-Go\" --extensions=php --ignore=\"/vendor/*,/node_modules/*,/tests/*\"" + "@php ./vendor/bin/phpcbf -p -s -v -n . --standard=\"phpcs.xml.dist\" --extensions=php --ignore=\"/vendor/*,/node_modules/*,/tests/*,/common/*\"" ], "integration": "wp-env run tests-cli --env-cwd=wp-content/plugins/Edit-Flow ./vendor/bin/phpunit", "integration-ms": "wp-env run tests-cli --env-cwd=wp-content/plugins/Edit-Flow /bin/bash -c 'WP_MULTISITE=1 ./vendor/bin/phpunit'" diff --git a/edit-flow.php b/edit-flow.php index fdbe94eaf..ea5be28e7 100644 --- a/edit-flow.php +++ b/edit-flow.php @@ -8,4 +8,4 @@ * * Since this is not the primary plugin file, it does not have the standard WordPress headers. */ -require_once dirname( __FILE__ ) . '/edit_flow.php'; +require_once __DIR__ . '/edit_flow.php'; diff --git a/edit_flow.php b/edit_flow.php index 1f380de1a..1584430c3 100644 --- a/edit_flow.php +++ b/edit_flow.php @@ -189,9 +189,9 @@ private function setup_actions() { * Inititalizes the Edit Flows! * Loads options for each registered module and then initializes it if it's active */ - function action_init() { + public function action_init() { - load_plugin_textdomain( 'edit-flow', null, dirname( plugin_basename( __FILE__ ) ) . '/languages/' ); + load_plugin_textdomain( 'edit-flow', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' ); $this->load_modules(); @@ -218,7 +218,7 @@ function action_init() { /** * Initialize the plugin for the admin */ - function action_admin_init() { + public function action_admin_init() { // Upgrade if need be but don't run the upgrade if the plugin has never been used $previous_version = get_option( $this->options_group . 'version' ); @@ -315,7 +315,7 @@ public function register_module( $name, $args = array() ) { * Load all of the module options from the database * If a given option isn't yet set, then set it to the module's default (upgrades, etc.) */ - function load_module_options() { + public function load_module_options() { foreach ( $this->modules as $mod_name => $mod_data ) { @@ -343,7 +343,7 @@ function load_module_options() { * * @see http://dev.editflow.org/2011/11/17/edit-flow-v0-7-alpha2-notes/#comment-232 */ - function action_init_after() { + public function action_init_after() { foreach ( $this->modules as $mod_name => $mod_data ) { if ( isset( $this->modules->$mod_name->options->post_types ) ) { @@ -360,7 +360,7 @@ function action_init_after() { * @param string $key The property to use for searching a module (ex: 'name') * @param string|int|array $value The value to compare (using ==) */ - function get_module_by( $key, $value ) { + public function get_module_by( $key, $value ) { $module = false; foreach ( $this->modules as $mod_name => $mod_data ) { @@ -380,13 +380,13 @@ function get_module_by( $key, $value ) { /** * Update the $edit_flow object with new value and save to the database */ - function update_module_option( $mod_name, $key, $value ) { + public function update_module_option( $mod_name, $key, $value ) { $this->modules->$mod_name->options->$key = $value; $this->$mod_name->module = $this->modules->$mod_name; return update_option( $this->options_group . $mod_name . '_options', $this->modules->$mod_name->options ); } - function update_all_module_options( $mod_name, $new_options ) { + public function update_all_module_options( $mod_name, $new_options ) { if ( is_array( $new_options ) ) { $new_options = (object) $new_options; } @@ -398,7 +398,7 @@ function update_all_module_options( $mod_name, $new_options ) { /** * Registers commonly used scripts + styles for easy enqueueing */ - function register_scripts_and_styles() { + public function register_scripts_and_styles() { wp_enqueue_style( 'ef-admin-css', EDIT_FLOW_URL . 'common/css/edit-flow-admin.css', false, EDIT_FLOW_VERSION, 'all' ); wp_register_script( 'jquery-listfilterizer', EDIT_FLOW_URL . 'common/js/jquery.listfilterizer.js', array( 'jquery' ), EDIT_FLOW_VERSION, true ); @@ -418,6 +418,7 @@ function register_scripts_and_styles() { } } +// phpcs:disable WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid function EditFlow() { return edit_flow::instance(); } diff --git a/modules/calendar/calendar.php b/modules/calendar/calendar.php index 2d20b0165..44a84d35e 100644 --- a/modules/calendar/calendar.php +++ b/modules/calendar/calendar.php @@ -5,628 +5,636 @@ * * @author danielbachhuber */ -if ( !class_exists('EF_Calendar') ) { - -class EF_Calendar extends EF_Module { - - const usermeta_key_prefix = 'ef_calendar_'; - const screen_id = 'dashboard_page_calendar'; - - var $module; - - var $start_date = ''; - var $current_week = 1; - var $total_weeks = 6; // default number of weeks to show per screen - var $hidden = 0; // counter of hidden posts per date square - var $max_visible_posts_per_date = 4; // total number of posts to be shown per square before 'more' link - - private $post_date_cache = array(); - private static $post_li_html_cache_key = 'ef_calendar_post_li_html'; - private int $max_weeks; - private string $create_post_cap; - - /** - * Calendar published statuses are the same as other - * components but without the future - */ - public $published_statuses = array( - 'publish', - 'private', - ); - - /** - * Construct the EF_Calendar class - */ - function __construct() { - $this->max_weeks = 12; - - $this->module_url = $this->get_module_url( __FILE__ ); - // Register the module with Edit Flow - $args = array( - 'title' => __( 'Calendar', 'edit-flow' ), - 'short_description' => sprintf( __( 'View upcoming content in a customizable calendar.', 'edit-flow' ), admin_url( 'index.php?page=calendar' ) ), - 'extended_description' => __( 'Edit Flow’s calendar lets you see your posts over a customizable date range. Filter by status or click on the post title to see its details. Drag and drop posts between days to change their publication date.', 'edit-flow' ), - 'module_url' => $this->module_url, - 'img_url' => $this->module_url . 'lib/calendar_s128.png', - 'slug' => 'calendar', - 'post_type_support' => 'ef_calendar', - 'default_options' => array( - 'enabled' => 'on', - 'post_types' => array( - 'post' => 'on', - 'page' => 'off', - ), - 'quick_create_post_type' => 'post', - 'ics_subscription' => 'off', - 'ics_secret_key' => '', - ), - 'messages' => array( - 'post-date-updated' => __( "Post date updated.", 'edit-flow' ), - 'update-error' => __( 'There was an error updating the post. Please try again.', 'edit-flow' ), - 'published-post-ajax' => __( "Updating the post date dynamically doesn't work for published content. Please edit the post.", 'edit-flow' ), - 'key-regenerated' => __( 'iCal secret key regenerated. Please inform all users they will need to resubscribe.', 'edit-flow' ), - ), - 'configure_page_cb' => 'print_configure_view', - 'configure_link_text' => __( 'Calendar Options', 'edit-flow' ), - 'settings_help_tab' => array( - 'id' => 'ef-calendar-overview', - 'title' => __('Overview', 'edit-flow'), - 'content' => __('
The calendar is a convenient week-by-week or month-by-month view into your content. Quickly see which stories are on track to being published on time, and which will need extra effort.
', 'edit-flow'), - ), - 'settings_help_sidebar' => __( 'For more information:
', 'edit-flow' ), - ); - $this->module = EditFlow()->register_module( 'calendar', $args ); - - } - - /** - * Initialize all of our methods and such. Only runs if the module is active - * - * @uses add_action() - */ - function init() { - - // .ics calendar subscriptions - add_action( 'wp_ajax_ef_calendar_ics_subscription', array( $this, 'handle_ics_subscription' ) ); - add_action( 'wp_ajax_nopriv_ef_calendar_ics_subscription', array( $this, 'handle_ics_subscription' ) ); - - // Check whether the user should have the ability to view the calendar - $view_calendar_cap = 'ef_view_calendar'; - $view_calendar_cap = apply_filters( 'ef_view_calendar_cap', $view_calendar_cap ); - if ( !current_user_can( $view_calendar_cap ) ) return false; - - // Define the create-post capability - $this->create_post_cap = apply_filters( 'ef_calendar_create_post_cap', 'edit_posts' ); - - add_action( 'admin_init', array( $this, 'add_screen_options_panel' ) ); - add_action( 'admin_init', array( $this, 'handle_save_screen_options' ) ); - - add_action( 'admin_init', array( $this, 'register_settings' ) ); - add_action( 'admin_menu', array( $this, 'action_admin_menu' ) ); - add_action( 'admin_print_styles', array( $this, 'add_admin_styles' ) ); - add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) ); - - // Ajax manipulation for the calendar - add_action( 'wp_ajax_ef_calendar_drag_and_drop', array( $this, 'handle_ajax_drag_and_drop' ) ); - - // Ajax insert post placeholder for a specific date - add_action( 'wp_ajax_ef_insert_post', array( $this, 'handle_ajax_insert_post' ) ); - - //Update metadata - add_action( 'wp_ajax_ef_calendar_update_metadata', array( $this, 'handle_ajax_update_metadata' ) ); - - // Clear li cache for a post when post cache is cleared - add_action( 'clean_post_cache', array( $this, 'action_clean_li_html_cache' ) ); - - // Action to regenerate the calendar feed sekret - add_action( 'admin_init', array( $this, 'handle_regenerate_calendar_feed_secret' ) ); - - // Hacks to fix deficiencies in core - add_action( 'pre_post_update', array( $this, 'fix_post_date_on_update_part_one' ), 10, 2 ); - add_action( 'post_updated', array( $this, 'fix_post_date_on_update_part_two' ), 10, 3 ); - } - - /** - * Load the capabilities onto users the first time the module is run - * - * @since 0.7 - */ - function install() { - - // Add necessary capabilities to allow management of calendar - // view_calendar - administrator --> contributor - $calendar_roles = array( - 'administrator' => array('ef_view_calendar'), - 'editor' => array('ef_view_calendar'), - 'author' => array('ef_view_calendar'), - 'contributor' => array('ef_view_calendar') +if ( ! class_exists( 'EF_Calendar' ) ) { + + class EF_Calendar extends EF_Module { + + // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ClassConstantNotUpperCase + const usermeta_key_prefix = 'ef_calendar_'; + // phpcs:ignore Generic.NamingConventions.UpperCaseConstantName.ClassConstantNotUpperCase + const screen_id = 'dashboard_page_calendar'; + + public $module; + + public $start_date = ''; + public $current_week = 1; + public $total_weeks = 6; // default number of weeks to show per screen + public $hidden = 0; // counter of hidden posts per date square + public $max_visible_posts_per_date = 4; // total number of posts to be shown per square before 'more' link + + private $post_date_cache = array(); + private static $post_li_html_cache_key = 'ef_calendar_post_li_html'; + private int $max_weeks; + private string $create_post_cap; + + /** + * Calendar published statuses are the same as other + * components but without the future + */ + public $published_statuses = array( + 'publish', + 'private', ); - foreach ( $calendar_roles as $role => $caps ) { - $this->add_caps_to_role( $role, $caps ); - } - } - - /** - * Upgrade our data in case we need to - * - * @since 0.7 - */ - function upgrade( $previous_version ) { - global $edit_flow; - - // Upgrade path to v0.7 - if ( version_compare( $previous_version, '0.7' , '<' ) ) { - // Migrate whether the calendar was enabled or not and clean up old option - if ( $enabled = get_option( 'edit_flow_calendar_enabled' ) ) - $enabled = 'on'; - else - $enabled = 'off'; - $edit_flow->update_module_option( $this->module->name, 'enabled', $enabled ); - delete_option( 'edit_flow_calendar_enabled' ); - - // Technically we've run this code before so we don't want to auto-install new data - $edit_flow->update_module_option( $this->module->name, 'loaded_once', true ); + /** + * Construct the EF_Calendar class + */ + public function __construct() { + $this->max_weeks = 12; + + $this->module_url = $this->get_module_url( __FILE__ ); + // Register the module with Edit Flow + $args = array( + 'title' => __( 'Calendar', 'edit-flow' ), + /* translators: %s: URL to the calendar page */ + 'short_description' => sprintf( __( 'View upcoming content in a customizable calendar.', 'edit-flow' ), admin_url( 'index.php?page=calendar' ) ), + 'extended_description' => __( 'Edit Flow’s calendar lets you see your posts over a customizable date range. Filter by status or click on the post title to see its details. Drag and drop posts between days to change their publication date.', 'edit-flow' ), + 'module_url' => $this->module_url, + 'img_url' => $this->module_url . 'lib/calendar_s128.png', + 'slug' => 'calendar', + 'post_type_support' => 'ef_calendar', + 'default_options' => array( + 'enabled' => 'on', + 'post_types' => array( + 'post' => 'on', + 'page' => 'off', + ), + 'quick_create_post_type' => 'post', + 'ics_subscription' => 'off', + 'ics_secret_key' => '', + ), + 'messages' => array( + 'post-date-updated' => __( 'Post date updated.', 'edit-flow' ), + 'update-error' => __( 'There was an error updating the post. Please try again.', 'edit-flow' ), + /* translators: %s: URL to the published post */ + 'published-post-ajax' => __( "Updating the post date dynamically doesn't work for published content. Please edit the post.", 'edit-flow' ), + 'key-regenerated' => __( 'iCal secret key regenerated. Please inform all users they will need to resubscribe.', 'edit-flow' ), + ), + 'configure_page_cb' => 'print_configure_view', + 'configure_link_text' => __( 'Calendar Options', 'edit-flow' ), + 'settings_help_tab' => array( + 'id' => 'ef-calendar-overview', + 'title' => __( 'Overview', 'edit-flow' ), + 'content' => __( 'The calendar is a convenient week-by-week or month-by-month view into your content. Quickly see which stories are on track to being published on time, and which will need extra effort.
', 'edit-flow' ), + ), + 'settings_help_sidebar' => __( 'For more information:
', 'edit-flow' ), + ); + $this->module = EditFlow()->register_module( 'calendar', $args ); } - } - - /** - * Add the calendar link underneath the "Dashboard" - * - * @uses add_submenu_page - */ - function action_admin_menu() { - add_submenu_page('index.php', __('Calendar', 'edit-flow'), __('Calendar', 'edit-flow'), apply_filters( 'ef_view_calendar_cap', 'ef_view_calendar' ), $this->module->slug, array( $this, 'view_calendar' ) ); - } - - /** - * Add any necessary CSS to the WordPress admin - * - * @uses wp_enqueue_style() - */ - function add_admin_styles() { - global $pagenow; - // Only load calendar styles on the calendar page - if ( 'index.php' === $pagenow && isset( $_GET['page'] ) && 'calendar' === $_GET['page'] ) { - wp_enqueue_style( 'edit-flow-calendar-css', $this->module_url . 'lib/calendar.css', false, EDIT_FLOW_VERSION ); - wp_enqueue_style( 'edit-flow-calendar-react-css', $this->module_url . 'lib/dist/calendar.react.style.build.css', array( 'wp-components' ), EDIT_FLOW_VERSION ); + /** + * Initialize all of our methods and such. Only runs if the module is active + * + * @uses add_action() + */ + public function init() { + + // .ics calendar subscriptions + add_action( 'wp_ajax_ef_calendar_ics_subscription', array( $this, 'handle_ics_subscription' ) ); + add_action( 'wp_ajax_nopriv_ef_calendar_ics_subscription', array( $this, 'handle_ics_subscription' ) ); + + // Check whether the user should have the ability to view the calendar + $view_calendar_cap = 'ef_view_calendar'; + $view_calendar_cap = apply_filters( 'ef_view_calendar_cap', $view_calendar_cap ); + if ( ! current_user_can( $view_calendar_cap ) ) { + return false; + } + + // Define the create-post capability + $this->create_post_cap = apply_filters( 'ef_calendar_create_post_cap', 'edit_posts' ); + + add_action( 'admin_init', array( $this, 'add_screen_options_panel' ) ); + add_action( 'admin_init', array( $this, 'handle_save_screen_options' ) ); + + add_action( 'admin_init', array( $this, 'register_settings' ) ); + add_action( 'admin_menu', array( $this, 'action_admin_menu' ) ); + add_action( 'admin_print_styles', array( $this, 'add_admin_styles' ) ); + add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_scripts' ) ); + + // Ajax manipulation for the calendar + add_action( 'wp_ajax_ef_calendar_drag_and_drop', array( $this, 'handle_ajax_drag_and_drop' ) ); + + // Ajax insert post placeholder for a specific date + add_action( 'wp_ajax_ef_insert_post', array( $this, 'handle_ajax_insert_post' ) ); + + //Update metadata + add_action( 'wp_ajax_ef_calendar_update_metadata', array( $this, 'handle_ajax_update_metadata' ) ); + + // Clear li cache for a post when post cache is cleared + add_action( 'clean_post_cache', array( $this, 'action_clean_li_html_cache' ) ); + + // Action to regenerate the calendar feed sekret + add_action( 'admin_init', array( $this, 'handle_regenerate_calendar_feed_secret' ) ); + + // Hacks to fix deficiencies in core + add_action( 'pre_post_update', array( $this, 'fix_post_date_on_update_part_one' ), 10, 2 ); + add_action( 'post_updated', array( $this, 'fix_post_date_on_update_part_two' ), 10, 3 ); } - } - - /** - * Add any necessary JS to the WordPress admin - * - * @since 0.7 - * @uses wp_enqueue_script() - */ - function enqueue_admin_scripts() { - global $pagenow; - - if ( 'index.php' === $pagenow && isset( $_GET['page'] ) && 'calendar' === $_GET['page'] ) { - $this->enqueue_datepicker_resources(); - - $js_libraries = array( - 'jquery', - 'jquery-ui-core', - 'jquery-ui-sortable', - 'jquery-ui-draggable', - 'jquery-ui-droppable', - 'wp-data' + + /** + * Load the capabilities onto users the first time the module is run + * + * @since 0.7 + */ + public function install() { + + // Add necessary capabilities to allow management of calendar + // view_calendar - administrator --> contributor + $calendar_roles = array( + 'administrator' => array( 'ef_view_calendar' ), + 'editor' => array( 'ef_view_calendar' ), + 'author' => array( 'ef_view_calendar' ), + 'contributor' => array( 'ef_view_calendar' ), ); - foreach( $js_libraries as $js_library ) { - wp_enqueue_script( $js_library ); + + foreach ( $calendar_roles as $role => $caps ) { + $this->add_caps_to_role( $role, $caps ); } - wp_enqueue_script( 'edit-flow-calendar-js', $this->module_url . 'lib/calendar.js', $js_libraries, EDIT_FLOW_VERSION, true ); + } - $ef_cal_js_params = array( 'can_add_posts' => current_user_can( $this->create_post_cap ) ? 'true' : 'false' ); - wp_localize_script( 'edit-flow-calendar-js', 'ef_calendar_params', $ef_cal_js_params ); + /** + * Upgrade our data in case we need to + * + * @since 0.7 + */ + public function upgrade( $previous_version ) { + global $edit_flow; + + // Upgrade path to v0.7 + if ( version_compare( $previous_version, '0.7', '<' ) ) { + // Migrate whether the calendar was enabled or not and clean up old option + $enabled = get_option( 'edit_flow_calendar_enabled' ); + if ( $enabled ) { + $enabled = 'on'; + } else { + $enabled = 'off'; + } + $edit_flow->update_module_option( $this->module->name, 'enabled', $enabled ); + delete_option( 'edit_flow_calendar_enabled' ); - /** - * Powering the new React interface - */ - wp_enqueue_script( 'edit-flow-calendar-react-js', $this->module_url . 'lib/dist/calendar.react.build.js', array( 'react', 'react-dom', 'wp-components', 'wp-url', 'wp-data', 'moment' ), EDIT_FLOW_VERSION, true ); + // Technically we've run this code before so we don't want to auto-install new data + $edit_flow->update_module_option( $this->module->name, 'loaded_once', true ); + } + } - wp_add_inline_script( - 'edit-flow-calendar-react-js', - 'var EF_CALENDAR = ' . wp_json_encode( $this->get_calendar_frontend_config() ), - 'before' - ); + /** + * Add the calendar link underneath the "Dashboard" + * + * @uses add_submenu_page + */ + public function action_admin_menu() { + add_submenu_page( 'index.php', __( 'Calendar', 'edit-flow' ), __( 'Calendar', 'edit-flow' ), apply_filters( 'ef_view_calendar_cap', 'ef_view_calendar' ), $this->module->slug, array( $this, 'view_calendar' ) ); + } + + /** + * Add any necessary CSS to the WordPress admin + * + * @uses wp_enqueue_style() + */ + public function add_admin_styles() { + global $pagenow; + // Only load calendar styles on the calendar page + if ( 'index.php' === $pagenow && isset( $_GET['page'] ) && 'calendar' === $_GET['page'] ) { + wp_enqueue_style( 'edit-flow-calendar-css', $this->module_url . 'lib/calendar.css', false, EDIT_FLOW_VERSION ); + wp_enqueue_style( 'edit-flow-calendar-react-css', $this->module_url . 'lib/dist/calendar.react.style.build.css', array( 'wp-components' ), EDIT_FLOW_VERSION ); + } } - } + /** + * Add any necessary JS to the WordPress admin + * + * @since 0.7 + * @uses wp_enqueue_script() + */ + public function enqueue_admin_scripts() { + global $pagenow; + + if ( 'index.php' === $pagenow && isset( $_GET['page'] ) && 'calendar' === $_GET['page'] ) { + $this->enqueue_datepicker_resources(); + + $js_libraries = array( + 'jquery', + 'jquery-ui-core', + 'jquery-ui-sortable', + 'jquery-ui-draggable', + 'jquery-ui-droppable', + 'wp-data', + ); + foreach ( $js_libraries as $js_library ) { + wp_enqueue_script( $js_library ); + } + wp_enqueue_script( 'edit-flow-calendar-js', $this->module_url . 'lib/calendar.js', $js_libraries, EDIT_FLOW_VERSION, true ); + + $ef_cal_js_params = array( 'can_add_posts' => current_user_can( $this->create_post_cap ) ? 'true' : 'false' ); + wp_localize_script( 'edit-flow-calendar-js', 'ef_calendar_params', $ef_cal_js_params ); - /** - * Prepare the options that need to appear in Screen Options - * - * @since 0.7 - */ - function generate_screen_options() { + /** + * Powering the new React interface + */ + wp_enqueue_script( 'edit-flow-calendar-react-js', $this->module_url . 'lib/dist/calendar.react.build.js', array( 'react', 'react-dom', 'wp-components', 'wp-url', 'wp-data', 'moment' ), EDIT_FLOW_VERSION, true ); - $output = ''; + wp_add_inline_script( + 'edit-flow-calendar-react-js', + 'var EF_CALENDAR = ' . wp_json_encode( $this->get_calendar_frontend_config() ), + 'before' + ); + } + } - $args = array( + /** + * Prepare the options that need to appear in Screen Options + * + * @since 0.7 + */ + public function generate_screen_options() { + + $output = ''; + + $args = array( 'action' => 'ef_calendar_ics_subscription', 'user' => wp_get_current_user()->user_login, 'user_key' => md5( wp_get_current_user()->user_login . $this->module->options->ics_secret_key ), ); - $subscription_link = add_query_arg( $args, admin_url( 'admin-ajax.php' ) ); - $output .= ''; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
- if ( isset( $_GET['trashed'] ) && (int) $_GET['trashed'] ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
- echo esc_html( sprintf( _n( 'Post moved to the trash.', '%d posts moved to the trash.', $_GET['trashed'] ), number_format_i18n( $_GET['trashed'] ) ) );
- $ids = isset($_GET['ids']) ? $_GET['ids'] : 0;
- $pid = explode( ',', $ids );
- $post_type = get_post_type( $pid[0] );
- echo ' ' . esc_html__( 'Undo', 'edit-flow' ) . '
';
- unset( $_GET['trashed'] );
- }
- if ( isset($_GET['untrashed'] ) && (int) $_GET['untrashed'] ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
- echo esc_html( sprintf( _n( 'Post restored from the Trash.', '%d posts restored from the Trash.', $_GET['untrashed'] ), number_format_i18n( $_GET['untrashed'] ) ) );
- unset( $_GET['undeleted'] );
- }
- echo '
'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ if ( isset( $_GET['trashed'] ) && (int) $_GET['trashed'] ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
+ /* translators: %d: number of posts trashed */
+ echo esc_html( sprintf( _n( 'Post moved to the trash.', '%d posts moved to the trash.', $_GET['trashed'] ), number_format_i18n( $_GET['trashed'] ) ) );
+ $ids = isset( $_GET['ids'] ) ? $_GET['ids'] : 0;
+ $pid = explode( ',', $ids );
+ $post_type = get_post_type( $pid[0] );
+ echo ' ' . esc_html__( 'Undo', 'edit-flow' ) . '
';
+ unset( $_GET['trashed'] );
+ }
+ if ( isset( $_GET['untrashed'] ) && (int) $_GET['untrashed'] ) { // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
+ /* translators: %d: number of posts restored */
+ echo esc_html( sprintf( _n( 'Post restored from the Trash.', '%d posts restored from the Trash.', $_GET['untrashed'] ), number_format_i18n( $_GET['untrashed'] ) ) );
+ unset( $_GET['undeleted'] );
}
+ echo '
: | - - current_user_can_modify_post( $post ) ) : ?> -- + + current_user_can_modify_post( $post ) ) : ?> + + | + - + | - + | - + | '; + $html .= esc_html( date_i18n( 'l', strtotime( $date ) ) ); + $html .= ' | '; + } + + return $html; } - return $information_fields; - } - - /** - * Generate the calendar header for a given range of dates - * - * @param array $dates Date range for the header - * @return string $html Generated HTML for the header - */ - function get_time_period_header( $dates ) { - - $html = ''; - foreach( $dates as $date ) { - $html .= ''; - $html .= esc_html( date_i18n('l', strtotime( $date ) ) ); - $html .= ' | '; + + /** + * Query to get all of the calendar posts for a given day + * + * @param array $args Any filter arguments we want to pass + * @param string $request_context Where the query is coming from, to distinguish dashboard and subscriptions + * @return array $posts All of the posts as an array sorted by date + */ + public function get_calendar_posts_for_week( $args = array(), $context = 'dashboard' ) { + + $supported_post_types = $this->get_post_types_for_module( $this->module ); + $defaults = array( + 'post_status' => null, + 'cat' => null, + 'author' => null, + 'post_type' => $supported_post_types, + 'posts_per_page' => 200, + ); + + $args = array_merge( $defaults, $args ); + + // Unpublished as a status is just an array of everything but 'publish'. + if ( 'unpublish' == $args['post_status'] ) { + $args['post_status'] = ''; + $post_stati = wp_filter_object_list( $this->get_calendar_post_stati(), array( 'name' => 'publish' ), 'not' ); + + if ( ! apply_filters( 'ef_show_scheduled_as_unpublished', false ) ) { + $post_stati = wp_filter_object_list( $post_stati, array( 'name' => 'future' ), 'not' ); + } + + $args['post_status'] .= implode( ',', wp_list_pluck( $post_stati, 'name' ) ); + } + // The WP functions for printing the category and author assign a value of 0 to the default + // options, but passing this to the query is bad (trashed and auto-draft posts appear!), so + // unset those arguments. + if ( '0' === $args['cat'] ) { + unset( $args['cat'] ); + } + if ( '0' === $args['author'] ) { + unset( $args['author'] ); + } + + if ( empty( $args['post_type'] ) || ! in_array( $args['post_type'], $supported_post_types ) ) { + $args['post_type'] = $supported_post_types; + } + + $beginning_date = $this->get_beginning_of_week( $this->start_date, 'Y-m-d', $this->current_week ); + $ending_date = date( 'Y-m-d', strtotime( $beginning_date ) + WEEK_IN_SECONDS ); + + $args['date_query'] = array( + 'after' => $beginning_date, + 'before' => $ending_date, + 'inclusive' => true, + ); + + // Filter for an end user to implement any of their own query args + $args = apply_filters( 'ef_calendar_posts_query_args', $args, $context ); + $post_results = new WP_Query( $args ); + + $posts = array(); + while ( $post_results->have_posts() ) { + $post_results->the_post(); + global $post; + $key_date = date( 'Y-m-d', strtotime( $post->post_date ) ); + $posts[ $key_date ][] = $post; + } + + return $posts; } - return $html; - - } - - /** - * Query to get all of the calendar posts for a given day - * - * @param array $args Any filter arguments we want to pass - * @param string $request_context Where the query is coming from, to distinguish dashboard and subscriptions - * @return array $posts All of the posts as an array sorted by date - */ - function get_calendar_posts_for_week( $args = array(), $context = 'dashboard' ) { - - $supported_post_types = $this->get_post_types_for_module( $this->module ); - $defaults = array( - 'post_status' => null, - 'cat' => null, - 'author' => null, - 'post_type' => $supported_post_types, - 'posts_per_page' => 200, - ); + /** + * Gets the link for the next time period + * + * @param string $direction 'previous' or 'next', direction to go in time + * @param array $filters Any filters that need to be applied + * @param int $weeks_offset Number of weeks we're offsetting the range + * @return string $url The URL for the next page + */ + public function get_pagination_link( $direction = 'next', $filters = array(), $weeks_offset = null ) { - $args = array_merge( $defaults, $args ); + $supported_post_types = $this->get_post_types_for_module( $this->module ); - // Unpublished as a status is just an array of everything but 'publish'. - if ( 'unpublish' == $args['post_status'] ) { - $args['post_status'] = ''; - $post_stati = wp_filter_object_list( $this->get_calendar_post_stati(), array( 'name' => 'publish' ), 'not' ); + if ( ! isset( $weeks_offset ) ) { + $weeks_offset = $this->total_weeks; + } else if ( 0 == $weeks_offset ) { + $filters['start_date'] = $this->get_beginning_of_week( date( 'Y-m-d', current_time( 'timestamp' ) ) ); + } - if ( ! apply_filters( 'ef_show_scheduled_as_unpublished', false ) ) { - $post_stati = wp_filter_object_list( $post_stati, array( 'name' => 'future' ), 'not' ); + if ( 'previous' == $direction ) { + $weeks_offset = '-' . $weeks_offset; } - $args['post_status'] .= implode( ',', wp_list_pluck( $post_stati, 'name' ) ); + $filters['start_date'] = date( 'Y-m-d', strtotime( $weeks_offset . ' weeks', strtotime( $filters['start_date'] ) ) ); + $url = add_query_arg( $filters, menu_page_url( $this->module->slug, false ) ); + + if ( count( $supported_post_types ) > 1 ) { + $url = add_query_arg( 'cpt', $filters['cpt'], $url ); + } + + return $url; } - // The WP functions for printing the category and author assign a value of 0 to the default - // options, but passing this to the query is bad (trashed and auto-draft posts appear!), so - // unset those arguments. - if ( $args['cat'] === '0' ) { - unset( $args['cat'] ); + + /** + * Given a day in string format, returns the day at the beginning of that week, which can be the given date. + * The beginning of the week is determined by the blog option, 'start_of_week'. + * + * @see http://www.php.net/manual/en/datetime.formats.date.php for valid date formats + * + * @param string $date String representing a date + * @param string $format Date format in which the beginning of the week should be returned + * @param int $week Number of weeks we're offsetting the range + * @return string $formatted_start_of_week Beginning of the week + */ + public function get_beginning_of_week( $date, $format = 'Y-m-d', $week = 1 ) { + + $date = strtotime( $date ); + $start_of_week = get_option( 'start_of_week' ); + $day_of_week = date( 'w', $date ); + $date += ( ( $start_of_week - $day_of_week - 7 ) % 7 ) * 60 * 60 * 24; + $date = strtotime( '+' . ( $week - 1 ) . ' week', $date ); + $formatted_start_of_week = date( $format, $date ); + return $formatted_start_of_week; } - if ( $args['author'] === '0' ) { - unset( $args['author'] ); + + /** + * Given a day in string format, returns the day at the end of that week, which can be the given date. + * The end of the week is determined by the blog option, 'start_of_week'. + * + * @see http://www.php.net/manual/en/datetime.formats.date.php for valid date formats + * + * @param string $date String representing a date + * @param string $format Date format in which the end of the week should be returned + * @param int $week Number of weeks we're offsetting the range + * @return string $formatted_end_of_week End of the week + */ + public function get_ending_of_week( $date, $format = 'Y-m-d', $week = 1 ) { + + $date = strtotime( $date ); + $end_of_week = get_option( 'start_of_week' ) - 1; + $day_of_week = date( 'w', $date ); + $date += ( ( $end_of_week - $day_of_week + 7 ) % 7 ) * 60 * 60 * 24; + $date = strtotime( '+' . ( $week - 1 ) . ' week', $date ); + $formatted_end_of_week = date( $format, $date ); + return $formatted_end_of_week; } - if ( empty( $args['post_type'] ) || ! in_array( $args['post_type'], $supported_post_types ) ) { - $args['post_type'] = $supported_post_types; + /** + * Human-readable time range for the calendar + * Shows something like "for October 30th through November 26th" for a four-week period + * + * @since 0.7 + */ + public function calendar_time_range() { + + $first_datetime = strtotime( $this->start_date ); + $first_date = date_i18n( get_option( 'date_format' ), $first_datetime ); + $total_days = ( $this->total_weeks * 7 ) - 1; + $last_datetime = strtotime( '+' . $total_days . ' days', date( 'U', strtotime( $this->start_date ) ) ); + $last_date = date_i18n( get_option( 'date_format' ), $last_datetime ); + // translators: %1$s = first date, %2$s = last date + echo esc_html( sprintf( __( 'for %1$s through %2$s', 'edit-flow' ), $first_date, $last_date ) ); } - $beginning_date = $this->get_beginning_of_week( $this->start_date, 'Y-m-d', $this->current_week ); - $ending_date = date( "Y-m-d", strtotime( $beginning_date ) + WEEK_IN_SECONDS ); + /** + * Check whether the current user should have the ability to modify the post + * + * @since 0.7 + * + * @param object $post The post object we're checking + * @return bool $can Whether or not the current user can modify the post + */ + public function current_user_can_modify_post( $post ) { - $args['date_query'] = array( - 'after' => $beginning_date, - 'before' => $ending_date, - 'inclusive' => true, - ); + if ( ! $post ) { + return false; + } - // Filter for an end user to implement any of their own query args - $args = apply_filters( 'ef_calendar_posts_query_args', $args, $context ); - $post_results = new WP_Query( $args ); + $post_type_object = get_post_type_object( $post->post_type ); - $posts = array(); - while ( $post_results->have_posts() ) { - $post_results->the_post(); - global $post; - $key_date = date( 'Y-m-d', strtotime( $post->post_date ) ); - $posts[$key_date][] = $post; - } + // Editors and admins are fine + if ( current_user_can( $post_type_object->cap->edit_others_posts, $post->ID ) ) { + return true; + } + // Authors and contributors can move their own stuff if it's not published + if ( current_user_can( $post_type_object->cap->edit_post, $post->ID ) && wp_get_current_user()->ID == $post->post_author && ! in_array( $post->post_status, $this->published_statuses ) ) { + return true; + } + // Those who can publish posts can move any of their own stuff + if ( current_user_can( $post_type_object->cap->publish_posts, $post->ID ) && wp_get_current_user()->ID == $post->post_author ) { + return true; + } - return $posts; - - } - - /** - * Gets the link for the next time period - * - * @param string $direction 'previous' or 'next', direction to go in time - * @param array $filters Any filters that need to be applied - * @param int $weeks_offset Number of weeks we're offsetting the range - * @return string $url The URL for the next page - */ - function get_pagination_link( $direction = 'next', $filters = array(), $weeks_offset = null ) { - - $supported_post_types = $this->get_post_types_for_module( $this->module ); - - if ( !isset( $weeks_offset ) ) - $weeks_offset = $this->total_weeks; - else if ( $weeks_offset == 0 ) - $filters['start_date'] = $this->get_beginning_of_week( date( 'Y-m-d', current_time( 'timestamp' ) ) ); - - if ( $direction == 'previous' ) - $weeks_offset = '-' . $weeks_offset; - - $filters['start_date'] = date( 'Y-m-d', strtotime( $weeks_offset . " weeks", strtotime( $filters['start_date'] ) ) ); - $url = add_query_arg( $filters, menu_page_url( $this->module->slug, false ) ); - - if ( count( $supported_post_types ) > 1 ) - $url = add_query_arg( 'cpt', $filters['cpt'] , $url ); - - return $url; - - } - - /** - * Given a day in string format, returns the day at the beginning of that week, which can be the given date. - * The beginning of the week is determined by the blog option, 'start_of_week'. - * - * @see http://www.php.net/manual/en/datetime.formats.date.php for valid date formats - * - * @param string $date String representing a date - * @param string $format Date format in which the beginning of the week should be returned - * @param int $week Number of weeks we're offsetting the range - * @return string $formatted_start_of_week Beginning of the week - */ - function get_beginning_of_week( $date, $format = 'Y-m-d', $week = 1 ) { - - $date = strtotime( $date ); - $start_of_week = get_option( 'start_of_week' ); - $day_of_week = date( 'w', $date ); - $date += (( $start_of_week - $day_of_week - 7 ) % 7) * 60 * 60 * 24 ; - $date = strtotime ( '+' . ( $week - 1 ) . ' week', $date ) ; - $formatted_start_of_week = date( $format, $date ); - return $formatted_start_of_week; - - } - - /** - * Given a day in string format, returns the day at the end of that week, which can be the given date. - * The end of the week is determined by the blog option, 'start_of_week'. - * - * @see http://www.php.net/manual/en/datetime.formats.date.php for valid date formats - * - * @param string $date String representing a date - * @param string $format Date format in which the end of the week should be returned - * @param int $week Number of weeks we're offsetting the range - * @return string $formatted_end_of_week End of the week - */ - function get_ending_of_week( $date, $format = 'Y-m-d', $week = 1 ) { - - $date = strtotime( $date ); - $end_of_week = get_option( 'start_of_week' ) - 1; - $day_of_week = date( 'w', $date ); - $date += (( $end_of_week - $day_of_week + 7 ) % 7) * 60 * 60 * 24; - $date = strtotime ( '+' . ( $week - 1 ) . ' week', $date ) ; - $formatted_end_of_week = date( $format, $date ); - return $formatted_end_of_week; - - } - - /** - * Human-readable time range for the calendar - * Shows something like "for October 30th through November 26th" for a four-week period - * - * @since 0.7 - */ - function calendar_time_range() { - - $first_datetime = strtotime( $this->start_date ); - $first_date = date_i18n( get_option( 'date_format' ), $first_datetime ); - $total_days = ( $this->total_weeks * 7 ) - 1; - $last_datetime = strtotime( "+" . $total_days . " days", date( 'U', strtotime( $this->start_date ) ) ); - $last_date = date_i18n( get_option( 'date_format' ), $last_datetime ); - echo esc_html( sprintf( __( 'for %1$s through %2$s', 'edit-flow' ), $first_date, $last_date ) ); - } - - /** - * Check whether the current user should have the ability to modify the post - * - * @since 0.7 - * - * @param object $post The post object we're checking - * @return bool $can Whether or not the current user can modify the post - */ - function current_user_can_modify_post( $post ) { - - if ( !$post ) return false; + } - $post_type_object = get_post_type_object( $post->post_type ); - - // Editors and admins are fine - if ( current_user_can( $post_type_object->cap->edit_others_posts, $post->ID ) ) - return true; - // Authors and contributors can move their own stuff if it's not published - if ( current_user_can( $post_type_object->cap->edit_post, $post->ID ) && wp_get_current_user()->ID == $post->post_author && !in_array( $post->post_status, $this->published_statuses ) ) - return true; - // Those who can publish posts can move any of their own stuff - if ( current_user_can( $post_type_object->cap->publish_posts, $post->ID ) && wp_get_current_user()->ID == $post->post_author ) - return true; - - return false; - } - - /** - * Register settings for notifications so we can partially use the Settings API - * We use the Settings API for form generation, but not saving because we have our - * own way of handling the data. - * - * @since 0.7 - */ - function register_settings() { + /** + * Register settings for notifications so we can partially use the Settings API + * We use the Settings API for form generation, but not saving because we have our + * own way of handling the data. + * + * @since 0.7 + */ + public function register_settings() { add_settings_section( $this->module->options_group_name . '_general', false, '__return_false', $this->module->options_group_name ); add_settings_field( 'post_types', __( 'Post types to show', 'edit-flow' ), array( $this, 'settings_post_types_option' ), $this->module->options_group_name, $this->module->options_group_name . '_general' ); add_settings_field( 'quick_create_post_type', __( 'Post type to create directly from calendar', 'edit-flow' ), array( $this, 'settings_quick_create_post_type_option' ), $this->module->options_group_name, $this->module->options_group_name . '_general' ); add_settings_field( 'ics_subscription', __( 'Subscription in iCal or Google Calendar', 'edit-flow' ), array( $this, 'settings_ics_subscription_option' ), $this->module->options_group_name, $this->module->options_group_name . '_general' ); + } - } - - /** - * Choose the post types that should be displayed on the calendar - * - * @since 0.7 - */ - function settings_post_types_option() { - global $edit_flow; - $edit_flow->settings->helper_option_custom_post_type( $this->module ); - } - - /** - * Choose the post type that should be created on the calendar - * - * @since 0.8 - */ - function settings_quick_create_post_type_option() { - - $allowed_post_types = $this->get_all_post_types(); - - echo ""; - - } - - /** - * Enable calendar subscriptions via .ics in iCal or Google Calendar - * - * @since 0.8 - */ - function settings_ics_subscription_option() { - $options = array( - 'off' => __( 'Disabled', 'edit-flow' ), - 'on' => __( 'Enabled', 'edit-flow' ), - ); - echo ''; + /** + * Choose the post type that should be created on the calendar + * + * @since 0.8 + */ + public function settings_quick_create_post_type_option() { - $regenerate_url = add_query_arg( 'action', 'ef_calendar_regenerate_calendar_feed_secret', admin_url( 'index.php' ) ); - $regenerate_url = wp_nonce_url( $regenerate_url, 'ef-regenerate-ics-key' ); - echo ' ' . esc_html_e( 'Regenerate calendar feed secret', 'edit-flow' ) . ''; + $allowed_post_types = $this->get_all_post_types(); - // If our secret key doesn't exist, create a new one - if ( empty( $this->module->options->ics_secret_key ) ) - EditFlow()->update_module_option( $this->module->name, 'ics_secret_key', wp_generate_password() ); - } + echo "'; + } + + /** + * Enable calendar subscriptions via .ics in iCal or Google Calendar + * + * @since 0.8 + */ + public function settings_ics_subscription_option() { + $options = array( + 'off' => __( 'Disabled', 'edit-flow' ), + 'on' => __( 'Enabled', 'edit-flow' ), + ); + echo ''; + + + $regenerate_url = add_query_arg( 'action', 'ef_calendar_regenerate_calendar_feed_secret', admin_url( 'index.php' ) ); + $regenerate_url = wp_nonce_url( $regenerate_url, 'ef-regenerate-ics-key' ); + echo ' ' . esc_html_e( 'Regenerate calendar feed secret', 'edit-flow' ) . ''; + + // If our secret key doesn't exist, create a new one + if ( empty( $this->module->options->ics_secret_key ) ) { + EditFlow()->update_module_option( $this->module->name, 'ics_secret_key', wp_generate_password() ); + } + } - /** - * Validate the data submitted by the user in calendar settings - * - * @since 0.7 - */ - function settings_validate( $new_options ) { + /** + * Validate the data submitted by the user in calendar settings + * + * @since 0.7 + */ + public function settings_validate( $new_options ) { - $options = (array)$this->module->options; + $options = (array) $this->module->options; - $options['post_types'] = $this->clean_post_type_options( $new_options['post_types'], $this->module->post_type_support ); + $options['post_types'] = $this->clean_post_type_options( $new_options['post_types'], $this->module->post_type_support ); - if ( in_array( $new_options['quick_create_post_type'], array_keys( $this->get_all_post_types() ) ) ) - $options['quick_create_post_type'] = $new_options['quick_create_post_type']; + if ( in_array( $new_options['quick_create_post_type'], array_keys( $this->get_all_post_types() ) ) ) { + $options['quick_create_post_type'] = $new_options['quick_create_post_type']; + } - if ( 'on' != $new_options['ics_subscription'] ) - $options['ics_subscription'] = 'off'; - else - $options['ics_subscription'] = 'on'; + if ( 'on' != $new_options['ics_subscription'] ) { + $options['ics_subscription'] = 'off'; + } else { + $options['ics_subscription'] = 'on'; + } - return $options; - } + return $options; + } - /** - * Settings page for calendar - */ - function print_configure_view() { - global $edit_flow; - ?> + /** + * Settings page for calendar + */ + public function print_configure_view() { + global $edit_flow; + ?> - print_ajax_response( 'error', $this->module->messages['nonce-failed'] ); + create_post_cap ) ) - $this->print_ajax_response( 'error', $this->module->messages['invalid-permissions'] ); + /** + * Ajax callback to insert a post placeholder for a particular date + * + * @since 0.8 + */ + public function handle_ajax_insert_post() { - if ( empty( $_POST['ef_insert_date'] ) ) { - $this->print_ajax_response( 'error', __( 'No date supplied.', 'edit-flow' ) ); - } + // Nonce check! + if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'ef-calendar-modify' ) ) { + $this->print_ajax_response( 'error', $this->module->messages['nonce-failed'] ); + } - // Post type has to be visible on the calendar to create a placeholder - if ( ! in_array( $this->module->options->quick_create_post_type, $this->get_post_types_for_module( $this->module ) ) ) - $this->print_ajax_response( 'error', __( 'Please change Quick Create to use a post type viewable on the calendar.', 'edit-flow' ) ); + // Check that the user has the right capabilities to add posts to the calendar (defaults to 'edit_posts') + if ( ! current_user_can( $this->create_post_cap ) ) { + $this->print_ajax_response( 'error', $this->module->messages['invalid-permissions'] ); + } - // Sanitize post values - $post_title = isset( $_POST['ef_insert_title'] ) ? sanitize_text_field( $_POST['ef_insert_title'] ) : null; + if ( empty( $_POST['ef_insert_date'] ) ) { + $this->print_ajax_response( 'error', __( 'No date supplied.', 'edit-flow' ) ); + } - if( ! $post_title ) { - $post_title = esc_html__( 'Untitled', 'edit-flow' ); - } + // Post type has to be visible on the calendar to create a placeholder + if ( ! in_array( $this->module->options->quick_create_post_type, $this->get_post_types_for_module( $this->module ) ) ) { + $this->print_ajax_response( 'error', __( 'Please change Quick Create to use a post type viewable on the calendar.', 'edit-flow' ) ); + } - $post_date = sanitize_text_field( $_POST['ef_insert_date'] ); + // Sanitize post values + $post_title = isset( $_POST['ef_insert_title'] ) ? sanitize_text_field( $_POST['ef_insert_title'] ) : null; - $post_status = $this->get_default_post_status(); + if ( ! $post_title ) { + $post_title = esc_html__( 'Untitled', 'edit-flow' ); + } - // Set new post parameters - $post_placeholder = array( - 'post_title' => $post_title, - 'post_status' => $post_status, - 'post_date' => date( 'Y-m-d H:i:s', strtotime( $post_date ) ), - 'post_type' => $this->module->options->quick_create_post_type, - ); + $post_date = sanitize_text_field( $_POST['ef_insert_date'] ); - // By default, adding a post to the calendar won't set the timestamp. - // If the user desires that to be the behavior, they can set the result of this filter to 'true' - // With how WordPress works internally, setting 'post_date_gmt' will set the timestamp - if ( apply_filters( 'ef_calendar_allow_ajax_to_set_timestamp', false ) ) - $post_placeholder['post_date_gmt'] = date( 'Y-m-d H:i:s', strtotime( $post_date ) ); + $post_status = $this->get_default_post_status(); - // Create the post - $post_id = wp_insert_post( $post_placeholder ); + // Set new post parameters + $post_placeholder = array( + 'post_title' => $post_title, + 'post_status' => $post_status, + 'post_date' => date( 'Y-m-d H:i:s', strtotime( $post_date ) ), + 'post_type' => $this->module->options->quick_create_post_type, + ); - if( $post_id ) { // success! + // By default, adding a post to the calendar won't set the timestamp. + // If the user desires that to be the behavior, they can set the result of this filter to 'true' + // With how WordPress works internally, setting 'post_date_gmt' will set the timestamp + if ( apply_filters( 'ef_calendar_allow_ajax_to_set_timestamp', false ) ) { + $post_placeholder['post_date_gmt'] = date( 'Y-m-d H:i:s', strtotime( $post_date ) ); + } - $post = get_post( $post_id ); + // Create the post + $post_id = wp_insert_post( $post_placeholder ); - // Generate the HTML for the post item so it can be injected - $post_li_html = $this->generate_post_li_html( $post, $post_date ); + if ( $post_id ) { // success! - // announce success and send back the html to inject - $this->print_ajax_response( 'success', $post_li_html ); + $post = get_post( $post_id ); - } else { - $this->print_ajax_response( 'error', __( 'Post could not be created', 'edit-flow' ) ); - } - } - - /** - * Returns the singular label for the posts that are - * quick-created on the calendar - * - * @return str Singular label for a post-type - */ - function get_quick_create_post_type_name(){ - - $post_type_slug = $this->module->options->quick_create_post_type; - $post_type_obj = get_post_type_object( $post_type_slug ); - - return $post_type_obj->labels->singular_name ? $post_type_obj->labels->singular_name : $post_type_slug; - } - - /** - * ajax_ef_calendar_update_metadata - * Update the metadata from the calendar. - * @return string representing the overlay - * - * @since 0.8 - */ - function handle_ajax_update_metadata() { - global $wpdb; - - if ( !isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'ef-calendar-modify' ) ) { - $this->print_ajax_response( 'error', $this->module->messages['nonce-failed'] ); + // Generate the HTML for the post item so it can be injected + $post_li_html = $this->generate_post_li_html( $post, $post_date ); + + // announce success and send back the html to inject + $this->print_ajax_response( 'success', $post_li_html ); + + } else { + $this->print_ajax_response( 'error', __( 'Post could not be created', 'edit-flow' ) ); + } } - if ( !isset( $_POST['post_id'] ) ) { - $this->print_ajax_response( 'error', $this->module->messages['missing-post'] ); + /** + * Returns the singular label for the posts that are + * quick-created on the calendar + * + * @return str Singular label for a post-type + */ + public function get_quick_create_post_type_name() { + + $post_type_slug = $this->module->options->quick_create_post_type; + $post_type_obj = get_post_type_object( $post_type_slug ); + + return $post_type_obj->labels->singular_name ? $post_type_obj->labels->singular_name : $post_type_slug; } - // Check that we got a proper post - $post_id = ( int )$_POST['post_id']; - $post = get_post( $post_id ); + /** + * ajax_ef_calendar_update_metadata + * Update the metadata from the calendar. + * @return string representing the overlay + * + * @since 0.8 + */ + public function handle_ajax_update_metadata() { + global $wpdb; - if ( ! $post ) - $this->print_ajax_response( 'error', $this->module->messages['missing-post'] ); + if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( $_POST['nonce'], 'ef-calendar-modify' ) ) { + $this->print_ajax_response( 'error', $this->module->messages['nonce-failed'] ); + } + if ( ! isset( $_POST['post_id'] ) ) { + $this->print_ajax_response( 'error', $this->module->messages['missing-post'] ); + } - if( $post->post_type == 'page' ) - $edit_check = 'edit_page'; - else - $edit_check = 'edit_post'; + // Check that we got a proper post + $post_id = (int) $_POST['post_id']; + $post = get_post( $post_id ); - if ( !current_user_can( $edit_check, $post->ID ) ) - $this->print_ajax_response( 'error', $this->module->messages['invalid-permissions'] ); + if ( ! $post ) { + $this->print_ajax_response( 'error', $this->module->messages['missing-post'] ); + } - // Check that the user can modify the post - if ( ! $this->current_user_can_modify_post( $post ) ) - $this->print_ajax_response( 'error', $this->module->messages['invalid-permissions'] ); - $default_types = array( - 'author', - 'taxonomy', - ); + if ( 'page' == $post->post_type ) { + $edit_check = 'edit_page'; + } else { + $edit_check = 'edit_post'; + } - $metadata_types = array(); + if ( ! current_user_can( $edit_check, $post->ID ) ) { + $this->print_ajax_response( 'error', $this->module->messages['invalid-permissions'] ); + } - if ( !$this->module_enabled( 'editorial_metadata' ) ) - $this->print_ajax_response( 'error', $this->module->messages['update-error'] ); + // Check that the user can modify the post + if ( ! $this->current_user_can_modify_post( $post ) ) { + $this->print_ajax_response( 'error', $this->module->messages['invalid-permissions'] ); + } - $metadata_types = array_keys( EditFlow()->editorial_metadata->get_supported_metadata_types() ); + $default_types = array( + 'author', + 'taxonomy', + ); - // Update an editorial metadata field - $metadata_term = isset( $_POST['metadata_term'] ) ? $_POST['metadata_term'] : ''; - $metadata_type = isset( $_POST['metadata_type'] ) ? $_POST['metadata_type'] : ''; - $incoming_metadata_value = isset( $_POST['metadata_value'] ) ? $_POST['metadata_value'] : ''; + $metadata_types = array(); - if ( isset( $_POST['metadata_type'] ) && in_array( $_POST['metadata_type'], $metadata_types ) ) { - $post_meta_key = sanitize_text_field( '_ef_editorial_meta_' . $_POST['metadata_type'] . '_' . $metadata_term ); + if ( ! $this->module_enabled( 'editorial_metadata' ) ) { + $this->print_ajax_response( 'error', $this->module->messages['update-error'] ); + } - //Javascript date parsing is terrible, so use strtotime in php - if ( $metadata_type == 'date' ) - $metadata_value = strtotime( sanitize_text_field( $incoming_metadata_value ) ); - else - $metadata_value = sanitize_text_field( $incoming_metadata_value ); + $metadata_types = array_keys( EditFlow()->editorial_metadata->get_supported_metadata_types() ); - update_post_meta( $post->ID, $post_meta_key, $metadata_value ); - $response = 'success'; - } else { - switch( $_POST['metadata_type'] ) { - case 'taxonomy': - case 'taxonomy hierarchical': - $response = wp_set_post_terms( $post->ID, $incoming_metadata_value, $metadata_term ); - break; - default: - $response = new WP_Error( 'invalid-type', __( 'Invalid metadata type', 'edit-flow' ) ); - break; + // Update an editorial metadata field + $metadata_term = isset( $_POST['metadata_term'] ) ? $_POST['metadata_term'] : ''; + $metadata_type = isset( $_POST['metadata_type'] ) ? $_POST['metadata_type'] : ''; + $incoming_metadata_value = isset( $_POST['metadata_value'] ) ? $_POST['metadata_value'] : ''; + + if ( isset( $_POST['metadata_type'] ) && in_array( $_POST['metadata_type'], $metadata_types ) ) { + $post_meta_key = sanitize_text_field( '_ef_editorial_meta_' . $_POST['metadata_type'] . '_' . $metadata_term ); + + //Javascript date parsing is terrible, so use strtotime in php + if ( 'date' == $metadata_type ) { + $metadata_value = strtotime( sanitize_text_field( $incoming_metadata_value ) ); + } else { + $metadata_value = sanitize_text_field( $incoming_metadata_value ); + } + + update_post_meta( $post->ID, $post_meta_key, $metadata_value ); + $response = 'success'; + } else { + switch ( $_POST['metadata_type'] ) { + case 'taxonomy': + case 'taxonomy hierarchical': + $response = wp_set_post_terms( $post->ID, $incoming_metadata_value, $metadata_term ); + break; + default: + $response = new WP_Error( 'invalid-type', __( 'Invalid metadata type', 'edit-flow' ) ); + break; + } + } + + //Assuming we've got to this point, just regurgitate the value + if ( ! is_wp_error( $response ) ) { + $this->print_ajax_response( 'success', $incoming_metadata_value ); + } else { + $this->print_ajax_response( 'error', __( 'Metadata could not be updated.', 'edit-flow' ) ); } } - //Assuming we've got to this point, just regurgitate the value - if ( ! is_wp_error( $response ) ) - $this->print_ajax_response( 'success', $incoming_metadata_value ); - else - $this->print_ajax_response( 'error', __( 'Metadata could not be updated.', 'edit-flow' ) ); - } - - function calendar_filters() { - $select_filter_names = array(); - - $select_filter_names['post_status'] = 'post_status'; - $select_filter_names['cat'] = 'cat'; - $select_filter_names['author'] = 'author'; - $select_filter_names['type'] = 'cpt'; - $select_filter_name['num_weeks'] = 'num_weeks'; - - return apply_filters( 'ef_calendar_filter_names', $select_filter_names ); - } - - /** - * Sanitize a $_GET or similar filter being used on the calendar - * - * @since 0.8 - * - * @param string $key Filter being sanitized - * @param string $dirty_value Value to be sanitized - * @return string $sanitized_value Safe to use value - */ - function sanitize_filter( $key, $dirty_value ) { - - switch( $key ) { - case 'post_status': - // Whitelist-based validation for this parameter - $valid_statuses = wp_list_pluck( $this->get_calendar_post_stati(), 'name' ); - $valid_statuses[] = 'unpublish'; - - if ( in_array( $dirty_value, $valid_statuses ) ) - return $dirty_value; - else - return ''; - break; - case 'cpt': - $cpt = sanitize_key( $dirty_value ); - $supported_post_types = $this->get_post_types_for_module( $this->module ); - if ( $cpt && in_array( $cpt, $supported_post_types ) ) - return $cpt; - else - return ''; - break; - case 'start_date': - return date( 'Y-m-d', strtotime( $dirty_value ) ); + public function calendar_filters() { + $select_filter_names = array(); + + $select_filter_names['post_status'] = 'post_status'; + $select_filter_names['cat'] = 'cat'; + $select_filter_names['author'] = 'author'; + $select_filter_names['type'] = 'cpt'; + $select_filter_name['num_weeks'] = 'num_weeks'; + + return apply_filters( 'ef_calendar_filter_names', $select_filter_names ); + } + + /** + * Sanitize a $_GET or similar filter being used on the calendar + * + * @since 0.8 + * + * @param string $key Filter being sanitized + * @param string $dirty_value Value to be sanitized + * @return string $sanitized_value Safe to use value + */ + public function sanitize_filter( $key, $dirty_value ) { + + switch ( $key ) { + case 'post_status': + // Whitelist-based validation for this parameter + $valid_statuses = wp_list_pluck( $this->get_calendar_post_stati(), 'name' ); + $valid_statuses[] = 'unpublish'; + + if ( in_array( $dirty_value, $valid_statuses ) ) { + return $dirty_value; + } else { + return ''; + } + break; + case 'cpt': + $cpt = sanitize_key( $dirty_value ); + $supported_post_types = $this->get_post_types_for_module( $this->module ); + if ( $cpt && in_array( $cpt, $supported_post_types ) ) { + return $cpt; + } else { + return ''; + } + break; + case 'start_date': + return date( 'Y-m-d', strtotime( $dirty_value ) ); break; - case 'cat': - case 'author': - return intval( $dirty_value ); + case 'cat': + case 'author': + return intval( $dirty_value ); break; - case 'num_weeks': - $num_weeks = intval( $dirty_value ); - if ( $num_weeks <= 0 ) { - return $this->total_weeks; - } else if ( $num_weeks > $this->max_weeks ) { - return $this->max_weeks; - } else { - return $num_weeks; - } - default: - return false; + case 'num_weeks': + $num_weeks = intval( $dirty_value ); + if ( $num_weeks <= 0 ) { + return $this->total_weeks; + } else if ( $num_weeks > $this->max_weeks ) { + return $this->max_weeks; + } else { + return $num_weeks; + } + default: + return false; break; + } + } + + /** + * When a post is updated, clean the
---|---|---|---|---|---|