PluginProbe
Tag Groups is the Advanced Way to Display Your Taxonomy Terms / trunk
Tag Groups is the Advanced Way to Display Your Taxonomy Terms vtrunk
2.2.2 2.2.1 2.2.0 trunk 1.40.0 1.40.2 1.41.0 1.42.1 1.43.0 1.43.1 1.43.10 1.43.10.1 1.43.2 1.43.3 1.43.4 1.43.5 1.43.6 1.43.7 1.43.9 1.44.0 1.44.1 1.44.3 1.44.3.1 2.0.0 2.0.1 All 36 releases
tag-groups / include / admin / class.admin.php

class.admin.php in Tag Groups is the Advanced Way to Display Your Taxonomy Terms trunk, at include/admin/class.admin.php

1,072 lines 46.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * @package Tag Groups
5 *
6 * @author Christoph Amthor
7 * @copyright 2018 Christoph Amthor (@ Chatty Mango, chattymango.com)
8 * @license GPL-3.0+
9 */
10
11 if (!class_exists('TagGroups_Admin')) {
12 // phpcs:ignore PSR1.Classes.ClassDeclaration.MissingNamespace, Squiz.Classes.ValidClassName.NotCamelCaps -- Legacy class structure
13 class TagGroups_Admin
14 {
15 /**
16 * Adds the submenus and the settings page to the admin backend
17 */
18 // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps -- Legacy method naming
19 public static function register_menus()
20 {
21 // Add the main menu
22 add_menu_page(__('Home', 'tag-groups'), 'Tag Groups', 'manage_options', 'tag-groups-settings', array( 'TagGroups_Settings', 'settings_page_home' ), 'dashicons-tag', '99.01');
23 // Define the menu structure
24 $tag_groups_admin_structure = array(
25 0 => array(
26 'title' => __('Taxonomies', 'tag-groups'),
27 'slug' => 'tag-groups-settings',
28 'parent' => 'tag-groups-settings',
29 'user_can' => 'manage_options',
30 'function' => array( 'TagGroups_Settings', 'settings_page_home' ),
31 ),
32 3 => array(
33 'title' => __('Front End', 'tag-groups'),
34 'slug' => 'tag-groups-settings-front-end',
35 'parent' => 'tag-groups-settings',
36 'user_can' => 'manage_options',
37 'function' => array( 'TagGroups_Settings', 'settings_page_front_end' ),
38 ),
39 4 => array(
40 'title' => __('Back End', 'tag-groups'),
41 'slug' => 'tag-groups-settings-back-end',
42 'parent' => 'tag-groups-settings',
43 'user_can' => 'manage_options',
44 'function' => array( 'TagGroups_Settings', 'settings_page_back_end' ),
45 ),
46 9 => array(
47 'title' => __('First Steps', 'tag-groups'),
48 'slug' => 'tag-groups-settings-first-steps',
49 'parent' => null,
50 'user_can' => 'manage_options',
51 'function' => array( 'TagGroups_Setup_Wizard', 'settings_page_onboarding' ),
52 ),
53 10 => array(
54 'title' => __('Setup Wizard', 'tag-groups'),
55 'slug' => 'tag-groups-settings-setup-wizard',
56 'parent' => null,
57 'user_can' => 'manage_options',
58 'function' => array( 'TagGroups_Setup_Wizard', 'settings_page_setup_wizard' ),
59 ),
60 11 => array(
61 'title' => __('Settings', 'tag-groups'),
62 'slug' => 'tag-groups-settings-general',
63 'parent' => 'tag-groups-settings',
64 'user_can' => 'manage_options',
65 'function' => array( 'TagGroups_Settings', 'settings_page_general' ),
66 ),
67 );
68 // hook for premium plugin to modify the menu
69 $tag_groups_admin_structure = apply_filters('tag_groups_admin_structure', $tag_groups_admin_structure);
70 // make sure they all have the right order
71 ksort($tag_groups_admin_structure);
72 // register the menus and pages
73 foreach ($tag_groups_admin_structure as $tag_groups_admin_page) {
74 add_submenu_page(!empty($tag_groups_admin_page['parent']) ? $tag_groups_admin_page['parent'] : '', $tag_groups_admin_page['title'], $tag_groups_admin_page['title'], $tag_groups_admin_page['user_can'], $tag_groups_admin_page['slug'], $tag_groups_admin_page['function']);
75 }
76 // for each registered taxonomy a tag group admin page
77 $tag_group_taxonomies = TagGroups_Options::get_option('tag_group_taxonomy', array( 'post_tag' ));
78 $tag_group_role_edit_groups = 'edit_pages';
79 if (TagGroups_Utilities::is_premium_plan()) {
80 $tag_group_role_edit_groups = TagGroups_Options::get_option('tag_group_role_edit_groups', 'edit_pages');
81 }
82
83 $tag_group_post_types = TagGroups_Taxonomy::post_types_from_taxonomies($tag_group_taxonomies);
84 foreach ($tag_group_post_types as $post_type) {
85 if ('post' == $post_type) {
86 $post_type_query = '';
87 } else {
88 $post_type_query = '?post_type=' . $post_type;
89 }
90
91 $submenu_page = add_submenu_page('edit.php' . $post_type_query, __('Tag Group Admin', 'tag-groups'), __('Tag Group Admin', 'tag-groups'), $tag_group_role_edit_groups, 'tag-groups_' . $post_type, array( 'TagGroups_Group_Admin', 'render_group_administration' ));
92 if (
93 TagGroups_Utilities::is_premium_plan()
94 && class_exists('TagGroups_Premium_Admin')
95 && method_exists('TagGroups_Premium_Admin', 'add_screen_option')
96 ) {
97 add_action("load-$submenu_page", array( 'TagGroups_Premium_Admin', 'add_screen_option' ));
98 }
99 }
100 }
101
102 /**
103 * Remove one of the Freemius submenus
104 *
105 * @return void
106 */
107 // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps -- Legacy method naming
108 public static function remove_submenus()
109 {
110
111 if (TagGroups_Utilities::is_free_plan()) {
112 remove_submenu_page('tag-groups-settings', 'tag-groups-settings-contact');
113 } else {
114 remove_submenu_page('tag-groups-settings', 'tag-groups-settings-wp-support-forum');
115 }
116 }
117
118 /**
119 * Create the html to add tags to tag groups on single tag view (after clicking tag for editing)
120 *
121 * @param type $tag
122 */
123 // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps -- Legacy method naming
124 public static function render_edit_tag_menu($tag)
125 {
126 global $tag_group_groups ;
127 $screen = get_current_screen();
128 if ('post' == $screen->post_type) {
129 $url_post_type = '';
130 } else {
131 $url_post_type = '&post_type=' . $screen->post_type;
132 }
133
134 $tag_group_admin_url = admin_url('edit.php?page=tag-groups_' . $screen->post_type . $url_post_type);
135 $term_groups = $tag_group_groups->get_all_with_position_as_key();
136 unset($term_groups[0]);
137 $tg_term = new TagGroups_Term($tag);
138 $view = new TagGroups_View('admin/edit_tag_main');
139 if (TagGroups_Utilities::is_premium_plan() && class_exists('TagGroups_Premium_View')) {
140 $view = new TagGroups_Premium_View('admin/edit_tag_main');
141 }
142
143 $view->set(array(
144 'term_groups' => $term_groups,
145 'screen' => $screen,
146 'tg_term' => $tg_term,
147 'tag_group_admin_url' => $tag_group_admin_url,
148 ));
149 $view->render();
150 }
151
152 /**
153 * Create the html to assign tags to tag groups upon new tag creation (left of the table)
154 *
155 * @param type $tag
156 */
157 // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps -- Legacy method naming
158 public static function render_new_tag_menu($tag)
159 {
160 global $tag_group_groups ;
161 $screen = get_current_screen();
162 $term_groups = $tag_group_groups->get_all_with_position_as_key();
163 unset($term_groups[0]);
164 $new_tag_initial_groups = array();
165 if (TagGroups_Utilities::is_premium_plan()) {
166 $new_tag_initial_groups = TagGroups_Options::get_option('tag_group_new_tag_default_groups', array());
167 }
168
169 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only operation for WPML translation context
170 if (empty($new_tag_initial_groups) && TagGroups_WPML::is_multilingual() && isset($_GET['trid']) && !empty($_GET['taxonomy'])) {
171 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only operation for WPML translation context
172 $trid = (int) $_GET['trid'];
173 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only operation for WPML translation context
174 $taxonomy = sanitize_title(wp_unslash($_GET['taxonomy']));
175 $translations = apply_filters('wpml_get_element_translations', null, $trid, "tax_{$taxonomy}");
176 $default_lang = apply_filters('wpml_default_language', null);
177 if (!empty($default_lang) && is_array($translations) && isset($translations[$default_lang])) {
178 $original_translation = $translations[$default_lang];
179 } else {
180 $original_translation = TagGroups_Utilities::get_first_element($translations);
181 }
182
183
184 if (!empty($original_translation)) {
185 $tg_original_term = new TagGroups_Term($original_translation->element_id);
186 $new_tag_initial_groups = $tg_original_term->get_groups();
187 }
188 }
189
190 $view = new TagGroups_View('admin/new_tag_from_list');
191 if (TagGroups_Utilities::is_premium_plan() && class_exists('TagGroups_Premium_View')) {
192 $view = new TagGroups_Premium_View('admin/new_tag_from_list');
193 }
194
195 $view->set(array(
196 'term_groups' => $term_groups,
197 'screen' => $screen,
198 'new_tag_initial_groups' => $new_tag_initial_groups,
199 ));
200 $view->render();
201 }
202
203 /**
204 * adds a custom column to the table of tags/terms
205 * thanks to http://coderrr.com/add-columns-to-a-taxonomy-terms-table/
206 *
207 * @global object $wp
208 * @param array $columns
209 * @return string
210 */
211 // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps -- Legacy method naming
212 public static function add_taxonomy_columns($columns)
213 {
214 global $wp ;
215 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only operation for column sorting
216 $new_order = (isset($_GET['order']) && 'asc' == $_GET['order'] && isset($_GET['orderby']) && 'term_group' == $_GET['orderby'] ? 'desc' : 'asc');
217 $screen = get_current_screen();
218 if (!empty($screen)) {
219 $taxonomy = $screen->taxonomy;
220 $link = add_query_arg(
221 array(
222 'orderby' => 'term_group',
223 'order' => $new_order,
224 'taxonomy' => $taxonomy,
225 ),
226 admin_url("edit-tags.php" . $wp->request)
227 );
228 $link_escaped = esc_url($link);
229 $columns['term_group'] = '<a href="' . $link_escaped . '"><span>' . __('Tag Groups', 'tag-groups') . '</span><span class="sorting-indicator"></span></a>';
230 } else {
231 $columns['term_group'] = '';
232 }
233
234 return $columns;
235 }
236
237 /**
238 * adds data into custom column of the table for each row
239 * thanks to http://coderrr.com/add-columns-to-a-taxonomy-terms-table/
240 *
241 * @param type $a
242 * @param type $b
243 * @param type $term_id
244 * @return string
245 */
246 // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps -- Legacy method naming
247 public static function add_taxonomy_column_content($content = '', $column_name = '', $term_id = 0)
248 {
249 global $tag_group_groups ;
250 if ('term_group' != $column_name) {
251 return $content;
252 }
253
254 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only operation for taxonomy display
255 if (!empty($_REQUEST['taxonomy'])) {
256 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only operation for taxonomy display
257 $taxonomy = sanitize_title(wp_unslash($_REQUEST['taxonomy']));
258 } else {
259 return '';
260 }
261
262 $term = get_term($term_id, $taxonomy);
263 if (isset($term)) {
264 $term_o = new TagGroups_Term($term);
265 return implode(', ', $tag_group_groups->get_labels_by_position($term_o->get_groups()));
266 } else {
267 return '';
268 }
269 }
270
271 /**
272 * Modify the term query so that we can sort by the term meta
273 *
274 * @param [type] $pieces
275 * @param [type] $taxonomies
276 * @param [type] $args
277 * @return void
278 */
279 // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps -- Legacy method naming
280 public static function sort_taxonomy_columns($pieces, $taxonomies, $args)
281 {
282 global $wpdb ;
283 $screen = get_current_screen();
284 $enabled_taxonomies = TagGroups_Taxonomy::get_enabled_taxonomies();
285 if (empty($screen) || !in_array($screen->taxonomy, $enabled_taxonomies)) {
286 return $pieces;
287 }
288 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only operation for column sorting
289 if (empty($_GET['orderby']) || 'term_group' != $_GET['orderby']) {
290 return $pieces;
291 }
292
293 // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Read-only operation for column sorting
294 if (isset($_GET['order']) && strtoupper(sanitize_key(wp_unslash($_GET['order']))) == 'DESC') {
295 $order = "DESC";
296 } else {
297 $order = 'ASC';
298 }
299
300 $pieces['join'] .= ' INNER JOIN ' . $wpdb->termmeta . ' AS tm ON t.term_id = tm.term_id ';
301 $pieces['where'] .= ' AND tm.meta_key = "_cm_term_group_array"';
302 $pieces['orderby'] = ' ORDER BY tm.meta_value ';
303 $pieces['order'] = $order;
304 return $pieces;
305 }
306
307 /**
308 * processing actions defined in bulk_admin_footer()
309 * credits http://www.foxrunsoftware.net
310 *
311 * @global int $tg_update_edit_term_group_called
312 * @return void
313 */
314 // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps -- Legacy method naming
315 public static function do_bulk_action()
316 {
317 global $tg_update_edit_term_group_called ;
318 $enabled_taxonomies = TagGroups_Taxonomy::get_enabled_taxonomies();
319 $screen = get_current_screen();
320 $taxonomy = $screen->taxonomy;
321 if (is_object($screen) && !in_array($taxonomy, $enabled_taxonomies)) {
322 return;
323 }
324 $show_filter_tags = TagGroups_Options::get_option('tag_group_show_filter_tags', 0);
325 if ($show_filter_tags) {
326 $tag_group_tags_filter = TagGroups_Options::get_option('tag_group_tags_filter', array());
327 /**
328 * Processing the filter
329 * Values come as POST (via menu, precedence) or GET (via link from group admin)
330 */
331
332 // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Filter operation
333 if (isset($_POST['term-filter'])) {
334 // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Filter operation
335 $term_filter = (int) $_POST['term-filter'];
336 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Filter operation
337 } elseif (isset($_GET['term-filter'])) {
338 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Filter operation
339 $term_filter = (int) $_GET['term-filter'];
340 // We need to remove the term-filter piece, or it will stay forever
341 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.InputNotValidated -- URL manipulation
342 $sendback = remove_query_arg(array( 'term-filter' ), wp_unslash($_SERVER['REQUEST_URI']));
343 }
344
345
346 if (isset($term_filter)) {
347 if ('-1' == $term_filter) {
348 unset($tag_group_tags_filter[$taxonomy]);
349 TagGroups_Options::update_option('tag_group_tags_filter', $tag_group_tags_filter);
350 } else {
351 $tag_group_tags_filter[$taxonomy] = $term_filter;
352 TagGroups_Options::update_option('tag_group_tags_filter', $tag_group_tags_filter);
353 /*
354 * Modify the query
355 */
356 add_action('terms_clauses', array( 'TagGroups_Admin', 'modify_terms_query' ), 10, 3);
357 }
358
359
360 if (isset($sendback)) {
361 // remove filter that destroys WPML's "&lang="
362 remove_all_filters('wp_redirect');
363 // escaping $sendback
364 wp_redirect(esc_url_raw($sendback));
365 exit;
366 }
367 } else {
368 /**
369 * If filter is set, make sure to modify the query
370 */
371 if (isset($tag_group_tags_filter[$taxonomy])) {
372 add_action('terms_clauses', array( 'TagGroups_Admin', 'modify_terms_query' ), 10, 3);
373 }
374 }
375 }
376
377 $wp_list_table = _get_list_table('WP_Terms_List_Table');
378 $action = $wp_list_table->current_action();
379 $allowed_actions = array( 'assign' );
380 if (!in_array($action, $allowed_actions)) {
381 return;
382 }
383
384 check_admin_referer('bulk-tags');
385
386 if (isset($_REQUEST['delete_tags'])) {
387 $term_ids = array_map('intval', (array) wp_unslash($_REQUEST['delete_tags']));
388 }
389
390 if (isset($_REQUEST['term-group-top'])) {
391 $term_group = (int) sanitize_text_field(wp_unslash($_REQUEST['term-group-top']));
392 } else {
393 return;
394 }
395
396 $sendback = remove_query_arg(array( 'assigned', 'deleted' ), wp_get_referer());
397 if (!$sendback) {
398 $sendback = admin_url('edit-tags.php?taxonomy=' . $taxonomy);
399 }
400
401 if (empty($term_ids)) {
402 $sendback = add_query_arg(
403 array(
404 'number_assigned' => 0,
405 'group_id' => $term_group,
406 ),
407 $sendback
408 );
409 $sendback = remove_query_arg(
410 array(
411 'action',
412 'action2',
413 'tags_input',
414 'post_author',
415 'comment_status',
416 'ping_status',
417 '_status',
418 'post',
419 'bulk_edit',
420 'post_view'
421 ),
422 $sendback
423 );
424 // escaping $sendback
425 wp_redirect(esc_url_raw($sendback));
426 exit;
427 }
428
429 $pagenum = $wp_list_table->get_pagenum();
430 $sendback = add_query_arg('paged', $pagenum, $sendback);
431 $tg_update_edit_term_group_called = true;
432 /**
433 * skip update_edit_term_group()
434 */
435 switch ($action) {
436 case 'assign':
437 $assigned = 0;
438 foreach ($term_ids as $term_id) {
439 $term = new TagGroups_Term($term_id);
440 if (false !== $term) {
441 if (0 == $term_group) {
442 if ($term->get_groups() != array( 0 )) {
443 $term->remove_all_groups()->save();
444 }
445 } else {
446 if (!in_array($term_group, $term->get_groups())) {
447 $term->add_group($term_group)->save();
448 }
449 }
450
451 $assigned++;
452 }
453 }
454
455 if (0 == $term_group) {
456 $message = _n('The term has been removed from all groups.', sprintf('%d terms have been removed from all groups.', number_format_i18n((int) $assigned)), (int) $assigned, 'tag-groups');
457 } else {
458 $tg_group = new TagGroups_Group($term_group);
459 $message = _n(sprintf('The term has been assigned to the group %s.', '<i>' . $tg_group->get_label() . '</i>'), sprintf('%d terms have been assigned to the group %s.', number_format_i18n((int) $assigned), '<i>' . $tg_group->get_label() . '</i>'), (int) $assigned, 'tag-groups');
460 }
461
462
463 break;
464 default:
465 // Need to show a message?
466
467
468 exit;
469 break;
470 }
471 TagGroups_Admin_Notice::add('success', $message);
472 $sendback = remove_query_arg(
473 array(
474 'action',
475 'action2',
476 'tags_input',
477 'post_author',
478 'comment_status',
479 'ping_status',
480 '_status',
481 'post',
482 'bulk_edit',
483 'post_view'
484 ),
485 $sendback
486 );
487 wp_redirect(esc_url_raw($sendback));
488 exit;
489 }
490
491 /**
492 * Filter the tags on the tag page
493 *
494 * @return void
495 */
496 // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps -- Legacy method naming
497 public static function do_filter_tags()
498 {
499 $enabled_taxonomies = TagGroups_Taxonomy::get_enabled_taxonomies();
500 $screen = get_current_screen();
501 $taxonomy = $screen->taxonomy;
502 if (is_object($screen) && !in_array($taxonomy, $enabled_taxonomies)) {
503 return;
504 }
505 $show_filter_tags = TagGroups_Options::get_option('tag_group_show_filter_tags', 0);
506 if (!$show_filter_tags) {
507 return;
508 }
509 $tag_group_tags_filter = TagGroups_Options::get_option('tag_group_tags_filter', array());
510 /**
511 * Processing the filter
512 * Values come as POST (via menu, precedence) or GET (via link from group admin)
513 */
514
515 // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Filter operation
516 if (isset($_POST['term-filter'])) {
517 // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Filter operation
518 $term_filter = (int) $_POST['term-filter'];
519 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Filter operation
520 } elseif (isset($_GET['term-filter'])) {
521 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Filter operation
522 $term_filter = (int) $_GET['term-filter'];
523 }
524
525
526 if (isset($term_filter)) {
527 if ('-1' == $term_filter) {
528 unset($tag_group_tags_filter[$taxonomy]);
529 TagGroups_Options::update_option('tag_group_tags_filter', $tag_group_tags_filter);
530 } else {
531 $tag_group_tags_filter[$taxonomy] = $term_filter;
532 TagGroups_Options::update_option('tag_group_tags_filter', $tag_group_tags_filter);
533 /*
534 * Modify the query
535 */
536 add_action('terms_clauses', array( 'TagGroups_Admin', 'modify_terms_query' ), 10, 3);
537 }
538
539
540 if (isset($sendback)) {
541 /**
542 * We need to remove the term-filter piece, or it will stay forever
543 *
544 * Also return to first page, trying to solve error "A variable mismatch has been detected."
545 */
546 $sendback = remove_query_arg(array( 'term-filter', 'paged' ));
547 /**
548 * let WP use $_SERVER['REQUEST_URI'] and apply whitelisting etc. if desired
549 *
550 * remove filter that destroys WPML's "&lang="
551 */
552 remove_all_filters('wp_redirect');
553 // escaping $sendback
554 wp_redirect(esc_url_raw($sendback));
555 exit;
556 }
557 } else {
558 /**
559 * If filter is set, make sure to modify the query
560 */
561 if (isset($tag_group_tags_filter[$taxonomy])) {
562 add_action('terms_clauses', array( 'TagGroups_Admin', 'modify_terms_query' ), 10, 3);
563 }
564 }
565 }
566
567 /**
568 * modifies Quick Edit link to call JS when clicked
569 * thanks to http://shibashake.com/WordPress-theme/expand-the-WordPress-quick-edit-menu
570 *
571 * @param array $actions
572 * @param object $tag
573 * @return array
574 */
575 // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps -- Legacy method naming
576 public static function expand_quick_edit_link($actions, $tag)
577 {
578 $screen = get_current_screen();
579 $enabled_taxonomies = TagGroups_Taxonomy::get_enabled_taxonomies();
580 if (is_object($screen) && !in_array($screen->taxonomy, $enabled_taxonomies)) {
581 return $actions;
582 }
583 $term_o = new TagGroups_Term($tag);
584 $groups = htmlspecialchars(wp_json_encode($term_o->get_groups()));
585 $nonce = wp_create_nonce('tag-groups-nonce');
586 $actions['inline hide-if-no-js'] = '<a href="javascript:void(0)" class="editinline" title="';
587 $actions['inline hide-if-no-js'] .= esc_attr(__('Edit this item inline', 'tag-groups')) . '" ';
588 $actions['inline hide-if-no-js'] .= " onclick=\"set_inline_tag_group_selected('{$groups}', '{$nonce}')\">";
589 $actions['inline hide-if-no-js'] .= __('Quick&nbsp;Edit', 'tag-groups');
590 $actions['inline hide-if-no-js'] .= '</a>';
591 return $actions;
592 }
593
594 /**
595 * adds JS function that sets the saved tag group for a given element when it's opened in quick edit
596 * thanks to http://shibashake.com/WordPress-theme/expand-the-WordPress-quick-edit-menu
597 *
598 * @return void
599 */
600 // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps -- Legacy method naming
601 public static function render_quick_edit_javascript()
602 {
603 $screen = get_current_screen();
604 $enabled_taxonomies = TagGroups_Taxonomy::get_enabled_taxonomies();
605 if (!in_array($screen->taxonomy, $enabled_taxonomies)) {
606 return;
607 }
608 $view = new TagGroups_View('partials/quick_edit_javascript');
609 if (TagGroups_Utilities::is_premium_plan() && class_exists('TagGroups_Premium_View')) {
610 $view = new TagGroups_Premium_View('partials/quick_edit_javascript');
611 }
612
613 $view->render();
614 }
615
616 /**
617 * Create the html to assign tags to tag groups directly in tag table ('quick edit')
618 *
619 * @return type
620 */
621 // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps -- Legacy method naming
622 public static function quick_edit_tag()
623 {
624 global $tg_quick_edit_tag_called, $tag_group_groups ;
625 if ($tg_quick_edit_tag_called) {
626 return;
627 }
628 $tg_quick_edit_tag_called = true;
629 $screen = get_current_screen();
630 $enabled_taxonomies = TagGroups_Taxonomy::get_enabled_taxonomies();
631 if (!in_array($screen->taxonomy, $enabled_taxonomies)) {
632 return;
633 }
634 $term_groups = $tag_group_groups->get_all_with_position_as_key();
635 unset($term_groups[0]);
636 $view = new TagGroups_View('partials/quick_edit_tag');
637 if (TagGroups_Utilities::is_premium_plan() && class_exists('TagGroups_Premium_View')) {
638 $view = new TagGroups_Premium_View('partials/quick_edit_tag');
639 }
640
641 $view->set(array(
642 'term_groups' => $term_groups,
643 'screen' => $screen,
644 ));
645 $view->render();
646 }
647
648 /**
649 * Adds a bulk action menu to a term list page
650 * credits http://www.foxrunsoftware.net
651 *
652 * @return void
653 */
654 // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps -- Legacy method naming
655 public static function bulk_admin_footer()
656 {
657 global $tag_group_groups ;
658 $enabled_taxonomies = TagGroups_Taxonomy::get_enabled_taxonomies();
659 $screen = get_current_screen();
660 if (is_object($screen) && !in_array($screen->taxonomy, $enabled_taxonomies)) {
661 return;
662 }
663 $term_groups = $tag_group_groups->get_all_with_position_as_key();
664 $view = new TagGroups_View('partials/bulk_admin_footer');
665 $view->set(array(
666 'term_groups' => $term_groups,
667 ));
668 $view->render();
669 }
670
671 /**
672 * Adds a filter menu to a term list page
673 *
674 * @return void
675 */
676 // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps -- Legacy method naming
677 public static function filter_admin_footer()
678 {
679 global $tag_group_groups ;
680 $enabled_taxonomies = TagGroups_Taxonomy::get_enabled_taxonomies();
681 $screen = get_current_screen();
682 if (is_object($screen) && !in_array($screen->taxonomy, $enabled_taxonomies)) {
683 return;
684 }
685 if (!TagGroups_Options::get_option('tag_group_show_filter_tags', 0)) {
686 return;
687 }
688 $term_groups = $tag_group_groups->get_all_with_position_as_key(true);
689 $tag_group_tags_filter = TagGroups_Options::get_option('tag_group_tags_filter', array());
690 if (isset($tag_group_tags_filter[$screen->taxonomy])) {
691 $tag_filter = $tag_group_tags_filter[$screen->taxonomy];
692 if ($tag_filter > 0) {
693 // check if group exists (could be deleted since last time the filter was set)
694 $tg_group = new TagGroups_Group($tag_filter);
695 if (!$tg_group->exists()) {
696 $tag_filter = -1;
697 }
698 }
699 } else {
700 $tag_filter = -1;
701 }
702
703 $view = new TagGroups_View('partials/filter_admin_footer');
704 $view->set(array(
705 'parents' => $tag_group_groups->get_parents(),
706 'term_groups' => $term_groups,
707 'tag_filter' => $tag_filter,
708 ));
709 $view->render();
710 }
711
712 /**
713 * Adds a button to reset the filter on the tags page, in case JavaScript breaks
714 *
715 * @since 1.25.0
716 *
717 * @param void
718 * @return void
719 */
720 // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps -- Legacy method naming
721 public static function add_admin_footer_text($text)
722 {
723 $screen = get_current_screen();
724 $enabled_taxonomies = TagGroups_Taxonomy::get_enabled_taxonomies();
725 if (!empty($screen) && 'edit-tags' == $screen->base && TagGroups_Options::get_option('tag_group_show_filter_tags', 0) && in_array($screen->taxonomy, $enabled_taxonomies)) {
726 $view = new TagGroups_View('partials/admin_footer');
727 $view->set('reset_url', esc_url(add_query_arg('term-filter', -1)));
728 return $view->return_html() . $text;
729 }
730
731 return $text;
732 }
733
734 /**
735 * Adds a button to reset the filter on the tags page, in case JavaScript breaks
736 *
737 * @since 1.25.0
738 *
739 * @param string $text
740 * @return string
741 */
742 // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps -- Legacy method naming
743 public static function add_admin_footer_rating_text($text)
744 {
745 // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Read-only check for admin page
746 if (empty($_GET['page']) || strpos(sanitize_key(wp_unslash($_GET['page'])), 'tag-groups') !== 0) {
747 return $text;
748 }
749
750 $view = new TagGroups_View('partials/admin_footer_rating');
751 $text = $view->return_html() . $text;
752 return $text;
753 }
754
755 /**
756 * Adds JS to the footer for nicer tooltips in the backend
757 *
758 * @since 1.43.5
759 *
760 * @param string $text
761 * @return string
762 */
763 // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps -- Legacy method naming
764 public static function add_admin_footer_tooltip_script($text)
765 {
766 $screen = get_current_screen();
767 // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Read-only check for admin page
768 if (empty($_GET['page']) || strpos(sanitize_key(wp_unslash($_GET['page'])), 'tag-groups') !== 0) {
769 /* Tooltip doesn't work well in Gutenberg sidebar */
770 // phpcs:ignore Squiz.PHP.CommentedOutCode.Found
771 // if ( ( empty( $_GET['page'] ) || strpos( $_GET['page'], 'tag-groups' ) !== 0 ) &&
772 // ( ! is_object( $screen ) || ! property_exists( $screen, 'base' ) || 'post' != $screen->base ) ) {
773 return $text;
774 }
775 $view = new TagGroups_View('partials/admin_footer_tooltip');
776 return $text . $view->return_html();
777 }
778
779 /**
780 * Adds a pull-down menu to the filters above the posts.
781 * Based on the code by Ohad Raz, http://wordpress.stackexchange.com/q/45436/2487
782 * License: Creative Commons Share Alike
783 *
784 * @return void
785 */
786 // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps -- Legacy method naming
787 public static function add_post_filter()
788 {
789 global $tag_group_groups ;
790 if (!TagGroups_Options::get_option('tag_group_show_filter', 1)) {
791 return;
792 }
793 $enabled_taxonomies = TagGroups_Taxonomy::get_enabled_taxonomies();
794 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only filter display
795 $post_type = (isset($_GET['post_type']) ? sanitize_title(wp_unslash($_GET['post_type'])) : 'post');
796 if (count(array_intersect($enabled_taxonomies, get_object_taxonomies($post_type)))) {
797 $term_groups = $tag_group_groups->get_all_term_group_label();
798 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only filter display
799 $current_term_group = (isset($_GET['tg_filter_posts_value']) ? sanitize_text_field(wp_unslash($_GET['tg_filter_posts_value'])) : '');
800 $view = new TagGroups_View('admin/post_filter');
801 $view->set(array(
802 'current_term_group' => $current_term_group,
803 'parents' => $tag_group_groups->get_parents(),
804 'term_groups' => $term_groups,
805 ));
806 $view->render();
807 }
808 }
809
810 /**
811 * Applies the filter, if used.
812 * Based on the code by Ohad Raz, http://wordpress.stackexchange.com/q/45436/2487
813 * License: Creative Commons Share Alike
814 *
815 * @global type $pagenow
816 * @param type $query
817 * @return type
818 */
819 // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps -- Legacy method naming
820 public static function apply_post_filter($query)
821 {
822 global $pagenow, $tag_group_groups ;
823 if ('edit.php' != $pagenow) {
824 return $query;
825 }
826 $show_filter_posts = TagGroups_Options::get_option('tag_group_show_filter', 0);
827 if (!$show_filter_posts) {
828 return $query;
829 }
830
831 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only filter application
832 if (isset($_GET['post_type'])) {
833 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only filter application
834 $post_type = sanitize_title(wp_unslash($_GET['post_type']));
835 } else {
836 $post_type = 'post';
837 }
838
839 /**
840 * Losing here the filter by language from Polylang, but currently no other way to show any posts when combining tax_query and meta_query
841 */
842 unset($query->query_vars['tax_query']);
843 $enabled_taxonomies = TagGroups_Taxonomy::get_enabled_taxonomies();
844 // note: removed restriction count( $tg_taxonomy ) <= 1 - rather let user figure out if the result works
845 $taxonomy_intersect = array_intersect($enabled_taxonomies, get_object_taxonomies($post_type));
846 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only filter application
847 if (count($taxonomy_intersect) && isset($_GET['tg_filter_posts_value']) && '' !== $_GET['tg_filter_posts_value']) {
848 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only filter application
849 $group_id = (int) $_GET['tg_filter_posts_value'];
850 if (TagGroups_Utilities::is_premium_plan()) {
851 $group_ids = $tag_group_groups->expand_parents(array( $group_id ));
852
853 if (count($group_ids) == 0) {
854 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Intentional meta_query for post filtering by tag groups
855 $query->query_vars['meta_query'] = array(
856 array(
857 'key' => '_cm_post_terms_dummy',
858 'compare' => 'EXISTS',
859 ),
860 );
861 } elseif (count($group_ids) == 1) {
862 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Intentional meta_query for post filtering by tag groups
863 $query->query_vars['meta_query'] = array(
864 array(
865 'key' => '_cm_post_terms_' . $group_ids[0],
866 'compare' => 'EXISTS',
867 ),
868 );
869 } else {
870 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Intentional meta_query for post filtering by tag groups
871 $query->query_vars['meta_query'] = array(
872 'relation' => 'OR',
873 );
874
875 foreach ($group_ids as $group_id) {
876 $query->query_vars['meta_query'][] = array(
877 'key' => '_cm_post_terms_' . $group_id,
878 'compare' => 'EXISTS',
879 );
880 }
881 }
882
883 return $query;
884 }
885
886 $tg_group = new TagGroups_Group($group_id);
887 $tags = $tg_group->get_group_terms($taxonomy_intersect, true, 'ids');
888 if (empty($tags)) {
889 /**
890 * We use a workaround to render an empty list
891 */
892 $query->query_vars['tag__in'] = array( 0 );
893 } else {
894 $query->query_vars['tag__in'] = $tags;
895 }
896 }
897
898 return $query;
899 }
900
901 /**
902 * AJAX handler to get a feed
903 */
904 // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps -- Legacy method naming
905 public static function ajax_get_feed()
906 {
907 if (!current_user_can('manage_options')) {
908 wp_die(-1, 403);
909 }
910
911 check_ajax_referer('tg_get_feed_nonce', 'nonce');
912
913 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- AJAX handler
914 if (isset($_REQUEST['url'])) {
915 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- AJAX handler
916 $url = esc_url_raw(wp_unslash($_REQUEST['url']));
917 } else {
918 $url = '';
919 }
920
921
922 if (strpos($url, 'https://chattymango.com/') !== 0) {
923 TagGroups_Error::log('[Tag Groups] Wrong feed URL: ' . $url);
924 TagGroups_Utilities::die();
925 }
926
927
928 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- AJAX handler
929 if (isset($_REQUEST['amount'])) {
930 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- AJAX handler
931 $amount = (int) $_REQUEST['amount'];
932 } else {
933 $amount = 5;
934 }
935 $amount = max(1, min(20, $amount));
936
937 /**
938 * Assuming that the posts URL is the $url minus the trailing /feed
939 */
940 $posts_url = preg_replace('/(.+)feed\\/?/i', '$1', $url);
941 $rss = new TagGroups_Feed();
942 if (defined('WP_DEBUG')) {
943 $rss->set_debug(WP_DEBUG);
944 }
945 $rss->set_url($url)->set_posts_url($posts_url)->set_amount($amount);
946 wp_send_json($rss->get_html());
947 }
948
949 /**
950 * Modifies the query to retrieve tags for filtering in the backend.
951 *
952 * @param array $pieces
953 * @param array $taxonomies
954 * @param array $args
955 * @return array
956 */
957 // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps -- Legacy method naming
958 public static function modify_terms_query($pieces, $taxonomies, $args)
959 {
960 $taxonomy = TagGroups_Utilities::get_first_element($taxonomies);
961 if (empty($taxonomy) || is_array($taxonomy)) {
962 $taxonomy = 'post_tag';
963 }
964 $show_filter_tags = TagGroups_Options::get_option('tag_group_show_filter_tags', 0);
965 if (!$show_filter_tags) {
966 return $pieces;
967 }
968 $tag_group_tags_filter = TagGroups_Options::get_option('tag_group_tags_filter', array());
969 if (isset($tag_group_tags_filter[$taxonomy])) {
970 $group_id = $tag_group_tags_filter[$taxonomy];
971 if ($group_id > 0) {
972 // check if group exists (could be deleted since last time the filter was set)
973 $tg_group = new TagGroups_Group($group_id);
974 if (!$tg_group->exists()) {
975 $group_id = -1;
976 }
977 }
978 } else {
979 $group_id = -1;
980 }
981
982
983 if ($group_id > -1) {
984 $tg_group = new TagGroups_Group($group_id);
985 $mq_sql = $tg_group->terms_clauses();
986 if (!empty($pieces['join'])) {
987 $pieces['join'] .= $mq_sql['join'];
988 } else {
989 $pieces['join'] = $mq_sql['join'];
990 }
991
992
993 if (!empty($pieces['where'])) {
994 $pieces['where'] .= $mq_sql['where'];
995 } else {
996 $pieces['where'] = $mq_sql['where'];
997 }
998 }
999
1000 return $pieces;
1001 }
1002
1003 /**
1004 * Adds Settings link to plugin list
1005 *
1006 * @param array $links
1007 * @return array
1008 */
1009 // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps -- Legacy method naming
1010 public static function add_plugin_settings_link($links)
1011 {
1012 $settings_link = '<a href="' . admin_url('admin.php?page=tag-groups-settings') . '">' . __('Settings', 'tag-groups') . '</a>';
1013 array_unshift($links, $settings_link);
1014 return $links;
1015 }
1016
1017 /**
1018 * Add a warning if the WPML/Polylang language switch is set to "all"
1019 *
1020 * @param void
1021 * @return void
1022 */
1023 // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps -- Legacy method naming
1024 public static function add_language_notice()
1025 {
1026 $screen = get_current_screen();
1027 if (!$screen || 'edit-tags' !== $screen->base && 'term' !== $screen->base) {
1028 return;
1029 }
1030 $enabled_taxonomies = TagGroups_Taxonomy::get_enabled_taxonomies();
1031 if (!in_array($screen->taxonomy, $enabled_taxonomies)) {
1032 return;
1033 }
1034
1035 if ('all' == TagGroups_WPML::get_current_language()) {
1036 $view = new TagGroups_View('partials/language_notice');
1037 $view->render();
1038 }
1039 }
1040
1041 /**
1042 * Add inline styling to the tags page
1043 *
1044 * @param void
1045 * @return void
1046 */
1047 // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps -- Legacy method naming
1048 public static function add_tag_page_styling()
1049 {
1050 $view = new TagGroups_View('partials/tag_page_inline_style');
1051 $view->render();
1052 }
1053
1054 /**
1055 * Recommend to run the migration
1056 *
1057 * @since 1.24.0
1058 *
1059 * @param void
1060 * @return void
1061 */
1062 // phpcs:ignore PSR1.Methods.CamelCapsMethodName.NotCamelCaps -- Legacy method naming
1063 public static function recommend_to_run_migration()
1064 {
1065 /* translators: %s is the href attribute with URL */
1066 TagGroups_Admin_Notice::add('info', sprintf(__('Please <a %s>click here to run the migration routines</a> to make sure we have migrated all tags.', 'tag-groups'), 'href="' . admin_url('admin.php?page=tag-groups-settings-general&process-tasks=migratetermmeta&task-set-name=Migration') . '"'));
1067 }
1068 }
1069 // class
1070
1071 }
1072