$item) {
if (
isset($item['control'])
&& self::getControlValue($item['control'], 1) === true // Is this control enabled? @TODO Dynamic way to define default value depending the control ; not all will be active (1) by default
&& self::isBlockEnabled($block['blockName']) // Controls are enabled for this block?
&& isset($item['enabled'])
&& (bool) $item['enabled'] === true
) {
if (self::displayBlock($block, $item['control'], $key, $device_type) === false) {
// Stop iteration; we reached a control that decides block shouln't be displayed
$show_block = false;
}
}
}
if (!$show_block) {
return '';
}
// Generate a unique identifier for the block if it doesn't have one
$block_identifier = self::getBlockIdentifier($block);
// Add our control class to the block
$block_content = self::addBlockControlClass($block_content, $block_identifier);
// Add CSS rules as backup
$block_content = self::outputControlCSS($block_content, $block, $block_identifier);
}
return $block_content;
}
/**
* Check if block in widgets area is using controls and decide to display or not in frontend,
* including its widget HTML wrapper.
*
* @param array $instance Widget instance
*
* @return bool false means block and its widget HTML wrapper is hidden
* @since 3.1.2
*
*/
public static function checkBlockControlsWidget($instance)
{
// Exclude REST API
if (
strpos(wp_get_raw_referer(), '/wp-admin/widgets.php')
&& isset($_SERVER['REQUEST_URI'])
&& false !== strpos(
filter_var(wp_unslash($_SERVER['REQUEST_URI']), FILTER_SANITIZE_URL),
'/wp-json/'
)
) {
return $instance;
}
if (
Utilities::settingIsEnabled('block_controls')
&& !empty($instance['content'])
&& has_blocks($instance['content'])
) {
$blocks = parse_blocks($instance['content']);
if (isset($blocks[0]['attrs']['advgbBlockControls']) && $blocks[0]['blockName']) {
$controls = $blocks[0]['attrs']['advgbBlockControls'];
foreach ($controls as $key => $item) {
if (
isset($item['control'])
&& self::getControlValue(
$item['control'],
1
) === true // Is this control enabled? @TODO Dynamic way to define default value depending the control ; not all will be active (1) by default
&& self::isBlockEnabled($blocks[0]['blockName']) // Controls are enabled for this block?
&& isset($item['enabled'])
&& (bool) $item['enabled'] === true
) {
if (self::displayBlock($blocks[0], $item['control'], $key) === false) {
return false; // This block is hidden
}
}
}
}
}
return $instance;
}
/**
* Check a single control against a block
*
* @param array $block Block object
* @param string $control Control to validate against a block. e.g. 'schedule'
* @param int $key Array position for $control
* @param mixed $device_type The current user devce type
*
* @return bool True to display block, false to hide
* @since 3.1.0
*
*/
private static function displayBlock($block, $control, $key, $device_type = null)
{
switch ($control) {
// Schedule control
default:
case 'schedule':
$bControl = $block['attrs']['advgbBlockControls'][$key];
// Backward compatibility - check if we have the old single schedule format
if (isset($bControl['schedules'])) {
// New format with multiple schedules
$schedules = $bControl['schedules'];
} else {
// Legacy format - convert to array
$schedules = [[
'dateFrom' => $bControl['dateFrom'] ?? null,
'dateTo' => $bControl['dateTo'] ?? null,
'recurring' => $bControl['recurring'] ?? false,
'days' => $bControl['days'] ?? [],
'timeFrom' => $bControl['timeFrom'] ?? null,
'timeTo' => $bControl['timeTo'] ?? null,
'timezone' => $bControl['timezone'] ?? null
]];
}
$shouldShow = false;
foreach ($schedules as $schedule) {
// Skip if schedule is empty
if (
empty($schedule['dateFrom']) && empty($schedule['dateTo']) &&
empty($schedule['timeFrom']) && empty($schedule['timeTo']) &&
empty($schedule['days'])
) {
continue;
}
$dateFrom = $dateTo = $recurring = $timeFrom = $timeTo = null;
$days = isset($schedule['days']) && is_array($schedule['days']) && count($schedule['days'])
? $schedule['days'] : [];
if (count($days)) {
// Convert JavaScript days (0=Sun) to PHP 'N' format (7=Sun)
$days = array_map(function ($day) {
$day = intval($day);
return $day === 0 ? 7 : $day;
}, $days);
}
// Timezone handling
if (
defined('ADVANCED_GUTENBERG_PRO_LOADED')
&& isset($schedule['timezone'])
&& !empty($schedule['timezone'])
&& method_exists('PPB_AdvancedGutenbergPro\Utils\Definitions', 'advgb_pro_set_timezone')
) {
$timezone = \PPB_AdvancedGutenbergPro\Utils\Definitions::advgb_pro_set_timezone(
esc_html($schedule['timezone'])
);
} else {
$timezone = wp_timezone();
}
// Start showing
if (!empty($schedule['dateFrom'])) {
$dateFrom = \DateTime::createFromFormat('Y-m-d\TH:i:s', $schedule['dateFrom'], $timezone);
// Reset seconds to zero to enable proper comparison
$dateFrom->setTime($dateFrom->format('H'), $dateFrom->format('i'), 0);
}
// Stop showing
if (!empty($schedule['dateTo'])) {
$dateTo = \DateTime::createFromFormat('Y-m-d\TH:i:s', $schedule['dateTo'], $timezone);
// Reset seconds to zero to enable proper comparison
$dateTo->setTime($dateTo->format('H'), $dateTo->format('i'), 0);
if ($dateFrom) {
// Recurring is only relevant when both dateFrom and dateTo are defined
$recurring = isset($schedule['recurring']) ? $schedule['recurring'] : false;
}
}
/**
* Time handling
* Time from and Time to exists and are valid.
* Valid times: "02:00:00", "19:35:00"
* Invalid times: "-06:00:00", "25:00:00"
*
*/
if (
!empty($schedule['timeFrom'])
&& !empty($schedule['timeTo'])
&& strtotime($schedule['timeFrom']) !== false
&& strtotime($schedule['timeTo']) !== false
) {
// Get current datetime with timezone
$timeNow = new \DateTime('now', $timezone);
$timeNow->format('Y-m-d\TH:i:s');
$timeFrom = clone $timeNow;
$timeTo = clone $timeNow;
// Replace with our time attributes in previously generated datetime
$timeFrom->modify($schedule['timeFrom']);
$timeTo->modify($schedule['timeTo']);
}
if ($dateFrom || $dateTo || $days || ($timeFrom && $timeTo)) {
// Fetch current time keeping in mind the timezone
$now = new \DateTime('now', $timezone);
$now->format('Y-m-d\TH:i:s');
/* Reset seconds to zero to enable proper comparison
* as the from and to dates have those as 0
* but do this only for the from comparison
* as we need the block to stop showing at the right time and not 1 minute extra
*/
$nowFrom = clone $now;
$nowFrom->setTime($now->format('H'), $now->format('i'), 0);
// Decide if block is displayed or not
if ($recurring) {
// Make the year same as today's
$dateFrom->setDate(
$nowFrom->format('Y'),
$dateFrom->format('m'),
$dateFrom->format('j')
);
$dateTo->setDate($nowFrom->format('Y'), $dateTo->format('m'), $dateTo->format('j'));
}
if (
(!$schedule['dateFrom'] || $dateFrom->getTimestamp() <= $nowFrom->getTimestamp()) // No "Start showing", or "Start showing" <= Now
&& (!$schedule['dateTo'] || $now->getTimestamp() < $dateTo->getTimestamp()) // No "Stop showing", or now < "Stop showing" &&
&& (!count($days) || in_array($nowFrom->format('N'), $days)) // "These days"
&& (!$schedule['timeFrom'] || $timeFrom->getTimestamp() <= $nowFrom->getTimestamp()) // No "Time from", or "Time
&& (!$schedule['timeTo'] || $now->getTimestamp() < $timeTo->getTimestamp()) // No "Time to", or now
) {
// If any schedule matches, show the block
$shouldShow = true;
return $shouldShow;
}
}
}
return $shouldShow;
break;
// User role control
case 'user_role':
$bControl = $block['attrs']['advgbBlockControls'][$key];
$selected_roles = is_array($bControl['roles']) && count($bControl['roles'])
? $bControl['roles'] : [];
if (count($selected_roles)) {
// Check if user role exists to avoid non-valid roles
foreach ($selected_roles as $key => $role) {
if (!$GLOBALS['wp_roles']->is_role($role)) {
unset($selected_roles[$key]);
}
}
}
// Check current user role visit
$user = wp_get_current_user();
$approach = isset($bControl['approach']) && !empty(sanitize_text_field($bControl['approach']))
? $bControl['approach'] : 'public';
switch ($approach) {
default:
case 'public':
return true;
break;
case 'hidden':
return false;
break;
case 'login':
return is_user_logged_in() ? true : false;
break;
case 'logout':
return !is_user_logged_in() ? true : false;
break;
case 'include':
return array_intersect($selected_roles, $user->roles) ? true : false;
break;
case 'exclude':
return !array_intersect($selected_roles, $user->roles) ? true : false;
break;
}
break;
case 'device_type':
$bControl = $block['attrs']['advgbBlockControls'][$key];
$selected_devices = isset($bControl['devices']) ? $bControl['devices'] : [];
// If no devices selected, show on all
if (empty($selected_devices)) {
return true;
}
// Use the pre-detected device type
return in_array($device_type, $selected_devices);
break;
case 'device_width':
$bControl = $block['attrs']['advgbBlockControls'][$key];
$min_width = isset($bControl['min_width']) ? intval($bControl['min_width']) : 0;
$max_width = isset($bControl['max_width']) ? intval($bControl['max_width']) : 0;
// Get current screen width
$screen_width = isset($_SERVER['HTTP_X_REQUESTED_WITH']) ?
(isset($_SERVER['HTTP_X_SCREEN_WIDTH']) ? intval($_SERVER['HTTP_X_SCREEN_WIDTH']) : 0) :
(isset($_SERVER['HTTP_X_CLIENT_WIDTH']) ? intval($_SERVER['HTTP_X_CLIENT_WIDTH']) : 0);
// If we couldn't detect screen width, return true
if ($screen_width === 0) {
return true;
}
// Check min/max width
$min_ok = ($min_width === 0) || ($screen_width >= $min_width);
$max_ok = ($max_width === 0) || ($screen_width <= $max_width);
return $min_ok && $max_ok;
break;
// Archive control
case 'archive':
$bControl = $block['attrs']['advgbBlockControls'][$key];
$taxonomies = is_array($bControl['taxonomies']) ? $bControl['taxonomies'] : [];
$taxQuery = get_queried_object();
if (!isset($taxQuery->taxonomy)) {
return true;
}
$merged_tax = []; // To store selected taxonomies. e.g. [ 'category', 'post_tag' ]
$merged_terms = []; // To store selected terms from all taxonomies. e.g. [99,72,51]
// Create taxonomies array
if (isset($taxonomies) && count($taxonomies)) {
foreach ($taxonomies as $item) {
$merged_tax[] = sanitize_text_field($item['tax']);
// Create terms array
if (isset($item['terms']) && count($item['terms'])) {
foreach ($item['terms'] as $term) {
$merged_terms[] = intval($term);
}
}
}
}
if (count($merged_tax)) {
$approach = isset($bControl['approach']) && !empty(sanitize_text_field($bControl['approach']))
? $bControl['approach'] : 'exclude';
switch ($approach) {
case 'include':
return self::checkTaxonomies($taxonomies, $merged_terms, $taxQuery) ? true : false;
break;
case 'exclude':
return self::checkTaxonomies($taxonomies, $merged_terms, $taxQuery) ? false : true;
break;
}
}
break;
// Pages control
case 'page':
$bControl = $block['attrs']['advgbBlockControls'][$key];
$selected = is_array($bControl['pages']) ? $bControl['pages'] : [];
if (count($selected)) {
$selected = array_map('sanitize_text_field', $selected);
$approach = isset($bControl['approach']) && !empty(sanitize_text_field($bControl['approach']))
? $bControl['approach'] : 'public';
switch ($approach) {
default:
case 'public':
return true;
break;
case 'include':
return self::checkPages($selected) ? true : false;
break;
case 'exclude':
return self::checkPages($selected) ? false : true;
break;
}
}
break;
}
return true;
}
/**
* Get device type
*
* @return string
*/
public static function getDeviceType()
{
AbstractDeviceParser::setVersionTruncation(AbstractDeviceParser::VERSION_TRUNCATION_NONE);
$userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
try {
$dd = new DeviceDetector($userAgent);
$dd->parse();
if ($dd->isBot()) {
return 'desktop';
}
$deviceType = $dd->getDevice();
// Map device types to our categories
switch ($deviceType) {
case AbstractDeviceParser::DEVICE_TYPE_SMARTPHONE:
case AbstractDeviceParser::DEVICE_TYPE_FEATURE_PHONE:
return 'mobile';
case AbstractDeviceParser::DEVICE_TYPE_TABLET:
return 'tablet';
case AbstractDeviceParser::DEVICE_TYPE_DESKTOP:
case AbstractDeviceParser::DEVICE_TYPE_TV:
case AbstractDeviceParser::DEVICE_TYPE_CONSOLE:
case AbstractDeviceParser::DEVICE_TYPE_CAR_BROWSER:
default:
return 'desktop';
}
} catch (Exception $e) {
// Fallback to basic detection if DeviceDetector fails
return self::basicDeviceDetection();
}
}
/**
* Basic device detection fallback function
*
* @return string
*/
private static function basicDeviceDetection()
{
$userAgent = strtolower($_SERVER['HTTP_USER_AGENT'] ?? '');
if (
strpos($userAgent, 'mobile') !== false ||
strpos($userAgent, 'android') !== false ||
strpos($userAgent, 'iphone') !== false
) {
return 'mobile';
} elseif (
strpos($userAgent, 'tablet') !== false ||
strpos($userAgent, 'ipad') !== false
) {
return 'tablet';
}
return 'desktop';
}
/**
* Generate a unique identifier for the block
*
* @param mixed $block
* @return string
*/
private static function getBlockIdentifier($block)
{
// Use block ID if available
if (isset($block['attrs']['block_id'])) {
return 'advgb-block-' . $block['attrs']['block_id'];
}
// Fallback to hash of block content
$content_hash = substr(md5($block['innerHTML']), 0, 8);
return 'advgb-dyn-' . $content_hash;
}
/**
* Add control class to block
*
* @param mixed $block_content
* @param mixed $identifier
*
* @return mixed
*/
private static function addBlockControlClass($block_content, $identifier)
{
// Skip if already processed
if (strpos($block_content, $identifier) !== false) {
return $block_content;
}
// Handle blocks with existing class attribute
if (strpos($block_content, 'class="') !== false) {
return preg_replace(
'/class="([^"]*)"/',
'class="$1 ' . esc_attr($identifier) . '"',
$block_content,
1
);
}
// Handle blocks without class attribute
return preg_replace(
'/<([a-zA-Z0-9_-]+)([^>]*)>/',
'<$1$2 class="' . esc_attr($identifier) . '">',
$block_content,
1
);
}
/**
* Additional css backup for device type and device width
* @param mixed $block_content
* @param mixed $block
* @param string $identifier
*
*/
public static function outputControlCSS($block_content, $block, $identifier)
{
if (
Utilities::settingIsEnabled('block_controls')
&& isset($block['attrs']['advgbBlockControls'])
&& $block['blockName']
) {
$controls = $block['attrs']['advgbBlockControls'];
$css_rules = [];
foreach ($controls as $key => $item) {
if (isset($item['control']) && isset($item['enabled']) && (bool) $item['enabled'] === true) {
switch ($item['control']) {
case 'device_type':
$devices = isset($item['devices']) ? $item['devices'] : [];
if (!empty($devices)) {
$hidden_devices = array_diff(['desktop', 'tablet', 'mobile'], $devices);
foreach ($hidden_devices as $device) {
switch ($device) {
case 'mobile':
$css_rules[] = "@media (max-width: 767px) { .{$identifier} { display: none !important; } }";
break;
case 'tablet':
$css_rules[] = "@media (min-width: 768px) and (max-width: 1024px) { .{$identifier} { display: none !important; } }";
break;
case 'desktop':
$css_rules[] = "@media (min-width: 1025px) { .{$identifier} { display: none !important; } }";
break;
}
}
}
break;
case 'device_width':
$min_width = isset($item['min_width']) ? intval($item['min_width']) : 0;
$max_width = isset($item['max_width']) ? intval($item['max_width']) : 0;
if ($min_width > 0) {
$css_rules[] = "@media (max-width: " . ($min_width - 1) . "px) { .{$identifier} { display: none !important; } }";
}
if ($max_width > 0) {
$css_rules[] = "@media (min-width: " . ($max_width + 1) . "px) { .{$identifier} { display: none !important; } }";
}
break;
}
}
}
if (!empty($css_rules)) {
$block_content = '' . $block_content;
}
}
return $block_content;
}
/**
* Check taxonomies in frontend
*
* @param array $taxonomies Array of taxonomies setup e.g. [ ['tax'=>'category', 'terms' => [172,99,3], 'all' => false], ['tax'=>'post_tag', 'terms' => [], 'all' => true] ]
* @param array $terms Array of term ids e.g. [172,99,3]
* @param object $taxQuery WP_Term
*
* @return bool
* @since 3.1.2
*
*/
public static function checkTaxonomies($taxonomies, $terms, $taxQuery)
{
foreach ($taxonomies as $item) {
if ((string) $item['tax'] === $taxQuery->taxonomy && (bool) $item['all']) {
// Taxonomy found & all terms
return true;
} elseif (in_array($taxQuery->term_id, $terms)) {
// Term found
return true;
} else {
// Nothing to do here
}
}
return false;
}
/**
* Check pages in frontend
*
* @param array $selected Array of pages e.g. ['home', 'search']
*
* @return bool
* @since 3.1.1
*
*/
public static function checkPages($selected)
{
if (
in_array('home', $selected)
&& ((is_home() && is_front_page())
|| is_front_page()
)
) {
return true;
} elseif (in_array('blog', $selected) && is_home()) {
return true;
} elseif (in_array('archive', $selected) && is_archive()) {
return true;
} elseif (in_array('search', $selected) && is_search()) {
return true;
} elseif (in_array('page404', $selected) && is_404()) {
return true;
}
return false;
}
/**
* Add attributes to ServerSideRender blocks to fix "Invalid parameter(s): attributes" error.
* As example: 'core/latest-comments'
* Related Gutenberg issue: https://github.com/WordPress/gutenberg/issues/16850
*
* @since 3.1.0 function renamed and migrated from AdvancedGutenbergMain
* @since 2.14.0
*/
public static function addAttributes()
{
$registered_blocks = \WP_Block_Type_Registry::get_instance()->get_all_registered();
foreach ($registered_blocks as $block) {
$block->attributes['advgbBlockControls'] = [
'type' => 'array',
'default' => [],
];
}
}
/**
* Make sure ServerSideRender blocks are rendererd correctly in editor.
* As example: 'core/latest-comments'
* https://github.com/brainstormforce/ultimate-addons-for-gutenberg/blob/master/classes/class-uagb-loader.php#L136-L194
*
* @since 3.1.0 function renamed and migrated from AdvancedGutenbergMain
* @since 2.14.0
*/
public static function removeAttributes($result, $server, $request)
{
if (strpos($request->get_route(), '/wp/v2/block-renderer') !== false) {
if (
isset($request['attributes'])
&& isset($request['attributes']['advgbBlockControls'])
) {
$attributes = $request['attributes'];
if ($attributes['advgbBlockControls']) {
unset($attributes['advgbBlockControls']);
}
$request['attributes'] = $attributes;
}
}
return $result;
}
/**
* Get a Block control value from database option
*
* @param string $name Setting name - e.g. 'schedule' from advgb_block_controls > controls
* @param bool $default Default value when $setting doesn't exist in $option
*
* @return bool
* @since 3.1.0
*
*/
public static function getControlValue($name, $default)
{
$settings = get_option('advgb_block_controls');
$value = isset($settings['controls'][$name])
? (bool) $settings['controls'][$name]
: (bool) $default;
return $value;
}
/**
* Check if Block controls are enabled for a particular block
*
* @param string $block Block name. e.g. 'core/paragraph'
*
* @return bool
* @since 3.1.0
*
*/
public static function isBlockEnabled($block)
{
$settings = get_option('advgb_block_controls');
if (
$settings
&& isset($settings['inactive_blocks'])
&& is_array($settings['inactive_blocks'])
&& count($settings['inactive_blocks']) > 0
&& in_array($block, $settings['inactive_blocks'])
) {
return false;
}
return true;
}
/**
* Save block controls page data
*
* @return boolean true on success, false on failure
* @since 3.1.0
*/
public static function save()
{
if (!current_user_can('activate_plugins')) {
return false;
}
// Controls
if (isset($_POST['save_controls'])) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- we check nonce below
if (
!wp_verify_nonce(
sanitize_key($_POST['advgb_controls_settings_nonce_field']),
'advgb_controls_settings_nonce'
)
) {
return false;
}
$advgb_block_controls = get_option('advgb_block_controls');
$advgb_block_controls['controls']['schedule'] = isset($_POST['schedule_control']) ? (bool) 1 : (bool) 0;
$advgb_block_controls['controls']['user_role'] = isset($_POST['user_role_control']) ? (bool) 1 : (bool) 0;
$advgb_block_controls['controls']['device_type'] = isset($_POST['device_type_control']) ? (bool) 1 : (bool) 0;
$advgb_block_controls['controls']['device_width'] = isset($_POST['device_width_control']) ? (bool) 1 : (bool) 0;
$advgb_block_controls['controls']['archive'] = isset($_POST['archive_control']) ? (bool) 1 : (bool) 0;
$advgb_block_controls['controls']['page'] = isset($_POST['page_control']) ? (bool) 1 : (bool) 0;
update_option('advgb_block_controls', $advgb_block_controls, false);
wp_safe_redirect(
add_query_arg(
[
'save' => 'success'
],
str_replace(
'/wp-admin/',
'',
sanitize_url($_POST['_wp_http_referer']) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
)
)
);
} // Blocks
elseif (isset($_POST['save_blocks'])) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- we check nonce below
if (
!wp_verify_nonce(
sanitize_key($_POST['advgb_controls_block_nonce_field']),
'advgb_controls_block_nonce'
)
) {
return false;
}
if (
isset($_POST['blocks_list'])
&& isset($_POST['active_blocks'])
&& is_array($_POST['active_blocks'])
) {
$blocks_list = array_map(
'sanitize_text_field',
json_decode(stripslashes($_POST['blocks_list'])) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
);
$active_blocks = array_map('sanitize_text_field', $_POST['active_blocks']);
$inactive_blocks = array_values(array_diff($blocks_list, $active_blocks));
// Save block controls
$block_controls = get_option('advgb_block_controls');
$block_controls['active_blocks'] = isset($active_blocks) ? $active_blocks : '';
$block_controls['inactive_blocks'] = isset($inactive_blocks) ? $inactive_blocks : '';
update_option('advgb_block_controls', $block_controls, false);
// Redirect with success message
wp_safe_redirect(
add_query_arg(
[
'tab' => 'blocks',
'save' => 'success'
],
str_replace(
'/wp-admin/',
'',
sanitize_url($_POST['_wp_http_referer']) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
)
)
);
} else {
// Redirect with error message / Nothing was saved
wp_safe_redirect(
add_query_arg(
[
'save' => 'error'
],
str_replace(
'/wp-admin/',
'',
sanitize_url($_POST['_wp_http_referer']) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
)
)
);
}
} else {
// Nothing to do here
}
return false;
}
/**
* Get controls status (enabled/disabled)
*
* @return array e.g.['schedule' => true, 'user_role' => true]
* @since 3.1.1
*/
private static function getControlsArray()
{
$block_controls = get_option('advgb_block_controls');
$result = [];
$controls = [
'schedule',
'user_role',
'device_type',
'device_width',
'archive',
'page'
];
if ($block_controls) {
foreach ($controls as $item) {
$result[$item] = isset($block_controls['controls'][$item])
? (bool) $block_controls['controls'][$item]
: (bool) 1;
}
} else {
foreach ($controls as $item) {
$result[$item] = (bool) 1;
}
}
return $result;
}
/**
* Javascript objects with controls configuration to load in Admin.
* 'advgb_block_controls_vars' object with controls configuration.
* 'advgb_blocks_list' object with all the saved blocks in 'advgb_blocks_list' option.
*
* @return void
* @since 3.1.0
*/
public static function adminData()
{
// Build blocks form and add filters functions
wp_add_inline_script(
'advgb_main_js',
"window.addEventListener('load', function () {
advgbGetBlockControls(
advgb_block_controls_vars.inactive_blocks,
'#advgb_block_controls_nonce_field',
'advgb_block_controls',
" . wp_json_encode(self::defaultExcludedBlocks()) . "
);
});"
);
do_action('enqueue_block_editor_assets');
// Block categories
$blockCategories = array();
if (function_exists('get_block_categories')) {
$blockCategories = get_block_categories(get_post());
} elseif (function_exists('gutenberg_get_block_categories')) {
$blockCategories = gutenberg_get_block_categories(get_post());
}
wp_add_inline_script(
'wp-blocks',
sprintf('wp.blocks.setCategories( %s );', wp_json_encode($blockCategories)),
'after'
);
// Block types
$block_type_registry = \WP_Block_Type_Registry::get_instance();
foreach ($block_type_registry->get_all_registered() as $block_name => $block_type) {
if (!empty($block_type->editor_script)) {
wp_enqueue_script($block_type->editor_script);
}
}
/* Get blocks saved in advgb_blocks_list option to include the ones that are missing
* as result of javascript method wp.blocks.getBlockTypes()
* e.g. blocks registered only via PHP
*/
$advgb_blocks_list = get_option('advgb_blocks_list');
if ($advgb_blocks_list && is_array($advgb_blocks_list)) {
$saved_blocks = $advgb_blocks_list;
} else {
$saved_blocks = [];
}
wp_localize_script(
'advgb_main_js',
'advgb_blocks_list',
$saved_blocks
);
// Active and inactive blocks
$block_controls = get_option('advgb_block_controls');
if (
$block_controls
&& isset($block_controls['active_blocks'])
&& isset($block_controls['inactive_blocks'])
&& is_array($block_controls['active_blocks'])
&& is_array($block_controls['inactive_blocks'])
) {
wp_localize_script(
'wp-blocks',
'advgb_block_controls_vars',
[
'controls' => self::getControlsArray(),
'active_blocks' => $block_controls['active_blocks'],
'inactive_blocks' => $block_controls['inactive_blocks']
]
);
} else {
// Nothing saved in database for current user role. Set empty (access to all blocks)
wp_localize_script(
'wp-blocks',
'advgb_block_controls_vars',
[
'controls' => self::getControlsArray(),
'active_blocks' => [],
'inactive_blocks' => []
]
);
}
}
/**
* Javascript objects with controls configuration to load in Editor.
* 'advgb_block_controls_vars' object with controls configuration.
*
* @return void
* @since 3.1.0
*/
public static function editorData()
{
$advgb_block_controls = get_option('advgb_block_controls');
if (
$advgb_block_controls
&& isset($advgb_block_controls['inactive_blocks'])
&& is_array($advgb_block_controls['inactive_blocks'])
&& count($advgb_block_controls['inactive_blocks']) > 0
) {
// Merge non supported saved and manually defined blocks
$non_supported = array_merge(
$advgb_block_controls['inactive_blocks'],
self::defaultExcludedBlocks()
);
$non_supported = array_unique($non_supported);
} else {
// Non supported manually defined blocks
$non_supported = self::defaultExcludedBlocks();
}
// Output js variable
wp_localize_script(
'wp-blocks',
'advgb_block_controls_vars',
[
'non_supported' => $non_supported,
'controls' => self::getControlsArray(),
'user_roles' => self::getUserRoles(),
'taxonomies' => self::getTaxonomies(),
'page' => self::getPages()
]
);
}
/**
* Block controls support for these blocks is not available
*
* @return void
* @since 3.1.0
*/
public static function defaultExcludedBlocks()
{
return [
'core/freeform',
'core/legacy-widget',
'core/widget-area',
'core/column',
'advgb/tab',
'advgb/column',
'advgb/accordion' // @TODO - Deprecated block. Remove later.
];
}
/**
* Enqueue assets for editor
*
* @param $wp_editor_dep Block editor dependency based on current screen. e.g. 'wp-editor'
*
* @return void
* @since 3.1.0
*
*/
public static function editorAssets($wp_editor_dep)
{
if (Utilities::settingIsEnabled('block_controls')) {
wp_enqueue_script(
'advgb_block_controls',
ADVANCED_GUTENBERG_PLUGIN_DIR_URL . 'assets/blocks/block-controls.js',
[
'wp-blocks',
'wp-i18n',
'wp-element',
'wp-data',
$wp_editor_dep,
'wp-plugins',
'wp-compose'
],
ADVANCED_GUTENBERG_VERSION,
true
);
}
}
/**
* Retrieve User roles
*
* @return array
* @since 3.1.0
*
*/
public static function getUserRoles()
{
global $wp_roles;
$result = [];
$roles_list = $wp_roles->get_names();
foreach ($roles_list as $roles => $role_name) {
$result[] = [
'slug' => $roles,
'title' => esc_attr(translate_user_role($role_name))
];
}
return $result;
}
/**
* Retrieve Taxonomies
*
* @return array
* @since 3.1.1
*
*/
public static function getTaxonomies()
{
$taxonomies = get_taxonomies();
$result = [];
$exclude = [
'nav_menu',
'link_category',
'post_format',
'wp_theme',
'wp_template_part_area'
];
foreach ($taxonomies as $item) {
$tax = get_taxonomy($item);
if (!in_array($item, $exclude)) {
$result[] = [
'slug' => $item,
'title' => $tax->labels->singular_name
];
}
}
return $result;
}
/**
* Retrieve Taxonomies selected in a block
*
* @param array $selected Selected taxonomies in the block
*
* @return array
* @since 3.1.1
*
*/
public static function getBlockTaxonomies($selected)
{
if (!is_array($selected) || !count($selected)) {
return [];
}
global $wp_taxonomies;
$result = [];
$taxonomies = $selected;
foreach ($wp_taxonomies as $key => $value) {
if (in_array($key, $taxonomies)) {
$result[] = [
'slug' => $key,
'name' => $value->labels->singular_name
];
}
}
return $result;
}
/**
* Retrieve Terms
*
* @param array $data Taxonomy slugs and term ids or search word
*
* @return array
* @since 3.1.1
*
*/
public static function getTerms($data)
{
if (
isset($data['taxonomies'])
&& is_array($data['taxonomies'])
&& count($data['taxonomies'])
) {
$taxonomies = array_map('sanitize_text_field', $data['taxonomies']);
$args['taxonomy'] = $taxonomies;
// Note: can't use search and include in the same request
if (isset($data['search']) && !empty($data['search'])) {
$args['search'] = sanitize_text_field($data['search']);
$args['number'] = 10;
}
if (isset($data['ids']) && is_array($data['ids']) && count($data['ids'])) {
$args['include'] = array_map('intval', $data['ids']);
}
$result = [];
/*/ Include "All terms" options
global $wp_taxonomies;
foreach( $taxonomies as $tax ) {
$result[] = [
'slug' => "all__{$tax}",
'title' => sprintf(
__( 'All %s terms', 'advanced-gutenberg' ),
$wp_taxonomies[$tax]->labels->singular_name
),
'tax' => $tax,
];
}*/
$term_query = new \WP_Term_Query($args);
if (!empty($term_query->terms)) {
foreach ($term_query->terms as $term) {
$taxLabel = $term->taxonomy;
// Get human readable taxonomy name
$blockTaxonomies = self::getBlockTaxonomies($taxonomies);
if (count($blockTaxonomies)) {
foreach ($blockTaxonomies as $tax) {
if ($tax['slug'] === $term->taxonomy) {
$taxLabel = $tax['name'];
break;
}
}
}
$result[] = [
'slug' => $term->term_id,
'title' => $term->name . ' (' . $taxLabel . ')',
'tax' => $term->taxonomy,
];
}
}
return $result;
}
return [];
}
/**
* Retrieve pages
*
* @return array
* @since 3.1.1
*
*/
public static function getPages()
{
return [
[
'slug' => 'home',
'title' => __('Home', 'advanced-gutenberg')
],
[
'slug' => 'blog',
'title' => __('Blog', 'advanced-gutenberg')
],
[
'slug' => 'archive',
'title' => __('Archive', 'advanced-gutenberg')
],
[
'slug' => 'search',
'title' => __('Search', 'advanced-gutenberg')
],
[
'slug' => 'page404',
'title' => __('404', 'advanced-gutenberg')
]
];
}
/**
* Register custom REST API routes
*
* @return array
* @since 3.1.1
*
*/
public static function registerCustomRoutes()
{
// Fetch searched terms from all selected taxonomies
register_rest_route(
'advgb/v1',
'/terms',
[
'methods' => 'GET',
'callback' => ['PublishPress\Blocks\Controls', 'getTerms'],
'args' => [
'search' => [
'validate_callback' => ['PublishPress\Blocks\Controls', 'validateString'],
'sanitize_callback' => 'sanitize_text_field',
'required' => false,
'type' => 'string'
],
'ids' => [
'validate_callback' => ['PublishPress\Blocks\Controls', 'validateArray'],
'sanitize_callback' => ['PublishPress\Blocks\Controls', 'sanitizeNumbersArray'],
'required' => false,
'type' => 'array'
],
'taxonomies' => [
'validate_callback' => ['PublishPress\Blocks\Controls', 'validateArray'],
'sanitize_callback' => ['PublishPress\Blocks\Controls', 'sanitizeStringsArray'],
'required' => true,
'type' => 'array'
],
],
'permission_callback' => function () {
return current_user_can('edit_others_posts');
}
]
);
}
/**
* Check if value is a string
*
* @param $value Value to check
*
* @return array
* @since 3.1.1
*
*/
public static function validateString($value)
{
return is_string($value);
}
/**
* Check if value is an array
*
* @param $value Value to check
*
* @return array
* @since 3.1.1
*
*/
public static function validateArray($value)
{
return is_array($value) && count($value);
}
/**
* Sanitize an array of strings
*
* @param $value Value to check
*
* @return array
* @since 3.1.1
*
*/
public static function sanitizeStringsArray($value)
{
return array_map('sanitize_key', $value);
}
/**
* Sanitize an array of numbers
*
* @param $value Value to check
*
* @return array
* @since 3.1.1
*
*/
public static function sanitizeNumbersArray($value)
{
return array_map('intval', $value);
}
}
}