' . $label . '

'; } return $label; } public function get_location_labels() { return [ self::OPTION_LOCATION_HEAD => esc_html__( 'Head', 'elementor-pro' ), self::OPTION_LOCATION_BODY_START => esc_html__( 'Body Start', 'elementor-pro' ), self::OPTION_LOCATION_BODY_END => esc_html__( 'Body End', 'elementor-pro' ), ]; } public function get_location_options() { return [ self::OPTION_LOCATION_HEAD => '', self::OPTION_LOCATION_BODY_START => sprintf( /* translators: %s: Body opening tag. */ esc_html__( '%s - Start', 'elementor-pro' ), '' ), self::OPTION_LOCATION_BODY_END => sprintf( /* translators: %s: Body closing tag. */ esc_html__( '%s - End', 'elementor-pro' ), '' ), ]; } public function get_priority_options() { $start = 1; $result = range( $start, self::OPTION_PRIORITY_LENGTH ); $result = array_combine( $result, $result ); return $result; } /** * Add script integrity. * * This is method is public, since its has to remove its own filter. * * @param string $html * @param mixed $handle * * @return string */ public function add_script_integrity( $html, $handle ) { if ( 'jshint' === $handle ) { $html = str_replace( '>', ' integrity="sha512-qcoitUjhkmNyPmbIOlUV/zd8MJvrVcKrNqnveMWS3C6MYOl5+HLwliRKUm/Ae/dfIok6+E54hjgVrAeS+sBAGA==" crossorigin="anonymous">', $html ); remove_filter( 'script_loader_tag', [ $this, 'add_script_integrity' ] ); } return $html; } protected function actions() { add_action( 'add_meta_boxes_' . Module::CPT, function () { $this->add_meta_boxes(); } ); add_action( 'save_post_' . Module::CPT, function( $post_id, $post, $update ) { return $this->save_post_meta( $post_id, $post ); }, 10, 3 ); add_action('post_submitbox_misc_actions', function ( $post ) { $this->add_meta_publish_options( $post ); } ); } private function get_fields() { return [ [ 'id' => 'open-div-meta-box', 'field_type' => 'html_tag', 'label' => false, 'tag' => 'div', 'attributes' => [ 'class' => 'elementor-custom-code-meta-box', ], ], [ 'id' => 'open-div-panel', 'field_type' => 'html_tag', 'label' => false, 'tag' => 'div', 'attributes' => [ 'class' => 'elementor-custom-code-panel', ], ], [ 'id' => 'open-div-placement', 'field_type' => 'html_tag', 'label' => false, 'tag' => 'div', 'attributes' => [ 'class' => 'elementor-custom-code-panel-placement', ], ], [ 'id' => self::FIELD_LOCATION, 'field_type' => 'select', 'label' => esc_html__( 'Location', 'elementor-pro' ) . ':', 'options' => $this->get_location_options(), 'info' => esc_html__( 'Define where the Custom Code will appear', 'elementor-pro' ), ], [ 'id' => 'open-div-placement', 'field_type' => 'html_tag', 'label' => false, 'tag' => 'div', 'attributes' => [ 'class' => 'elementor-custom-code-options-placement', ], ], [ 'id' => self::FILED_EXTRA_OPTIONS, 'field_type' => 'checkbox', 'options' => [ self::INPUT_OPTION_ENSURE_JQUERY => esc_html__( 'Always load jQuery', 'elementor-pro' ), ], 'info' => esc_html__( 'If your snippet includes jQuery, this will ensure it will work for all visitors. It may have a minor impact on loading speed.', 'elementor-pro' ), ], [ 'id' => 'close-div-placement', 'field_type' => 'html_tag', 'label' => false, 'tag' => 'div', 'close' => true, ], [ 'id' => self::FIELD_PRIORITY, 'field_type' => 'select', 'label' => esc_html__( 'Priority', 'elementor-pro' ) . ':', 'options' => $this->get_priority_options(), 'info' => esc_html__( 'Define in which order the Custom Code will appear', 'elementor-pro' ), ], [ 'id' => 'close-div-placement', 'field_type' => 'html_tag', 'label' => false, 'tag' => 'div', 'close' => true, ], [ 'id' => 'close-div-panel', 'field_type' => 'html_tag', 'label' => false, 'tag' => 'div', 'close' => true, ], [ 'id' => 'close-div-meta-box', 'field_type' => 'html_tag', 'label' => false, 'tag' => 'div', 'close' => true, ], [ 'id' => 'open-div-code-mirror-holder', 'field_type' => 'html_tag', 'label' => false, 'tag' => 'div', 'attributes' => [ 'class' => 'elementor-custom-code-codemirror-holder', ], ], [ 'id' => 'open-div-code-mirror', 'field_type' => 'html_tag', 'label' => false, 'tag' => 'div', 'attributes' => [ 'class' => 'elementor-custom-code-codemirror', ], ], [ 'id' => self::FIELD_CODE, 'field_type' => 'textarea', 'label' => '', 'extra_attributes' => [ 'class' => 'hidden', ], ], [ 'id' => 'close-div-code-mirror', 'field_type' => 'html_tag', 'label' => false, 'tag' => 'div', 'close' => true, ], [ 'id' => 'close-div-code-mirror-holder', 'field_type' => 'html_tag', 'label' => false, 'tag' => 'div', 'close' => true, ], ]; } private function get_code_editor_settings() { // TODO: Handle `enqueue_code_editor_scripts` to work with `lint => 'true'`. return [ 'type' => 'text/html', 'codemirror' => [ 'indentUnit' => 2, 'tabSize' => 2, 'gutters' => [ 'CodeMirror-lint-markers' ], ], ]; } private function enqueue_code_editor_scripts( $field_code_id ) { // Add integrity attribute to jshint. add_filter( 'script_loader_tag', [ $this, 'add_script_integrity' ], 10, 2 ); wp_enqueue_script( 'htmlhint' ); wp_enqueue_script( 'csslint' ); wp_deregister_script( 'jshint' ); wp_enqueue_script( 'jshint', 'https://cdnjs.cloudflare.com/ajax/libs/jshint/2.12.0/jshint.min.js', [], '2.12.0' ); /** * Some of the plugins may load 'code-editor' for their needs and change the default behavior, so it should * re-initialize the code editor with 'custom code' settings. */ if ( wp_script_is( 'code-editor' ) ) { wp_add_inline_script( 'custom-code-metabox', sprintf( 'wp.codeEditor.initialize( jQuery( "#%s"), %s );', $field_code_id, wp_json_encode( wp_get_code_editor_settings( $this->get_code_editor_settings() ) ) ) ); } else { wp_enqueue_code_editor( $this->get_code_editor_settings() ); wp_add_inline_script( 'code-editor', sprintf( 'wp.codeEditor.initialize( jQuery( "#%s") );', $field_code_id ) ); } } private function render_meta_box() { $fields = $this->get_fields(); if ( ! empty( $_REQUEST['action'] ) && 'edit' == $_REQUEST['action'] ) { $post = get_post( \ElementorPro\Core\Utils::_unstable_get_super_global_value( $_REQUEST, 'post' ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verification is not required here. foreach ( self::INPUT_FIELDS as $input_field ) { $field_meta = get_post_meta( $post->ID, "_elementor_$input_field", true ); if ( ! empty( $field_meta ) ) { $key = array_search( $input_field, array_column( $fields, 'id' ) ); if ( false !== $key ) { $fields[ $key ]['saved'] = $field_meta; } } } } // The method, support fields only. $this->print_metabox( $fields ); /** * Elementor metabox render. * * Fires before custom scripts are enqueued, since enqueue depends on * render handlers. * * @param Custom_Code_Metabox $this An instance of custom code metabox. * @param int|false $id The ID of the current WordPress post. * False if post is not set. */ do_action( 'elementor-pro/metabox/render', $this, get_the_ID() ); // Init codemirror. $this->enqueue_code_editor_scripts( self::FIELD_CODE ); } private function save_post_meta( $post_id, $post ) { if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return $post_id; } // Check the user's permissions. if ( ! current_user_can( 'edit_post', $post_id ) ) { return $post_id; } if ( get_post_status( $post->ID ) === 'auto-draft' ) { return $post_id; } // PHPCS - Should not validate for nonce (already done in WordPress save_post). $post_data = $_POST; // phpcs:ignore WordPress.Security.NonceVerification.Missing foreach ( self::INPUT_FIELDS as $field ) { if ( isset( $post_data[ $field ] ) && ! Utils::is_empty( $post_data[ $field ] ) ) { if ( self::FIELD_CODE === $field ) { $post_meta = $post_data[ $field ]; } else { $post_meta = sanitize_text_field( $post_data[ $field ] ); } if ( ! current_user_can( 'unfiltered_html' ) ) { $post_meta = wp_kses_post( $post_meta ); } update_post_meta( $post->ID, "_elementor_$field", $post_meta ); /** @var \ElementorPro\Modules\ThemeBuilder\Module $theme_builder */ $theme_builder = Plugin::instance()->modules_manager->get_modules( 'theme-builder' ); $theme_builder->get_conditions_manager()->get_cache()->regenerate(); } elseif ( self::FILED_EXTRA_OPTIONS === $field ) { $input_options = []; foreach ( self::INPUT_OPTIONS as $input_option ) { $key = self::FILED_EXTRA_OPTIONS . '_' . $input_option; $input_option_value = \ElementorPro\Core\Utils::_unstable_get_super_global_value( $post_data, $key ); if ( 'on' === $input_option_value ) { $input_options [] = $input_option; } } update_post_meta( $post->ID, "_elementor_$field", $input_options ); } } // Temporary workaround for applying conditions for draft custom code post. if ( ! empty( $post_data['_conditions'] ) ) { $conditions = (array) json_decode( wp_unslash( $post_data['_conditions'] ) ); foreach ( $conditions as $key => $item ) { $item_assoc_array = (array) $item; $conditions[ $key ] = [ $item_assoc_array['type'], $item_assoc_array['name'], $item_assoc_array['sub'], $item_assoc_array['subId'], ]; } /** @var \ElementorPro\Modules\ThemeBuilder\Module $theme_builder */ $theme_builder = Plugin::instance()->modules_manager->get_modules( 'theme-builder' ); $theme_builder->get_conditions_manager()->save_conditions( $post_id, $conditions ); } } private function add_meta_boxes() { add_meta_box( 'elementor-custom-code', __( 'Custom code', 'elementor-pro' ), function() { $this->render_meta_box(); }, module::CPT, 'normal', 'default' ); } private function add_meta_publish_options( $post ) { if ( Module::CPT === $post->post_type ) { ?>