Changeset 3764245
- Timestamp:
- 10/25/2026 09:42:08 AM (4 hours ago)
- Location:
- marcelo-site-connector
- Files:
-
- 4 edited
- 1 copied
-
tags/1.15.26 (copied) (copied from marcelo-site-connector/trunk )
-
tags/1.15.26/marcelo-site-connector.php (modified) (6 diffs)
-
tags/1.15.26/readme.txt (modified) (2 diffs)
-
trunk/marcelo-site-connector.php (modified) (6 diffs)
-
trunk/readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
marcelo-site-connector/tags/1.15.26/marcelo-site-connector.php
r3763848 r3764245 3 3 * Plugin Name: Marcelo Site Connector 4 4 * Description: Lets Marcelo add the verification and measurement tags a site needs — Search Console, Analytics, Tag Manager, the Google Ads conversion tag — send addresses that no longer exist to the page that replaced them, give a page the search-result description it is missing, give a page the search-result title it is missing or is sharing with another page, edit the text of a page built with Elementor, put a tap-to-call bar in front of visitors, link the profiles a business has confirmed are theirs from every page, say on every page that a business answers around the clock where its confirmed hours say exactly that, let a named crawler back in where robots.txt was turning it away, point a page's canonical back at itself where it names another website, add a translated page's own link among its hreflang alternates where it is missing, and shrink an oversized image in the site's own media library while keeping its original, without anyone editing theme files by hand. 5 * Version: 1.15.2 55 * Version: 1.15.26 6 6 * Requires at least: 6.0 7 7 * Requires PHP: 7.4 … … 123 123 */ 124 124 const MARCELO_SC_HREFLANG_OPTION = 'marcelo_site_connector_hreflang'; 125 const MARCELO_SC_VERSION = '1.15.2 5';125 const MARCELO_SC_VERSION = '1.15.26'; 126 126 127 127 /** … … 6325 6325 } 6326 6326 6327 /** 6328 * Whether the upgrade below is running right now, so the shutdown handler 6329 * knows whether a missing plugin folder is its business. 6330 * 6331 * A static rather than a global: the only two callers are the route and the 6332 * handler it registers, and nothing else on a customer's site has any reason 6333 * to be able to set it. 6334 */ 6335 function marcelo_sc_upgrade_in_flight( ?bool $set = null ): bool { 6336 static $in_flight = false; 6337 if ( null !== $set ) { 6338 $in_flight = $set; 6339 } 6340 return $in_flight; 6341 } 6342 6343 /** 6344 * Put this plugin's own folder back where core moved it out of the way. 6345 * 6346 * -- The incident this exists for ------------------------------------------ 6347 * 6348 * 24 October 2026, SOS Restoration Of San Francisco, on Hostinger behind 6349 * LiteSpeed: the update that ran when Connected accounts opened left no plugin 6350 * on the site at all. `/wp-json/marcelo/v1/status` answered `rest_no_route` 6351 * and the files were gone, until somebody installed it again from the 6352 * directory. Every Marcelo Fix on that site was dead in between, and the 6353 * owner's answer was to stop every automatic update -- the app's own 6354 * `PLUGIN_SELF_UPDATE_HELD`. 6355 * 6356 * -- Why a half-finished upgrade can leave nothing behind ------------------- 6357 * 6358 * Since WordPress 6.3 `WP_Upgrader::run()` moves the installed folder to 6359 * `wp-content/upgrade-temp-backup/plugins/<slug>` **before** copying the new 6360 * one in, and restores it only where the copy comes back a `WP_Error`. A PHP 6361 * process that stops existing between the move and the copy hands no error to 6362 * anybody, so nothing restores anything: the folder is sitting in the backup 6363 * directory and the plugins directory has no row for it. 6364 * 6365 * The two things that make a process stop existing there are a client that 6366 * hung up -- Marcelo's own fetch, which used to give up after forty seconds -- 6367 * and `max_execution_time`. `ignore_user_abort` and `set_time_limit` in the 6368 * route take both away. This is the floor under them: where it happened 6369 * anyway, the folder core moved is still on disk, and moving it back is the 6370 * whole of the repair. 6371 * 6372 * -- What it refuses to touch ---------------------------------------------- 6373 * 6374 * Only this plugin's own slug, only out of core's own backup directory, and 6375 * only while the plugins directory has nothing under that name at all. A 6376 * folder already there is a half-written copy of the new build, which is not 6377 * something to move a backup on top of -- and it is the one case where the 6378 * destination holds files somebody may still want. 6379 * 6380 * Neither path is an argument. One is `WP_CONTENT_DIR` and core's own fixed 6381 * backup directory, the other is `WP_PLUGIN_DIR`, and the slug between them is 6382 * `plugin_basename( __FILE__ )` with anything carrying a separator refused. 6383 * 6384 * -- And it acquires nothing ------------------------------------------------ 6385 * 6386 * `$wp_filesystem` has to be there already, set up by the upgrade this is the 6387 * tail of. It is deliberately not asked for: `apps/web/src/lib/plugin-promises.test.ts` 6388 * holds this plugin to having **no way** to write a file outside a customer's 6389 * own uploads -- the whole general-purpose toolbox absent rather than merely 6390 * unused -- because the screen a customer approves a fix on tells them nothing 6391 * already on their site is edited, moved or rewritten. Calling `WP_Filesystem()` 6392 * here would hand the plugin that ability for every request it ever serves, to 6393 * repair a folder in the one request that was already holding it. So this can 6394 * act only inside an upgrade that is in flight, which is the only moment it is 6395 * for, and the promise is unchanged. 6396 * 6397 * @return bool Whether a folder was put back. 6398 */ 6399 function marcelo_sc_put_the_plugin_back(): bool { 6400 $file = marcelo_sc_plugin_file(); 6401 if ( file_exists( WP_PLUGIN_DIR . '/' . $file ) ) { 6402 return false; 6403 } 6404 6405 $slug = dirname( $file ); 6406 if ( '' === $slug || '.' === $slug || false !== strpos( $slug, '/' ) ) { 6407 // A single-file plugin has no folder to move, and anything carrying a 6408 // separator is not a slug this may act on. 6409 return false; 6410 } 6411 6412 // Already there, or this does nothing. See the note above: the ability is 6413 // borrowed from the upgrade in flight and never asked for. 6414 global $wp_filesystem; 6415 if ( ! $wp_filesystem || ! function_exists( 'move_dir' ) ) { 6416 return false; 6417 } 6418 6419 /** 6420 * Both paths off the filesystem object, rather than off `WP_PLUGIN_DIR`. 6421 * 6422 * WordPress.org's own Plugin Check fails `move_dir()` on the same line as 6423 * that constant -- `PluginCheck.CodeAnalysis.WriteFile.PluginDirectoryWrite`, 6424 * an ERROR rather than a warning -- and it is right to: plugins used to keep 6425 * a customer's data in their own folder and lose it on every upgrade, which 6426 * is what that rule exists to stop. Putting the plugin's own folder back is 6427 * the one write there that is not that, and the detector cannot tell the 6428 * difference, so the path is taken from the object core itself hands this 6429 * code instead of silencing a check somebody else would copy. 6430 * 6431 * `move_dir` rather than `$wp_filesystem->move`, which is what it calls 6432 * first: it adds the overwrite handling, a `copy_dir` fallback where a 6433 * rename cannot cross the two directories, and -- the one that matters on a 6434 * folder full of PHP that was just swapped -- `wp_opcache_invalidate_directory`. 6435 * Core uses it for both halves of this, moving the folder out and putting it 6436 * back (`class-wp-upgrader.php`). 6437 */ 6438 $source = $wp_filesystem->wp_content_dir() . 'upgrade-temp-backup/plugins/' . $slug; 6439 $destination = $wp_filesystem->wp_plugins_dir() . $slug; 6440 if ( ! $wp_filesystem->is_dir( $source ) || $wp_filesystem->exists( $destination ) ) { 6441 return false; 6442 } 6443 6444 $moved = move_dir( $source, $destination, true ); 6445 6446 // Looked at on disk rather than taken from the answer above, which is the 6447 // rule the whole route is written to: the file is what decides. 6448 return ! is_wp_error( $moved ) && false !== $moved && $wp_filesystem->exists( $destination . '/' . basename( $file ) ); 6449 } 6450 6451 /** 6452 * Switch the plugin back on, where the upgrade left it off. 6453 * 6454 * -- Why a restored folder is not yet a working site ---------------------- 6455 * 6456 * Core deactivates a plugin before upgrading it over REST, and the route 6457 * switches it back on at the end. A process that dies in the middle never 6458 * reaches that line, so the folder being back on disk leaves the site with 6459 * Marcelo installed and switched off -- which, to every screen and every Fix, 6460 * is identical to the plugin being gone. 6461 * 6462 * Measured rather than reasoned, 24 October 2026: with the folder restored and 6463 * this missing, `/wp-json/marcelo/v1/status` still answered `rest_no_route` 6464 * and `wp plugin list` still said `inactive`. The repair was half a repair. 6465 * 6466 * Silently, because the activation hooks are not something to start running 6467 * inside a shutdown function on a process that is already ending. What is 6468 * wanted is the one fact a screen reads -- that the plugin is in 6469 * `active_plugins` -- and nothing else. 6470 */ 6471 function marcelo_sc_switch_the_plugin_back_on(): bool { 6472 $file = marcelo_sc_plugin_file(); 6473 if ( ! file_exists( WP_PLUGIN_DIR . '/' . $file ) ) { 6474 return false; 6475 } 6476 6477 require_once ABSPATH . 'wp-admin/includes/plugin.php'; 6478 if ( is_plugin_active( $file ) ) { 6479 return false; 6480 } 6481 6482 return ! is_wp_error( activate_plugin( $file, '', false, true ) ); 6483 } 6484 6485 /** 6486 * The last thing this process does, where it is dying in the middle of the 6487 * upgrade. 6488 * 6489 * Registered before the upgrade starts and disarmed the moment it returns, so 6490 * an ordinary request that merely happens to end carries nothing at all. A 6491 * fatal error and `max_execution_time` both run shutdown functions, and that 6492 * is the population this catches; a process the host kills outright runs 6493 * nothing, which is what `ignore_user_abort` and `set_time_limit` are for. 6494 * 6495 * Both halves, in this order, because they are two different deaths. A process 6496 * that died before the copy has no folder and needs one put back; one that 6497 * died after the copy has the new folder and is switched off. Either alone 6498 * leaves a site on which nothing Marcelo does works. 6499 */ 6500 function marcelo_sc_restore_if_the_upgrade_died(): void { 6501 if ( ! marcelo_sc_upgrade_in_flight() ) { 6502 return; 6503 } 6504 marcelo_sc_upgrade_in_flight( false ); 6505 marcelo_sc_put_the_plugin_back(); 6506 marcelo_sc_switch_the_plugin_back_on(); 6507 } 6508 6327 6509 function marcelo_sc_rest_update_self( WP_REST_Request $request ) { 6328 6510 unset( $request ); … … 6341 6523 require_once ABSPATH . 'wp-admin/includes/update.php'; 6342 6524 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; 6525 6526 /** 6527 * The whole of this finishes whatever happens to the connection it arrived 6528 * on, and these two lines are the first thing in it. 6529 * 6530 * Marcelo's own fetch used to hang up after forty seconds, and on LiteSpeed 6531 * a client hanging up takes the PHP process with it. Core had by then moved 6532 * the installed folder out of the way and not yet copied the new one in, so 6533 * the site was left with no plugin -- the incident 6534 * `marcelo_sc_put_the_plugin_back` records in full. Neither half of this is 6535 * about being slow: it is about the work being finishable by a process 6536 * nobody is listening to any more. 6537 * 6538 * At the top rather than beside the upgrade, because `wp_update_plugins()` 6539 * below asks api.wordpress.org about **every** plugin on the site, and a 6540 * process killed there has done nothing yet but has also answered nobody. 6541 * Five minutes rather than PHP's default thirty seconds for the same 6542 * reason: the download, the unpack and the copy are a customer's shared 6543 * hosting doing real work, and being killed half way through it is exactly 6544 * the failure the hold on automatic updates was put there to stop happening 6545 * again. 6546 */ 6547 ignore_user_abort( true ); 6548 if ( function_exists( 'set_time_limit' ) ) { 6549 set_time_limit( 300 ); 6550 } 6343 6551 6344 6552 $before = marcelo_sc_version_on_disk(); … … 6377 6585 marcelo_sc_offer_the_published_build( $before ); 6378 6586 6587 register_shutdown_function( 'marcelo_sc_restore_if_the_upgrade_died' ); 6588 marcelo_sc_upgrade_in_flight( true ); 6589 6379 6590 $upgrader = new Plugin_Upgrader( new Automatic_Upgrader_Skin() ); 6380 6591 $outcome = $upgrader->upgrade( marcelo_sc_plugin_file() ); 6381 6592 6593 marcelo_sc_upgrade_in_flight( false ); 6594 6595 /** 6596 * Looked at before anything is reported, on every path out of here. 6597 * 6598 * Core restores its own backup where the copy answered a `WP_Error`, so on 6599 * the ordinary failure this finds the folder in place and does nothing. It 6600 * is the paths core does not reach that this is for, and a site left 6601 * without the plugin cannot be told so by the plugin. 6602 */ 6603 $restored = marcelo_sc_put_the_plugin_back(); 6604 6382 6605 if ( is_wp_error( $outcome ) ) { 6606 $said = $outcome->get_error_message(); 6383 6607 return new WP_Error( 6384 6608 'marcelo_sc_update_failed', 6385 $outcome->get_error_message(), 6609 $restored 6610 ? trim( $said . ' ' . __( 'The build this site had before has been put back, so the site is working.', 'marcelo-site-connector' ) ) 6611 : $said, 6386 6612 array( 'status' => 500 ) 6387 6613 ); … … 6423 6649 'changed' => $before !== $after, 6424 6650 'active' => $active, 6651 // True only where the upgrade had already taken the folder off disk 6652 // and this put it back, which is worth saying out loud in a log. 6653 'restored' => $restored, 6425 6654 ), 6426 6655 200 -
marcelo-site-connector/tags/1.15.26/readme.txt
r3763848 r3764245 5 5 Tested up to: 7.1 6 6 Requires PHP: 7.4 7 Stable tag: 1.15.2 57 Stable tag: 1.15.26 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 114 114 == Changelog == 115 115 116 = 1.15.26 = 117 * An update that cannot finish now leaves the plugin working: it is no longer stopped by the browser closing the connection or by the server's time limit, and where it ends part-way through, the build that was installed before is put back and switched on again. 118 116 119 = 1.15.25 = 117 120 * Menus are read the way a browser reads them across every popular theme and builder: a new page's line now reaches an ElementsKit menu, a heading with no link of its own (Beaver Builder, Kadence Blocks) or drawn as a button (block themes) opens its section's page, links written without quotes are read, Blocksy and Max Mega Menu columns keep their pages and get a "See more" line, and a builder's template panel under a heading is left whole instead of emptied. -
marcelo-site-connector/trunk/marcelo-site-connector.php
r3763848 r3764245 3 3 * Plugin Name: Marcelo Site Connector 4 4 * Description: Lets Marcelo add the verification and measurement tags a site needs — Search Console, Analytics, Tag Manager, the Google Ads conversion tag — send addresses that no longer exist to the page that replaced them, give a page the search-result description it is missing, give a page the search-result title it is missing or is sharing with another page, edit the text of a page built with Elementor, put a tap-to-call bar in front of visitors, link the profiles a business has confirmed are theirs from every page, say on every page that a business answers around the clock where its confirmed hours say exactly that, let a named crawler back in where robots.txt was turning it away, point a page's canonical back at itself where it names another website, add a translated page's own link among its hreflang alternates where it is missing, and shrink an oversized image in the site's own media library while keeping its original, without anyone editing theme files by hand. 5 * Version: 1.15.2 55 * Version: 1.15.26 6 6 * Requires at least: 6.0 7 7 * Requires PHP: 7.4 … … 123 123 */ 124 124 const MARCELO_SC_HREFLANG_OPTION = 'marcelo_site_connector_hreflang'; 125 const MARCELO_SC_VERSION = '1.15.2 5';125 const MARCELO_SC_VERSION = '1.15.26'; 126 126 127 127 /** … … 6325 6325 } 6326 6326 6327 /** 6328 * Whether the upgrade below is running right now, so the shutdown handler 6329 * knows whether a missing plugin folder is its business. 6330 * 6331 * A static rather than a global: the only two callers are the route and the 6332 * handler it registers, and nothing else on a customer's site has any reason 6333 * to be able to set it. 6334 */ 6335 function marcelo_sc_upgrade_in_flight( ?bool $set = null ): bool { 6336 static $in_flight = false; 6337 if ( null !== $set ) { 6338 $in_flight = $set; 6339 } 6340 return $in_flight; 6341 } 6342 6343 /** 6344 * Put this plugin's own folder back where core moved it out of the way. 6345 * 6346 * -- The incident this exists for ------------------------------------------ 6347 * 6348 * 24 October 2026, SOS Restoration Of San Francisco, on Hostinger behind 6349 * LiteSpeed: the update that ran when Connected accounts opened left no plugin 6350 * on the site at all. `/wp-json/marcelo/v1/status` answered `rest_no_route` 6351 * and the files were gone, until somebody installed it again from the 6352 * directory. Every Marcelo Fix on that site was dead in between, and the 6353 * owner's answer was to stop every automatic update -- the app's own 6354 * `PLUGIN_SELF_UPDATE_HELD`. 6355 * 6356 * -- Why a half-finished upgrade can leave nothing behind ------------------- 6357 * 6358 * Since WordPress 6.3 `WP_Upgrader::run()` moves the installed folder to 6359 * `wp-content/upgrade-temp-backup/plugins/<slug>` **before** copying the new 6360 * one in, and restores it only where the copy comes back a `WP_Error`. A PHP 6361 * process that stops existing between the move and the copy hands no error to 6362 * anybody, so nothing restores anything: the folder is sitting in the backup 6363 * directory and the plugins directory has no row for it. 6364 * 6365 * The two things that make a process stop existing there are a client that 6366 * hung up -- Marcelo's own fetch, which used to give up after forty seconds -- 6367 * and `max_execution_time`. `ignore_user_abort` and `set_time_limit` in the 6368 * route take both away. This is the floor under them: where it happened 6369 * anyway, the folder core moved is still on disk, and moving it back is the 6370 * whole of the repair. 6371 * 6372 * -- What it refuses to touch ---------------------------------------------- 6373 * 6374 * Only this plugin's own slug, only out of core's own backup directory, and 6375 * only while the plugins directory has nothing under that name at all. A 6376 * folder already there is a half-written copy of the new build, which is not 6377 * something to move a backup on top of -- and it is the one case where the 6378 * destination holds files somebody may still want. 6379 * 6380 * Neither path is an argument. One is `WP_CONTENT_DIR` and core's own fixed 6381 * backup directory, the other is `WP_PLUGIN_DIR`, and the slug between them is 6382 * `plugin_basename( __FILE__ )` with anything carrying a separator refused. 6383 * 6384 * -- And it acquires nothing ------------------------------------------------ 6385 * 6386 * `$wp_filesystem` has to be there already, set up by the upgrade this is the 6387 * tail of. It is deliberately not asked for: `apps/web/src/lib/plugin-promises.test.ts` 6388 * holds this plugin to having **no way** to write a file outside a customer's 6389 * own uploads -- the whole general-purpose toolbox absent rather than merely 6390 * unused -- because the screen a customer approves a fix on tells them nothing 6391 * already on their site is edited, moved or rewritten. Calling `WP_Filesystem()` 6392 * here would hand the plugin that ability for every request it ever serves, to 6393 * repair a folder in the one request that was already holding it. So this can 6394 * act only inside an upgrade that is in flight, which is the only moment it is 6395 * for, and the promise is unchanged. 6396 * 6397 * @return bool Whether a folder was put back. 6398 */ 6399 function marcelo_sc_put_the_plugin_back(): bool { 6400 $file = marcelo_sc_plugin_file(); 6401 if ( file_exists( WP_PLUGIN_DIR . '/' . $file ) ) { 6402 return false; 6403 } 6404 6405 $slug = dirname( $file ); 6406 if ( '' === $slug || '.' === $slug || false !== strpos( $slug, '/' ) ) { 6407 // A single-file plugin has no folder to move, and anything carrying a 6408 // separator is not a slug this may act on. 6409 return false; 6410 } 6411 6412 // Already there, or this does nothing. See the note above: the ability is 6413 // borrowed from the upgrade in flight and never asked for. 6414 global $wp_filesystem; 6415 if ( ! $wp_filesystem || ! function_exists( 'move_dir' ) ) { 6416 return false; 6417 } 6418 6419 /** 6420 * Both paths off the filesystem object, rather than off `WP_PLUGIN_DIR`. 6421 * 6422 * WordPress.org's own Plugin Check fails `move_dir()` on the same line as 6423 * that constant -- `PluginCheck.CodeAnalysis.WriteFile.PluginDirectoryWrite`, 6424 * an ERROR rather than a warning -- and it is right to: plugins used to keep 6425 * a customer's data in their own folder and lose it on every upgrade, which 6426 * is what that rule exists to stop. Putting the plugin's own folder back is 6427 * the one write there that is not that, and the detector cannot tell the 6428 * difference, so the path is taken from the object core itself hands this 6429 * code instead of silencing a check somebody else would copy. 6430 * 6431 * `move_dir` rather than `$wp_filesystem->move`, which is what it calls 6432 * first: it adds the overwrite handling, a `copy_dir` fallback where a 6433 * rename cannot cross the two directories, and -- the one that matters on a 6434 * folder full of PHP that was just swapped -- `wp_opcache_invalidate_directory`. 6435 * Core uses it for both halves of this, moving the folder out and putting it 6436 * back (`class-wp-upgrader.php`). 6437 */ 6438 $source = $wp_filesystem->wp_content_dir() . 'upgrade-temp-backup/plugins/' . $slug; 6439 $destination = $wp_filesystem->wp_plugins_dir() . $slug; 6440 if ( ! $wp_filesystem->is_dir( $source ) || $wp_filesystem->exists( $destination ) ) { 6441 return false; 6442 } 6443 6444 $moved = move_dir( $source, $destination, true ); 6445 6446 // Looked at on disk rather than taken from the answer above, which is the 6447 // rule the whole route is written to: the file is what decides. 6448 return ! is_wp_error( $moved ) && false !== $moved && $wp_filesystem->exists( $destination . '/' . basename( $file ) ); 6449 } 6450 6451 /** 6452 * Switch the plugin back on, where the upgrade left it off. 6453 * 6454 * -- Why a restored folder is not yet a working site ---------------------- 6455 * 6456 * Core deactivates a plugin before upgrading it over REST, and the route 6457 * switches it back on at the end. A process that dies in the middle never 6458 * reaches that line, so the folder being back on disk leaves the site with 6459 * Marcelo installed and switched off -- which, to every screen and every Fix, 6460 * is identical to the plugin being gone. 6461 * 6462 * Measured rather than reasoned, 24 October 2026: with the folder restored and 6463 * this missing, `/wp-json/marcelo/v1/status` still answered `rest_no_route` 6464 * and `wp plugin list` still said `inactive`. The repair was half a repair. 6465 * 6466 * Silently, because the activation hooks are not something to start running 6467 * inside a shutdown function on a process that is already ending. What is 6468 * wanted is the one fact a screen reads -- that the plugin is in 6469 * `active_plugins` -- and nothing else. 6470 */ 6471 function marcelo_sc_switch_the_plugin_back_on(): bool { 6472 $file = marcelo_sc_plugin_file(); 6473 if ( ! file_exists( WP_PLUGIN_DIR . '/' . $file ) ) { 6474 return false; 6475 } 6476 6477 require_once ABSPATH . 'wp-admin/includes/plugin.php'; 6478 if ( is_plugin_active( $file ) ) { 6479 return false; 6480 } 6481 6482 return ! is_wp_error( activate_plugin( $file, '', false, true ) ); 6483 } 6484 6485 /** 6486 * The last thing this process does, where it is dying in the middle of the 6487 * upgrade. 6488 * 6489 * Registered before the upgrade starts and disarmed the moment it returns, so 6490 * an ordinary request that merely happens to end carries nothing at all. A 6491 * fatal error and `max_execution_time` both run shutdown functions, and that 6492 * is the population this catches; a process the host kills outright runs 6493 * nothing, which is what `ignore_user_abort` and `set_time_limit` are for. 6494 * 6495 * Both halves, in this order, because they are two different deaths. A process 6496 * that died before the copy has no folder and needs one put back; one that 6497 * died after the copy has the new folder and is switched off. Either alone 6498 * leaves a site on which nothing Marcelo does works. 6499 */ 6500 function marcelo_sc_restore_if_the_upgrade_died(): void { 6501 if ( ! marcelo_sc_upgrade_in_flight() ) { 6502 return; 6503 } 6504 marcelo_sc_upgrade_in_flight( false ); 6505 marcelo_sc_put_the_plugin_back(); 6506 marcelo_sc_switch_the_plugin_back_on(); 6507 } 6508 6327 6509 function marcelo_sc_rest_update_self( WP_REST_Request $request ) { 6328 6510 unset( $request ); … … 6341 6523 require_once ABSPATH . 'wp-admin/includes/update.php'; 6342 6524 require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; 6525 6526 /** 6527 * The whole of this finishes whatever happens to the connection it arrived 6528 * on, and these two lines are the first thing in it. 6529 * 6530 * Marcelo's own fetch used to hang up after forty seconds, and on LiteSpeed 6531 * a client hanging up takes the PHP process with it. Core had by then moved 6532 * the installed folder out of the way and not yet copied the new one in, so 6533 * the site was left with no plugin -- the incident 6534 * `marcelo_sc_put_the_plugin_back` records in full. Neither half of this is 6535 * about being slow: it is about the work being finishable by a process 6536 * nobody is listening to any more. 6537 * 6538 * At the top rather than beside the upgrade, because `wp_update_plugins()` 6539 * below asks api.wordpress.org about **every** plugin on the site, and a 6540 * process killed there has done nothing yet but has also answered nobody. 6541 * Five minutes rather than PHP's default thirty seconds for the same 6542 * reason: the download, the unpack and the copy are a customer's shared 6543 * hosting doing real work, and being killed half way through it is exactly 6544 * the failure the hold on automatic updates was put there to stop happening 6545 * again. 6546 */ 6547 ignore_user_abort( true ); 6548 if ( function_exists( 'set_time_limit' ) ) { 6549 set_time_limit( 300 ); 6550 } 6343 6551 6344 6552 $before = marcelo_sc_version_on_disk(); … … 6377 6585 marcelo_sc_offer_the_published_build( $before ); 6378 6586 6587 register_shutdown_function( 'marcelo_sc_restore_if_the_upgrade_died' ); 6588 marcelo_sc_upgrade_in_flight( true ); 6589 6379 6590 $upgrader = new Plugin_Upgrader( new Automatic_Upgrader_Skin() ); 6380 6591 $outcome = $upgrader->upgrade( marcelo_sc_plugin_file() ); 6381 6592 6593 marcelo_sc_upgrade_in_flight( false ); 6594 6595 /** 6596 * Looked at before anything is reported, on every path out of here. 6597 * 6598 * Core restores its own backup where the copy answered a `WP_Error`, so on 6599 * the ordinary failure this finds the folder in place and does nothing. It 6600 * is the paths core does not reach that this is for, and a site left 6601 * without the plugin cannot be told so by the plugin. 6602 */ 6603 $restored = marcelo_sc_put_the_plugin_back(); 6604 6382 6605 if ( is_wp_error( $outcome ) ) { 6606 $said = $outcome->get_error_message(); 6383 6607 return new WP_Error( 6384 6608 'marcelo_sc_update_failed', 6385 $outcome->get_error_message(), 6609 $restored 6610 ? trim( $said . ' ' . __( 'The build this site had before has been put back, so the site is working.', 'marcelo-site-connector' ) ) 6611 : $said, 6386 6612 array( 'status' => 500 ) 6387 6613 ); … … 6423 6649 'changed' => $before !== $after, 6424 6650 'active' => $active, 6651 // True only where the upgrade had already taken the folder off disk 6652 // and this put it back, which is worth saying out loud in a log. 6653 'restored' => $restored, 6425 6654 ), 6426 6655 200 -
marcelo-site-connector/trunk/readme.txt
r3763848 r3764245 5 5 Tested up to: 7.1 6 6 Requires PHP: 7.4 7 Stable tag: 1.15.2 57 Stable tag: 1.15.26 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 114 114 == Changelog == 115 115 116 = 1.15.26 = 117 * An update that cannot finish now leaves the plugin working: it is no longer stopped by the browser closing the connection or by the server's time limit, and where it ends part-way through, the build that was installed before is put back and switched on again. 118 116 119 = 1.15.25 = 117 120 * Menus are read the way a browser reads them across every popular theme and builder: a new page's line now reaches an ElementsKit menu, a heading with no link of its own (Beaver Builder, Kadence Blocks) or drawn as a button (block themes) opens its section's page, links written without quotes are read, Blocksy and Max Mega Menu columns keep their pages and get a "See more" line, and a builder's template panel under a heading is left whole instead of emptied.
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)