Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions assets/css/page-widgets/todo.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand Down
4 changes: 1 addition & 3 deletions assets/js/focus-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@
const prplGetIndicatorElement = ( content, taskId, points ) => {
// Create an <img> 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 )
: '';
Expand Down
4 changes: 2 additions & 2 deletions assets/js/yoast-focus-element.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion classes/admin/class-enqueue.php
Original file line number Diff line number Diff line change
Expand Up @@ -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,
],
];
Expand Down
2 changes: 1 addition & 1 deletion classes/admin/class-page.php
Original file line number Diff line number Diff line change
Expand Up @@ -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' ),
Expand Down
13 changes: 13 additions & 0 deletions classes/admin/widgets/class-todo.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
45 changes: 40 additions & 5 deletions classes/class-badges.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 );
}

/**
Expand All @@ -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 );
}

/**
Expand Down Expand Up @@ -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.
Expand All @@ -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;
}
}
2 changes: 1 addition & 1 deletion classes/class-lessons.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public function get_remote_api_items() {
[
'site' => \get_site_url(),
'license_key' => \progress_planner()->get_license_key(),
'locale' => apply_filters( 'prpl_lesson_locale', \get_locale() ),
'locale' => \apply_filters( 'prpl_lesson_locale', \get_locale() ),
],
$url
);
Expand Down
26 changes: 19 additions & 7 deletions classes/class-plugin-upgrade-tasks.php
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
23 changes: 18 additions & 5 deletions classes/class-suggested-tasks.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion classes/suggested-tasks/providers/class-core-update.php
Original file line number Diff line number Diff line change
Expand Up @@ -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'] =
'<img src="' . \esc_attr( \constant( 'PROGRESS_PLANNER_URL' ) . '/assets/images/icon_progress_planner.svg' ) . '" style="width:1rem;padding-left:0.25rem;padding-right:0.25rem;vertical-align:middle;" alt="Progress Planner" />' .
'<img src="' . \esc_attr( \progress_planner()->get_ui__branding()->get_admin_menu_icon() ) . '" style="width:1rem;padding-left:0.25rem;padding-right:0.25rem;vertical-align:middle;" alt="Progress Planner" />' .
'<a href="' . \esc_url( \admin_url( 'admin.php?page=progress-planner' ) ) . '" target="_parent">' . \esc_html__( 'Click here to celebrate your completed task!', 'progress-planner' ) . '</a>';
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,8 @@ protected function is_plugin_active() {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}

$plugins = get_plugins();
$this->is_plugin_active = isset( $plugins[ $this->plugin_path ] ) && is_plugin_active( $this->plugin_path );
$plugins = \get_plugins();
$this->is_plugin_active = isset( $plugins[ $this->plugin_path ] ) && \is_plugin_active( $this->plugin_path );
}

return $this->is_plugin_active;
Expand All @@ -168,7 +168,7 @@ protected function get_autoloaded_options_count() {

if ( null === $this->autoloaded_options_count ) {
$autoload_values = \wp_autoload_values_to_autoload();
$placeholders = implode( ',', array_fill( 0, count( $autoload_values ), '%s' ) );
$placeholders = \implode( ',', \array_fill( 0, \count( $autoload_values ), '%s' ) );

// phpcs:disable WordPress.DB
$this->autoloaded_options_count = $wpdb->get_var(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public function check_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 ) ) {
if ( \count( $public_post_types ) < \count( $previosly_set_public_post_types ) ) {
return;
}

Expand Down Expand Up @@ -180,8 +180,8 @@ public function handle_interactive_task_specific_submit() {
}

$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 );
$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 );

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
],
]
);
Expand Down
2 changes: 1 addition & 1 deletion progress-planner.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

require_once PROGRESS_PLANNER_DIR . '/autoload.php';

if ( ! function_exists( 'progress_planner' ) ) {
if ( ! \function_exists( 'progress_planner' ) ) {
/**
* Get the progress planner instance.
*
Expand Down
4 changes: 2 additions & 2 deletions tests/phpunit/test-class-base.php
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ public function test_is_debug_mode_enabled() {
$this->assertFalse( $result );

// Set the current user to a user with the manage_options capability.
wp_set_current_user( 1 );
\wp_set_current_user( 1 );

// Option is set, and the user has the manage_options capability.
$result = $this->base_instance->is_debug_mode_enabled();
Expand All @@ -228,7 +228,7 @@ public function test_is_debug_mode_enabled() {
$this->assertIsBool( $result );

// Unset the current user.
wp_set_current_user( 0 );
\wp_set_current_user( 0 );
}
}

Expand Down
2 changes: 1 addition & 1 deletion tests/phpunit/test-class-page-types.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public static function set_lessons_cache() {
[
'site' => \get_site_url(),
'license_key' => \progress_planner()->get_license_key(),
'locale' => apply_filters( 'prpl_lesson_locale', \get_locale() ),
'locale' => \apply_filters( 'prpl_lesson_locale', \get_locale() ),
],
$url
);
Expand Down
2 changes: 1 addition & 1 deletion tests/phpunit/traits/test-class-ajax-security-aioseo.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public function test_verify_aioseo_active_or_fail_not_active() {
} catch ( \WPAjaxDieContinueException $e ) {
// WordPress Core's dieHandler() calls ob_get_clean() which gets output and cleans buffer.
// Get the response.
$response = json_decode( $this->_last_response, true );
$response = \json_decode( $this->_last_response, true );
$this->assertFalse( $response['success'] );
$this->assertArrayHasKey( 'data', $response );
$this->assertArrayHasKey( 'message', $response['data'] );
Expand Down
4 changes: 2 additions & 2 deletions tests/phpunit/traits/test-class-ajax-security-base.php
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public function test_verify_nonce_or_fail_invalid() {
} catch ( \WPAjaxDieContinueException $e ) {
// WordPress Core's dieHandler() calls ob_get_clean() which gets output and cleans buffer.
// Get the response.
$response = json_decode( $this->_last_response, true );
$response = \json_decode( $this->_last_response, true );
$this->assertFalse( $response['success'] );
$this->assertArrayHasKey( 'data', $response );
$this->assertArrayHasKey( 'message', $response['data'] );
Expand Down Expand Up @@ -168,7 +168,7 @@ public function test_verify_capability_or_fail_non_admin() {
} catch ( \WPAjaxDieContinueException $e ) {
// WordPress Core's dieHandler() calls ob_get_clean() which gets output and cleans buffer.
// Get the response.
$response = json_decode( $this->_last_response, true );
$response = \json_decode( $this->_last_response, true );
$this->assertFalse( $response['success'] );
$this->assertArrayHasKey( 'data', $response );
$this->assertArrayHasKey( 'message', $response['data'] );
Expand Down
2 changes: 1 addition & 1 deletion tests/phpunit/traits/test-class-ajax-security-yoast.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public function test_verify_yoast_active_or_fail_not_active() {
} catch ( \WPAjaxDieContinueException $e ) {
// WordPress Core's dieHandler() calls ob_get_clean() which gets output and cleans buffer.
// Get the response.
$response = json_decode( $this->_last_response, true );
$response = \json_decode( $this->_last_response, true );
$this->assertFalse( $response['success'] );
$this->assertArrayHasKey( 'data', $response );
$this->assertArrayHasKey( 'message', $response['data'] );
Expand Down
2 changes: 1 addition & 1 deletion views/dashboard-widgets/score.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@

<?php if ( \current_user_can( 'manage_options' ) ) : ?>
<div class="prpl-dashboard-widget-footer">
<img src="<?php echo \esc_attr( \constant( 'PROGRESS_PLANNER_URL' ) . '/assets/images/icon_progress_planner.svg' ); ?>" style="width:1.85em;" alt="" />
<span style="display:inline-flex;width:1.85em;height:1.85em;"><?php echo \progress_planner()->get_ui__branding()->get_admin_menu_icon( true ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- SVG markup. ?></span>
<div>
<?php $prpl_pending_celebration_tasks = \progress_planner()->get_suggested_tasks_db()->get_tasks_by( [ 'post_status' => 'pending' ] ); ?>
<?php if ( $prpl_pending_celebration_tasks ) : ?>
Expand Down
2 changes: 1 addition & 1 deletion views/dashboard-widgets/todo.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

?>
<div id="prpl-dashboard-widget-todo-header">
<img src="<?php echo \esc_attr( \constant( 'PROGRESS_PLANNER_URL' ) . '/assets/images/icon_progress_planner.svg' ); ?>" style="width:2.5em;" alt="" />
<span style="display:inline-flex;width:2.5em;height:2.5em;"><?php echo \progress_planner()->get_ui__branding()->get_admin_menu_icon( true ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- SVG markup. ?></span>
<p><?php \esc_html_e( 'Keep track of all your tasks and make sure your site is up-to-date!', 'progress-planner' ); ?></p>
</div>
<?php
Expand Down
Loading
Loading