PluginProbe
JCH Optimize / trunk
JCH Optimize vtrunk
6.0.2 6.0.1 trunk 1.0.1 1.0.2 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.2.0 1.2.1 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.1.0 2.10.0 2.2.0 2.2.1 All 58 releases
jch-optimize / src / Plugin / Admin.php

Admin.php in JCH Optimize trunk, at src/Plugin/Admin.php

477 lines 14.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * JCH Optimize - Performs several front-end optimizations for fast downloads
5 *
6 * @package jchoptimize/wordpress-platform
7 * @author Samuel Marshall <samuel@jch-optimize.net>
8 * @copyright Copyright (c) 2020 Samuel Marshall / JCH Optimize
9 * @license GNU/GPLv3, or later. See LICENSE file
10 *
11 * If LICENSE file missing, see <http://www.gnu.org/licenses/>.
12 */
13
14 namespace JchOptimize\WordPress\Plugin;
15
16 use _JchOptimizeVendor\V91\Joomla\DI\ContainerAwareInterface;
17 use _JchOptimizeVendor\V91\Joomla\DI\ContainerAwareTrait;
18 use _JchOptimizeVendor\V91\Joomla\Input\Input;
19 use _JchOptimizeVendor\V91\Psr\Log\LoggerAwareInterface;
20 use _JchOptimizeVendor\V91\Psr\Log\LoggerAwareTrait;
21 use Exception;
22 use JchOptimize\Core\Admin\Ajax\Ajax;
23 use JchOptimize\Core\Mvc\Controller;
24 use JchOptimize\Core\Platform\PathsInterface;
25 use JchOptimize\Core\Registry;
26 use JchOptimize\WordPress\Container\ContainerFactory;
27 use JchOptimize\WordPress\Contracts\WillEnqueueAssets;
28 use JchOptimize\WordPress\Controller\PageCache;
29 use JchOptimize\WordPress\ControllerResolver;
30 use JchOptimize\WordPress\Admin\Settings\ControlRenderer;
31 use JchOptimize\WordPress\Admin\Settings\SectionCallbacks;
32 use JchOptimize\WordPress\Admin\Settings\FieldCallbacks;
33 use JchOptimize\WordPress\Admin\Settings\ConfigurationSchema;
34 use JetBrains\PhpStorm\NoReturn;
35
36 use function __;
37 use function add_action;
38 use function add_options_page;
39 use function add_settings_field;
40 use function add_settings_section;
41 use function add_submenu_page;
42 use function admin_url;
43 use function check_admin_referer;
44 use function current_user_can;
45 use function delete_transient;
46 use function esc_url_raw;
47 use function get_transient;
48 use function plugin_basename;
49 use function register_setting;
50 use function wp_add_inline_script;
51 use function wp_create_nonce;
52 use function wp_enqueue_script;
53 use function wp_enqueue_style;
54 use function wp_register_script;
55 use function wp_register_style;
56
57 use const JCH_PRO;
58 use const JCH_VERSION;
59
60 class Admin implements LoggerAwareInterface, ContainerAwareInterface
61 {
62 use LoggerAwareTrait;
63 use ContainerAwareTrait;
64
65 private string $hookSuffix = '';
66
67 private ?Controller $resolvedController = null;
68
69 public function __construct(
70 private Registry $params,
71 private ControllerResolver $controllerResolver,
72 private PathsInterface $paths,
73 ) {
74 }
75
76 public static function publishAdminNotices(): void
77 {
78 try {
79 if ($messages = get_transient('jch-optimize_notices')) {
80 foreach ($messages as $message) {
81 echo <<<HTML
82 <div class="notice notice-{$message['type']} is-dismissible"><p>{$message['message']}</p></div>
83 HTML;
84 }
85
86 delete_transient('jch-optimize_notices');
87 }
88 } catch (Exception $e) {
89 }
90 }
91
92 public function addAdminMenu(): void
93 {
94 $menuTitle = JCH_PRO ? 'JCH Optimize Pro' : 'JCH Optimize';
95 $this->hookSuffix = (string)add_options_page(
96 __('JCH Optimize Settings', 'jch-optimize'),
97 $menuTitle,
98 'manage_options',
99 'jch_optimize',
100 [$this, 'loadAdminPage']
101 );
102
103 add_action('admin_enqueue_scripts', [$this, 'loadResourceFiles']);
104
105 if ($this->hookSuffix !== '') {
106 add_action("load-{$this->hookSuffix}", [$this, 'initializeSettings']);
107 }
108
109 add_action('admin_init', [$this, 'checkMessages']);
110 }
111
112 public function addPageCacheMenu(): void
113 {
114 if (current_user_can('manage_options')) {
115 return;
116 }
117
118 $this->hookSuffix = (string)add_submenu_page(
119 'tools.php', // parent
120 __('JCH Optimize Page Cache', 'jch-optimize'), // page title
121 __('Page Cache', 'jch-optimize'), // menu title
122 'jch_clear_page_cache', // capability
123 'jch_optimize_page_cache', // slug
124 [$this, 'renderPageCacheOnly']
125 );
126
127 if ($this->hookSuffix !== '') {
128 add_action("load-{$this->hookSuffix}", [$this, 'initializeSettings']);
129 add_action("load-{$this->hookSuffix}", [
130 $this->getContainer()->get(PageCache::class),
131 'enqueueAssets'
132 ]);
133 }
134
135 // Show a card on Tools → Available Tools
136 add_action('tool_box', function () {
137 if (!current_user_can('jch_clear_page_cache')) {
138 return;
139 }
140
141 $url = menu_page_url('jch_optimize_page_cache', false);
142 ?>
143 <div class="card">
144 <h2 class="title"><?php
145 esc_html_e('JCH Optimize Page Cache', 'jch-optimize'); ?></h2>
146 <p><?php
147 esc_html_e('Delete specific cached pages without accessing full settings.', 'jch-optimize'); ?></p>
148 <p><a href="<?php
149 echo esc_url($url); ?>">
150 <?php
151 esc_html_e('Open Page Cache', 'jch-optimize'); ?>
152 </a></p>
153 </div>
154 <?php
155 });
156 }
157
158
159 public function loadAdminPage(): void
160 {
161 try {
162 $this->resolvedController?->execute() ?? $this->controllerResolver->resolve();
163 } catch (Exception $e) {
164 $class = get_class($e);
165 echo <<<HTML
166 <h1>Application Error</h1>
167 <p>Please submit the following error message and trace in a support request:</p>
168 <div class="alert alert-danger"> {$class} &mdash; {$e->getMessage()} </div>
169 <pre class="well"> {$e->getTraceAsString()} </pre>
170 HTML;
171 }
172 }
173
174 public function renderPageCacheOnly(): void
175 {
176 $this->getContainer()->get(PageCache::class)
177 ->setRedirect('tools.php?page=jch_optimize_page_cache')
178 ->setLayout('page_cache_only.php')
179 ->execute();
180 }
181
182 public function registerOptions(): void
183 {
184 //Buffer output to allow for redirection
185 ob_start();
186
187 register_setting('jchOptimizeOptionsPage', 'jch-optimize_settings', ['type' => 'array']);
188 }
189
190 /**
191 * @param string $hookSuffix The current admin page
192 *
193 * @return void
194 */
195 public function loadResourceFiles(string $hookSuffix): void
196 {
197 if ($this->hookSuffix !== $hookSuffix) {
198 return;
199 }
200
201 $this->enqueueGlobalAssets();
202
203 if ($this->resolvedController instanceof WillEnqueueAssets) {
204 $this->resolvedController->enqueueAssets();
205 }
206 }
207
208 private function enqueueGlobalAssets(): void
209 {
210 wp_enqueue_style('jch-bootstrap');
211 wp_enqueue_style('jch-admin');
212 wp_enqueue_style('jch-fonts');
213 //wp_enqueue_style('jch-choices-base');
214 wp_enqueue_style('jch-dashicons-wordpress');
215 wp_enqueue_style('jch-dashicons');
216 wp_enqueue_style('jch-wordpress');
217
218 wp_enqueue_script('jch-platform-wordpress');
219 wp_enqueue_script('jch-bootstrap');
220 wp_enqueue_script('jch-admin-utility');
221
222 wp_enqueue_script('jch-collapsible-js');
223 }
224
225 public function initializeSettings(): void
226 {
227 //Css files
228 wp_register_style(
229 'jch-bootstrap',
230 JCH_PLUGIN_URL . 'media/bootstrap/css/bootstrap.min.css',
231 [],
232 JCH_VERSION
233 );
234
235 wp_register_style('jch-admin', JCH_PLUGIN_URL . 'media/core/css/admin.css', [], JCH_VERSION);
236 wp_register_style('jch-fonts', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css');
237 //wp_register_style('jch-choices-base', JCH_PLUGIN_URL . 'media/choices.js/styles/base.css', [], JCH_VERSION);
238 wp_register_style('jch-wordpress', JCH_PLUGIN_URL . 'media/css/wordpress.css', ['jch-dashicons'], JCH_VERSION);
239 wp_register_style(
240 'jch-dashicons-wordpress',
241 JCH_PLUGIN_URL . 'media/css/dashicons-wordpress.css',
242 [],
243 JCH_VERSION
244 );
245 wp_register_style(
246 'jch-dashicons',
247 JCH_PLUGIN_URL . 'media/core/css/dashicons.css',
248 ['jch-dashicons-wordpress'],
249 JCH_VERSION
250 );
251
252 //JavaScript files
253 wp_register_script(
254 'jch-bootstrap',
255 JCH_PLUGIN_URL . 'media/bootstrap/js/bootstrap.bundle.min.js',
256 ['jquery'],
257 JCH_VERSION,
258 true
259 );
260 wp_register_script(
261 'jch-platform-wordpress',
262 JCH_PLUGIN_URL . 'media/js/platform-wordpress.js',
263 ['jquery'],
264 JCH_VERSION,
265 true
266 );
267 wp_register_script(
268 'jch-admin-utility',
269 JCH_PLUGIN_URL . 'media/core/js/admin-utility.js',
270 ['jquery'],
271 JCH_VERSION,
272 true
273 );
274
275 $this->resolvedController = $this->controllerResolver->getController();
276
277 $loader_image = $this->paths->mediaUrl() . '/core/images/loader.gif';
278 $multiselect_nonce = wp_create_nonce('jch_optimize_multiselect');
279 $optimize_image_nonce = wp_create_nonce('jch_optimize_image');
280 $imageLoaderJs = <<<JS
281 const jch_optimize_image_url_nonce = '{$optimize_image_nonce}';
282 const jch_loader_image_url = "{$loader_image}";
283 const jch_multiselect_url_nonce = '{$multiselect_nonce}';
284 JS;
285
286 wp_add_inline_script('jch-platform-wordpress', $imageLoaderJs, 'before');
287
288 $popoverJs = <<<JS
289 window.onload = function(){
290 var popoverTriggerList = [].slice.call(document.querySelectorAll('.hasPopover'));
291 var popoverList = popoverTriggerList.map(function(popoverTriggerEl){
292 return new bootstrap.Popover(popoverTriggerEl, {
293 html: true,
294 container: '#jch-bs-admin-ui',
295 placement: 'right',
296 trigger: 'hover focus'
297 });
298 });
299 }
300 JS;
301 wp_add_inline_script('jch-bootstrap', $popoverJs);
302
303 /** @psalm-var array<string, array<string, array{0:string, 1:string, 2?:bool}>> $aSettingsArray */
304 $aSettingsArray = ConfigurationSchema::getSettingsArray();
305
306 foreach ($aSettingsArray as $section => $aSettings) {
307 add_settings_section('jch-optimize_' . $section . '_section', '', [
308 SectionCallbacks::class,
309 $section
310 ], 'jchOptimizeOptionsPage');
311
312
313 foreach ($aSettings as $setting => $args) {
314 list($title, $description, $new, $class) = array_pad($args, 4, false);
315
316 $id = 'jch-optimize_' . $setting;
317 $title = ControlRenderer::description($title, $description, $new);
318 $args = [];
319
320 if ($class !== false) {
321 $args['class'] = $class;
322 }
323
324 add_settings_field($id, $title, [
325 FieldCallbacks::class,
326 $setting
327 ], 'jchOptimizeOptionsPage', 'jch-optimize_' . $section . '_section', $args);
328 }
329 }
330 }
331
332 public function getCurrentAdminUri(): string
333 {
334 $uri = isset($_SERVER['REQUEST_URI']) ? esc_url_raw(wp_unslash($_SERVER['REQUEST_URI'])) : '';
335 $uri = preg_replace('|^.*/wp-admin/|i', '', $uri);
336
337 if (!$uri) {
338 return '';
339 }
340
341 return $uri;
342 }
343
344 public function checkMessages(): void
345 {
346 if (get_transient('jch-optimize_notices')) {
347 add_action('admin_notices', [__CLASS__, 'publishAdminNotices']);
348 }
349 }
350
351 /**
352 * @param string[] $actionLinks An array of plugin action links. By default, this can include 'activate', 'deactivate', and 'delete'.
353 * With Multisite active this can also include 'network_active' and 'network_only' items.
354 * @param string $pluginFile Path to the plugin file relative to the plugins directory
355 *
356 * @return array
357 */
358 public function loadActionLinks(array $actionLinks, string $pluginFile): array
359 {
360 static $this_plugin;
361
362 if (!$this_plugin) {
363 $this_plugin = plugin_basename(JCH_PLUGIN_FILE);
364 }
365
366 if ($pluginFile == $this_plugin) {
367 $settingsLink = '<a href="' . admin_url('options-general.php?page=jch_optimize') . '">' . __(
368 'Settings'
369 ) . '</a>';
370 array_unshift($actionLinks, $settingsLink);
371 }
372
373 return $actionLinks;
374 }
375
376 #[NoReturn]
377 public function doAjaxFileTree(): void
378 {
379 check_admin_referer('jch_optimize_filetree');
380
381 if (current_user_can('manage_options')) {
382 echo Ajax::getInstance('FileTree')->run();
383 }
384
385 die();
386 }
387
388 #[NoReturn]
389 public function doAjaxMultiSelect(): void
390 {
391 check_admin_referer('jch_optimize_multiselect');
392 $_POST = wp_unslash($_POST);
393 $_REQUEST = wp_unslash($_REQUEST);
394
395 if (current_user_can('manage_options')) {
396 echo Ajax::getInstance('MultiSelect')->run();
397 }
398
399 die();
400 }
401
402 #[NoReturn]
403 public function doAjaxOptimizeImages(): void
404 {
405 check_admin_referer('jch_optimize_image');
406
407 if (current_user_can('manage_options')) {
408 Ajax::getInstance('OptimizeImage')->run();
409 }
410
411 die();
412 }
413
414 #[NoReturn]
415 public function doAjaxConfigureSettings(): void
416 {
417 if (current_user_can('manage_options')) {
418 $container = ContainerFactory::getInstance();
419 $container->get(ControllerResolver::class)->resolve();
420 }
421
422 die();
423 }
424
425 #[NoReturn]
426 public function doAjaxOnClickIcon(): void
427 {
428 if (current_user_can('manage_options')) {
429 $container = ContainerFactory::getInstance();
430 check_admin_referer($container->get(Input::class)->get('task'));
431 $container->get(ControllerResolver::class)->resolve();
432 }
433
434 die();
435 }
436
437 #[NoReturn]
438 public function doAjaxGetCacheInfo(): void
439 {
440 $container = ContainerFactory::getInstance();
441 $container->get('getcacheinfo')->execute();
442
443 die();
444 }
445
446 #[NoReturn]
447 public function doAjaxJchConfigureJsTableBody(): void
448 {
449 $container = ContainerFactory::getInstance();
450 $container->get('criticaljstablebody')->execute();
451
452 die();
453 }
454
455 #[NoReturn]
456 public function doAjaxJchConfigureJsAutoSave(): void
457 {
458 $container = ContainerFactory::getInstance();
459 $container->get('criticaljsautosave')->execute();
460
461 die();
462 }
463
464 #[NoReturn]
465 public function doAjaxJchCfVerify(): void
466 {
467 check_admin_referer('jch_optimize_verify_cf_token');
468 if (current_user_can('manage_options')) {
469 $container = ContainerFactory::getInstance();
470 $container->get(Input::class)->def('task', 'CfVerifyToken');
471 $container->get(ControllerResolver::class)->resolve();
472 }
473
474 die();
475 }
476 }
477