Changeset 3260159
- Timestamp:
- 03/22/2025 11:54:13 PM (12 months ago)
- Location:
- nhrrob-options-table-manager
- Files:
-
- 18 edited
- 1 copied
-
tags/1.1.7-beta1 (copied) (copied from nhrrob-options-table-manager/trunk)
-
tags/1.1.7-beta1/assets/css/admin.css (modified) (1 diff)
-
tags/1.1.7-beta1/assets/js/admin.js (modified) (3 diffs)
-
tags/1.1.7-beta1/composer.json (modified) (1 diff)
-
tags/1.1.7-beta1/includes/Ajax.php (modified) (6 diffs)
-
tags/1.1.7-beta1/includes/Traits/GlobalTrait.php (modified) (1 diff)
-
tags/1.1.7-beta1/includes/views/admin/settings/index.php (modified) (2 diffs)
-
tags/1.1.7-beta1/nhrrob-options-table-manager.php (modified) (2 diffs)
-
tags/1.1.7-beta1/readme.txt (modified) (2 diffs)
-
tags/1.1.7-beta1/vendor/composer/installed.php (modified) (2 diffs)
-
trunk/assets/css/admin.css (modified) (1 diff)
-
trunk/assets/js/admin.js (modified) (3 diffs)
-
trunk/composer.json (modified) (1 diff)
-
trunk/includes/Ajax.php (modified) (6 diffs)
-
trunk/includes/Traits/GlobalTrait.php (modified) (1 diff)
-
trunk/includes/views/admin/settings/index.php (modified) (2 diffs)
-
trunk/nhrrob-options-table-manager.php (modified) (2 diffs)
-
trunk/readme.txt (modified) (2 diffs)
-
trunk/vendor/composer/installed.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
nhrrob-options-table-manager/tags/1.1.7-beta1/assets/css/admin.css
r3256055 r3260159 378 378 background-color: #FF9800; 379 379 } 380 381 #nhrotm-data-table tfoot input { 382 padding: 8px 10px; 383 margin: 0; 384 border: 1px solid #aaa; 385 border-radius: 5px; 386 font-weight: normal; 387 } 388 389 /* Filter */ 390 391 .nhrotm-filter-container { 392 margin-bottom: 20px; 393 padding: 15px; 394 background: #fff; 395 border-radius: 5px; 396 box-shadow: 0 1px 3px rgba(0,0,0,0.1); 397 } 398 399 .nhrotm-filter-row { 400 display: flex; 401 /* justify-content: space-between; */ 402 justify-content: center; 403 align-items: center; 404 /* margin-bottom: 15px; */ 405 gap: 10px; 406 } 407 408 .nhrotm-filter-group { 409 display: flex; 410 align-items: center; 411 gap: 10px; 412 } 413 414 #option-type-filter { 415 min-width: 200px; 416 padding: 4px 8px; 417 border: 1px solid #ddd; 418 border-radius: 4px; 419 } 420 421 #delete-expired-transients { 422 background-color: #e74c3c; 423 padding: 4px 8px; 424 color: white; 425 border: none; 426 } -
nhrrob-options-table-manager/tags/1.1.7-beta1/assets/js/admin.js
r3256055 r3260159 37 37 "type": "GET", 38 38 "url": nhrotmOptionsTableManager.ajaxUrl + "?action=nhrotm_option_table_data&nonce="+nhrotmOptionsTableManager.nonce, 39 "data": function(d) { 40 // Add column search values to the request 41 for (let i = 0; i < d.columns.length; i++) { 42 d.columns[i].search.value = $('#nhrotm-data-table tfoot input').eq(i).val(); 43 } 44 45 // Option type filter 46 $('#delete-expired-transients').attr('disabled', true); 47 d.optionTypeFilter = $('#option-type-filter').val(); 48 49 if ( d.optionTypeFilter === 'all-transients' ) { 50 $('#delete-expired-transients').attr('disabled', false); 51 52 let currentSearch = d.columns[1].search.value || ''; 53 if (!currentSearch.includes('transient_')) { 54 d.columns[1].search.value = 'transient_' + currentSearch; 55 } 56 } 57 } 39 58 }, 40 59 "columns": [ … … 49 68 // "scrollCollapse": true, 50 69 // "paging": true, 51 // "order": [[0, 'asc']], // Default order on the first column in ascending 70 // "order": [[0, 'asc']], // Default order on the first column in ascending, 71 "initComplete": function () { 72 this.api() 73 .columns() 74 .every(function () { 75 let column = this; 76 let title = column.footer().textContent; 77 78 // Create input element 79 let input = document.createElement('input'); 80 input.placeholder = title; 81 column.footer().replaceChildren(input); 82 83 input.addEventListener('keyup', function() { 84 if (column.search() !== this.value) { 85 column.search(this.value).draw(); 86 } 87 }); 88 }); 89 } 52 90 }); 53 91 … … 424 462 }); 425 463 } 464 465 // Filtering 466 // Add filter dropdown handler 467 $('#option-type-filter').on('change', function() { 468 table.ajax.reload(); 469 }); 470 471 // Add "Delete All Transients" button handler 472 $('#delete-expired-transients').on('click', function() { 473 if (confirm('Are you sure you want to delete expired transients? This action cannot be undone.')) { 474 $.ajax({ 475 url: nhrotmOptionsTableManager.ajaxUrl, 476 type: 'POST', 477 data: { 478 action: 'nhrotm_delete_expired_transients', 479 nonce: nhrotmOptionsTableManager.nonce 480 }, 481 success: function(response) { 482 if (response.success) { 483 showToast("Expired transients deleted successfully!", "success"); 484 table.ajax.reload(); 485 } else { 486 showToast('Failed to delete transients: ' + response.data, "error"); 487 } 488 }, 489 error: function() { 490 showToast('An error occurred while deleting transients.', "error"); 491 } 492 }); 493 } 494 }); 426 495 427 496 }); -
nhrrob-options-table-manager/tags/1.1.7-beta1/composer.json
r3256055 r3260159 26 26 }, 27 27 "scripts": { 28 "deploy": "composer install --no-dev && wp dist-archive . && composer install" 28 "deploy": "composer install --no-dev && wp dist-archive . && composer install", 29 "dev": "composer install", 30 "build": "composer install --no-dev" 29 31 } 30 32 } -
nhrrob-options-table-manager/tags/1.1.7-beta1/includes/Ajax.php
r3256055 r3260159 17 17 add_action('wp_ajax_nhrotm_edit_option', [ $this, 'edit_option' ]); 18 18 add_action('wp_ajax_nhrotm_delete_option', [ $this, 'delete_option' ]); 19 add_action('wp_ajax_nhrotm_delete_expired_transients', [ $this, 'delete_expired_transients' ]); 19 20 add_action('wp_ajax_nhrotm_option_usage_analytics', [ $this, 'option_usage_analytics' ]); 20 21 … … 44 45 // Search parameter 45 46 $search = isset($_GET['search']['value']) ? sanitize_text_field(wp_unslash($_GET['search']['value'])) : ''; 47 $option_type_filter = isset($_GET['optionTypeFilter']) && in_array($_GET['optionTypeFilter'], ['all-options', 'all-transients', 'active-transients', 'expired-transients']) ? sanitize_text_field(wp_unslash($_GET['optionTypeFilter'])) : 'all-options'; 46 48 47 49 // Sorting parameters 48 50 $order_column_index = isset($_GET['order'][0]['column']) ? intval($_GET['order'][0]['column']) : 0; 49 51 $order_direction = isset($_GET['order'][0]['dir']) && in_array($_GET['order'][0]['dir'], ['asc', 'desc']) ? strtolower( sanitize_text_field( wp_unslash( $_GET['order'][0]['dir'] ) ) ) : 'asc'; 50 52 51 53 // Define columns in the correct order for sorting 52 54 $columns = ['option_id', 'option_name', 'option_value', 'autoload']; … … 64 66 ); 65 67 66 // Prepare queries for different order options 67 // Using separate complete queries for each column and direction to avoid concatenation 68 // Get column search values 69 $column_search = []; 70 if (isset($_GET['columns']) && is_array( $_GET['columns'] )) { 71 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 72 $columns = $this->sanitize_recursive( wp_unslash( $_GET['columns'] ) ); 73 74 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 75 foreach ($_GET['columns'] as $column) { 76 if (isset($column['search']['value'])) { 77 $column_search[] = sanitize_text_field(wp_unslash($column['search']['value'])); 78 } else { 79 $column_search[] = ''; 80 } 81 } 82 } 83 84 // Build WHERE clause for search conditions 85 $where_clauses = []; 86 87 // Global search 68 88 if (!empty($search)) { 69 89 $search_like = '%' . $wpdb->esc_like($search) . '%'; 90 $where_clauses[] = $wpdb->prepare( 91 "(option_name LIKE %s OR option_value LIKE %s)", 92 $search_like, 93 $search_like 94 ); 95 } 96 97 // Individual column searches 98 if (!empty($column_search)) { 99 // option_id column (index 0) 100 if (!empty($column_search[0])) { 101 // For numeric column, use exact match or range 102 if (is_numeric($column_search[0])) { 103 $where_clauses[] = $wpdb->prepare("option_id = %d", intval($column_search[0])); 104 } 105 } 70 106 71 // Get filtered count 72 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 73 $filtered_records = $wpdb->get_var( 74 $wpdb->prepare( 75 "SELECT COUNT(*) FROM {$wpdb->prefix}options WHERE option_name LIKE %s OR option_value LIKE %s", 76 $search_like, 77 $search_like 78 ) 79 ); 80 81 // Get data with search and properly hardcoded ORDER BY 82 if ($order_column === 'option_id') { 83 if ($order_direction === 'desc') { 84 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 85 $data = $wpdb->get_results( 86 $wpdb->prepare( 87 "SELECT * FROM {$wpdb->prefix}options 88 WHERE option_name LIKE %s OR option_value LIKE %s 89 ORDER BY option_id DESC 90 LIMIT %d, %d", 91 $search_like, $search_like, $start, $length 92 ), 93 ARRAY_A 94 ); 95 } else { 96 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 97 $data = $wpdb->get_results( 98 $wpdb->prepare( 99 "SELECT * FROM {$wpdb->prefix}options 100 WHERE option_name LIKE %s OR option_value LIKE %s 101 ORDER BY option_id ASC 102 LIMIT %d, %d", 103 $search_like, $search_like, $start, $length 104 ), 105 ARRAY_A 106 ); 107 } 108 } elseif ($order_column === 'option_name') { 109 if ($order_direction === 'desc') { 110 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 111 $data = $wpdb->get_results( 112 $wpdb->prepare( 113 "SELECT * FROM {$wpdb->prefix}options 114 WHERE option_name LIKE %s OR option_value LIKE %s 115 ORDER BY option_name DESC 116 LIMIT %d, %d", 117 $search_like, $search_like, $start, $length 118 ), 119 ARRAY_A 120 ); 121 } else { 122 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 123 $data = $wpdb->get_results( 124 $wpdb->prepare( 125 "SELECT * FROM {$wpdb->prefix}options 126 WHERE option_name LIKE %s OR option_value LIKE %s 127 ORDER BY option_name ASC 128 LIMIT %d, %d", 129 $search_like, $search_like, $start, $length 130 ), 131 ARRAY_A 132 ); 133 } 134 } elseif ($order_column === 'option_value') { 135 if ($order_direction === 'desc') { 136 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 137 $data = $wpdb->get_results( 138 $wpdb->prepare( 139 "SELECT * FROM {$wpdb->prefix}options 140 WHERE option_name LIKE %s OR option_value LIKE %s 141 ORDER BY option_value DESC 142 LIMIT %d, %d", 143 $search_like, $search_like, $start, $length 144 ), 145 ARRAY_A 146 ); 147 } else { 148 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 149 $data = $wpdb->get_results( 150 $wpdb->prepare( 151 "SELECT * FROM {$wpdb->prefix}options 152 WHERE option_name LIKE %s OR option_value LIKE %s 153 ORDER BY option_value ASC 154 LIMIT %d, %d", 155 $search_like, $search_like, $start, $length 156 ), 157 ARRAY_A 158 ); 159 } 160 } elseif ($order_column === 'autoload') { 161 if ($order_direction === 'desc') { 162 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 163 $data = $wpdb->get_results( 164 $wpdb->prepare( 165 "SELECT * FROM {$wpdb->prefix}options 166 WHERE option_name LIKE %s OR option_value LIKE %s 167 ORDER BY autoload DESC 168 LIMIT %d, %d", 169 $search_like, $search_like, $start, $length 170 ), 171 ARRAY_A 172 ); 173 } else { 174 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 175 $data = $wpdb->get_results( 176 $wpdb->prepare( 177 "SELECT * FROM {$wpdb->prefix}options 178 WHERE option_name LIKE %s OR option_value LIKE %s 179 ORDER BY autoload ASC 180 LIMIT %d, %d", 181 $search_like, $search_like, $start, $length 182 ), 183 ARRAY_A 184 ); 185 } 186 } else { 187 // Default fallback 188 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 189 $data = $wpdb->get_results( 190 $wpdb->prepare( 191 "SELECT * FROM {$wpdb->prefix}options 192 WHERE option_name LIKE %s OR option_value LIKE %s 193 ORDER BY option_id ASC 194 LIMIT %d, %d", 195 $search_like, $search_like, $start, $length 196 ), 197 ARRAY_A 107 // option_name column (index 1) 108 if (!empty($column_search[1])) { 109 $where_clauses[] = $wpdb->prepare( 110 "option_name LIKE %s", 111 '%' . $wpdb->esc_like($column_search[1]) . '%' 198 112 ); 199 113 } 200 } else {201 // No search applied, use total as filtered count202 $filtered_records = $total_records;203 114 204 // Get data without search and properly hardcoded ORDER BY 205 if ($order_column === 'option_id') { 206 if ($order_direction === 'desc') { 207 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 208 $data = $wpdb->get_results( 209 $wpdb->prepare( 210 "SELECT * FROM {$wpdb->prefix}options 211 ORDER BY option_id DESC 212 LIMIT %d, %d", 213 $start, $length 214 ), 215 ARRAY_A 216 ); 217 } else { 218 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 219 $data = $wpdb->get_results( 220 $wpdb->prepare( 221 "SELECT * FROM {$wpdb->prefix}options 222 ORDER BY option_id ASC 223 LIMIT %d, %d", 224 $start, $length 225 ), 226 ARRAY_A 227 ); 228 } 229 } elseif ($order_column === 'option_name') { 230 if ($order_direction === 'desc') { 231 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 232 $data = $wpdb->get_results( 233 $wpdb->prepare( 234 "SELECT * FROM {$wpdb->prefix}options 235 ORDER BY option_name DESC 236 LIMIT %d, %d", 237 $start, $length 238 ), 239 ARRAY_A 240 ); 241 } else { 242 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 243 $data = $wpdb->get_results( 244 $wpdb->prepare( 245 "SELECT * FROM {$wpdb->prefix}options 246 ORDER BY option_name ASC 247 LIMIT %d, %d", 248 $start, $length 249 ), 250 ARRAY_A 251 ); 252 } 253 } elseif ($order_column === 'option_value') { 254 if ($order_direction === 'desc') { 255 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 256 $data = $wpdb->get_results( 257 $wpdb->prepare( 258 "SELECT * FROM {$wpdb->prefix}options 259 ORDER BY option_value DESC 260 LIMIT %d, %d", 261 $start, $length 262 ), 263 ARRAY_A 264 ); 265 } else { 266 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 267 $data = $wpdb->get_results( 268 $wpdb->prepare( 269 "SELECT * FROM {$wpdb->prefix}options 270 ORDER BY option_value ASC 271 LIMIT %d, %d", 272 $start, $length 273 ), 274 ARRAY_A 275 ); 276 } 277 } elseif ($order_column === 'autoload') { 278 if ($order_direction === 'desc') { 279 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 280 $data = $wpdb->get_results( 281 $wpdb->prepare( 282 "SELECT * FROM {$wpdb->prefix}options 283 ORDER BY autoload DESC 284 LIMIT %d, %d", 285 $start, $length 286 ), 287 ARRAY_A 288 ); 289 } else { 290 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 291 $data = $wpdb->get_results( 292 $wpdb->prepare( 293 "SELECT * FROM {$wpdb->prefix}options 294 ORDER BY autoload ASC 295 LIMIT %d, %d", 296 $start, $length 297 ), 298 ARRAY_A 299 ); 300 } 301 } else { 302 // Default fallback 303 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 304 $data = $wpdb->get_results( 305 $wpdb->prepare( 306 "SELECT * FROM {$wpdb->prefix}options 307 ORDER BY option_id ASC 308 LIMIT %d, %d", 309 $start, $length 310 ), 311 ARRAY_A 115 // option_value column (index 2) 116 if (!empty($column_search[2])) { 117 $where_clauses[] = $wpdb->prepare( 118 "option_value LIKE %s", 119 '%' . $wpdb->esc_like($column_search[2]) . '%' 312 120 ); 313 121 } 314 } 122 123 // autoload column (index 3) 124 if (!empty($column_search[3])) { 125 $where_clauses[] = $wpdb->prepare( 126 "autoload LIKE %s", 127 '%' . $wpdb->esc_like($column_search[3]) . '%' 128 ); 129 } 130 } 131 132 // Combine WHERE clauses 133 $where_sql = ''; 134 if (!empty($where_clauses)) { 135 $where_sql = 'WHERE ' . implode(' AND ', $where_clauses); 136 } 137 138 // Count filtered records 139 $filtered_records_sql = "SELECT COUNT(*) FROM {$wpdb->prefix}options {$where_sql}"; 140 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared 141 $filtered_records = $wpdb->get_var($filtered_records_sql); 142 143 // SQL for ordering 144 $order_sql = "ORDER BY {$order_column} {$order_direction}"; 145 146 // Get data with search, order, and pagination 147 $data_sql = "SELECT * FROM {$wpdb->prefix}options {$where_sql} {$order_sql} LIMIT %d, %d"; 148 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 149 $data = $wpdb->get_results( 150 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 151 $wpdb->prepare($data_sql, $start, $length), 152 ARRAY_A 153 ); 315 154 316 155 // Wrap the option_value in the scrollable-cell div … … 318 157 $is_protected = in_array($row['option_name'], $this->get_protected_options()); 319 158 $protected_attr = $is_protected ? sprintf('title="%s" disabled', esc_attr__('Protected', 'nhrrob-options-table-manager')) : ''; 159 160 if ( 'all-transients' === $option_type_filter ) { 161 // all options are transients 162 $transient_name = str_replace('_transient_', '', $row['option_name']); 163 $transient_value = get_transient($transient_name); 164 165 $transient_status = $transient_value ? '[active]' : '[expired]'; 166 $row['option_name'] = esc_html($transient_status . $row['option_name']); 167 } 320 168 321 169 $row['option_value'] = '<div class="scrollable-cell">' . esc_html($row['option_value']) . '</div>'; … … 329 177 $protected_attr, 330 178 ); 331 } 179 } 332 180 333 181 // Prepare response for DataTables … … 628 476 629 477 wp_die(); 478 } 479 480 /** 481 * Delete expired transients from the options table 482 */ 483 public function delete_expired_transients() { 484 // Verify nonce 485 if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'nhrotm-admin-nonce')) { 486 wp_send_json_error('Invalid nonce'); 487 wp_die(); 488 } 489 490 global $wpdb; 491 492 // Get all transient options 493 // phpcs:ignore:WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 494 $transients = $wpdb->get_results( 495 "SELECT option_name, option_value 496 FROM {$wpdb->options} 497 WHERE option_name LIKE '%_transient_%' 498 AND option_name NOT LIKE '%_transient_timeout_%'", 499 ARRAY_A 500 ); 501 502 try { 503 $deleted_transients = []; 504 505 foreach ($transients as $transient) { 506 507 $transient_name = str_replace('_transient_', '', $transient['option_name']); 508 509 if (false === get_transient($transient_name)) { 510 // Transient has expired, delete it 511 $deleted_transients[] = $transient_name; 512 delete_transient(esc_sql( $transient_name ) ); 513 } 514 } 515 516 wp_send_json_success([ 517 'message' => 'Expired transients deleted successfully', 518 'count' => count($deleted_transients), 519 'deleted_transients' => $deleted_transients, 520 ]); 521 } catch (\Exception $e) { 522 wp_send_json_error('Database error'); 523 wp_die(); 524 } 630 525 } 631 526 -
nhrrob-options-table-manager/tags/1.1.7-beta1/includes/Traits/GlobalTrait.php
r3256055 r3260159 282 282 283 283 // Helper function to recursively sanitize arrays and objects 284 public function sanitize_recursive(&$data) { 285 // Handle arrays using array_walk_recursive for deep sanitization 284 public function sanitize_recursive($data) { 286 285 if (is_array($data)) { 287 array_walk_recursive($data, function (&$item, $key) { 288 if ($key !== 'content') { 289 $item = sanitize_text_field($item); // Only sanitize if the key is not 'content' 290 } 291 }); 292 } 293 // Handle objects by converting them to arrays and applying the function recursively 294 elseif (is_object($data)) { 295 foreach ($data as $key => &$value) { 296 if ($key === 'content') { 297 continue; // Skip sanitization for 'content' fields 298 } 299 300 if (is_scalar($value)) { 301 $value = sanitize_text_field($value); 302 } else { 303 $this->sanitize_recursive($value); // Recurse for arrays or nested objects 286 $sanitized_array = []; 287 foreach ($data as $key => $value) { 288 $sanitized_key = sanitize_key($key); 289 if ($sanitized_key !== '') { 290 $sanitized_array[$sanitized_key] = $this->sanitize_recursive($value); 304 291 } 305 292 } 293 return $sanitized_array; 294 } elseif (is_object($data)) { 295 $sanitized_object = new \stdClass(); 296 $object_vars = get_object_vars($data); 297 298 foreach ($object_vars as $key => $value) { 299 $sanitized_key = $this->sanitize_key($key); 300 if ($sanitized_key !== '') { 301 $sanitized_object->$sanitized_key = $this->sanitize_recursive($value); 302 } 303 } 304 return $sanitized_object; 305 } else { 306 return $this->sanitize_item($data); 306 307 } 307 308 } 308 309 309 public function castValues(&$array, $option_name = '') { 310 // $exceptional_option_names = $this->exceptional_option_names(); 311 312 foreach ($array as $key => &$value) { 313 if (is_array($value)) { 314 $this->castValues($value); // Recursive call for nested arrays 315 } elseif (is_numeric($value)) { 316 $value = (int) $value; // Convert numeric strings to integers 317 } elseif ($value === "true") { 318 $value = true; // Convert "true" string to boolean true 319 } elseif ($value === "false") { 320 $value = false; // Convert "false" string to boolean false 321 } elseif (empty($value) && $key === "recurrence") { 322 $value = false; // Ensure recurrence defaults to false if empty 323 } 324 325 // if ( ! empty( $option_name ) && in_array( $option_name, $exceptional_option_names ) ) { 326 // if ( empty($value) && $key === "recurrence" ) { 327 // $value = false; 328 // } 329 // } 310 public function sanitize_item( $item ){ 311 $item_formatted = ''; 312 313 if ( is_numeric( $item )) { 314 $item_formatted = intval( $item ); 315 } elseif ( is_email( $item )) { 316 $item_formatted = sanitize_email( $item ); 317 } else { 318 $item_formatted = sanitize_text_field( wp_unslash( $item ) ); 330 319 } 320 321 return $item_formatted; 331 322 } 332 323 -
nhrrob-options-table-manager/tags/1.1.7-beta1/includes/views/admin/settings/index.php
r3256055 r3260159 19 19 </div> 20 20 21 <!-- Filter starts --> 22 <div class="nhrotm-filter-container"> 23 <div class="nhrotm-filter-row"> 24 <div class="nhrotm-filter-group"> 25 <select id="option-type-filter"> 26 <option value="all-options"><?php esc_html_e('All Options', 'nhrrob-options-table-manager'); ?></option> 27 <option value="all-transients"><?php esc_html_e('All Transients', 'nhrrob-options-table-manager'); ?></option> 28 </select> 29 </div> 30 <div class="nhrotm-filter-group"> 31 <button id="delete-expired-transients" class="button button-danger" disabled><?php esc_html_e('Delete Expired Transients', 'nhrrob-options-table-manager'); ?></button> 32 </div> 33 </div> 34 </div> 35 <!-- Filter ends --> 36 21 37 <table id="nhrotm-data-table" class="nhrotm-data-table wp-list-table widefat fixed striped"> 22 38 <thead> … … 30 46 </thead> 31 47 48 <tfoot> 49 <tr> 50 <th><?php esc_html_e('Option ID', 'nhrrob-options-table-manager'); ?></th> 51 <th><?php esc_html_e('Option Name', 'nhrrob-options-table-manager'); ?></th> 52 <th><?php esc_html_e('Option Value', 'nhrrob-options-table-manager'); ?></th> 53 <th><?php esc_html_e('Autoload', 'nhrrob-options-table-manager'); ?></th> 54 <th><?php esc_html_e('Action', 'nhrrob-options-table-manager'); ?></th> 55 </tr> 56 </tfoot> 57 32 58 <tbody> 33 59 -
nhrrob-options-table-manager/tags/1.1.7-beta1/nhrrob-options-table-manager.php
r3256164 r3260159 6 6 * Author: Nazmul Hasan Robin 7 7 * Author URI: https://profiles.wordpress.org/nhrrob/ 8 * Version: 1.1. 68 * Version: 1.1.7-beta1 9 9 * Requires at least: 6.0 10 10 * Requires PHP: 7.4 … … 28 28 * @var string 29 29 */ 30 const nhrotm_version = '1.1. 6';30 const nhrotm_version = '1.1.7-beta1'; 31 31 32 32 /** -
nhrrob-options-table-manager/tags/1.1.7-beta1/readme.txt
r3256164 r3260159 5 5 Tested up to: 6.7 6 6 Requires PHP: 7.4 7 Stable tag: 1.1.6 7 Stable tag: 1.1.6 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 85 85 86 86 == Changelog == 87 88 = 1.1.7 - 23/03/2025 = 89 - Added: Column search feature 90 - Added: Filter by option type - option or transient 91 - Added: Delete all expired transients button and functionality 92 - Few minor bug fixing & improvements 87 93 88 94 = 1.1.6 - 15/03/2025 = -
nhrrob-options-table-manager/tags/1.1.7-beta1/vendor/composer/installed.php
r3256164 r3260159 1 1 <?php return array( 2 2 'root' => array( 3 'pretty_version' => 'dev- trunk',4 'version' => 'dev- trunk',3 'pretty_version' => 'dev-master', 4 'version' => 'dev-master', 5 5 'type' => 'wordpress-plugin', 6 6 'install_path' => __DIR__ . '/../../', 7 7 'aliases' => array(), 8 'reference' => ' d2fb624425f2ff9b9547ceac02969a74b7af62a7',8 'reference' => '1313080a34a75fa363e2c99b07d90d28b2ca3308', 9 9 'name' => 'nhrotm/options-table-manager', 10 10 'dev' => false, … … 12 12 'versions' => array( 13 13 'nhrotm/options-table-manager' => array( 14 'pretty_version' => 'dev- trunk',15 'version' => 'dev- trunk',14 'pretty_version' => 'dev-master', 15 'version' => 'dev-master', 16 16 'type' => 'wordpress-plugin', 17 17 'install_path' => __DIR__ . '/../../', 18 18 'aliases' => array(), 19 'reference' => ' d2fb624425f2ff9b9547ceac02969a74b7af62a7',19 'reference' => '1313080a34a75fa363e2c99b07d90d28b2ca3308', 20 20 'dev_requirement' => false, 21 21 ), -
nhrrob-options-table-manager/trunk/assets/css/admin.css
r3256055 r3260159 378 378 background-color: #FF9800; 379 379 } 380 381 #nhrotm-data-table tfoot input { 382 padding: 8px 10px; 383 margin: 0; 384 border: 1px solid #aaa; 385 border-radius: 5px; 386 font-weight: normal; 387 } 388 389 /* Filter */ 390 391 .nhrotm-filter-container { 392 margin-bottom: 20px; 393 padding: 15px; 394 background: #fff; 395 border-radius: 5px; 396 box-shadow: 0 1px 3px rgba(0,0,0,0.1); 397 } 398 399 .nhrotm-filter-row { 400 display: flex; 401 /* justify-content: space-between; */ 402 justify-content: center; 403 align-items: center; 404 /* margin-bottom: 15px; */ 405 gap: 10px; 406 } 407 408 .nhrotm-filter-group { 409 display: flex; 410 align-items: center; 411 gap: 10px; 412 } 413 414 #option-type-filter { 415 min-width: 200px; 416 padding: 4px 8px; 417 border: 1px solid #ddd; 418 border-radius: 4px; 419 } 420 421 #delete-expired-transients { 422 background-color: #e74c3c; 423 padding: 4px 8px; 424 color: white; 425 border: none; 426 } -
nhrrob-options-table-manager/trunk/assets/js/admin.js
r3256055 r3260159 37 37 "type": "GET", 38 38 "url": nhrotmOptionsTableManager.ajaxUrl + "?action=nhrotm_option_table_data&nonce="+nhrotmOptionsTableManager.nonce, 39 "data": function(d) { 40 // Add column search values to the request 41 for (let i = 0; i < d.columns.length; i++) { 42 d.columns[i].search.value = $('#nhrotm-data-table tfoot input').eq(i).val(); 43 } 44 45 // Option type filter 46 $('#delete-expired-transients').attr('disabled', true); 47 d.optionTypeFilter = $('#option-type-filter').val(); 48 49 if ( d.optionTypeFilter === 'all-transients' ) { 50 $('#delete-expired-transients').attr('disabled', false); 51 52 let currentSearch = d.columns[1].search.value || ''; 53 if (!currentSearch.includes('transient_')) { 54 d.columns[1].search.value = 'transient_' + currentSearch; 55 } 56 } 57 } 39 58 }, 40 59 "columns": [ … … 49 68 // "scrollCollapse": true, 50 69 // "paging": true, 51 // "order": [[0, 'asc']], // Default order on the first column in ascending 70 // "order": [[0, 'asc']], // Default order on the first column in ascending, 71 "initComplete": function () { 72 this.api() 73 .columns() 74 .every(function () { 75 let column = this; 76 let title = column.footer().textContent; 77 78 // Create input element 79 let input = document.createElement('input'); 80 input.placeholder = title; 81 column.footer().replaceChildren(input); 82 83 input.addEventListener('keyup', function() { 84 if (column.search() !== this.value) { 85 column.search(this.value).draw(); 86 } 87 }); 88 }); 89 } 52 90 }); 53 91 … … 424 462 }); 425 463 } 464 465 // Filtering 466 // Add filter dropdown handler 467 $('#option-type-filter').on('change', function() { 468 table.ajax.reload(); 469 }); 470 471 // Add "Delete All Transients" button handler 472 $('#delete-expired-transients').on('click', function() { 473 if (confirm('Are you sure you want to delete expired transients? This action cannot be undone.')) { 474 $.ajax({ 475 url: nhrotmOptionsTableManager.ajaxUrl, 476 type: 'POST', 477 data: { 478 action: 'nhrotm_delete_expired_transients', 479 nonce: nhrotmOptionsTableManager.nonce 480 }, 481 success: function(response) { 482 if (response.success) { 483 showToast("Expired transients deleted successfully!", "success"); 484 table.ajax.reload(); 485 } else { 486 showToast('Failed to delete transients: ' + response.data, "error"); 487 } 488 }, 489 error: function() { 490 showToast('An error occurred while deleting transients.', "error"); 491 } 492 }); 493 } 494 }); 426 495 427 496 }); -
nhrrob-options-table-manager/trunk/composer.json
r3256055 r3260159 26 26 }, 27 27 "scripts": { 28 "deploy": "composer install --no-dev && wp dist-archive . && composer install" 28 "deploy": "composer install --no-dev && wp dist-archive . && composer install", 29 "dev": "composer install", 30 "build": "composer install --no-dev" 29 31 } 30 32 } -
nhrrob-options-table-manager/trunk/includes/Ajax.php
r3256055 r3260159 17 17 add_action('wp_ajax_nhrotm_edit_option', [ $this, 'edit_option' ]); 18 18 add_action('wp_ajax_nhrotm_delete_option', [ $this, 'delete_option' ]); 19 add_action('wp_ajax_nhrotm_delete_expired_transients', [ $this, 'delete_expired_transients' ]); 19 20 add_action('wp_ajax_nhrotm_option_usage_analytics', [ $this, 'option_usage_analytics' ]); 20 21 … … 44 45 // Search parameter 45 46 $search = isset($_GET['search']['value']) ? sanitize_text_field(wp_unslash($_GET['search']['value'])) : ''; 47 $option_type_filter = isset($_GET['optionTypeFilter']) && in_array($_GET['optionTypeFilter'], ['all-options', 'all-transients', 'active-transients', 'expired-transients']) ? sanitize_text_field(wp_unslash($_GET['optionTypeFilter'])) : 'all-options'; 46 48 47 49 // Sorting parameters 48 50 $order_column_index = isset($_GET['order'][0]['column']) ? intval($_GET['order'][0]['column']) : 0; 49 51 $order_direction = isset($_GET['order'][0]['dir']) && in_array($_GET['order'][0]['dir'], ['asc', 'desc']) ? strtolower( sanitize_text_field( wp_unslash( $_GET['order'][0]['dir'] ) ) ) : 'asc'; 50 52 51 53 // Define columns in the correct order for sorting 52 54 $columns = ['option_id', 'option_name', 'option_value', 'autoload']; … … 64 66 ); 65 67 66 // Prepare queries for different order options 67 // Using separate complete queries for each column and direction to avoid concatenation 68 // Get column search values 69 $column_search = []; 70 if (isset($_GET['columns']) && is_array( $_GET['columns'] )) { 71 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 72 $columns = $this->sanitize_recursive( wp_unslash( $_GET['columns'] ) ); 73 74 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized 75 foreach ($_GET['columns'] as $column) { 76 if (isset($column['search']['value'])) { 77 $column_search[] = sanitize_text_field(wp_unslash($column['search']['value'])); 78 } else { 79 $column_search[] = ''; 80 } 81 } 82 } 83 84 // Build WHERE clause for search conditions 85 $where_clauses = []; 86 87 // Global search 68 88 if (!empty($search)) { 69 89 $search_like = '%' . $wpdb->esc_like($search) . '%'; 90 $where_clauses[] = $wpdb->prepare( 91 "(option_name LIKE %s OR option_value LIKE %s)", 92 $search_like, 93 $search_like 94 ); 95 } 96 97 // Individual column searches 98 if (!empty($column_search)) { 99 // option_id column (index 0) 100 if (!empty($column_search[0])) { 101 // For numeric column, use exact match or range 102 if (is_numeric($column_search[0])) { 103 $where_clauses[] = $wpdb->prepare("option_id = %d", intval($column_search[0])); 104 } 105 } 70 106 71 // Get filtered count 72 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 73 $filtered_records = $wpdb->get_var( 74 $wpdb->prepare( 75 "SELECT COUNT(*) FROM {$wpdb->prefix}options WHERE option_name LIKE %s OR option_value LIKE %s", 76 $search_like, 77 $search_like 78 ) 79 ); 80 81 // Get data with search and properly hardcoded ORDER BY 82 if ($order_column === 'option_id') { 83 if ($order_direction === 'desc') { 84 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 85 $data = $wpdb->get_results( 86 $wpdb->prepare( 87 "SELECT * FROM {$wpdb->prefix}options 88 WHERE option_name LIKE %s OR option_value LIKE %s 89 ORDER BY option_id DESC 90 LIMIT %d, %d", 91 $search_like, $search_like, $start, $length 92 ), 93 ARRAY_A 94 ); 95 } else { 96 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 97 $data = $wpdb->get_results( 98 $wpdb->prepare( 99 "SELECT * FROM {$wpdb->prefix}options 100 WHERE option_name LIKE %s OR option_value LIKE %s 101 ORDER BY option_id ASC 102 LIMIT %d, %d", 103 $search_like, $search_like, $start, $length 104 ), 105 ARRAY_A 106 ); 107 } 108 } elseif ($order_column === 'option_name') { 109 if ($order_direction === 'desc') { 110 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 111 $data = $wpdb->get_results( 112 $wpdb->prepare( 113 "SELECT * FROM {$wpdb->prefix}options 114 WHERE option_name LIKE %s OR option_value LIKE %s 115 ORDER BY option_name DESC 116 LIMIT %d, %d", 117 $search_like, $search_like, $start, $length 118 ), 119 ARRAY_A 120 ); 121 } else { 122 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 123 $data = $wpdb->get_results( 124 $wpdb->prepare( 125 "SELECT * FROM {$wpdb->prefix}options 126 WHERE option_name LIKE %s OR option_value LIKE %s 127 ORDER BY option_name ASC 128 LIMIT %d, %d", 129 $search_like, $search_like, $start, $length 130 ), 131 ARRAY_A 132 ); 133 } 134 } elseif ($order_column === 'option_value') { 135 if ($order_direction === 'desc') { 136 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 137 $data = $wpdb->get_results( 138 $wpdb->prepare( 139 "SELECT * FROM {$wpdb->prefix}options 140 WHERE option_name LIKE %s OR option_value LIKE %s 141 ORDER BY option_value DESC 142 LIMIT %d, %d", 143 $search_like, $search_like, $start, $length 144 ), 145 ARRAY_A 146 ); 147 } else { 148 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 149 $data = $wpdb->get_results( 150 $wpdb->prepare( 151 "SELECT * FROM {$wpdb->prefix}options 152 WHERE option_name LIKE %s OR option_value LIKE %s 153 ORDER BY option_value ASC 154 LIMIT %d, %d", 155 $search_like, $search_like, $start, $length 156 ), 157 ARRAY_A 158 ); 159 } 160 } elseif ($order_column === 'autoload') { 161 if ($order_direction === 'desc') { 162 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 163 $data = $wpdb->get_results( 164 $wpdb->prepare( 165 "SELECT * FROM {$wpdb->prefix}options 166 WHERE option_name LIKE %s OR option_value LIKE %s 167 ORDER BY autoload DESC 168 LIMIT %d, %d", 169 $search_like, $search_like, $start, $length 170 ), 171 ARRAY_A 172 ); 173 } else { 174 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 175 $data = $wpdb->get_results( 176 $wpdb->prepare( 177 "SELECT * FROM {$wpdb->prefix}options 178 WHERE option_name LIKE %s OR option_value LIKE %s 179 ORDER BY autoload ASC 180 LIMIT %d, %d", 181 $search_like, $search_like, $start, $length 182 ), 183 ARRAY_A 184 ); 185 } 186 } else { 187 // Default fallback 188 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 189 $data = $wpdb->get_results( 190 $wpdb->prepare( 191 "SELECT * FROM {$wpdb->prefix}options 192 WHERE option_name LIKE %s OR option_value LIKE %s 193 ORDER BY option_id ASC 194 LIMIT %d, %d", 195 $search_like, $search_like, $start, $length 196 ), 197 ARRAY_A 107 // option_name column (index 1) 108 if (!empty($column_search[1])) { 109 $where_clauses[] = $wpdb->prepare( 110 "option_name LIKE %s", 111 '%' . $wpdb->esc_like($column_search[1]) . '%' 198 112 ); 199 113 } 200 } else {201 // No search applied, use total as filtered count202 $filtered_records = $total_records;203 114 204 // Get data without search and properly hardcoded ORDER BY 205 if ($order_column === 'option_id') { 206 if ($order_direction === 'desc') { 207 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 208 $data = $wpdb->get_results( 209 $wpdb->prepare( 210 "SELECT * FROM {$wpdb->prefix}options 211 ORDER BY option_id DESC 212 LIMIT %d, %d", 213 $start, $length 214 ), 215 ARRAY_A 216 ); 217 } else { 218 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 219 $data = $wpdb->get_results( 220 $wpdb->prepare( 221 "SELECT * FROM {$wpdb->prefix}options 222 ORDER BY option_id ASC 223 LIMIT %d, %d", 224 $start, $length 225 ), 226 ARRAY_A 227 ); 228 } 229 } elseif ($order_column === 'option_name') { 230 if ($order_direction === 'desc') { 231 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 232 $data = $wpdb->get_results( 233 $wpdb->prepare( 234 "SELECT * FROM {$wpdb->prefix}options 235 ORDER BY option_name DESC 236 LIMIT %d, %d", 237 $start, $length 238 ), 239 ARRAY_A 240 ); 241 } else { 242 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 243 $data = $wpdb->get_results( 244 $wpdb->prepare( 245 "SELECT * FROM {$wpdb->prefix}options 246 ORDER BY option_name ASC 247 LIMIT %d, %d", 248 $start, $length 249 ), 250 ARRAY_A 251 ); 252 } 253 } elseif ($order_column === 'option_value') { 254 if ($order_direction === 'desc') { 255 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 256 $data = $wpdb->get_results( 257 $wpdb->prepare( 258 "SELECT * FROM {$wpdb->prefix}options 259 ORDER BY option_value DESC 260 LIMIT %d, %d", 261 $start, $length 262 ), 263 ARRAY_A 264 ); 265 } else { 266 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 267 $data = $wpdb->get_results( 268 $wpdb->prepare( 269 "SELECT * FROM {$wpdb->prefix}options 270 ORDER BY option_value ASC 271 LIMIT %d, %d", 272 $start, $length 273 ), 274 ARRAY_A 275 ); 276 } 277 } elseif ($order_column === 'autoload') { 278 if ($order_direction === 'desc') { 279 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 280 $data = $wpdb->get_results( 281 $wpdb->prepare( 282 "SELECT * FROM {$wpdb->prefix}options 283 ORDER BY autoload DESC 284 LIMIT %d, %d", 285 $start, $length 286 ), 287 ARRAY_A 288 ); 289 } else { 290 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 291 $data = $wpdb->get_results( 292 $wpdb->prepare( 293 "SELECT * FROM {$wpdb->prefix}options 294 ORDER BY autoload ASC 295 LIMIT %d, %d", 296 $start, $length 297 ), 298 ARRAY_A 299 ); 300 } 301 } else { 302 // Default fallback 303 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 304 $data = $wpdb->get_results( 305 $wpdb->prepare( 306 "SELECT * FROM {$wpdb->prefix}options 307 ORDER BY option_id ASC 308 LIMIT %d, %d", 309 $start, $length 310 ), 311 ARRAY_A 115 // option_value column (index 2) 116 if (!empty($column_search[2])) { 117 $where_clauses[] = $wpdb->prepare( 118 "option_value LIKE %s", 119 '%' . $wpdb->esc_like($column_search[2]) . '%' 312 120 ); 313 121 } 314 } 122 123 // autoload column (index 3) 124 if (!empty($column_search[3])) { 125 $where_clauses[] = $wpdb->prepare( 126 "autoload LIKE %s", 127 '%' . $wpdb->esc_like($column_search[3]) . '%' 128 ); 129 } 130 } 131 132 // Combine WHERE clauses 133 $where_sql = ''; 134 if (!empty($where_clauses)) { 135 $where_sql = 'WHERE ' . implode(' AND ', $where_clauses); 136 } 137 138 // Count filtered records 139 $filtered_records_sql = "SELECT COUNT(*) FROM {$wpdb->prefix}options {$where_sql}"; 140 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared 141 $filtered_records = $wpdb->get_var($filtered_records_sql); 142 143 // SQL for ordering 144 $order_sql = "ORDER BY {$order_column} {$order_direction}"; 145 146 // Get data with search, order, and pagination 147 $data_sql = "SELECT * FROM {$wpdb->prefix}options {$where_sql} {$order_sql} LIMIT %d, %d"; 148 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 149 $data = $wpdb->get_results( 150 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared 151 $wpdb->prepare($data_sql, $start, $length), 152 ARRAY_A 153 ); 315 154 316 155 // Wrap the option_value in the scrollable-cell div … … 318 157 $is_protected = in_array($row['option_name'], $this->get_protected_options()); 319 158 $protected_attr = $is_protected ? sprintf('title="%s" disabled', esc_attr__('Protected', 'nhrrob-options-table-manager')) : ''; 159 160 if ( 'all-transients' === $option_type_filter ) { 161 // all options are transients 162 $transient_name = str_replace('_transient_', '', $row['option_name']); 163 $transient_value = get_transient($transient_name); 164 165 $transient_status = $transient_value ? '[active]' : '[expired]'; 166 $row['option_name'] = esc_html($transient_status . $row['option_name']); 167 } 320 168 321 169 $row['option_value'] = '<div class="scrollable-cell">' . esc_html($row['option_value']) . '</div>'; … … 329 177 $protected_attr, 330 178 ); 331 } 179 } 332 180 333 181 // Prepare response for DataTables … … 628 476 629 477 wp_die(); 478 } 479 480 /** 481 * Delete expired transients from the options table 482 */ 483 public function delete_expired_transients() { 484 // Verify nonce 485 if (!isset($_POST['nonce']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['nonce'])), 'nhrotm-admin-nonce')) { 486 wp_send_json_error('Invalid nonce'); 487 wp_die(); 488 } 489 490 global $wpdb; 491 492 // Get all transient options 493 // phpcs:ignore:WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching 494 $transients = $wpdb->get_results( 495 "SELECT option_name, option_value 496 FROM {$wpdb->options} 497 WHERE option_name LIKE '%_transient_%' 498 AND option_name NOT LIKE '%_transient_timeout_%'", 499 ARRAY_A 500 ); 501 502 try { 503 $deleted_transients = []; 504 505 foreach ($transients as $transient) { 506 507 $transient_name = str_replace('_transient_', '', $transient['option_name']); 508 509 if (false === get_transient($transient_name)) { 510 // Transient has expired, delete it 511 $deleted_transients[] = $transient_name; 512 delete_transient(esc_sql( $transient_name ) ); 513 } 514 } 515 516 wp_send_json_success([ 517 'message' => 'Expired transients deleted successfully', 518 'count' => count($deleted_transients), 519 'deleted_transients' => $deleted_transients, 520 ]); 521 } catch (\Exception $e) { 522 wp_send_json_error('Database error'); 523 wp_die(); 524 } 630 525 } 631 526 -
nhrrob-options-table-manager/trunk/includes/Traits/GlobalTrait.php
r3256055 r3260159 282 282 283 283 // Helper function to recursively sanitize arrays and objects 284 public function sanitize_recursive(&$data) { 285 // Handle arrays using array_walk_recursive for deep sanitization 284 public function sanitize_recursive($data) { 286 285 if (is_array($data)) { 287 array_walk_recursive($data, function (&$item, $key) { 288 if ($key !== 'content') { 289 $item = sanitize_text_field($item); // Only sanitize if the key is not 'content' 290 } 291 }); 292 } 293 // Handle objects by converting them to arrays and applying the function recursively 294 elseif (is_object($data)) { 295 foreach ($data as $key => &$value) { 296 if ($key === 'content') { 297 continue; // Skip sanitization for 'content' fields 298 } 299 300 if (is_scalar($value)) { 301 $value = sanitize_text_field($value); 302 } else { 303 $this->sanitize_recursive($value); // Recurse for arrays or nested objects 286 $sanitized_array = []; 287 foreach ($data as $key => $value) { 288 $sanitized_key = sanitize_key($key); 289 if ($sanitized_key !== '') { 290 $sanitized_array[$sanitized_key] = $this->sanitize_recursive($value); 304 291 } 305 292 } 293 return $sanitized_array; 294 } elseif (is_object($data)) { 295 $sanitized_object = new \stdClass(); 296 $object_vars = get_object_vars($data); 297 298 foreach ($object_vars as $key => $value) { 299 $sanitized_key = $this->sanitize_key($key); 300 if ($sanitized_key !== '') { 301 $sanitized_object->$sanitized_key = $this->sanitize_recursive($value); 302 } 303 } 304 return $sanitized_object; 305 } else { 306 return $this->sanitize_item($data); 306 307 } 307 308 } 308 309 309 public function castValues(&$array, $option_name = '') { 310 // $exceptional_option_names = $this->exceptional_option_names(); 311 312 foreach ($array as $key => &$value) { 313 if (is_array($value)) { 314 $this->castValues($value); // Recursive call for nested arrays 315 } elseif (is_numeric($value)) { 316 $value = (int) $value; // Convert numeric strings to integers 317 } elseif ($value === "true") { 318 $value = true; // Convert "true" string to boolean true 319 } elseif ($value === "false") { 320 $value = false; // Convert "false" string to boolean false 321 } elseif (empty($value) && $key === "recurrence") { 322 $value = false; // Ensure recurrence defaults to false if empty 323 } 324 325 // if ( ! empty( $option_name ) && in_array( $option_name, $exceptional_option_names ) ) { 326 // if ( empty($value) && $key === "recurrence" ) { 327 // $value = false; 328 // } 329 // } 310 public function sanitize_item( $item ){ 311 $item_formatted = ''; 312 313 if ( is_numeric( $item )) { 314 $item_formatted = intval( $item ); 315 } elseif ( is_email( $item )) { 316 $item_formatted = sanitize_email( $item ); 317 } else { 318 $item_formatted = sanitize_text_field( wp_unslash( $item ) ); 330 319 } 320 321 return $item_formatted; 331 322 } 332 323 -
nhrrob-options-table-manager/trunk/includes/views/admin/settings/index.php
r3256055 r3260159 19 19 </div> 20 20 21 <!-- Filter starts --> 22 <div class="nhrotm-filter-container"> 23 <div class="nhrotm-filter-row"> 24 <div class="nhrotm-filter-group"> 25 <select id="option-type-filter"> 26 <option value="all-options"><?php esc_html_e('All Options', 'nhrrob-options-table-manager'); ?></option> 27 <option value="all-transients"><?php esc_html_e('All Transients', 'nhrrob-options-table-manager'); ?></option> 28 </select> 29 </div> 30 <div class="nhrotm-filter-group"> 31 <button id="delete-expired-transients" class="button button-danger" disabled><?php esc_html_e('Delete Expired Transients', 'nhrrob-options-table-manager'); ?></button> 32 </div> 33 </div> 34 </div> 35 <!-- Filter ends --> 36 21 37 <table id="nhrotm-data-table" class="nhrotm-data-table wp-list-table widefat fixed striped"> 22 38 <thead> … … 30 46 </thead> 31 47 48 <tfoot> 49 <tr> 50 <th><?php esc_html_e('Option ID', 'nhrrob-options-table-manager'); ?></th> 51 <th><?php esc_html_e('Option Name', 'nhrrob-options-table-manager'); ?></th> 52 <th><?php esc_html_e('Option Value', 'nhrrob-options-table-manager'); ?></th> 53 <th><?php esc_html_e('Autoload', 'nhrrob-options-table-manager'); ?></th> 54 <th><?php esc_html_e('Action', 'nhrrob-options-table-manager'); ?></th> 55 </tr> 56 </tfoot> 57 32 58 <tbody> 33 59 -
nhrrob-options-table-manager/trunk/nhrrob-options-table-manager.php
r3256164 r3260159 6 6 * Author: Nazmul Hasan Robin 7 7 * Author URI: https://profiles.wordpress.org/nhrrob/ 8 * Version: 1.1. 68 * Version: 1.1.7-beta1 9 9 * Requires at least: 6.0 10 10 * Requires PHP: 7.4 … … 28 28 * @var string 29 29 */ 30 const nhrotm_version = '1.1. 6';30 const nhrotm_version = '1.1.7-beta1'; 31 31 32 32 /** -
nhrrob-options-table-manager/trunk/readme.txt
r3256164 r3260159 5 5 Tested up to: 6.7 6 6 Requires PHP: 7.4 7 Stable tag: 1.1.6 7 Stable tag: 1.1.6 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 85 85 86 86 == Changelog == 87 88 = 1.1.7 - 23/03/2025 = 89 - Added: Column search feature 90 - Added: Filter by option type - option or transient 91 - Added: Delete all expired transients button and functionality 92 - Few minor bug fixing & improvements 87 93 88 94 = 1.1.6 - 15/03/2025 = -
nhrrob-options-table-manager/trunk/vendor/composer/installed.php
r3256164 r3260159 1 1 <?php return array( 2 2 'root' => array( 3 'pretty_version' => 'dev- trunk',4 'version' => 'dev- trunk',3 'pretty_version' => 'dev-master', 4 'version' => 'dev-master', 5 5 'type' => 'wordpress-plugin', 6 6 'install_path' => __DIR__ . '/../../', 7 7 'aliases' => array(), 8 'reference' => ' d2fb624425f2ff9b9547ceac02969a74b7af62a7',8 'reference' => '1313080a34a75fa363e2c99b07d90d28b2ca3308', 9 9 'name' => 'nhrotm/options-table-manager', 10 10 'dev' => false, … … 12 12 'versions' => array( 13 13 'nhrotm/options-table-manager' => array( 14 'pretty_version' => 'dev- trunk',15 'version' => 'dev- trunk',14 'pretty_version' => 'dev-master', 15 'version' => 'dev-master', 16 16 'type' => 'wordpress-plugin', 17 17 'install_path' => __DIR__ . '/../../', 18 18 'aliases' => array(), 19 'reference' => ' d2fb624425f2ff9b9547ceac02969a74b7af62a7',19 'reference' => '1313080a34a75fa363e2c99b07d90d28b2ca3308', 20 20 'dev_requirement' => false, 21 21 ),
Note: See TracChangeset
for help on using the changeset viewer.