class-settings-addons.php
4 months ago
class-settings-base.php
5 months ago
class-settings-builder.php
5 months ago
class-settings-capabilities.php
5 months ago
class-settings-folders.php
5 months ago
class-settings-galleries.php
5 months ago
class-settings-general.php
5 months ago
class-settings-licenses.php
5 months ago
class-settings-lightboxes.php
4 months ago
class-settings-remote-library.php
5 months ago
class-settings-folders.php
357 lines
| 1 | <?php |
| 2 | /** |
| 3 | * Responsive Lightbox Folders Settings |
| 4 | * |
| 5 | * @package Responsive_Lightbox |
| 6 | */ |
| 7 | |
| 8 | // exit if accessed directly |
| 9 | if ( ! defined( 'ABSPATH' ) ) |
| 10 | exit; |
| 11 | |
| 12 | /** |
| 13 | * Responsive Lightbox Folders Settings class. |
| 14 | * |
| 15 | * @class Responsive_Lightbox_Settings_Folders |
| 16 | */ |
| 17 | class Responsive_Lightbox_Settings_Folders extends Responsive_Lightbox_Settings_Base { |
| 18 | |
| 19 | /** |
| 20 | * Tab key identifier. |
| 21 | * |
| 22 | * @var string |
| 23 | */ |
| 24 | const TAB_KEY = 'folders'; |
| 25 | |
| 26 | /** |
| 27 | * Class constructor. |
| 28 | * |
| 29 | * @return void |
| 30 | */ |
| 31 | public function __construct() { |
| 32 | parent::__construct(); |
| 33 | |
| 34 | // allow to load old taxonomies from settings page |
| 35 | add_action( 'wp_ajax_rl-folders-load-old-taxonomies', [ $this, 'load_old_taxonomies' ] ); |
| 36 | } |
| 37 | |
| 38 | /** |
| 39 | * Validate settings for Folders tab. |
| 40 | * |
| 41 | * Handles field sanitization for folder settings. |
| 42 | * |
| 43 | * @param array $input Input data from form submission. |
| 44 | * @return array Validated data. |
| 45 | */ |
| 46 | public function validate( $input ) { |
| 47 | // check if this is a reset operation |
| 48 | if ( $this->is_reset_request() ) { |
| 49 | $input = $this->merge_with_defaults( [] ); |
| 50 | add_settings_error( 'reset_rl_folders', 'settings_restored', esc_html__( 'Settings restored to defaults.', 'responsive-lightbox' ), 'updated' ); |
| 51 | return $input; |
| 52 | } |
| 53 | |
| 54 | // sanitize all fields |
| 55 | $input = $this->sanitize_fields( $input, 'folders' ); |
| 56 | |
| 57 | // validate folders_source against filtered options |
| 58 | $allowed_sources = array_keys( apply_filters( 'rl_folders_source_options', [ |
| 59 | 'rl_media_folder' => __( 'RLG Folder (recommended)', 'responsive-lightbox' ), |
| 60 | 'custom_taxonomy' => __( 'Custom Taxonomy', 'responsive-lightbox' ) |
| 61 | ] ) ); |
| 62 | |
| 63 | if ( ! in_array( $input['folders_source'], $allowed_sources, true ) ) { |
| 64 | $input['folders_source'] = 'rl_media_folder'; |
| 65 | } |
| 66 | |
| 67 | // validate media_taxonomy only when using custom_taxonomy mode |
| 68 | if ( $input['folders_source'] === 'custom_taxonomy' ) { |
| 69 | // check if media_taxonomy is empty or invalid |
| 70 | if ( empty( $input['media_taxonomy'] ) || $input['media_taxonomy'] === 'rl_media_folder' ) { |
| 71 | $input['folders_source'] = 'rl_media_folder'; |
| 72 | add_settings_error( 'media_taxonomy', 'media_taxonomy_fallback', esc_html__( 'Media Folders selection is required when using Custom Taxonomy. Reverted to RLG Folder.', 'responsive-lightbox' ), 'error' ); |
| 73 | } else { |
| 74 | // validate against DB taxonomies + registered hierarchical attachment taxonomies |
| 75 | $db_taxonomies = $this->get_taxonomies(); |
| 76 | $db_taxonomies = is_array( $db_taxonomies ) ? $db_taxonomies : []; |
| 77 | |
| 78 | $registered_taxonomies = get_taxonomies( |
| 79 | [ |
| 80 | 'object_type' => [ 'attachment' ], |
| 81 | 'hierarchical' => true, |
| 82 | '_builtin' => false |
| 83 | ], |
| 84 | 'names', |
| 85 | 'and' |
| 86 | ); |
| 87 | $registered_taxonomies = is_array( $registered_taxonomies ) ? $registered_taxonomies : []; |
| 88 | |
| 89 | $available_taxonomies = array_values( array_unique( array_merge( $db_taxonomies, $registered_taxonomies ) ) ); |
| 90 | $available_taxonomies = array_values( array_diff( $available_taxonomies, [ 'rl_media_folder', 'rl_media_tag', 'language' ] ) ); |
| 91 | $available_taxonomies = array_map( 'sanitize_key', $available_taxonomies ); |
| 92 | |
| 93 | if ( ! in_array( $input['media_taxonomy'], $available_taxonomies, true ) ) { |
| 94 | $input['folders_source'] = 'rl_media_folder'; |
| 95 | add_settings_error( 'media_taxonomy', 'media_taxonomy_invalid', esc_html__( 'Selected taxonomy does not exist. Reverted to RLG Folder.', 'responsive-lightbox' ), 'error' ); |
| 96 | } |
| 97 | } |
| 98 | } |
| 99 | |
| 100 | // enforce RLG-only features when using custom taxonomy |
| 101 | if ( $input['folders_source'] === 'custom_taxonomy' ) { |
| 102 | $input['media_tags'] = false; |
| 103 | $input['show_in_menu'] = false; |
| 104 | $input['folders_removal'] = false; |
| 105 | $input['jstree_wholerow'] = false; |
| 106 | } |
| 107 | |
| 108 | return $input; |
| 109 | } |
| 110 | |
| 111 | /** |
| 112 | * Provide settings data for this tab. |
| 113 | * |
| 114 | * @param array $data Settings data. |
| 115 | * @return array |
| 116 | */ |
| 117 | public function settings_data( $data ) { |
| 118 | // get main instance |
| 119 | $rl = Responsive_Lightbox(); |
| 120 | |
| 121 | $data[self::TAB_KEY] = [ |
| 122 | 'option_name' => 'responsive_lightbox_folders', |
| 123 | 'option_group' => 'responsive_lightbox_folders', |
| 124 | 'validate' => [ $this, 'validate' ], |
| 125 | 'sections' => [ |
| 126 | 'responsive_lightbox_folders' => [ |
| 127 | 'title' => __( 'Folders Settings', 'responsive-lightbox' ), |
| 128 | 'description' => '', |
| 129 | 'fields' => [ |
| 130 | 'active' => [ |
| 131 | 'title' => __( 'Folders', 'responsive-lightbox' ), |
| 132 | 'type' => 'boolean', |
| 133 | 'label' => __( 'Enable media folders.', 'responsive-lightbox' ) |
| 134 | ], |
| 135 | 'folders_source' => [ |
| 136 | 'title' => __( 'Folders Source', 'responsive-lightbox' ), |
| 137 | 'type' => 'radio', |
| 138 | 'description' => __( 'Select the source for organizing media and Media Folder galleries.', 'responsive-lightbox' ) . ' ' . __( 'If you have ever used categories or custom taxonomies for media library you may try to <a id="rl-folders-load-old-taxonomies" href="#">load and use them.</a>', 'responsive-lightbox' ), |
| 139 | 'options' => apply_filters( 'rl_folders_source_options', [ |
| 140 | 'rl_media_folder' => __( 'RLG Folder (recommended)', 'responsive-lightbox' ), |
| 141 | 'custom_taxonomy' => __( 'Custom Taxonomy', 'responsive-lightbox' ) |
| 142 | ] ) |
| 143 | ], |
| 144 | 'media_taxonomy' => [ |
| 145 | 'title' => '', |
| 146 | 'type' => 'select', |
| 147 | 'description' => __( 'Select the data source media folders.', 'responsive-lightbox' ), |
| 148 | 'after_field' => '<span class="spinner rl-spinner"></span>', |
| 149 | 'options' => [], |
| 150 | 'logic' => [ |
| 151 | 'field' => 'folders_source', |
| 152 | 'operator' => 'is', |
| 153 | 'value' => 'custom_taxonomy', |
| 154 | 'action' => 'show', |
| 155 | ], |
| 156 | 'animation' => 'slide' |
| 157 | ], |
| 158 | 'media_ui' => [ |
| 159 | 'title' => __( 'Media Library UI', 'responsive-lightbox' ), |
| 160 | 'type' => 'boolean', |
| 161 | 'label' => __( 'Enable Media Library folders UI.', 'responsive-lightbox' ), |
| 162 | 'description' => __( 'Disable to keep taxonomy available for galleries without adding Media Library UI.', 'responsive-lightbox' ) |
| 163 | ], |
| 164 | 'media_tags' => [ |
| 165 | 'title' => __( 'Media Tags', 'responsive-lightbox' ), |
| 166 | 'type' => 'boolean', |
| 167 | 'label' => __( 'Enable media tags.', 'responsive-lightbox' ), |
| 168 | 'description' => __( 'Enable if you want to use media tags.', 'responsive-lightbox' ), |
| 169 | 'logic' => [ |
| 170 | 'field' => 'media_ui', |
| 171 | 'operator' => 'isnot', |
| 172 | 'value' => 'true', |
| 173 | 'action' => 'disable', |
| 174 | ] |
| 175 | ], |
| 176 | 'show_in_menu' => [ |
| 177 | 'title' => __( 'Show in Menu', 'responsive-lightbox' ), |
| 178 | 'type' => 'boolean', |
| 179 | 'label' => __( 'Enable to show the taxonomy in the admin menu.', 'responsive-lightbox' ), |
| 180 | 'logic' => [ |
| 181 | 'field' => 'media_ui', |
| 182 | 'operator' => 'isnot', |
| 183 | 'value' => 'true', |
| 184 | 'action' => 'disable', |
| 185 | ] |
| 186 | ], |
| 187 | 'folders_removal' => [ |
| 188 | 'title' => __( 'Subfolder Removal', 'responsive-lightbox' ), |
| 189 | 'type' => 'boolean', |
| 190 | 'label' => __( 'Select to remove subfolders when parent folder is deleted.', 'responsive-lightbox' ), |
| 191 | 'logic' => [ |
| 192 | 'field' => 'media_ui', |
| 193 | 'operator' => 'isnot', |
| 194 | 'value' => 'true', |
| 195 | 'action' => 'disable', |
| 196 | ] |
| 197 | ], |
| 198 | 'jstree_wholerow' => [ |
| 199 | 'title' => __( 'Whole Row', 'responsive-lightbox' ), |
| 200 | 'type' => 'boolean', |
| 201 | 'label' => __( 'Enable to highlight folder\'s row as a clickable area.', 'responsive-lightbox' ), |
| 202 | 'logic' => [ |
| 203 | 'field' => 'media_ui', |
| 204 | 'operator' => 'isnot', |
| 205 | 'value' => 'true', |
| 206 | 'action' => 'disable', |
| 207 | ] |
| 208 | ] |
| 209 | ] |
| 210 | ] |
| 211 | ] |
| 212 | ]; |
| 213 | |
| 214 | return $data; |
| 215 | } |
| 216 | |
| 217 | /** |
| 218 | * Load previously used media taxonomies via AJAX. |
| 219 | * |
| 220 | * @return void |
| 221 | */ |
| 222 | public function load_old_taxonomies() { |
| 223 | // check capability |
| 224 | if ( ! current_user_can( 'manage_options' ) ) |
| 225 | wp_send_json_error( [ 'message' => __( 'You do not have permission to perform this action.', 'responsive-lightbox' ) ] ); |
| 226 | |
| 227 | // no data? |
| 228 | if ( ! isset( $_POST['taxonomies'], $_POST['nonce'] ) ) |
| 229 | wp_send_json_error( [ 'message' => __( 'Missing required data.', 'responsive-lightbox' ) ] ); |
| 230 | |
| 231 | // invalid taxonomies format? |
| 232 | if ( ! is_array( $_POST['taxonomies'] ) ) |
| 233 | wp_send_json_error( [ 'message' => __( 'Invalid data format.', 'responsive-lightbox' ) ] ); |
| 234 | |
| 235 | // invalid nonce? |
| 236 | if ( ! wp_verify_nonce( $_POST['nonce'], 'rl-folders-ajax-taxonomies-nonce' ) ) |
| 237 | wp_send_json_error( [ 'message' => __( 'Security check failed. Please refresh the page and try again.', 'responsive-lightbox' ) ] ); |
| 238 | |
| 239 | // validate taxonomies are strings |
| 240 | $taxonomies_input = array_filter( $_POST['taxonomies'], 'is_string' ); |
| 241 | $taxonomies_input = array_map( 'sanitize_key', $taxonomies_input ); |
| 242 | |
| 243 | // get taxonomies used by attachments (DB) and currently registered |
| 244 | $db_taxonomies = $this->get_taxonomies(); |
| 245 | $db_taxonomies = is_array( $db_taxonomies ) ? $db_taxonomies : []; |
| 246 | |
| 247 | $registered_taxonomies = get_taxonomies( |
| 248 | [ |
| 249 | 'object_type' => [ 'attachment' ], |
| 250 | 'hierarchical' => true, |
| 251 | '_builtin' => false |
| 252 | ], |
| 253 | 'names', |
| 254 | 'and' |
| 255 | ); |
| 256 | $registered_taxonomies = is_array( $registered_taxonomies ) ? $registered_taxonomies : []; |
| 257 | |
| 258 | // combine DB-discovered and currently registered taxonomies |
| 259 | $fields = array_values( array_unique( array_merge( $db_taxonomies, $registered_taxonomies ) ) ); |
| 260 | |
| 261 | // any results? |
| 262 | if ( ! empty( $fields ) ) { |
| 263 | // remove main taxonomy |
| 264 | if ( ( $key = array_search( 'rl_media_folder', $fields, true ) ) !== false ) |
| 265 | unset( $fields[$key] ); |
| 266 | |
| 267 | // remove media tags |
| 268 | if ( ( $key = array_search( 'rl_media_tag', $fields, true ) ) !== false ) |
| 269 | unset( $fields[$key] ); |
| 270 | |
| 271 | // remove polylang taxonomy if present |
| 272 | if ( ( $key = array_search( 'language', $fields, true ) ) !== false ) |
| 273 | unset( $fields[$key] ); |
| 274 | |
| 275 | // sanitize and normalize |
| 276 | $fields = array_map( 'sanitize_key', $fields ); |
| 277 | $fields = array_filter( $fields, 'is_string' ); |
| 278 | $fields = array_values( array_unique( $fields ) ); |
| 279 | } |
| 280 | |
| 281 | // save discovered taxonomies to options (merge with existing, preserve on scan failure) |
| 282 | $options = get_option( 'responsive_lightbox_folders', [] ); |
| 283 | $existing = isset( $options['custom_taxonomies'] ) && is_array( $options['custom_taxonomies'] ) ? $options['custom_taxonomies'] : []; |
| 284 | |
| 285 | if ( ! empty( $fields ) ) { |
| 286 | $options['custom_taxonomies'] = array_values( array_unique( array_merge( $existing, $fields ) ) ); |
| 287 | update_option( 'responsive_lightbox_folders', $options ); |
| 288 | } |
| 289 | // Note: We don't clear custom_taxonomies when scan returns nothing to avoid |
| 290 | // wiping stored taxonomies on transient DB failures or when no new taxonomies exist |
| 291 | |
| 292 | // send taxonomies with counts and message |
| 293 | $count = count( $fields ); |
| 294 | $new_taxonomies = array_values( array_diff( $fields, $taxonomies_input ) ); |
| 295 | $count_new = count( $new_taxonomies ); |
| 296 | |
| 297 | if ( $count > 0 ) { |
| 298 | if ( $count_new > 0 ) { |
| 299 | $message = sprintf( |
| 300 | /* translators: 1: total taxonomies, 2: newly added taxonomies */ |
| 301 | __( '%1$d custom taxonomies available. %2$d new added to the list.', 'responsive-lightbox' ), |
| 302 | $count, |
| 303 | $count_new |
| 304 | ); |
| 305 | } else { |
| 306 | $message = sprintf( |
| 307 | _n( |
| 308 | '%d custom taxonomy available (registered or previously loaded). No new taxonomies were added.', |
| 309 | '%d custom taxonomies available (registered or previously loaded). No new taxonomies were added.', |
| 310 | $count, |
| 311 | 'responsive-lightbox' |
| 312 | ), |
| 313 | $count |
| 314 | ); |
| 315 | } |
| 316 | } else { |
| 317 | $message = __( 'No custom taxonomies found.', 'responsive-lightbox' ); |
| 318 | } |
| 319 | |
| 320 | wp_send_json_success( [ |
| 321 | 'taxonomies' => array_values( $fields ), |
| 322 | 'count' => $count, |
| 323 | 'count_new' => $count_new, |
| 324 | 'message' => $message |
| 325 | ] ); |
| 326 | } |
| 327 | |
| 328 | /** |
| 329 | * Get all previously used media taxonomies. |
| 330 | * |
| 331 | * @global object $wpdb |
| 332 | * |
| 333 | * @return array |
| 334 | */ |
| 335 | private function get_taxonomies() { |
| 336 | global $wpdb; |
| 337 | |
| 338 | // query |
| 339 | $fields = $wpdb->get_col( " |
| 340 | SELECT DISTINCT tt.taxonomy |
| 341 | FROM " . $wpdb->prefix . "term_taxonomy tt |
| 342 | LEFT JOIN " . $wpdb->prefix . "term_relationships tr ON tt.term_taxonomy_id = tr.term_taxonomy_id |
| 343 | LEFT JOIN " . $wpdb->prefix . "posts p ON p.ID = tr.object_id |
| 344 | WHERE p.post_type = 'attachment' |
| 345 | ORDER BY tt.taxonomy ASC" |
| 346 | ); |
| 347 | |
| 348 | if ( ! empty( $fields ) ) { |
| 349 | // remove polylang taxonomy |
| 350 | if ( ( $key = array_search( 'language', $fields, true ) ) !== false ) |
| 351 | unset( $fields[$key] ); |
| 352 | } |
| 353 | |
| 354 | return $fields; |
| 355 | } |
| 356 | } |
| 357 |