From 423a1fc03d607c2167d1bc79322d0097a0136432 Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Mon, 3 Nov 2025 13:51:59 +0100 Subject: [PATCH 1/9] add set about, contact, faq page interactive tasks --- assets/css/page-widgets/suggested-tasks.css | 101 +++++++++ assets/css/settings-page.css | 2 +- assets/js/recommendations/set-page.js | 140 ++++++++++++ classes/admin/class-page-settings.php | 82 +++++--- .../suggested-tasks/class-tasks-manager.php | 6 + .../providers/class-set-page-about.php | 55 +++++ .../providers/class-set-page-contact.php | 55 +++++ .../providers/class-set-page-faq.php | 55 +++++ .../providers/class-set-page-task.php | 199 ++++++++++++++++++ views/setting/page-select.php | 7 +- 10 files changed, 664 insertions(+), 38 deletions(-) create mode 100644 assets/js/recommendations/set-page.js create mode 100644 classes/suggested-tasks/providers/class-set-page-about.php create mode 100644 classes/suggested-tasks/providers/class-set-page-contact.php create mode 100644 classes/suggested-tasks/providers/class-set-page-faq.php create mode 100644 classes/suggested-tasks/providers/class-set-page-task.php diff --git a/assets/css/page-widgets/suggested-tasks.css b/assets/css/page-widgets/suggested-tasks.css index 4f83b716c1..4c54faf02b 100644 --- a/assets/css/page-widgets/suggested-tasks.css +++ b/assets/css/page-widgets/suggested-tasks.css @@ -495,3 +495,104 @@ } } } + +/*------------------------------------*\ + Page select setting. +\*------------------------------------*/ +.prpl-pages-item { + + &:has(input[type="radio"][value="yes"]:checked), + &:has(input[type="radio"][value="no"]:checked) { + + h3 { + + .icon-exclamation-circle { + display: block; + } + + .icon-check-circle { + display: none; + } + } + } + + &:has(option[value=""]:not(:checked)):has(input[type="radio"][value="yes"]:checked), + &:has(input[type="radio"][value="not-applicable"]:checked) { + + h3 { + + .icon-check-circle { + display: block; + } + + .icon-exclamation-circle { + display: none; + } + } + } + + .item-actions, + .prpl-select-page { + display: flex; + align-items: center; + gap: 1rem; + } + + .remind-button, + .assign-button { + + svg { + width: 1rem; + height: 1rem; + } + } + + h3 { + font-size: 1.15rem; + margin: 0; + + display: flex; + align-items: center; + gap: 0.5rem; + + .icon { + width: 1em; + height: 1em; + display: none; + } + } + + p { + margin-block-start: 0.5rem; + margin-block-end: 1rem; + } + + .radios { + margin-bottom: 1rem; + } + + .prpl-radio-wrapper { + display: flex; + justify-content: space-between; + align-items: center; + + [data-action="select"], + [data-action="create"] { + visibility: hidden; + } + + &:has(input[type="radio"]:checked) { + + [data-action="select"], + [data-action="create"] { + visibility: visible; + } + } + + &:has(input[type="radio"][value="not-applicable"]) { + padding-top: 0.25rem; + + /* Add bit height, because we dont have button or select */ + } + } +} diff --git a/assets/css/settings-page.css b/assets/css/settings-page.css index 70d431ed64..a5a1ebb46b 100644 --- a/assets/css/settings-page.css +++ b/assets/css/settings-page.css @@ -211,7 +211,7 @@ } } - .prpl-button { + .prpl-button-link { color: var(--prpl-color-gray-7); text-decoration: none; border: 1px solid var(--prpl-color-border); diff --git a/assets/js/recommendations/set-page.js b/assets/js/recommendations/set-page.js new file mode 100644 index 0000000000..fbca6755be --- /dev/null +++ b/assets/js/recommendations/set-page.js @@ -0,0 +1,140 @@ +/* global prplInteractiveTaskFormListener, progressPlanner, prplDocumentReady */ + +/* + * Set page settings (About, Contact, FAQ, etc.) + * + * Dependencies: progress-planner/recommendations/interactive-task + */ + +// Initialize custom submit handlers for all set-page tasks. +prplDocumentReady( function () { + // Find all set-page popovers. + const popovers = document.querySelectorAll( + '[id^="prpl-popover-set-page-"]' + ); + + popovers.forEach( function ( popover ) { + // Extract page name from popover ID (e.g., "prpl-popover-set-page-about" -> "about") + const popoverId = popover.id; + const match = popoverId.match( /prpl-popover-set-page-(.+)/ ); + if ( ! match ) { + return; + } + + const pageName = match[ 1 ]; + const taskId = 'set-page-' + pageName; + + // Skip if already initialized. + if ( popover.dataset.setPageInitialized ) { + return; + } + popover.dataset.setPageInitialized = 'true'; + + prplInteractiveTaskFormListener.customSubmit( { + taskId, + popoverId, + callback: () => { + return new Promise( ( resolve, reject ) => { + const pageValue = document.querySelector( + '#' + + popoverId + + ' input[name="pages[' + + pageName + + '][have_page]"]:checked' + ); + + if ( ! pageValue ) { + reject( { + success: false, + error: new Error( 'Page value not found' ), + } ); + return; + } + + const pageId = document.querySelector( + '#' + + popoverId + + ' select[name="pages[' + + pageName + + '][id]"]' + ); + + fetch( progressPlanner.ajaxUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams( { + action: 'prpl_interactive_task_submit_set-page', + nonce: progressPlanner.nonce, + have_page: pageValue.value, + id: pageId ? pageId.value : '', + task_id: taskId, + } ), + } ) + .then( ( response ) => response.json() ) + .then( ( data ) => { + if ( data.success ) { + resolve( { response: data, success: true } ); + } else { + reject( { success: false, error: data } ); + } + } ) + .catch( ( error ) => { + reject( { success: false, error } ); + } ); + } ); + }, + } ); + } ); +} ); + +const prplTogglePageSelectorSettingVisibility = function ( page, value ) { + const itemRadiosWrapperEl = document.querySelector( + `.prpl-pages-item-${ page } .radios` + ); + + if ( ! itemRadiosWrapperEl ) { + return; + } + + const selectPageWrapper = + itemRadiosWrapperEl.querySelector( '.prpl-select-page' ); + + if ( ! selectPageWrapper ) { + return; + } + + // Show only create button. + if ( 'no' === value || 'not-applicable' === value ) { + // Hide wrapper. + selectPageWrapper.style.visibility = 'visible'; + } +}; + +prplDocumentReady( function () { + document + .querySelectorAll( 'input[type="radio"][data-page]' ) + .forEach( function ( radio ) { + const page = radio.getAttribute( 'data-page' ), + value = radio.value; + + if ( radio ) { + // Show/hide the page selector setting if radio is checked. + if ( radio.checked ) { + prplTogglePageSelectorSettingVisibility( page, value ); + } + + // Add listeners for all radio buttons. + radio.addEventListener( 'change', function () { + prplTogglePageSelectorSettingVisibility( page, value ); + } ); + } + } ); +} ); diff --git a/classes/admin/class-page-settings.php b/classes/admin/class-page-settings.php index 7d7d42c8f0..4388b1d7e3 100644 --- a/classes/admin/class-page-settings.php +++ b/classes/admin/class-page-settings.php @@ -143,49 +143,65 @@ public function store_settings_form_options() { // Sanitize the pages array at point of reception. $pages = \map_deep( \wp_unslash( $_POST['pages'] ), 'sanitize_text_field' ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized - foreach ( $pages as $type => $page_args ) { - $need_page = isset( $page_args['have_page'] ) ? $page_args['have_page'] : ''; - - \progress_planner()->get_page_types()->set_no_page_needed( - $type, - 'not-applicable' === $need_page - ); - - // Remove the post-meta from the existing posts. - $existing_posts = \progress_planner()->get_page_types()->get_posts_by_type( 'any', $type ); - foreach ( $existing_posts as $post ) { - if ( $post->ID === (int) $page_args['id'] && 'no' !== $page_args['have_page'] ) { - continue; - } + $this->set_page_values( $pages ); + } - // Get the term-ID for the type. - $term = \get_term_by( 'slug', $type, Page_Types::TAXONOMY_NAME ); - if ( ! $term instanceof \WP_Term ) { - continue; - } + $this->save_settings(); + $this->save_post_types(); - // Remove the assigned terms from the `progress_planner_page_types` taxonomy. - \wp_remove_object_terms( $post->ID, $term->term_id, Page_Types::TAXONOMY_NAME ); - } + \do_action( 'progress_planner_settings_form_options_stored' ); + + \wp_send_json_success( \esc_html__( 'Options stored successfully', 'progress-planner' ) ); + } + + /** + * Set the page value. + * + * @param array $pages The pages. + * + * @return void + */ + public function set_page_values( $pages ) { - // Skip if the ID is not set. - if ( ! isset( $page_args['id'] ) || 1 > (int) $page_args['id'] ) { + if ( empty( $pages ) ) { + return; + } + + foreach ( $pages as $type => $page_args ) { + $need_page = isset( $page_args['have_page'] ) ? $page_args['have_page'] : ''; + + \progress_planner()->get_page_types()->set_no_page_needed( + $type, + 'not-applicable' === $need_page + ); + + // Remove the post-meta from the existing posts. + $existing_posts = \progress_planner()->get_page_types()->get_posts_by_type( 'any', $type ); + foreach ( $existing_posts as $post ) { + if ( $post->ID === (int) $page_args['id'] && 'no' !== $page_args['have_page'] ) { continue; } - if ( 'no' !== $page_args['have_page'] ) { - // Add the term to the `progress_planner_page_types` taxonomy. - \progress_planner()->get_page_types()->set_page_type_by_id( (int) $page_args['id'], $type ); + // Get the term-ID for the type. + $term = \get_term_by( 'slug', $type, Page_Types::TAXONOMY_NAME ); + if ( ! $term instanceof \WP_Term ) { + continue; } - } - } - $this->save_settings(); - $this->save_post_types(); + // Remove the assigned terms from the `progress_planner_page_types` taxonomy. + \wp_remove_object_terms( $post->ID, $term->term_id, Page_Types::TAXONOMY_NAME ); + } - \do_action( 'progress_planner_settings_form_options_stored' ); + // Skip if the ID is not set. + if ( ! isset( $page_args['id'] ) || 1 > (int) $page_args['id'] ) { + continue; + } - \wp_send_json_success( \esc_html__( 'Options stored successfully', 'progress-planner' ) ); + if ( 'no' !== $page_args['have_page'] ) { + // Add the term to the `progress_planner_page_types` taxonomy. + \progress_planner()->get_page_types()->set_page_type_by_id( (int) $page_args['id'], $type ); + } + } } /** diff --git a/classes/suggested-tasks/class-tasks-manager.php b/classes/suggested-tasks/class-tasks-manager.php index d20164df94..2eae76f88d 100644 --- a/classes/suggested-tasks/class-tasks-manager.php +++ b/classes/suggested-tasks/class-tasks-manager.php @@ -39,6 +39,9 @@ use Progress_Planner\Suggested_Tasks\Providers\Set_Date_Format; use Progress_Planner\Suggested_Tasks\Providers\SEO_Plugin; use Progress_Planner\Suggested_Tasks\Providers\Improve_Pdf_Handling; +use Progress_Planner\Suggested_Tasks\Providers\Set_Page_About; +use Progress_Planner\Suggested_Tasks\Providers\Set_Page_FAQ; +use Progress_Planner\Suggested_Tasks\Providers\Set_Page_Contact; /** * Tasks_Manager class. @@ -87,6 +90,9 @@ public function __construct() { new Set_Date_Format(), new SEO_Plugin(), new Improve_Pdf_Handling(), + new Set_Page_About(), + new Set_Page_FAQ(), + new Set_Page_Contact(), ]; // Add the plugin integration. diff --git a/classes/suggested-tasks/providers/class-set-page-about.php b/classes/suggested-tasks/providers/class-set-page-about.php new file mode 100644 index 0000000000..6bcceba8cb --- /dev/null +++ b/classes/suggested-tasks/providers/class-set-page-about.php @@ -0,0 +1,55 @@ +'; + \esc_html_e( 'The About page is a page that introduces your website to visitors. It is a great way to tell your story and share your mission with your audience.', 'progress-planner' ); + echo '

'; + } +} diff --git a/classes/suggested-tasks/providers/class-set-page-contact.php b/classes/suggested-tasks/providers/class-set-page-contact.php new file mode 100644 index 0000000000..d97dc4a684 --- /dev/null +++ b/classes/suggested-tasks/providers/class-set-page-contact.php @@ -0,0 +1,55 @@ +'; + \esc_html_e( 'The Contact page is a page that provides a way for your visitors to contact you. It is a great way to help your visitors get in touch with you and ask questions.', 'progress-planner' ); + echo '

'; + } +} diff --git a/classes/suggested-tasks/providers/class-set-page-faq.php b/classes/suggested-tasks/providers/class-set-page-faq.php new file mode 100644 index 0000000000..001d2d5e2c --- /dev/null +++ b/classes/suggested-tasks/providers/class-set-page-faq.php @@ -0,0 +1,55 @@ +'; + \esc_html_e( 'The FAQ page is a page that provides answers to frequently asked questions about your website. It is a great way to help your visitors find the information they need quickly and easily.', 'progress-planner' ); + echo '

'; + } +} diff --git a/classes/suggested-tasks/providers/class-set-page-task.php b/classes/suggested-tasks/providers/class-set-page-task.php new file mode 100644 index 0000000000..fe2624b8f8 --- /dev/null +++ b/classes/suggested-tasks/providers/class-set-page-task.php @@ -0,0 +1,199 @@ +get_admin__enqueue()->enqueue_script( + 'progress-planner/recommendations/set-page', + $this->get_enqueue_data() + ); + self::$script_enqueued = true; + } + } + + /** + * Check if the task condition is satisfied. + * (bool) true means that the task condition is satisfied, meaning that we don't need to add the task or task was completed. + * + * @return bool + */ + public function should_add_task() { + $pages = \progress_planner()->get_admin__page_settings()->get_settings(); + + if ( ! isset( $pages[ static::PAGE_NAME ] ) ) { + return false; + } + + return 'no' === $pages[ static::PAGE_NAME ]['isset']; + } + + /** + * Print the popover input field for the form. + * + * @return void + */ + public function print_popover_form_contents() { + $pages = \progress_planner()->get_admin__page_settings()->get_settings(); + $page = $pages[ static::PAGE_NAME ]; + + \progress_planner()->the_view( + 'setting/page-select.php', + [ + 'prpl_setting' => $page, + 'context' => 'popover', + ] + ); + $this->print_submit_button( \__( 'Set page', 'progress-planner' ) ); + } + + /** + * Handle the interactive task submit. + * + * This is only for interactive tasks that change core permalink settings. + * The $_POST data is expected to be: + * - have_page: (string) The value to update the setting to. + * - id: (int) The ID of the page to update. + * - task_id: (string) The task ID (e.g., "set-page-about") to identify the page type. + * - nonce: (string) The nonce. + * + * @return void + */ + public static function handle_interactive_task_specific_submit() { + + // Check if the user has the necessary capabilities. + if ( ! \current_user_can( 'manage_options' ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'You do not have permission to update settings.', 'progress-planner' ) ] ); + } + + // Check the nonce. + if ( ! \check_ajax_referer( 'progress_planner', 'nonce', false ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'Invalid nonce.', 'progress-planner' ) ] ); + } + + if ( ! isset( $_POST['have_page'] ) || ! isset( $_POST['task_id'] ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'Missing value.', 'progress-planner' ) ] ); + } + + $have_page = \trim( \sanitize_text_field( \wp_unslash( $_POST['have_page'] ) ) ); + $id = isset( $_POST['id'] ) ? (int) \trim( \sanitize_text_field( \wp_unslash( $_POST['id'] ) ) ) : 0; + $task_id = \trim( \sanitize_text_field( \wp_unslash( $_POST['task_id'] ) ) ); + + if ( empty( $have_page ) || empty( $task_id ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'Invalid page value.', 'progress-planner' ) ] ); + } + + // Extract page name from task ID (e.g., "set-page-about" -> "about"). + $page_name = \str_replace( 'set-page-', '', $task_id ); + if ( empty( $page_name ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'Invalid task ID.', 'progress-planner' ) ] ); + } + + // Validate page name against allowed page types. + $pages = \progress_planner()->get_admin__page_settings()->get_settings(); + if ( ! isset( $pages[ $page_name ] ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'Invalid page name.', 'progress-planner' ) ] ); + } + + // Update the page value. + \progress_planner()->get_admin__page_settings()->set_page_values( + [ + $page_name => [ + 'id' => (int) $id, + 'have_page' => $have_page, // yes, no, not-applicable. + ], + ] + ); + + \wp_send_json_success( [ 'message' => \esc_html__( 'Page updated.', 'progress-planner' ) ] ); + } + + /** + * Add task actions specific to this task. + * + * @param array $data The task data. + * @param array $actions The existing actions. + * + * @return array + */ + public function add_task_actions( $data = [], $actions = [] ) { + $actions[] = [ + 'priority' => 10, + 'html' => '' . \esc_html__( 'Set', 'progress-planner' ) . '', + ]; + + return $actions; + } +} diff --git a/views/setting/page-select.php b/views/setting/page-select.php index fbae1937d7..405b703bc0 100644 --- a/views/setting/page-select.php +++ b/views/setting/page-select.php @@ -9,13 +9,12 @@ if ( ! \defined( 'ABSPATH' ) ) { exit; } - $prpl_setting_value = isset( $prpl_setting['value'] ) ? $prpl_setting['value'] : ''; +$prpl_context = isset( $context ) ? $context : ''; // Default value for the radio button. $prpl_radio_value = '_no_page_needed' === $prpl_setting_value ? 'not-applicable' : 'no'; $prpl_radio_value = \is_numeric( $prpl_setting_value ) && 0 < $prpl_setting_value ? 'yes' : $prpl_radio_value; - ?>
From 80c42c4d17c5b15a0903de12d8fe5e34d7e75c5a Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Mon, 3 Nov 2025 15:12:48 +0100 Subject: [PATCH 2/9] convert "set valuable post types" to interactive --- assets/css/page-widgets/suggested-tasks.css | 15 ++++ .../set-valuable-post-types.js | 49 +++++++++++ classes/admin/class-page-settings.php | 18 ++-- .../class-set-valuable-post-types.php | 83 ++++++++++++++++++- 4 files changed, 157 insertions(+), 8 deletions(-) create mode 100644 assets/js/recommendations/set-valuable-post-types.js diff --git a/assets/css/page-widgets/suggested-tasks.css b/assets/css/page-widgets/suggested-tasks.css index 4c54faf02b..d1e98f70bf 100644 --- a/assets/css/page-widgets/suggested-tasks.css +++ b/assets/css/page-widgets/suggested-tasks.css @@ -596,3 +596,18 @@ } } } + +/*------------------------------------*\ + Post types selection. +\*------------------------------------*/ +.prpl-post-types-selection { + + label { + display: block; + margin-top: 0.75rem; + + &:first-child { + margin-top: 0; + } + } +} diff --git a/assets/js/recommendations/set-valuable-post-types.js b/assets/js/recommendations/set-valuable-post-types.js new file mode 100644 index 0000000000..218133b48e --- /dev/null +++ b/assets/js/recommendations/set-valuable-post-types.js @@ -0,0 +1,49 @@ +/* global prplInteractiveTaskFormListener, progressPlanner */ + +/* + * Set valuable post types. + * + * Dependencies: progress-planner/recommendations/interactive-task + */ + +prplInteractiveTaskFormListener.customSubmit( { + taskId: 'set-valuable-post-types', + popoverId: 'prpl-popover-set-valuable-post-types', + callback: () => { + return new Promise( ( resolve, reject ) => { + const postTypes = document.querySelectorAll( + '#prpl-popover-set-valuable-post-types input[name="prpl-post-types-include[]"]:checked' + ); + + if ( ! postTypes.length ) { + reject( { + success: false, + error: new Error( 'No post types selected' ), + } ); + return; + } + + const postTypesValues = Array.from( postTypes ).map( + ( type ) => type.value + ); + + fetch( progressPlanner.ajaxUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + body: new URLSearchParams( { + action: 'prpl_interactive_task_submit_set-valuable-post-types', + nonce: progressPlanner.nonce, + 'prpl-post-types-include': postTypesValues, + } ), + } ) + .then( ( response ) => { + resolve( { response, success: true } ); + } ) + .catch( ( error ) => { + reject( { success: false, error } ); + } ); + } ); + }, +} ); diff --git a/classes/admin/class-page-settings.php b/classes/admin/class-page-settings.php index 4388b1d7e3..052e846aa6 100644 --- a/classes/admin/class-page-settings.php +++ b/classes/admin/class-page-settings.php @@ -146,8 +146,14 @@ public function store_settings_form_options() { $this->set_page_values( $pages ); } + // It's possible that none of the post types are selected, so we need to handle that case. + $post_types = isset( $_POST['prpl-post-types-include'] ) + ? \array_map( 'sanitize_text_field', \wp_unslash( $_POST['prpl-post-types-include'] ) ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing + : []; + + $this->save_post_types( $post_types ); + $this->save_settings(); - $this->save_post_types(); \do_action( 'progress_planner_settings_form_options_stored' ); @@ -221,13 +227,13 @@ public function save_settings() { /** * Save the post types. * + * @param array $post_types The post types. + * * @return void */ - public function save_post_types() { - // Nonce is already checked in store_settings_form_options() which calls this method. - $include_post_types = isset( $_POST['prpl-post-types-include'] ) // phpcs:ignore WordPress.Security.NonceVerification.Missing - ? \array_map( 'sanitize_text_field', \wp_unslash( $_POST['prpl-post-types-include'] ) ) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing - // If no post types are selected, use the default post types (post and page can be deregistered). + public function save_post_types( $post_types = [] ) { + $include_post_types = ! empty( $post_types ) + ? $post_types : \array_intersect( [ 'post', 'page' ], \progress_planner()->get_settings()->get_public_post_types() ); \progress_planner()->get_settings()->set( 'include_post_types', $include_post_types ); diff --git a/classes/suggested-tasks/providers/class-set-valuable-post-types.php b/classes/suggested-tasks/providers/class-set-valuable-post-types.php index b93aea3054..da40622055 100644 --- a/classes/suggested-tasks/providers/class-set-valuable-post-types.php +++ b/classes/suggested-tasks/providers/class-set-valuable-post-types.php @@ -10,7 +10,7 @@ /** * Add tasks for settings saved. */ -class Set_Valuable_Post_Types extends Tasks { +class Set_Valuable_Post_Types extends Tasks_Interactive { /** * The provider ID. @@ -19,6 +19,13 @@ class Set_Valuable_Post_Types extends Tasks { */ protected const PROVIDER_ID = 'set-valuable-post-types'; + /** + * The provider ID. + * + * @var string + */ + public const POPOVER_ID = 'set-valuable-post-types'; + /** * Whether the task is an onboarding task. * @@ -56,6 +63,7 @@ protected function get_url() { */ public function init() { \add_action( 'progress_planner_settings_form_options_stored', [ $this, 'remove_upgrade_option' ] ); + \add_action( 'wp_ajax_prpl_interactive_task_submit_set-valuable-post-types', [ $this, 'handle_interactive_task_specific_submit' ] ); } /** @@ -119,6 +127,77 @@ public function is_task_completed( $task_id = '' ) { return false === \get_option( 'progress_planner_set_valuable_post_types', false ); } + /** + * Handle the interactive task submit. + * + * @return void + */ + public function handle_interactive_task_specific_submit() { + // Check if the user has the necessary capabilities. + if ( ! \current_user_can( 'manage_options' ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'You do not have permission to update settings.', 'progress-planner' ) ] ); + } + + // Check the nonce. + if ( ! \check_ajax_referer( 'progress_planner', 'nonce', false ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'Invalid nonce.', 'progress-planner' ) ] ); + } + + if ( ! isset( $_POST['prpl-post-types-include'] ) ) { + \wp_send_json_error( [ 'message' => \esc_html__( 'Missing post types.', 'progress-planner' ) ] ); + } + + $post_types = \wp_unslash( $_POST['prpl-post-types-include'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- array elements are sanitized below. + $post_types = explode( ',', $post_types ); + $post_types = array_map( 'sanitize_text_field', $post_types ); + + \progress_planner()->get_admin__page_settings()->save_post_types( $post_types ); + + \wp_send_json_success( [ 'message' => \esc_html__( 'Setting updated.', 'progress-planner' ) ] ); + } + + /** + * Print the popover instructions. + * + * @return void + */ + public function print_popover_instructions() { + echo '

