PluginProbe
Adminify – White Label, Admin Menu Editor, Login Customizer / 4.1.3
Adminify – White Label, Admin Menu Editor, Login Customizer v4.1.3
4.3.1 4.3.0 4.2.26 4.2.25 4.2.24 4.2.23 4.2.22 4.2.21 4.2.20 4.2.19 4.2.18 4.2.17 4.2.16 4.2.15 4.2.14 4.2.13 4.2.12 4.2.11 4.2.10 4.2.9 4.2.8 4.2.7 4.2.6 4.2.5 4.1.17 All 164 releases
adminify / Inc / Modules / Folders / Folders.php

Folders.php in Adminify – White Label, Admin Menu Editor, Login Customizer 4.1.3, at Inc/Modules/Folders/Folders.php

1,131 lines 45.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WPAdminify\Inc\Modules\Folders;
4
5 use WPAdminify\Inc\Utils;
6 use WPAdminify\Inc\Admin\AdminSettings;
7 if ( !defined( 'ABSPATH' ) ) {
8 exit;
9 }
10 class Folders {
11 private static $postIds;
12
13 public $options;
14
15 public function __construct() {
16 $this->options = (array) AdminSettings::get_instance()->get( 'folders' );
17 $needed_keys = ['user_roles', 'enable_for', 'media'];
18 add_action( 'init', [$this, 'register_folders_terms'], 999 );
19 add_action( 'admin_footer', [$this, 'add_footer_markup'] );
20 add_action( 'admin_enqueue_scripts', [$this, 'enqueue_scripts'], PHP_INT_MAX );
21 add_action( 'wp_ajax_adminify_folder', [$this, 'handle_adminify_folder'] );
22 add_action( 'ajax_query_attachments_args', [$this, 'filter_grid'] );
23 add_filter( 'pre_get_posts', [$this, 'filter_list'] );
24 add_action( 'admin_init', [$this, 'modify_columns_actions'] );
25 add_action( 'print_media_templates', [$this, 'modify_media_templates'] );
26 add_filter( 'wp_prepare_attachment_for_js', [$this, 'modify_attachment_for_js'] );
27 add_action( 'pre-upload-ui', [$this, 'wp_adminify_select_folder_when_upload'] );
28 add_filter(
29 'attachment_fields_to_edit',
30 [$this, 'wp_adminify_edit_attachment_fields'],
31 25,
32 2
33 );
34 add_action( 'wp_ajax_wp_adminify_assign_media_folder', array($this, 'wp_adminify_handle_folder_assignment') );
35 add_action( 'restrict_manage_posts', [$this, 'wp_adminify_show_folders_in_list_filter'] );
36 add_action( 'add_attachment', [$this, 'wp_adminify_assign_media_folder_to_new_attachment'] );
37 add_action( 'enqueue_block_editor_assets', [$this, 'enqueue_scripts_for_media_uploads'], PHP_INT_MAX );
38 // add_action( 'customize_controls_print_footer_scripts', [ $this, 'add_adminify_body_class' ] );
39 // add_action( 'customize_controls_enqueue_scripts', [ $this, 'enqueue_scripts_for_media_uploads' ], PHP_INT_MAX );
40 // Elementor editor support
41 // add_action( 'elementor/editor/footer', [ $this, 'add_adminify_body_class' ] );
42 // add_action( 'elementor/editor/before_enqueue_scripts', [ $this, 'enqueue_scripts_for_media_uploads' ], PHP_INT_MAX );
43 }
44
45 /**
46 * Add wp-adminify class to body
47 * Required for folder widget CSS to work in media modal (Elementor, Customizer, etc.)
48 */
49 public function add_adminify_body_class() {
50 if ( !(bool) $this->options['media'] ) {
51 return;
52 }
53 ?>
54 <script>
55 document.body.classList.add('wp-adminify');
56 </script>
57 <?php
58 }
59
60 public function modify_attachment_for_js( $response ) {
61 if ( !(bool) $this->options['media'] ) {
62 return $response;
63 }
64 $folders = wp_get_post_terms( $response['id'], 'media_folder' );
65 $folder_ids = wp_list_pluck( $folders, 'term_id' );
66 $response['media_folder'] = implode( ',', $folder_ids );
67 return $response;
68 }
69
70 public function get_post_types() {
71 $post_types = get_post_types( [
72 'public' => true,
73 ] );
74 // @todo add more configurations here
75 return $post_types;
76 }
77
78 public function filter_list( $query ) {
79 global $typenow;
80 global $pagenow;
81 $post_type = $typenow;
82 if ( empty( $post_type ) ) {
83 if ( $pagenow == 'edit.php' ) {
84 $post_type = 'post';
85 } elseif ( $pagenow == 'upload.php' ) {
86 $post_type = 'attachment';
87 }
88 }
89 if ( !isset( $query->query['post_type'] ) ) {
90 return $query;
91 }
92 $taxonomy = self::get_post_type_taxonomy( $post_type );
93 if ( !isset( $_REQUEST[$taxonomy] ) ) {
94 return $query;
95 }
96 $term = sanitize_text_field( wp_unslash( $_REQUEST[$taxonomy] ) );
97 if ( $term != '-1' ) {
98 return $query;
99 }
100 unset($query->query_vars[$taxonomy]);
101 $tax_query = [
102 'taxonomy' => $taxonomy,
103 'operator' => __( 'NOT EXISTS', 'adminify' ),
104 ];
105 $query->set( 'tax_query', [$tax_query] );
106 $query->tax_query = new \WP_Tax_Query([$tax_query]);
107 return $query;
108 }
109
110 public function filter_grid( $args ) {
111 $taxonomy = self::get_post_type_taxonomy( 'attachment' );
112 if ( !isset( $args[$taxonomy] ) ) {
113 return $args;
114 }
115 $term = sanitize_text_field( $args[$taxonomy] );
116 if ( $term != '-1' ) {
117 return $args;
118 }
119 unset($args[$taxonomy]);
120 $args['tax_query'] = [[
121 'taxonomy' => $taxonomy,
122 'operator' => __( 'NOT EXISTS', 'adminify' ),
123 ]];
124 return $args;
125 }
126
127 public function is_module_active( $post_type = null ) {
128 $current_screen = get_current_screen();
129 if ( empty( $current_screen->base ) || !in_array( $current_screen->base, ['upload', 'edit', 'media'] ) ) {
130 return false;
131 }
132 if ( $current_screen->base === 'media' && $current_screen->action === 'add' ) {
133 $post_type = 'attachment';
134 }
135 if ( empty( $post_type ) ) {
136 if ( empty( $post_type = $current_screen->post_type ) ) {
137 return false;
138 }
139 }
140 if ( $post_type == 'attachment' ) {
141 return (bool) $this->options['media'];
142 }
143 $default_post_types = ['post', 'page'];
144 $folders_enable_for = (array) $this->options['enable_for'];
145 $folders_enable_for = array_intersect( $folders_enable_for, $default_post_types );
146 if ( in_array( $post_type, $folders_enable_for ) ) {
147 return true;
148 }
149 return false;
150 }
151
152 // Add folder app markup in footer but move it to wpbody
153 public function add_footer_markup() {
154 if ( $this->is_module_active() ) {
155 ?>
156 <div id="wp-adminify--folder-app"></div>
157 <script>
158 (function() {
159 var folderApp = document.getElementById('wp-adminify--folder-app');
160 var wpbody = document.getElementById('wpbody');
161 var wpbodyContent = document.getElementById('wpbody-content');
162 if (folderApp && wpbody && wpbodyContent) {
163 wpbody.insertBefore(folderApp, wpbodyContent);
164 }
165 })();
166 </script>
167 <?php
168 }
169 }
170
171 public function get_uncategorized_posts( $post_type ) {
172 global $wpdb;
173 $post_table = $wpdb->prefix . 'posts';
174 $term_table = $wpdb->prefix . 'term_relationships';
175 $term_taxonomy_table = $wpdb->prefix . 'term_taxonomy';
176 $post_type_tax = self::get_post_type_taxonomy( $post_type );
177 if ( $post_type != 'attachment' ) {
178 $query = "SELECT COUNT(DISTINCT({$post_table}.ID)) AS total_records FROM {$post_table} WHERE 1=1 AND (\n NOT EXISTS (\n SELECT 1\n FROM {$term_table}\n INNER JOIN {$term_taxonomy_table}\n ON {$term_taxonomy_table}.term_taxonomy_id = {$term_table}.term_taxonomy_id\n WHERE {$term_taxonomy_table}.taxonomy = '%s'\n AND {$term_table}.object_id = {$post_table}.ID\n )\n ) AND {$post_table}.post_type = '%s' AND (({$post_table}.post_status = 'publish' OR {$post_table}.post_status = 'future' OR {$post_table}.post_status = 'draft' OR {$post_table}.post_status = 'private'))";
179 } else {
180 $query = "SELECT COUNT(DISTINCT({$post_table}.ID)) AS total_records FROM {$post_table} WHERE 1=1 AND (\n NOT EXISTS (\n SELECT 1\n FROM {$term_table}\n INNER JOIN {$term_taxonomy_table}\n ON {$term_taxonomy_table}.term_taxonomy_id = {$term_table}.term_taxonomy_id\n WHERE {$term_taxonomy_table}.taxonomy = '%s'\n AND {$term_table}.object_id = {$post_table}.ID\n )\n ) AND {$post_table}.post_type = '%s' AND {$post_table}.post_status = 'inherit'";
181 }
182 $query = $wpdb->prepare( $query, $post_type_tax, $post_type );
183 $total = $wpdb->get_var( $query );
184 return ( empty( $total ) ? 0 : $total );
185 }
186
187 public function get_folders( $post_type, $post_type_tax ) {
188 $folders = get_terms( $post_type_tax, [
189 'hide_empty' => false,
190 'pad_counts' => true,
191 ] );
192 if ( is_wp_error( $folders ) ) {
193 return [];
194 }
195 foreach ( $folders as $folder ) {
196 $posts = $this->get_posts_by_folder( $folder->term_id, $post_type, $post_type_tax );
197 $color = get_term_meta( $folder->term_id, '_wp_adminify_fodler_color', true );
198 if ( empty( $color ) ) {
199 $color = '';
200 }
201 $folder->color = $color;
202 $folder->count = count( $posts );
203 $folder->posts = $posts;
204 }
205 return $folders;
206 }
207
208 public function enqueue_scripts() {
209 if ( !$this->is_module_active() ) {
210 return;
211 }
212 $current_screen = get_current_screen();
213 wp_enqueue_style(
214 'wp-adminify--folder',
215 WP_ADMINIFY_URL . 'assets/admin/css/wp-adminify--folder' . Utils::assets_ext( '.css' ),
216 [],
217 WP_ADMINIFY_VER
218 );
219 wp_enqueue_style(
220 'wp-adminify-simple-line-icons',
221 WP_ADMINIFY_ASSETS . '/vendors/font-icons/simple-line-icons/css/simple-line-icons' . Utils::assets_ext( '.css' ),
222 false,
223 WP_ADMINIFY_VER
224 );
225 $post_type = $current_screen->post_type;
226 $post_type_tax = self::get_post_type_taxonomy( $post_type );
227 if ( $current_screen->base === 'media' && $current_screen->action === 'add' ) {
228 $post_type = 'attachment';
229 $post_type_tax = 'media_folder';
230 }
231 $media_folders = get_terms( [
232 'taxonomy' => 'media_folder',
233 'hide_empty' => false,
234 ] );
235 $media_folders_data = [];
236 foreach ( $media_folders as $media_folder ) {
237 $media_folders_data[$media_folder->term_id] = [
238 'name' => $media_folder->name,
239 'slug' => $media_folder->slug,
240 'term_id' => $media_folder->term_id,
241 ];
242 }
243 $data = [
244 'adminurl' => admin_url(),
245 'ajaxurl' => admin_url( 'admin-ajax.php' ),
246 'post_type' => $post_type,
247 'nonce' => wp_create_nonce( '__adminify-folder-secirity__' ),
248 'post_type_tax' => $post_type_tax,
249 'media_folders_data' => $media_folders_data,
250 'is_pro' => wp_validate_boolean( jltwp_adminify()->can_use_premium_code__premium_only() ),
251 'pro_notice' => Utils::adminify_upgrade_pro(),
252 ];
253 $data = array_merge( $data, $this->refreshed_folder_data( $post_type, $post_type_tax ) );
254 // Adminify Folder
255 // wp_enqueue_script('wp-adminify-vue-vendors', WP_ADMINIFY_ASSETS . 'admin/js/vendor' . Utils::assets_ext('.js'), [], WP_ADMINIFY_VER, true);
256 wp_enqueue_script(
257 'wp-adminify--folder',
258 WP_ADMINIFY_ASSETS . 'admin/js/wp-adminify--folder' . Utils::assets_ext( '.js' ),
259 array('jquery', 'jquery-ui-droppable', 'jquery-ui-draggable'),
260 WP_ADMINIFY_VER,
261 true
262 );
263 wp_localize_script( 'wp-adminify--folder', 'wp_adminify__folder_data', $data );
264 $inline_script_content = '
265 (function($) {
266 $(document).ready(function() {
267
268 let selectedFolder = null;
269
270 if (typeof wp.media !== "undefined") {
271 const urlParams = new URLSearchParams(window.location.search);
272 const currentFolder = urlParams.get("media_folder");
273 selectedFolder = currentFolder;
274
275 const MediaLibraryTaxonomyFilter = wp.media.view.AttachmentFilters.extend({
276 id: "wp-adminify-media-folder-filter",
277 createFilters: function() {
278 const filters = {};
279 filters.all = {
280 text: "All folders",
281 props: { folders: "" },
282 priority: 10
283 };
284 _.each(window.wp_adminify__folder_data?.media_folders_data || {}, function(value) {
285 filters[value.slug] = {
286 text: value.name,
287 props: { folders: value.slug },
288 priority: 20
289 };
290 });
291 this.filters = filters;
292 },
293 initialize: function() {
294 wp.media.view.AttachmentFilters.prototype.initialize.apply(this, arguments);
295 if (currentFolder) {
296 this.model.set("folders", currentFolder);
297 setTimeout(() => {
298 this.$el.find("select").val(currentFolder);
299 }, 100);
300 }
301 }
302 });
303
304 const AttachmentsBrowser = wp.media.view.AttachmentsBrowser;
305 wp.media.view.AttachmentsBrowser = AttachmentsBrowser.extend({
306 createToolbar: function() {
307 AttachmentsBrowser.prototype.createToolbar.call(this);
308 this.toolbar.set("MediaLibraryTaxonomyFilter", new MediaLibraryTaxonomyFilter({
309 controller: this.controller,
310 model: this.collection.props,
311 priority: -75
312 }).render());
313 }
314 });
315 }
316
317 if (typeof wp.Uploader === "function") {
318 $.extend(wp.Uploader.prototype, {
319 init: function() {
320 selectedFolder = $("#folders").val() || currentFolder;
321
322 $("body").on("change", "#folders", function() {
323 selectedFolder = $(this).val();
324 });
325
326 if (this.uploader) {
327 this.uploader.bind("BeforeUpload", function(up, file) {
328 up.settings.multipart_params = up.settings.multipart_params || {};
329 up.settings.multipart_params.folder_id = selectedFolder;
330 });
331 this.uploader.bind("UploadComplete", function() {
332 if (typeof wp !== "undefined" && wp.media && wp.media.frame) {
333 const frame = wp.media.frame;
334 const state = frame.state();
335 const library = state.get("library");
336
337 if (library) {
338 library.props.set("ignore", Date.now());
339 state.trigger("reset");
340 }
341 }
342 });
343 }
344 }
345 });
346 }
347
348 });
349 })(jQuery);
350 ';
351 wp_add_inline_script( 'media-views', $inline_script_content, 'after' );
352 }
353
354 function enqueue_scripts_for_media_uploads() {
355 // Skip on customizer - not needed there
356 // if ( is_customize_preview() || ( function_exists( 'get_current_screen' ) && get_current_screen() && get_current_screen()->base === 'customize' ) ) {
357 // return;
358 // }
359 wp_enqueue_media();
360 wp_enqueue_script( 'media-views' );
361 wp_enqueue_script( 'wp-dom-ready' );
362 // Enqueue folder styles for modal
363 wp_enqueue_style(
364 'wp-adminify--folder',
365 WP_ADMINIFY_URL . 'assets/admin/css/wp-adminify--folder' . Utils::assets_ext( '.css' ),
366 [],
367 WP_ADMINIFY_VER
368 );
369 wp_enqueue_style(
370 'wp-adminify-simple-line-icons',
371 WP_ADMINIFY_ASSETS . '/vendors/font-icons/simple-line-icons/css/simple-line-icons' . Utils::assets_ext( '.css' ),
372 false,
373 WP_ADMINIFY_VER
374 );
375 // Enqueue folder script for modal sidebar
376 wp_enqueue_script(
377 'wp-adminify--popup-folder',
378 WP_ADMINIFY_ASSETS . 'admin/js/wp-adminify--popup-folder' . Utils::assets_ext( '.js' ),
379 array(
380 'jquery',
381 'jquery-ui-droppable',
382 'jquery-ui-draggable',
383 'wp-element',
384 'wp-dom-ready'
385 ),
386 WP_ADMINIFY_VER,
387 true
388 );
389 wp_enqueue_script(
390 'wp-adminify--folder',
391 WP_ADMINIFY_ASSETS . 'admin/js/wp-adminify--folder' . Utils::assets_ext( '.js' ),
392 array(
393 'jquery',
394 'jquery-ui-droppable',
395 'jquery-ui-draggable',
396 'wp-element',
397 'wp-dom-ready'
398 ),
399 WP_ADMINIFY_VER,
400 true
401 );
402 $post_type = 'attachment';
403 $post_type_tax = 'media_folder';
404 // Get simplified folder data for dropdown filter
405 $media_folders = get_terms( [
406 'taxonomy' => 'media_folder',
407 'hide_empty' => false,
408 ] );
409 $media_folders_data = [];
410 foreach ( $media_folders as $folder ) {
411 $media_folders_data[$folder->term_id] = [
412 'name' => $folder->name,
413 'slug' => $folder->slug,
414 'term_id' => $folder->term_id,
415 ];
416 }
417 // Build full folder data for React sidebar
418 $data = apply_filters( 'wp_adminify_folder_data', [
419 'adminurl' => admin_url(),
420 'ajaxurl' => admin_url( 'admin-ajax.php' ),
421 'post_type' => $post_type,
422 'nonce' => wp_create_nonce( '__adminify-folder-secirity__' ),
423 'post_type_tax' => $post_type_tax,
424 'media_folders_data' => $media_folders_data,
425 'is_pro' => wp_validate_boolean( jltwp_adminify()->can_use_premium_code__premium_only() ),
426 'pro_notice' => Utils::adminify_upgrade_pro(),
427 'is_rtl' => is_rtl(),
428 ] );
429 // Add full folder data (folders, hierarchy, counts)
430 $data = array_merge( $data, $this->refreshed_folder_data( $post_type, $post_type_tax ) );
431 // Localize the data to multiple script handles
432 // This ensures data is available in different contexts
433 wp_localize_script( 'wp-adminify--folder', 'wp_adminify__folder_data', $data );
434 wp_localize_script( 'wp-dom-ready', 'wp_adminify__folder_data', $data );
435 wp_localize_script( 'media-views', 'wp_adminify__folder_data', $data );
436 $inline_script_content = '
437 (function($) {
438 $(document).ready(function() {
439
440 let selectedFolder = null;
441
442 if (typeof wp.media !== "undefined") {
443 const urlParams = new URLSearchParams(window.location.search);
444 const currentFolder = urlParams.get("media_folder");
445 selectedFolder = currentFolder;
446
447 const MediaLibraryTaxonomyFilter = wp.media.view.AttachmentFilters.extend({
448 id: "wp-adminify-media-folder-filter",
449 createFilters: function() {
450 const filters = {};
451 filters.all = {
452 text: "All folders",
453 props: { folders: "" },
454 priority: 10
455 };
456 _.each(window.wp_adminify__folder_data?.media_folders_data || {}, function(value) {
457 filters[value.slug] = {
458 text: value.name,
459 props: { folders: value.slug },
460 priority: 20
461 };
462 });
463 this.filters = filters;
464 },
465 initialize: function() {
466 wp.media.view.AttachmentFilters.prototype.initialize.apply(this, arguments);
467 if (currentFolder) {
468 this.model.set("folders", currentFolder);
469 setTimeout(() => {
470 this.$el.find("select").val(currentFolder);
471 }, 100);
472 }
473 }
474 });
475
476 // Note: AttachmentsBrowser extension for folder sidebar is handled in wp-adminify--folder.js
477 }
478
479 if (typeof wp.Uploader === "function") {
480 $.extend(wp.Uploader.prototype, {
481 init: function() {
482 selectedFolder = $("#folders").val() || currentFolder;
483
484 $("body").on("change", "#folders", function() {
485 selectedFolder = $(this).val();
486 });
487
488 if (this.uploader) {
489 this.uploader.bind("BeforeUpload", function(up, file) {
490 up.settings.multipart_params = up.settings.multipart_params || {};
491 up.settings.multipart_params.folder_id = selectedFolder;
492 });
493 this.uploader.bind("UploadComplete", function() {
494 if (typeof wp !== "undefined" && wp.media && wp.media.frame) {
495 const frame = wp.media.frame;
496 const state = frame.state();
497 const library = state.get("library");
498
499 if (library) {
500 library.props.set("ignore", Date.now());
501 state.trigger("reset");
502 }
503 }
504 });
505 }
506 }
507 });
508 }
509
510 });
511 })(jQuery);
512 ';
513 wp_add_inline_script( 'media-views', $inline_script_content, 'after' );
514 }
515
516 public function modify_media_templates() {
517 ?>
518
519 <script>
520 var attachment_template = jQuery('#tmpl-attachment');
521 if ( attachment_template.length ) {
522 var template = attachment_template.html().replace( 'data.orientation }}">', 'data.orientation }}" data-folders="{{ data.media_folder }}">' );
523 attachment_template.html( template );
524 }
525 </script>
526
527 <?php
528 }
529
530 public function handle_adminify_folder() {
531 check_ajax_referer( '__adminify-folder-secirity__' );
532 if ( !current_user_can( 'edit_posts' ) ) {
533 wp_send_json_error( array(
534 'message' => __( 'You do not have permission to perform this action.', 'adminify' ),
535 ) );
536 }
537 if ( !empty( $_POST['route'] ) ) {
538 $route_handler = 'handle_' . sanitize_text_field( wp_unslash( $_POST['route'] ) );
539 if ( is_callable( get_class( $this ), $route_handler ) ) {
540 $this->{$route_handler}( $_POST );
541 }
542 }
543 wp_send_json_error( [
544 'message' => __( 'Something is wrong, no route found' ),
545 ], 400 );
546 }
547
548 public function handle_delete_folders( $data ) {
549 if ( empty( $data['term_ids'] ) || empty( $data['post_type'] ) || empty( $data['post_type_tax'] ) ) {
550 wp_send_json_error( [
551 'message' => __( 'Something is wrong, few args are missing' ),
552 ], 400 );
553 }
554 $term_ids = $data['term_ids'];
555 $post_type = $data['post_type'];
556 $post_type_tax = $data['post_type_tax'];
557 foreach ( $term_ids as $term_id ) {
558 wp_delete_term( $term_id, $post_type_tax );
559 }
560 $data = $this->refreshed_folder_data( $post_type, $post_type_tax );
561 wp_send_json_success( $data );
562 }
563
564 public function handle_rename_folder( $data ) {
565 if ( empty( $data['term_id'] ) || empty( $data['post_type'] ) || empty( $data['post_type_tax'] ) || empty( $data['folder_name'] ) || empty( $data['folder_color_tag'] ) ) {
566 wp_send_json_error( [
567 'message' => __( 'Something is wrong, few args are missing' ),
568 ], 400 );
569 }
570 $term_id = $data['term_id'];
571 $folder_name = $data['folder_name'];
572 $post_type = $data['post_type'];
573 $post_type_tax = $data['post_type_tax'];
574 $folder_color_tag = $data['folder_color_tag'];
575 $update = wp_update_term( $term_id, $post_type_tax, [
576 'name' => $folder_name,
577 ] );
578 if ( is_wp_error( $update ) ) {
579 wp_send_json_success( [
580 'message' => $update->get_error_message(),
581 ], 202 );
582 }
583 update_term_meta( $term_id, '_wp_adminify_fodler_color', $folder_color_tag );
584 $data = $this->refreshed_folder_data( $post_type, $post_type_tax );
585 wp_send_json_success( $data );
586 }
587
588 public function handle_move_to_folder( $data ) {
589 if ( empty( $data['post_ids'] ) || empty( $data['folder_id'] ) || empty( $data['post_type'] ) || empty( $data['post_type_tax'] ) ) {
590 wp_send_json_error( [
591 'message' => __( 'Something is wrong, few args are missing' ),
592 ], 400 );
593 }
594 $post_ids = (array) $data['post_ids'];
595 $folder_id = sanitize_text_field( $data['folder_id'] );
596 $post_type = sanitize_text_field( $data['post_type'] );
597 $post_type_tax = sanitize_text_field( $data['post_type_tax'] );
598 global $mode;
599 $mode = ( empty( $data['mode'] ) ? 'list' : sanitize_text_field( $data['mode'] ) );
600 $move_to_folder = wp_validate_boolean( $data['move_to_folder'] );
601 foreach ( $post_ids as $post_id ) {
602 if ( $folder_id == 'uncategorized' ) {
603 wp_set_object_terms(
604 $post_id,
605 '',
606 $post_type_tax,
607 false
608 );
609 continue;
610 }
611 $term = get_term( $folder_id );
612 if ( !empty( $term ) && isset( $term->slug ) ) {
613 wp_set_object_terms(
614 $post_id,
615 $term->slug,
616 $post_type_tax,
617 !$move_to_folder
618 );
619 }
620 }
621 $updated_rows = '';
622 if ( $post_type == 'attachment' ) {
623 global $wp_query;
624 $args = [
625 'posts_per_page' => count( $post_ids ),
626 'orderby' => 'title',
627 'order' => 'ASC',
628 'post_type' => $post_type,
629 'post_status' => 'any',
630 'post__in' => $post_ids,
631 ];
632 $wp_query = new \WP_Query($args);
633 $wp_list_table = \_get_list_table( 'WP_Media_List_Table', [
634 'screen' => sanitize_text_field( wp_unslash( $_POST['screen'] ) ),
635 ] );
636 foreach ( $post_ids as $post_id ) {
637 ob_start();
638 $wp_list_table->display_rows();
639 $updated_rows = ob_get_clean();
640 }
641 } else {
642 $wp_list_table = \_get_list_table( 'WP_Posts_List_Table', [
643 'screen' => sanitize_text_field( wp_unslash( $_POST['screen'] ) ),
644 ] );
645 foreach ( $post_ids as $post_id ) {
646 $level = 0;
647 if ( is_post_type_hierarchical( $wp_list_table->screen->post_type ) ) {
648 $request_post = [get_post( $post_id )];
649 $parent = $request_post[0]->post_parent;
650 while ( $parent > 0 ) {
651 $parent_post = get_post( $parent );
652 $parent = $parent_post->post_parent;
653 $level++;
654 }
655 }
656 ob_start();
657 $wp_list_table->display_rows( [get_post( $post_id )], $level );
658 $updated_rows .= ob_get_clean();
659 }
660 }
661 $data = [
662 'updated_rows' => $updated_rows,
663 ];
664 $data = array_merge( $data, $this->refreshed_folder_data( $post_type, $post_type_tax ) );
665 wp_send_json_success( $data );
666 }
667
668 public function handle_refresh_folders( $data ) {
669 if ( empty( $data['post_type'] ) || empty( $data['post_type_tax'] ) ) {
670 wp_send_json_error( [
671 'message' => __( 'Something is wrong, few args are missing' ),
672 ], 400 );
673 }
674 $post_type = $data['post_type'];
675 $post_type_tax = $data['post_type_tax'];
676 wp_send_json_success( $this->refreshed_folder_data( $post_type, $post_type_tax ) );
677 }
678
679 public function handle_create_new_folder( $data ) {
680 if ( empty( $data['post_type'] ) || empty( $data['post_type_tax'] ) ) {
681 wp_send_json_error( [
682 'message' => __( 'Something is wrong, few args are missing' ),
683 ], 400 );
684 }
685 if ( empty( $data['new_folder_name'] ) || empty( $data['folder_color_tag'] ) ) {
686 wp_send_json_error( [
687 'message' => __( 'Something is wrong, few args are missing' ),
688 ], 400 );
689 }
690 $post_type = $data['post_type'];
691 $post_type_tax = $data['post_type_tax'];
692 $new_folder_name = $data['new_folder_name'];
693 $folder_color_tag = $data['folder_color_tag'];
694 $parent_term_id = 0;
695 if ( jltwp_adminify()->can_use_premium_code__premium_only() && !empty( $data['parent_folder'] ) ) {
696 $parent_term_id = $data['parent_folder'];
697 }
698 $insert_data = wp_insert_term( $new_folder_name, $post_type_tax, [
699 'parent' => $parent_term_id,
700 ] );
701 if ( !is_wp_error( $insert_data ) ) {
702 update_term_meta( $insert_data['term_id'], '_wp_adminify_fodler_color', $folder_color_tag );
703 wp_send_json_success( $this->refreshed_folder_data( $post_type, $post_type_tax ) );
704 } else {
705 wp_send_json_success( [
706 'message' => $insert_data->get_error_message(),
707 ], 202 );
708 }
709 }
710
711 function refreshed_folder_data( $post_type, $post_type_tax ) {
712 remove_filter( 'pre_get_posts', [$this, 'filter_list'] );
713 return [
714 'folders' => $this->get_folders( $post_type, $post_type_tax ),
715 'folder_hierarchy' => _get_term_hierarchy( $post_type_tax ),
716 'total_posts' => wp_count_posts( $post_type ),
717 'total_uncat_posts' => $this->get_uncategorized_posts( $post_type ),
718 ];
719 }
720
721 function wp_set_post_categories( $post_ID = 0, $post_categories = [], $append = false ) {
722 $post_ID = (int) $post_ID;
723 $post_type = get_post_type( $post_ID );
724 $post_status = get_post_status( $post_ID );
725 // If $post_categories isn't already an array, make it one.
726 $post_categories = (array) $post_categories;
727 if ( empty( $post_categories ) ) {
728 /**
729 * Filters post types (in addition to 'post') that require a default category.
730 *
731 * @since 5.5.0
732 *
733 * @param string[] $post_types An array of post type names. Default empty array.
734 */
735 $default_category_post_types = apply_filters( 'default_category_post_types', [] );
736 // Regular posts always require a default category.
737 $default_category_post_types = array_merge( $default_category_post_types, ['post'] );
738 if ( in_array( $post_type, $default_category_post_types, true ) && is_object_in_taxonomy( $post_type, 'category' ) && 'auto-draft' !== $post_status ) {
739 $post_categories = [get_option( 'default_category' )];
740 $append = false;
741 } else {
742 $post_categories = [];
743 }
744 } elseif ( 1 === count( $post_categories ) && '' === reset( $post_categories ) ) {
745 return true;
746 }
747 return wp_set_post_terms(
748 $post_ID,
749 $post_categories,
750 'category',
751 $append
752 );
753 }
754
755 public function get_posts_by_folder( $term_id, $post_type, $taxonomy ) {
756 $posts = get_posts( [
757 'numberposts' => -1,
758 'post_status' => 'any',
759 'post_type' => $post_type,
760 'fields' => 'ids',
761 'tax_query' => [[
762 'operator' => 'IN',
763 'taxonomy' => $taxonomy,
764 'field' => 'term_id',
765 'terms' => $term_id,
766 ]],
767 ] );
768 return $posts;
769 }
770
771 public static function get_post_type_taxonomy( $post_type ) {
772 if ( $post_type == 'page' ) {
773 return 'folder';
774 }
775 if ( $post_type == 'attachment' ) {
776 $post_type = 'media';
777 }
778 return $post_type . '_folder';
779 }
780
781 public function register_folders_terms() {
782 $post_types = $this->get_post_types();
783 foreach ( $post_types as $post_type ) {
784 $labels = [
785 'name' => esc_html__( 'Folders', 'adminify' ),
786 'singular_name' => esc_html__( 'Folder', 'adminify' ),
787 'all_items' => esc_html__( 'All Folders', 'adminify' ),
788 'edit_item' => esc_html__( 'Edit Folder', 'adminify' ),
789 'update_item' => esc_html__( 'Update Folder', 'adminify' ),
790 'add_new_item' => esc_html__( 'Add New Folder', 'adminify' ),
791 'new_item_name' => esc_html__( 'Add folder name', 'adminify' ),
792 'menu_name' => esc_html__( 'Folders', 'adminify' ),
793 'search_items' => esc_html__( 'Search Folders', 'adminify' ),
794 'parent_item' => esc_html__( 'Parent Folder', 'adminify' ),
795 ];
796 $args = [
797 'label' => esc_html__( 'Folder', 'adminify' ),
798 'labels' => $labels,
799 'show_tagcloud' => false,
800 'hierarchical' => true,
801 'public' => false,
802 'show_ui' => false,
803 'show_in_menu' => false,
804 'show_in_rest' => true,
805 'show_admin_column' => true,
806 'query_var' => true,
807 'rewrite' => false,
808 'capabilities' => [
809 'manage_terms' => 'manage_categories',
810 'edit_terms' => 'manage_categories',
811 'delete_terms' => 'manage_categories',
812 'assign_terms' => 'manage_categories',
813 ],
814 ];
815 $taxonomy = self::get_post_type_taxonomy( $post_type );
816 register_taxonomy( $taxonomy, $post_type, $args );
817 }
818 }
819
820 public function modify_columns_actions() {
821 $post_types = $this->get_post_types();
822 foreach ( $post_types as $post_type ) {
823 if ( $post_type == 'post' ) {
824 add_filter( 'manage_edit-post_columns', [$this, 'manage_columns_head'] );
825 add_filter( 'manage_posts_columns', [$this, 'manage_columns_head'] );
826 add_action(
827 'manage_posts_custom_column',
828 [$this, 'manage_columns_content'],
829 10,
830 2
831 );
832 add_filter( 'bulk_actions-edit-post', [$this, 'custom_bulk_action'] );
833 } elseif ( $post_type == 'page' ) {
834 add_filter( 'manage_edit-page_columns', [$this, 'manage_columns_head'] );
835 add_filter( 'manage_page_posts_columns', [$this, 'manage_columns_head'] );
836 add_action(
837 'manage_page_posts_custom_column',
838 [$this, 'manage_columns_content'],
839 10,
840 2
841 );
842 add_filter( 'bulk_actions-edit-page', [$this, 'custom_bulk_action'] );
843 } elseif ( $post_type == 'attachment' ) {
844 add_filter( 'manage_media_columns', [$this, 'manage_columns_head'] );
845 add_action(
846 'manage_media_custom_column',
847 [$this, 'manage_columns_content'],
848 10,
849 2
850 );
851 } else {
852 add_filter( 'manage_edit-' . $post_type . '_columns', [$this, 'manage_columns_head'], 99999 );
853 add_action(
854 'manage_' . $post_type . '_posts_custom_column',
855 [$this, 'manage_columns_content'],
856 2,
857 2
858 );
859 add_filter( 'bulk_actions-edit-' . $post_type, [$this, 'custom_bulk_action'] );
860 }
861 }
862 }
863
864 function manage_columns_head( $posts_columns ) {
865 $post_type = null;
866 if ( isset( $_REQUEST['post_type'] ) ) {
867 $post_type = sanitize_text_field( wp_unslash( $_REQUEST['post_type'] ) );
868 }
869 if ( $this->is_module_active( $post_type ) ) {
870 $title = sprintf( __( 'Moving selected items', 'adminify' ), '<span class="adminify-folder-items--count"></span>' );
871 return [
872 'adminify_move' => '<div class="adminify-move-multiple adminify-col" title="' . esc_attr__( 'Move selected items', 'adminify' ) . '"><span class="dashicons dashicons-move"></span><span class="adminify-move-file--title">' . wp_kses_post( $title ) . '</span><div class="adminify-items"></div></div>',
873 ] + $posts_columns;
874 }
875 return $posts_columns;
876 }
877
878 function manage_columns_content( $column_name, $post_ID ) {
879 $post_type = null;
880 if ( isset( $_REQUEST['post_type'] ) ) {
881 $post_type = sanitize_text_field( wp_unslash( $_REQUEST['post_type'] ) );
882 }
883 $postIDs = self::$postIds;
884 $folder_ids = $this->get_folders_by_post( $post_ID );
885 $folder_ids = implode( ',', $folder_ids );
886 if ( !is_array( $postIDs ) ) {
887 $postIDs = [];
888 }
889 if ( !in_array( $post_ID, $postIDs ) ) {
890 $postIDs[] = $post_ID;
891 self::$postIds = $postIDs;
892 if ( $this->is_module_active( $post_type ) ) {
893 if ( $column_name == 'adminify_move' ) {
894 $title = get_the_title();
895 if ( strlen( $title ) > 20 ) {
896 $title = substr( $title, 0, 20 ) . '...';
897 }
898 echo Utils::wp_kses_custom( '<div class="adminify-move-file" data-id="' . esc_attr( $post_ID ) . '" data-folders="' . esc_attr( $folder_ids ) . '"><span class="adminify-move dashicons dashicons-move"></span><span class="adminify-move-file--title">' . esc_html( $title ) . '</span></div>' );
899 }
900 }
901 }
902 }
903
904 public function get_folders_by_post( $post_ID, $return_type = 'ids' ) {
905 $folder_tax = self::get_post_type_taxonomy( get_post_type( $post_ID ) );
906 $folders = wp_get_post_terms( $post_ID, $folder_tax );
907 if ( $return_type == 'ids' ) {
908 return wp_list_pluck( $folders, 'term_id' );
909 }
910 return $folders;
911 }
912
913 public function custom_bulk_action( $bulk_actions ) {
914 $bulk_actions['move_to_folder'] = __( 'Move to Folder', 'adminify' );
915 return $bulk_actions;
916 }
917
918 public function wp_adminify_select_folder_when_upload() {
919 if ( !$this->options['media'] ) {
920 return;
921 }
922 $selected = ( isset( $_GET['media_folder'] ) ? $_GET['media_folder'] : '' );
923 wp_dropdown_categories( array(
924 'show_option_none' => 'Choose folder',
925 'taxonomy' => 'media_folder',
926 'name' => 'folder_id',
927 'id' => 'folders',
928 'orderby' => 'name',
929 'selected' => $selected,
930 'hierarchical' => true,
931 'value_field' => 'slug',
932 'hide_empty' => 0,
933 ) );
934 }
935
936 public function wp_adminify_assign_media_folder_to_new_attachment( $post_ID ) {
937 if ( isset( $_POST['folder_id'] ) && !empty( $_POST['folder_id'] ) && $_POST['folder_id'] !== '-1' ) {
938 $term = get_term_by( 'slug', $_POST['folder_id'], 'media_folder' );
939 if ( !$term && is_numeric( $_POST['folder_id'] ) ) {
940 $term = get_term_by( 'id', (int) $_POST['folder_id'], 'media_folder' );
941 }
942 if ( $term && !is_wp_error( $term ) ) {
943 wp_set_object_terms( $post_ID, (int) $term->term_id, 'media_folder' );
944 $post_type = 'attachment';
945 $post_type_tax = $_POST['folder_id'];
946 } else {
947 error_log( 'Media Folder: Could not find term for value "' . $_POST['folder_id'] . '" for attachment ID ' . $post_ID );
948 }
949 $this->refreshed_folder_data( $post_type, $post_type_tax );
950 }
951 }
952
953 public function wp_adminify_edit_attachment_fields( $form_fields, $post ) {
954 if ( !$this->options['media'] ) {
955 return;
956 }
957 $folder_fields = array(
958 'label' => 'Folders',
959 'show_in_edit' => false,
960 'input' => 'html',
961 'value' => '',
962 );
963 $taxonomy_name = 'media_folder';
964 // get the assigned media library folders from the cache
965 $terms = get_the_terms( $post->ID, $taxonomy_name );
966 if ( $terms ) {
967 $folder_fields['value'] = join( ', ', wp_list_pluck( $terms, 'slug' ) );
968 }
969 ob_start();
970 $this->wp_adminify_render_terms_dropdown( $post->ID, $taxonomy_name );
971 $html = ob_get_contents();
972 ob_end_clean();
973 $folder_fields['html'] = $html;
974 $form_fields[$taxonomy_name] = $folder_fields;
975 return $form_fields;
976 }
977
978 public function wp_adminify_render_terms_dropdown( $post_id, $taxonomy ) {
979 $selected_terms = wp_get_object_terms( $post_id, $taxonomy, array(
980 'fields' => 'ids',
981 ) );
982 $selected_term = ( !empty( $selected_terms ) ? $selected_terms[0] : 0 );
983 $terms = get_terms( array(
984 'taxonomy' => $taxonomy,
985 'hide_empty' => false,
986 'parent' => 0,
987 ) );
988 echo '<select name="media_folder_select" class="media-folder-select" data-attachment-id="' . $post_id . '">';
989 echo '<option value="">— Select Folder —</option>';
990 if ( !empty( $terms ) && !is_wp_error( $terms ) ) {
991 foreach ( $terms as $term ) {
992 $this->wp_adminify_render_term_option( $term, $selected_term, $taxonomy );
993 }
994 }
995 echo '</select>';
996 $this->wp_adminify_add_folder_script();
997 }
998
999 public function wp_adminify_render_term_option(
1000 $term,
1001 $selected_term,
1002 $taxonomy,
1003 $depth = 0
1004 ) {
1005 $indent = str_repeat( '&nbsp;&nbsp;&nbsp;', $depth );
1006 $selected = selected( $term->term_id, $selected_term, false );
1007 echo sprintf(
1008 '<option value="%s" %s>%s%s</option>',
1009 $term->term_id,
1010 $selected,
1011 $indent,
1012 esc_html( $term->name )
1013 );
1014 // Get children
1015 $children = get_terms( array(
1016 'taxonomy' => $taxonomy,
1017 'hide_empty' => false,
1018 'parent' => $term->term_id,
1019 ) );
1020 if ( !empty( $children ) && !is_wp_error( $children ) ) {
1021 foreach ( $children as $child ) {
1022 $this->wp_adminify_render_term_option(
1023 $child,
1024 $selected_term,
1025 $taxonomy,
1026 $depth + 1
1027 );
1028 }
1029 }
1030 }
1031
1032 public function wp_adminify_add_folder_script() {
1033 ?>
1034 <script type="text/javascript">
1035 jQuery(document).ready(function($) {
1036 $('.media-folder-select').on('change', function() {
1037 var $select = $(this);
1038 var attachment_id = $select.data('attachment-id');
1039 var term_id = $select.val();
1040
1041 if (!term_id) return;
1042
1043 // Store original value in case we need to revert
1044 $select.data('prev-value', $select.val());
1045
1046 // Show loading indicator
1047 var $spinner = $('<span class="spinner is-active"></span>');
1048 $select
1049 .prop('disabled', true)
1050 .after($spinner);
1051
1052 $.ajax({
1053 url: ajaxurl,
1054 type: 'POST',
1055 data: {
1056 action: 'wp_adminify_assign_media_folder',
1057 attachment_id: attachment_id,
1058 term_id: term_id,
1059 security: '<?php
1060 echo wp_create_nonce( "wp_adminify_media_folder" );
1061 ?>'
1062 },
1063 success: function(response) {
1064 if (response.success) {
1065 } else {
1066 $select.val($select.data('prev-value'));
1067 console.log('Error: ' + (response.data || 'Failed to update folder'));
1068 }
1069 },
1070 error: function(xhr, status, error) {
1071 $select.val($select.data('prev-value'));
1072 console.log('Error: ' + error);
1073 },
1074 complete: function() {
1075 // Always clean up
1076 $spinner.remove();
1077 $select.prop('disabled', false);
1078 }
1079 });
1080 });
1081 });
1082 </script>
1083 <?php
1084 }
1085
1086 public function wp_adminify_handle_folder_assignment() {
1087 check_ajax_referer( 'wp_adminify_media_folder', 'security' );
1088 if ( !current_user_can( 'upload_files' ) ) {
1089 wp_send_json_error( __( 'You do not have permission to perform this action.', 'adminify' ) );
1090 }
1091 $attachment_id = ( isset( $_POST['attachment_id'] ) ? intval( $_POST['attachment_id'] ) : 0 );
1092 $term_id = ( isset( $_POST['term_id'] ) ? intval( $_POST['term_id'] ) : 0 );
1093 $taxonomy = 'media_folder';
1094 if ( !$attachment_id || !$term_id ) {
1095 wp_send_json_error( 'Invalid data' );
1096 }
1097 // First remove all terms from this taxonomy
1098 wp_delete_object_term_relationships( $attachment_id, $taxonomy );
1099 // Add the new term
1100 $result = wp_set_object_terms( $attachment_id, $term_id, $taxonomy );
1101 if ( is_wp_error( $result ) ) {
1102 wp_send_json_error( $result->get_error_message() );
1103 }
1104 wp_send_json_success();
1105 }
1106
1107 public function wp_adminify_show_folders_in_list_filter() {
1108 global $typenow;
1109 if ( !$this->is_module_active() ) {
1110 return;
1111 }
1112 if ( 'attachment' !== $typenow ) {
1113 return;
1114 }
1115 $selected = ( isset( $_GET['media_folder'] ) ? $_GET['media_folder'] : '' );
1116 wp_dropdown_categories( array(
1117 'show_option_all' => 'All folders',
1118 'taxonomy' => 'media_folder',
1119 'name' => 'folder_id',
1120 'id' => 'wp-adminify-media-folder-filter',
1121 'orderby' => 'name',
1122 'selected' => $selected,
1123 'hierarchical' => true,
1124 'value_field' => 'slug',
1125 'depth' => 3,
1126 'hide_empty' => false,
1127 ) );
1128 }
1129
1130 }
1131