editor.php 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123
  1. <?php
  2. namespace Elementor;
  3. use Elementor\Core\Responsive\Responsive;
  4. use Elementor\Core\Settings\Manager as SettingsManager;
  5. use Elementor\TemplateLibrary\Source_Local;
  6. if ( ! defined( 'ABSPATH' ) ) {
  7. exit; // Exit if accessed directly.
  8. }
  9. /**
  10. * Elementor editor.
  11. *
  12. * Elementor editor handler class is responsible for initializing Elementor
  13. * editor and register all the actions needed to display the editor.
  14. *
  15. * @since 1.0.0
  16. */
  17. class Editor {
  18. /**
  19. * The nonce key for Elementor editor.
  20. */
  21. const EDITING_NONCE_KEY = 'elementor-editing';
  22. /**
  23. * User capability required to access Elementor editor.
  24. */
  25. const EDITING_CAPABILITY = 'edit_posts';
  26. /**
  27. * Post ID.
  28. *
  29. * Holds the ID of the current post being edited.
  30. *
  31. * @since 1.0.0
  32. * @access private
  33. *
  34. * @var int Post ID.
  35. */
  36. private $_post_id;
  37. /**
  38. * Whether the edit mode is active.
  39. *
  40. * Used to determine whether we are in edit mode.
  41. *
  42. * @since 1.0.0
  43. * @access private
  44. *
  45. * @var bool Whether the edit mode is active.
  46. */
  47. private $_is_edit_mode;
  48. /**
  49. * Editor templates.
  50. *
  51. * Holds the editor templates used by Marionette.js.
  52. *
  53. * @since 1.0.0
  54. * @access private
  55. *
  56. * @var array Editor templates.
  57. */
  58. private $_editor_templates = [];
  59. /**
  60. * Init.
  61. *
  62. * Initialize Elementor editor. Registers all needed actions to run Elementor,
  63. * removes conflicting actions etc.
  64. *
  65. * Fired by `admin_action_elementor` action.
  66. *
  67. * @since 1.0.0
  68. * @access public
  69. *
  70. * @param bool $die Optional. Whether to die at the end. Default is `true`.
  71. */
  72. public function init( $die = true ) {
  73. if ( empty( $_REQUEST['post'] ) ) { // WPCS: CSRF ok.
  74. return;
  75. }
  76. $this->_post_id = absint( $_REQUEST['post'] );
  77. if ( ! $this->is_edit_mode( $this->_post_id ) ) {
  78. return;
  79. }
  80. // Send MIME Type header like WP admin-header.
  81. @header( 'Content-Type: ' . get_option( 'html_type' ) . '; charset=' . get_option( 'blog_charset' ) );
  82. // Use requested id and not the global in order to avoid conflicts with plugins that changes the global post.
  83. query_posts( [
  84. 'p' => $this->_post_id,
  85. 'post_type' => get_post_type( $this->_post_id ),
  86. ] );
  87. Plugin::$instance->db->switch_to_post( $this->_post_id );
  88. add_filter( 'show_admin_bar', '__return_false' );
  89. // Remove all WordPress actions
  90. remove_all_actions( 'wp_head' );
  91. remove_all_actions( 'wp_print_styles' );
  92. remove_all_actions( 'wp_print_head_scripts' );
  93. remove_all_actions( 'wp_footer' );
  94. // Handle `wp_head`
  95. add_action( 'wp_head', 'wp_enqueue_scripts', 1 );
  96. add_action( 'wp_head', 'wp_print_styles', 8 );
  97. add_action( 'wp_head', 'wp_print_head_scripts', 9 );
  98. add_action( 'wp_head', 'wp_site_icon' );
  99. add_action( 'wp_head', [ $this, 'editor_head_trigger' ], 30 );
  100. // Handle `wp_footer`
  101. add_action( 'wp_footer', 'wp_print_footer_scripts', 20 );
  102. add_action( 'wp_footer', 'wp_auth_check_html', 30 );
  103. add_action( 'wp_footer', [ $this, 'wp_footer' ] );
  104. // Handle `wp_enqueue_scripts`
  105. remove_all_actions( 'wp_enqueue_scripts' );
  106. // Also remove all scripts hooked into after_wp_tiny_mce.
  107. remove_all_actions( 'after_wp_tiny_mce' );
  108. add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_scripts' ], 999999 );
  109. add_action( 'wp_enqueue_scripts', [ $this, 'enqueue_styles' ], 999999 );
  110. // Change mode to Builder
  111. Plugin::$instance->db->set_is_elementor_page( $this->_post_id );
  112. // Post Lock
  113. if ( ! $this->get_locked_user( $this->_post_id ) ) {
  114. $this->lock_post( $this->_post_id );
  115. }
  116. // Setup default heartbeat options
  117. add_filter( 'heartbeat_settings', function( $settings ) {
  118. $settings['interval'] = 15;
  119. return $settings;
  120. } );
  121. // Tell to WP Cache plugins do not cache this request.
  122. Utils::do_not_cache();
  123. $this->print_editor_template();
  124. // From the action it's an empty string, from tests its `false`
  125. if ( false !== $die ) {
  126. die;
  127. }
  128. }
  129. /**
  130. * Retrieve post ID.
  131. *
  132. * Get the ID of the current post.
  133. *
  134. * @since 1.8.0
  135. * @access public
  136. *
  137. * @return int Post ID.
  138. */
  139. public function get_post_id() {
  140. return $this->_post_id;
  141. }
  142. /**
  143. * Redirect to new URL.
  144. *
  145. * Used as a fallback function for the old URL structure of Elementor page
  146. * edit URL.
  147. *
  148. * Fired by `template_redirect` action.
  149. *
  150. * @since 1.6.0
  151. * @access public
  152. */
  153. public function redirect_to_new_url() {
  154. if ( ! isset( $_GET['elementor'] ) ) {
  155. return;
  156. }
  157. $post_id = get_the_ID();
  158. if ( ! User::is_current_user_can_edit( $post_id ) || ! Plugin::$instance->db->is_built_with_elementor( $post_id ) ) {
  159. return;
  160. }
  161. wp_redirect( Utils::get_edit_link( $post_id ) );
  162. die;
  163. }
  164. /**
  165. * Whether the edit mode is active.
  166. *
  167. * Used to determine whether we are in the edit mode.
  168. *
  169. * @since 1.0.0
  170. * @access public
  171. *
  172. * @param int $post_id Optional. Post ID. Default is `null`, the current
  173. * post ID.
  174. *
  175. * @return bool Whether the edit mode is active.
  176. */
  177. public function is_edit_mode( $post_id = null ) {
  178. if ( null !== $this->_is_edit_mode ) {
  179. return $this->_is_edit_mode;
  180. }
  181. if ( empty( $post_id ) ) {
  182. $post_id = $this->_post_id;
  183. }
  184. if ( ! User::is_current_user_can_edit( $post_id ) ) {
  185. return false;
  186. }
  187. // Ajax request as Editor mode
  188. $actions = [
  189. 'elementor',
  190. // Templates
  191. 'elementor_get_templates',
  192. 'elementor_save_template',
  193. 'elementor_get_template',
  194. 'elementor_delete_template',
  195. 'elementor_export_template',
  196. 'elementor_import_template',
  197. ];
  198. if ( isset( $_REQUEST['action'] ) && in_array( $_REQUEST['action'], $actions ) ) {
  199. return true;
  200. }
  201. return false;
  202. }
  203. /**
  204. * Lock post.
  205. *
  206. * Mark the post as currently being edited by the current user.
  207. *
  208. * @since 1.0.0
  209. * @access public
  210. *
  211. * @param int $post_id The ID of the post being edited.
  212. */
  213. public function lock_post( $post_id ) {
  214. if ( ! function_exists( 'wp_set_post_lock' ) ) {
  215. require_once( ABSPATH . 'wp-admin/includes/post.php' );
  216. }
  217. wp_set_post_lock( $post_id );
  218. }
  219. /**
  220. * Get locked user.
  221. *
  222. * Check what user is currently editing the post.
  223. *
  224. * @since 1.0.0
  225. * @access public
  226. *
  227. * @param int $post_id The ID of the post being edited.
  228. *
  229. * @return \WP_User|false User information or false if the post is not locked.
  230. */
  231. public function get_locked_user( $post_id ) {
  232. if ( ! function_exists( 'wp_check_post_lock' ) ) {
  233. require_once( ABSPATH . 'wp-admin/includes/post.php' );
  234. }
  235. $locked_user = wp_check_post_lock( $post_id );
  236. if ( ! $locked_user ) {
  237. return false;
  238. }
  239. return get_user_by( 'id', $locked_user );
  240. }
  241. /**
  242. * Print panel HTML.
  243. *
  244. * Include the wrapper template of the editor.
  245. *
  246. * @since 1.0.0
  247. * @deprecated 2.2.0 Use `Editor::print_editor_template` instead
  248. * @access public
  249. */
  250. public function print_panel_html() {
  251. _deprecated_function( __METHOD__, '2.2.0', 'Editor::print_editor_template' );
  252. $this->print_editor_template();
  253. }
  254. /**
  255. * Print Editor Template.
  256. *
  257. * Include the wrapper template of the editor.
  258. *
  259. * @since 2.2.0
  260. * @access public
  261. */
  262. public function print_editor_template() {
  263. include( 'editor-templates/editor-wrapper.php' );
  264. }
  265. /**
  266. * Enqueue scripts.
  267. *
  268. * Registers all the editor scripts and enqueues them.
  269. *
  270. * @since 1.0.0
  271. * @access public
  272. */
  273. public function enqueue_scripts() {
  274. remove_action( 'wp_enqueue_scripts', [ $this, __FUNCTION__ ], 999999 );
  275. // Set the global data like $post, $authordata and etc
  276. setup_postdata( $this->_post_id );
  277. global $wp_styles, $wp_scripts;
  278. $plugin = Plugin::$instance;
  279. // Reset global variable
  280. $wp_styles = new \WP_Styles(); // WPCS: override ok.
  281. $wp_scripts = new \WP_Scripts(); // WPCS: override ok.
  282. $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG || defined( 'ELEMENTOR_TESTS' ) && ELEMENTOR_TESTS ) ? '' : '.min';
  283. // Hack for waypoint with editor mode.
  284. wp_register_script(
  285. 'elementor-waypoints',
  286. ELEMENTOR_ASSETS_URL . 'lib/waypoints/waypoints-for-editor.js',
  287. [
  288. 'jquery',
  289. ],
  290. '4.0.2',
  291. true
  292. );
  293. wp_register_script(
  294. 'backbone-marionette',
  295. ELEMENTOR_ASSETS_URL . 'lib/backbone/backbone.marionette' . $suffix . '.js',
  296. [
  297. 'backbone',
  298. ],
  299. '2.4.5',
  300. true
  301. );
  302. wp_register_script(
  303. 'backbone-radio',
  304. ELEMENTOR_ASSETS_URL . 'lib/backbone/backbone.radio' . $suffix . '.js',
  305. [
  306. 'backbone',
  307. ],
  308. '1.0.4',
  309. true
  310. );
  311. wp_register_script(
  312. 'perfect-scrollbar',
  313. ELEMENTOR_ASSETS_URL . 'lib/perfect-scrollbar/perfect-scrollbar.jquery' . $suffix . '.js',
  314. [
  315. 'jquery',
  316. ],
  317. '0.6.12',
  318. true
  319. );
  320. wp_register_script(
  321. 'jquery-easing',
  322. ELEMENTOR_ASSETS_URL . 'lib/jquery-easing/jquery-easing' . $suffix . '.js',
  323. [
  324. 'jquery',
  325. ],
  326. '1.3.2',
  327. true
  328. );
  329. wp_register_script(
  330. 'nprogress',
  331. ELEMENTOR_ASSETS_URL . 'lib/nprogress/nprogress' . $suffix . '.js',
  332. [],
  333. '0.2.0',
  334. true
  335. );
  336. wp_register_script(
  337. 'tipsy',
  338. ELEMENTOR_ASSETS_URL . 'lib/tipsy/tipsy' . $suffix . '.js',
  339. [
  340. 'jquery',
  341. ],
  342. '1.0.0',
  343. true
  344. );
  345. wp_register_script(
  346. 'jquery-elementor-select2',
  347. ELEMENTOR_ASSETS_URL . 'lib/e-select2/js/e-select2.full' . $suffix . '.js',
  348. [
  349. 'jquery',
  350. ],
  351. '4.0.6-rc.1',
  352. true
  353. );
  354. wp_register_script(
  355. 'flatpickr',
  356. ELEMENTOR_ASSETS_URL . 'lib/flatpickr/flatpickr' . $suffix . '.js',
  357. [
  358. 'jquery',
  359. ],
  360. '1.12.0',
  361. true
  362. );
  363. wp_register_script(
  364. 'ace',
  365. 'https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.5/ace.js',
  366. [],
  367. '1.2.5',
  368. true
  369. );
  370. wp_register_script(
  371. 'ace-language-tools',
  372. 'https://cdnjs.cloudflare.com/ajax/libs/ace/1.2.5/ext-language_tools.js',
  373. [
  374. 'ace',
  375. ],
  376. '1.2.5',
  377. true
  378. );
  379. wp_register_script(
  380. 'jquery-hover-intent',
  381. ELEMENTOR_ASSETS_URL . 'lib/jquery-hover-intent/jquery-hover-intent' . $suffix . '.js',
  382. [],
  383. '1.0.0',
  384. true
  385. );
  386. wp_register_script(
  387. 'elementor-dialog',
  388. ELEMENTOR_ASSETS_URL . 'lib/dialog/dialog' . $suffix . '.js',
  389. [
  390. 'jquery-ui-position',
  391. ],
  392. '4.5.0',
  393. true
  394. );
  395. wp_register_script(
  396. 'elementor-editor',
  397. ELEMENTOR_ASSETS_URL . 'js/editor' . $suffix . '.js',
  398. [
  399. 'wp-auth-check',
  400. 'jquery-ui-sortable',
  401. 'jquery-ui-resizable',
  402. 'backbone-marionette',
  403. 'backbone-radio',
  404. 'perfect-scrollbar',
  405. 'nprogress',
  406. 'tipsy',
  407. 'imagesloaded',
  408. 'heartbeat',
  409. 'jquery-elementor-select2',
  410. 'flatpickr',
  411. 'elementor-dialog',
  412. 'ace',
  413. 'ace-language-tools',
  414. 'jquery-hover-intent',
  415. ],
  416. ELEMENTOR_VERSION,
  417. true
  418. );
  419. /**
  420. * Before editor enqueue scripts.
  421. *
  422. * Fires before Elementor editor scripts are enqueued.
  423. *
  424. * @since 1.0.0
  425. */
  426. do_action( 'elementor/editor/before_enqueue_scripts' );
  427. $document = Plugin::$instance->documents->get_doc_or_auto_save( $this->_post_id );
  428. // Get document data *after* the scripts hook - so plugins can run compatibility before get data, but *before* enqueue the editor script - so elements can enqueue their own scripts that depended in editor script.
  429. $editor_data = $document->get_elements_raw_data( null, true );
  430. wp_enqueue_script( 'elementor-editor' );
  431. // Tweak for WP Admin menu icons
  432. wp_print_styles( 'editor-buttons' );
  433. $locked_user = $this->get_locked_user( $this->_post_id );
  434. if ( $locked_user ) {
  435. $locked_user = $locked_user->display_name;
  436. }
  437. $page_title_selector = get_option( 'elementor_page_title_selector' );
  438. if ( empty( $page_title_selector ) ) {
  439. $page_title_selector = 'h1.entry-title';
  440. }
  441. $post_type_object = get_post_type_object( $document->get_main_post()->post_type );
  442. $current_user_can_publish = current_user_can( $post_type_object->cap->publish_posts );
  443. $config = [
  444. 'version' => ELEMENTOR_VERSION,
  445. 'ajaxurl' => admin_url( 'admin-ajax.php' ),
  446. 'home_url' => home_url(),
  447. 'nonce' => $this->create_nonce( get_post_type() ),
  448. 'data' => $editor_data,
  449. // @TODO: `post_id` is bc since 2.0.0
  450. 'post_id' => $this->_post_id,
  451. 'document' => $document->get_config(),
  452. 'autosave_interval' => AUTOSAVE_INTERVAL,
  453. 'current_user_can_publish' => $current_user_can_publish,
  454. 'controls' => $plugin->controls_manager->get_controls_data(),
  455. 'elements' => $plugin->elements_manager->get_element_types_config(),
  456. 'widgets' => $plugin->widgets_manager->get_widget_types_config(),
  457. 'schemes' => [
  458. 'items' => $plugin->schemes_manager->get_registered_schemes_data(),
  459. 'enabled_schemes' => Schemes_Manager::get_enabled_schemes(),
  460. ],
  461. 'default_schemes' => $plugin->schemes_manager->get_schemes_defaults(),
  462. 'settings' => SettingsManager::get_settings_managers_config(),
  463. 'system_schemes' => $plugin->schemes_manager->get_system_schemes(),
  464. 'wp_editor' => $this->get_wp_editor_config(),
  465. 'settings_page_link' => Settings::get_url(),
  466. 'elementor_site' => 'https://go.elementor.com/about-elementor/',
  467. 'docs_elementor_site' => 'https://go.elementor.com/docs/',
  468. 'help_the_content_url' => 'https://go.elementor.com/the-content-missing/',
  469. 'help_preview_error_url' => 'https://go.elementor.com/preview-not-loaded/',
  470. 'help_right_click_url' => 'https://go.elementor.com/meet-right-click/',
  471. 'assets_url' => ELEMENTOR_ASSETS_URL,
  472. 'locked_user' => $locked_user,
  473. 'user' => [
  474. 'restrictions' => $plugin->role_manager->get_user_restrictions_array(),
  475. 'is_administrator' => current_user_can( 'manage_options' ),
  476. 'introduction' => User::is_should_view_introduction(),
  477. ],
  478. 'is_rtl' => is_rtl(),
  479. 'locale' => get_locale(),
  480. 'rich_editing_enabled' => filter_var( get_user_meta( get_current_user_id(), 'rich_editing', true ), FILTER_VALIDATE_BOOLEAN ),
  481. 'page_title_selector' => $page_title_selector,
  482. 'tinymceHasCustomConfig' => class_exists( 'Tinymce_Advanced' ),
  483. 'inlineEditing' => Plugin::$instance->widgets_manager->get_inline_editing_config(),
  484. 'dynamicTags' => Plugin::$instance->dynamic_tags->get_config(),
  485. 'i18n' => [
  486. 'elementor' => __( 'Elementor', 'elementor' ),
  487. 'delete' => __( 'Delete', 'elementor' ),
  488. 'cancel' => __( 'Cancel', 'elementor' ),
  489. /* translators: %s: Element name. */
  490. 'edit_element' => __( 'Edit %s', 'elementor' ),
  491. // Menu.
  492. 'about_elementor' => __( 'About Elementor', 'elementor' ),
  493. 'color_picker' => __( 'Color Picker', 'elementor' ),
  494. 'elementor_settings' => __( 'Dashboard Settings', 'elementor' ),
  495. 'global_colors' => __( 'Default Colors', 'elementor' ),
  496. 'global_fonts' => __( 'Default Fonts', 'elementor' ),
  497. 'global_style' => __( 'Style', 'elementor' ),
  498. 'settings' => __( 'Settings', 'elementor' ),
  499. // Elements.
  500. 'inner_section' => __( 'Inner Section', 'elementor' ),
  501. // Control Order.
  502. 'asc' => __( 'Ascending order', 'elementor' ),
  503. 'desc' => __( 'Descending order', 'elementor' ),
  504. // Clear Page.
  505. 'clear_page' => __( 'Delete All Content', 'elementor' ),
  506. 'dialog_confirm_clear_page' => __( 'Attention: We are going to DELETE ALL CONTENT from this page. Are you sure you want to do that?', 'elementor' ),
  507. // Panel Preview Mode.
  508. 'back_to_editor' => __( 'Show Panel', 'elementor' ),
  509. 'preview' => __( 'Hide Panel', 'elementor' ),
  510. // Inline Editing.
  511. 'type_here' => __( 'Type Here', 'elementor' ),
  512. // Library.
  513. 'an_error_occurred' => __( 'An error occurred', 'elementor' ),
  514. 'category' => __( 'Category', 'elementor' ),
  515. 'delete_template' => __( 'Delete Template', 'elementor' ),
  516. 'delete_template_confirm' => __( 'Are you sure you want to delete this template?', 'elementor' ),
  517. 'import_template_dialog_header' => __( 'Import Document Settings', 'elementor' ),
  518. 'import_template_dialog_message' => __( 'Do you want to also import the document settings of the template?', 'elementor' ),
  519. 'import_template_dialog_message_attention' => __( 'Attention: Importing may override previous settings.', 'elementor' ),
  520. 'library' => __( 'Library', 'elementor' ),
  521. 'no' => __( 'No', 'elementor' ),
  522. 'page' => __( 'Page', 'elementor' ),
  523. /* translators: %s: Template type. */
  524. 'save_your_template' => __( 'Save Your %s to Library', 'elementor' ),
  525. 'save_your_template_description' => __( 'Your designs will be available for export and reuse on any page or website', 'elementor' ),
  526. 'section' => __( 'Section', 'elementor' ),
  527. 'templates_empty_message' => __( 'This is where your templates should be. Design it. Save it. Reuse it.', 'elementor' ),
  528. 'templates_empty_title' => __( 'Haven’t Saved Templates Yet?', 'elementor' ),
  529. 'templates_no_favorites_message' => __( 'You can mark any pre-designed template as a favorite.', 'elementor' ),
  530. 'templates_no_favorites_title' => __( 'No Favorite Templates', 'elementor' ),
  531. 'templates_no_results_message' => __( 'Please make sure your search is spelled correctly or try a different words.', 'elementor' ),
  532. 'templates_no_results_title' => __( 'No Results Found', 'elementor' ),
  533. 'templates_request_error' => __( 'The following error(s) occurred while processing the request:', 'elementor' ),
  534. 'yes' => __( 'Yes', 'elementor' ),
  535. // Incompatible Device.
  536. 'device_incompatible_header' => __( 'Your browser isn\'t compatible', 'elementor' ),
  537. 'device_incompatible_message' => __( 'Your browser isn\'t compatible with all of Elementor\'s editing features. We recommend you switch to another browser like Chrome or Firefox.', 'elementor' ),
  538. 'proceed_anyway' => __( 'Proceed Anyway', 'elementor' ),
  539. // Preview not loaded.
  540. 'learn_more' => __( 'Learn More', 'elementor' ),
  541. 'preview_el_not_found_header' => __( 'Sorry, the content area was not found in your page.', 'elementor' ),
  542. 'preview_el_not_found_message' => __( 'You must call \'the_content\' function in the current template, in order for Elementor to work on this page.', 'elementor' ),
  543. 'preview_not_loading_header' => __( 'The preview could not be loaded', 'elementor' ),
  544. 'preview_not_loading_message' => __( 'We\'re sorry, but something went wrong. Click on \'Learn more\' and follow each of the steps to quickly solve it.', 'elementor' ),
  545. // Gallery.
  546. 'delete_gallery' => __( 'Reset Gallery', 'elementor' ),
  547. 'dialog_confirm_gallery_delete' => __( 'Are you sure you want to reset this gallery?', 'elementor' ),
  548. /* translators: %s: The number of images. */
  549. 'gallery_images_selected' => __( '%s Images Selected', 'elementor' ),
  550. 'gallery_no_images_selected' => __( 'No Images Selected', 'elementor' ),
  551. 'insert_media' => __( 'Insert Media', 'elementor' ),
  552. // Take Over.
  553. /* translators: %s: User name. */
  554. 'dialog_user_taken_over' => __( '%s has taken over and is currently editing. Do you want to take over this page editing?', 'elementor' ),
  555. 'go_back' => __( 'Go Back', 'elementor' ),
  556. 'take_over' => __( 'Take Over', 'elementor' ),
  557. // Revisions.
  558. /* translators: %s: Element type. */
  559. 'delete_element' => __( 'Delete %s', 'elementor' ),
  560. /* translators: %s: Template type. */
  561. 'dialog_confirm_delete' => __( 'Are you sure you want to remove this %s?', 'elementor' ),
  562. // Saver.
  563. 'before_unload_alert' => __( 'Please note: All unsaved changes will be lost.', 'elementor' ),
  564. 'published' => __( 'Published', 'elementor' ),
  565. 'publish' => __( 'Publish', 'elementor' ),
  566. 'save' => __( 'Save', 'elementor' ),
  567. 'saved' => __( 'Saved', 'elementor' ),
  568. 'update' => __( 'Update', 'elementor' ),
  569. 'submit' => __( 'Submit', 'elementor' ),
  570. 'working_on_draft_notification' => __( 'This is just a draft. Play around and when you\'re done - click update.', 'elementor' ),
  571. 'keep_editing' => __( 'Keep Editing', 'elementor' ),
  572. 'have_a_look' => __( 'Have a look', 'elementor' ),
  573. 'view_all_revisions' => __( 'View All Revisions', 'elementor' ),
  574. 'dismiss' => __( 'Dismiss', 'elementor' ),
  575. 'saving_disabled' => __( 'Saving has been disabled until you’re reconnected.', 'elementor' ),
  576. // Ajax
  577. 'server_error' => __( 'Server Error', 'elementor' ),
  578. 'server_connection_lost' => __( 'Connection Lost', 'elementor' ),
  579. 'unknown_error' => __( 'Unknown Error', 'elementor' ),
  580. // Context Menu
  581. 'duplicate' => __( 'Duplicate', 'elementor' ),
  582. 'copy' => __( 'Copy', 'elementor' ),
  583. 'paste' => __( 'Paste', 'elementor' ),
  584. 'copy_style' => __( 'Copy Style', 'elementor' ),
  585. 'paste_style' => __( 'Paste Style', 'elementor' ),
  586. 'reset_style' => __( 'Reset Style', 'elementor' ),
  587. 'save_as_global' => __( 'Save as a Global', 'elementor' ),
  588. 'save_as_block' => __( 'Save as Template', 'elementor' ),
  589. 'new_column' => __( 'Add New Column', 'elementor' ),
  590. 'copy_all_content' => __( 'Copy All Content', 'elementor' ),
  591. 'delete_all_content' => __( 'Delete All Content', 'elementor' ),
  592. 'navigator' => __( 'Navigator', 'elementor' ),
  593. // Right Click Introduction
  594. 'meet_right_click_header' => __( 'Meet Right Click', 'elementor' ),
  595. 'meet_right_click_message' => __( 'Now you can access all editing actions using right click.', 'elementor' ),
  596. 'got_it' => __( 'Got It', 'elementor' ),
  597. // TODO: Remove.
  598. 'autosave' => __( 'Autosave', 'elementor' ),
  599. 'elementor_docs' => __( 'Documentation', 'elementor' ),
  600. 'reload_page' => __( 'Reload Page', 'elementor' ),
  601. 'session_expired_header' => __( 'Timeout', 'elementor' ),
  602. 'session_expired_message' => __( 'Your session has expired. Please reload the page to continue editing.', 'elementor' ),
  603. 'soon' => __( 'Soon', 'elementor' ),
  604. 'unknown_value' => __( 'Unknown Value', 'elementor' ),
  605. ],
  606. ];
  607. $localized_settings = [];
  608. /**
  609. * Localize editor settings.
  610. *
  611. * Filters the editor localized settings.
  612. *
  613. * @since 1.0.0
  614. *
  615. * @param array $localized_settings Localized settings.
  616. * @param int $post_id The ID of the current post being edited.
  617. */
  618. $localized_settings = apply_filters( 'elementor/editor/localize_settings', $localized_settings, $this->_post_id );
  619. if ( ! empty( $localized_settings ) ) {
  620. $config = array_replace_recursive( $config, $localized_settings );
  621. }
  622. echo '<script>' . PHP_EOL;
  623. echo '/* <![CDATA[ */' . PHP_EOL;
  624. $config_json = wp_json_encode( $config );
  625. unset( $config );
  626. if ( get_option( 'elementor_editor_break_lines' ) ) {
  627. // Add new lines to avoid memory limits in some hosting servers that handles the buffer output according to new line characters
  628. $config_json = str_replace( '}},"', '}},' . PHP_EOL . '"', $config_json );
  629. }
  630. echo 'var ElementorConfig = ' . $config_json . ';' . PHP_EOL;
  631. echo '/* ]]> */' . PHP_EOL;
  632. echo '</script>';
  633. $plugin->controls_manager->enqueue_control_scripts();
  634. /**
  635. * After editor enqueue scripts.
  636. *
  637. * Fires after Elementor editor scripts are enqueued.
  638. *
  639. * @since 1.0.0
  640. */
  641. do_action( 'elementor/editor/after_enqueue_scripts' );
  642. }
  643. /**
  644. * Enqueue styles.
  645. *
  646. * Registers all the editor styles and enqueues them.
  647. *
  648. * @since 1.0.0
  649. * @access public
  650. */
  651. public function enqueue_styles() {
  652. /**
  653. * Before editor enqueue styles.
  654. *
  655. * Fires before Elementor editor styles are enqueued.
  656. *
  657. * @since 1.0.0
  658. */
  659. do_action( 'elementor/editor/before_enqueue_styles' );
  660. $suffix = Utils::is_script_debug() ? '' : '.min';
  661. $direction_suffix = is_rtl() ? '-rtl' : '';
  662. wp_register_style(
  663. 'font-awesome',
  664. ELEMENTOR_ASSETS_URL . 'lib/font-awesome/css/font-awesome' . $suffix . '.css',
  665. [],
  666. '4.7.0'
  667. );
  668. wp_register_style(
  669. 'elementor-select2',
  670. ELEMENTOR_ASSETS_URL . 'lib/e-select2/css/e-select2' . $suffix . '.css',
  671. [],
  672. '4.0.6-rc.1'
  673. );
  674. wp_register_style(
  675. 'elementor-icons',
  676. ELEMENTOR_ASSETS_URL . 'lib/eicons/css/elementor-icons' . $suffix . '.css',
  677. [],
  678. '3.8.0'
  679. );
  680. wp_register_style(
  681. 'google-font-roboto',
  682. 'https://fonts.googleapis.com/css?family=Roboto:300,400,500,700',
  683. [],
  684. ELEMENTOR_VERSION
  685. );
  686. wp_register_style(
  687. 'flatpickr',
  688. ELEMENTOR_ASSETS_URL . 'lib/flatpickr/flatpickr' . $suffix . '.css',
  689. [],
  690. '1.12.0'
  691. );
  692. wp_register_style(
  693. 'elementor-editor',
  694. ELEMENTOR_ASSETS_URL . 'css/editor' . $direction_suffix . $suffix . '.css',
  695. [
  696. 'font-awesome',
  697. 'elementor-select2',
  698. 'elementor-icons',
  699. 'wp-auth-check',
  700. 'google-font-roboto',
  701. 'flatpickr',
  702. ],
  703. ELEMENTOR_VERSION
  704. );
  705. wp_enqueue_style( 'elementor-editor' );
  706. if ( Responsive::has_custom_breakpoints() ) {
  707. $breakpoints = Responsive::get_breakpoints();
  708. wp_add_inline_style( 'elementor-editor', '.elementor-device-tablet #elementor-preview-responsive-wrapper { width: ' . $breakpoints['md'] . 'px; }' );
  709. }
  710. /**
  711. * After editor enqueue styles.
  712. *
  713. * Fires after Elementor editor styles are enqueued.
  714. *
  715. * @since 1.0.0
  716. */
  717. do_action( 'elementor/editor/after_enqueue_styles' );
  718. }
  719. /**
  720. * Get WordPress editor config.
  721. *
  722. * Config the default WordPress editor with custom settings for Elementor use.
  723. *
  724. * @since 1.9.0
  725. * @access private
  726. */
  727. private function get_wp_editor_config() {
  728. // Remove all TinyMCE plugins.
  729. remove_all_filters( 'mce_buttons', 10 );
  730. remove_all_filters( 'mce_external_plugins', 10 );
  731. if ( ! class_exists( '\_WP_Editors', false ) ) {
  732. require( ABSPATH . WPINC . '/class-wp-editor.php' );
  733. }
  734. // WordPress 4.8 and higher
  735. if ( method_exists( '\_WP_Editors', 'print_tinymce_scripts' ) ) {
  736. \_WP_Editors::print_default_editor_scripts();
  737. \_WP_Editors::print_tinymce_scripts();
  738. }
  739. ob_start();
  740. wp_editor(
  741. '%%EDITORCONTENT%%',
  742. 'elementorwpeditor',
  743. [
  744. 'editor_class' => 'elementor-wp-editor',
  745. 'editor_height' => 250,
  746. 'drag_drop_upload' => true,
  747. ]
  748. );
  749. $config = ob_get_clean();
  750. // Don't call \_WP_Editors methods again
  751. remove_action( 'admin_print_footer_scripts', [ '_WP_Editors', 'editor_js' ], 50 );
  752. remove_action( 'admin_print_footer_scripts', [ '_WP_Editors', 'print_default_editor_scripts' ], 45 );
  753. \_WP_Editors::editor_js();
  754. return $config;
  755. }
  756. /**
  757. * Editor head trigger.
  758. *
  759. * Fires the 'elementor/editor/wp_head' action in the head tag in Elementor
  760. * editor.
  761. *
  762. * @since 1.0.0
  763. * @access public
  764. */
  765. public function editor_head_trigger() {
  766. /**
  767. * Elementor editor head.
  768. *
  769. * Fires on Elementor editor head tag.
  770. *
  771. * Used to prints scripts or any other data in the head tag.
  772. *
  773. * @since 1.0.0
  774. */
  775. do_action( 'elementor/editor/wp_head' );
  776. }
  777. /**
  778. * Add editor template.
  779. *
  780. * Registers new editor templates.
  781. *
  782. * @since 1.0.0
  783. * @access public
  784. *
  785. * @param string $template Can be either a link to template file or template
  786. * HTML content.
  787. * @param string $type Optional. Whether to handle the template as path
  788. * or text. Default is `path`.
  789. */
  790. public function add_editor_template( $template, $type = 'path' ) {
  791. if ( 'path' === $type ) {
  792. ob_start();
  793. include $template;
  794. $template = ob_get_clean();
  795. }
  796. $this->_editor_templates[] = $template;
  797. }
  798. /**
  799. * WP footer.
  800. *
  801. * Prints Elementor editor with all the editor templates, and render controls,
  802. * widgets and content elements.
  803. *
  804. * Fired by `wp_footer` action.
  805. *
  806. * @since 1.0.0
  807. * @access public
  808. */
  809. public function wp_footer() {
  810. $plugin = Plugin::$instance;
  811. $plugin->controls_manager->render_controls();
  812. $plugin->widgets_manager->render_widgets_content();
  813. $plugin->elements_manager->render_elements_content();
  814. $plugin->schemes_manager->print_schemes_templates();
  815. $plugin->dynamic_tags->print_templates();
  816. $this->init_editor_templates();
  817. foreach ( $this->_editor_templates as $editor_template ) {
  818. echo $editor_template;
  819. }
  820. /**
  821. * Elementor editor footer.
  822. *
  823. * Fires on Elementor editor before closing the body tag.
  824. *
  825. * Used to prints scripts or any other HTML before closing the body tag.
  826. *
  827. * @since 1.0.0
  828. */
  829. do_action( 'elementor/editor/footer' );
  830. }
  831. /**
  832. * Set edit mode.
  833. *
  834. * Used to update the edit mode.
  835. *
  836. * @since 1.0.0
  837. * @access public
  838. *
  839. * @param bool $edit_mode Whether the edit mode is active.
  840. */
  841. public function set_edit_mode( $edit_mode ) {
  842. $this->_is_edit_mode = $edit_mode;
  843. }
  844. /**
  845. * Editor constructor.
  846. *
  847. * Initializing Elementor editor and redirect from old URL structure of
  848. * Elementor editor.
  849. *
  850. * @since 1.0.0
  851. * @access public
  852. */
  853. public function __construct() {
  854. add_action( 'admin_action_elementor', [ $this, 'init' ] );
  855. add_action( 'template_redirect', [ $this, 'redirect_to_new_url' ] );
  856. // Handle autocomplete feature for URL control.
  857. add_filter( 'wp_link_query_args', [ $this, 'filter_wp_link_query_args' ] );
  858. add_filter( 'wp_link_query', [ $this, 'filter_wp_link_query' ] );
  859. }
  860. public function filter_wp_link_query_args( $query ) {
  861. $library_cpt_key = array_search( Source_Local::CPT, $query['post_type'], true );
  862. if ( false !== $library_cpt_key ) {
  863. unset( $query['post_type'][ $library_cpt_key ] );
  864. }
  865. return $query;
  866. }
  867. public function filter_wp_link_query( $results ) {
  868. if ( isset( $_POST['editor'] ) && 'elementor' === $_POST['editor'] ) {
  869. $post_type_object = get_post_type_object( 'post' );
  870. $post_label = $post_type_object->labels->singular_name;
  871. foreach ( $results as & $result ) {
  872. if ( 'post' === get_post_type( $result['ID'] ) ) {
  873. $result['info'] = $post_label;
  874. }
  875. }
  876. }
  877. return $results;
  878. }
  879. /**
  880. * Create nonce.
  881. *
  882. * If the user has edit capabilities, it creates a cryptographic token to
  883. * give him access to Elementor editor.
  884. *
  885. * @since 1.8.1
  886. * @since 1.8.7 The `$post_type` parameter was introduces.
  887. * @access public
  888. *
  889. * @param string $post_type The post type to check capabilities.
  890. *
  891. * @return null|string The nonce token, or `null` if the user has no edit
  892. * capabilities.
  893. */
  894. public function create_nonce( $post_type ) {
  895. $post_type_object = get_post_type_object( $post_type );
  896. $capability = $post_type_object->cap->{self::EDITING_CAPABILITY};
  897. if ( ! current_user_can( $capability ) ) {
  898. return null;
  899. }
  900. return wp_create_nonce( self::EDITING_NONCE_KEY );
  901. }
  902. /**
  903. * Verify nonce.
  904. *
  905. * The user is given an amount of time to use the token, so therefore, since
  906. * the user ID and `$action` remain the same, the independent variable is
  907. * the time.
  908. *
  909. * @since 1.8.1
  910. * @access public
  911. *
  912. * @param string $nonce Nonce that was used in the form to verify.
  913. *
  914. * @return false|int If the nonce is invalid it returns `false`. If the
  915. * nonce is valid and generated between 0-12 hours ago it
  916. * returns `1`. If the nonce is valid and generated
  917. * between 12-24 hours ago it returns `2`.
  918. */
  919. public function verify_nonce( $nonce ) {
  920. return wp_verify_nonce( $nonce, self::EDITING_NONCE_KEY );
  921. }
  922. /**
  923. * Verify request nonce.
  924. *
  925. * Whether the request nonce verified or not.
  926. *
  927. * @since 1.8.1
  928. * @access public
  929. *
  930. * @return bool True if request nonce verified, False otherwise.
  931. */
  932. public function verify_request_nonce() {
  933. return ! empty( $_REQUEST['_nonce'] ) && $this->verify_nonce( $_REQUEST['_nonce'] );
  934. }
  935. /**
  936. * Verify ajax nonce.
  937. *
  938. * Verify request nonce and send a JSON request, if not verified returns an
  939. * error.
  940. *
  941. * @since 1.9.0
  942. * @access public
  943. */
  944. public function verify_ajax_nonce() {
  945. if ( ! $this->verify_request_nonce() ) {
  946. wp_send_json_error( new \WP_Error( 'token_expired', 'Nonce token expired.' ) );
  947. }
  948. }
  949. /**
  950. * Init editor templates.
  951. *
  952. * Initialize default elementor templates used in the editor panel.
  953. *
  954. * @since 1.7.0
  955. * @access private
  956. */
  957. private function init_editor_templates() {
  958. $template_names = [
  959. 'global',
  960. 'panel',
  961. 'panel-elements',
  962. 'repeater',
  963. 'library-layout',
  964. 'templates',
  965. 'navigator',
  966. ];
  967. foreach ( $template_names as $template_name ) {
  968. $this->add_editor_template( __DIR__ . "/editor-templates/$template_name.php" );
  969. }
  970. }
  971. }