| @@ -2,9 +2,9 @@ | ||
| 2 | 2 | /** |
| 3 | 3 | * @package VikWP - Libraries |
| 4 | 4 | * @subpackage adapter.module |
| 5 | 5 | * @author E4J s.r.l. |
| 6 | - * @copyright Copyright (C) 2021 E4J s.r.l. All Rights Reserved. | |
| 6 | + * @copyright Copyright (C) 2023 E4J s.r.l. All Rights Reserved. | |
| 7 | 7 | * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL |
| 8 | 8 | * @link https://vikwp.com |
| 9 | 9 | */ |
| 10 | 10 | |
| @@ -15,10 +15,10 @@ | ||
| 15 | 15 | |
| 16 | 16 | /** |
| 17 | 17 | * Adapter class to extend WP widget functionalities. |
| 18 | 18 | * |
| 19 | - * @see WP_Widget | |
| 20 | - * @since 10.0 | |
| 19 | + * @see WP_Widget | |
| 20 | + * @since 10.0 | |
| 21 | 21 | */ |
| 22 | 22 | class JWidget extends WP_Widget |
| 23 | 23 | { |
| 24 | 24 | /** |
| @@ -57,8 +57,18 @@ | ||
| 57 | 57 | */ |
| 58 | 58 | protected $registered = false; |
| 59 | 59 | |
| 60 | 60 | /** |
| 61 | + * An incremental counter to make sure the used ID is always unique, | |
| 62 | + * since Gutenberg seems to always use the number ID for the widgets | |
| 63 | + * published under the same page. | |
| 64 | + * | |
| 65 | + * @var int | |
| 66 | + * @since 10.1.59 | |
| 67 | + */ | |
| 68 | + protected static $incrementalCounter = 0; | |
| 69 | + | |
| 70 | + /** | |
| 61 | 71 | * Class constructor. |
| 62 | 72 | * |
| 63 | 73 | * @param string $path The widget absolute path. |
| 64 | 74 | * |
| @@ -119,11 +129,10 @@ | ||
| 119 | 129 | * @since 10.1.22 |
| 120 | 130 | */ |
| 121 | 131 | if (!headers_sent()) |
| 122 | 132 | { |
| 123 | - add_filter('wp_enqueue_scripts', function() | |
| 124 | - { | |
| 125 | - wp_enqueue_script('jquery', null, array(), false, false); | |
| 133 | + add_filter('wp_enqueue_scripts', function() { | |
| 134 | + wp_enqueue_script('jquery', null, [], false, false); | |
| 126 | 135 | }); |
| 127 | 136 | } |
| 128 | 137 | } |
| 129 | 138 | |
| @@ -140,18 +149,33 @@ | ||
| 140 | 149 | // make the module helper accessible |
| 141 | 150 | JLoader::import('adapter.module.helper'); |
| 142 | 151 | JModuleHelper::setPath($this->_path); |
| 143 | 152 | |
| 153 | + $layout = $this->_path . DIRECTORY_SEPARATOR . $this->_id . '.php'; | |
| 154 | + | |
| 155 | + // check if the widget owns a layout | |
| 156 | + if (!JFile::exists($layout)) | |
| 157 | + { | |
| 158 | + return; | |
| 159 | + } | |
| 160 | + | |
| 144 | 161 | /** |
| 145 | - * Include system.js file to support JFormValidator. | |
| 146 | - * | |
| 147 | - * @todo Check whether this callback is actually needed. | |
| 148 | - * The comment below mentions about the support for | |
| 149 | - * the validator, but it shouldn't be used in the | |
| 150 | - * front-end. In case the system.js file is required | |
| 151 | - * we could consider to load Bootstrap only in case | |
| 152 | - * we are in the site section. | |
| 162 | + * If we are under a block preview, prefer a different layout. | |
| 163 | + * | |
| 164 | + * @since 10.1.71 | |
| 153 | 165 | */ |
| 166 | + if ($this->isBlockPreview()) { | |
| 167 | + // check whether the block preview layout is supported by this widget | |
| 168 | + $blockPreviewLayout = ABSPATH . '/wp-content/plugins/' . $this->_option . '/libraries/wordpress/fse/modules/' . $this->_id . '.php'; | |
| 169 | + | |
| 170 | + if (JFile::exists($blockPreviewLayout)) | |
| 171 | + { | |
| 172 | + // block preview layout supported, use it in place of the default one | |
| 173 | + $layout = JPath::clean($blockPreviewLayout); | |
| 174 | + } | |
| 175 | + } | |
| 176 | + | |
| 177 | + // include system.js file to support JoomlaCore | |
| 154 | 178 | JHtml::fetch('system.js'); |
| 155 | 179 | |
| 156 | 180 | /** |
| 157 | 181 | * Added support for module class suffix. |
| @@ -176,16 +200,8 @@ | ||
| 176 | 200 | { |
| 177 | 201 | echo $args['before_title'] . apply_filters('widget_title', $config['title']) . $args['after_title']; |
| 178 | 202 | } |
| 179 | 203 | |
| 180 | - $layout = $this->_path . DIRECTORY_SEPARATOR . $this->_id . '.php'; | |
| 181 | - | |
| 182 | - // check if the widget owns a layout | |
| 183 | - if (!is_file($layout)) | |
| 184 | - { | |
| 185 | - return; | |
| 186 | - } | |
| 187 | - | |
| 188 | 204 | // wrap the $config in a registry |
| 189 | 205 | $params = new JObject($config); |
| 190 | 206 | |
| 191 | 207 | /** |
| @@ -193,9 +209,9 @@ | ||
| 193 | 209 | * |
| 194 | 210 | * @since 10.1.30 |
| 195 | 211 | */ |
| 196 | 212 | $module = new stdClass; |
| 197 | - $module->id = $this->number; | |
| 213 | + $module->id = ++static::$incrementalCounter; | |
| 198 | 214 | |
| 199 | 215 | /** |
| 200 | 216 | * Plugins can manipulate the configuration of the widget at runtime. |
| 201 | 217 | * Fires before dispatching the widget in the front-end. |
| @@ -648,6 +664,448 @@ | ||
| 648 | 664 | |
| 649 | 665 | // flag as already registered |
| 650 | 666 | $this->registered = true; |
| 651 | 667 | } |
| 668 | + } | |
| 669 | + | |
| 670 | + /** | |
| 671 | + * Converts a WordPress widget into a WordPress block. | |
| 672 | + * The usage of this method requires an external script with a | |
| 673 | + * path built as the following one: | |
| 674 | + * /modules/mod_[PLUGIN]_[NAME]/[PLUGIN]-[NAME]-widget-block.js | |
| 675 | + * | |
| 676 | + * @param string $blockScriptUri The relative URI of the script declaring the tools | |
| 677 | + * that will be actually used by the block. | |
| 678 | + * @param array $data The widget manifest data (@see WP_Block_Type). | |
| 679 | + * | |
| 680 | + * @return void | |
| 681 | + * | |
| 682 | + * @since 10.1.51 | |
| 683 | + */ | |
| 684 | + protected function registerBlockType(string $blockScriptUri, array $data) | |
| 685 | + { | |
| 686 | + /** | |
| 687 | + * Make sure Gutenberg is up and running to avoid | |
| 688 | + * any fatal errors, as the register_block_type() | |
| 689 | + * function may be not available on old instances. | |
| 690 | + */ | |
| 691 | + if (!function_exists('register_block_type')) | |
| 692 | + { | |
| 693 | + return; | |
| 694 | + } | |
| 695 | + | |
| 696 | + // create a block identifier for this widget | |
| 697 | + $block_id = preg_replace("/^mod_{$this->_option}_/", '', $this->_id); | |
| 698 | + $block_id = preg_replace("/_/", '-', $block_id) . '-widget-block'; | |
| 699 | + | |
| 700 | + // define the block manifest | |
| 701 | + $data = array_merge([ | |
| 702 | + 'id' => $this->_option . '/' . $block_id, | |
| 703 | + 'title' => $this->name, | |
| 704 | + 'description' => $this->widget_options['description'], | |
| 705 | + 'textdomain' => $this->_option, | |
| 706 | + 'category' => 'widgets', | |
| 707 | + 'attributes' => [], | |
| 708 | + 'supports' => [ | |
| 709 | + // do not edit as HTML | |
| 710 | + 'html' => false, | |
| 711 | + // use the block just once per post | |
| 712 | + 'multiple' => true, | |
| 713 | + // don't allow the block to be converted into a reusable block | |
| 714 | + 'reusable' => false, | |
| 715 | + ], | |
| 716 | + ], $data); | |
| 717 | + | |
| 718 | + $form = []; | |
| 719 | + | |
| 720 | + // get form fieldsets | |
| 721 | + foreach ($this->_form->getFieldset() as $fieldset) | |
| 722 | + { | |
| 723 | + $set = []; | |
| 724 | + $set['name'] = (string) $fieldset->attributes()->name; | |
| 725 | + $set['title'] = JText::translate((string) $fieldset->attributes()->label ?: 'COM_MENUS_' . strtoupper($set['name']) . '_FIELDSET_LABEL'); | |
| 726 | + $set['fields'] = []; | |
| 727 | + | |
| 728 | + // take only the fields that belong to this fieldset | |
| 729 | + $fields = $fieldset->xpath('//fieldset[@name="' . $set['name'] . '"] //field'); | |
| 730 | + | |
| 731 | + /** | |
| 732 | + * Add support for title field by creating a custom XML field, | |
| 733 | + * only if the XML of the module doesn't declare it. | |
| 734 | + * | |
| 735 | + * @since 10.1.21 | |
| 736 | + */ | |
| 737 | + if ($set['name'] === 'basic' && !$this->_form->getField('title')) | |
| 738 | + { | |
| 739 | + // create title field | |
| 740 | + $title = simplexml_load_string('<field name="title" type="text" default="" label="COM_MODULES_FIELD_TITLE_LABEL" description="COM_MODULES_FIELD_TITLE_DESC" />'); | |
| 741 | + // push title at the beginning of the list | |
| 742 | + array_unshift($fields, $title); | |
| 743 | + } | |
| 744 | + | |
| 745 | + // get form fields | |
| 746 | + foreach ($fields as $field) | |
| 747 | + { | |
| 748 | + // get form field | |
| 749 | + $field = JFormField::getInstance($field); | |
| 750 | + | |
| 751 | + // skip the module class suffix field as it is supported by default by Gutenberg blocks | |
| 752 | + if ($field->name === 'moduleclass_sfx') | |
| 753 | + { | |
| 754 | + continue; | |
| 755 | + } | |
| 756 | + | |
| 757 | + // attach module path (useful to obtain the available layouts) | |
| 758 | + $field->bind($this->_path, 'modpath'); | |
| 759 | + $field->bind($this->_option, 'modowner'); | |
| 760 | + | |
| 761 | + // obtain field layout data | |
| 762 | + $displayData = array_merge( | |
| 763 | + [ | |
| 764 | + 'type' => $field->type, | |
| 765 | + 'layout' => $field->layoutId, | |
| 766 | + 'label' => JText::translate($field->label ?? ''), | |
| 767 | + 'description' => strip_tags(JText::translate($field->description ?? '')), | |
| 768 | + 'showon' => $field->showon, | |
| 769 | + ], | |
| 770 | + $field->getLayoutData() | |
| 771 | + ); | |
| 772 | + | |
| 773 | + // in case the field does not provide the layout, use the HTML type | |
| 774 | + // and render here the input data | |
| 775 | + if (!$field->layoutId) | |
| 776 | + { | |
| 777 | + // convert a HTML document into a JSON-compatible structure | |
| 778 | + $json = $this->createElementsFromHtml($field->getInput()); | |
| 779 | + | |
| 780 | + // overwrite type and inject the converted structure within the layout attribute | |
| 781 | + $displayData['type'] = 'html'; | |
| 782 | + $displayData['layout'] = $json; | |
| 783 | + } | |
| 784 | + | |
| 785 | + // fetch default value | |
| 786 | + $default = $displayData['value'] ?? ''; | |
| 787 | + $default = ($default !== '' && $default !== null) ? $default : ($displayData['default'] ?? ($field->multiple ? [] : '')); | |
| 788 | + | |
| 789 | + // normalize options structure | |
| 790 | + if (isset($displayData['options']) && is_array($displayData['options'])) | |
| 791 | + { | |
| 792 | + /** | |
| 793 | + * In case the default value is not a valid option, use the first available one. | |
| 794 | + * | |
| 795 | + * @todo In case the option is not an associative array, the following condition | |
| 796 | + * will not work. If we want to extend this compatibility we should manually | |
| 797 | + * iterate the normalized array in search of an option with matching value. | |
| 798 | + */ | |
| 799 | + if (is_scalar($default) && !isset($displayData['options'][$default])) | |
| 800 | + { | |
| 801 | + $default = key($displayData['options']); | |
| 802 | + } | |
| 803 | + | |
| 804 | + $options = []; | |
| 805 | + | |
| 806 | + foreach ($displayData['options'] as $value => $label) | |
| 807 | + { | |
| 808 | + if (is_object($label) || is_array($label)) | |
| 809 | + { | |
| 810 | + $label = (object) $label; | |
| 811 | + | |
| 812 | + $options[] = [ | |
| 813 | + 'label' => JText::translate($label->text), | |
| 814 | + 'value' => $label->value, | |
| 815 | + ]; | |
| 816 | + } | |
| 817 | + else | |
| 818 | + { | |
| 819 | + $options[] = [ | |
| 820 | + 'label' => JText::translate($label), | |
| 821 | + 'value' => $value, | |
| 822 | + ]; | |
| 823 | + } | |
| 824 | + } | |
| 825 | + | |
| 826 | + $displayData['options'] = $options; | |
| 827 | + } | |
| 828 | + | |
| 829 | + if ($field->multiple) | |
| 830 | + { | |
| 831 | + $attrType = 'array'; | |
| 832 | + } | |
| 833 | + else if ($field->type === 'radio' && $field->class === 'btn-group btn-group-yesno') | |
| 834 | + { | |
| 835 | + $attrType = 'integer'; | |
| 836 | + } | |
| 837 | + else | |
| 838 | + { | |
| 839 | + $attrType = 'string'; | |
| 840 | + } | |
| 841 | + | |
| 842 | + if (!empty($displayData['name'])) | |
| 843 | + { | |
| 844 | + // bind field attributes | |
| 845 | + $data['attributes'][$displayData['name']] = [ | |
| 846 | + 'type' => $attrType, | |
| 847 | + 'default' => $default, | |
| 848 | + ]; | |
| 849 | + } | |
| 850 | + | |
| 851 | + // enqueue form field | |
| 852 | + $set['fields'][] = $displayData; | |
| 853 | + } | |
| 854 | + | |
| 855 | + $form[] = $set; | |
| 856 | + } | |
| 857 | + | |
| 858 | + // register the script declaring the reusable functions for Gutenberg | |
| 859 | + wp_register_script( | |
| 860 | + $this->_option . '-gutenberg-tools', | |
| 861 | + $blockScriptUri . 'js/gutenberg-tools.js', | |
| 862 | + ['wp-blocks', 'wp-element', 'wp-i18n'], | |
| 863 | + constant(strtoupper($this->_option . '_software_version')) | |
| 864 | + ); | |
| 865 | + | |
| 866 | + // register the script that contains all the JS functions used | |
| 867 | + // to implement a new block for Gutenberg editor | |
| 868 | + wp_register_script( | |
| 869 | + $this->_option . '-gutenberg-widgets', | |
| 870 | + $blockScriptUri . 'js/gutenberg-widgets.js', | |
| 871 | + ['wp-blocks', 'wp-element', 'wp-i18n'], | |
| 872 | + constant(strtoupper($this->_option . '_software_version')) | |
| 873 | + ); | |
| 874 | + | |
| 875 | + // register the script that will be used to support this widget | |
| 876 | + // as Gutenberg block editor | |
| 877 | + wp_register_script( | |
| 878 | + $this->_option . '-' . $block_id, | |
| 879 | + plugin_dir_url($this->_path) . $this->_id . '/' . $this->_option . '-' . $block_id . '.js', | |
| 880 | + ['wp-blocks', 'wp-element', 'wp-i18n'], | |
| 881 | + constant(strtoupper($this->_option . '_software_version')) | |
| 882 | + ); | |
| 883 | + | |
| 884 | + // Pass the manifest data to the script previously loaded. | |
| 885 | + // The object variable will be named as: | |
| 886 | + // MOD_[PLUGIN]_[NAME]_BLOCK_DATA | |
| 887 | + wp_localize_script( | |
| 888 | + $this->_option . '-' . $block_id, | |
| 889 | + strtoupper($this->_id . '_block_data'), | |
| 890 | + array_merge( | |
| 891 | + $data, | |
| 892 | + [ | |
| 893 | + 'form' => $form, | |
| 894 | + ] | |
| 895 | + ) | |
| 896 | + ); | |
| 897 | + | |
| 898 | + // create a new block type, which must provide the scripts previously loaded | |
| 899 | + register_block_type($this->_option . '/' . $block_id, array_merge($data, [ | |
| 900 | + 'render_callback' => function($config) { | |
| 901 | + // prepare widget arguments | |
| 902 | + $args = [ | |
| 903 | + 'before_widget' => '<div class="widget widget_' . $this->_id . '" id="' . $this->_id . '_' . (++static::$incrementalCounter) . '">', | |
| 904 | + 'before_title' => '<h3 class="widget-title">', | |
| 905 | + 'after_title' => '</h3>', | |
| 906 | + 'after_widget' => '</div>', | |
| 907 | + ]; | |
| 908 | + | |
| 909 | + // adjust widget configuration | |
| 910 | + $config['moduleclass_sfx'] = $config['className'] ?? ''; | |
| 911 | + | |
| 912 | + $requestUri = JFactory::getApplication()->input->server->getString('REQUEST_URI', ''); | |
| 913 | + | |
| 914 | + $is_rest_api = strpos($requestUri, trailingslashit(rest_get_url_prefix())) !== false | |
| 915 | + || JUri::getInstance($requestUri)->hasVar('rest_route'); | |
| 916 | + | |
| 917 | + // define a callback to include a placeholder in case the widget is not able to render any contents | |
| 918 | + $previewPlaceholderCallback = function($id, &$html) { | |
| 919 | + // get rid of any script and style declared by the widget layout | |
| 920 | + $test = preg_replace("/<script(?:.*?)>(?:.*?)<\/script>/s", '', $html); | |
| 921 | + $test = preg_replace("/<style(?:.*?)>(?:.*?)<\/style>/s", '', $test); | |
| 922 | + | |
| 923 | + // check whether the test var contains some texts | |
| 924 | + if (!trim(strip_tags($test))) { | |
| 925 | + $html = '<div style="padding: 10px; background: #eee; border: 2px solid #ddd;">' | |
| 926 | + . JText::translate('COM_MODULES_PREVIEW_NOT_AVAIL') | |
| 927 | + . '</div>'; | |
| 928 | + } | |
| 929 | + }; | |
| 930 | + | |
| 931 | + // start buffer | |
| 932 | + ob_start(); | |
| 933 | + | |
| 934 | + if ($is_rest_api || is_admin()) | |
| 935 | + { | |
| 936 | + // overwrite the callback to register an asset declaration at runtime | |
| 937 | + JFactory::getDocument()->attachToHeadCustomCallback = function($callback) { | |
| 938 | + // prevent the system document from displaying the asset | |
| 939 | + }; | |
| 940 | + | |
| 941 | + // register a callback to display a placeholder when the widget contents are empty | |
| 942 | + add_action('vik_widget_after_dispatch_site', $previewPlaceholderCallback, 10, 2); | |
| 943 | + } | |
| 944 | + | |
| 945 | + // render the widget for the front-end | |
| 946 | + $this->widget($args, $config); | |
| 947 | + | |
| 948 | + // if we are under a REST API, the block is probably | |
| 949 | + // requesting a server-side rendering of the widget | |
| 950 | + if ($is_rest_api) | |
| 951 | + { | |
| 952 | + // force WordPress to include the styles and the scripts | |
| 953 | + // within the rendered HTML | |
| 954 | + wp_print_styles(); | |
| 955 | + | |
| 956 | + // DO NOT print the scripts to prevent JS errors | |
| 957 | + // wp_print_head_scripts(); | |
| 958 | + } | |
| 959 | + | |
| 960 | + // get contents | |
| 961 | + $html = ob_get_contents(); | |
| 962 | + // clear buffer | |
| 963 | + ob_end_clean(); | |
| 964 | + | |
| 965 | + if ($is_rest_api || is_admin()) | |
| 966 | + { | |
| 967 | + // get rid of any script declared by the widget layout | |
| 968 | + $html = preg_replace("/<script(?:.*?)>(?:.*?)<\/script>/s", '', $html); | |
| 969 | + | |
| 970 | + // restore the original callback used to register the assets | |
| 971 | + JFactory::getDocument()->attachToHeadCustomCallback = null; | |
| 972 | + | |
| 973 | + // unregister the callback used to display a placeholder when the widget contents are empty | |
| 974 | + remove_action('vik_widget_after_dispatch_site', $previewPlaceholderCallback); | |
| 975 | + } | |
| 976 | + | |
| 977 | + return $html; | |
| 978 | + }, | |
| 979 | + 'editor_script_handles' => [ | |
| 980 | + $this->_option . '-gutenberg-tools', | |
| 981 | + $this->_option . '-gutenberg-widgets', | |
| 982 | + $this->_option . '-' . $block_id, | |
| 983 | + ], | |
| 984 | + ])); | |
| 985 | + } | |
| 986 | + | |
| 987 | + /** | |
| 988 | + * Converts an HTML string into a JSON-compatible structure. | |
| 989 | + * | |
| 990 | + * @param string $html The HTML to convert. | |
| 991 | + * | |
| 992 | + * @return object[] A list of nodes. | |
| 993 | + * | |
| 994 | + * @since 10.1.51 | |
| 995 | + */ | |
| 996 | + private function createElementsFromHtml(string $html) | |
| 997 | + { | |
| 998 | + // wrap the HTML into a dom document | |
| 999 | + $dom = new DOMDocument; | |
| 1000 | + $dom->loadHTML($html); | |
| 1001 | + | |
| 1002 | + // set up root | |
| 1003 | + $root = new stdClass; | |
| 1004 | + $root->tag = 'html'; | |
| 1005 | + $root->children = []; | |
| 1006 | + | |
| 1007 | + // recursively extract the nodes from the document | |
| 1008 | + $this->extractHtmlElements($dom, $root); | |
| 1009 | + | |
| 1010 | + // take only the children of the root ("html") | |
| 1011 | + return $root->children; | |
| 1012 | + } | |
| 1013 | + | |
| 1014 | + /** | |
| 1015 | + * Extracts the HTML tags from the provided node. | |
| 1016 | + * | |
| 1017 | + * @param DOMNode $domNode The current DOM node to scan. | |
| 1018 | + * @param object &$parent When the extracted tags should be attached. | |
| 1019 | + * | |
| 1020 | + * @return void | |
| 1021 | + * | |
| 1022 | + * @since 10.1.51 | |
| 1023 | + */ | |
| 1024 | + private function extractHtmlElements(DOMNode $domNode, &$parent) | |
| 1025 | + { | |
| 1026 | + foreach ($domNode->childNodes as $node) | |
| 1027 | + { | |
| 1028 | + $tag = $parent; | |
| 1029 | + | |
| 1030 | + if (!in_array($node->nodeName, ['html', 'body'])) | |
| 1031 | + { | |
| 1032 | + $tag = new stdClass; | |
| 1033 | + $tag->tag = $node->nodeName; | |
| 1034 | + $tag->children = []; | |
| 1035 | + | |
| 1036 | + if ($node->hasAttributes()) | |
| 1037 | + { | |
| 1038 | + $tag->attributes = []; | |
| 1039 | + | |
| 1040 | + foreach ($node->attributes as $attr) | |
| 1041 | + { | |
| 1042 | + if ($attr->nodeName === 'style') | |
| 1043 | + { | |
| 1044 | + $tag->attributes['style'] = []; | |
| 1045 | + | |
| 1046 | + // convert style string into an associative array | |
| 1047 | + if (preg_match_all("/([a-z0-9-]+)\s*:\s*([^;]+);/i", (string) $attr->nodeValue, $matches)) | |
| 1048 | + { | |
| 1049 | + for ($i = 0; $i < count($matches[0]); $i++) | |
| 1050 | + { | |
| 1051 | + $propertyName = $matches[1][$i]; | |
| 1052 | + $propertyValue = $matches[2][$i]; | |
| 1053 | + | |
| 1054 | + $tag->attributes['style'][$propertyName] = $propertyValue; | |
| 1055 | + } | |
| 1056 | + } | |
| 1057 | + } | |
| 1058 | + else | |
| 1059 | + { | |
| 1060 | + $tag->attributes[$attr->nodeName] = $attr->nodeValue; | |
| 1061 | + } | |
| 1062 | + } | |
| 1063 | + } | |
| 1064 | + | |
| 1065 | + if ($node->nodeName === '#text') | |
| 1066 | + { | |
| 1067 | + $parent->content = trim((string) $node->nodeValue); | |
| 1068 | + } | |
| 1069 | + else | |
| 1070 | + { | |
| 1071 | + $parent->children[] = $tag; | |
| 1072 | + } | |
| 1073 | + } | |
| 1074 | + | |
| 1075 | + if ($node->hasChildNodes()) | |
| 1076 | + { | |
| 1077 | + $this->extractHtmlElements($node, $tag); | |
| 1078 | + } | |
| 1079 | + } | |
| 1080 | + } | |
| 1081 | + | |
| 1082 | + /** | |
| 1083 | + * Checks whether the block is displayed by a block preview. | |
| 1084 | + * | |
| 1085 | + * @return bool | |
| 1086 | + * | |
| 1087 | + * @since 1.10.71 | |
| 1088 | + */ | |
| 1089 | + protected function isBlockPreview() | |
| 1090 | + { | |
| 1091 | + $app = JFactory::getApplication(); | |
| 1092 | + | |
| 1093 | + // check whether we are displaying the widget on the block preview | |
| 1094 | + $restPrefix = trailingslashit(rest_get_url_prefix()); | |
| 1095 | + $isPreview = strpos($app->input->server->getString('REQUEST_URI', ''), $restPrefix) !== false | |
| 1096 | + || JUri::getInstance($app->input->server->getString('REQUEST_URI', ''))->hasVar('rest_route') | |
| 1097 | + || strpos($app->input->server->getString('REQUEST_URI', ''), '/wp-admin/') !== false; | |
| 1098 | + | |
| 1099 | + /** | |
| 1100 | + * This hook can be used to determine at runtime whether we should display | |
| 1101 | + * a preview of the widget for a block editor. | |
| 1102 | + * | |
| 1103 | + * @param bool $isPreview Whether the preview layout should be preferred. | |
| 1104 | + * | |
| 1105 | + * @since 1.10.71 | |
| 1106 | + */ | |
| 1107 | + $isPreview = apply_filters('vikwp_widget_block_preview', $isPreview); | |
| 1108 | + | |
| 1109 | + return $isPreview; | |
| 652 | 1110 | } |
| 653 | 1111 | } |