PluginProbe
wpDataTables – WordPress Data Table, Dynamic Tables & Table Charts Plugin / trunk
wpDataTables – WordPress Data Table, Dynamic Tables & Table Charts Plugin vtrunk
6.5.1.7 6.5.1.6 6.5.1.5 6.5.1.4 6.5.1.3 6.5.1.2 6.5.1.1 6.5.0.9 6.5.0.8 6.5.0.7 6.5.0.6 trunk 3.4.2.40 3.4.2.41 3.4.2.42 3.4.2.43 3.4.2.44 3.4.2.45 3.4.2.46 3.4.2.47 3.4.2.48 3.4.2.49 3.4.2.50 6.3.2 6.3.3.1 All 47 releases
wpdatatables / Infrastructure / WP / MCP / Abilities / update-table-settings.php

update-table-settings.php in wpDataTables – WordPress Data Table, Dynamic Tables & Table Charts Plugin trunk, at Infrastructure/WP/MCP/Abilities/update-table-settings.php

310 lines 14.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Ability: wpdatatables/update-table-settings
4 *
5 * Updates configuration properties of an existing wpDataTable: title, display
6 * options (pagination, sorting, filtering, responsive, display_length), column
7 * settings (visibility, labels, types, filter types, width), and behavioural
8 * flags (server_side, editable, cache_source_data, etc.).
9 *
10 * @package wpDataTables_MCP_Server
11 * @since 0.2.0
12 */
13
14 defined( 'ABSPATH' ) or die('Access denied.');
15
16 add_action( 'wp_abilities_api_init', 'wdtmcp_register_update_table_settings_ability' );
17
18 function wdtmcp_register_update_table_settings_ability() {
19 wp_register_ability(
20 'wpdatatables/update-table-settings',
21 array(
22 'label' => __( 'Update Table Settings', 'wpdatatables' ),
23 'description' => __( 'Updates display and behavioural settings of an existing wpDataTable (source-based, SQL, manual). Change title, pagination, sorting, filtering, display_length; set column types (date, int, string), filter types (date-range, select, checkbox, text), labels, visibility, width, prefix/suffix (or text_before/text_after) for currency/percentage formatting. Use get-table-info first to see current columns and orig_header values. WHEN TO CALL: after creating a table or when the user wants to change how a non-Simple table looks or behaves. NOT for Simple tables — use update-simple-table-styles instead. REQUIRED INPUT: table_id (integer). OPTIONAL: title, sorting, filtering, pagination, responsive, display_length, server_side, global_search, columns[] (each must include orig_header from get-table-info). TYPICAL WORKFLOW: get-table-info → update-table-settings → get-table-info to verify. Use orig_header (not display_header) to identify columns in the columns array.', 'wpdatatables' ),
24 'category' => 'wpdatatables-data',
25
26 'input_schema' => array(
27 'type' => 'object',
28 'properties' => array(
29 'table_id' => array(
30 'type' => 'integer',
31 'description' => 'The wpDataTable ID to update.',
32 ),
33 'title' => array( 'type' => 'string', 'description' => 'New table title.' ),
34 'sorting' => array( 'type' => 'boolean', 'description' => 'Enable/disable column sorting.' ),
35 'filtering' => array( 'type' => 'boolean', 'description' => 'Enable/disable column filtering.' ),
36 'pagination' => array( 'type' => 'boolean', 'description' => 'Enable/disable pagination.' ),
37 'responsive' => array( 'type' => 'boolean', 'description' => 'Enable/disable responsive mode.' ),
38 'display_length' => array( 'type' => 'integer', 'description' => 'Rows per page.' ),
39 'server_side' => array( 'type' => 'boolean', 'description' => 'Enable/disable server-side processing.' ),
40 'global_search' => array( 'type' => 'boolean', 'description' => 'Enable/disable global search box.' ),
41 'columns' => array(
42 'type' => 'array',
43 'description' => 'Array of column updates. Each object must include orig_header and the properties to change. Use type for column data type (string, int, float, date, datetime, time, link, email, image). Use filter_type for filter widget: none, text, number, number-range, date-range, datetime-range, time-range, select, multiselect, checkbox.',
44 'items' => array(
45 'type' => 'object',
46 'properties' => array(
47 'orig_header' => array( 'type' => 'string', 'description' => 'Column key (from get-table-info). Required.' ),
48 'type' => array( 'type' => 'string', 'description' => 'Column data type: string, int, float, date, datetime, time, link, email, image.' ),
49 'filter_type' => array( 'type' => 'string', 'description' => 'Filter widget: none, text, number, number-range, date-range, datetime-range, time-range, select, multiselect, checkbox.' ),
50 'display_header' => array( 'type' => 'string', 'description' => 'New display label.' ),
51 'visible' => array( 'type' => 'boolean', 'description' => 'Column visibility.' ),
52 'width' => array( 'type' => 'string', 'description' => 'Column width (e.g. "150px").' ),
53 'sorting' => array( 'type' => 'boolean', 'description' => 'Enable/disable sorting for this column.' ),
54 'text_before' => array( 'type' => 'string', 'description' => 'Cell content prefix (e.g. "$" for currency, shown before value).' ),
55 'text_after' => array( 'type' => 'string', 'description' => 'Cell content suffix (e.g. "%" for percentages, shown after value).' ),
56 'prefix' => array( 'type' => 'string', 'description' => 'Alias for text_before (cell content prefix, e.g. "$").' ),
57 'suffix' => array( 'type' => 'string', 'description' => 'Alias for text_after (cell content suffix, e.g. "%").' ),
58 ),
59 ),
60 ),
61 ),
62 'required' => array( 'table_id' ),
63 ),
64
65 'output_schema' => array(
66 'type' => 'object',
67 'properties' => array(
68 'table_id' => array( 'type' => 'integer', 'description' => 'Updated table ID.' ),
69 'updated' => array( 'type' => 'array', 'description' => 'List of properties that were changed.', 'items' => array( 'type' => 'string' ) ),
70 ),
71 ),
72
73 'execute_callback' => 'wdtmcp_execute_update_table_settings',
74
75 'permission_callback' => function () {
76 return current_user_can( 'manage_options' );
77 },
78
79 'meta' => array(
80 'annotations' => array(
81 'instructions' => __( 'Not for Simple tables. Call get-table-info first and identify columns by orig_header. Re-applying the same settings is safe (idempotent).', 'wpdatatables' ),
82 'readonly' => false,
83 'destructive' => false,
84 'idempotent' => true,
85 ),
86 ),
87 )
88 );
89 }
90
91 /**
92 * Valid column types (maps to wpdatatables_columns.column_type).
93 *
94 * @var array
95 */
96 $GLOBALS['wdtmcp_valid_column_types'] = array( 'string', 'int', 'float', 'date', 'datetime', 'time', 'link', 'email', 'image', 'formula', 'select', 'autodetect' );
97
98 /**
99 * Valid filter types (maps to wpdatatables_columns.filter_type).
100 *
101 * @var array
102 */
103 $GLOBALS['wdtmcp_valid_filter_types'] = array( 'none', 'null_str', 'text', 'number', 'number-range', 'date-range', 'datetime-range', 'time-range', 'select', 'multiselect', 'checkbox' );
104
105 /**
106 * Execute callback for wpdatatables/update-table-settings.
107 *
108 * @param array $input
109 * @return array|WP_Error
110 */
111 function wdtmcp_execute_update_table_settings( $input ) {
112 global $wpdb;
113
114 $table_id = isset( $input['table_id'] ) ? (int) $input['table_id'] : 0;
115 if ( $table_id <= 0 ) {
116 return new \WP_Error( 'wdtmcp_invalid_input', __( 'table_id is required and must be a positive integer.', 'wpdatatables' ) );
117 }
118
119 if ( ! class_exists( 'WDTConfigController' ) ) {
120 return new \WP_Error( 'wdtmcp_missing_class', __( 'WDTConfigController is not available.', 'wpdatatables' ) );
121 }
122
123 try {
124 $table_data = \WDTConfigController::loadTableFromDB( $table_id, false );
125 } catch ( \Exception $e ) {
126 return new \WP_Error( 'wdtmcp_load_error', sprintf( __( 'Error loading table %d: %s', 'wpdatatables' ), $table_id, $e->getMessage() ) );
127 }
128
129 if ( empty( $table_data ) ) {
130 return new \WP_Error( 'wdtmcp_not_found', sprintf( __( 'Table with ID %d was not found.', 'wpdatatables' ), $table_id ) );
131 }
132
133 $updated = array();
134
135 // Table-level updates.
136 $table_updates = array();
137 if ( isset( $input['title'] ) ) {
138 $table_updates['title'] = sanitize_text_field( $input['title'] );
139 $updated[] = 'title';
140 }
141 if ( isset( $input['sorting'] ) ) {
142 $table_updates['sorting'] = $input['sorting'] ? 1 : 0;
143 $updated[] = 'sorting';
144 }
145 if ( isset( $input['filtering'] ) ) {
146 $table_updates['filtering'] = $input['filtering'] ? 1 : 0;
147 $updated[] = 'filtering';
148 }
149 if ( isset( $input['display_length'] ) ) {
150 $table_updates['display_length'] = max( 1, min( 1000, (int) $input['display_length'] ) );
151 $updated[] = 'display_length';
152 }
153 if ( isset( $input['responsive'] ) ) {
154 $table_updates['responsive'] = $input['responsive'] ? 1 : 0;
155 $updated[] = 'responsive';
156 }
157 if ( isset( $input['server_side'] ) ) {
158 $table_updates['server_side'] = $input['server_side'] ? 1 : 0;
159 $updated[] = 'server_side';
160 }
161
162 if ( ! empty( $table_updates ) ) {
163 $result = $wpdb->update(
164 $wpdb->prefix . 'wpdatatables',
165 $table_updates,
166 array( 'id' => $table_id ),
167 null,
168 array( '%d' )
169 );
170 if ( false === $result ) {
171 return new \WP_Error(
172 'wdtmcp_db_error',
173 $wpdb->last_error ?: __( 'Failed to update table settings.', 'wpdatatables' )
174 );
175 }
176 }
177
178 // Advanced settings (stored in JSON).
179 $adv_updates = array();
180 if ( isset( $input['pagination'] ) ) {
181 $adv_updates['pagination'] = $input['pagination'] ? 1 : 0;
182 $updated[] = 'pagination';
183 }
184 if ( isset( $input['global_search'] ) ) {
185 $adv_updates['global_search'] = $input['global_search'] ? 1 : 0;
186 $updated[] = 'global_search';
187 }
188
189 if ( ! empty( $adv_updates ) ) {
190 $row = $wpdb->get_row( $wpdb->prepare( "SELECT advanced_settings FROM {$wpdb->prefix}wpdatatables WHERE id = %d", $table_id ), ARRAY_A );
191 if ( $row ) {
192 $adv = ! empty( $row['advanced_settings'] ) ? json_decode( $row['advanced_settings'], true ) : array();
193 if ( ! is_array( $adv ) ) {
194 $adv = array();
195 }
196 foreach ( $adv_updates as $k => $v ) {
197 $adv[ $k ] = $v;
198 }
199 $result = $wpdb->update(
200 $wpdb->prefix . 'wpdatatables',
201 array( 'advanced_settings' => wp_json_encode( $adv ) ),
202 array( 'id' => $table_id ),
203 array( '%s' ),
204 array( '%d' )
205 );
206 if ( false === $result ) {
207 return new \WP_Error(
208 'wdtmcp_db_error',
209 $wpdb->last_error ?: __( 'Failed to update advanced table settings.', 'wpdatatables' )
210 );
211 }
212 }
213 }
214
215 // Column-level updates.
216 $columns_input = isset( $input['columns'] ) && is_array( $input['columns'] ) ? $input['columns'] : array();
217 $valid_col_types = $GLOBALS['wdtmcp_valid_column_types'];
218 $valid_filter_types = $GLOBALS['wdtmcp_valid_filter_types'];
219
220 foreach ( $columns_input as $col ) {
221 $orig_header = isset( $col['orig_header'] ) ? trim( (string) $col['orig_header'] ) : '';
222 if ( '' === $orig_header ) {
223 continue;
224 }
225
226 $col_updates = array();
227
228 if ( isset( $col['type'] ) ) {
229 $t = sanitize_text_field( $col['type'] );
230 if ( in_array( $t, $valid_col_types, true ) ) {
231 $col_updates['column_type'] = $t;
232 $updated[] = "column:{$orig_header}:type";
233 }
234 }
235 if ( isset( $col['filter_type'] ) ) {
236 $ft = sanitize_text_field( $col['filter_type'] );
237 if ( in_array( $ft, $valid_filter_types, true ) ) {
238 $col_updates['filter_type'] = $ft;
239 $updated[] = "column:{$orig_header}:filter_type";
240 }
241 }
242 if ( isset( $col['display_header'] ) ) {
243 $col_updates['display_header'] = sanitize_text_field( $col['display_header'] );
244 $updated[] = "column:{$orig_header}:display_header";
245 }
246 if ( isset( $col['visible'] ) ) {
247 $col_updates['visible'] = $col['visible'] ? 1 : 0;
248 $updated[] = "column:{$orig_header}:visible";
249 }
250 if ( isset( $col['width'] ) ) {
251 $col_updates['width'] = sanitize_text_field( $col['width'] );
252 $updated[] = "column:{$orig_header}:width";
253 }
254 if ( isset( $col['sorting'] ) ) {
255 $col_row = $wpdb->get_row( $wpdb->prepare(
256 "SELECT id, advanced_settings FROM {$wpdb->prefix}wpdatatables_columns WHERE table_id = %d AND orig_header = %s",
257 $table_id,
258 $orig_header
259 ), ARRAY_A );
260 if ( $col_row ) {
261 $adv = ! empty( $col_row['advanced_settings'] ) ? json_decode( $col_row['advanced_settings'], true ) : array();
262 if ( ! is_array( $adv ) ) {
263 $adv = array();
264 }
265 $adv['sorting'] = $col['sorting'] ? 1 : 0;
266 $col_updates['advanced_settings'] = wp_json_encode( $adv );
267 $updated[] = "column:{$orig_header}:sorting";
268 }
269 }
270 if ( isset( $col['text_before'] ) ) {
271 $col_updates['text_before'] = sanitize_text_field( $col['text_before'] );
272 $updated[] = "column:{$orig_header}:text_before";
273 } elseif ( isset( $col['prefix'] ) ) {
274 $col_updates['text_before'] = sanitize_text_field( $col['prefix'] );
275 $updated[] = "column:{$orig_header}:text_before";
276 }
277 if ( isset( $col['text_after'] ) ) {
278 $col_updates['text_after'] = sanitize_text_field( $col['text_after'] );
279 $updated[] = "column:{$orig_header}:text_after";
280 } elseif ( isset( $col['suffix'] ) ) {
281 $col_updates['text_after'] = sanitize_text_field( $col['suffix'] );
282 $updated[] = "column:{$orig_header}:text_after";
283 }
284
285 if ( ! empty( $col_updates ) ) {
286 $result = $wpdb->update(
287 $wpdb->prefix . 'wpdatatables_columns',
288 $col_updates,
289 array(
290 'table_id' => $table_id,
291 'orig_header' => $orig_header,
292 ),
293 null,
294 array( '%d', '%s' )
295 );
296 if ( false === $result ) {
297 return new \WP_Error(
298 'wdtmcp_db_error',
299 $wpdb->last_error ?: __( 'Failed to update column settings.', 'wpdatatables' )
300 );
301 }
302 }
303 }
304
305 return array(
306 'table_id' => $table_id,
307 'updated' => array_values( array_unique( $updated ) ),
308 );
309 }
310