class-admin-asset-manager.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  1. <?php
  2. /**
  3. * WPSEO plugin file.
  4. *
  5. * @package WPSEO\Admin
  6. */
  7. /**
  8. * This class registers all the necessary styles and scripts. Also has methods for the enqueing of scripts and styles. It automatically adds a prefix to the handle.
  9. */
  10. class WPSEO_Admin_Asset_Manager {
  11. /**
  12. * @var WPSEO_Admin_Asset_Location
  13. */
  14. protected $asset_location;
  15. /**
  16. * Prefix for naming the assets.
  17. */
  18. const PREFIX = 'yoast-seo-';
  19. /**
  20. * @var string prefix for naming the assets.
  21. */
  22. private $prefix;
  23. /**
  24. * Constructs a manager of assets. Needs a location to know where to register assets at.
  25. *
  26. * @param WPSEO_Admin_Asset_Location $asset_location The provider of the asset location.
  27. * @param string $prefix The prefix for naming assets.
  28. */
  29. public function __construct( WPSEO_Admin_Asset_Location $asset_location = null, $prefix = self::PREFIX ) {
  30. if ( $asset_location === null ) {
  31. $asset_location = self::create_default_location();
  32. }
  33. $this->asset_location = $asset_location;
  34. $this->prefix = $prefix;
  35. }
  36. /**
  37. * Enqueues scripts.
  38. *
  39. * @param string $script The name of the script to enqueue.
  40. */
  41. public function enqueue_script( $script ) {
  42. wp_enqueue_script( $this->prefix . $script );
  43. }
  44. /**
  45. * Enqueues styles.
  46. *
  47. * @param string $style The name of the style to enqueue.
  48. */
  49. public function enqueue_style( $style ) {
  50. wp_enqueue_style( $this->prefix . $style );
  51. }
  52. /**
  53. * Registers scripts based on it's parameters.
  54. *
  55. * @param WPSEO_Admin_Asset $script The script to register.
  56. */
  57. public function register_script( WPSEO_Admin_Asset $script ) {
  58. wp_register_script(
  59. $this->prefix . $script->get_name(),
  60. $this->asset_location->get_url( $script, WPSEO_Admin_Asset::TYPE_JS ),
  61. $script->get_deps(),
  62. $script->get_version(),
  63. $script->is_in_footer()
  64. );
  65. }
  66. /**
  67. * Registers styles based on it's parameters.
  68. *
  69. * @param WPSEO_Admin_Asset $style The style to register.
  70. */
  71. public function register_style( WPSEO_Admin_Asset $style ) {
  72. wp_register_style(
  73. $this->prefix . $style->get_name(),
  74. $this->asset_location->get_url( $style, WPSEO_Admin_Asset::TYPE_CSS ),
  75. $style->get_deps(),
  76. $style->get_version(),
  77. $style->get_media()
  78. );
  79. }
  80. /**
  81. * Calls the functions that register scripts and styles with the scripts and styles to be registered as arguments.
  82. */
  83. public function register_assets() {
  84. $this->register_scripts( $this->scripts_to_be_registered() );
  85. $this->register_styles( $this->styles_to_be_registered() );
  86. }
  87. /**
  88. * Registers all the scripts passed to it.
  89. *
  90. * @param array $scripts The scripts passed to it.
  91. */
  92. public function register_scripts( $scripts ) {
  93. foreach ( $scripts as $script ) {
  94. $script = new WPSEO_Admin_Asset( $script );
  95. $this->register_script( $script );
  96. }
  97. }
  98. /**
  99. * Registers all the styles it recieves.
  100. *
  101. * @param array $styles Styles that need to be registerd.
  102. */
  103. public function register_styles( $styles ) {
  104. foreach ( $styles as $style ) {
  105. $style = new WPSEO_Admin_Asset( $style );
  106. $this->register_style( $style );
  107. }
  108. }
  109. /**
  110. * A list of styles that shouldn't be registered but are needed in other locations in the plugin.
  111. *
  112. * @return array
  113. */
  114. public function special_styles() {
  115. $flat_version = $this->flatten_version( WPSEO_VERSION );
  116. return array(
  117. 'inside-editor' => new WPSEO_Admin_Asset( array(
  118. 'name' => 'inside-editor',
  119. 'src' => 'inside-editor-' . $flat_version,
  120. ) ),
  121. );
  122. }
  123. /**
  124. * Flattens a version number for use in a filename
  125. *
  126. * @param string $version The original version number.
  127. *
  128. * @return string The flattened version number.
  129. */
  130. public function flatten_version( $version ) {
  131. $parts = explode( '.', $version );
  132. if ( count( $parts ) === 2 && preg_match( '/^\d+$/', $parts[1] ) === 1 ) {
  133. $parts[] = '0';
  134. }
  135. return implode( '', $parts );
  136. }
  137. /**
  138. * Creates a default location object for use in the admin asset manager.
  139. *
  140. * @return WPSEO_Admin_Asset_Location The location to use in the asset manager.
  141. */
  142. public static function create_default_location() {
  143. if ( defined( 'YOAST_SEO_DEV_SERVER' ) && YOAST_SEO_DEV_SERVER ) {
  144. $url = defined( 'YOAST_SEO_DEV_SERVER_URL' ) ? YOAST_SEO_DEV_SERVER_URL : WPSEO_Admin_Asset_Dev_Server_Location::DEFAULT_URL;
  145. return new WPSEO_Admin_Asset_Dev_Server_Location( $url );
  146. }
  147. return new WPSEO_Admin_Asset_SEO_Location( WPSEO_FILE );
  148. }
  149. /**
  150. * Returns the scripts that need to be registered.
  151. *
  152. * @todo Data format is not self-documenting. Needs explanation inline. R.
  153. *
  154. * @return array scripts that need to be registered.
  155. */
  156. protected function scripts_to_be_registered() {
  157. $select2_language = 'en';
  158. $user_locale = WPSEO_Utils::get_user_locale();
  159. $language = WPSEO_Utils::get_language( $user_locale );
  160. if ( file_exists( WPSEO_PATH . "js/dist/select2/i18n/{$user_locale}.js" ) ) {
  161. $select2_language = $user_locale; // Chinese and some others use full locale.
  162. }
  163. elseif ( file_exists( WPSEO_PATH . "js/dist/select2/i18n/{$language}.js" ) ) {
  164. $select2_language = $language;
  165. }
  166. $flat_version = $this->flatten_version( WPSEO_VERSION );
  167. $backport_wp_dependencies = array( self::PREFIX . 'react-dependencies' );
  168. // If Gutenberg is present we can borrow their globals for our own.
  169. if ( $this->should_load_gutenberg_assets() ) {
  170. $backport_wp_dependencies[] = 'wp-element';
  171. $backport_wp_dependencies[] = 'wp-data';
  172. $backport_wp_dependencies[] = 'wp-components';
  173. /*
  174. * The version of TinyMCE that Gutenberg uses is incompatible with
  175. * the one core uses. So we need to make sure that the core version
  176. * is used in the classic editor.
  177. *
  178. * $_GET is used here because as far as I am aware you cannot use
  179. * filter_input to check for the existence of a query variable.
  180. */
  181. if ( wp_script_is( 'tinymce-latest', 'registered' ) && isset( $_GET['classic-editor'] ) ) {
  182. wp_deregister_script( 'tinymce-latest' );
  183. wp_register_script( 'tinymce-latest', includes_url( 'js/tinymce/' ) . 'wp-tinymce.php', array( 'jquery' ), false, true );
  184. }
  185. }
  186. else {
  187. if ( wp_script_is( 'lodash', 'registered' ) ) {
  188. $backport_wp_dependencies[] = 'lodash';
  189. }
  190. else {
  191. if ( ! wp_script_is( self::PREFIX . 'lodash', 'registered' ) ) {
  192. wp_register_script( self::PREFIX . 'lodash-base', plugins_url( 'js/vendor/lodash.min.js', WPSEO_FILE ), array(), false, true );
  193. wp_register_script( self::PREFIX . 'lodash', plugins_url( 'js/vendor/lodash-noconflict.js', WPSEO_FILE ), array( self::PREFIX . 'lodash-base' ), false, true );
  194. }
  195. $backport_wp_dependencies[] = self::PREFIX . 'lodash';
  196. }
  197. }
  198. // If Gutenberg's babel polyfill is not present, use our own.
  199. $babel_polyfill = 'wp-polyfill-ecmascript';
  200. if ( ! wp_script_is( 'wp-polyfill-ecmascript', 'registered' ) ) {
  201. $babel_polyfill = self::PREFIX . 'babel-polyfill';
  202. }
  203. return array(
  204. array(
  205. 'name' => 'react-dependencies',
  206. // Load webpack-commons for bundle support.
  207. 'src' => 'commons-' . $flat_version,
  208. 'deps' => array( $babel_polyfill ),
  209. ),
  210. array(
  211. 'name' => 'search-appearance',
  212. 'src' => 'search-appearance-' . $flat_version,
  213. 'deps' => array(
  214. self::PREFIX . 'react-dependencies',
  215. self::PREFIX . 'components',
  216. ),
  217. ),
  218. array(
  219. 'name' => 'wp-globals-backport',
  220. 'src' => 'wp-seo-wp-globals-backport-' . $flat_version,
  221. 'deps' => $backport_wp_dependencies,
  222. ),
  223. array(
  224. 'name' => 'yoast-modal',
  225. 'src' => 'wp-seo-modal-' . $flat_version,
  226. 'deps' => array(
  227. 'jquery',
  228. self::PREFIX . 'wp-globals-backport',
  229. self::PREFIX . 'components',
  230. ),
  231. ),
  232. array(
  233. 'name' => 'help-center',
  234. 'src' => 'wp-seo-help-center-' . $flat_version,
  235. 'deps' => array(
  236. 'jquery',
  237. self::PREFIX . 'wp-globals-backport',
  238. self::PREFIX . 'components',
  239. ),
  240. ),
  241. array(
  242. 'name' => 'admin-script',
  243. 'src' => 'wp-seo-admin-' . $flat_version,
  244. 'deps' => array(
  245. 'jquery',
  246. 'jquery-ui-core',
  247. 'jquery-ui-progressbar',
  248. self::PREFIX . 'select2',
  249. self::PREFIX . 'select2-translations',
  250. self::PREFIX . 'react-dependencies',
  251. ),
  252. ),
  253. array(
  254. 'name' => 'admin-media',
  255. 'src' => 'wp-seo-admin-media-' . $flat_version,
  256. 'deps' => array(
  257. 'jquery',
  258. 'jquery-ui-core',
  259. self::PREFIX . 'react-dependencies',
  260. ),
  261. ),
  262. array(
  263. 'name' => 'network-admin-script',
  264. 'src' => 'wp-seo-network-admin-' . $flat_version,
  265. 'deps' => array( 'jquery' ),
  266. ),
  267. array(
  268. 'name' => 'bulk-editor',
  269. 'src' => 'wp-seo-bulk-editor-' . $flat_version,
  270. 'deps' => array(
  271. 'jquery',
  272. self::PREFIX . 'react-dependencies',
  273. ),
  274. ),
  275. array(
  276. 'name' => 'admin-global-script',
  277. 'src' => 'wp-seo-admin-global-' . $flat_version,
  278. 'deps' => array(
  279. 'jquery',
  280. self::PREFIX . 'react-dependencies',
  281. ),
  282. ),
  283. array(
  284. 'name' => 'metabox',
  285. 'src' => 'wp-seo-metabox-' . $flat_version,
  286. 'deps' => array(
  287. 'jquery',
  288. self::PREFIX . 'select2',
  289. self::PREFIX . 'select2-translations',
  290. self::PREFIX . 'wp-globals-backport',
  291. ),
  292. 'in_footer' => false,
  293. ),
  294. array(
  295. 'name' => 'featured-image',
  296. 'src' => 'wp-seo-featured-image-' . $flat_version,
  297. 'deps' => array(
  298. 'jquery',
  299. self::PREFIX . 'react-dependencies',
  300. ),
  301. ),
  302. array(
  303. 'name' => 'admin-gsc',
  304. 'src' => 'wp-seo-admin-gsc-' . $flat_version,
  305. 'deps' => array(
  306. self::PREFIX . 'react-dependencies',
  307. ),
  308. 'in_footer' => false,
  309. ),
  310. array(
  311. 'name' => 'post-scraper',
  312. 'src' => 'wp-seo-post-scraper-' . $flat_version,
  313. 'deps' => array(
  314. self::PREFIX . 'replacevar-plugin',
  315. self::PREFIX . 'shortcode-plugin',
  316. 'wp-util',
  317. 'wp-api',
  318. self::PREFIX . 'wp-globals-backport',
  319. self::PREFIX . 'analysis',
  320. self::PREFIX . 'react-dependencies',
  321. self::PREFIX . 'components',
  322. ),
  323. ),
  324. array(
  325. 'name' => 'term-scraper',
  326. 'src' => 'wp-seo-term-scraper-' . $flat_version,
  327. 'deps' => array(
  328. self::PREFIX . 'replacevar-plugin',
  329. self::PREFIX . 'wp-globals-backport',
  330. self::PREFIX . 'analysis',
  331. self::PREFIX . 'components',
  332. ),
  333. ),
  334. array(
  335. 'name' => 'replacevar-plugin',
  336. 'src' => 'wp-seo-replacevar-plugin-' . $flat_version,
  337. 'deps' => array(
  338. self::PREFIX . 'react-dependencies',
  339. self::PREFIX . 'analysis',
  340. self::PREFIX . 'components',
  341. ),
  342. ),
  343. array(
  344. 'name' => 'shortcode-plugin',
  345. 'src' => 'wp-seo-shortcode-plugin-' . $flat_version,
  346. 'deps' => array(
  347. self::PREFIX . 'react-dependencies',
  348. self::PREFIX . 'analysis',
  349. ),
  350. ),
  351. array(
  352. 'name' => 'recalculate',
  353. 'src' => 'wp-seo-recalculate-' . $flat_version,
  354. 'deps' => array(
  355. 'jquery',
  356. 'jquery-ui-core',
  357. 'jquery-ui-progressbar',
  358. self::PREFIX . 'analysis',
  359. self::PREFIX . 'react-dependencies',
  360. ),
  361. ),
  362. array(
  363. 'name' => 'primary-category',
  364. 'src' => 'wp-seo-metabox-category-' . $flat_version,
  365. 'deps' => array(
  366. 'jquery',
  367. 'wp-util',
  368. self::PREFIX . 'react-dependencies',
  369. self::PREFIX . 'wp-globals-backport',
  370. ),
  371. ),
  372. array(
  373. 'name' => 'select2',
  374. 'src' => 'select2/select2.full',
  375. 'suffix' => '.min',
  376. 'deps' => array(
  377. 'jquery',
  378. ),
  379. 'version' => '4.0.3',
  380. ),
  381. array(
  382. 'name' => 'select2-translations',
  383. 'src' => 'select2/i18n/' . $select2_language,
  384. 'deps' => array(
  385. 'jquery',
  386. self::PREFIX . 'select2',
  387. ),
  388. 'version' => '4.0.3',
  389. 'suffix' => '',
  390. ),
  391. array(
  392. 'name' => 'configuration-wizard',
  393. 'src' => 'configuration-wizard-' . $flat_version,
  394. 'deps' => array(
  395. 'jquery',
  396. self::PREFIX . 'wp-globals-backport',
  397. self::PREFIX . 'components',
  398. ),
  399. ),
  400. array(
  401. 'name' => 'reindex-links',
  402. 'src' => 'wp-seo-reindex-links-' . $flat_version,
  403. 'deps' => array(
  404. 'jquery',
  405. 'jquery-ui-core',
  406. 'jquery-ui-progressbar',
  407. self::PREFIX . 'react-dependencies',
  408. ),
  409. ),
  410. array(
  411. 'name' => 'edit-page-script',
  412. 'src' => 'wp-seo-edit-page-' . $flat_version,
  413. 'deps' => array(
  414. 'jquery',
  415. self::PREFIX . 'react-dependencies',
  416. ),
  417. ),
  418. array(
  419. 'name' => 'quick-edit-handler',
  420. 'src' => 'wp-seo-quick-edit-handler-' . $flat_version,
  421. 'deps' => array(
  422. 'jquery',
  423. self::PREFIX . 'react-dependencies',
  424. ),
  425. 'in_footer' => true,
  426. ),
  427. array(
  428. 'name' => 'api',
  429. 'src' => 'wp-seo-api-' . $flat_version,
  430. 'deps' => array(
  431. 'wp-api',
  432. 'jquery',
  433. self::PREFIX . 'react-dependencies',
  434. ),
  435. ),
  436. array(
  437. 'name' => 'dashboard-widget',
  438. 'src' => 'wp-seo-dashboard-widget-' . $flat_version,
  439. 'deps' => array(
  440. self::PREFIX . 'api',
  441. 'jquery',
  442. self::PREFIX . 'wp-globals-backport',
  443. self::PREFIX . 'components',
  444. ),
  445. ),
  446. array(
  447. 'name' => 'filter-explanation',
  448. 'src' => 'wp-seo-filter-explanation-' . $flat_version,
  449. 'deps' => array(
  450. 'jquery',
  451. self::PREFIX . 'react-dependencies',
  452. ),
  453. ),
  454. array(
  455. 'name' => 'analysis',
  456. 'src' => 'analysis-' . $flat_version,
  457. ),
  458. array(
  459. 'name' => 'components',
  460. 'src' => 'components-' . $flat_version,
  461. 'deps' => array( self::PREFIX . 'analysis' ),
  462. ),
  463. array(
  464. 'name' => 'structured-data-blocks',
  465. 'src' => 'wp-seo-structured-data-blocks-' . $flat_version,
  466. 'deps' => array( 'wp-blocks', 'wp-i18n', 'wp-element' ),
  467. ),
  468. array(
  469. 'name' => 'babel-polyfill',
  470. 'src' => 'babel-polyfill-' . $flat_version,
  471. ),
  472. );
  473. }
  474. /**
  475. * Returns the styles that need to be registered.
  476. *
  477. * @todo Data format is not self-documenting. Needs explanation inline. R.
  478. *
  479. * @return array styles that need to be registered.
  480. */
  481. protected function styles_to_be_registered() {
  482. $flat_version = $this->flatten_version( WPSEO_VERSION );
  483. return array(
  484. array(
  485. 'name' => 'admin-css',
  486. 'src' => 'yst_plugin_tools-' . $flat_version,
  487. 'deps' => array( self::PREFIX . 'toggle-switch' ),
  488. ),
  489. array(
  490. 'name' => 'toggle-switch',
  491. 'src' => 'toggle-switch-' . $flat_version,
  492. ),
  493. array(
  494. 'name' => 'dismissible',
  495. 'src' => 'wpseo-dismissible-' . $flat_version,
  496. ),
  497. array(
  498. 'name' => 'alerts',
  499. 'src' => 'alerts-' . $flat_version,
  500. ),
  501. array(
  502. 'name' => 'edit-page',
  503. 'src' => 'edit-page-' . $flat_version,
  504. ),
  505. array(
  506. 'name' => 'featured-image',
  507. 'src' => 'featured-image-' . $flat_version,
  508. ),
  509. array(
  510. 'name' => 'metabox-css',
  511. 'src' => 'metabox-' . $flat_version,
  512. 'deps' => array(
  513. self::PREFIX . 'select2',
  514. ),
  515. ),
  516. array(
  517. 'name' => 'wp-dashboard',
  518. 'src' => 'dashboard-' . $flat_version,
  519. ),
  520. array(
  521. 'name' => 'scoring',
  522. 'src' => 'yst_seo_score-' . $flat_version,
  523. ),
  524. array(
  525. 'name' => 'adminbar',
  526. 'src' => 'adminbar-' . $flat_version,
  527. ),
  528. array(
  529. 'name' => 'primary-category',
  530. 'src' => 'metabox-primary-category-' . $flat_version,
  531. ),
  532. array(
  533. 'name' => 'select2',
  534. 'src' => 'select2/select2',
  535. 'suffix' => '.min',
  536. 'version' => '4.0.1',
  537. 'rtl' => false,
  538. ),
  539. array(
  540. 'name' => 'admin-global',
  541. 'src' => 'admin-global-' . $flat_version,
  542. ),
  543. array(
  544. 'name' => 'yoast-components',
  545. 'src' => 'yoast-components-' . $flat_version,
  546. ),
  547. array(
  548. 'name' => 'extensions',
  549. 'src' => 'yoast-extensions-' . $flat_version,
  550. ),
  551. array(
  552. 'name' => 'filter-explanation',
  553. 'src' => 'filter-explanation-' . $flat_version,
  554. ),
  555. array(
  556. 'name' => 'search-appearance',
  557. 'src' => 'search-appearance-' . $flat_version,
  558. ),
  559. array(
  560. 'name' => 'structured-data-blocks',
  561. 'src' => 'structured-data-blocks-' . $flat_version,
  562. 'deps' => array( 'wp-edit-blocks' ),
  563. ),
  564. );
  565. }
  566. /**
  567. * Checks if the Gutenberg assets must be loaded.
  568. *
  569. * @return bool True wheter Gutenberg assets must be loaded.
  570. */
  571. protected function should_load_gutenberg_assets() {
  572. // When Gutenberg is not active, just return false.
  573. if ( ! function_exists( 'gutenberg_register_scripts_and_styles' ) ) {
  574. return false;
  575. }
  576. // When working in the classic editor shipped with Gutenberg, the assets shouldn't be loaded. Fixes IE11 bug.
  577. if ( isset( $_GET['classic-editor'] ) ) {
  578. return false;
  579. }
  580. // When classic editor plugin and Gutenberg are active, the Gutenberg assets shouldn't be loaded.
  581. if ( function_exists( 'classic_editor_is_gutenberg_active' ) && classic_editor_is_gutenberg_active() ) {
  582. return false;
  583. }
  584. return true;
  585. }
  586. }