From f3f79a7c82ed3ec3231a5ba36238095508733fc0 Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Fri, 30 Jan 2026 15:56:46 +0100 Subject: [PATCH 1/5] WIP --- classes/class-badges.php | 45 +++++++++++++++++++++++++++---- views/popovers/badge-streak.php | 1 + views/popovers/monthly-badges.php | 1 + 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/classes/class-badges.php b/classes/class-badges.php index 904c94f0b..2c751ba63 100644 --- a/classes/class-badges.php +++ b/classes/class-badges.php @@ -83,7 +83,19 @@ public function __construct() { * @return \Progress_Planner\Badges\Badge[] */ public function get_badges( $context ) { - return isset( $this->$context ) ? $this->$context : []; + // phpcs:ignore Generic.Commenting.DocComment.MissingShort -- Inline @var for PHPStan. + /** @var \Progress_Planner\Badges\Badge[] $badges */ + $badges = isset( $this->$context ) ? $this->$context : []; + + /** + * Filter the badges for a context. + * + * @param \Progress_Planner\Badges\Badge[] $badges The badges. + * @param string $context The badges context. + * + * @return \Progress_Planner\Badges\Badge[] + */ + return \apply_filters( 'progress_planner_badges', $badges, $context ); } /** @@ -101,7 +113,18 @@ public function get_badge( $badge_id ) { } } } - return null; + + /** + * Filter for retrieving a single badge by ID. + * + * Allows external plugins to provide custom badges. + * + * @param \Progress_Planner\Badges\Badge|null $badge The badge object or null. + * @param string $badge_id The badge ID. + * + * @return \Progress_Planner\Badges\Badge|null + */ + return \apply_filters( 'progress_planner_get_badge', null, $badge_id ); } /** @@ -183,6 +206,8 @@ public function get_latest_completed_badge() { // Get the settings for badges (stores completion dates). $settings = \progress_planner()->get_settings()->get( 'badges', [] ); + // phpcs:ignore Generic.Commenting.DocComment.MissingShort -- Inline @var for PHPStan. + /** @var string|null $latest_date */ $latest_date = null; // Loop through all badge contexts to find the most recently completed badge. @@ -204,20 +229,30 @@ public function get_latest_completed_badge() { if ( null === $latest_date ) { $this->latest_completed_badge = $badge; if ( isset( $settings[ $badge->get_id() ]['date'] ) ) { - $latest_date = $settings[ $badge->get_id() ]['date']; + $latest_date = (string) $settings[ $badge->get_id() ]['date']; } continue; } // Compare completion dates as Unix timestamps to find the most recent. // Using >= ensures that if multiple badges complete simultaneously, the last one processed wins. - if ( \DateTime::createFromFormat( 'Y-m-d H:i:s', $settings[ $badge->get_id() ]['date'] )->format( 'U' ) >= \DateTime::createFromFormat( 'Y-m-d H:i:s', $latest_date )->format( 'U' ) ) { - $latest_date = $settings[ $badge->get_id() ]['date']; + if ( \DateTime::createFromFormat( 'Y-m-d H:i:s', (string) $settings[ $badge->get_id() ]['date'] )->format( 'U' ) >= \DateTime::createFromFormat( 'Y-m-d H:i:s', $latest_date )->format( 'U' ) ) { + $latest_date = (string) $settings[ $badge->get_id() ]['date']; $this->latest_completed_badge = $badge; } } } + /** + * Filter the latest completed badge. + * + * @param \Progress_Planner\Badges\Badge|null $badge The latest completed badge. + * @param string|null $latest_date The latest completion date. + * + * @return \Progress_Planner\Badges\Badge|null + */ + $this->latest_completed_badge = \apply_filters( 'progress_planner_latest_completed_badge', $this->latest_completed_badge, $latest_date ); + return $this->latest_completed_badge; } } diff --git a/views/popovers/badge-streak.php b/views/popovers/badge-streak.php index 90e1d9853..dbeabeca0 100644 --- a/views/popovers/badge-streak.php +++ b/views/popovers/badge-streak.php @@ -33,6 +33,7 @@ the_view( 'popovers/parts/badge-streak-progressbar.php', [ 'prpl_context' => 'content' ] ); ?> + + From da6457a6abf6b226bf46438b8fbebd272763c428 Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Thu, 5 Feb 2026 12:57:13 +0100 Subject: [PATCH 2/5] filter out the activity category --- classes/class-suggested-tasks.php | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/classes/class-suggested-tasks.php b/classes/class-suggested-tasks.php index e25fc6036..d88ff5dc7 100644 --- a/classes/class-suggested-tasks.php +++ b/classes/class-suggested-tasks.php @@ -100,12 +100,25 @@ public function init(): void { * @return void */ public function insert_activity( string $task_id ): void { + /** + * Filter the activity category for a completed task. + * + * Allows customizing the category used when recording task completion activities. + * For example, onboarding tasks may use 'onboarding_task' instead of 'suggested_task' + * to exclude them from monthly badge calculations. + * + * @param string $category The activity category (default: 'suggested_task'). + * @param string $task_id The task ID being completed. + */ + $category = \apply_filters( 'progress_planner_task_activity_category', 'suggested_task', $task_id ); + // Insert an activity. - $activity = new Suggested_Task_Activity(); - $activity->type = 'completed'; - $activity->data_id = (string) $task_id; - $activity->date = new \DateTime(); - $activity->user_id = \get_current_user_id(); + $activity = new Suggested_Task_Activity(); + $activity->category = $category; + $activity->type = 'completed'; + $activity->data_id = (string) $task_id; + $activity->date = new \DateTime(); + $activity->user_id = \get_current_user_id(); $activity->save(); // Allow other classes to react to the completion of a suggested task. From af4cf920f15588123c1b491e12787997e3ac93b0 Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Thu, 5 Feb 2026 15:11:59 +0100 Subject: [PATCH 3/5] tweak when onboarding tasks should show, pp-hosts compat --- classes/class-plugin-upgrade-tasks.php | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/classes/class-plugin-upgrade-tasks.php b/classes/class-plugin-upgrade-tasks.php index a111e7906..f1762c9d6 100644 --- a/classes/class-plugin-upgrade-tasks.php +++ b/classes/class-plugin-upgrade-tasks.php @@ -104,14 +104,26 @@ protected function add_initial_onboarding_tasks() { public function maybe_add_onboarding_tasks() { $onboard_task_provider_ids = \apply_filters( 'prpl_onboarding_task_providers', [] ); - // Privacy policy is not accepted, so it's a fresh install. - $fresh_install = ! \progress_planner()->is_privacy_policy_accepted(); - - // Check if task providers option exists, it will not on fresh installs and v1.0.4 and older. - $old_task_providers = \get_option( 'progress_planner_previous_version_task_providers', [] ); + // Check if task providers option exists. If not, it's either a fresh install or upgrading from v1.0.4 or older. + $old_task_providers = \get_option( 'progress_planner_previous_version_task_providers', [] ); + $task_providers_option_set = false !== \get_option( 'progress_planner_previous_version_task_providers', false ); + + // Fresh install detection: + // - Option doesn't exist AND privacy policy not yet accepted (standalone fresh install) + // - Option doesn't exist AND running as branded/hosted version (pp-hosts fresh install, privacy auto-accepted) + // In these cases, save current providers as baseline without showing upgrade popover. + if ( ! $task_providers_option_set ) { + $is_branded_version = \defined( 'PROGRESS_PLANNER_BRANDING_ID' ); + $is_privacy_policy_pending = ! \progress_planner()->is_privacy_policy_accepted(); + + if ( $is_branded_version || $is_privacy_policy_pending ) { + // Fresh install - save current providers as baseline and skip upgrade popover. + \update_option( 'progress_planner_previous_version_task_providers', \array_unique( $onboard_task_provider_ids ), SORT_REGULAR ); + return; + } - // We're upgrading from v1.0.4 or older, set the old task providers to what we had before the upgrade. - if ( ! $fresh_install && empty( $old_task_providers ) ) { + // Upgrading from v1.0.4 or older (standalone, privacy accepted but no task providers option). + // Set baseline to what existed before the upgrade. $old_task_providers = [ 'core-blogdescription', 'wp-debug-display', From 30d51207186ac112e9f5b6ee01696d578e38199a Mon Sep 17 00:00:00 2001 From: Filip Ilic Date: Fri, 6 Feb 2026 15:24:26 +0100 Subject: [PATCH 4/5] Replace hardcoded Ravi icon with branding system icon Use get_admin_menu_icon() from the branding system instead of hardcoded icon_progress_planner.svg references, so hosts with custom branding automatically get their own icon everywhere. Closes #51 Co-Authored-By: Claude Opus 4.6 --- assets/css/page-widgets/todo.css | 4 ++-- assets/js/focus-element.js | 4 +--- assets/js/yoast-focus-element.js | 4 ++-- classes/admin/class-enqueue.php | 2 +- classes/admin/class-page.php | 2 +- classes/admin/widgets/class-todo.php | 13 +++++++++++++ .../suggested-tasks/providers/class-core-update.php | 2 +- .../yoast/class-add-yoast-providers.php | 4 ++-- views/dashboard-widgets/score.php | 2 +- views/dashboard-widgets/todo.php | 2 +- views/welcome.php | 2 +- 11 files changed, 26 insertions(+), 15 deletions(-) diff --git a/assets/css/page-widgets/todo.css b/assets/css/page-widgets/todo.css index cdd6898d9..1c7745fa5 100644 --- a/assets/css/page-widgets/todo.css +++ b/assets/css/page-widgets/todo.css @@ -39,7 +39,7 @@ display: inline-block; width: 24px; height: 24px; - background-image: url("../../images/icon_progress_planner.svg"); + background-image: var(--prpl-icon-url); background-size: contain; background-repeat: no-repeat; } @@ -136,7 +136,7 @@ display: inline-block; width: 24px; height: 24px; - background-image: url("../../images/icon_progress_planner.svg"); + background-image: var(--prpl-icon-url); background-size: contain; background-repeat: no-repeat; } diff --git a/assets/js/focus-element.js b/assets/js/focus-element.js index 09b56d2e0..174b38b77 100644 --- a/assets/js/focus-element.js +++ b/assets/js/focus-element.js @@ -8,9 +8,7 @@ const prplGetIndicatorElement = ( content, taskId, points ) => { // Create an element. const imgEl = document.createElement( 'img' ); - imgEl.src = - progressPlannerFocusElement.base_url + - '/assets/images/icon_progress_planner.svg'; + imgEl.src = progressPlannerFocusElement.iconUrl; imgEl.alt = points ? prplL10n( 'fixThisIssue' ).replace( '%d', points ) : ''; diff --git a/assets/js/yoast-focus-element.js b/assets/js/yoast-focus-element.js index e35cda92e..f5b1b01c1 100644 --- a/assets/js/yoast-focus-element.js +++ b/assets/js/yoast-focus-element.js @@ -14,7 +14,7 @@ class ProgressPlannerYoastFocus { constructor() { this.container = document.querySelector( '#yoast-seo-settings' ); this.tasks = progressPlannerYoastFocusElement.tasks; - this.baseUrl = progressPlannerYoastFocusElement.base_url; + this.iconUrl = progressPlannerYoastFocusElement.iconUrl; if ( this.container ) { this.init(); @@ -219,7 +219,7 @@ class ProgressPlannerYoastFocus { // Create an icon image. const iconImg = document.createElement( 'img' ); - iconImg.src = this.baseUrl + '/assets/images/icon_progress_planner.svg'; + iconImg.src = this.iconUrl; iconImg.alt = 'Ravi'; iconImg.width = 16; iconImg.height = 16; diff --git a/classes/admin/class-enqueue.php b/classes/admin/class-enqueue.php index 895908994..5908b9850 100644 --- a/classes/admin/class-enqueue.php +++ b/classes/admin/class-enqueue.php @@ -301,7 +301,7 @@ public function localize_script( $handle, $localize_data = [] ) { $localize_data = [ 'name' => 'prplCelebrate', 'data' => [ - 'raviIconUrl' => \constant( 'PROGRESS_PLANNER_URL' ) . '/assets/images/icon_progress_planner.svg', + 'raviIconUrl' => \progress_planner()->get_ui__branding()->get_admin_menu_icon(), 'confettiOptions' => $confetti_options, ], ]; diff --git a/classes/admin/class-page.php b/classes/admin/class-page.php index 21c58a864..73d38c170 100644 --- a/classes/admin/class-page.php +++ b/classes/admin/class-page.php @@ -261,7 +261,7 @@ public function maybe_enqueue_focus_el_script( $hook ) { 'tasks' => $tasks_details, 'totalPoints' => $total_points, 'completedPoints' => $completed_points, - 'base_url' => \constant( 'PROGRESS_PLANNER_URL' ), + 'iconUrl' => \progress_planner()->get_ui__branding()->get_admin_menu_icon(), 'l10n' => [ /* translators: %d: The number of points. */ 'fixThisIssue' => \esc_html__( 'Fix this issue to get %d point(s) in Progress Planner', 'progress-planner' ), diff --git a/classes/admin/widgets/class-todo.php b/classes/admin/widgets/class-todo.php index 171abb3d0..bfc2903d2 100644 --- a/classes/admin/widgets/class-todo.php +++ b/classes/admin/widgets/class-todo.php @@ -26,6 +26,19 @@ final class ToDo extends Widget { */ protected $width = 2; + /** + * Enqueue styles. + * + * @return void + */ + public function enqueue_styles() { + parent::enqueue_styles(); + \wp_add_inline_style( + "progress-planner/page-widgets/{$this->id}", + ':root { --prpl-icon-url: url("' . \progress_planner()->get_ui__branding()->get_admin_menu_icon() . '"); }' + ); + } + /** * Print the widget content. * diff --git a/classes/suggested-tasks/providers/class-core-update.php b/classes/suggested-tasks/providers/class-core-update.php index 0a681b437..63f00960e 100644 --- a/classes/suggested-tasks/providers/class-core-update.php +++ b/classes/suggested-tasks/providers/class-core-update.php @@ -89,7 +89,7 @@ public function add_core_update_link( $update_actions ) { foreach ( \progress_planner()->get_suggested_tasks_db()->get_tasks_by( [ 'post_status' => 'publish' ] ) as $task ) { if ( $this->get_task_id() === \progress_planner()->get_suggested_tasks()->get_task_id_from_slug( $task->post_name ) ) { $update_actions['prpl_core_update'] = - 'Progress Planner' . + 'Progress Planner' . '' . \esc_html__( 'Click here to celebrate your completed task!', 'progress-planner' ) . ''; break; } diff --git a/classes/suggested-tasks/providers/integrations/yoast/class-add-yoast-providers.php b/classes/suggested-tasks/providers/integrations/yoast/class-add-yoast-providers.php index c4a96db06..1eeb1f91e 100644 --- a/classes/suggested-tasks/providers/integrations/yoast/class-add-yoast-providers.php +++ b/classes/suggested-tasks/providers/integrations/yoast/class-add-yoast-providers.php @@ -66,8 +66,8 @@ public function enqueue_assets( $hook ) { [ 'name' => 'progressPlannerYoastFocusElement', 'data' => [ - 'tasks' => $focus_tasks, - 'base_url' => \constant( 'PROGRESS_PLANNER_URL' ), + 'tasks' => $focus_tasks, + 'iconUrl' => \progress_planner()->get_ui__branding()->get_admin_menu_icon(), ], ] ); diff --git a/views/dashboard-widgets/score.php b/views/dashboard-widgets/score.php index 5908b4951..da80254c8 100644 --- a/views/dashboard-widgets/score.php +++ b/views/dashboard-widgets/score.php @@ -69,7 +69,7 @@