gallery shortcode for Photonic. * This is used as an interim conversion step for switching shortcodes to a different, non-gallery shortcode. * This is crucial for Gutenberg support, as the "Convert to Blocks" capability of Gutenberg is broken (https://github.com/WordPress/gutenberg/issues/10674). * * @since 2.10 */ if (!class_exists('WP_List_Table')) { require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php'; } class Shortcode_Usage extends WP_List_Table { public $items = []; public string $tag; private int $per_page = 100; public function __construct($args = []) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter parent::__construct( [ 'singular' => 'post', 'plural' => 'posts', 'ajax' => false, ] ); $this->tag = 'gallery'; add_filter('removable_query_args', [&$this, 'remove_args']); } /** * List of columns displayed in the table * * @return array */ public function get_columns(): array { return [ 'cb' => '', 'title' => esc_html__('Post Title', 'photonic'), 'type' => esc_html__('Post Type', 'photonic'), 'status' => esc_html__('Post Status', 'photonic'), 'shortcodes' => esc_html__('Gallery Shortcodes used by Photonic', 'photonic'), ]; } /** * List of columns sortable by the user * * @return array */ public function get_sortable_columns(): array { return [ 'type' => ['type', true], 'title' => ['title', true], 'status' => ['status', false], ]; } /** * Main method to build out the list items */ public function prepare_items() { $this->_column_headers = [ $this->get_columns(), [], $this->get_sortable_columns(), 'title' ]; $this->process_bulk_action(); global $wpdb; $results = $wpdb->get_results( "SELECT ID, post_type, post_status, post_title, post_content FROM {$wpdb->posts} where post_type not in ('revision', 'attachment', 'nav_menu_item', 'oembed_cache') and post_status not in ('trash', 'inherit')", ARRAY_A ); $pattern = get_shortcode_regex([$this->tag]); $types = ['default', 'wp', 'flickr', 'smugmug', 'picasa', 'google', 'zenfolio', 'instagram']; $layouts = ['square', 'circle', 'random', 'masonry', 'mosaic', 'strip-above', 'strip-below', 'strip-right', 'no-strip']; $data = []; foreach ($results as $post) { preg_match_all('/' . $pattern . '/s', $post['post_content'], $matches, PREG_OFFSET_CAPTURE); if (!empty($matches) && !empty($matches[0]) && !empty($matches[1]) && !empty($matches[2]) && !empty($matches[3])) { $to_change = []; foreach ($matches[1] as $instance => $start) { if ('' === $start[0]) { if (!empty($matches[3][$instance])) { $attributes = shortcode_parse_atts($matches[3][$instance][0]); if ((!empty($attributes['type']) && in_array($attributes['type'], $types, true)) || (empty($attributes['type']) && !empty($attributes['style']) && in_array($attributes['style'], $layouts, true))) { $to_change[] = "" . esc_html($matches[0][$instance][0]) . ""; } } } } if (!empty($to_change)) { $data[] = [ 'id' => $post['ID'], 'type' => $post['post_type'], 'status' => $post['post_status'], 'title' => $post['post_title'], 'shortcodes' => $to_change, ]; } } } $current_page = $this->get_pagenum(); $total_items = count($data); $data = array_slice($data, (($current_page - 1) * $this->per_page), $this->per_page); $this->items = $data; $this->set_pagination_args( [ 'total_items' => $total_items, 'per_page' => $this->per_page, 'total_pages' => ceil($total_items / $this->per_page), ] ); } /** * Default output for a column, if a column-specific output is not defined * * @param object $item * @param string $column_name * @return null|string */ protected function column_default($item, $column_name): ?string { return isset($item[$column_name]) ? esc_html($item[$column_name]) : null; } /** * Adds the "Checkbox" column for bulk actions * * @param object $item * @return string */ protected function column_cb($item): string { return sprintf('', $item['id']); } protected function column_title($item): string { $actions = [ 'edit' => '' . esc_html__('Edit', 'photonic') . '', 'view' => '' . esc_html__('View', 'photonic') . '', 'replace_shortcode_individual' => '' . esc_html__('Replace Shortcodes', 'photonic') . '', ]; return $item['title'] . $this->row_actions($actions); } protected function column_shortcodes($item): string { return implode("
\n", $item['shortcodes']); } public function no_items() { echo sprintf(esc_html__('No instances of Photonic found with the %s shortcode', 'photonic'), "" . esc_html($this->tag) . ""); } protected function get_bulk_actions(): array { return [ 'replace_shortcode' => esc_html__('Replace Shortcode', 'photonic') ]; } private function process_bulk_action() { if ('replace_shortcode' === $this->current_action() && !empty($_POST['photonic_post']) && !empty($_POST['_photonic_replacement_nonce']) && wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['_photonic_replacement_nonce'])), 'photonic-replace-shortcode-' . get_current_user_id())) { $post_ids = $_POST['photonic_post']; // Cannot sanitize this since it is an array. Will sanitize each of its components in the array_walk. array_walk($post_ids, 'sanitize_text_field'); } elseif ('replace_shortcode_individual' === $this->current_action() && !empty($_REQUEST['photonic_post_id'])) { $post_ids = [sanitize_text_field(wp_unslash($_REQUEST['photonic_post_id']))]; } if (!empty($post_ids)) { global $wpdb, $photonic_alternative_shortcode; if (empty($photonic_alternative_shortcode) || strtolower($photonic_alternative_shortcode) === 'gallery') { echo "
\n

\n"; echo sprintf( esc_html__('Cannot update the posts because a custom shortcode has not been set up under %s.', 'photonic'), 'Photonic → Settings → Generic Options → Generic Settings → Custom Shortcode' ); echo "\n

\n
\n"; return; } $r_tag = $photonic_alternative_shortcode; $o_len = strlen($this->tag); $r_len = strlen($r_tag); $results = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_type, post_status, post_title, post_content FROM {$wpdb->posts} where post_type not in ('revision', 'attachment', 'nav_menu_item', 'oembed_cache') and post_status not in ('trash', 'inherit') and ID in (" . implode(', ', array_fill(0, count($post_ids), '%s')) . ")", ...$post_ids ), ARRAY_A ); $pattern = get_shortcode_regex([$this->tag]); $types = ['default', 'wp', 'flickr', 'smugmug', 'picasa', 'google', 'zenfolio', 'instagram']; $layouts = ['square', 'circle', 'random', 'masonry', 'mosaic', 'strip-above', 'strip-below', 'strip-right', 'no-strip']; $count = 0; $got_error = false; foreach ($results as $id => $post) { preg_match_all('/' . $pattern . '/s', $post['post_content'], $matches, PREG_OFFSET_CAPTURE); $changed = false; if (!empty($matches) && !empty($matches[0]) && !empty($matches[1]) && !empty($matches[2]) && !empty($matches[3])) { $instances = []; $init = $post['post_content']; foreach ($matches[1] as $instance => $start) { if ('' === $start[0]) { if (!empty($matches[3][$instance])) { $attributes = shortcode_parse_atts($matches[3][$instance][0]); if ((!empty($attributes['type']) && in_array($attributes['type'], $types, true)) || (empty($attributes['type']) && !empty($attributes['style']) && in_array($attributes['style'], $layouts, true))) { $offset = count($instances) * ($r_len - $o_len); $upto = substr($init, 0, $matches[0][$instance][1] + $offset); $instances[] = $instance; $replacement = str_replace('[' . $this->tag, '[' . $r_tag, $matches[0][$instance][0]); $after = substr($init, $matches[0][$instance][1] + $offset + strlen($matches[0][$instance][0])); $init = $upto . $replacement . $after; $changed = true; } } } } if ($changed) { $update = $wpdb->update($wpdb->posts, ['post_content' => $init], ['ID' => $post['ID']]); if (false === $update) { $got_error = true; } elseif (0 !== $update) { $count++; } } } } if ($got_error) { $type = 'error'; $message = esc_html__('Failed to replace shortcodes due to an error. Please open a support ticket.', 'photonic'); } elseif (0 === $count) { $type = 'warning'; $message = esc_html__('0 replacements made. If this is not what you were expecting please open a support ticket.', 'photonic'); } else { $type = 'success'; $message = esc_html( sprintf( _n( '%d post updated with the shortcode replacement.', '%d posts updated with the shortcode replacement.', $count, 'photonic' ), $count ) ); } echo "
\n

\n"; echo wp_kses_post($message); echo "\n

\n
\n"; } } public function remove_args($args) { $args[] = 'action'; $args[] = 'photonic_post_id'; return $args; } }