PluginProbe
Photonic Gallery & Lightbox for Flickr, SmugMug & Others / 3.36
Photonic Gallery & Lightbox for Flickr, SmugMug & Others v3.36
3.36 3.35 3.34 3.33 2.19 2.20 2.21 2.22 2.23 2.24 2.25 2.26 2.27 2.28 2.29 2.30 2.31 2.32 2.33 2.34 2.40 2.41 2.42 2.43 2.44 All 141 releases
photonic / Admin / Shortcode_Usage.php

Shortcode_Usage.php in Photonic Gallery & Lightbox for Flickr, SmugMug & Others 3.36, at Admin/Shortcode_Usage.php

284 lines 9.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Photonic_Plugin\Admin;
4
5 if (!current_user_can('edit_posts')) {
6 wp_die(esc_html__('You are not authorized to use this capability.', 'photonic'));
7 }
8
9 use WP_List_Table;
10
11 /**
12 * Generates a table showing the usage of the <code>gallery</code> shortcode for Photonic.
13 * This is used as an interim conversion step for switching shortcodes to a different, non-<code>gallery</code> shortcode.
14 * This is crucial for Gutenberg support, as the "Convert to Blocks" capability of Gutenberg is broken (https://github.com/WordPress/gutenberg/issues/10674).
15 *
16 * @since 2.10
17 */
18
19 if (!class_exists('WP_List_Table')) {
20 require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
21 }
22
23 class Shortcode_Usage extends WP_List_Table {
24 public $items = [];
25 public string $tag;
26 private int $per_page = 100;
27
28 public function __construct($args = []) { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter
29 parent::__construct(
30 [
31 'singular' => 'post',
32 'plural' => 'posts',
33 'ajax' => false,
34 ]
35 );
36 $this->tag = 'gallery';
37 add_filter('removable_query_args', [&$this, 'remove_args']);
38 }
39
40 /**
41 * List of columns displayed in the table
42 *
43 * @return array
44 */
45 public function get_columns(): array {
46 return [
47 'cb' => '<input type="checkbox" />',
48 'title' => esc_html__('Post Title', 'photonic'),
49 'type' => esc_html__('Post Type', 'photonic'),
50 'status' => esc_html__('Post Status', 'photonic'),
51 'shortcodes' => esc_html__('Gallery Shortcodes used by Photonic', 'photonic'),
52 ];
53 }
54
55 /**
56 * List of columns sortable by the user
57 *
58 * @return array
59 */
60 public function get_sortable_columns(): array {
61 return [
62 'type' => ['type', true],
63 'title' => ['title', true],
64 'status' => ['status', false],
65 ];
66 }
67
68 /**
69 * Main method to build out the list items
70 */
71 public function prepare_items() {
72 $this->_column_headers = [
73 $this->get_columns(),
74 [],
75 $this->get_sortable_columns(),
76 'title'
77 ];
78
79 $this->process_bulk_action();
80
81 global $wpdb;
82 $results = $wpdb->get_results(
83 "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')",
84 ARRAY_A
85 );
86
87 $pattern = get_shortcode_regex([$this->tag]);
88 $types = ['default', 'wp', 'flickr', 'smugmug', 'picasa', 'google', 'zenfolio', 'instagram'];
89 $layouts = ['square', 'circle', 'random', 'masonry', 'mosaic', 'strip-above', 'strip-below', 'strip-right', 'no-strip'];
90 $data = [];
91
92 foreach ($results as $post) {
93 preg_match_all('/' . $pattern . '/s', $post['post_content'], $matches, PREG_OFFSET_CAPTURE);
94 if (!empty($matches) && !empty($matches[0]) && !empty($matches[1]) && !empty($matches[2]) && !empty($matches[3])) {
95 $to_change = [];
96 foreach ($matches[1] as $instance => $start) {
97 if ('' === $start[0]) {
98 if (!empty($matches[3][$instance])) {
99 $attributes = shortcode_parse_atts($matches[3][$instance][0]);
100 if ((!empty($attributes['type']) && in_array($attributes['type'], $types, true)) ||
101 (empty($attributes['type']) && !empty($attributes['style']) && in_array($attributes['style'], $layouts, true))) {
102 $to_change[] = "<code>" . esc_html($matches[0][$instance][0]) . "</code>";
103 }
104 }
105 }
106 }
107 if (!empty($to_change)) {
108 $data[] = [
109 'id' => $post['ID'],
110 'type' => $post['post_type'],
111 'status' => $post['post_status'],
112 'title' => $post['post_title'],
113 'shortcodes' => $to_change,
114 ];
115 }
116 }
117 }
118 $current_page = $this->get_pagenum();
119 $total_items = count($data);
120 $data = array_slice($data, (($current_page - 1) * $this->per_page), $this->per_page);
121 $this->items = $data;
122 $this->set_pagination_args(
123 [
124 'total_items' => $total_items,
125 'per_page' => $this->per_page,
126 'total_pages' => ceil($total_items / $this->per_page),
127 ]
128 );
129 }
130
131 /**
132 * Default output for a column, if a column-specific output is not defined
133 *
134 * @param object $item
135 * @param string $column_name
136 * @return null|string
137 */
138 protected function column_default($item, $column_name): ?string {
139 return isset($item[$column_name]) ? esc_html($item[$column_name]) : null;
140 }
141
142 /**
143 * Adds the "Checkbox" column for bulk actions
144 *
145 * @param object $item
146 * @return string
147 */
148 protected function column_cb($item): string {
149 return sprintf('<input type="checkbox" name="photonic_post[]" value="%s" />', $item['id']);
150 }
151
152 protected function column_title($item): string {
153 $actions = [
154 'edit' => '<a href="' . get_edit_post_link($item['id']) . '">' . esc_html__('Edit', 'photonic') . '</a>',
155 'view' => '<a href="' . get_permalink($item['id']) . '">' . esc_html__('View', 'photonic') . '</a>',
156 'replace_shortcode_individual' => '<a href="' . admin_url('admin.php?page=photonic-shortcode-replace&action=replace_shortcode_individual&photonic_post_id=' . $item['id']) . '" class="photonic-shortcode-replace">' . esc_html__('Replace Shortcodes', 'photonic') . '</a>',
157 ];
158
159 return $item['title'] . $this->row_actions($actions);
160 }
161
162 protected function column_shortcodes($item): string {
163 return implode("<br/>\n", $item['shortcodes']);
164 }
165
166 public function no_items() {
167 echo sprintf(esc_html__('No instances of Photonic found with the %s shortcode', 'photonic'), "<code>" . esc_html($this->tag) . "</code>");
168 }
169
170 protected function get_bulk_actions(): array {
171 return [
172 'replace_shortcode' => esc_html__('Replace Shortcode', 'photonic')
173 ];
174 }
175
176 private function process_bulk_action() {
177 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())) {
178 $post_ids = $_POST['photonic_post']; // Cannot sanitize this since it is an array. Will sanitize each of its components in the array_walk.
179 array_walk($post_ids, 'sanitize_text_field');
180 }
181 elseif ('replace_shortcode_individual' === $this->current_action() && !empty($_REQUEST['photonic_post_id'])) {
182 $post_ids = [sanitize_text_field(wp_unslash($_REQUEST['photonic_post_id']))];
183 }
184
185 if (!empty($post_ids)) {
186 global $wpdb, $photonic_alternative_shortcode;
187 if (empty($photonic_alternative_shortcode) || strtolower($photonic_alternative_shortcode) === 'gallery') {
188 echo "<div class='notice notice-error is-dismissible'>\n<p>\n";
189 echo sprintf(
190 esc_html__('Cannot update the posts because a custom shortcode has not been set up under %s.', 'photonic'),
191 '<strong><em>Photonic &rarr; Settings &rarr; Generic Options &rarr; Generic Settings &rarr; Custom Shortcode</em></strong>'
192 );
193 echo "\n</p>\n</div>\n";
194 return;
195 }
196
197 $r_tag = $photonic_alternative_shortcode;
198 $o_len = strlen($this->tag);
199 $r_len = strlen($r_tag);
200
201 $results = $wpdb->get_results(
202 $wpdb->prepare(
203 "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')) . ")",
204 ...$post_ids
205 ),
206 ARRAY_A
207 );
208
209 $pattern = get_shortcode_regex([$this->tag]);
210 $types = ['default', 'wp', 'flickr', 'smugmug', 'picasa', 'google', 'zenfolio', 'instagram'];
211 $layouts = ['square', 'circle', 'random', 'masonry', 'mosaic', 'strip-above', 'strip-below', 'strip-right', 'no-strip'];
212
213 $count = 0;
214 $got_error = false;
215 foreach ($results as $id => $post) {
216 preg_match_all('/' . $pattern . '/s', $post['post_content'], $matches, PREG_OFFSET_CAPTURE);
217 $changed = false;
218 if (!empty($matches) && !empty($matches[0]) && !empty($matches[1]) && !empty($matches[2]) && !empty($matches[3])) {
219 $instances = [];
220 $init = $post['post_content'];
221 foreach ($matches[1] as $instance => $start) {
222 if ('' === $start[0]) {
223 if (!empty($matches[3][$instance])) {
224 $attributes = shortcode_parse_atts($matches[3][$instance][0]);
225 if ((!empty($attributes['type']) && in_array($attributes['type'], $types, true)) ||
226 (empty($attributes['type']) && !empty($attributes['style']) && in_array($attributes['style'], $layouts, true))) {
227 $offset = count($instances) * ($r_len - $o_len);
228 $upto = substr($init, 0, $matches[0][$instance][1] + $offset);
229 $instances[] = $instance;
230 $replacement = str_replace('[' . $this->tag, '[' . $r_tag, $matches[0][$instance][0]);
231 $after = substr($init, $matches[0][$instance][1] + $offset + strlen($matches[0][$instance][0]));
232 $init = $upto . $replacement . $after;
233 $changed = true;
234 }
235 }
236 }
237 }
238 if ($changed) {
239 $update = $wpdb->update($wpdb->posts, ['post_content' => $init], ['ID' => $post['ID']]);
240 if (false === $update) {
241 $got_error = true;
242 }
243 elseif (0 !== $update) {
244 $count++;
245 }
246 }
247 }
248 }
249 if ($got_error) {
250 $type = 'error';
251 $message = esc_html__('Failed to replace shortcodes due to an error. Please open a support ticket.', 'photonic');
252 }
253 elseif (0 === $count) {
254 $type = 'warning';
255 $message = esc_html__('0 replacements made. If this is not what you were expecting please open a support ticket.', 'photonic');
256 }
257 else {
258 $type = 'success';
259 $message = esc_html(
260 sprintf(
261 _n(
262 '%d post updated with the shortcode replacement.',
263 '%d posts updated with the shortcode replacement.',
264 $count,
265 'photonic'
266 ),
267 $count
268 )
269 );
270 }
271
272 echo "<div class='notice notice-" . sanitize_html_class($type) . " is-dismissible'>\n<p>\n";
273 echo wp_kses_post($message);
274 echo "\n</p>\n</div>\n";
275 }
276 }
277
278 public function remove_args($args) {
279 $args[] = 'action';
280 $args[] = 'photonic_post_id';
281 return $args;
282 }
283 }
284