Escape everything that should be escaped. Add nonce checks where needed. Sanitize all inputs. Apply Code style changes across the codebase. Correct many deprecation notices. Optimize load order of many filters.
1278 lines
29 KiB
PHP
1278 lines
29 KiB
PHP
<?php
|
|
/**
|
|
* Base class to UI elements that are rendered on the backend and the frontend.
|
|
*
|
|
* @package WP_Ultimo\UI
|
|
* @subpackage Base_Element
|
|
* @since 2.0.0
|
|
*/
|
|
|
|
namespace WP_Ultimo\UI;
|
|
|
|
use WP_Ultimo\Database\Sites\Site_Type;
|
|
|
|
// Exit if accessed directly
|
|
defined('ABSPATH') || exit;
|
|
|
|
/**
|
|
* Base class to UI elements that are rendered on the backend and the frontend.
|
|
*
|
|
* @since 2.0.0
|
|
*/
|
|
abstract class Base_Element {
|
|
|
|
/**
|
|
* The id of the element.
|
|
*
|
|
* Something simple, without prefixes, like 'checkout', or 'pricing-tables'.
|
|
*
|
|
* This is used to construct shortcodes by prefixing the id with 'wu_'
|
|
* e.g. an id checkout becomes the shortcode 'wu_checkout' and
|
|
* to generate the Gutenberg block by prefixing it with 'wp-ultimo/'
|
|
* e.g. checkout would become the block 'wp-ultimo/checkout'.
|
|
*
|
|
* @since 2.0.0
|
|
* @var string
|
|
*/
|
|
protected $id;
|
|
|
|
/**
|
|
* Should this element be hidden by default?
|
|
*
|
|
* @since 2.0.0
|
|
* @var boolean
|
|
*/
|
|
protected $hidden_by_default = false;
|
|
|
|
/**
|
|
* Controls whether or not the widget and element should display.
|
|
*
|
|
* @since 2.0.0
|
|
* @var boolean
|
|
*/
|
|
protected $display = true;
|
|
|
|
/**
|
|
* Controls if this is a public element to be used in pages/shortcodes by user.
|
|
*
|
|
* @since 2.0.24
|
|
* @var boolean
|
|
*/
|
|
protected $public = false;
|
|
|
|
/**
|
|
* Keep an array with registered public elements.
|
|
*
|
|
* @since 2.0.24
|
|
* @var boolean
|
|
*/
|
|
protected static $public_elements = [];
|
|
|
|
/**
|
|
* If the element exists, we pre-load the parameters.
|
|
*
|
|
* @since 2.0.0
|
|
* @var false|array
|
|
*/
|
|
protected $pre_loaded_attributes = false;
|
|
|
|
/**
|
|
* Only load (run the setup method) once,
|
|
*
|
|
* This is specially true when in the admin context,
|
|
*
|
|
* @since 2.0.0
|
|
* @var boolean
|
|
*/
|
|
protected $loaded = false;
|
|
|
|
/**
|
|
* Keeps a cached list of metabox ids to shave some time.
|
|
*
|
|
* @since 2.0.0
|
|
* @var array
|
|
*/
|
|
protected static $metabox_cache = null;
|
|
|
|
/**
|
|
* Sometimes we need to know if the element was actually loaded
|
|
* - meaning found on a page - even if tall elements are forcefully
|
|
* setup.
|
|
*
|
|
* @since 2.0.11
|
|
* @var boolean
|
|
*/
|
|
protected $actually_loaded = false;
|
|
|
|
/**
|
|
* The icon of the UI element.
|
|
*
|
|
* E.g. return fa fa-search.
|
|
*
|
|
* @since 2.0.0
|
|
* @param string $context One of the values: block, elementor or bb.
|
|
* @return string
|
|
*/
|
|
abstract public function get_icon($context = 'block');
|
|
|
|
/**
|
|
* The title of the UI element.
|
|
*
|
|
* This is used on the Blocks list of Gutenberg.
|
|
* You should return a string with the localized title.
|
|
* e.g. return __('My Element', 'wp-multisite-waas').
|
|
*
|
|
* @since 2.0.0
|
|
* @return string
|
|
*/
|
|
abstract public function get_title();
|
|
|
|
/**
|
|
* The description of the UI element.
|
|
*
|
|
* This is also used on the Gutenberg block list
|
|
* to explain what this block is about.
|
|
* You should return a string with the localized title.
|
|
* e.g. return __('Adds a checkout form to the page', 'wp-multisite-waas').
|
|
*
|
|
* @since 2.0.0
|
|
* @return string
|
|
*/
|
|
abstract public function get_description();
|
|
|
|
/**
|
|
* The list of fields to be added to Gutenberg.
|
|
*
|
|
* If you plan to add Gutenberg controls to this block,
|
|
* you'll need to return an array of fields, following
|
|
* our fields interface (@see inc/ui/class-field.php).
|
|
*
|
|
* You can create new Gutenberg panels by adding fields
|
|
* with the type 'header'. See the Checkout Elements for reference.
|
|
*
|
|
* @see inc/ui/class-checkout-element.php
|
|
*
|
|
* Return an empty array if you don't have controls to add.
|
|
*
|
|
* @since 2.0.0
|
|
* @return array
|
|
*/
|
|
abstract public function fields();
|
|
|
|
/**
|
|
* The list of keywords for this element.
|
|
*
|
|
* Return an array of strings with keywords describing this
|
|
* element. Gutenberg uses this to help customers find blocks.
|
|
*
|
|
* e.g.:
|
|
* return array(
|
|
* 'WP Multisite WaaS',
|
|
* 'Checkout',
|
|
* 'Form',
|
|
* 'Cart',
|
|
* );
|
|
*
|
|
* @since 2.0.0
|
|
* @return array
|
|
*/
|
|
abstract public function keywords();
|
|
|
|
/**
|
|
* List of default parameters for the element.
|
|
*
|
|
* If you are planning to add controls using the fields,
|
|
* it might be a good idea to use this method to set defaults
|
|
* for the parameters you are expecting.
|
|
*
|
|
* These defaults will be used inside a 'wp_parse_args' call
|
|
* before passing the parameters down to the block render
|
|
* function and the shortcode render function.
|
|
*
|
|
* @since 2.0.0
|
|
* @return array
|
|
*/
|
|
abstract public function defaults();
|
|
|
|
/**
|
|
* The content to be output on the screen.
|
|
*
|
|
* Should return HTML markup to be used to display the block.
|
|
* This method is shared between the block render method and
|
|
* the shortcode implementation.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param array $atts Parameters of the block/shortcode.
|
|
* @param string|null $content The content inside the shortcode.
|
|
* @return string
|
|
*/
|
|
abstract public function output($atts, $content = null);
|
|
|
|
// Boilerplate -----------------------------------
|
|
|
|
/**
|
|
* Initializes the singleton.
|
|
*
|
|
* @since 2.0.0
|
|
* @return void
|
|
*/
|
|
public function init(): void {
|
|
|
|
add_action('plugins_loaded', [$this, 'register_form']);
|
|
|
|
add_action('init', [$this, 'register_shortcode']);
|
|
|
|
add_action('wp_enqueue_scripts', [$this, 'enqueue_element_scripts']);
|
|
|
|
add_action("wu_{$this->id}_scripts", [$this, 'register_default_scripts']);
|
|
|
|
add_action("wu_{$this->id}_scripts", [$this, 'register_scripts']);
|
|
|
|
add_action('wp', [$this, 'maybe_setup']);
|
|
|
|
add_action('admin_head', [$this, 'setup_for_admin'], 100);
|
|
|
|
add_filter('pre_render_block', [$this, 'setup_for_block_editor'], 100, 2);
|
|
|
|
add_action('wu_element_preview', [$this, 'setup_preview']);
|
|
|
|
add_action(
|
|
'init',
|
|
function () {
|
|
do_action('wu_element_loaded', $this);
|
|
},
|
|
5,
|
|
0
|
|
);
|
|
|
|
if ($this->public) {
|
|
self::register_public_element($this);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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): void {
|
|
|
|
static::$public_elements[] = $element;
|
|
}
|
|
|
|
/**
|
|
* Retrieves the public registered elements.
|
|
*
|
|
* @since 2.0.24
|
|
* @return array
|
|
*/
|
|
public static function get_public_elements() {
|
|
|
|
return static::$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 ($this->get_id() === $block['blockName']) {
|
|
$should_load = true;
|
|
}
|
|
|
|
/**
|
|
* 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',
|
|
[
|
|
'core/shortcode',
|
|
'core/paragraph',
|
|
]
|
|
);
|
|
|
|
if (in_array($block['blockName'], $blocks_to_check, true)) {
|
|
if ($this->contains_current_element($block['innerHTML'])) {
|
|
$should_load = true;
|
|
}
|
|
}
|
|
|
|
if ($should_load) {
|
|
if ($this->is_preview()) {
|
|
$this->setup_preview();
|
|
} else {
|
|
$this->setup();
|
|
}
|
|
}
|
|
|
|
return $short_circuit;
|
|
}
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
$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);
|
|
}
|
|
|
|
$contains_metaboxes = wu_get_isset($wp_meta_boxes, $screen->id) || wu_get_isset($wp_meta_boxes, $pagenow);
|
|
|
|
$elements_to_cache = [];
|
|
|
|
$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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set a local cache so we don't have to loop it all over again.
|
|
*/
|
|
self::$metabox_cache = $elements_to_cache;
|
|
}
|
|
|
|
return $found;
|
|
}
|
|
|
|
/**
|
|
* Setup element on admin pages.
|
|
*
|
|
* @since 2.0.0
|
|
* @return void
|
|
*/
|
|
public function setup_for_admin(): void {
|
|
|
|
if (true === $this->loaded) {
|
|
return;
|
|
}
|
|
|
|
$element_id = "wp-ultimo-{$this->id}-element";
|
|
|
|
if (self::search_in_metaboxes($element_id)) {
|
|
$this->loaded = true;
|
|
|
|
$this->setup();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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(): void {
|
|
|
|
global $post;
|
|
|
|
if (is_admin() || empty($post)) {
|
|
return;
|
|
}
|
|
|
|
if ($this->contains_current_element($post->post_content, $post)) {
|
|
if ($this->is_preview()) {
|
|
$this->setup_preview();
|
|
} else {
|
|
$this->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() {}
|
|
|
|
/**
|
|
* Allows the setup in the context of previews.
|
|
*
|
|
* @since 2.0.0
|
|
* @return void
|
|
*/
|
|
public function 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;
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
|
|
/**
|
|
* 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 ('shortcode' === $type) {
|
|
|
|
/**
|
|
* Tries to parse the shortcode out of the content
|
|
* passed using the WordPress shortcode regex.
|
|
*/
|
|
$shortcode_regex = get_shortcode_regex([$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 ('block' === $type) {
|
|
|
|
/**
|
|
* 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 ($this->get_id() === $block['blockName']) {
|
|
return $block['attrs'];
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
|
|
/**
|
|
* Adds custom CSS to the signup screen.
|
|
*
|
|
* @since 2.0.0
|
|
* @return void
|
|
*/
|
|
public function enqueue_element_scripts(): void {
|
|
|
|
global $post;
|
|
|
|
if ( ! is_a($post, '\WP_Post')) {
|
|
return;
|
|
}
|
|
|
|
$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);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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_value The default value.
|
|
*
|
|
* @return mixed
|
|
*/
|
|
public function get_pre_loaded_attribute($name, $default_value = false) {
|
|
|
|
if (false === $this->pre_loaded_attributes || ! is_array($this->pre_loaded_attributes)) {
|
|
return false;
|
|
}
|
|
|
|
return wu_get_isset($this->pre_loaded_attributes, $name, $default_value);
|
|
}
|
|
|
|
/**
|
|
* Registers the shortcode.
|
|
*
|
|
* @since 2.0.0
|
|
* @return void
|
|
*/
|
|
public function register_shortcode(): void {
|
|
|
|
if (wu_get_current_site()->get_type() === Site_Type::CUSTOMER_OWNED && is_admin() === false) {
|
|
return;
|
|
}
|
|
|
|
add_shortcode($this->get_shortcode_id(), [$this, 'display']);
|
|
}
|
|
|
|
/**
|
|
* Registers the forms.
|
|
*
|
|
* @since 2.0.0
|
|
* @return void
|
|
*/
|
|
public function register_form(): void {
|
|
/*
|
|
* Add Generator Forms
|
|
*/
|
|
wu_register_form(
|
|
"shortcode_{$this->id}",
|
|
[
|
|
'render' => [$this, 'render_generator_modal'],
|
|
'handler' => '__return_empty_string',
|
|
'capability' => 'manage_network',
|
|
]
|
|
);
|
|
|
|
/*
|
|
* Add Customize Forms
|
|
*/
|
|
wu_register_form(
|
|
"customize_{$this->id}",
|
|
[
|
|
'render' => [$this, 'render_customize_modal'],
|
|
'handler' => [$this, 'handle_customize_modal'],
|
|
'capability' => 'manage_network',
|
|
]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Adds the modal to copy the shortcode for this particular element.
|
|
*
|
|
* @since 2.0.0
|
|
* @return void
|
|
*/
|
|
public function render_generator_modal(): void {
|
|
|
|
$fields = $this->fields();
|
|
|
|
$defaults = $this->defaults();
|
|
|
|
$state = [];
|
|
|
|
foreach ($fields as $field_slug => &$field) {
|
|
if ('header' === $field['type'] || 'note' === $field['type']) {
|
|
unset($fields[ $field_slug ]);
|
|
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Additional State.
|
|
*
|
|
* We need to keep track of the state
|
|
* specially when we're dealing with
|
|
* complex fields, such as group.
|
|
*/
|
|
$additional_state = [];
|
|
|
|
if ('group' === $field['type']) {
|
|
foreach ($field['fields'] as $sub_field_slug => &$sub_field) {
|
|
$sub_field['html_attr'] = [
|
|
'v-model.lazy' => "attributes.{$sub_field_slug}",
|
|
];
|
|
|
|
$additional_state[ $sub_field_slug ] = wu_request($sub_field_slug, wu_get_isset($defaults, $sub_field_slug));
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Set v-model
|
|
*/
|
|
$field['html_attr'] = [
|
|
'v-model.lazy' => "attributes.{$field_slug}",
|
|
];
|
|
|
|
$required = wu_get_isset($field, 'required');
|
|
|
|
if (wu_get_isset($field, 'required')) {
|
|
$shows = [];
|
|
|
|
foreach ($required as $key => $value) {
|
|
$value = is_string($value) ? "\"$value\"" : $value;
|
|
|
|
$shows[] = "attributes.{$key} == $value";
|
|
}
|
|
|
|
$field['wrapper_html_attr'] = [
|
|
'v-show' => implode(' && ', $shows),
|
|
];
|
|
|
|
$state[ $field_slug . '_shortcode_requires' ] = $required;
|
|
}
|
|
|
|
$state[ $field_slug ] = wu_request($field_slug, wu_get_isset($defaults, $field_slug));
|
|
}
|
|
|
|
$fields['shortcode_result'] = [
|
|
'type' => 'note',
|
|
'wrapper_classes' => 'sm:wu-block',
|
|
'desc' => '<div class="wu-w-full"><span class="wu-my-1 wu-text-2xs wu-uppercase wu-font-bold wu-block">' . __('Result', 'wp-multisite-waas') . '</span><pre v-html="shortcode" id="wu-shortcode" style="overflow-x: scroll !important;" class="wu-text-center wu-p-4 wu-m-0 wu-mt-2 wu-rounded wu-content-center wu-bg-gray-800 wu-text-white wu-font-mono wu-border wu-border-solid wu-border-gray-300 wu-max-h-screen wu-overflow-x-scroll"></pre></div>',
|
|
];
|
|
|
|
$fields['submit_copy'] = [
|
|
'type' => 'submit',
|
|
'title' => __('Copy Shortcode', 'wp-multisite-waas'),
|
|
'value' => 'edit',
|
|
'classes' => 'button button-primary wu-w-full wu-copy',
|
|
'wrapper_classes' => 'wu-items-end',
|
|
'html_attr' => [
|
|
'data-clipboard-action' => 'copy',
|
|
'data-clipboard-target' => '#wu-shortcode',
|
|
],
|
|
];
|
|
|
|
$form = new \WP_Ultimo\UI\Form(
|
|
$this->id,
|
|
$fields,
|
|
[
|
|
'views' => 'admin-pages/fields',
|
|
'classes' => 'wu-modal-form wu-widget-list wu-striped wu-m-0 wu-mt-0 wu-w-full',
|
|
'field_wrapper_classes' => 'wu-w-full wu-box-border wu-items-center wu-flex wu-justify-between wu-p-4 wu-m-0 wu-border-t wu-border-l-0 wu-border-r-0 wu-border-b-0 wu-border-gray-300 wu-border-solid',
|
|
'html_attr' => [
|
|
'data-wu-app' => "{$this->id}_generator",
|
|
'data-state' => wu_convert_to_state(
|
|
[
|
|
'id' => $this->get_shortcode_id(),
|
|
'defaults' => $defaults,
|
|
'attributes' => $state,
|
|
]
|
|
),
|
|
],
|
|
]
|
|
);
|
|
|
|
echo '<div class="wu-styling">';
|
|
|
|
$form->render();
|
|
|
|
echo '</div>';
|
|
}
|
|
|
|
/**
|
|
* Adds the modal customize the widget block
|
|
*
|
|
* @since 2.0.0
|
|
* @return void
|
|
*/
|
|
public function render_customize_modal(): void {
|
|
|
|
$fields = [];
|
|
|
|
$fields['hide'] = [
|
|
'type' => 'toggle',
|
|
'title' => __('Hide Element', 'wp-multisite-waas'),
|
|
'desc' => __('Be careful. Hiding an element from the account page might remove important functionality from your customers\' reach.', 'wp-multisite-waas'),
|
|
'value' => $this->hidden_by_default,
|
|
'classes' => 'button button-primary wu-w-full',
|
|
'wrapper_classes' => 'wu-items-end',
|
|
];
|
|
|
|
$fields = array_merge($fields, $this->fields());
|
|
|
|
$saved_settings = $this->get_widget_settings();
|
|
|
|
$defaults = $this->defaults();
|
|
|
|
$state = array_merge($defaults, $saved_settings);
|
|
|
|
foreach ($fields as $field_slug => &$field) {
|
|
if ('header' === $field['type']) {
|
|
unset($fields[ $field_slug ]);
|
|
|
|
continue;
|
|
}
|
|
|
|
$value = wu_get_isset($saved_settings, $field_slug, null);
|
|
|
|
if (null !== $value) {
|
|
$field['value'] = $value;
|
|
}
|
|
}
|
|
|
|
$fields['save_line'] = [
|
|
'type' => 'group',
|
|
'classes' => 'wu-justify-between',
|
|
'wrapper_classes' => 'wu-bg-gray-100',
|
|
'fields' => [
|
|
'restore' => [
|
|
'type' => 'submit',
|
|
'title' => __('Reset Settings', 'wp-multisite-waas'),
|
|
'value' => 'edit',
|
|
'classes' => 'button',
|
|
'wrapper_classes' => 'wu-mb-0',
|
|
],
|
|
'submit' => [
|
|
'type' => 'submit',
|
|
'title' => __('Save Changes', 'wp-multisite-waas'),
|
|
'value' => 'edit',
|
|
'classes' => 'button button-primary',
|
|
'wrapper_classes' => 'wu-mb-0',
|
|
],
|
|
],
|
|
];
|
|
|
|
$form = new \WP_Ultimo\UI\Form(
|
|
$this->id,
|
|
$fields,
|
|
[
|
|
'views' => 'admin-pages/fields',
|
|
'classes' => 'wu-modal-form wu-widget-list wu-striped wu-m-0 wu-mt-0',
|
|
'field_wrapper_classes' => 'wu-w-full wu-box-border wu-items-center wu-flex wu-justify-between wu-p-4 wu-m-0 wu-border-t wu-border-l-0 wu-border-r-0 wu-border-b-0 wu-border-gray-300 wu-border-solid',
|
|
'html_attr' => [
|
|
'data-wu-app' => "{$this->id}_customize",
|
|
'data-state' => wu_convert_to_state($state),
|
|
],
|
|
]
|
|
);
|
|
|
|
echo '<div class="wu-styling">';
|
|
|
|
$form->render();
|
|
|
|
echo '</div>';
|
|
}
|
|
|
|
/**
|
|
* Saves the customization settings for a given widget.
|
|
*
|
|
* @since 2.0.0
|
|
* @return void
|
|
*/
|
|
public function handle_customize_modal(): void {
|
|
|
|
$settings = [];
|
|
|
|
if (wu_request('submit') !== 'restore') {
|
|
$fields = $this->fields();
|
|
|
|
$fields['hide'] = [
|
|
'type' => 'toggle',
|
|
];
|
|
|
|
foreach ($fields as $field_slug => $field) {
|
|
$setting = wu_request($field_slug, false);
|
|
|
|
if (false !== $setting || 'toggle' === $field['type']) {
|
|
$settings[ $field_slug ] = $setting;
|
|
}
|
|
}
|
|
}
|
|
|
|
$this->save_widget_settings($settings);
|
|
$referer = isset($_SERVER['HTTP_REFERER']) ? sanitize_url(wp_unslash($_SERVER['HTTP_REFERER'])) : '';
|
|
|
|
wp_send_json_success(
|
|
[
|
|
'send' => [
|
|
'scope' => 'window',
|
|
'function_name' => 'wu_block_ui',
|
|
'data' => '#wpcontent',
|
|
],
|
|
'redirect_url' => add_query_arg('updated', 1, $referer),
|
|
]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Registers scripts and styles necessary to render this.
|
|
*
|
|
* @since 2.0.0
|
|
* @return void
|
|
*/
|
|
public function register_default_scripts(): void {
|
|
|
|
wp_enqueue_style('wu-admin');
|
|
}
|
|
|
|
/**
|
|
* Registers scripts and styles necessary to render this.
|
|
*
|
|
* @since 2.0.0
|
|
* @return void
|
|
*/
|
|
public function register_scripts() {}
|
|
|
|
/**
|
|
* Loads dependencies that might not be available at render time.
|
|
*
|
|
* @since 2.0.0
|
|
* @return void
|
|
*/
|
|
public function dependencies() {}
|
|
|
|
/**
|
|
* Returns the ID of this UI element.
|
|
*
|
|
* @since 2.0.0
|
|
* @return string
|
|
*/
|
|
public function get_id() {
|
|
|
|
return sprintf('wp-ultimo/%s', $this->id);
|
|
}
|
|
|
|
/**
|
|
* Returns the ID of this UI element.
|
|
*
|
|
* @since 2.0.0
|
|
* @return string
|
|
*/
|
|
public function get_shortcode_id() {
|
|
|
|
return str_replace('-', '_', sprintf('wu_%s', $this->id));
|
|
}
|
|
|
|
/**
|
|
* Treats the attributes before passing them down to the output method.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param array $atts The element attributes.
|
|
* @return string
|
|
*/
|
|
public function display($atts) {
|
|
|
|
if ( ! $this->should_display()) {
|
|
return; // bail if the display was set to false.
|
|
}
|
|
|
|
$this->dependencies();
|
|
|
|
$atts = wp_parse_args($atts, $this->defaults());
|
|
|
|
/*
|
|
* Account for the 'className' Gutenberg attribute.
|
|
*/
|
|
$atts['className'] = trim('wu-' . $this->id . ' wu-element ' . wu_get_isset($atts, 'className', ''));
|
|
|
|
/*
|
|
* Pass down the element so we can use helpers.
|
|
*/
|
|
$atts['element'] = $this;
|
|
|
|
return call_user_func([$this, 'output'], $atts);
|
|
}
|
|
|
|
/**
|
|
* Retrieves a cleaned up version of the content.
|
|
*
|
|
* This method strips out vue reactivity tags and more.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param array $atts The element attributes.
|
|
* @return string
|
|
*/
|
|
public function display_template($atts) {
|
|
|
|
$content = $this->display($atts);
|
|
|
|
$content = str_replace(
|
|
[
|
|
'v-',
|
|
'data-wu',
|
|
'data-state',
|
|
],
|
|
'inactive-',
|
|
$content
|
|
);
|
|
|
|
$content = str_replace(
|
|
[
|
|
'{{',
|
|
'}}',
|
|
],
|
|
'',
|
|
$content
|
|
);
|
|
|
|
return $content;
|
|
}
|
|
|
|
/**
|
|
* Checks if we need to display admin management attachments.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function should_display_customize_controls() {
|
|
|
|
return apply_filters('wu_element_display_super_admin_notice', current_user_can('manage_network'), $this);
|
|
}
|
|
|
|
/**
|
|
* Adds the element as a inline block, without the admin widget frame.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param string $screen_id The screen id.
|
|
* @param string $hook The hook to add the content to. Defaults to admin_notices.
|
|
* @param array $atts Array containing the shortcode attributes.
|
|
* @return void
|
|
*/
|
|
public function as_inline_content($screen_id, $hook = 'admin_notices', $atts = []): void {
|
|
|
|
if ( ! function_exists('get_current_screen')) {
|
|
_doing_it_wrong(__METHOD__, esc_html__('An element can not be loaded as inline content unless the get_current_screen() function is already available.', 'wp-multisite-waas'), '2.0.0');
|
|
|
|
return;
|
|
}
|
|
|
|
$screen = get_current_screen();
|
|
|
|
if ( ! $screen || $screen->id !== $screen_id) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Run the setup in this case;
|
|
*/
|
|
$this->setup();
|
|
|
|
if ( ! $this->should_display()) {
|
|
return; // bail if the display was set to false.
|
|
|
|
}
|
|
|
|
if (empty($atts)) {
|
|
$atts = $this->get_widget_settings();
|
|
}
|
|
|
|
$control_classes = '';
|
|
|
|
if (wu_get_isset($atts, 'hide', $this->hidden_by_default)) {
|
|
if ( ! $this->should_display_customize_controls()) {
|
|
return;
|
|
}
|
|
|
|
$control_classes = 'wu-customize-mode wu-opacity-25';
|
|
}
|
|
|
|
add_action(
|
|
$hook,
|
|
function () use ($atts, $control_classes) {
|
|
|
|
echo '<div class="wu-inline-widget">';
|
|
|
|
echo '<div class="wu-inline-widget-body ' . esc_attr($control_classes) . '">';
|
|
|
|
echo $this->display($atts); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
|
|
|
echo '</div>';
|
|
|
|
$this->super_admin_notice();
|
|
|
|
echo '</div>';
|
|
}
|
|
);
|
|
|
|
do_action("wu_{$this->id}_scripts", null, $this);
|
|
}
|
|
|
|
/**
|
|
* Save the widget options.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param array $settings The settings to save. Key => value array.
|
|
* @return void
|
|
*/
|
|
public function save_widget_settings($settings): void {
|
|
|
|
$key = wu_replace_dashes($this->id);
|
|
|
|
wu_save_setting("widget_{$key}_settings", $settings);
|
|
}
|
|
|
|
/**
|
|
* Retrieves the settings for a particular widget.
|
|
*
|
|
* @since 2.0.0
|
|
* @return array
|
|
*/
|
|
public function get_widget_settings() {
|
|
|
|
$key = wu_replace_dashes($this->id);
|
|
|
|
return wu_get_setting("widget_{$key}_settings", []);
|
|
}
|
|
|
|
/**
|
|
* Adds the element as a metabox.
|
|
*
|
|
* @since 2.0.0
|
|
*
|
|
* @param string $screen_id The screen id.
|
|
* @param string $position Position on the screen.
|
|
* @param array $atts Array containing the shortcode attributes.
|
|
* @return void
|
|
*/
|
|
public function as_metabox($screen_id, $position = 'normal', $atts = []): void {
|
|
|
|
$this->setup();
|
|
|
|
if ( ! $this->should_display()) {
|
|
return; // bail if the display was set to false.
|
|
|
|
}
|
|
|
|
if (empty($atts)) {
|
|
$atts = $this->get_widget_settings();
|
|
}
|
|
|
|
$control_classes = '';
|
|
|
|
if (wu_get_isset($atts, 'hide')) {
|
|
if ( ! $this->should_display_customize_controls()) {
|
|
return;
|
|
}
|
|
|
|
$control_classes = 'wu-customize-mode wu-opacity-25';
|
|
}
|
|
|
|
add_meta_box(
|
|
"wp-ultimo-{$this->id}-element",
|
|
$this->get_title(),
|
|
function () use ($atts, $control_classes) {
|
|
|
|
echo '<div class="wu-metabox-widget ' . esc_attr($control_classes) . '">';
|
|
|
|
echo $this->display($atts); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
|
|
|
|
echo '</div>';
|
|
|
|
$this->super_admin_notice();
|
|
},
|
|
$screen_id,
|
|
$position,
|
|
'high'
|
|
);
|
|
|
|
do_action("wu_{$this->id}_scripts", null, $this);
|
|
}
|
|
|
|
/**
|
|
* Adds note for super admins.
|
|
*
|
|
* Adds an admin notice to let the super admin know
|
|
* how to use the widgets.
|
|
*
|
|
* @since 2.0.0
|
|
* @return void
|
|
*/
|
|
public function super_admin_notice(): void {
|
|
|
|
$should_display = $this->should_display_customize_controls();
|
|
|
|
if ($should_display) {
|
|
?>
|
|
<div class="wu-styling">
|
|
<div class="wu-widget-inset">
|
|
<div class="wubox wu-no-underline wu-p-4 wu-bg-gray-200 wu-block wu-mt-4 wu-text-center wu-text-sm wu-text-gray-600 wu-m-auto wu-border-solid wu-border-0 wu-border-t wu-border-gray-400">
|
|
<a class="wubox wu-no-underline" title="Customize" href="<?php echo esc_attr(wu_get_form_url("shortcode_{$this->id}")); ?>">
|
|
<?php esc_html_e('Customize this element', 'wp-multisite-waas'); ?>
|
|
</a>
|
|
<?php esc_html_e(', or'); ?>
|
|
<a class="wubox wu-no-underline" title="Shortcode" href="<?php echo esc_attr(wu_get_form_url("customize_{$this->id}")); ?>">
|
|
<?php esc_html_e('generate a shortcode', 'wp-multisite-waas'); ?>
|
|
</a>
|
|
<?php esc_html_e('to use it on the front-end!', 'wp-multisite-waas'); ?>
|
|
<?php echo wu_tooltip(__('You are seeing this because you are a super admin', 'wp-multisite-waas')); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<?php
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if we are in a preview context.
|
|
*
|
|
* @since 2.0.0
|
|
* @return boolean
|
|
*/
|
|
public function is_preview() {
|
|
|
|
$is_preview = false;
|
|
|
|
if (did_action('init')) {
|
|
$is_preview = wu_request('preview') && current_user_can('edit_posts');
|
|
}
|
|
|
|
return apply_filters('wu_element_is_preview', false, $this);
|
|
}
|
|
|
|
/**
|
|
* Get controls whether or not the widget and element should display..
|
|
*
|
|
* @since 2.0.0
|
|
* @return boolean
|
|
*/
|
|
public function should_display() {
|
|
|
|
return $this->display || $this->is_preview();
|
|
}
|
|
|
|
/**
|
|
* Set controls whether or not the widget and element should display..
|
|
*
|
|
* @since 2.0.0
|
|
* @param boolean $display Controls whether or not the widget and element should display.
|
|
* @return void
|
|
*/
|
|
public function set_display($display): void {
|
|
|
|
$this->display = $display;
|
|
}
|
|
|
|
/**
|
|
* Checks if the current element was actually loaded.
|
|
*
|
|
* @since 2.0.11
|
|
* @return boolean
|
|
*/
|
|
public function is_actually_loaded() {
|
|
|
|
return $this->actually_loaded;
|
|
}
|
|
}
|