id}_scripts", array($this, 'register_default_scripts')); add_action("wu_{$this->id}_scripts", array($this, 'register_scripts')); add_action('wp', array($this, 'maybe_setup')); add_action('admin_head', array($this, 'setup_for_admin'), 100); add_filter('pre_render_block', array($this, 'setup_for_block_editor'), 100, 2); add_action('wu_element_preview', array($this, 'setup_preview')); do_action('wu_element_loaded', $this); if ($this->public) { Base_Element::register_public_element($this); } // end if; } // end init; /** * Register a public element to further use. * * @since 2.0.24 * @param mixed $element The element instance to be registered. * @return void */ public static function register_public_element($element) { static::$public_elements[] = $element; } // end register_public_element; /** * Retrieves the public registered elements. * * @since 2.0.24 * @return array */ public static function get_public_elements() { return static::$public_elements; } // end get_public_elements; /** * Sets blocks up for the block editor. * * @since 2.0.0 * * @param null $short_circuit The value passed. * @param array $block The parsed block data. * @return null */ public function setup_for_block_editor($short_circuit, $block) { $should_load = false; if ($block['blockName'] === $this->get_id()) { $should_load = true; } // end if; /** * We might need to add additional blocks later. * * @since 2.0.0 * @return array */ $blocks_to_check = apply_filters('wu_element_block_types_to_check', array( 'core/shortcode', 'core/paragraph', )); if (in_array($block['blockName'], $blocks_to_check, true)) { if ($this->contains_current_element($block['innerHTML'])) { $should_load = true; } // end if; } // end if; if ($should_load) { if ($this->is_preview()) { $this->setup_preview(); } else { $this->setup(); } // end if; } // end if; return $short_circuit; } // end setup_for_block_editor; /** * Search for an element id on the list of metaboxes. * * Builds a cached list of elements on the first run. * Then uses the cache to run a simple in_array check. * * @since 2.0.0 * * @param string $element_id The element ID. * @return bool */ protected static function search_in_metaboxes($element_id) { global $wp_meta_boxes, $pagenow; /* * Bail if things don't look normal or in the right context. */ if (!function_exists('get_current_screen')) { return; } // end if; $screen = get_current_screen(); /* * First, check on cache, to avoid recalculating it time and time again. */ if (is_array(self::$metabox_cache)) { return in_array($element_id, self::$metabox_cache, true); } // end if; $contains_metaboxes = wu_get_isset($wp_meta_boxes, $screen->id) || wu_get_isset($wp_meta_boxes, $pagenow); $elements_to_cache = array(); $found = false; if (is_array($wp_meta_boxes) && $contains_metaboxes && is_array($wp_meta_boxes[$screen->id])) { foreach ($wp_meta_boxes[$screen->id] as $position => $priorities) { foreach ($priorities as $priority => $metaboxes) { foreach ($metaboxes as $metabox_id => $metabox) { $elements_to_cache[] = $metabox_id; if ($metabox_id === $element_id) { $found = true; } // end if; } // end foreach; } // end foreach; } // end foreach; /** * Set a local cache so we don't have to loop it all over again. */ self::$metabox_cache = $elements_to_cache; } // end if; return $found; } // end search_in_metaboxes; /** * Setup element on admin pages. * * @since 2.0.0 * @return void */ public function setup_for_admin() { if ($this->loaded === true) { return; } // end if; $element_id = "wp-ultimo-{$this->id}-element"; if (self::search_in_metaboxes($element_id)) { $this->loaded = true; $this->setup(); } // end if; } // end setup_for_admin; /** * Maybe run setup, when the shortcode or block is found. * * @todo check if this is working only when necessary. * @since 2.0.0 * @return void */ public function maybe_setup() { global $post; if (is_admin() || empty($post)) { return; } // end if; if ($this->contains_current_element($post->post_content, $post)) { if ($this->is_preview()) { $this->setup_preview(); } else { $this->setup(); } // end if; } // end if; } // end maybe_setup; /** * Runs early on the request lifecycle as soon as we detect the shortcode is present. * * @since 2.0.0 * @return void */ public function setup() {} // end setup; /** * Allows the setup in the context of previews. * * @since 2.0.0 * @return void */ public function setup_preview() {} // end setup_preview; /** * Checks content to see if the current element is present. * * This check uses different methods, covering classic shortcodes, * blocks. It also adds a generic filter so developers can * add additional tests for different builders and so on. * * @since 2.0.0 * * @param string $content The content that might contain the element. * @param null|\WP_Post $post The WP Post, if it exists. * @return bool */ protected function contains_current_element($content, $post = null) { /** * If parameters where pre-loaded, * we can skip the entire check and return true. */ if (is_array($this->pre_loaded_attributes)) { return true; } // end if; /* * First, check for default shortcodes * saved as regular post content. */ $shortcode = $this->get_shortcode_id(); if (has_shortcode($content, $shortcode)) { $this->pre_loaded_attributes = $this->maybe_extract_arguments($content, 'shortcode'); $this->actually_loaded = true; return true; } // end if; /* * Handle the Block Editor * and Gutenberg. */ $block = $this->get_id(); if (has_block($block, $content)) { $this->pre_loaded_attributes = $this->maybe_extract_arguments($content, 'block'); $this->actually_loaded = true; return true; } // end if; /* * Runs generic version so plugins can extend it. */ $this->pre_loaded_attributes = $this->maybe_extract_arguments($content, 'other'); $contains_element = false; /** * Last option is to check for the post force setting. */ if ($post && get_post_meta($post->ID, '_wu_force_elements_loading', true)) { $contains_element = true; } // end if; /** * Allow developers to change the results of the initial search. * * This is useful for third-party builders and such. * * @since 2.0.0 * @param bool $contains_elements If the element is contained on the content. * @param string $content The content being examined. * @param self The current element. */ return apply_filters('wu_contains_element', $contains_element, $content, $this, $post); } // end contains_current_element; /** * Tries to extract element arguments depending on the element type. * * @since 2.0.0 * * @param string $content The content to parse. * @param string $type The element type. Can be one of shortcode, block, and other. * @return false|array */ protected function maybe_extract_arguments($content, $type = 'shortcode') { if ($type === 'shortcode') { /** * Tries to parse the shortcode out of the content * passed using the WordPress shortcode regex. */ $shortcode_regex = get_shortcode_regex(array($this->get_shortcode_id())); preg_match_all('/' . $shortcode_regex . '/', $content, $matches, PREG_SET_ORDER); return !empty($matches) ? shortcode_parse_atts($matches[0][3]) : false; } elseif ($type === 'block') { /** * Next, try to parse attrs from blocks * by parsing them out and finding the correct one. */ $block_content = parse_blocks($content); foreach ($block_content as $block) { if ($block['blockName'] === $this->get_id()) { return $block['attrs']; } // end if; } // end foreach; return false; } // end if; /** * Adds generic filter to allow developers * to extend this parser to deal with additional * builders or plugins. * * @since 2.0.0 * @return false|array */ return apply_filters('wu_element_maybe_extract_arguments', false, $content, $type, $this); } // end maybe_extract_arguments; /** * Adds custom CSS to the signup screen. * * @since 2.0.0 * @return void */ public function enqueue_element_scripts() { global $post; if (!is_a($post, '\WP_Post')) { return; } // end if; $should_enqueue_scripts = apply_filters('wu_element_should_enqueue_scripts', false, $post, $this->get_shortcode_id()); if ($should_enqueue_scripts || $this->contains_current_element($post->post_content, $post)) { /** * Triggers the enqueue scripts hook. * * This is used by the element to hook its * register_scripts method. * * @since 2.0.0 */ do_action("wu_{$this->id}_scripts", $post, $this); } // end if; } // end enqueue_element_scripts; /** * Tries to parse the shortcode content on page load. * * This allow us to have access to parameters before the shortcode * gets actually parsed by the post content functions such as * the_content(). It is useful if you need to access that * date way earlier in the page lifecycle. * * @since 2.0.0 * * @param string $name The parameter name. * @param mixed $default The default value. * @return mixed */ public function get_pre_loaded_attribute($name, $default = false) { if ($this->pre_loaded_attributes === false || !is_array($this->pre_loaded_attributes)) { return false; } // end if; return wu_get_isset($this->pre_loaded_attributes, $name, $default); } // end get_pre_loaded_attribute; /** * Registers the shortcode. * * @since 2.0.0 * @return void */ public function register_shortcode() { if (wu_get_current_site()->get_type() === Site_Type::CUSTOMER_OWNED && is_admin() === false) { return; } // end if; add_shortcode($this->get_shortcode_id(), array($this, 'display')); } // end register_shortcode; /** * Registers the forms. * * @since 2.0.0 * @return void */ public function register_form() { /* * Add Generator Forms */ wu_register_form("shortcode_{$this->id}", array( 'render' => array($this, 'render_generator_modal'), 'handler' => '__return_empty_string', 'capability' => 'manage_network', )); /* * Add Customize Forms */ wu_register_form("customize_{$this->id}", array( 'render' => array($this, 'render_customize_modal'), 'handler' => array($this, 'handle_customize_modal'), 'capability' => 'manage_network', )); } // end register_form; /** * Adds the modal to copy the shortcode for this particular element. * * @since 2.0.0 * @return void */ public function render_generator_modal() { $fields = $this->fields(); $defaults = $this->defaults(); $state = array(); foreach ($fields as $field_slug => &$field) { if ($field['type'] === 'header' || $field['type'] === 'note') { unset($fields[$field_slug]); continue; } // end if; /* * Additional State. * * We need to keep track of the state * specially when we're dealing with * complex fields, such as group. */ $additional_state = array(); if ($field['type'] === 'group') { foreach ($field['fields'] as $sub_field_slug => &$sub_field) { $sub_field['html_attr'] = array( 'v-model.lazy' => "attributes.{$sub_field_slug}", ); $additional_state[$sub_field_slug] = wu_request($sub_field_slug, wu_get_isset($defaults, $sub_field_slug)); } // end foreach; continue; } // end if; /* * Set v-model */ $field['html_attr'] = array( 'v-model.lazy' => "attributes.{$field_slug}", ); $required = wu_get_isset($field, 'required'); if (wu_get_isset($field, 'required')) { $shows = array(); foreach ($required as $key => $value) { $value = is_string($value) ? "\"$value\"" : $value; $shows[] = "attributes.{$key} == $value"; } // end foreach; $field['wrapper_html_attr'] = array( 'v-show' => implode(' && ', $shows), ); $state[$field_slug . '_shortcode_requires'] = $required; } // end if; $state[$field_slug] = wu_request($field_slug, wu_get_isset($defaults, $field_slug)); } // end foreach; $fields['shortcode_result'] = array( 'type' => 'note', 'wrapper_classes' => 'sm:wu-block', 'desc' => '