PluginProbe
Microthemer Lite – Visual Editor to Customize CSS / trunk
Microthemer Lite – Visual Editor to Customize CSS vtrunk
trunk 5.0.0.2
microthemer / src / AssetAuth.php

AssetAuth.php in Microthemer Lite – Visual Editor to Customize CSS trunk, at src/AssetAuth.php

617 lines 19.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /*
4 * AssetAuth
5 *
6 * For logged in administrators
7 * Load asset editing resources on the frontend and admin area (if edit $context is passed into the construct method)
8 * This class loads on the admin area even when not editing, so that a response can be given to MT from the admin area too
9 */
10
11 namespace Microthemer;
12
13 class AssetAuth extends AssetLoad {
14
15 use PluginTrait;
16
17 var $context = 'assetAuth';
18
19 var $builderBlockedEdit = false;
20 var $assetLoadingKey = 'asset_loading';
21 var $draft = true;
22 var $globalStylesheetRequiredKey = "global_stylesheet_required";
23 var $globalJSRequiredKey = "load_js";
24
25 function __construct($context){
26
27 $this->context = $context;
28
29 // no need to run MT frontend script on Oxygen intermediate iframe
30 // only one the actual site preview
31 if (isset($_GET['ct_builder']) && !isset($_GET['oxygen_iframe'])){
32 return;
33 }
34
35 // run common init with standalone asset loader
36 parent::__construct();
37
38 // initialise functionality for administrator
39 $this->initAuth();
40 }
41
42 // editing-specific functionality
43 function initAuth(){
44
45 // get the directory paths
46 include dirname(__FILE__) .'/../get-dir-paths.inc.php';
47
48 if ($this->hasContentCapability()){
49 $this->contentClass = new Content\AssetAuthContent($this, TVR_DEV_MODE);
50 }
51
52 // setup plugin text domain - not sure if this is needed as JS text strings run on parent window
53 // but let's see when reviewing code
54 $this->loadTextDomain();
55
56 // determine if we're displaying draft or actively published content
57 $this->getFileStub();
58
59 // hook save post
60 $this->hookPostSaved();
61 $this->hookAjaxUrlSetup();
62
63 if ($this->isFrontend){
64 $this->hookRedirect();
65 $this->nonLoggedInMode();
66 $this->hookAdminBarLink();
67 $this->hookDequeue();
68 }
69
70 // hookJS doesn't run in the admin area so just hook MT JavaScript
71 if ($this->isAdminArea){
72
73 // don't show on Divi page - this can cause issues
74 $exclude = isset($_GET['et_fb']);
75
76 if (!$exclude){
77 $this->deferHookIfAdmin('current_screen', 'hookMTJS');
78 }
79
80 }
81
82 // note, must come after hookMTJS as inline data is attached to the tvr_mcth_frontend handle
83 //$this->deferHookIfAdmin('current_screen', 'hookFrontendData');
84 }
85
86 // support viewing the frontend as a logged-out user
87 function hookAjaxUrlSetup(){
88 add_action('init', array(&$this, 'setAdminAjaxUrl'), $this->defaultActionHookOrder);
89 }
90
91 // support redirection
92 function hookRedirect(){
93 add_action('wp', array(&$this, 'redirect'), $this->defaultActionHookOrder);
94 }
95
96 // Add link to admin bar
97 function hookAdminBarLink(){
98 if (!empty($this->preferences['admin_bar_shortcut'])) {
99 add_action( 'admin_bar_menu', array(&$this, 'adminBarLink'), $this->defaultActionHookOrder);
100 }
101 }
102
103 function hookDequeue(){
104 add_action( 'wp_print_scripts', array(&$this, 'dequeueScripts'), $this->defaultActionHookOrder );
105 }
106
107 // Dequeue scripts that conflict with MT - this only happens for logged in admins
108 function dequeueScripts(){
109
110 // Swiper.js loaded by Woo Essential plugin makes mousewheel scroll very slow
111 wp_dequeue_script( 'dnwoo_swiper_frontend' );
112 }
113
114 // add_action( 'save_post', 'set_private_categories' );
115 function hookPostSaved(){
116 add_action('save_post', array(&$this, 'postSaved'));
117 }
118
119 // add frontend JS data (inc the login page)
120 /*function hookFrontendData(){
121
122 $p = &$this->preferences;
123
124 $action_hook = $this->getCSSActionHook($p);
125
126 // determine the action execution order
127 $action_order = $this->getCSSActionOrder($p);
128
129 //wp_die('$action_hook: ' . $action_hook);
130
131 // load on login page too
132 if ($this->isFrontend){
133 add_action( 'login_head', array(&$this, 'addFrontendData'), $action_order);
134 }
135
136 // add the frontend data script
137 add_action( $action_hook, array(&$this, 'addFrontendData'), $action_order);
138
139 }*/
140
141 // action hook when a post is saved
142 // we need to update the theme template map as the arrangement of patterns and template parts may have changed
143 function postSaved($post_id){
144 Common::maybeUpdateTemplateCache($this->micro_root_dir, null, true);
145 }
146
147 function addMTPlaceholder(){
148
149 // this interferes with the logic test by echoing output, and isn't needed then
150 if (!isset($_GET['test_logic'])){
151
152 //wp_die('basty_current_filter: ' . current_filter());
153
154 $this->enqueueOrAdd(
155 true,
156 'mt-placeholder', // id must not start with 'microthemer' or it will be removed on browser tab sync
157 '',
158 array(
159 'inline' => true,
160 'code' => $this->supportAdminAssets() ? '.wp-block {}' : '',
161 'doNotDoItem' => true
162 )
163 );
164 }
165
166 }
167
168 function addMTCSS(){
169
170 // use the $wp_styles->add() method rather than enqueue if order is specified
171 // or loading stylesheets in footer which only works if using $wp_styles->add()
172 $add = $this->addInsteadOfEnqueue();
173
174 // dev vs production stylesheet
175 $min = !TVR_DEV_MODE ? '.min' : '';
176
177 // load file
178 $this->enqueueOrAdd(
179 $add,
180 'microthemer-overlay',
181 $this->thispluginurl.'css/frontend'.$min.'.css?v='.$this->version
182 );
183
184 }
185
186 function hookMTJS(){
187
188 $action_hook = $this->checkBlockEditorScreen()
189 ? 'enqueue_block_assets'
190 : $this->hooks['enqueue_scripts'];
191
192 add_action($action_hook, array(&$this, 'addMTJS'), $this->defaultActionHookOrder);
193 }
194
195 function addMTJS(){
196
197 $p = $this->preferences;
198 $min = !TVR_DEV_MODE ? '-min' : '/page';
199 $jsPath = $this->thispluginurl.'js'.$min;
200
201 // Common dependencies
202 wp_enqueue_script('jquery');
203 wp_enqueue_script('jquery-ui-tooltip');
204
205 // load mt-block.js on block editor pages
206 if ($this->isBlockEditorScreen){
207
208 $js_path = 'js' . (TVR_DEV_MODE ? '/mod/' : '-min/') . 'mt-block.js';
209
210 wp_enqueue_script(
211 'tvr_block_classes',
212 $this->thispluginurl . $js_path,
213 array( 'wp-blocks', 'wp-element', 'wp-compose', 'lodash' ),
214 filemtime($this->thisplugindir . $js_path),
215 false // Set it to true if you want it to be loaded in the footer
216 );
217 }
218
219 // MT preview script
220 // For editing styles on the frontend and the admin area
221 // But also for the Microthemer interface to receive a response from the admin area without editing
222 // e.g. Frontend loaded, Folder loading config
223 wp_register_script(
224 'tvr_mcth_frontend',
225 $jsPath.'/frontend.js?v='.$this->version,
226 array('jquery', 'jquery-ui-tooltip')
227 );
228
229 wp_enqueue_script( 'tvr_mcth_frontend');
230
231 // Print theme variables to inline JSON object for Tailwind and AI usage
232 $this->contentMethod('renderThemeVariablesConfig');
233
234 // Load tailwind JIT if enabled
235 $this->contentMethod('maybeLoadTailwindProcessor', array(&$p, $jsPath));
236
237 // the previous system of hooking this separately did not work on the wp-login.php page
238 // And I think it added unnecessary complexity too
239 $this->addFrontendData();
240 }
241
242 function addFrontendData($returnData = false) {
243
244 if ( is_user_logged_in() || isset($_GET['mt_nonlog']) ) {
245
246 global $wp_version;
247 $p = &$this->preferences;
248 $min = !TVR_DEV_MODE ? '-min' : '/mod';
249 $asset_loading = !empty($p[$this->assetLoadingKey])
250 ? $p[$this->assetLoadingKey]
251 : array();
252
253 // ensure that folderLoading config has been set
254 // it won't be if stylesheet_order has a value
255 if (!$this->folderLoadingChecked){
256
257 //echo 'getCondAssets ';
258 if (isset($asset_loading['logic'])){
259 $this->conditionalAssets($asset_loading['logic'], false, true);
260 }
261
262 // Now we have folderLoading config, queue scripts and maybe hook HTML mods
263 $this->contentMethod('initContentAmendments');
264 }
265
266 // Get the folder loading status of any draft folder too
267 $eligibleForLoading = !$this->isAdminArea || $this->supportAdminAssets();
268 $draftFolder = isset($_COOKIE['microthemer_draft_folder'])
269 ? json_decode(wp_unslash($_COOKIE["microthemer_draft_folder"]), true) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- structure enforced by json_decode
270 : false;
271
272 // if we need to test draft folder logic that hasn't been saved
273 if ($draftFolder && $eligibleForLoading){
274 $logic = new Logic($this->logicSettings);
275 $this->folderLoading[$draftFolder['slug']] = $logic->result($draftFolder['expr'])
276 ? 'empty'
277 : 0;
278 }
279
280 $MTDynFrontData = array_merge(
281 array(
282 'draftFolder' => $draftFolder,
283 'iframe-url' => rawurlencode(
284 esc_url(
285 Common::strip_page_builder_and_other_params($this->currentPageURL()),
286 null,
287 'read'
288 )
289 ),
290 'mt-show-admin-bar' => !empty($p['admin_bar_preview'])
291 ? intval($p['admin_bar_preview'])
292 : 1,
293
294 // note: folderLoading may need to hook this data to wp_footer if stylesheet_in_footer
295 'folderLoading' => $this->folderLoading,
296 'assetLoadingLogic' => !empty($p['asset_loading']['logic'])
297 ? $p['asset_loading']['logic']
298 : array(),
299 'builderBlockedEdit' => $this->builderBlockedEdit,
300 'broadcast' => !empty($p['sync_browser_tabs'])
301 ? $this->thispluginurl . 'js'.$min.'/mt-broadcast.js?v='.$this->version
302 : false,
303 'isAdminArea' => $this->isAdminArea,
304
305 'add_block_classes_all' => !empty($p['add_block_classes_all']),
306
307 // Flag to the frontend script that asset editing is / isn't supported
308 'interactions' => $this->context === 'edit',
309
310 // flag if bricks builder is active
311 'bricksBuilderActive' => $this->isBricksUi(),
312
313 // Ajax URL for saving data after monitoring the frontend e.g. Tailwind class usage
314 'wp_ajax_url' => $this->wp_ajax_url,
315
316 // WordPress info
317 'wp_version' => $wp_version,
318 'theme' => get_stylesheet(),
319 'template' => Helper::getCurrentTemplateSlug(),
320 'home_url' => $this->home_url
321
322 ),
323 $this->pageMeta()
324 );
325
326 // get Oxygen page width
327 if ( function_exists('oxygen_vsb_get_page_width') ){
328
329 $MTDynFrontData['oxygen'] = array(
330 'page-width' => intval( oxygen_vsb_get_page_width() )
331 );
332 }
333
334 wp_add_inline_script(
335 'tvr_mcth_frontend',
336 'window.MTDynFrontData = '. wp_json_encode( $MTDynFrontData ) .';',
337 'before'
338 );
339
340 if ($returnData){
341 return $MTDynFrontData;
342 }
343
344 //wp_die('$returnData: <pre>'.print_r(['$MTDynFrontData' => wp_scripts()], 1).'</pre>' );
345
346 }
347 }
348
349 function authOnlyData($min){
350
351 // pageHasMods, what mods the folder has...
352
353 return array(
354 'jQueryScript' => includes_url().'js/jquery/jquery.min.js?v='.$this->version,
355 'MTFscript' => $this->thispluginurl.'js'.$min.'/frontend.js?v='.$this->version
356 );
357 }
358
359
360 function adminBarLink($wp_admin_bar) {
361
362 if (!current_user_can('manage_options')){
363 return false;
364 }
365
366 $parent = !empty($this->preferences['top_level_shortcut']) ? false : 'site-name';
367 $currentPageURL = Common::strip_page_builder_and_other_params($this->currentPageURL());
368 $post = $this->pageMeta(); //$this->getCurrentPostData();
369
370 // format URL
371 $href = $this->wp_blog_admin_url . 'admin.php?page=' . $this->microthemeruipage .
372 '&mt_preview_url=' . rawurlencode(esc_url($currentPageURL))
373 . '&mt_item_id=' . rawurlencode($post['post_id'])
374 . '&mt_path_label=' . rawurlencode($post['post_title'])
375 . '&_wpnonce=' . wp_create_nonce( 'mt-preview-nonce' );
376
377 // add menu item
378 $wp_admin_bar->add_node(array(
379 'id' => 'wp-mcr-shortcut',
380 'title' => $this->appNameFull,
381 'parent' => $parent,
382 'href' => $href,
383 'meta' => array(
384 'class' => 'wp-mcr-shortcut',
385 /* translators: %s: application name */
386 'title' => sprintf(__('Edit with %s', 'microthemer'), $this->appName)
387 )
388 ));
389 }
390
391 function getFileStub(){
392
393 $user_id = get_current_user_id();
394
395 if (!empty($this->preferences['draft_mode'])
396 && !empty($this->preferences['draft_mode_uids'])
397 && in_array($user_id, $this->preferences['draft_mode_uids'])
398 ) {
399 $this->fileStub = 'draft';
400 }
401 }
402
403 // perform redirect for e.g. Oxygen edit URL params for the current post
404 // this is more performant than getting the edit links in the quick edit menu
405 function redirect(){
406
407 // redirect to Oxygen edit page
408 if ( isset($_GET['mto2_edit_link']) && function_exists('oxygen_add_posts_quick_action_link') ){
409
410 $nonce = !empty($_GET["_wpnonce"]) ? sanitize_text_field(wp_unslash($_GET["_wpnonce"])) : false;
411
412 if (current_user_can('manage_options') && wp_verify_nonce( $nonce, 'mt_builder_redirect_check' )) {
413
414 global $post;
415
416 // try to get link
417 $edit_link = \oxygen_add_posts_quick_action_link(array(), $post);
418
419 // we have a valid URL
420 if (!empty($edit_link['oxy_edit'])){
421
422 preg_match('/href="(.+?)"/', $edit_link['oxy_edit'], $matches);
423
424 if (!empty($matches[1])){
425
426 $edit_url = $matches[1];
427
428 wp_safe_redirect( $edit_url );
429 exit;
430 }
431 }
432
433 else {
434
435 $reason = 'unknown';
436
437 // warn that oxygen did not allow edit screen
438 if (!oxygen_vsb_current_user_can_access()) {
439 $reason = 'user-privileges';
440 } if (get_option("oxygen_vsb_ignore_post_type_{$post->post_type}") == 'true') {
441 $reason = 'post-type';
442 } if (is_oxygen_edit_post_locked()) {
443 $reason = 'edit-lock';
444 }
445
446 $this->builderBlockedEdit = array(
447 'builder' => 'oxygen',
448 'reason' => $reason
449 );
450 }
451 }
452
453 else {
454 die('Permission denied');
455 }
456 }
457 }
458
459 function nonLoggedInMode(){
460
461 if (isset($_GET['mt_nonlog'])) {
462
463 $nonce = !empty($_GET["_wpnonce"]) ? sanitize_text_field(wp_unslash($_GET["_wpnonce"])) : false;
464
465 if (current_user_can('manage_options') and wp_verify_nonce( $nonce, 'mt_nonlog_check' ) ) {
466 wp_set_current_user(-1);
467 }
468
469 else {
470 die('Permission denied');
471 }
472 }
473 }
474
475 function getCacheParam(){
476 return 'nomtcache=' . time();
477 }
478
479 // get the current page for iframe-meta and loading WP page after clicking WP admin MT option
480 function currentPageURL() {
481 // Sanitize as a complete URL; sanitize_text_field() removes valid percent-encoded path/query octets.
482 return esc_url_raw(
483 Common::get_protocol() . wp_unslash($_SERVER["HTTP_HOST"]) . wp_unslash($_SERVER["REQUEST_URI"])
484 );
485 }
486
487 function getCurrentPostData($id = 0){
488
489 $post = get_post($id);
490
491 return array(
492 'post_title' => isset( $post->post_title ) ? $post->post_title : '',
493 'post_id' => isset( $post->ID ) ? $post->ID : 0
494 );
495 }
496
497 // we support the logic test if this file (for administrators only) is in use and GET param is set
498 function supportLogicTest(){
499 return isset($_GET['test_logic']);
500 }
501
502 function doLogicTest($folders, $logic, $forceAll = false){
503
504 $testFolder = isset($_GET['test_logic'])
505 ? sanitize_text_field(wp_unslash($_GET["test_logic"]))
506 : null;
507 $testAll = isset($_GET['test_all']) || $forceAll;
508 $getStylesheets = isset($_GET['get_simple_stylesheets']);
509 $stylesheets = '';
510 $getFrontData = isset($_GET['get_front_data']);
511 $defaultResponse = array(
512 'result' => 1,
513 'resultString' => 'true',
514 'logic' => 'Not set',
515 'analysis' => 'Either no logic or no folder settings have been defined, so this folder will load globally (on the frontend)',
516 'num_statements' => 0,
517 'load' => 'Yes'
518 );
519 $adminSupportedResponse = array_merge($defaultResponse, array(
520 'result' => 0,
521 'resultString' => 'false',
522 'load' => 'No',
523 'analysis' => 'No logic has been defined, so this folder will load on the frontend only',
524 ));
525 $adminUnsupportedResponse = array_merge($adminSupportedResponse, array(
526 'analysis' => 'CSS in the admin area has not been enabled, optionally do this via Settings > Preferences.',
527 ));
528
529 // if Microthemer has provided live gutenberg HTML with new template-parts/patterns/navigation
530 // we need to update the map and possibly the post_content
531 if (Helper::isLiveContentTest()){
532 Common::updateLiveTemplateData($this->micro_root_dir);
533 }
534
535 // set default evaluation response
536 $evaluation = $this->isAdminArea
537 ? (
538 $this->supportAdminAssets()
539 ? $adminSupportedResponse
540 : $adminUnsupportedResponse
541 )
542 : $defaultResponse;
543
544 $eligibleForLoading = !$this->isAdminArea || $this->supportAdminAssets();
545
546 //
547 foreach ($folders as $folder){
548
549 $slug = $folder['slug'];
550 $file_exists = file_exists($this->rootDir . 'mt/conditional/draft/' . $slug . '.css');
551
552 // if a condition has been set
553 if (isset($folder['expr'])){
554
555 // log all folders that load on the current page
556 if ($testAll){
557 $result = $logic->result($folder['expr']);
558 $this->folderLoading[$slug] = $eligibleForLoading && $result
559 ? ($file_exists
560 ? (is_string($result) ? $result : 1) // preserve string result like 'blocksOnly'
561 : 'empty')
562 : 0;
563
564 // For FSE page changes (which only replaces inner content) we need to replace MT assets.
565 if ($getStylesheets && $file_exists && $this->folderLoading[$slug]){
566 // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedStylesheet -- AJAX/FSE injects markup; styles are not a normal page enqueue.
567 $stylesheets.= '<link rel="stylesheet" href="'.esc_url($this->micro_root_url.'mt/conditional/draft/'.$slug.'.css?'.$this->cacheParam).'" id="microthemer-'.esc_attr($slug).'-css">' . "\n";
568 }
569 }
570
571 // test a single folder, and provide debug info
572 else {
573
574 // bail if we have the result for a test folder
575 if ($eligibleForLoading && $testFolder === $folder['slug']){
576
577 $evaluation = $logic->result(
578 $folder['expr'],
579 true,
580 $file_exists
581 );
582
583 break;
584 }
585 }
586 }
587
588 }
589
590 $dataToReturn = $testAll
591 ? ($getFrontData
592 ? $this->addFrontendData(true)
593 : ($getStylesheets
594 // as array (not a string), so we can tease apart from any leading HTML before the json
595 ? array('stylesheets' => $stylesheets)
596 : $this->folderLoading)
597 )
598 : $evaluation;
599
600 //$dataToReturn['debugOutput'] = Helper::$debugOutput;
601 //wp_die('Test all <pre>' . print_r($this->folderLoading, 1) . '</pre>');
602 //echo 'Helper::$debugOutput <pre>' . Helper::$debugOutput . '</pre>';
603
604 // return test folder result - unless we are just running this to set folderLoading
605 if (!$forceAll){
606 $this->testResultResponse($dataToReturn);
607 }
608
609 }
610
611 function testResultResponse($testEvaluation){
612 $this->jsonResponse($testEvaluation);
613 }
614
615 }
616
617