PluginProbe
Content Control – The Ultimate Content Restriction Plugin! Restrict Content, Create Conditional Blocks & More / 2.6.1
Content Control – The Ultimate Content Restriction Plugin! Restrict Content, Create Conditional Blocks & More v2.6.1
trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.1.0 1.1.1 1.1.10 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8 2.0.0 2.0.1 2.0.10 2.0.11 2.0.12 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 All 47 releases
← All changes | classes/Controllers/Admin/Upgrades.php +130 -58 2.0.42.6.1 View file →
@@ -39,15 +39,18 @@
39 39 /**
40 40 * Initialize the settings page.
41 41 */
42 42 public function init() {
43 - add_action( 'init', [ $this, 'hooks' ] );
43 + add_action( 'admin_init', [ $this, 'hooks' ] );
44 44 add_action( 'wp_ajax_content_control_upgrades', [ $this, 'ajax_handler' ] );
45 45 add_filter( 'content_control/settings-page_localized_vars', [ $this, 'localize_vars' ] );
46 + add_action( 'content_control/update_version', '\\ContentControl\\maybe_force_v2_migrations' );
46 47 }
47 48
48 49 /**
49 50 * Hook into relevant WP actions.
51 + *
52 + * @return void
50 53 */
51 54 public function hooks() {
52 55 if ( is_admin() && current_user_can( 'manage_options' ) ) {
53 56 add_action( 'admin_notices', [ $this, 'admin_notices' ] );
@@ -63,8 +66,9 @@
63 66 */
64 67 public function all_upgrades() {
65 68 return [
66 69 // Version 2 upgrades.
70 + '\ContentControl\Upgrades\Backup_2',
67 71 '\ContentControl\Upgrades\PluginMeta_2',
68 72 '\ContentControl\Upgrades\Settings_2',
69 73 '\ContentControl\Upgrades\UserMeta_2',
70 74 '\ContentControl\Upgrades\Restrictions_2',
@@ -76,9 +80,9 @@
76 80 *
77 81 * @return boolean
78 82 */
79 83 public function has_upgrades() {
80 - return count( $this->get_required_upgrades() );
84 + return (bool) count( $this->get_required_upgrades() );
81 85 }
82 86
83 87 /**
84 88 * Get a list of required upgrades.
@@ -87,42 +91,40 @@
87 91 *
88 92 * @return \ContentControl\Base\Upgrade[]
89 93 */
90 94 public function get_required_upgrades() {
91 - static $required_upgrades = null;
95 + $required_upgrades = [];
92 96
93 - if ( null === $required_upgrades ) {
94 - $all_upgrades = $this->all_upgrades();
95 - $required_upgrades = [];
97 + $all_upgrades = $this->all_upgrades();
98 + $required_upgrades = [];
96 99
97 - foreach ( $all_upgrades as $upgrade_class ) {
98 - if ( ! class_exists( $upgrade_class ) ) {
99 - continue;
100 - }
100 + foreach ( $all_upgrades as $upgrade_class ) {
101 + if ( ! class_exists( $upgrade_class ) ) {
102 + continue;
103 + }
101 104
102 - /**
103 - * Upgrade class instance.
104 - *
105 - * @var \ContentControl\Base\Upgrade $upgrade
106 - */
107 - $upgrade = new $upgrade_class();
105 + /**
106 + * Upgrade class instance.
107 + *
108 + * @var \ContentControl\Base\Upgrade $upgrade
109 + */
110 + $upgrade = new $upgrade_class();
108 111
109 - // Check if required, and if so, add it to the list.
110 - if ( is_upgrade_complete( $upgrade ) ) {
111 - continue;
112 - } elseif ( ! $upgrade->is_required() ) {
113 - // If its not required, mark it as done.
114 - mark_upgrade_complete( $upgrade );
115 - continue;
116 - }
117 -
118 - $required_upgrades[] = $upgrade;
112 + // Check if required, and if so, add it to the list.
113 + if ( is_upgrade_complete( $upgrade ) ) {
114 + continue;
115 + } elseif ( ! $upgrade->is_required() ) {
116 + // If its not required, mark it as done.
117 + mark_upgrade_complete( $upgrade );
118 + continue;
119 119 }
120 120
121 - // Sort the required upgrades based on prerequisites.
122 - $required_upgrades = $this->sort_upgrades_by_prerequisites( $required_upgrades );
121 + $required_upgrades[] = $upgrade;
123 122 }
124 123
124 + // Sort the required upgrades based on prerequisites.
125 + $required_upgrades = $this->sort_upgrades_by_prerequisites( $required_upgrades );
126 +
125 127 return $required_upgrades;
126 128 }
127 129
128 130 /**
@@ -133,15 +135,20 @@
133 135 * @return \ContentControl\Base\Upgrade[]
134 136 */
135 137 private function sort_upgrades_by_prerequisites( $upgrades ) {
136 138 // Build the graph of upgrades and their dependencies.
137 - $graph = [];
139 + $graph = [];
140 + // Build a lookup table of upgrades by name.
138 141 $upgrade_by_name = [];
142 +
143 + // Build the graph & lookup tables.
139 144 foreach ( $upgrades as $upgrade ) {
140 145 $updgrade_name = get_upgrade_name( $upgrade );
141 146
147 + // Add the upgrade to the graph with its dependencies.
142 148 $graph[ $updgrade_name ] = $upgrade->get_dependencies();
143 149
150 + // Add the upgrade to the lookup table.
144 151 $upgrade_by_name[ $updgrade_name ] = $upgrade;
145 152 }
146 153
147 154 // Perform a topological sort on the graph.
@@ -146,26 +153,29 @@
146 153
147 154 // Perform a topological sort on the graph.
148 155 $sorted = $this->topological_sort( $graph );
149 156
150 - // Rebuild the list of upgrades in the sorted order.
151 - foreach ( $sorted as $key => $value ) {
152 - $sorted[ $key ] = $upgrade_by_name[ $value ];
157 + $sorted_upgrades = [];
158 +
159 + // Map the sorted ugprade list with the upgrade from the lookup table.
160 + foreach ( $sorted as $upgrade_name ) {
161 + $upgrade = isset( $upgrade_by_name[ $upgrade_name ] ) ? $upgrade_by_name[ $upgrade_name ] : null;
162 + $sorted_upgrades[] = $upgrade;
153 163 }
154 164
155 165 // Remove null values, these are upgrades that have been marked as done.
156 - $sorted = array_filter( $sorted );
166 + $sorted_upgrades = array_filter( $sorted_upgrades );
157 167
158 168 // Return the sorted upgrades.
159 - return $sorted;
169 + return $sorted_upgrades;
160 170 }
161 171
162 172 /**
163 173 * Perform a topological sort on a graph.
164 174 *
165 - * @param array $graph Graph to sort.
175 + * @param array<string,array<string>> $graph Graph to sort.
166 176 *
167 - * @return array
177 + * @return array<string>
168 178 */
169 179 private function topological_sort( $graph ) {
170 180 $visited = [];
171 181 $sorted = [];
@@ -179,12 +189,14 @@
179 189
180 190 /**
181 191 * Visit a node in the graph for topological sort.
182 192 *
183 - * @param mixed $node Node to visit.
184 - * @param array $graph Graph to sort.
185 - * @param array $visited List of visited nodes.
186 - * @param array $sorted List of sorted nodes.
193 + * @param string $node Node to visit.
194 + * @param array<string,array<string>> $graph Graph to sort.
195 + * @param array<string,bool> $visited List of visited nodes.
196 + * @param array<string> $sorted List of sorted nodes.
197 + *
198 + * @return void
187 199 */
188 200 private function visit_node( $node, $graph, &$visited, &$sorted ) {
189 201 if ( isset( $visited[ $node ] ) ) {
190 202 // Node already visited, skip.
@@ -192,10 +204,12 @@
192 204 }
193 205
194 206 $visited[ $node ] = true;
195 207
196 - foreach ( $graph[ $node ] as $dependency ) {
197 - $this->visit_node( $dependency, $graph, $visited, $sorted );
208 + if ( isset( $graph[ $node ] ) && ! empty( $graph[ $node ] ) ) {
209 + foreach ( $graph[ $node ] as $dependency ) {
210 + $this->visit_node( $dependency, $graph, $visited, $sorted );
211 + }
198 212 }
199 213
200 214 $sorted[] = $node;
201 215 }
@@ -200,9 +214,11 @@
200 214 $sorted[] = $node;
201 215 }
202 216
203 217 /**
204 - * AJAX Handler
218 + * AJAX Handler.
219 + *
220 + * @return void
205 221 */
206 222 public function ajax_handler() {
207 223 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
208 224 if ( ! isset( $_REQUEST['nonce'] ) || ! wp_verify_nonce( wp_unslash( $_REQUEST['nonce'] ), 'content_control_upgrades' ) ) {
@@ -213,35 +229,74 @@
213 229 wp_send_json_error( __( 'You do not have permission to run upgrades.', 'content-control' ) );
214 230 }
215 231
216 232 try {
217 - $stream = new \ContentControl\Services\UpgradeStream( 'upgrades' );
218 233 $upgrades = $this->get_required_upgrades();
219 234 $count = count( $upgrades );
235 + $stream = new \ContentControl\Services\UpgradeStream( 'upgrades' );
236 + $stream->start();
220 237
221 238 // First do/while loop starts the stream and breaks if connection aborted.
222 239 do {
223 - $stream->start();
224 240 $stream->start_upgrades( $count, __( 'Upgrades started', 'content-control' ) );
225 241
226 242 $failed_upgrades = [];
227 243
228 244 // This second while loop runs the upgrades.
229 - while ( ! empty( $upgrades ) ) {
230 - $upgrade = array_shift( $upgrades );
245 + while ( ! empty( $this->get_required_upgrades() ) ) {
246 + $required_upgrades = $this->get_required_upgrades();
247 + $upgrade = array_shift( $required_upgrades );
248 + $upgrade_name = get_upgrade_name( $upgrade );
231 249
250 + if ( ! isset( $failed_upgrades[ $upgrade_name ] ) ) {
251 + $failed_upgrades[ $upgrade_name ] = 0;
252 + } elseif ( $failed_upgrades[ $upgrade_name ] > 0 ) {
253 + $stream->send_event(
254 + 'task:retry',
255 + [
256 + 'message' => __( 'Retrying upgrade', 'content-control' ),
257 + 'data' => [
258 + 'key' => $upgrade_name,
259 + 'label' => $upgrade->label(),
260 + ],
261 + ]
262 + );
263 + }
264 +
265 + if ( $failed_upgrades[ $upgrade_name ] > 2 ) {
266 + $stream->send_error( [
267 + 'message' => __( 'Some upgrades failed to complete.', 'content-control' ),
268 + ] );
269 +
270 + $stream->send_event( 'upgrades:error', [
271 + 'message' => __( 'Upgrade did not complete, see error logs above.', 'content-control' ),
272 + 'data' => $failed_upgrades,
273 + ] );
274 + return;
275 + }
276 +
232 277 $result = $upgrade->stream_run( $stream );
233 278
234 279 if ( is_wp_error( $result ) ) {
235 - $stream->send_error( $result );
280 + $stream->send_error( [
281 + 'message' => sprintf(
282 + // translators: %s: error message.
283 + __( 'Some upgrades failed to complete: %s', 'content-control' ),
284 + $result->get_error_message()
285 + ),
286 + 'data' => $result,
287 + ] );
236 288 } elseif ( false !== $result ) {
237 289 mark_upgrade_complete( $upgrade );
238 290 } else {
239 291 // False means the upgrade failed.
240 - $failed_upgrades[] = get_upgrade_name( $upgrade );
292 + ++$failed_upgrades[ $upgrade_name ];
241 293 }
242 294 }
243 295
296 + // Filter out any upgrades that have fail counts of 0.
297 + $failed_upgrades = array_filter( $failed_upgrades );
298 +
244 299 if ( ! empty( $failed_upgrades ) ) {
245 300 $stream->send_error( [
246 301 'message' => __( 'Some upgrades failed to complete.', 'content-control' ),
247 302 'data' => $failed_upgrades,
@@ -252,14 +307,20 @@
252 307 $stream->complete_upgrades( __( 'Upgrades complete!', 'content-control' ) );
253 308 }
254 309 } while ( ! $stream->should_abort() );
255 310 } catch ( \Exception $e ) {
256 - $stream->send_error( $e );
311 + if ( isset( $stream ) ) {
312 + $stream->send_error( $e );
313 + } else {
314 + wp_send_json_error( $e );
315 + }
257 316 }
258 317 }
259 318
260 319 /**
261 - * AJAX Handler
320 + * AJAX Handler.
321 + *
322 + * @return void
262 323 */
263 324 public function ajax_handler_demo() {
264 325 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
265 326 if ( ! isset( $_REQUEST['nonce'] ) || ! wp_verify_nonce( wp_unslash( $_REQUEST['nonce'] ), 'content_control_upgrades' ) ) {
@@ -310,17 +371,26 @@
310 371
311 372 $stream->complete_upgrades( __( 'Upgrades complete!', 'content-control' ) );
312 373 } while ( ! $stream->should_abort() && ! empty( $upgrades ) );
313 374 } catch ( \Exception $e ) {
314 - $stream->send_error( $e );
375 + if ( isset( $stream ) ) {
376 + $stream->send_error( [
377 + 'message' => $e->getMessage(),
378 + 'data' => $e,
379 + ] );
380 + } else {
381 + wp_send_json_error( $e );
382 + }
315 383 }
316 384 }
317 385
318 386 /**
319 387 * Render admin notices if available.
388 + *
389 + * @return void
320 390 */
321 391 public function admin_notices() {
322 - if ( ! is_admin() ) {
392 + if ( ! is_admin() || ! current_user_can( $this->container->get_permission( 'manage_settings' ) ) ) {
323 393 return;
324 394 }
325 395
326 396 if ( ! $this->has_upgrades() ) {
@@ -377,11 +447,9 @@
377 447 </div>
378 448
379 449 <div class="notice-content">
380 450 <p>
381 - <strong>
382 - <?php esc_html_e( 'Content Control has been updated and needs to run some database upgrades.', 'content-control' ); ?>
383 - </strong>
451 + <strong><?php esc_html_e( 'Content Control has been updated and needs to run some database upgrades.', 'content-control' ); ?></strong>
384 452 </p>
385 453 <ul class="notice-actions">
386 454 <li>
387 455 <a class="content-control-go-to-settings button button-tertiary" href="<?php echo esc_attr( admin_url( 'options-general.php?page=content-control-settings' ) ); ?>" data-reason="am_now">
@@ -396,13 +464,17 @@
396 464
397 465 /**
398 466 * Add localized vars to settings page if there are upgrades to run.
399 467 *
400 - * @param array $vars Localized vars.
468 + * @param array<string,mixed> $vars Localized vars.
401 469 *
402 - * @return array
470 + * @return array<string,mixed>
403 471 */
404 472 public function localize_vars( $vars ) {
473 + if ( ! is_admin() || ! current_user_can( $this->container->get_permission( 'manage_settings' ) ) ) {
474 + return $vars;
475 + }
476 +
405 477 $vars['hasUpgrades'] = false;
406 478
407 479 if ( ! $this->has_upgrades() ) {
408 480 return $vars;