Pages
1 week ago
AssetsController.php
2 weeks ago
PageRenderer.php
2 months ago
index.php
3 years ago
AssetsController.php
302 lines
| 1 | <?php declare(strict_types = 1); |
| 2 | |
| 3 | namespace MailPoet\AdminPages; |
| 4 | |
| 5 | if (!defined('ABSPATH')) exit; |
| 6 | |
| 7 | |
| 8 | use MailPoet\Config\Env; |
| 9 | use MailPoet\Config\Renderer; |
| 10 | use MailPoet\WP\Functions as WPFunctions; |
| 11 | |
| 12 | class AssetsController { |
| 13 | /** @var Renderer */ |
| 14 | private $renderer; |
| 15 | |
| 16 | /** @var WPFunctions */ |
| 17 | private $wp; |
| 18 | |
| 19 | /** @var bool */ |
| 20 | private $skipAdminPagesDependencies = false; |
| 21 | |
| 22 | public function __construct( |
| 23 | Renderer $renderer, |
| 24 | WPFunctions $wp |
| 25 | ) { |
| 26 | $this->renderer = $renderer; |
| 27 | $this->wp = $wp; |
| 28 | } |
| 29 | |
| 30 | public function setupAdminPagesDependencies(): void { |
| 31 | if ($this->skipAdminPagesDependencies) { |
| 32 | return; |
| 33 | } |
| 34 | $this->registerAdminDeps(); |
| 35 | $this->wp->wpEnqueueScript('mailpoet_admin'); |
| 36 | } |
| 37 | |
| 38 | public function setupHomepageDependencies(): void { |
| 39 | $this->wp->wpEnqueueStyle('mailpoet_homepage', $this->getCssUrl('mailpoet-homepage.css')); |
| 40 | } |
| 41 | |
| 42 | public function setupNewsletterEditorDependencies(): void { |
| 43 | $this->enqueueJsEntrypoint('newsletter_editor', ['underscore']); |
| 44 | $this->wp->wpEnqueueStyle('mailpoet_newsletter_editor', $this->getCssUrl('mailpoet-editor.css')); |
| 45 | } |
| 46 | |
| 47 | public function setupFormEditorDependencies(): void { |
| 48 | $this->skipAdminPagesDependencies = true; |
| 49 | $this->setupFormEditorLocalizationDependency(); |
| 50 | $dependencies = ['mailpoet_mailpoet', 'underscore']; |
| 51 | if ($this->wp->wpEnqueueCodeEditor(['type' => 'text/css']) !== false) { |
| 52 | $dependencies[] = 'code-editor'; |
| 53 | } |
| 54 | $this->enqueueJsEntrypoint('form_editor', $dependencies, false); |
| 55 | } |
| 56 | |
| 57 | public function setupSettingsDependencies(): void { |
| 58 | $this->enqueueJsEntrypoint('settings'); |
| 59 | } |
| 60 | |
| 61 | public function setupTagsDependencies(): void { |
| 62 | $this->enqueueJsEntrypoint('tags'); |
| 63 | $this->setupDataViewsDependencies(); |
| 64 | $this->wp->wpEnqueueStyle('mailpoet_tags', $this->getCssUrl('mailpoet-tags.css')); |
| 65 | } |
| 66 | |
| 67 | public function setupCustomFieldsDependencies(): void { |
| 68 | $this->enqueueJsEntrypoint('custom_fields'); |
| 69 | $this->setupDataViewsDependencies(); |
| 70 | $this->wp->wpEnqueueStyle('mailpoet_custom_fields', $this->getCssUrl('mailpoet-custom-fields.css')); |
| 71 | } |
| 72 | |
| 73 | /** |
| 74 | * Enqueue the WordPress component + DataViews styles required by listings |
| 75 | * that render `@wordpress/dataviews`. Listings already shipped via the |
| 76 | * shared admin JS bundle (e.g. Forms) only need the styles enqueued; their |
| 77 | * JS lives in `webpack-admin-index`. |
| 78 | * |
| 79 | * `wp-components` is registered by WordPress core. `wp-dataviews` is |
| 80 | * package-shipped, so we bundle its build-style into `mailpoet-dataviews.css`. |
| 81 | */ |
| 82 | public function setupDataViewsDependencies(): void { |
| 83 | $this->wp->wpEnqueueStyle('wp-components'); |
| 84 | $this->wp->wpEnqueueStyle('mailpoet_dataviews', $this->getCssUrl('mailpoet-dataviews.css')); |
| 85 | } |
| 86 | |
| 87 | public function setupDynamicSegmentsDependencies(): void { |
| 88 | $this->wp->wpEnqueueStyle('mailpoet_templates', $this->getCssUrl('mailpoet-templates.css')); |
| 89 | $this->wp->wpEnqueueStyle('mailpoet_dynamic_segments', $this->getCssUrl('mailpoet-dynamic-segments.css')); |
| 90 | } |
| 91 | |
| 92 | public function setupAutomationListingDependencies(): void { |
| 93 | $this->enqueueJsEntrypoint('automation'); |
| 94 | $this->setupDataViewsDependencies(); |
| 95 | $this->wp->wpEnqueueStyle('mailpoet_automation', $this->getCssUrl('mailpoet-automation.css')); |
| 96 | } |
| 97 | |
| 98 | public function setupAutomationTemplatesDependencies(): void { |
| 99 | $this->enqueueJsEntrypoint('automation_templates'); |
| 100 | $this->wp->wpEnqueueStyle('mailpoet_automation_templates', $this->getCssUrl('mailpoet-automation-templates.css')); |
| 101 | } |
| 102 | |
| 103 | public function setupAutomationEditorDependencies(): void { |
| 104 | $this->enqueueJsEntrypoint('automation_editor', ['wp-date']); |
| 105 | $this->wp->wpEnqueueStyle('mailpoet_automation_editor', $this->getCssUrl('mailpoet-automation-editor.css')); |
| 106 | } |
| 107 | |
| 108 | public function setupAutomationAnalyticsDependencies(): void { |
| 109 | $this->enqueueJsEntrypoint('automation_analytics'); |
| 110 | $this->wp->wpEnqueueStyle('mailpoet_automation_analytics', $this->getCssUrl('mailpoet-automation-analytics.css')); |
| 111 | } |
| 112 | |
| 113 | public function setupAutomationPreviewEmbedDependencies(): void { |
| 114 | $this->enqueueJsEntrypoint('automation_preview_embed'); |
| 115 | $this->wp->wpEnqueueStyle('mailpoet_automation_templates', $this->getCssUrl('mailpoet-automation-templates.css')); |
| 116 | } |
| 117 | |
| 118 | public function setupAutomationFlowEmbedDependencies(): void { |
| 119 | $this->enqueueJsEntrypoint('automation_flow_embed'); |
| 120 | $this->wp->wpEnqueueStyle('mailpoet_automation_analytics', $this->getCssUrl('mailpoet-automation-analytics.css')); |
| 121 | } |
| 122 | |
| 123 | private function enqueueJsEntrypoint(string $asset, array $dependencies = [], bool $withAdminDeps = true): void { |
| 124 | if ($withAdminDeps) { |
| 125 | $this->registerAdminDeps(); |
| 126 | } |
| 127 | |
| 128 | $assetData = $this->getScriptAssetData($asset); |
| 129 | $dependencies = array_values(array_unique(array_merge( |
| 130 | $dependencies, |
| 131 | $assetData['dependencies'], |
| 132 | $withAdminDeps ? ['mailpoet_admin'] : [] |
| 133 | ))); |
| 134 | |
| 135 | $name = "mailpoet_$asset"; |
| 136 | $this->wp->wpEnqueueScript( |
| 137 | $name, |
| 138 | Env::$assetsUrl . '/dist/js/' . $this->renderer->getJsAsset("$asset.js"), |
| 139 | $dependencies, |
| 140 | $assetData['version'], |
| 141 | true |
| 142 | ); |
| 143 | $this->wp->wpSetScriptTranslations($name, 'mailpoet'); |
| 144 | |
| 145 | // Ensure Lodash doesn't override Underscore from WordPress on "window._" global. |
| 146 | // Checking for "_.at" detects Lodash (the function doesn't exist in Underscore). |
| 147 | $noConflict = 'if (window._ && window._.at && window._.noConflict) window._.noConflict();'; |
| 148 | if ($withAdminDeps) { |
| 149 | $this->wp->wpAddInlineScript('mailpoet_admin_commons', $noConflict); |
| 150 | $this->wp->wpAddInlineScript('mailpoet_mailpoet', $noConflict); |
| 151 | $this->wp->wpAddInlineScript('mailpoet_admin_vendor', $noConflict); |
| 152 | $this->wp->wpAddInlineScript('mailpoet_admin', $noConflict); |
| 153 | } |
| 154 | $this->wp->wpAddInlineScript($name, $noConflict); |
| 155 | } |
| 156 | |
| 157 | /** |
| 158 | * @return array{dependencies: string[], version: string} |
| 159 | */ |
| 160 | private function getScriptAssetData(string $asset): array { |
| 161 | $assetPaths = [ |
| 162 | 'form_editor' => Env::$assetsPath . '/dist/js/form_editor.asset.json', |
| 163 | ]; |
| 164 | $fallback = [ |
| 165 | 'dependencies' => [], |
| 166 | 'version' => Env::$version, |
| 167 | ]; |
| 168 | $assetPath = $assetPaths[$asset] ?? null; |
| 169 | if ($assetPath === null) { |
| 170 | return $fallback; |
| 171 | } |
| 172 | |
| 173 | if (!file_exists($assetPath)) { |
| 174 | $this->logScriptAssetDataIssue(sprintf('Missing script asset metadata for "%s" at "%s".', $asset, $assetPath)); |
| 175 | return $fallback; |
| 176 | } |
| 177 | |
| 178 | $assetData = json_decode((string)file_get_contents($assetPath), true); |
| 179 | if (!is_array($assetData)) { |
| 180 | $this->logScriptAssetDataIssue(sprintf('Invalid script asset metadata for "%s" at "%s".', $asset, $assetPath)); |
| 181 | return $fallback; |
| 182 | } |
| 183 | |
| 184 | $dependencies = is_array($assetData['dependencies'] ?? null) ? $assetData['dependencies'] : []; |
| 185 | $version = is_string($assetData['version'] ?? null) ? $assetData['version'] : $fallback['version']; |
| 186 | |
| 187 | return [ |
| 188 | 'dependencies' => array_values(array_filter($dependencies, 'is_string')), |
| 189 | 'version' => $version, |
| 190 | ]; |
| 191 | } |
| 192 | |
| 193 | private function logScriptAssetDataIssue(string $message): void { |
| 194 | if (defined('WP_DEBUG') && WP_DEBUG && function_exists('error_log')) { |
| 195 | // phpcs:disable QITStandard.PHP.DebugCode.DebugFunctionFound |
| 196 | error_log('[MailPoet] ' . $message); // phpcs:ignore Squiz.PHP.DiscouragedFunctions.Discouraged |
| 197 | // phpcs:enable QITStandard.PHP.DebugCode.DebugFunctionFound |
| 198 | } |
| 199 | } |
| 200 | |
| 201 | private function registerAdminDeps(): void { |
| 202 | // runtime |
| 203 | $this->registerFooterScript('mailpoet_runtime', $this->getScriptUrl('runtime.js')); |
| 204 | |
| 205 | // vendor |
| 206 | $this->registerFooterScript('mailpoet_vendor', $this->getScriptUrl('vendor.js')); |
| 207 | |
| 208 | // commons |
| 209 | $this->registerFooterScript('mailpoet_admin_commons', $this->getScriptUrl('commons.js')); |
| 210 | $this->wp->wpSetScriptTranslations('mailpoet_admin_commons', 'mailpoet'); |
| 211 | |
| 212 | // mailpoet |
| 213 | $this->registerFooterScript('mailpoet_mailpoet', $this->getScriptUrl('mailpoet.js')); |
| 214 | $this->wp->wpSetScriptTranslations('mailpoet_mailpoet', 'mailpoet'); |
| 215 | |
| 216 | // admin_vendor |
| 217 | $this->registerFooterScript( |
| 218 | 'mailpoet_admin_vendor', |
| 219 | $this->getScriptUrl('admin_vendor.js'), |
| 220 | [ |
| 221 | 'wp-i18n', |
| 222 | 'mailpoet_runtime', |
| 223 | 'mailpoet_vendor', |
| 224 | 'mailpoet_admin_commons', |
| 225 | 'mailpoet_mailpoet', |
| 226 | ] |
| 227 | ); |
| 228 | |
| 229 | // append Parsley validation string translations |
| 230 | $this->wp->wpAddInlineScript('mailpoet_admin_vendor', $this->renderer->render('parsley-translations.html')); |
| 231 | |
| 232 | // enqueue "mailpoet_admin_vendor" so the hook fires after it, but before "mailpoet_admin" |
| 233 | $this->wp->wpEnqueueScript('mailpoet_admin_vendor'); |
| 234 | if ($this->wp->didAction('mailpoet_scripts_admin_before') === 0) { |
| 235 | $this->wp->doAction('mailpoet_scripts_admin_before'); |
| 236 | } |
| 237 | |
| 238 | // admin |
| 239 | $this->registerFooterScript( |
| 240 | 'mailpoet_admin', |
| 241 | $this->getScriptUrl('admin.js'), |
| 242 | ['mailpoet_admin_vendor', 'wp-preferences-persistence'] |
| 243 | ); |
| 244 | $this->wp->wpLocalizeScript('mailpoet_admin', 'mailpoet_preferences_data', [ |
| 245 | 'currentUserId' => $this->wp->getCurrentUserId(), |
| 246 | 'preloadedData' => $this->getPersistedPreferences(), |
| 247 | ]); |
| 248 | $this->wp->wpSetScriptTranslations('mailpoet_admin', 'mailpoet'); |
| 249 | } |
| 250 | |
| 251 | private function setupFormEditorLocalizationDependency(): void { |
| 252 | $this->wp->wpRegisterScript('mailpoet_mailpoet', false, [], Env::$version, true); |
| 253 | $this->wp->wpAddInlineScript( |
| 254 | 'mailpoet_mailpoet', |
| 255 | <<<'JAVASCRIPT' |
| 256 | window.mailpoet_i18n = window.mailpoet_i18n || {}; |
| 257 | window.MailPoet = window.MailPoet || {}; |
| 258 | window.MailPoet.I18n = window.MailPoet.I18n || { |
| 259 | add: function(key, value) { |
| 260 | window.mailpoet_i18n[key] = value; |
| 261 | }, |
| 262 | t: function(key) { |
| 263 | return window.mailpoet_i18n[key] || 'TRANSLATION "%1$s" NOT FOUND'.replace('%1$s', key); |
| 264 | }, |
| 265 | all: function() { |
| 266 | return window.mailpoet_i18n; |
| 267 | } |
| 268 | }; |
| 269 | JAVASCRIPT, |
| 270 | 'before' |
| 271 | ); |
| 272 | $this->wp->wpEnqueueScript('mailpoet_mailpoet'); |
| 273 | } |
| 274 | |
| 275 | private function getPersistedPreferences(): \stdClass { |
| 276 | $currentUserId = $this->wp->getCurrentUserId(); |
| 277 | if (!$currentUserId) { |
| 278 | return new \stdClass(); |
| 279 | } |
| 280 | |
| 281 | $preferences = $this->wp->getUserMeta( |
| 282 | $currentUserId, |
| 283 | $this->wp->getBlogPrefix() . 'persisted_preferences', |
| 284 | true |
| 285 | ); |
| 286 | |
| 287 | return is_array($preferences) ? (object)$preferences : new \stdClass(); |
| 288 | } |
| 289 | |
| 290 | private function getScriptUrl(string $name): string { |
| 291 | return Env::$assetsUrl . '/dist/js/' . $this->renderer->getJsAsset($name); |
| 292 | } |
| 293 | |
| 294 | private function getCssUrl(string $name): string { |
| 295 | return Env::$assetsUrl . '/dist/css/' . $this->renderer->getCssAsset($name); |
| 296 | } |
| 297 | |
| 298 | private function registerFooterScript(string $handle, string $src, array $deps = []): void { |
| 299 | $this->wp->wpRegisterScript($handle, $src, $deps, Env::$version, true); |
| 300 | } |
| 301 | } |
| 302 |