'; + \esc_html_e( 'You\'re in control of what counts as valuable content. We\'ll track and reward activity only for the post types you select here.', 'progress-planner' ); + echo '

'; + } + + /** + * Print the popover form contents. + * + * @return void + */ + public function print_popover_form_contents() { + $prpl_saved_settings = \progress_planner()->get_settings()->get_post_types_names(); + $prpl_post_types = \progress_planner()->get_settings()->get_public_post_types(); + + // Early exit if there are no public post types. + if ( empty( $prpl_post_types ) ) { + return; + } + ?> +
+ + + +
+ print_submit_button( \__( 'Set', 'progress-planner' ) ); + } + /** * Add task actions specific to this task. * @@ -130,7 +209,7 @@ public function is_task_completed( $task_id = '' ) { public function add_task_actions( $data = [], $actions = [] ) { $actions[] = [ 'priority' => 10, - 'html' => '
' . \esc_html__( 'Go to the settings page', 'progress-planner' ) . '', + 'html' => '' . \esc_html__( 'Set', 'progress-planner' ) . '', ]; return $actions; From 59fbffe7745a3d5bb08635350daed96c637a2b7f Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Mon, 3 Nov 2025 16:50:51 +0100 Subject: [PATCH 3/9] add task when new public post type is detected --- .../class-set-valuable-post-types.php | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/classes/suggested-tasks/providers/class-set-valuable-post-types.php b/classes/suggested-tasks/providers/class-set-valuable-post-types.php index da40622055..e54233acb4 100644 --- a/classes/suggested-tasks/providers/class-set-valuable-post-types.php +++ b/classes/suggested-tasks/providers/class-set-valuable-post-types.php @@ -64,6 +64,58 @@ protected function get_url() { public function init() { \add_action( 'progress_planner_settings_form_options_stored', [ $this, 'remove_upgrade_option' ] ); \add_action( 'wp_ajax_prpl_interactive_task_submit_set-valuable-post-types', [ $this, 'handle_interactive_task_specific_submit' ] ); + + // On late init hook we need to check if the public post types are changed. + \add_action( 'init', [ $this, 'check_public_post_types' ], PHP_INT_MAX - 1 ); + } + + /** + * Check if the public post types are changed. + * + * @return void + */ + public function check_public_post_types() { + $previosly_set_public_post_types = \array_unique( \get_option( 'progress_planner_public_post_types', [] ) ); + $public_post_types = \array_unique( \progress_planner()->get_settings()->get_public_post_types() ); + + // Sort the public post types. + \sort( $previosly_set_public_post_types ); + \sort( $public_post_types ); + + // Compare the previously set public post types with the current public post types. + if ( $previosly_set_public_post_types === $public_post_types ) { + return; + } + + // Update the previously set public post types. + \update_option( 'progress_planner_public_post_types', $public_post_types ); + + // Exit if post type was removed, or it is not public anymore, since the user will not to able to make different selection. + if ( count( $public_post_types ) < count( $previosly_set_public_post_types ) ) { + return; + } + + // If we're here that means that there is new public post type. + + // Check if the task exists, if it does and it is published do nothing. + $task = \progress_planner()->get_suggested_tasks_db()->get_tasks_by( [ 'provider_id' => static::PROVIDER_ID ] ); + if ( ! empty( $task ) && 'publish' === $task[0]->post_status ) { + return; + } + + // If it is trashed, change it's status to publish. + if ( ! empty( $task ) && 'trash' === $task[0]->post_status ) { + \wp_update_post( + [ + 'ID' => $task[0]->ID, + 'post_status' => 'publish', + ] + ); + return; + } + + // If we're here then we need to add it. + \progress_planner()->get_suggested_tasks_db()->add( $this->modify_injection_task_data( $this->get_task_details() ) ); } /** From 1749352ed3f753389133db440c3852e061e6be86 Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Thu, 4 Dec 2025 15:37:12 +0100 Subject: [PATCH 4/9] set homepage --- .../suggested-tasks/class-tasks-manager.php | 2 + .../providers/class-set-page-home.php | 55 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 classes/suggested-tasks/providers/class-set-page-home.php diff --git a/classes/suggested-tasks/class-tasks-manager.php b/classes/suggested-tasks/class-tasks-manager.php index 2eae76f88d..77edc5e0ca 100644 --- a/classes/suggested-tasks/class-tasks-manager.php +++ b/classes/suggested-tasks/class-tasks-manager.php @@ -39,6 +39,7 @@ use Progress_Planner\Suggested_Tasks\Providers\Set_Date_Format; use Progress_Planner\Suggested_Tasks\Providers\SEO_Plugin; use Progress_Planner\Suggested_Tasks\Providers\Improve_Pdf_Handling; +use Progress_Planner\Suggested_Tasks\Providers\Set_Page_Home; use Progress_Planner\Suggested_Tasks\Providers\Set_Page_About; use Progress_Planner\Suggested_Tasks\Providers\Set_Page_FAQ; use Progress_Planner\Suggested_Tasks\Providers\Set_Page_Contact; @@ -90,6 +91,7 @@ public function __construct() { new Set_Date_Format(), new SEO_Plugin(), new Improve_Pdf_Handling(), + new Set_Page_Home(), new Set_Page_About(), new Set_Page_FAQ(), new Set_Page_Contact(), diff --git a/classes/suggested-tasks/providers/class-set-page-home.php b/classes/suggested-tasks/providers/class-set-page-home.php new file mode 100644 index 0000000000..4c2955076b --- /dev/null +++ b/classes/suggested-tasks/providers/class-set-page-home.php @@ -0,0 +1,55 @@ +'; + \esc_html_e( 'The Home page is the first page that visitors see when they visit your website. It is a great way to welcome your visitors and introduce your website to them.', 'progress-planner' ); + echo '

'; + } +} From a6fc827afbc4e82d34a5316ce8248ae75cd5d0f5 Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Fri, 12 Dec 2025 15:39:20 +0100 Subject: [PATCH 5/9] no need for "Set Home Page" task, we detect that correctly --- .../suggested-tasks/class-tasks-manager.php | 2 - .../providers/class-set-page-home.php | 55 ------------------- 2 files changed, 57 deletions(-) delete mode 100644 classes/suggested-tasks/providers/class-set-page-home.php diff --git a/classes/suggested-tasks/class-tasks-manager.php b/classes/suggested-tasks/class-tasks-manager.php index 5c76a96f82..1a9082ed89 100644 --- a/classes/suggested-tasks/class-tasks-manager.php +++ b/classes/suggested-tasks/class-tasks-manager.php @@ -40,7 +40,6 @@ use Progress_Planner\Suggested_Tasks\Providers\Set_Date_Format; use Progress_Planner\Suggested_Tasks\Providers\SEO_Plugin; use Progress_Planner\Suggested_Tasks\Providers\Improve_Pdf_Handling; -use Progress_Planner\Suggested_Tasks\Providers\Set_Page_Home; use Progress_Planner\Suggested_Tasks\Providers\Set_Page_About; use Progress_Planner\Suggested_Tasks\Providers\Set_Page_FAQ; use Progress_Planner\Suggested_Tasks\Providers\Set_Page_Contact; @@ -93,7 +92,6 @@ public function __construct() { new Set_Date_Format(), new SEO_Plugin(), new Improve_Pdf_Handling(), - new Set_Page_Home(), new Set_Page_About(), new Set_Page_FAQ(), new Set_Page_Contact(), diff --git a/classes/suggested-tasks/providers/class-set-page-home.php b/classes/suggested-tasks/providers/class-set-page-home.php deleted file mode 100644 index 4c2955076b..0000000000 --- a/classes/suggested-tasks/providers/class-set-page-home.php +++ /dev/null @@ -1,55 +0,0 @@ -'; - \esc_html_e( 'The Home page is the first page that visitors see when they visit your website. It is a great way to welcome your visitors and introduce your website to them.', 'progress-planner' ); - echo '

'; - } -} From 2d213f0e75e37516c16e844ebc7a762e9ecbf09c Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Fri, 12 Dec 2025 15:44:23 +0100 Subject: [PATCH 6/9] tweak page description --- classes/suggested-tasks/providers/class-set-page-about.php | 5 ++++- classes/suggested-tasks/providers/class-set-page-contact.php | 5 ++++- classes/suggested-tasks/providers/class-set-page-faq.php | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/classes/suggested-tasks/providers/class-set-page-about.php b/classes/suggested-tasks/providers/class-set-page-about.php index 6bcceba8cb..738614233a 100644 --- a/classes/suggested-tasks/providers/class-set-page-about.php +++ b/classes/suggested-tasks/providers/class-set-page-about.php @@ -49,7 +49,10 @@ protected function get_title() { */ public function print_popover_instructions() { echo '

'; - \esc_html_e( 'The About page is a page that introduces your website to visitors. It is a great way to tell your story and share your mission with your audience.', 'progress-planner' ); + \esc_html_e( 'Your About page tells visitors who you are, what you do, and why they should trust you. Share your story, mission, and the people behind your website to build a personal connection with your audience.', 'progress-planner' ); + echo '

'; + echo '

'; + \esc_html_e( 'You can set this page from the Sidebar on the Page Edit screen.', 'progress-planner' ); echo '

'; } } diff --git a/classes/suggested-tasks/providers/class-set-page-contact.php b/classes/suggested-tasks/providers/class-set-page-contact.php index d97dc4a684..ab4b743182 100644 --- a/classes/suggested-tasks/providers/class-set-page-contact.php +++ b/classes/suggested-tasks/providers/class-set-page-contact.php @@ -49,7 +49,10 @@ protected function get_title() { */ public function print_popover_instructions() { echo '

'; - \esc_html_e( 'The Contact page is a page that provides a way for your visitors to contact you. It is a great way to help your visitors get in touch with you and ask questions.', 'progress-planner' ); + \esc_html_e( 'A Contact page makes it easy for visitors, customers, and potential partners to reach you. Include a contact form, email address, or other ways people can get in touch.', 'progress-planner' ); + echo '

'; + echo '

'; + \esc_html_e( 'You can set this page from the Sidebar on the Page Edit screen.', 'progress-planner' ); echo '

'; } } diff --git a/classes/suggested-tasks/providers/class-set-page-faq.php b/classes/suggested-tasks/providers/class-set-page-faq.php index 001d2d5e2c..dca2f3e355 100644 --- a/classes/suggested-tasks/providers/class-set-page-faq.php +++ b/classes/suggested-tasks/providers/class-set-page-faq.php @@ -49,7 +49,10 @@ protected function get_title() { */ public function print_popover_instructions() { echo '

'; - \esc_html_e( 'The FAQ page is a page that provides answers to frequently asked questions about your website. It is a great way to help your visitors find the information they need quickly and easily.', 'progress-planner' ); + \esc_html_e( 'An FAQ page answers common questions your visitors have, saving them time and reducing your support workload. It also helps with SEO by targeting long-tail search queries.', 'progress-planner' ); + echo '

'; + echo '

'; + \esc_html_e( 'You can set this page from the Sidebar on the Page Edit screen.', 'progress-planner' ); echo '

'; } } From 9e20cd40810a62f547ada00e40275ff591802dba Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Fri, 12 Dec 2025 15:50:20 +0100 Subject: [PATCH 7/9] adjust description again --- classes/suggested-tasks/providers/class-set-page-about.php | 2 +- classes/suggested-tasks/providers/class-set-page-contact.php | 2 +- classes/suggested-tasks/providers/class-set-page-faq.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/classes/suggested-tasks/providers/class-set-page-about.php b/classes/suggested-tasks/providers/class-set-page-about.php index 738614233a..fbf9aa8d66 100644 --- a/classes/suggested-tasks/providers/class-set-page-about.php +++ b/classes/suggested-tasks/providers/class-set-page-about.php @@ -49,7 +49,7 @@ protected function get_title() { */ public function print_popover_instructions() { echo '

'; - \esc_html_e( 'Your About page tells visitors who you are, what you do, and why they should trust you. Share your story, mission, and the people behind your website to build a personal connection with your audience.', 'progress-planner' ); + \esc_html_e( 'Your About page tells your story. It tells your visitors who you are, what your business is, and why your website exists. It humanizes your business by telling visitors about yourself and your team.', 'progress-planner' ); echo '

'; echo '

'; \esc_html_e( 'You can set this page from the Sidebar on the Page Edit screen.', 'progress-planner' ); diff --git a/classes/suggested-tasks/providers/class-set-page-contact.php b/classes/suggested-tasks/providers/class-set-page-contact.php index ab4b743182..235a0e6fc7 100644 --- a/classes/suggested-tasks/providers/class-set-page-contact.php +++ b/classes/suggested-tasks/providers/class-set-page-contact.php @@ -49,7 +49,7 @@ protected function get_title() { */ public function print_popover_instructions() { echo '

'; - \esc_html_e( 'A Contact page makes it easy for visitors, customers, and potential partners to reach you. Include a contact form, email address, or other ways people can get in touch.', 'progress-planner' ); + \esc_html_e( 'A strong contact page is essential for capturing leads and enhancing customer service.', 'progress-planner' ); echo '

'; echo '

'; \esc_html_e( 'You can set this page from the Sidebar on the Page Edit screen.', 'progress-planner' ); diff --git a/classes/suggested-tasks/providers/class-set-page-faq.php b/classes/suggested-tasks/providers/class-set-page-faq.php index dca2f3e355..20f0e1c336 100644 --- a/classes/suggested-tasks/providers/class-set-page-faq.php +++ b/classes/suggested-tasks/providers/class-set-page-faq.php @@ -49,7 +49,7 @@ protected function get_title() { */ public function print_popover_instructions() { echo '

'; - \esc_html_e( 'An FAQ page answers common questions your visitors have, saving them time and reducing your support workload. It also helps with SEO by targeting long-tail search queries.', 'progress-planner' ); + \esc_html_e( 'An FAQ page is essential for quickly answering your visitors’ most common questions. It’s beneficial for e-commerce sites, where customers frequently have questions about products, orders, and return policies.', 'progress-planner' ); echo '

'; echo '

'; \esc_html_e( 'You can set this page from the Sidebar on the Page Edit screen.', 'progress-planner' ); From a848013a37bcb96b4c7a57b360489a3f799e974e Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Mon, 15 Dec 2025 11:56:55 +0100 Subject: [PATCH 8/9] remove "Settings page" & "Fill in the settings page" task --- assets/css/settings-page.css | 350 ------------------ assets/js/settings-page.js | 94 ----- classes/admin/class-enqueue.php | 4 +- classes/admin/class-page-settings.php | 74 ---- classes/admin/class-page.php | 21 +- .../suggested-tasks/class-tasks-manager.php | 2 - .../providers/class-set-page-task.php | 2 +- .../class-set-valuable-post-types.php | 21 -- .../providers/class-settings-saved.php | 88 ----- classes/utils/class-deprecations.php | 1 - .../test-class-admin-page-settings.php | 46 --- tests/phpunit/test-class-disable-comments.php | 2 +- .../test-class-search-engine-visibility.php | 2 +- tests/phpunit/test-class-settings-saved.php | 32 -- views/admin-page-settings.php | 48 --- views/page-settings/pages.php | 33 -- views/page-settings/post-types.php | 52 --- views/page-settings/settings.php | 38 -- 18 files changed, 6 insertions(+), 904 deletions(-) delete mode 100644 assets/css/settings-page.css delete mode 100644 assets/js/settings-page.js delete mode 100644 classes/suggested-tasks/providers/class-settings-saved.php delete mode 100644 tests/phpunit/test-class-settings-saved.php delete mode 100644 views/admin-page-settings.php delete mode 100644 views/page-settings/pages.php delete mode 100644 views/page-settings/post-types.php delete mode 100644 views/page-settings/settings.php diff --git a/assets/css/settings-page.css b/assets/css/settings-page.css deleted file mode 100644 index a5a1ebb46b..0000000000 --- a/assets/css/settings-page.css +++ /dev/null @@ -1,350 +0,0 @@ -/* stylelint-disable-next-line selector-class-pattern */ -.progress-planner_page_progress-planner-settings { - - #wpwrap { - background-color: var(--prpl-background); - } - - ul#adminmenu { - - a.wp-has-current-submenu, - > li.current > a.current { - - &::after { - border-right-color: var(--prpl-background) !important; - } - } - } - - .prpl-settings-wrap { - - h1 { - display: flex; - align-items: center; - padding: 1.2rem; - margin-bottom: 2rem; - - span { - font-weight: 600; - } - } - - #prpl-settings { - - .prpl-widget-wrapper { - padding: var(--prpl-settings-page-gap) var(--prpl-settings-page-gap) 2rem var(--prpl-settings-page-gap); - } - } - } - - .prpl-settings-form-wrap { - background-color: var(--prpl-background-paper); - - border: 1px solid var(--prpl-color-border); - border-radius: var(--prpl-border-radius); - padding: var(--prpl-padding); - box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.07), -2px 0 6px rgba(0, 0, 0, 0.07); - } - - .prpl-settings-section-wrapper { - border: 1px solid var(--prpl-color-border); - border-radius: var(--prpl-border-radius); - padding: var(--prpl-padding); - flex-grow: 1; - } - - .prpl-settings-section-title { - display: flex; - align-items: center; - gap: 0.5rem; - background: var(--prpl-background-monthly); - padding: 1.2rem; - border-radius: 0.5rem; - margin-bottom: var(--prpl-padding); - - &:first-child { - margin-top: 0; - } - - .icon { - width: 1.25em; - height: 1.25em; - } - } - - .prpl-pages-item { - border: 1px solid var(--prpl-color-border); - border-radius: var(--prpl-border-radius); - padding: var(--prpl-padding); - flex-grow: 1; - width: 45%; - - &:has(input[type="radio"][value="yes"]:checked), - &:has(input[type="radio"][value="no"]:checked) { - - h3 { - - .icon-exclamation-circle { - display: block; - } - - .icon-check-circle { - display: none; - } - } - } - - &:has(option[value=""]:not(:checked)):has(input[type="radio"][value="yes"]:checked), - &:has(input[type="radio"][value="not-applicable"]:checked) { - - h3 { - - .icon-check-circle { - display: block; - } - - .icon-exclamation-circle { - display: none; - } - } - } - - .item-actions, - .prpl-select-page { - display: flex; - align-items: center; - gap: 1rem; - } - - .remind-button, - .assign-button { - - svg { - width: 1rem; - height: 1rem; - } - } - - h3 { - font-size: 1.15rem; - margin: 0; - - display: flex; - align-items: center; - gap: 0.5rem; - - .icon { - width: 1em; - height: 1em; - display: none; - } - } - - p { - margin-block-start: 0.5rem; - margin-block-end: 1rem; - } - - .radios { - margin-bottom: 1rem; - } - - .prpl-radio-wrapper { - display: flex; - justify-content: space-between; - align-items: center; - - [data-action="select"], - [data-action="create"] { - visibility: hidden; - } - - &:has(input[type="radio"]:checked) { - - [data-action="select"], - [data-action="create"] { - visibility: visible; - } - } - - &:has(input[type="radio"][value="not-applicable"]) { - padding-top: 0.25rem; /* Add bit height, because we dont have button or select */ - } - } - } - - .prpl-column-pages { - margin-bottom: var(--prpl-gap); - - .prpl-settings-section-title { - background: var(--prpl-background-setting-pages); - - .icon { - - path { - fill: var(--prpl-color-setting-pages-icon); - } - } - } - } - - .prpl-pages-list { - display: flex; - flex-wrap: wrap; - gap: var(--prpl-settings-page-gap); - - .item-description { - - h3 { - margin-bottom: 2rem; - } - - & > p { - display: none; - } - } - - .radios { - display: flex; - flex-direction: column; - gap: 0.5rem; - } - } - - .prpl-button-link { - color: var(--prpl-color-gray-7); - text-decoration: none; - border: 1px solid var(--prpl-color-border); - border-radius: var(--prpl-border-radius); - padding: 0.5em 0.5em; - font-size: 14px; /* It matches wrapper. - itemRadiosWrapperEl.querySelector( - '.prpl-select-page' - ).style.visibility = 'hidden'; - } - - // Show only select and edit button. - if ( 'yes' === value ) { - // Show - /> - labels->name ); // @phpstan-ignore-line property.nonObject ?> - - -

- - diff --git a/views/page-settings/settings.php b/views/page-settings/settings.php deleted file mode 100644 index 928c1149d6..0000000000 --- a/views/page-settings/settings.php +++ /dev/null @@ -1,38 +0,0 @@ - - -
-

- - the_asset( 'images/icon_user.svg' ); ?> - - - - -

-
-
- -
-
-
From 19f6490dfcf54d87823545a63f9ab8b97ba75cb1 Mon Sep 17 00:00:00 2001 From: Ari Stathopoulos Date: Mon, 15 Dec 2025 13:44:22 +0200 Subject: [PATCH 9/9] fix failing tests --- tests/phpunit/test-class-suggested-tasks.php | 12 ++++++------ tests/phpunit/test-class-upgrade-migration-130.php | 1 - 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/phpunit/test-class-suggested-tasks.php b/tests/phpunit/test-class-suggested-tasks.php index 7605cc30f8..a13eef01d0 100644 --- a/tests/phpunit/test-class-suggested-tasks.php +++ b/tests/phpunit/test-class-suggested-tasks.php @@ -42,19 +42,19 @@ public function test_task_cleanup() { 'provider_id' => 'update-core', ], [ - 'post_title' => 'settings-saved-' . \gmdate( 'YW' ), - 'task_id' => 'settings-saved-' . \gmdate( 'YW' ), + 'post_title' => 'core-siteicon-' . \gmdate( 'YW' ), + 'task_id' => 'core-siteicon-' . \gmdate( 'YW' ), 'date' => \gmdate( 'YW' ), - 'provider_id' => 'settings-saved', + 'provider_id' => 'core-siteicon', 'category' => 'configuration', ], // Not repetitive task, but with past date. [ - 'post_title' => 'settings-saved-202451', - 'task_id' => 'settings-saved-202451', + 'post_title' => 'core-siteicon-202451', + 'task_id' => 'core-siteicon-202451', 'date' => '202451', - 'provider_id' => 'settings-saved', + 'provider_id' => 'core-siteicon', 'category' => 'configuration', ], diff --git a/tests/phpunit/test-class-upgrade-migration-130.php b/tests/phpunit/test-class-upgrade-migration-130.php index 2a784fe9df..76f76065a8 100644 --- a/tests/phpunit/test-class-upgrade-migration-130.php +++ b/tests/phpunit/test-class-upgrade-migration-130.php @@ -47,7 +47,6 @@ public function test_recreating_tasks_from_activities() { 'review-post-2810-202517', 'review-post-4467-202517', 'update-core-202401', - 'settings-saved-202501', 'review-post-4530-202517', 'review-post-4477-202517', 'review-post-4569-202517',