Files
wp-multisite-waas/inc/ui/class-base-element.php
2025-02-09 12:30:02 -07:00

1283 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-ultimo').
*
* @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-ultimo').
*
* @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']);
// Init should be the correct time to call this to avoid the deprecated notice from I18N.
// But it doesn't work for some reason, fix later.
// add_action('init', function () {
do_action('wu_element_loaded', $this);
// } );
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 The default value.
* @return mixed
*/
public function get_pre_loaded_attribute($name, $default = 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);
}
/**
* 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-ultimo') . '</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-ultimo'),
'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-ultimo'),
'desc' => __('Be careful. Hiding an element from the account page might remove important functionality from your customers\' reach.', 'wp-ultimo'),
'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-ultimo'),
'value' => 'edit',
'classes' => 'button',
'wrapper_classes' => 'wu-mb-0',
],
'submit' => [
'type' => 'submit',
'title' => __('Save Changes', 'wp-ultimo'),
'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);
wp_send_json_success(
[
'send' => [
'scope' => 'window',
'function_name' => 'wu_block_ui',
'data' => '#wpcontent',
],
'redirect_url' => add_query_arg('updated', 1, $_SERVER['HTTP_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__, __('An element can not be loaded as inline content unless the get_current_screen() function is already available.', 'wp-ultimo'), '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 ' . $control_classes . '">';
echo $this->display($atts);
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 ' . $control_classes . '">';
echo $this->display($atts);
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) {
// translators: %1$s is the URL to the customize modal. %2$s is the URL of the shortcode generation modal
$message = __('<a class="wubox wu-no-underline" title="Customize" href="%1$s">Customize this element</a>, or <a class="wubox wu-no-underline" title="Shortcode" href="%2$s">generate a shortcode</a> to use it on the front-end!', 'wp-ultimo');
$message .= wu_tooltip(__('You are seeing this because you are a super admin', 'wp-ultimo'));
$link_shortcode = wu_get_form_url("shortcode_{$this->id}");
$link_customize = wu_get_form_url("customize_{$this->id}");
$text = sprintf(
$message,
$link_customize,
$link_shortcode
);
$html = '
<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">
' . $text . '
</div>
</div>
</div>
';
echo $html;
}
}
/**
* 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;
}
}