-
Notifications
You must be signed in to change notification settings - Fork 193
/
class-sensei-home-tasks-provider.php
254 lines (225 loc) · 8.1 KB
/
class-sensei-home-tasks-provider.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
<?php
/**
* File containing Sensei_Home_Tasks_Provider class.
*
* @package sensei-lms
* @since 4.8.0
*/
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly.
}
/**
* Class responsible for generating the Tasks structure for Sensei Home screen.
*/
class Sensei_Home_Tasks_Provider {
const COMPLETED_TASKS_OPTION_KEY = 'sensei_home_tasks_list_is_completed';
/**
* Tells if the WP hooks were attached or not.
*
* @var bool
*/
private static $attached_hooks = false;
/**
* The option name that wp-calypso automatically fetches to use
* as a reference for Launchpad tasks' complete statuses.
*
* @var string
*/
const CALYPSO_LAUNCHPAD_STATUSES_NAME = 'launchpad_checklist_tasks_statuses';
/**
* Class constructor.
*/
public function __construct() {
$this->attach_tasks_statuses_hooks();
}
/**
* Returns the Tasks.
*
* @return array
*/
public function get(): array {
return [
'items' => $this->get_tasks(),
'site' => $this->get_site(),
'course' => $this->get_course(),
'is_completed' => (bool) get_option( self::COMPLETED_TASKS_OPTION_KEY, false ),
];
}
/**
* Actual logic to decide what tasks have to be returned.
*
* @return array[]
*/
private function get_tasks(): array {
// TODO Implement the logic for this.
$core_tasks = [
new Sensei_Home_Task_Setup_Site(),
new Sensei_Home_Task_Create_First_Course(),
new Sensei_Home_Task_Configure_Learning_Mode(),
new Sensei_Home_Task_Publish_First_Course(),
];
if ( Sensei_Home_Task_Pro_Upsell::is_active() ) {
$core_tasks[] = new Sensei_Home_Task_Pro_Upsell();
}
$tasks = [];
/**
* Each one of the core tasks.
*
* @var Sensei_Home_Task $core_task
*/
foreach ( $core_tasks as $core_task ) {
$tasks[ $core_task::get_id() ] = $this->map_task( $core_task );
}
/**
* Filters the list of tasks that will be later displayed in the Sensei Home header.
*
* @since 4.8.0
*
* @hook sensei_home_tasks
*
* @param {array[]} $tasks A dictionary of tasks indexed by task ID.
* @property {string} `$tasks[]['id']` The task ID. Must be unique.
* @property {string} `$tasks[]['title']` The task title.
* @property {int} `$tasks[]['priority']` Number used in frontend to sort tasks in ascending order.
* @property {string} `$tasks[]['url']` Optional. Destination URL for users when clicking on the task.
* @property {string} `$tasks[]['image']` Optional. Source url or path for the featured image when this task is the first pending one.
* @property {bool} `$tasks[]['done']` Whether the task is considered done or not.
* @return {array} Filtered tasks.
*/
return apply_filters( 'sensei_home_tasks', $tasks );
}
/**
* Maps a specific Sensei_Home_Task to a basic array structure. Will execute the code in `is_completed` for all entries.
*
* @param Sensei_Home_Task $task The actual task to map.
* @return array
*/
private function map_task( Sensei_Home_Task $task ): array {
return [
'id' => $task::get_id(),
'title' => $task->get_title(),
'priority' => $task->get_priority(),
'url' => $task->get_url(),
'done' => $task->is_completed(),
];
}
/**
* Return the site information needed for the task list component.
*
* @return array The site info, including title and image (which is the custom logo) URL.
*/
private function get_site() {
$custom_logo_id = get_theme_mod( 'custom_logo' );
$image = wp_get_attachment_image_src( $custom_logo_id, 'full' );
return [
// Title is persisted with encoded specialchars. We need to decode so that consumer decides what to do with it.
'title' => wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES ),
'image' => $image ? $image[0] : null,
];
}
/**
* Return the course information needed for the task list component, including title and image (which is the
* featured image) for that course. Please note that the data for the demo course is never returned by this method.
*
* @return array|null The course information, including title, the permalink and the URL for the featured image.
*/
private function get_course() {
global $wpdb;
$cache_key = 'home/metadata/tasks/course';
$cache_group = 'sensei/temporary';
$result = wp_cache_get( $cache_key, $cache_group );
if ( false === $result ) {
$prefix = $wpdb->esc_like( Sensei_Data_Port_Manager::SAMPLE_COURSE_SLUG );
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Safe-ish and rare query.
$post_id = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM {$wpdb->posts} WHERE post_type='course' AND post_status IN ('publish', 'draft') AND post_name NOT LIKE %s ORDER BY post_status='published' DESC, ID ASC LIMIT 1", "{$prefix}%" ) );
if ( null === $post_id ) {
$result = null;
} else {
$post = get_post( $post_id );
$image = get_the_post_thumbnail_url( $post_id, 'full' );
$result = [
'title' => $post->post_title,
'permalink' => get_permalink( $post_id ),
'image' => $image ? $image : null,
];
wp_cache_set( $cache_key, $result, $cache_group, 60 );
}
}
return $result;
}
/**
* Mark tasks list as completed.
*
* @param bool $completed Whether the task list must be marked as completed or uncompleted.
*/
public function mark_as_completed( $completed = true ) {
update_option( self::COMPLETED_TASKS_OPTION_KEY, $completed, false );
}
/**
* Attaches required hooks.
*/
private function attach_tasks_statuses_hooks() {
// Attach the hooks only once.
if ( self::$attached_hooks ) {
return;
}
self::$attached_hooks = true;
add_action( 'save_post_course', [ $this, 'log_course_completion_tasks' ], 10, 3 );
// Attach some hooks only for Atomic sites.
if ( ! Sensei_Utils::is_atomic_platform() ) {
return;
}
// Attach the update_tasks_statuses method to filters and actions
// that can affect the status of the Sensei Home tasks.
add_action( 'save_post_course', [ $this, 'update_tasks_statuses' ] );
add_action( 'wp_ajax_sensei_settings_section_visited', [ $this, 'update_tasks_statuses' ] );
}
/**
* Updates the tasks_statuses in wp options
*/
public function update_tasks_statuses() {
$tasks_statuses = [];
foreach ( $this->get_tasks() as $task ) {
// Convert the names of the tasks into snake case.
$task_name = str_replace( '-', '_', $task['id'] );
$tasks_statuses[ $task_name ] = $task['done'];
}
// Overwrite the existing values with the new ones before updating.
$tasks_statuses = array_merge( get_option( self::CALYPSO_LAUNCHPAD_STATUSES_NAME, [] ), $tasks_statuses );
update_option( self::CALYPSO_LAUNCHPAD_STATUSES_NAME, $tasks_statuses );
}
/**
* Logs completion of the "Create your first course" and "Publish your first course" tasks.
*
* @internal
*
* @since 4.20.1
*
* @param int $post_id Post ID.
* @param WP_Post $post Post object.
* @param bool $update Whether this is an existing post being updated.
*/
public function log_course_completion_tasks( $post_id, $post, $update ) {
if ( ! $update ) {
return;
}
if ( (bool) get_option( self::COMPLETED_TASKS_OPTION_KEY, false ) ) {
return;
}
// Handle "Create your first course" task completion.
if ( 'draft' === $post->post_status ) {
$create_first_course_task = new Sensei_Home_Task_Create_First_Course();
if ( ! $create_first_course_task->is_completed() ) {
sensei_log_event( 'home_task_complete', [ 'type' => $create_first_course_task::get_id() ] );
update_option( Sensei_Home_Task_Create_First_Course::CREATED_FIRST_COURSE_OPTION_KEY, 1, false );
}
} elseif ( 'publish' === $post->post_status ) { // Handle "Publish your first course" task completion.
$publish_first_course_task = new Sensei_Home_Task_Publish_First_Course();
// Log task completion event if this is the first published course.
if ( ! $publish_first_course_task->is_completed() ) {
sensei_log_event( 'home_task_complete', [ 'type' => $publish_first_course_task::get_id() ] );
update_option( Sensei_Home_Task_Publish_First_Course::PUBLISHED_FIRST_COURSE_OPTION_KEY, 1, false );
}
}
}
}