| 1 |
<?php |
| 2 |
/** |
| 3 |
* Deactivation-survey module. |
| 4 |
* |
| 5 |
* Asks why, once, on the way out: intercepts the Deactivate link on plugins.php, |
| 6 |
* offers an optional reason + free-text note, then lets the deactivation proceed. |
| 7 |
* Ported from `latest`, where it lived as `includes/Core/DeactivationSurvey.php` |
| 8 |
* plus a `react-src/deactivate-survey/` entry — neither of which has a home in the |
| 9 |
* modular layout, so the feature becomes its own module here. |
| 10 |
* |
| 11 |
* Self-contained on purpose: it owns its endpoint, its bundle, and its enqueue. |
| 12 |
* Nothing else in the plugin runs on plugins.php, and nothing here runs anywhere |
| 13 |
* else. |
| 14 |
* |
| 15 |
* @package Templately |
| 16 |
*/ |
| 17 |
|
| 18 |
namespace Templately\Modules\DeactivationSurvey; |
| 19 |
|
| 20 |
use Templately\Core\Module_Base; |
| 21 |
use Templately\Modules\DeactivationSurvey\REST\Survey; |
| 22 |
|
| 23 |
class Module extends Module_Base { |
| 24 |
|
| 25 |
public function get_name(): string { |
| 26 |
return 'deactivation-survey'; |
| 27 |
} |
| 28 |
|
| 29 |
protected function init_hooks(): void { |
| 30 |
add_action( 'admin_enqueue_scripts', [ $this, 'enqueue' ] ); |
| 31 |
} |
| 32 |
|
| 33 |
public function register_rest_routes(): void { |
| 34 |
Survey::get_instance()->register_routes(); |
| 35 |
} |
| 36 |
|
| 37 |
/** |
| 38 |
* Load the survey on the plugins screen, and only there. |
| 39 |
* |
| 40 |
* Deliberately its own `admin_enqueue_scripts` callback rather than an entry in |
| 41 |
* `Core\Admin`'s allowed-hooks list: that list governs the SPA's assets, and |
| 42 |
* adding plugins.php to it would pull the whole admin bundle onto a screen that |
| 43 |
* needs a 30KB widget. The module owning its own enqueue is also what keeps |
| 44 |
* this feature deletable in one directory. |
| 45 |
* |
| 46 |
* @param string $hook Current admin page. |
| 47 |
*/ |
| 48 |
public function enqueue( $hook = '' ) { |
| 49 |
if ( 'plugins.php' !== $hook ) { |
| 50 |
return; |
| 51 |
} |
| 52 |
|
| 53 |
if ( ! current_user_can( 'deactivate_plugins' ) ) { |
| 54 |
return; |
| 55 |
} |
| 56 |
|
| 57 |
templately()->assets->enqueue( |
| 58 |
'templately-inter', |
| 59 |
set_url_scheme( '//fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap' ) |
| 60 |
); |
| 61 |
/** |
| 62 |
* plugins.php is not our screen, so the survey gets the SCOPED Tailwind build, |
| 63 |
* never `css/tailwind.css`. The unscoped bundle is compiled with |
| 64 |
* `important: true` and emits its utilities unlayered, so every name WordPress |
| 65 |
* also uses won the cascade: `.inline` beat `.notice.inline` and `.hidden`, |
| 66 |
* collapsing the update notice in each plugin row to a coloured sliver and |
| 67 |
* revealing the auto-update error placeholders meant to stay hidden. |
| 68 |
* `css/tailwind-survey.css` is the same source and config with every selector |
| 69 |
* prefixed by `.templately-scope`, the container the modal portal mounts into. |
| 70 |
*/ |
| 71 |
templately()->assets->enqueue( 'templately-tailwind-survey', 'css/tailwind-survey.css', [ 'templately-inter' ] ); |
| 72 |
templately()->assets->enqueue( 'templately-deactivation-survey', 'css/deactivation-survey-style.css', [ 'templately-tailwind-survey' ] ); |
| 73 |
templately()->assets->enqueue( 'templately', 'js/deactivation-survey.js', [ 'regenerator-runtime' ], true ); |
| 74 |
templately()->assets->localize( 'templately', 'templatelyDeactivationSurvey', [ |
| 75 |
'nonce' => wp_create_nonce( 'wp_rest' ), |
| 76 |
'restUrl' => esc_url_raw( rest_url( 'templately/v1/deactivation-feedback' ) ), |
| 77 |
'pluginBasename' => TEMPLATELY_PLUGIN_BASENAME, |
| 78 |
'reasons' => Survey::get_reasons_for_js(), |
| 79 |
] ); |
| 80 |
} |
| 81 |
} |
| 82 |
|