ADVANCED_GUTENBERG_UPGRADE_LINK,
'upgradeText' => __( 'Upgrade to Pro', 'advanced-gutenberg' ),
'proText' => __( 'Pro', 'advanced-gutenberg' ),
'promoBlocks' => $promoBlocks
]
);
}
/**
* Main page
*
* @return void
* @since 3.0.0
*/
public function loadMainPage()
{
if (! current_user_can('activate_plugins')) {
return false;
}
self::commonAdminPagesAssets();
wp_localize_script(
'advgb_main_js',
'advgb_main_dashboard',
[
'ajaxurl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('advgb_main_features_nonce')
]
);
$this->loadPage('main');
}
/**
* Settings page
*
* @return void
* @since 3.0.0
*/
public function loadSettingsPage()
{
if (! current_user_can('activate_plugins')) {
return false;
}
self::commonAdminPagesAssets();
$this->loadPage('settings');
}
/**
* Block access page
*
* @return void
* @since 3.0.0
*/
public function loadBlockAccessPage()
{
// Check users permissions
if (! current_user_can('administrator')) {
return false;
}
// Block access is disabled
if (! Utilities::settingIsEnabled('enable_block_access')) {
wp_die(
sprintf(
esc_html__(
'This feature is disabled. In order to use, please enable back through %sDashboard%s.',
'advanced-gutenberg'
),
'',
''
)
);
}
self::commonAdminPagesAssets();
/* Access current user blocks and saved blocks to build 2 javascript objects.
* 'advgbCUserRole' object for current user role from form dropdown
* 'advgb_blocks_list' object with all the saved blocks in 'advgb_blocks_list' option
*/
$this->blocksFeatureData(
'access',
// The object name to store the active/inactive blocks. To see it in browser console: advgbCUserRole.access
'advgb_blocks_user_roles', // Database option to check current user role's active/inactive blocks
// Hide deprecated blocks, disabled legacy blocks, and any globally disabled via Extra Blocks toggles
array_merge(self::hiddenDeprecatedBlocks(), self::allDisabledBlocks())
);
// Render form
$this->blocksFeatureForm(
'access', // Feature name in lowercase
__('Block Permissions', 'advanced-gutenberg') // Name of the feature
);
}
/**
* Block usage page
*
* @return void
* @since 3.0.0
*/
public function loadBlockUsagePage()
{
// Check users permissions
if (! current_user_can('manage_options')) {
return false;
}
// Block usage is disabled
if (! Utilities::settingIsEnabled('enable_block_usage')) {
wp_die(
sprintf(
esc_html__(
'This feature is disabled. In order to use, please enable back through %sDashboard%s.',
'advanced-gutenberg'
),
'',
''
)
);
}
self::commonAdminPagesAssets();
/* Access current user blocks and saved blocks to build 2 javascript objects.
* 'advgbCUserRole' object for current user role from form dropdown
* 'advgb_blocks_list' object with all the saved blocks in 'advgb_blocks_list' option
*/
$this->blocksFeatureData(
'access',
// The object name to store the active/inactive blocks. To see it in browser console: advgbCUserRole.access
'advgb_blocks_user_roles' // Database option to check current user role's active/inactive blocks
);
$this->blocksUsageData();
$this->blocksUsagePage();
}
/**
* Post Notes page
*
* @return void
*/
public function loadPostNotesPage()
{
if (! current_user_can('edit_posts')) {
return false;
}
self::commonAdminPagesAssets();
require_once plugin_dir_path(dirname(__FILE__)) . 'incl/pages/post-notes.php';
}
/**
* Add a "Notes" column to the Posts/Pages list screens, placed after
* the built-in comments column when present.
*
* @param array $columns Existing list table columns.
*
* @return array
*/
public function addPostNotesColumn($columns)
{
$new = [];
foreach ($columns as $key => $label) {
$new[$key] = $label;
if ($key === 'comments') {
$new['advgb_notes'] = __('Notes', 'advanced-gutenberg');
}
}
// No comments column on this screen: append before Date, or at the end
if (! isset($new['advgb_notes'])) {
if (isset($new['date'])) {
$date = $new['date'];
unset($new['date']);
$new['advgb_notes'] = __('Notes', 'advanced-gutenberg');
$new['date'] = $date;
} else {
$new['advgb_notes'] = __('Notes', 'advanced-gutenberg');
}
}
return $new;
}
/**
* Mark the "Notes" column sortable on the Posts/Pages list screens.
*
* @param array $columns Sortable columns (column key => orderby value).
*
* @return array
*/
public function addPostNotesSortableColumn($columns)
{
// 'desc' as third arg would set initial direction; keep WP default (asc)
$columns['advgb_notes'] = 'advgb_notes';
return $columns;
}
/**
* Order the Posts/Pages list by open-notes count when the Notes column
* header is clicked. The count is computed live (not stored as meta),
* so sorting needs a LEFT JOIN on the comments table.
*
* @param array $clauses SQL clauses for the query.
* @param \WP_Query $query Current query.
*
* @return array
*/
public function sortPostsByNotesClauses($clauses, $query)
{
if (! is_admin() || ! $query->is_main_query()) {
return $clauses;
}
if ($query->get('orderby') !== 'advgb_notes') {
return $clauses;
}
global $wpdb;
// Open notes: type "note", status hold (comment_approved '0'), top-level
$clauses['join'] .= " LEFT JOIN {$wpdb->comments} advgb_note_count"
. " ON advgb_note_count.comment_post_ID = {$wpdb->posts}.ID"
. " AND advgb_note_count.comment_type = 'note'"
. " AND advgb_note_count.comment_approved = '0'"
. " AND advgb_note_count.comment_parent = 0";
$clauses['groupby'] = "{$wpdb->posts}.ID";
$order = strtoupper($query->get('order')) === 'DESC' ? 'DESC' : 'ASC';
$clauses['orderby'] = "COUNT(DISTINCT advgb_note_count.comment_ID) {$order}, {$wpdb->posts}.post_date DESC";
return $clauses;
}
/**
* Print bubble styles for the Notes column. Core's comment-bubble CSS is
* scoped to .column-comments in list-tables.css, so the same classes render
* unstyled in our column — these rules mirror core's, scoped to ours.
*
* @return void
*/
public function printPostNotesColumnStyles()
{
?>
postNotesListCounts[ $post_id ])) {
$ids = [ $post_id ];
if ($wp_query instanceof \WP_Query && is_array($wp_query->posts)) {
foreach ($wp_query->posts as $list_post) {
$list_id = is_object($list_post) ? (int) $list_post->ID : (int) $list_post;
if ($list_id && ! isset($this->postNotesListCounts[ $list_id ])) {
$ids[] = $list_id;
}
}
}
$ids = array_values(array_unique($ids));
foreach ($ids as $id) {
$this->postNotesListCounts[ $id ] = 0;
}
// Open notes: type "note", status hold (comment_approved '0'), top-level
$placeholders = implode(',', array_fill(0, count($ids), '%d'));
// phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.PreparedSQLPlaceholders.UnfinishedPrepare, WordPress.DB.DirectDatabaseQuery -- The IN list contains only generated %d placeholders; counts are cached for the request.
$rows = $wpdb->get_results($wpdb->prepare("SELECT comment_post_ID AS post_id, COUNT(*) AS open_notes FROM {$wpdb->comments} WHERE comment_post_ID IN ($placeholders) AND comment_type = 'note' AND comment_approved = '0' AND comment_parent = 0 GROUP BY comment_post_ID", $ids));
foreach ((array) $rows as $row) {
$this->postNotesListCounts[ (int) $row->post_id ] = (int) $row->open_notes;
}
}
return $this->postNotesListCounts[ $post_id ];
}
/**
* Render the "Notes" column: a badge with the count of open (unresolved)
* notes on the post, linking to the post editor. Shows a dash when the
* post has no open notes.
*
* @param string $column Column key being rendered.
* @param int $post_id Current post ID.
*
* @return void
*/
public function renderPostNotesColumn($column, $post_id)
{
if ($column !== 'advgb_notes') {
return;
}
$open_notes = $this->getListOpenNotesCount($post_id);
if (! $open_notes) {
echo '—';
echo '' . esc_html__('No open notes', 'advanced-gutenberg') . '';
return;
}
$edit_link = get_edit_post_link($post_id);
$sr_text = sprintf(
/* translators: %d: number of open notes */
esc_html(_n('%d open note', '%d open notes', $open_notes, 'advanced-gutenberg')),
(int) $open_notes
);
/* Replicate the default Comments column exactly: core puts the
* post-com-count classes on the anchor itself and the count inside
* the grey bubble span — reusing that structure verbatim means the
* bubble, tail, and hover states all come from core's stylesheet. */
$count_label = (int) $open_notes > 99 ? '99+' : (string) (int) $open_notes;
if ($edit_link) {
printf(
'
',
esc_html($count_label),
$sr_text // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
);
}
}
/**
* Register the "Screen Options" tab content for the Post Notes page:
* a "Notes per page" number field and column show/hide checkboxes.
* Must run on load-{page}, before admin-header.php renders the tab.
*
* @return void
*/
public function addPostNotesScreenOptions()
{
add_screen_option('per_page', [
'label' => __('Notes per page', 'advanced-gutenberg'),
'default' => 20,
'option' => 'advgb_post_notes_per_page',
]);
$screen = get_current_screen();
if (! $screen) {
return;
}
add_filter('manage_' . $screen->id . '_columns', function () {
return [
// '_title' is the core-recognized key for a mandatory, non-hideable column
'_title' => __('Post', 'advanced-gutenberg'),
'post_type' => __('Post Type', 'advanced-gutenberg'),
'note' => __('Note', 'advanced-gutenberg'),
'author' => __('Author', 'advanced-gutenberg'),
'date' => __('Date', 'advanced-gutenberg'),
'status' => __('Status', 'advanced-gutenberg'),
];
});
}
/**
* Validate/save the "Notes per page" Screen Option value.
*
* @param mixed $status Screen option value to save, or false to skip.
* @param string $option Screen option name being saved.
* @param mixed $value Value submitted by the user.
*
* @return mixed
*/
public function setPostNotesScreenOption($status, $option, $value)
{
if ($option === 'advgb_post_notes_per_page') {
$value = (int) $value;
return ($value > 0) ? min(999, $value) : $status;
}
return $status;
}
/**
* Block controls page
*
* @return void
* @since 3.1.0
*/
public function loadBlockControlsPage()
{
// Check users permissions
if (! current_user_can('administrator')) {
return false;
}
// Block controls is disabled
if (! Utilities::settingIsEnabled('block_controls')) {
wp_die(
sprintf(
esc_html__(
'This feature is disabled. In order to use, please enable back through %sDashboard%s.',
'advanced-gutenberg'
),
'',
''
)
);
}
self::commonAdminPagesAssets();
self::commonAdminPagesAssets();
// Output blocks through javascript
PublishPress\Blocks\Controls::adminData();
$this->loadPage('block-controls');
}
/**
* Block settings page
*
* @return void
* @since 3.0.0
*/
public function loadBlockSettingsPage()
{
if (! current_user_can('activate_plugins')) {
return false;
}
// PublishPress blocks is disabled
if (! Utilities::settingIsEnabled('enable_advgb_blocks')) {
wp_die(
sprintf(
esc_html__(
'This feature is disabled. In order to use, please enable back through %sDashboard%s.',
'advanced-gutenberg'
),
'',
''
)
);
}
self::commonAdminPagesAssets();
Utilities::enqueueToolTipsAssets(); // Tooltips describing each block
wp_enqueue_media(); // Content Display gear -> Default thumbnail picker
$this->loadPage('block-settings');
}
/**
* Save handler for the Extra Blocks page.
* Auto-hooked on 'load-{hook}' via registerMainMenu() ($slug . '_save_page').
* Handles the Content Display block's Default thumbnail.
*
* @return bool|void
*/
public function advgb_block_settings_save_page()
{
if (! current_user_can('activate_plugins')) {
return false;
}
if (isset($_POST['save_content_display_thumb'])) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- we check nonce below
if (
! wp_verify_nonce(
sanitize_key($_POST['advgb_content_display_nonce_field']),
'advgb_content_display_nonce'
)
) {
return false;
}
$advgb_settings = get_option('advgb_settings');
$advgb_settings['rp_default_thumb'] = [
'url' => esc_url_raw($_POST['post_default_thumb']),
'id' => (int) $_POST['post_default_thumb_id']
];
update_option('advgb_settings', $advgb_settings);
if (isset($_REQUEST['_wp_http_referer'])) {
wp_safe_redirect(admin_url('admin.php?page=advgb_block_settings&save=success'));
exit;
}
} // Global per-block enable/disable toggles
elseif (isset($_POST['save_blocks_enabled'])) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- we check nonce below
if (
! wp_verify_nonce(
sanitize_key($_POST['advgb_blocks_enabled_nonce_field']),
'advgb_blocks_enabled_nonce'
)
) {
return false;
}
$all_blocks = isset($_POST['advgb_blocks_all'])
? array_filter(array_map('sanitize_text_field', explode(',', wp_unslash($_POST['advgb_blocks_all']))))
: [];
$checked = (isset($_POST['advgb_blocks_enabled']) && is_array($_POST['advgb_blocks_enabled']))
? array_map('sanitize_text_field', array_keys($_POST['advgb_blocks_enabled']))
: [];
$state = [];
foreach ($all_blocks as $name) {
// Checkboxes only post when checked; everything else is disabled
$state[$name] = in_array($name, $checked, true) ? 1 : 0;
}
update_option('advgb_blocks_enabled', $state, false);
if (isset($_REQUEST['_wp_http_referer'])) {
wp_safe_redirect(admin_url('admin.php?page=advgb_block_settings&save=success'));
exit;
}
}
}
/**
* Email & form page
*
* @return void
* @since 3.0.0
*/
public function loadEmailFormPage()
{
if (! current_user_can('activate_plugins')) {
return false;
}
self::commonAdminPagesAssets();
$this->loadPage('email-form');
}
/**
* Custom styles page
*
* @return void
* @since 3.0.0
*/
public function loadCustomStylesPage()
{
if (! current_user_can('activate_plugins')) {
return false;
}
// Custom styles is disabled
if (! Utilities::settingIsEnabled('enable_custom_styles')) {
wp_die(
sprintf(
esc_html__(
'This feature is disabled. In order to use, please enable back through %sDashboard%s.',
'advanced-gutenberg'
),
'',
''
)
);
}
wp_enqueue_script('less_js');
wp_enqueue_script('advgb_codemirror_js');
wp_enqueue_script('codemirror_hint');
wp_enqueue_script('codemirror_mode_css');
wp_enqueue_script('codemirror_hint_css');
wp_enqueue_script('advgb_custom_styles_js');
wp_localize_script(
'advgb_custom_styles_js',
'advgbCustomStyles',
[
'isProActive' => Utilities::isProActive(),
'copyLabel' => esc_attr__('Copy', 'advanced-gutenberg'),
'deleteLabel' => esc_attr__('Delete', 'advanced-gutenberg'),
]
);
wp_enqueue_style( 'advgb_custom_styles_css' );
wp_enqueue_style('codemirror_css');
wp_enqueue_style('codemirror_hint_style');
self::commonAdminPagesAssets();
$this->loadPage('custom-styles');
}
/**
* PublishPress admin footer
*
* @return void
* @since 3.0.0
*/
public function adminFooter($footer)
{
global $current_screen;
// When free and pro are active, avoid displaying our footer twice
if (defined('ADVANCED_GUTENBERG_FOOTER_DISPLAYED')) {
return $footer;
}
// Only display in PublishPress Blocks admin pages
$pages = [
'toplevel_page_advgb_main',
'blocks_page_advgb_block_access',
'blocks_page_advgb_block_usage',
'blocks_page_advgb_block_settings',
'blocks_page_advgb_custom_styles',
'blocks_page_advgb_settings',
'blocks_page_advgb_block_controls'
];
if (! in_array($current_screen->base, $pages)) {
return $footer;
}
$html = '';
if (! defined('ADVANCED_GUTENBERG_PRO_LOADED') || Utilities::settingIsEnabled('enable_pp_branding')) {
$html .= '';
}
// $footer is the "Thank you for creating with WordPress" text
$footer = $html . $footer;
if (! defined('ADVANCED_GUTENBERG_FOOTER_DISPLAYED')) {
define('ADVANCED_GUTENBERG_FOOTER_DISPLAYED', true);
}
return $footer;
}
/**
* Admin menu styles
*
* @return void
* @since 3.1.0
*/
public function adminMenuStyles()
{
wp_enqueue_style('advgb_admin_menu_styles');
}
/**
* Let's save a trigger option when a new plugin has been activated
* We use the boolean value of advgb_maybe_new_blocks option
* to trigger an update for advgb_blocks_list option through initBlocksList()
*
* @return void
* @since 3.1.4.2
*
*/
public function maybeNewBlocks($plugin, $network_activation)
{
update_option('advgb_maybe_new_blocks', intval(true), false);
}
/**
* Register meta data to use on editor sidebar
*
* @return void
*/
public function registerPostMeta()
{
register_post_meta(
'',
'advgb_blocks_editor_width',
array(
'show_in_rest' => true,
'single' => true,
'type' => 'string',
)
);
register_post_meta(
'',
'advgb_blocks_columns_visual_guide',
array(
'show_in_rest' => true,
'single' => true,
'type' => 'string',
)
);
}
/**
* Get a setting from a database option and return a working value
* to output in an HTML form
*
* @param string $option Database option name. e.g. 'advgb_settings'
* @param mixed $name Setting name - e.g. 'lorem' from advgb_settings option
* @param string $type $setting field type. 'checkbox' or 'text'
* @param mixed $default Default value when $setting doesn't exist in $option
*
* @return mixed
* @since 3.0.0
*/
public function getOptionSetting($option, $name, $type, $default)
{
$settings = get_option($option);
switch ($type) {
case 'checkbox':
$result = isset($settings[ $name ]) && $settings[ $name ] ? 'checked' : '';
if (! isset($settings[ $name ]) && $default === 1) {
$result = 'checked';
}
break;
case 'text': // For input types: text, number and textarea
$result = isset($settings[ $name ]) && ! empty($settings[ $name ]) ? $settings[ $name ] : $default;
break;
case 'array':
$result = isset($settings[ $name ]) && ! empty($settings[ $name ]) ? (array) $settings[ $name ] : $default;
break;
}
return $result;
}
/**
* Convert Editor Width value into a string
*
* @param int $value Editor width number
*
* @return string
*/
public function getAdvgbEditorWidth($value)
{
$result = '';
switch ($value) {
default:
$result = 'default';
break;
case '75':
$result = 'large';
break;
case '95':
$result = 'full';
break;
}
return $result;
}
/**
* Get Global Columns Visual Guide
*
* @deprecated 3.2.0 - Use colsVisualGuideGlobal() instead
*
* @param int $value Columns Visual Guide check
*
* @return string
*/
public function getAdvgbColsVisualGuideGlobal($value)
{
return ( isset($value) && ( $value == 0 ) ) ? 'disable' : 'enable';
}
/**
* Get Global Columns Visual Guide (new method)
*
* @since 3.2.0
*
* @return string
*/
public function colsVisualGuideGlobal()
{
$saved_settings = get_option('advgb_settings');
return isset($saved_settings['enable_columns_visual_guide'])
&& ! (bool) $saved_settings['enable_columns_visual_guide']
? 'disable'
: 'enable';
}
/**
* Set body classes for Editor width and Columns visual guide
*
* @param string $classes CSS class from body
*/
public function setAdvgEditorBodyClassses($classes)
{
$saved_settings = get_option('advgb_settings');
if ('post' === get_current_screen()->base) {
$saved_settings = get_option('advgb_settings');
global $post;
$editorWidth = get_post_meta($post->ID, 'advgb_blocks_editor_width', true);
$editorColsVG = get_post_meta($post->ID, 'advgb_blocks_columns_visual_guide', true);
$editorWidthGlobal = isset($saved_settings['editor_width'])
&& ! empty($saved_settings['editor_width'])
? $this->getAdvgbEditorWidth($saved_settings['editor_width'])
: 'default';
$editorColsVGGlobal = $this->colsVisualGuideGlobal();
// Editor width
if (isset($editorWidth) && ! empty($editorWidth)) {
// Editor width - Post meta
$classes .= ' advgb-editor-width-' . $editorWidth . ' ';
} else {
// Editor width - Global configuration
$classes .= ' advgb-editor-width-' . $editorWidthGlobal . ' ';
}
// Columns visual guide
if (isset($editorColsVG) && ! empty($editorColsVG)) {
// Columns visual guide - Post meta
$classes .= ' advgb-editor-col-guide-' . $editorColsVG . ' ';
} else {
// Columns visual guide - Global configuration
$classes .= ' advgb-editor-col-guide-' . $editorColsVGGlobal . ' ';
}
return $classes;
} elseif ('widgets' === get_current_screen()->id) {
// Use global Columns Visual Guide in Appearance > Widgets
$editorColsVGGlobal = $this->colsVisualGuideGlobal();
$classes .= ' advgb-editor-col-guide-' . $editorColsVGGlobal;
}
return $classes;
}
/**
* Save settings page data
*
* @return mixed
* @since 3.0.0
*/
public function advgb_settings_save_page()
{
if (! current_user_can('activate_plugins')) {
return false;
}
// General settings
if (isset($_POST['save_settings_general'])) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- we check nonce below
if (
! wp_verify_nonce(
sanitize_key($_POST['advgb_settings_general_nonce_field']),
'advgb_settings_general_nonce'
)
) {
return false;
}
$advgb_settings = get_option('advgb_settings');
$advgb_settings['blocks_icon_color'] = sanitize_hex_color($_POST['blocks_icon_color']);
update_option('advgb_settings', $advgb_settings);
if (isset($_REQUEST['_wp_http_referer'])) {
wp_safe_redirect(admin_url('admin.php?page=advgb_settings&tab=extra-blocks&save=success'));
exit;
}
} // General settings
elseif (isset($_POST['save_settings_block_features'])) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- we check nonce below
if (
! wp_verify_nonce(
sanitize_key($_POST['advgb_settings_block_features_nonce_field']),
'advgb_settings_block_features_nonce'
)
) {
return false;
}
$advgb_settings = get_option('advgb_settings');
$advgb_settings['block_features_settings'] = isset($_POST['block_features_settings']) ? map_deep($_POST['block_features_settings'], 'sanitize_text_field') : [];
$advgb_settings['setting_last_saved_role'] = isset($_POST['setting_last_saved_role']) ? sanitize_key($_POST['setting_last_saved_role']) : '';
update_option('advgb_settings', $advgb_settings);
if (isset($_REQUEST['_wp_http_referer'])) {
wp_safe_redirect(admin_url('admin.php?page=advgb_settings&tab=block-features&save=success'));
exit;
}
} // Maps settings
elseif (isset($_POST['save_settings_maps'])) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- we check nonce below
if (
! wp_verify_nonce(
sanitize_key($_POST['advgb_settings_maps_nonce_field']),
'advgb_settings_maps_nonce'
)
) {
return false;
}
$advgb_settings = get_option('advgb_settings');
$advgb_settings['google_api_key'] = sanitize_text_field($_POST['google_api_key']);
update_option('advgb_settings', $advgb_settings);
if (isset($_REQUEST['_wp_http_referer'])) {
wp_safe_redirect(admin_url('admin.php?page=advgb_settings&tab=extra-blocks&subtab=maps&save=success'));
exit;
}
} // Email & forms settings
elseif (isset($_POST['save_email_config'])) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- we check nonce below
if (! isset($_POST['advgb_email_config_nonce_field'])) {
return false;
}
if (
! wp_verify_nonce(
sanitize_key($_POST['advgb_email_config_nonce_field']),
'advgb_email_config_nonce'
)
) {
return false;
}
$save_config = [];
$save_config['contact_form_sender_name'] = sanitize_text_field($_POST['contact_form_sender_name']);
$save_config['contact_form_sender_email'] = sanitize_email($_POST['contact_form_sender_email']);
$save_config['contact_form_email_title'] = sanitize_text_field($_POST['contact_form_email_title']);
$save_config['contact_form_email_receiver'] = sanitize_email($_POST['contact_form_email_receiver']);
update_option('advgb_email_sender', $save_config);
if (isset($_REQUEST['_wp_http_referer'])) {
wp_safe_redirect(
admin_url('admin.php?page=advgb_settings&tab=extra-blocks&subtab=forms&save=success')
);
exit; // @TODO - Do we really need this?
}
} // reCAPTCHA settings
elseif (isset($_POST['save_recaptcha_config'])) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- we check nonce below
if (! isset($_POST['advgb_captcha_nonce_field'])) {
return false;
}
if (
! wp_verify_nonce(
sanitize_key($_POST['advgb_captcha_nonce_field']),
'advgb_captcha_nonce'
)
) {
return false;
}
$save_config = [];
if (isset($_POST['recaptcha_enable'])) {
$save_config['recaptcha_enable'] = 1;
} else {
$save_config['recaptcha_enable'] = 0;
}
$save_config['recaptcha_site_key'] = sanitize_text_field($_POST['recaptcha_site_key']);
$save_config['recaptcha_secret_key'] = sanitize_text_field($_POST['recaptcha_secret_key']);
$save_config['recaptcha_language'] = sanitize_text_field($_POST['recaptcha_language']);
$save_config['recaptcha_theme'] = sanitize_text_field($_POST['recaptcha_theme']);
update_option('advgb_recaptcha_config', $save_config);
if (isset($_REQUEST['_wp_http_referer'])) {
wp_safe_redirect(
admin_url('admin.php?page=advgb_settings&tab=extra-blocks&subtab=recaptcha&save=success')
);
exit;
}
} // Data export
elseif (isset($_POST['block_data_export'])) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- we check nonce below
if (
! wp_verify_nonce(
sanitize_key($_POST['advgb_export_data_nonce_field']),
'advgb_export_data_nonce'
)
) {
return false;
}
$postValue = sanitize_text_field($_POST['block_data_export']);
$postValue = explode('.', $postValue);
$dataExport = $postValue[0];
$dataType = $postValue[1];
if ($dataExport === 'contact_form') {
$dataSaved = get_option('advgb_contacts_saved');
if (! is_array($dataSaved) || ! $dataSaved) {
return false;
}
return $this->downloadContactFormData( $dataSaved, $dataType );
} elseif ($dataExport === 'newsletter') {
$dataSaved = get_option('advgb_newsletter_saved');
if (! is_array($dataSaved) || ! $dataSaved) {
return false;
}
return $this->downloadNewsletterData( $dataSaved, $dataType );
}
return false;
} // Post Notes settings
elseif (isset($_POST['save_settings_post_notes'])) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- we check nonce below
if (
! wp_verify_nonce(
sanitize_key($_POST['advgb_settings_post_notes_nonce_field']),
'advgb_settings_post_notes_nonce'
)
) {
return false;
}
$advgb_settings = get_option('advgb_settings');
$advgb_settings['post_notes_block_toolbar'] = isset($_POST['post_notes_block_toolbar']) ? 1 : 0;
update_option('advgb_settings', $advgb_settings);
if (isset($_REQUEST['_wp_http_referer'])) {
wp_safe_redirect(admin_url('admin.php?page=advgb_settings&tab=post-notes&save=success'));
exit;
}
} // Legacy blocks settings
elseif (isset($_POST['save_settings_legacy'])) { // phpcs:ignore WordPress.Security.NonceVerification.Missing -- we check nonce below
if (
! wp_verify_nonce(
sanitize_key($_POST['advgb_settings_legacy_nonce_field']),
'advgb_settings_legacy_nonce'
)
) {
return false;
}
$submitted = isset($_POST['advgb_legacy_blocks']) && is_array($_POST['advgb_legacy_blocks'])
? array_map('sanitize_text_field', wp_unslash($_POST['advgb_legacy_blocks']))
: [];
$legacy_state = [];
foreach (array_keys(self::legacyBlocksMap()) as $slug) {
$legacy_state[$slug] = ! empty($submitted[$slug]) ? 1 : 0;
}
update_option('advgb_legacy_blocks', $legacy_state, false);
update_option('advgb_legacy_settings_migrated', 1, false);
// Legacy features
$advgb_settings = get_option('advgb_settings');
$advgb_settings = is_array($advgb_settings) ? $advgb_settings : [];
$advgb_settings['enable_core_blocks_features'] = isset($_POST['enable_core_blocks_features']) ? 1 : 0;
$advgb_settings['gallery_lightbox'] = isset($_POST['gallery_lightbox']) ? 1 : 0;
$advgb_settings['gallery_lightbox_caption'] = (int) $_POST['gallery_lightbox_caption'];
update_option('advgb_settings', $advgb_settings);
if (isset($_REQUEST['_wp_http_referer'])) {
wp_safe_redirect(admin_url('admin.php?page=advgb_settings&tab=legacy&save=success'));
exit;
}
}
return false;
}
/**
* Save block access page data
* Name is build in registerMainMenu() > $function_name
*
* @return boolean true on success, false on failure
* @since 3.0.0
*/
public function advgb_block_access_save_page()
{
if (! current_user_can('activate_plugins')) {
return false;
}
if ( isset( $_POST['advgb_block_access_save'] ) ) {
// Save Block Access for single role
$this->blocksFeatureSave(
'access',
'advgb_blocks_user_roles'
);
return true;
}
if ( isset( $_POST['advgb_block_access_save_all_roles'] ) ) {
// Save Block Access for all roles
$this->blocksFeatureSaveAllRoles(
'access',
'advgb_blocks_user_roles'
);
return true;
}
return false;
}
/**
* Save block controls page data
* Name is build in registerMainMenu() > $function_name
*
* @return boolean true on success, false on failure
* @since 3.1.0
*/
public function advgb_block_controls_save_page()
{
PublishPress\Blocks\Controls::save();
}
/**
* Load Block Styles in in frontend
*
* @return void
*/
public function loadCustomStylesFrontend()
{
$post_content = get_the_content();
$this->getCustomStylesContent($post_content);
// @TODO Check for Custom styles in use in Widgets
}
/**
* Get Block Styles in use in Contennt
*
* @return string
*/
public function getCustomStylesContent($content) {
if (!Utilities::settingIsEnabled('enable_custom_styles')) {
return '';
}
$css = $this->getCustomStylesContentCss();
if (!empty($css)) {
echo '';
}
}
/**
* Get custom block style CSS for frontend or editor iframe output.
*
* @return string
*/
private function getCustomStylesContentCss()
{
$custom_styles = get_option('advgb_custom_styles', AdvancedGutenbergBlockStyles::$default_custom_styles);
if (!is_array($custom_styles)) {
return '';
}
$css = '';
foreach ($custom_styles as $styles) {
if (empty($styles['name'])) {
continue;
}
if (isset($styles['generated_css']) && !empty($styles['generated_css'])) {
$css .= AdvancedGutenbergBlockStyles::add_block_style_aliases(
$styles['generated_css'],
$styles['name']
);
} elseif (isset($styles['css'])) {
// Fallback: generate CSS on the fly for legacy data
$css .= AdvancedGutenbergBlockStyles::generate_final_css($styles['css'], $styles['name']);
}
}
return $css;
}
private function getAdminEditorCustomStyles() {
$content = '';
$custom_styles = get_option('advgb_custom_styles', AdvancedGutenbergBlockStyles::$default_custom_styles);
if (is_array($custom_styles)) {
foreach ($custom_styles as $styles) {
// post editor
$class_name = '.block-editor-writing-flow .' . $styles['name'];
$class_name .= ', .block-editor-writing-flow .is-style-' . $styles['name'];
// resusable block editor
$class_name .= ', .editor-visual-editor iframe .' . $styles['name'];
$class_name .= ', .editor-visual-editor iframe .is-style-' . $styles['name'];
$class_name .= ', .block-editor-iframe__body .' . $styles['name'];
$class_name .= ', .block-editor-iframe__body .is-style-' . $styles['name'];
if (isset($styles['generated_css']) && !empty($styles['generated_css'])) {
$generated_css = AdvancedGutenbergBlockStyles::add_block_style_aliases(
$styles['generated_css'],
$styles['name']
);
$admin_css = preg_replace(
'/(? in admin
*
* @return void
*/
public function loadCustomStylesAdmin() {
if (!Utilities::settingIsEnabled('enable_custom_styles')) {
return;
}
$custom_styles = $this->getAdminEditorCustomStyles();
if (!empty($custom_styles)) {
echo '';
}
}
/**
* Load custom block style CSS inside the block editor iframe.
*
* @param array $settings Block editor settings.
*
* @return array
*/
public function injectCustomStylesIntoEditorIframe($settings)
{
if (!Utilities::settingIsEnabled('enable_custom_styles')) {
return $settings;
}
$custom_styles = $this->getCustomStylesContentCss();
if (empty($custom_styles)) {
return $settings;
}
if (!isset($settings['styles']) || !is_array($settings['styles'])) {
$settings['styles'] = [];
}
$settings['styles'][] = [
'css' => AdvancedGutenbergBlockStyles::sanitize_css($custom_styles),
];
return $settings;
}
public function addCustomStylesToReusableBlocks() {
if (!Utilities::settingIsEnabled('enable_custom_styles')) {
return;
}
add_filter('block_editor_settings_all', array( $this, 'injectCustomStylesIntoEditorIframe' ), 20);
}
/**
* Register frontend custom style class restoration.
*
* @return void
*/
public function registerCustomStyleFrontendFilter()
{
if (is_admin() || !Utilities::settingIsEnabled('enable_custom_styles')) {
return;
}
add_filter('render_block', array( $this, 'applyCustomStyleClassOnFrontend' ), 10, 2);
}
/**
* Restore the custom style class when a block save function drops extraProps.className.
*
* @param string $block_content Rendered block HTML.
* @param array $block Parsed block data.
*
* @return string
*/
public function applyCustomStyleClassOnFrontend($block_content, $block)
{
if (
empty($block_content)
|| empty($block['attrs']['customStyle'])
|| !is_string($block['attrs']['customStyle'])
) {
return $block_content;
}
$custom_style = sanitize_html_class($block['attrs']['customStyle']);
if (empty($custom_style)) {
return $block_content;
}
if (class_exists('WP_HTML_Tag_Processor')) {
$processor = new WP_HTML_Tag_Processor($block_content);
if (
method_exists($processor, 'add_class')
&& method_exists($processor, 'get_updated_html')
&& $processor->next_tag()
) {
if (method_exists($processor, 'has_class') && $processor->has_class($custom_style)) {
return $block_content;
}
$processor->add_class($custom_style);
return $processor->get_updated_html();
}
}
return $this->addClassToFirstHtmlTag($block_content, $custom_style);
}
/**
* Add a class to the first HTML tag for WordPress versions without Tag Processor support.
*
* @param string $html HTML content.
* @param string $class_name Class to add.
*
* @return string
*/
private function addClassToFirstHtmlTag($html, $class_name)
{
return preg_replace_callback('/<([a-z][a-z0-9:-]*)(\s[^>]*)?>/i', function ($matches) use ($class_name) {
$tag = $matches[0];
if (preg_match('/\sclass=(["\'])(.*?)\1/i', $tag)) {
return preg_replace_callback('/\sclass=(["\'])(.*?)\1/i', function ($class_matches) use ($class_name) {
$classes = trim($class_matches[2]);
if (preg_match('/\b' . preg_quote($class_name, '/') . '\b/', $classes)) {
return $class_matches[0];
}
return ' class=' . $class_matches[1] . esc_attr(trim($classes . ' ' . $class_name)) . $class_matches[1];
}, $tag, 1);
}
return preg_replace('/(\s*\/?)>$/', ' class="' . esc_attr($class_name) . '"$1>', $tag);
}, $html, 1);
}
/**
* Set blocks spacing in editor
*
* @return void
*/
public function setBlocksSpacingAdmin()
{
$saved_settings = get_option('advgb_settings');
if (isset($saved_settings['enable_blocks_spacing']) && $saved_settings['enable_blocks_spacing']) {
$blocks_spacing = isset($saved_settings['blocks_spacing']) ? $saved_settings['blocks_spacing'] : 0;
echo '';
}
}
/**
* Save block [feature] by user role - e.g. access feature means enable/disable blocks in editor
*
* @param string $feature Feature name in lowercase - e.g. 'access'
* @param string $option Database option to update - e.g. 'advgb_blocks_user_roles'
*
* @return void
* @since 3.0.0
*/
public function blocksFeatureSave($feature, $option)
{
// Check nonce field exist
if (! isset($_POST[ 'advgb_block_' . $feature . '_nonce_field' ])) {
return false;
}
// Verify nonce
if (
! wp_verify_nonce(
sanitize_key(wp_unslash($_POST[ 'advgb_block_' . $feature . '_nonce_field' ])),
'advgb_nonce'
)
) {
return false;
}
if (! current_user_can('administrator')) {
return false;
}
$user_role = !empty($_POST['user_role']) ? sanitize_text_field(wp_unslash($_POST['user_role'])) : '';
if (
isset($_POST['blocks_list'])
&& isset($_POST['active_blocks'])
&& is_array($_POST['active_blocks'])
&& isset($_POST['user_role'])
&& ! empty($_POST['user_role'])
) {
// @TODO - Check if user role exists - https://gist.github.com/hlashbrooke/8f901da7c6f0d107add7
$blocks_list = array_map(
'sanitize_text_field',
json_decode(wp_unslash($_POST['blocks_list'])) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
);
$active_blocks = array_map('sanitize_text_field', wp_unslash($_POST['active_blocks']));
$inactive_blocks = array_values(array_diff($blocks_list, $active_blocks));
// Save feature by role
$block_feature_by_role = get_option($option);
$block_feature_by_role = is_array($block_feature_by_role) ? $block_feature_by_role : [];
$block_feature_by_role[ $user_role ]['active_blocks'] = isset($active_blocks) ? $active_blocks : '';
$block_feature_by_role[ $user_role ]['inactive_blocks'] = isset($inactive_blocks) ? $inactive_blocks : '';
update_option($option, $block_feature_by_role, false);
// Redirect with success message
wp_safe_redirect(
add_query_arg(
[
'user_role' => $user_role,
'save' => 'success'
],
wp_get_referer()
)
);
exit;
} else {
// Redirect with error message / Nothing was saved
wp_safe_redirect(
add_query_arg(
[
'user_role' => $user_role,
'save' => 'error'
],
wp_get_referer()
)
);
exit;
}
}
/**
* Save blocks feature for all roles
*
* @param string $feature Feature name in lowercase
* @param string $database_option Database option to update
*
* @return void
* @since 3.5.2
*/
public function blocksFeatureSaveAllRoles( $feature, $database_option ) {
// Check nonce field exist
if (! isset($_POST[ 'advgb_block_' . $feature . '_nonce_field' ])) {
return false;
}
// Verify nonce
if (
! wp_verify_nonce(
sanitize_key(wp_unslash($_POST[ 'advgb_block_' . $feature . '_nonce_field' ])),
'advgb_nonce'
)
) {
return false;
}
if (! current_user_can('administrator')) {
return false;
}
$user_role = !empty($_POST['user_role']) ? sanitize_text_field(wp_unslash($_POST['user_role'])) : '';
if (
isset($_POST['blocks_list'])
&& isset($_POST['active_blocks'])
&& is_array($_POST['active_blocks'])
&& isset($_POST['user_role'])
&& ! empty($_POST['user_role'])
) {
$blocks_list = array_map(
'sanitize_text_field',
json_decode(wp_unslash($_POST['blocks_list'])) // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
);
$active_blocks = isset( $_POST['active_blocks'] ) ? array_map('sanitize_text_field', wp_unslash($_POST['active_blocks'])) : [];
$inactive_blocks = array_values(array_diff($blocks_list, $active_blocks));
$all_roles = wp_roles()->roles;
$blocks_user_roles = get_option( $database_option, array() );
foreach ( $all_roles as $role_key => $role_data ) {
$blocks_user_roles[ $role_key ] = array(
'active_blocks' => $active_blocks,
'inactive_blocks' => $inactive_blocks,
);
}
// Save to database
update_option( $database_option, $blocks_user_roles, false );
// Redirect with success message
wp_safe_redirect(
add_query_arg(
[
'user_role' => $user_role,
'save' => 'success'
],
wp_get_referer()
)
);
exit;
} else {
// Redirect with error message / Nothing was saved
wp_safe_redirect(
add_query_arg(
[
'user_role' => $user_role,
'save' => 'error'
],
wp_get_referer()
)
);
exit;
}
}
/**
* Return current select user role
*
* @return string
* @since 2.14.1
*
*/
public function advgbBlocksFeatureCUserRole()
{
if (isset($_REQUEST['user_role']) && ! empty($_REQUEST['user_role'])) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- advgb_nonce in place
return sanitize_text_field($_REQUEST['user_role']); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- advgb_nonce in place
} else {
return 'administrator';
}
}
public function blocksUsageData()
{
// 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());
}
// 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
*/
$saved_blocks = [];
$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
);
$saved_blocks = array_merge(
$saved_blocks,
(array) get_option('advgb_blocks_list')
);
wp_enqueue_style('wp-components');
wp_enqueue_style(
'advgb_block_usage_styles',
ADVANCED_GUTENBERG_PLUGIN_DIR_URL . 'assets/css/block-usage.css',
['wp-components'],
ADVANCED_GUTENBERG_VERSION
);
wp_enqueue_script(
'advgb_block_usage',
ADVANCED_GUTENBERG_PLUGIN_DIR_URL . 'assets/pages/block-usage.js',
['wp-element', 'wp-components', 'wp-api-fetch', 'wp-i18n'],
ADVANCED_GUTENBERG_VERSION,
true
);
$localize_data = [
'ajaxUrl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('block_usage_nonce'),
'blockCategories' => $blockCategories,
'saved_blocks' => $saved_blocks,
'postTypes' => $this->get_editor_post_types(),
'isProActive' => Utilities::isProActive(),
'promoLink' => ADVANCED_GUTENBERG_UPGRADE_LINK,
'upgradeText' => __( 'Upgrade to Pro', 'advanced-gutenberg' ),
'proText' => __( 'Pro', 'advanced-gutenberg' ),
'initialData' => [
'usage' => [],
'lastScanDate' => '',
'lastSelectedBlock' => '',
'posts' => []
],
'currentUser' => [
'canEditPosts' => current_user_can('edit_posts')
]
];
wp_localize_script(
'advgb_block_usage',
'advgb_block_usage_data',
$localize_data
);
wp_set_script_translations(
'advgb_block_usage',
'advanced-gutenberg',
plugin_dir_path(ADVANCED_GUTENBERG_PLUGIN) . 'languages'
);
}
public function get_editor_post_types()
{
$post_types = get_post_types(['show_in_rest' => true], 'objects');
$result = [];
foreach ($post_types as $post_type) {
if (post_type_supports($post_type->name, 'editor')) {
$result[$post_type->name] = $post_type->labels->singular_name;
}
}
return $result;
}
/**
* Access current user blocks and saved blocks to build 2 javascript objects.
* 'advgbCUserRole' object for current user role from form dropdown
* 'advgb_blocks_list' object with all the saved blocks in 'advgb_blocks_list' option
*
* @param string $feature The object name to store the active/inactive blocks - e.g. 'access' => advgbCUserRole.access
* @param string $option Database option to check current user role's active/inactive blocks - e.g. 'advgb_blocks_user_roles'
* @param array $exclude Blocks to exclude from appearing in the feature form (is different to inactive_blocks!). e.g. ['core/paragraph','core/list']
*
* @return void
* @since 3.0.0
*/
public function blocksFeatureData($feature, $option, $exclude = [])
{
// Build blocks form and add filters functions
wp_add_inline_script(
'advgb_main_js',
"window.addEventListener('load', function () {
advgbGetBlocksFeature(
advgbCUserRole.{$feature}.inactive_blocks,
'#advgb_block_{$feature}_nonce_field',
'advgb_block_{$feature}',
" . wp_json_encode($exclude) . "
);
});"
);
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
);
// Current role
$current_user_role = $this->advgbBlocksFeatureCUserRole();
// Active and inactive blocks for current user role
$block_feature_by_role = get_option($option);
if (
! empty($block_feature_by_role[ $current_user_role ])
&& is_array($block_feature_by_role[ $current_user_role ])
) {
wp_localize_script(
'wp-blocks',
'advgbCUserRole',
[
'user_role' => $current_user_role,
$feature => [
'active_blocks' => $block_feature_by_role[ $current_user_role ]['active_blocks'],
'inactive_blocks' => $block_feature_by_role[ $current_user_role ]['inactive_blocks']
]
]
);
} else {
// Nothing saved in database for current user role. Set empty (access to all blocks)
wp_localize_script(
'wp-blocks',
'advgbCUserRole',
[
'user_role' => $current_user_role,
$feature => [
'active_blocks' => [],
'inactive_blocks' => []
]
]
);
}
}
/**
* Get the blocks feature form - e.g. Block Access
*
* @param string $feature Feature name in lowercase - e.g. 'access'
* @param string $label Name of the feature with text-domain - e.g. __('Block Permissions', 'advanced-gutenberg')
*
* @return void
* @since 3.0.0
*/
public function blocksFeatureForm($feature, $label)
{
// Current role
$current_user_role = $this->advgbBlocksFeatureCUserRole();
?>
screenDescription(
__('Choose which blocks are available to each user role, including default WordPress blocks.', 'advanced-gutenberg')
);
?>
screenDescription(
__('Scan your site to see where blocks are used across your posts.', 'advanced-gutenberg')
);
?>
downloadContactFormData( $dataSaved, $dataType );
} elseif ($dataExport === 'newsletter') {
$dataSaved = get_option('advgb_newsletter_saved');
if (! is_array($dataSaved) || ! $dataSaved) {
return false;
}
return $this->downloadNewsletterData( $dataSaved, $dataType );
}
return false;
}
/**
* Download saved contact form entries.
*
* @param array $data_saved Saved data.
* @param string $data_type Export type.
*
* @return false
*/
private function downloadContactFormData( $data_saved, $data_type )
{
switch ( $data_type ) {
case 'csv':
$rows = array();
$int = 1;
foreach ( $data_saved as $data_val ) {
$rows[] = array(
$int,
$this->getSavedFormField( $data_val, 'date' ),
$this->getSavedFormField( $data_val, 'name' ),
$this->getSavedFormField( $data_val, 'email' ),
$this->getSavedFormField( $data_val, 'msg' ),
);
$int++;
}
$this->sendCsvDownload(
'advgb_contact_form-' . date( 'm-d-Y' ) . '.csv',
array( '#', 'Date', 'Name', 'Email', 'Message' ),
$rows
);
break;
case 'json':
$this->sendJsonDownload( 'advgb_contact_form-' . date( 'm-d-Y' ) . '.json', $data_saved );
break;
}
return false;
}
/**
* Download saved newsletter entries.
*
* @param array $data_saved Saved data.
* @param string $data_type Export type.
*
* @return false
*/
private function downloadNewsletterData( $data_saved, $data_type )
{
switch ( $data_type ) {
case 'csv':
$rows = array();
$int = 1;
foreach ( $data_saved as $data_val ) {
$rows[] = array(
$int,
$this->getSavedFormField( $data_val, 'date' ),
$this->getSavedFormField( $data_val, 'fname' ),
$this->getSavedFormField( $data_val, 'lname' ),
$this->getSavedFormField( $data_val, 'email' ),
);
$int++;
}
$this->sendCsvDownload(
'advgb_newsletter-' . date( 'm-d-Y' ) . '.csv',
array( '#', 'Date', 'First Name', 'Last Name', 'Email' ),
$rows
);
break;
case 'json':
$this->sendJsonDownload( 'advgb_newsletter-' . date( 'm-d-Y' ) . '.json', $data_saved );
break;
}
return false;
}
/**
* Send a CSV download using fputcsv.
*
* @param string $filename Download filename.
* @param array $headers CSV headers.
* @param array $rows CSV rows.
*
* @return void
*/
private function sendCsvDownload( $filename, $headers, $rows )
{
header( 'Content-Type: text/csv; charset=utf-8' );
header( 'Content-Disposition: attachment; filename=' . $filename );
header( 'Pragma: no-cache' );
header( 'Expires: 0' );
$output = fopen( 'php://output', 'w' ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fopen
if ( $output === false ) {
exit;
}
fputcsv( $output, $headers, ',', '"', '\\' );
foreach ( $rows as $row ) {
fputcsv( $output, $this->neutralizeCsvRow( $row ), ',', '"', '\\' );
}
fclose( $output ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_fclose
exit;
}
/**
* Send a JSON download.
*
* @param string $filename Download filename.
* @param array $data Export data.
*
* @return void
*/
private function sendJsonDownload( $filename, $data )
{
header( 'Content-Type: application/json; charset=utf-8' );
header( 'Content-Disposition: attachment; filename=' . $filename );
header( 'Pragma: no-cache' );
header( 'Expires: 0' );
echo wp_json_encode( $data ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
exit;
}
/**
* Get a saved form field without raising notices for legacy malformed rows.
*
* @param mixed $entry Saved entry.
* @param string $field Field name.
*
* @return string
*/
private function getSavedFormField( $entry, $field )
{
if ( ! is_array( $entry ) || ! isset( $entry[ $field ] ) ) {
return '';
}
return $entry[ $field ];
}
/**
* Neutralize each CSV cell that spreadsheet apps may evaluate as a formula.
*
* @param array $row CSV row.
*
* @return array
*/
private function neutralizeCsvRow( $row )
{
$neutralized = array();
foreach ( $row as $value ) {
$neutralized[] = $this->neutralizeCsvCell( $value );
}
return $neutralized;
}
/**
* Prefix spreadsheet formula-leading CSV cells with a single quote.
*
* @param mixed $value CSV cell value.
*
* @return string
*/
private function neutralizeCsvCell( $value )
{
if ( is_array( $value ) || is_object( $value ) ) {
$value = wp_json_encode( $value );
$value = $value === false ? '' : $value;
}
$value = (string) $value;
if ( $value === '' ) {
return '';
}
if ( preg_match( '/^[\t\r\n]/', $value ) || preg_match( '/^[\s]*[=+\-@]/', $value ) ) {
return "'" . $value;
}
return $value;
}
/**
* Retrieve the active and inactive blocks for users regard to PublishPress Blocks access
*
* @return array
*/
public function getUserRoleBlocksData()
{
$user_roles_data = [
'blocks' => false,
'user_block_features' => false
];
$blocks_data_completed = false;
// Get user role
if (is_multisite() && is_super_admin()) {
/* Since a super admin in a child site from a multiste network doesn't have roles
* so we set 'administrator' as role */
$current_user_role = 'administrator';
} else {
$current_user = wp_get_current_user();
if (empty($current_user->roles) || empty($current_user->roles[0])) {
return $user_roles_data;
}
if (count($current_user->roles) === 1) {
// User has 1 user role
$current_user_role = $current_user->roles[0]; // string
} elseif (count($current_user->roles) > 1) {
// User has 2 roles or more
$current_user_role = $current_user->roles; // array
} else {
// Nothing to do here - no user roles
return $user_roles_data;
}
}
// user block features
$block_features_settings = $this->getOptionSetting('advgb_settings', 'block_features_settings', 'array', []);
$block_features_settings_data = [];
if (!empty($block_features_settings)) {
foreach ($current_user->roles as $user_role) {
if (isset($block_features_settings[$user_role])) {
$role_settings = $block_features_settings[$user_role];
// Merge disable_block_adding
if (!empty($role_settings["disable_block_adding"])) {
$block_features_settings_data["disable_block_adding"] = $role_settings["disable_block_adding"];
}
// Merge disable_pattern_directory
if (!empty($role_settings["disable_pattern_directory"])) {
$block_features_settings_data["disable_pattern_directory"] = $role_settings["disable_pattern_directory"];
}
// Merge disable_openverse
if (!empty($role_settings["disable_openverse"])) {
$block_features_settings_data["disable_openverse"] = $role_settings["disable_openverse"];
}
}
}
}
$user_roles_data['user_block_features'] = $block_features_settings_data;
if (!Utilities::settingIsEnabled('enable_block_access')) {
$blocks_data_completed = true;
}
if (!$blocks_data_completed) {
// All saved blocks (even the ones possibly not detected by Block Permissions)
$all_blocks = get_option('advgb_blocks_list');
// Get the array from advgb_blocks_user_roles with all the Block permissions roles
$advgb_blocks_user_roles = ! empty(get_option('advgb_blocks_user_roles')) ? get_option('advgb_blocks_user_roles') : [];
// User has 1 user role - string
if (gettype($current_user_role) === 'string') {
// Get the array from advgb_blocks_user_roles option that match current user role
$advgb_blocks_user_roles = array_key_exists(
$current_user_role,
$advgb_blocks_user_roles
) ? (array) $advgb_blocks_user_roles[ $current_user_role ] : [];
} else {
// User has 2 roles or more - array
$active_r_ = $inactive_r_ = [];
foreach ($current_user_role as $cur_) {
// Get the array from advgb_blocks_user_roles option that match current user role
if (array_key_exists($cur_, $advgb_blocks_user_roles)) {
$active_r_ = array_merge(
$active_r_,
(array) $advgb_blocks_user_roles[ $cur_ ]['active_blocks']
);
$inactive_r_ = array_merge(
$inactive_r_,
(array) $advgb_blocks_user_roles[ $cur_ ]['inactive_blocks']
);
}
}
// Remove duplicates
$active_r_ = array_unique($active_r_);
$inactive_r_ = array_unique($inactive_r_);
/* Remove active blocks if also exists as inactive in any of the assigned roles;
* only blocks activated in all the assigned roles will remain as active.
*
* Example:
*
* User has "Lorem" and "Ipsum" roles assigned
*
* "Dolor block" is enabled for "Lorem" role but disabled for "Ipsum" role,
* then "Dolor block" is set as inactive [X].
*
* "Dolor block" is enabled for "Lorem" and also for "Ipsum" role,
* then "Dolor block" is set as active [✓].
* */
$active_r_ = array_diff($active_r_, $inactive_r_);
$advgb_blocks_user_roles = [
'active_blocks' => array_values($active_r_),
'inactive_blocks' => array_values($inactive_r_)
];
}
if (is_array($advgb_blocks_user_roles) && count($advgb_blocks_user_roles) > 0) {
if (
is_array($advgb_blocks_user_roles['active_blocks']) &&
is_array($advgb_blocks_user_roles['inactive_blocks'])
) {
// Include the blocks stored in advgb_blocks_list option but not detected by Block Access
foreach ($all_blocks as $one_block) {
if (
! in_array($one_block['name'], $advgb_blocks_user_roles['active_blocks']) &&
! in_array($one_block['name'], $advgb_blocks_user_roles['inactive_blocks'])
) {
array_push($advgb_blocks_user_roles['active_blocks'], $one_block['name']);
}
}
}
$user_roles_data['blocks'] = $advgb_blocks_user_roles;
$blocks_data_completed = true;
}
}
if (!$blocks_data_completed) {
// If advgb_blocks_user_roles option doesn't exists, then allow all blocks
if (! is_array($all_blocks)) {
$all_blocks = array();
} else {
// Extract block name only and skip the other properties (title, icon, category)
foreach ($all_blocks as $block_key => $block_value) {
$all_blocks[ $block_key ] = $all_blocks[ $block_key ]['name'];
}
// Include Legacy Widget for execution; this is not saved in db
array_push($all_blocks, 'core/legacy-widget');
// Remove duplicated just in case
$all_blocks = array_unique($all_blocks);
}
/* Make sure specific blocks are included as active - Since 2.11.6
* core/widget-group added since 3.1.4.2
* core/legacy-widget removed since 3.1.5
*/
$include_blocks = [
'core/widget-group'
];
foreach ($include_blocks as $item) {
if (! in_array($item, $all_blocks)) {
array_push(
$all_blocks,
$item
);
}
}
$user_roles_data['blocks'] = [
'active_blocks' => $all_blocks,
'inactive_blocks' => array()
];
}
return $user_roles_data;
}
/**
* Register block config page
*
* @return void
*/
public function registerBlockConfigPage()
{
$advgb_block = array(
'accordions',
'button',
'image',
'list',
'table',
'video',
'contact-form',
'container',
'count-up',
'images-slider',
'map',
'newsletter',
'recent-posts',
'social-links',
'summary',
'adv-tabs',
'testimonial',
'woo-products',
'columns',
'column',
'login-form',
'search-bar',
'icon',
'infobox'
);
foreach ($advgb_block as $block) {
add_submenu_page(
'admin.php?',
__('Block Config', 'advanced-gutenberg'),
__('Block Config', 'advanced-gutenberg'),
'manage_options',
'advgb-' . $block,
array( $this, 'loadBlockConfigView' )
);
}
}
/**
* Function to get and load the block config view
*
* @param string $block View to load
*
* @return void
*/
public function loadBlockConfigView($block = '')
{
if (! $block) {
$block = sanitize_text_field($_GET['page']); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- view only
}
$core_styles = [ 'common', 'buttons', 'forms', 'edit' ];
foreach ($core_styles as $style) {
wp_enqueue_style($style);
}
wp_enqueue_style(
'minicolors_css',
ADVANCED_GUTENBERG_PLUGIN_DIR_URL . 'assets/css/jquery.minicolors.css'
);
wp_enqueue_style(
'block_config_css',
ADVANCED_GUTENBERG_PLUGIN_DIR_URL . 'assets/css/block-config.css'
);
$loadingImage = ADVANCED_GUTENBERG_PLUGIN_DIR_URL . 'assets/images/loading.gif';
wp_add_inline_style(
'block_config_css',
'#advgb-loading-screen-image {background-image: url(' . $loadingImage . ')}'
);
wp_enqueue_script(
'minicolors_js',
ADVANCED_GUTENBERG_PLUGIN_DIR_URL . 'assets/js/jquery.minicolors.min.js',
array(),
ADVANCED_GUTENBERG_VERSION
);
wp_enqueue_script(
'block_config_js',
ADVANCED_GUTENBERG_PLUGIN_DIR_URL . 'assets/js/block-config.js',
array( 'jquery' ),
ADVANCED_GUTENBERG_VERSION
);
$blocks_settings_list = PublishPress\Blocks\Configuration::defaultConfig();
$advgb_blocks_default_config = get_option('advgb_blocks_default_config');
$current_block = $block;
if (! isset($blocks_settings_list[ $current_block ])) {
wp_die(esc_html(__(
'Default configuration for this block is not available.',
'advanced-gutenberg'
)));
return;
}
$current_block_settings = $blocks_settings_list[ $current_block ];
$current_block_settings_value = array();
if ($advgb_blocks_default_config !== false) {
if (isset($advgb_blocks_default_config[ $current_block ])) {
$current_block_settings_value = $advgb_blocks_default_config[ $current_block ];
}
}
require_once(plugin_dir_path(__FILE__) . 'pages/block-settings/block-config.php');
}
/**
* Function to get and load the page
*
* @param string $page Page to load
*
* @return void
* @since 3.0.0
*/
public function loadPage($page)
{
if (file_exists(plugin_dir_path(__FILE__) . 'pages/' . $page . '.php')) {
include_once(plugin_dir_path(__FILE__) . 'pages/' . $page . '.php');
}
}
/**
* Function to get and load a tab inside a page
*
* @param string $page Page folder where tab files are stored
* @param string $tab Tab to load
*
* @return void
* @since 3.0.0
*/
public function loadPageTab($page, $tab, $default = 'general')
{
$valid_tab = true;
if (!preg_match('/^[a-zA-Z0-9_-]+$/', $tab)) {
$valid_tab = false;
}
if ($valid_tab && file_exists(plugin_dir_path(__FILE__) . 'pages/' . $page . '/' . $tab . '.php')) {
include_once(plugin_dir_path(__FILE__) . 'pages/' . $page . '/' . $tab . '.php');
} elseif (
$valid_tab &&
defined('ADVANCED_GUTENBERG_BASE_PRO_PATH') &&
file_exists(ADVANCED_GUTENBERG_BASE_PRO_PATH . '/incl/pages/' . $page . '/' . $tab . '.php')
) {
// Maybe is a pro page? Look in another location
include_once(ADVANCED_GUTENBERG_BASE_PRO_PATH . '/incl/pages/' . $page . '/' . $tab . '.php');
} else {
wp_add_inline_style(
'advgb_admin_styles',
'.publishpress-admin .nav-tab-wrapper { display: none !important; }'
);
printf(
wp_kses_post(__('Page not found. Go to %sDashboard%s', 'advanced-gutenberg')),
'',
''
);
}
}
/**
* Output a short explanation for an admin screen.
*
* @param string $description Screen description.
*
* @return void
*/
public function screenDescription($description)
{
if (empty($description)) {
return;
}
?>
screenDescription(
__('Manage reusable blocks that can be inserted across your site.', 'advanced-gutenberg')
);
return $views;
}
/**
* Output a description for the Auto Insert Blocks list screen.
*
* @param array $views List table views.
*
* @return array
*/
public function autoInsertBlocksDescription($views)
{
$this->screenDescription(
__(
'Create rules to automatically insert reusable blocks into posts by position, category, tag, or other criteria.',
'advanced-gutenberg'
)
);
return $views;
}
/**
* Function to build tabs menu
*
* @param string $page Page slug. e.g. 'advgb_settings'
* @param string $active Active tab slug. e.g. 'recaptcha'
* @param array $tabs Tabs list
*
* @return string
* @since 3.0.0
*/
public function buildTabs($page, $active, $tabs)
{
if (! is_array($tabs) || empty($tabs)) {
return '';
}
$html = '';
return $html;
}
/**
* Function to load assets for post/page on front-end before gutenberg rendering
*
* @param array $block Block data
*
* @return array New block data
*/
public function contentPreRender($block)
{
// Search for needed blocks then add styles to it
$style = $this->addBlocksStyles($block);
// Empty fragments change WordPress layout wrapper detection without adding output.
// Keep asset loading above for core/gallery and nested PublishPress blocks.
if ($style === '') {
return $block;
}
/* Content Display block doesn't render styles
* as the rest of blocks as first level block (not as a child),
* so we add the inline CSS in head */
if (
$block['blockName'] === 'advgb/recent-posts'
&& ! empty($style)
) {
wp_add_inline_style(
'advgb_recent_posts_styles',
strip_tags($style)
);
} else {
// Rest of the blocks
array_push($block['innerContent'], $style);
}
return $block;
}
/**
* Recursive loop to find nested blocks and load their blocks's CSS and media files
*
* @param object $block Nested block
* @param string $style_html CSS Styles
* @param integer $level Nested block level
*
* @return string CSS Styles
* @since 2.6.1
*/
public function advgb_getNestedBlocksStyles($block, $level = 2, &$style_html = array())
{
if (isset($block['innerBlocks'])) {
foreach ($block['innerBlocks'] as $key => $inner_block) {
// Get styles
$new_style_html = $this->advgb_SetStylesForBlocks(
$inner_block['attrs'],
$inner_block['blockName']
);
// Add the styles to the array
$style_html[] = $new_style_html;
//echo str_repeat("--", $level) . $inner_block['blockName'] . ' [ ' . $level . ' ] ';
self::advgb_getNestedBlocksStyles($inner_block, $level + 1, $style_html);
}
}
$final_styles = $style_html;
if (! is_string($final_styles)) {
// Convert array to string
$final_styles = implode('', array_unique($style_html));
}
//echo '' . $final_styles . ''; // This output is correct!
return $final_styles;
}
/**
* @TODO Remove later
* Check to disable autop used to prevent unwanted paragraphs to blocks
*
* @param string $filter_name filter name; 'the_content' or 'widget_block_content'
*
* @return void
*/
public function checkToDisableWpautop($filter_name)
{
$saved_settings = false;
if (has_filter($filter_name, 'wpautop')) {
if (! $saved_settings) {
$saved_settings = get_option('advgb_settings');
}
if (! empty($saved_settings['disable_wpautop'])) {
remove_filter($filter_name, 'wpautop');
}
}
}
/**
* Add nonce to blocks with form tag
*
* @param string $content Post content
*
* @return string
*/
public function addNonceToFormBlocks($block)
{
$nonce_field = '';
$block = str_replace(
'
checkToDisableWpautop('the_content');
$this->setFrontendAssets($content);
$content = $this->groupStylesTag($content);
return $content;
}
/**
* Load assets for widgets on front-end after gutenberg rendering
*
* @param string $text Widget content
*
* @return string
*/
public function addFrontendWidgetAssets($text)
{
$this->checkToDisableWpautop('widget_block_content');
$this->setFrontendAssets($text);
$text = $this->groupStylesTag($text, false);
return $text;
}
/**
* Set frontend assets and styles for posts
*
* @param string $content Post content
*
* @return string
*/
public function setFrontendAssets($content)
{
wp_enqueue_script(
'jquery',
'https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js'
); // @TODO - Load locally
// Preview in Customizer > Widgets
if (isset($_GET['customize_theme'])) {
wp_enqueue_style('colorbox_style');
wp_enqueue_style('slick_style');
wp_enqueue_style('slick_theme_style');
wp_enqueue_script('colorbox_js');
wp_enqueue_script('slick_js');
wp_enqueue_script('advgb_masonry_js');
wp_enqueue_script(
'advgb_widgets_customizer_js',
ADVANCED_GUTENBERG_PLUGIN_DIR_URL . 'assets/js/widgets-customizer.js',
array(
'colorbox_js',
'slick_js',
'advgb_masonry_js'
),
ADVANCED_GUTENBERG_VERSION
);
// Pro
if (defined('ADVANCED_GUTENBERG_PRO_LOADED') && Utilities::settingIsEnabled('enable_advgb_blocks')) {
if (
method_exists(
'PPB_AdvancedGutenbergPro\Utils\Definitions',
'advgb_pro_widgets_customizer_frontend'
)
) {
PPB_AdvancedGutenbergPro\Utils\Definitions::advgb_pro_widgets_customizer_frontend();
}
}
// Patch for Twenty Twenty-One
$this->fixCssGridFooterWidgets();
}
// @TODO Support for Site Editor generated pages
if (strpos($content, 'wp-block-advgb-count-up') !== false) {
$content = preg_replace_callback(
'@
]*?advgb\-count\-up\-columns.*?(
)@s',
array( $this, 'decodeHtmlEntity' ),
$content
);
}
// @TODO Support for Site Editor generated pages
if (strpos($content, 'advgb-accordion-wrapper') !== false) {
$content = preg_replace_callback(
'@
]*?advgb\-accordion\-wrapper.*?(
.?)@s',
array( $this, 'decodeHtmlEntity' ),
$content
);
}
// @TODO Support for Site Editor generated pages
if (strpos($content, 'advgb-tabs-wrapper') !== false) {
// Fix broken tags: <strong> and <em>
$content = preg_replace_callback(
array(
'@
(.*?)<(?:strong|em)>(.*?)</(?:strong|em)>(.*?)
@s',
'@
(.*?)<(?:strong|em)>(.*?)</(?:strong|em)>(.*?)
@s'
),
array( $this, 'decodeHtmlEntity' ),
$content
);
}
// @TODO Support for Site Editor generated pages
if (strpos($content, 'advgb-testimonial') !== false) {
$content = preg_replace_callback(
'@
]*?advgb\-testimonial.*?(
)@s',
array( $this, 'decodeHtmlEntity' ),
$content
);
}
return $content;
}
/**
* Add styles for Adv Button and Adv List
*
* @param array $block Block data
*
* @return string HTML style
*/
public function addBlocksStyles($block)
{
$style_html = '';
$blockName = $block['blockName'];
$blockAttrs = $block['attrs'];
if ($blockName) {
// Get styles for parent blocks
$style_html .= $this->advgb_SetStylesForBlocks($blockAttrs, $blockName);
// Parse styles for nested blocks in WP 5.5+
global $wp_version;
if ($wp_version >= 5.5) {
// Check blocks in 2nd level and beyond
$style_html .= $this->advgb_getNestedBlocksStyles($block);
}
}
$style_html = $style_html ? '' : '';
return preg_replace('/\s\s+/', '', $style_html);
}
/**
* Find all style tag and append them in the end of content
*
* @param string $content Content to be prettify
* @param boolean $is_post if the content is false, is a widget
*
* @return string New prettified string
*/
public function groupStylesTag($content, $is_post = true)
{
// Group styles tag in the end of the content
preg_match_all('/()(.*?)(<\/style>)/mis', $content, $styles_html);
$styles_tag = '';
if (count($styles_html[0])) {
foreach ($styles_html[0] as $key => $style_html) {
$content = str_replace($style_html, '', $content);
$styles_tag .= $styles_html[2][ $key ];
}
}
if ($styles_tag) {
$content .= '';
}
return $content;
}
/**
* Convert html entity to real character
*
* @param string $match Matched string
*
* @return mixed
*/
public function decodeHtmlEntity($match)
{
return str_replace('<', '<', $match[0]);
}
/**
* Function to load external plugins for tinyMCE
*
* @param array $plgs External plugins
*
* @return array
*/
public function addTinyMceExternal(array $plgs)
{
$plgs['customstyles'] = ADVANCED_GUTENBERG_PLUGIN_DIR_URL . 'assets/blocks/customstyles/plugin.js';
return $plgs;
}
/**
* Function to add buttons for tinyMCE toolbars
*
* @param array $buttons TinyMCE buttons
*
* @return array
*/
public function addTinyMceButtons(array $buttons)
{
array_push($buttons, 'customstyles');
return $buttons;
}
/**
* Render html for block config settings fields
*
* @param array $fieldset Array of field to render
* @param array $data Data will load as value to field
*
* @return boolean Echo html content
*/
public function renderBlockConfigFields($fieldset, $data)
{
$html = '';
foreach ($fieldset as $category) {
$html .= '