/
msm-sitemap-builder-cron.php
369 lines (314 loc) · 12.2 KB
/
msm-sitemap-builder-cron.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
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
<?php
class MSM_Sitemap_Builder_Cron {
public static function setup() {
add_action( 'msm_update_sitemap_for_year_month_date', array( __CLASS__, 'schedule_sitemap_update_for_year_month_date' ), 10, 2 );
add_action( 'msm_cron_generate_sitemap_for_year', array( __CLASS__, 'generate_sitemap_for_year' ) );
add_action( 'msm_cron_generate_sitemap_for_year_month', array( __CLASS__, 'generate_sitemap_for_year_month' ) );
add_action( 'msm_cron_generate_sitemap_for_year_month_day', array( __CLASS__, 'generate_sitemap_for_year_month_day' ) );
if ( is_admin() ) {
add_filter( 'msm_sitemap_actions', array( __CLASS__, 'add_actions' ) );
add_filter( 'msm_sitemap_create_status', array( __CLASS__, 'sitemap_create_status' ) );
add_action( 'msm_sitemap_action-generate', array( __CLASS__, 'action_generate' ) );
add_action( 'msm_sitemap_action-generate_from_latest', array( __CLASS__, 'action_generate_from_latest' ) );
add_action( 'msm_sitemap_action-halt_generation', array( __CLASS__, 'action_halt' ) );
add_action( 'msm_sitemap_action-reset_sitemap_data', array( __CLASS__, 'action_reset_data' ) );
}
}
/**
* Adds the builder cron actions to the sitemaps admin page.
*
* Hooked into the msm_sitemap_actions filter.
*
* @param array $actions The actions to show on the admin page.
* @return array
*/
public static function add_actions( $actions ) {
// No actions for private blogs
if ( ! Metro_Sitemap::is_blog_public() ) {
return $actions;
}
$sitemap_create_in_progress = get_option( 'msm_sitemap_create_in_progress' ) === true;
$sitemap_halt_in_progress = get_option( 'msm_stop_processing' ) === true;
$actions['generate'] = array( 'text' => __( 'Generate from all articles', 'metro-sitemaps' ), 'enabled' => ! $sitemap_create_in_progress && ! $sitemap_halt_in_progress );
$actions['generate_from_latest'] = array( 'text' => __( 'Generate from latest articles', 'metro-sitemaps' ), 'enabled' => ! $sitemap_create_in_progress && ! $sitemap_halt_in_progress );
$actions['halt_generation'] = array( 'text' => __( 'Halt Sitemap Generation', 'metro-sitemaps' ), 'enabled' => $sitemap_create_in_progress && ! $sitemap_halt_in_progress );
$actions['reset_sitemap_data'] = array( 'text' => __( 'Reset Sitemap Data', 'metro-sitemaps' ), 'enabled' => ! $sitemap_create_in_progress && ! $sitemap_halt_in_progress );
return $actions;
}
/**
* Adds the "Halting" sitemap create status as this status is specific to the
* builder cron.
*
* Hooked into the msm_sitemap_create_status filter.
*
* @param string $status The status text to show the user on the admin page.
* @return string The status text.
*/
public static function sitemap_create_status( $status ) {
if ( get_option( 'msm_stop_processing' ) === true && get_option( 'msm_sitemap_create_in_progress' ) === true )
return __( 'Halting', 'metro-sitemaps' );
return $status;
}
/**
* Generates full sitemaps for the site.
*
* Hooked into the msm_sitemap_actions-generate action.
*/
public static function action_generate() {
$sitemap_create_in_progress = get_option( 'msm_sitemap_create_in_progress' );
self::generate_full_sitemap();
if ( false !== get_option( 'msm_sitemap_create_in_progress', false ) ) {
update_option( 'msm_sitemap_create_in_progress', true );
} else {
add_option( 'msm_sitemap_create_in_progress', true, '', 'no' );
}
if ( empty( $sitemap_create_in_progress ) ) {
Metro_Sitemap::show_action_message( __( 'Starting sitemap generation...', 'metro-sitemaps' ) );
} else {
Metro_Sitemap::show_action_message( __( 'Resuming sitemap creation', 'metro-sitemaps' ) );
}
}
/**
* Generates sitemaps from the latest posts.
*
* Hooked into the msm_sitemap_actions-generate_from_latest action
*/
public static function action_generate_from_latest() {
$last_modified = Metro_Sitemap::get_last_modified_posts();
if ( count( $last_modified ) > 0 ) {
Metro_Sitemap::update_sitemap_from_modified_posts();
Metro_Sitemap::show_action_message( __( 'Updating sitemap from latest articles...', 'metro-sitemaps' ) );
} else {
Metro_Sitemap::show_action_message( __( 'Cannot generate from latest articles: no posts updated lately.', 'metro-sitemaps' ), 'error' );
}
}
/**
* Halts sitemap generation on the next cron run. Saves current position for resuming.
*
* Hooked into the msm_sitemap_actions-halt_generation action.
*/
public static function action_halt() {
// Can only halt generation if sitemap creation is already in process
if ( get_option( 'msm_stop_processing' ) === true ) {
Metro_Sitemap::show_action_message( __( 'Cannot stop sitemap generation: sitemap generation is already being halted.', 'metro-sitemaps' ), 'warning' );
} else if ( get_option( 'msm_sitemap_create_in_progress' ) === true ) {
update_option( 'msm_stop_processing', true );
Metro_Sitemap::show_action_message( __( 'Stopping Sitemap generation', 'metro-sitemaps' ) );
} else {
Metro_Sitemap::show_action_message( __( 'Cannot stop sitemap generation: sitemap generation not in progress', 'metro-sitemaps' ), 'warning' );
}
}
/**
* Resets sitemap data and prints out a message to the user.
*
* Hooked into the msm_sitemap_actions-reset_sitemap_data action.
*/
public static function action_reset_data() {
// Do the same as when we finish then tell use to delete manuallyrather than remove all data
self::reset_sitemap_data();
Metro_Sitemap::show_action_message( sprintf(
__( '<p>Sitemap data reset. If you want to completely remove the data you must do so manually by deleting all posts with post type <code>%1$s</code>.</p><p>The WP-CLI command to do this is: <code>%2$s</code></p>', 'msm-sitemap' ),
Metro_Sitemap::SITEMAP_CPT,
'wp post delete $(wp post list --post_type=' . Metro_Sitemap::SITEMAP_CPT . ' --format=ids)'
) );
}
/**
* Reset sitemap options
*/
public static function reset_sitemap_data() {
// Remove the stats meta information
delete_post_meta_by_key( 'msm_indexed_url_count' );
// Remove the XML sitemap data
delete_post_meta_by_key( 'msm_sitemap_xml' );
// Delete state options
delete_option( 'msm_days_to_process' );
delete_option( 'msm_months_to_process' );
delete_option( 'msm_years_to_process' );
delete_option( 'msm_stop_processing' );
delete_option( 'msm_sitemap_create_in_progress' );
// Delete stats options
delete_option( 'msm_sitemap_indexed_url_count' );
}
public static function schedule_sitemap_update_for_year_month_date( $date, $time ) {
list( $year, $month, $day ) = $date;
wp_schedule_single_event(
$time,
'msm_cron_generate_sitemap_for_year_month_day',
array(
array(
'year' => $year,
'month' => $month,
'day' => $day,
),
)
);
}
/*
* We want to generate the entire sitemap catalogue async to avoid running into timeout and memory issues.
*
* Here's how it all works:
*
* -- Get year range for content
* -- Store these years in options table
* -- Cascade through all months and days in reverse order i.e. newest first
* -- Generate cron event for each individual day and when finished queue up the cron for the next one
* -- Add each post from that day to the custom post
*
*/
/**
* Generate full sitemap
*/
public static function generate_full_sitemap() {
global $wpdb;
$is_partial_or_running = get_option( 'msm_years_to_process' );
if ( empty( $is_partial_or_running ) ) {
$all_years_with_posts = Metro_Sitemap::check_year_has_posts();
update_option( 'msm_years_to_process', $all_years_with_posts );
} else {
$all_years_with_posts = $is_partial_or_running;
}
if ( 0 == count( $all_years_with_posts ) )
return; // Cannot generate sitemaps if there are no posts
$time = time();
$next_year = end( $all_years_with_posts );
wp_schedule_single_event(
$time,
'msm_cron_generate_sitemap_for_year',
array(
array(
'year' => $next_year,
),
)
);
}
/**
* Generate sitemap for a given year
* @param mixed[] $args
*/
public static function generate_sitemap_for_year( $args ) {
$is_partial_or_running = get_option( 'msm_months_to_process' );
$year = $args['year'];
$max_month = 12;
if ( $year == date( 'Y' ) ) {
$max_month = date( 'n' );
}
if ( empty( $is_partial_or_running ) ) {
$months = range( 1, $max_month );
update_option( 'msm_months_to_process', $months );
} else {
$months = $is_partial_or_running;
}
$time = time();
$next_month = end($months);
wp_schedule_single_event(
$time,
'msm_cron_generate_sitemap_for_year_month',
array(
array(
'year' => $year,
'month' => $next_month,
),
)
);
}
/**
* Generate sitemap for a given month in a given year
* @param mixed[] $args
*/
public static function generate_sitemap_for_year_month( $args ) {
$is_partial_or_running = get_option( 'msm_days_to_process' );
$year = $args['year'];
$month = $args['month'];
// cal_days_in_month doesn't exist on WP.com so set it to a possible max. Will skip invalid dates as no posts will be found
if ( ! function_exists( 'cal_days_in_month' ) ) {
$max_days = 31;
} else {
$max_days = cal_days_in_month( CAL_GREGORIAN, (int) $month, (int) $year );
}
if ( date( 'Y' ) == $year && $month == date( 'n' ) ) {
$max_days = date( 'j' );
}
if ( empty( $is_partial_or_running ) ) {
$days = range( 1, $max_days );
update_option( 'msm_days_to_process', $days );
} else {
$days = $is_partial_or_running;
}
$next_day = end($days);
$time = time();
wp_schedule_single_event(
$time,
'msm_cron_generate_sitemap_for_year_month_day',
array(
array(
'year' => $year,
'month' => $month,
'day' => $next_day,
),
)
);
}
/**
* Generate sitemap for a given year, month, day
* @param mixed[] $args
*/
public static function generate_sitemap_for_year_month_day( $args ) {
$year = $args['year'];
$month = $args['month'];
$day = $args['day'];
$date_stamp = Metro_Sitemap::get_date_stamp( $year, $month, $day );
if ( Metro_Sitemap::date_range_has_posts( $date_stamp, $date_stamp ) ) {
Metro_Sitemap::generate_sitemap_for_date( $date_stamp );
}
self::find_next_day_to_process( $year, $month, $day );
}
/**
* Find the next day with posts to process
* @param int $year
* @param int $month
* @param int $day
* @return void, just updates options.
*/
public static function find_next_day_to_process( $year, $month, $day ) {
$halt = get_option( 'msm_stop_processing' ) === true;
if ( $halt || ! Metro_Sitemap::is_blog_public() ) {
// Allow user to bail out of the current process, doesn't remove where the job got up to
// or If the blog became private while sitemaps were enabled, stop here.
delete_option( 'msm_stop_processing' );
delete_option( 'msm_sitemap_create_in_progress' );
return;
}
update_option( 'msm_sitemap_create_in_progress', true );
$days_being_processed = get_option( 'msm_days_to_process' );
$months_being_processed = get_option( 'msm_months_to_process' );
$years_being_processed = get_option( 'msm_years_to_process' );
$total_days = count( $days_being_processed );
$total_months = count( $months_being_processed );
$total_years = count( $years_being_processed );
if ( $total_days && $day > 1 ) {
// Day has finished
unset( $days_being_processed[$total_days - 1] );
update_option( 'msm_days_to_process', $days_being_processed );
self::generate_sitemap_for_year_month( array( 'year' => $year, 'month' => $month ) );
} else if ( $total_months and $month > 1 ) {
// Month has finished
unset( $months_being_processed[ $total_months - 1] );
delete_option( 'msm_days_to_process' );
update_option( 'msm_months_to_process', $months_being_processed );
self::generate_sitemap_for_year( array( 'year' => $year ) );
} else if ( $total_years > 1 ) {
// Year has finished
unset( $years_being_processed[ $total_years - 1] );
delete_option( 'msm_days_to_process' );
delete_option( 'msm_months_to_process' );
update_option( 'msm_years_to_process', $years_being_processed );
self::generate_full_sitemap();
} else {
// We've finished - remove all options
delete_option( 'msm_days_to_process' );
delete_option( 'msm_months_to_process' );
delete_option( 'msm_years_to_process' );
delete_option( 'msm_sitemap_create_in_progress' );
}
}
}