Skip to content
4 changes: 4 additions & 0 deletions classes/class-base.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
* @method \Progress_Planner\Rest\Tasks get_rest__tasks()
* @method \Progress_Planner\Todo get_todo()
* @method \Progress_Planner\Utils\Onboard get_utils__onboard()
* @method \Progress_Planner\Utils\Security_Update_Monitor get_utils__security_update_monitor()
* @method \Progress_Planner\Utils\Playground get_utils__playground()
* @method \Progress_Planner\Admin\Page get_admin__page()
* @method \Progress_Planner\Admin\Tour get_admin__tour()
Expand Down Expand Up @@ -120,6 +121,9 @@ public function init() {

$this->get_suggested_tasks();

// Watches for core security releases; must run on front-end/cron requests too.
$this->get_utils__security_update_monitor();

$this->get_admin__editor();

$this->get_actions__content();
Expand Down
100 changes: 85 additions & 15 deletions classes/class-suggested-tasks.php
Original file line number Diff line number Diff line change
Expand Up @@ -156,23 +156,34 @@ public function delete_activity( string $task_id ): void {
* @return void
*/
public function on_automatic_updates_complete(): void {
$pending_tasks = \progress_planner()->get_suggested_tasks_db()->get(
[
'numberposts' => 1,
'post_status' => 'publish',
'provider_id' => 'update-core',
'date_query' => [ [ 'after' => 'this Monday' ] ],
]
);
$providers = [
// The repetitive update-core task only counts within the current week.
'update-core' => [ 'date_query' => [ [ 'after' => 'this Monday' ] ] ],
// The security-update task persists until the update is installed.
'security-update' => [],
];

if ( empty( $pending_tasks ) ) {
return;
}
foreach ( $providers as $provider_id => $extra_args ) {
$pending_tasks = \progress_planner()->get_suggested_tasks_db()->get(
\array_merge(
[
'numberposts' => 1,
'post_status' => 'publish',
'provider_id' => $provider_id,
],
$extra_args
)
);

if ( empty( $pending_tasks ) ) {
continue;
}

\progress_planner()->get_suggested_tasks_db()->update_recommendation( $pending_tasks[0]->ID, [ 'post_status' => 'trash' ] );
\progress_planner()->get_suggested_tasks_db()->update_recommendation( $pending_tasks[0]->ID, [ 'post_status' => 'trash' ] );

// Insert an activity.
$this->insert_activity( \progress_planner()->get_suggested_tasks()->get_task_id_from_slug( $pending_tasks[0]->post_name ) );
// Insert an activity.
$this->insert_activity( \progress_planner()->get_suggested_tasks()->get_task_id_from_slug( $pending_tasks[0]->post_name ) );
}
}

/**
Expand Down Expand Up @@ -484,6 +495,12 @@ public function rest_api_tax_query( $args, $request ) {
$include_providers = \array_intersect( $include_providers, $request_providers );
}

// While a security-update task is pending, it is the only recommendation
// shown to users who can install it.
if ( $this->should_lock_down_recommendations( $request, $include_providers ) ) {
$include_providers = [ 'security-update' ];
}

$tax_query[] = [
'taxonomy' => 'prpl_recommendations_provider',
'field' => 'slug',
Expand All @@ -508,6 +525,38 @@ public function rest_api_tax_query( $args, $request ) {
return $args;
}

/**
* Check whether a REST recommendations query must be locked down to the security-update task.
*
* The lockdown only applies to publish-status queries from users who can install
* the update. The user's own to-do list (provider "user") and pending-celebration
* queries are never locked down.
*
* @param \WP_REST_Request $request The request object.
* @param array $include_providers The provider IDs available to the current user.
*
* @return bool
*/
private function should_lock_down_recommendations( $request, $include_providers ) {
// Only publish-status queries are locked down (celebrations & snoozed tasks flow normally).
$statuses = isset( $request['status'] ) ? (array) $request['status'] : [ 'publish' ];
if ( [ 'publish' ] !== \array_values( $statuses ) ) {
return false;
}

// The user's own to-do list is never locked down.
if ( isset( $request['provider'] ) && [ 'user' ] === \explode( ',', $request['provider'] ) ) {
return false;
}

// Only users who can install the update are locked down.
if ( ! \in_array( 'security-update', $include_providers, true ) ) {
return false;
}

return \progress_planner()->get_utils__security_update_monitor()->is_security_lockdown_active();
}

/**
* Sanitize a recommendation before it is inserted or updated via the REST API.
*
Expand Down Expand Up @@ -553,7 +602,7 @@ public function rest_prepare_recommendation( $response, $post ) {
if ( $provider ) {
$response->data['prpl_provider'] = $provider_term[0];
// Link should be added during run time, since it is not added for users without required capability.
$response->data['meta']['prpl_url'] = $response->data['meta']['prpl_url'] && $provider->capability_required()
$response->data['meta']['prpl_url'] = ! empty( $response->data['meta']['prpl_url'] ) && $provider->capability_required()
? \esc_url( (string) $response->data['meta']['prpl_url'] )
: '';

Expand Down Expand Up @@ -600,6 +649,27 @@ public function get_tasks_in_rest_format( array $args = [] ) {
]
);

// While a security-update task is pending, it is the only recommendation
// served to users who can install it. User to-do lists and non-publish
// statuses (e.g. pending celebrations) are not affected.
if ( [ 'publish' ] === \array_values( (array) $args['post_status'] )
&& [ 'user' ] !== $args['include_provider']
&& \in_array(
'security-update',
\array_map(
static function ( $provider ) {
return $provider->get_provider_id();
},
$this->tasks_manager->get_task_providers_available_for_user()
),
true
)
&& \progress_planner()->get_utils__security_update_monitor()->is_security_lockdown_active()
) {
$args['include_provider'] = [ 'security-update' ];
$args['exclude_provider'] = [];
}

// Build query args for get_tasks_by.
$query_args = [
'post_status' => $args['post_status'],
Expand Down
2 changes: 2 additions & 0 deletions classes/suggested-tasks/class-tasks-manager.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use Progress_Planner\Suggested_Tasks\Providers\Permalink_Structure;
use Progress_Planner\Suggested_Tasks\Providers\Php_Version;
use Progress_Planner\Suggested_Tasks\Providers\Search_Engine_Visibility;
use Progress_Planner\Suggested_Tasks\Providers\Security_Update;
use Progress_Planner\Suggested_Tasks\Tasks_Interface;
use Progress_Planner\Suggested_Tasks\Providers\Integrations\Yoast\Add_Yoast_Providers;
use Progress_Planner\Suggested_Tasks\Providers\Integrations\AIOSEO\Add_AIOSEO_Providers;
Expand Down Expand Up @@ -64,6 +65,7 @@ public function __construct() {
new Content_Create(),
new Content_Review(),
new Core_Update(),
new Security_Update(),
new Blog_Description(),
new Debug_Display(),
new Disable_Comments(),
Expand Down
Loading
Loading