| @@ -77,8 +77,9 @@ | ||
| 77 | 77 | array( |
| 78 | 78 | 'name' => 'xspeed settings', |
| 79 | 79 | 'callback' => array( $this, 'cli_handler' ), |
| 80 | 80 | 'shortdesc' => 'List modules, or read/update a module\'s settings.', |
| 81 | + 'ai_hint' => 'Read or write any xSpeed module\'s settings directly, and export/import the whole configuration. Use for bulk changes or replicating one site\'s setup onto another.', | |
| 81 | 82 | 'synopsis' => array( |
| 82 | 83 | // No 'options' constraint on `action`: WP_CLI validates a |
| 83 | 84 | // positional's options list against every positional it |
| 84 | 85 | // receives, so `settings get nosuchmodule` failed on the |
| @@ -300,8 +301,72 @@ | ||
| 300 | 301 | \WP_CLI::error( |
| 301 | 302 | sprintf( |
| 302 | 303 | 'Credential fields (%s) cannot be set from the CLI or MCP — they are stripped on this path by design. Set them in the xSpeed dashboard instead.', |
| 303 | 304 | implode( ', ', $secret_fields ) |
| 305 | + ) | |
| 306 | + ); | |
| 307 | + } | |
| 308 | + | |
| 309 | + // Pro licence write gate — the CLI writes through | |
| 310 | + // Settings_Manager::update() and so never reaches | |
| 311 | + // Module::update_settings(), where the gate lives. Without this a | |
| 312 | + // `wp xspeed settings update <pro-module>` turns a Pro feature on with | |
| 313 | + // no licence, exactly as the MCP handler did. (#185) | |
| 314 | + $module_object = \XSpeed\Module_Registry::get( $module ); | |
| 315 | + if ( $module_object && $module_object->is_license_locked() ) { | |
| 316 | + \XSpeed\Activity_Log::record( | |
| 317 | + 'license_write_refused', | |
| 318 | + sprintf( | |
| 319 | + /* translators: %s: module slug. */ | |
| 320 | + __( 'Refused a CLI settings write to the Pro module "%s" — no valid license.', 'xspeed' ), | |
| 321 | + $module | |
| 322 | + ), | |
| 323 | + \XSpeed\Activity_Log::WARN | |
| 324 | + ); | |
| 325 | + \WP_CLI::error( | |
| 326 | + sprintf( | |
| 327 | + '"%s" is a Pro module and this site has no active license, so the write was refused. Nothing was changed.', | |
| 328 | + $module | |
| 329 | + ) | |
| 330 | + ); | |
| 331 | + } | |
| 332 | + | |
| 333 | + // Refuse rather than report success over a write that won't happen. | |
| 334 | + // update() walks the schema, so an out-of-schema key is never written | |
| 335 | + // and never mentioned; an in-schema key with an invalid value is | |
| 336 | + // dropped back to the stored value just as quietly. Both used to exit | |
| 337 | + // 0 with "Success", which an agent — or a human script — cannot tell | |
| 338 | + // apart from a real write. (#206) | |
| 339 | + $report = Settings_Manager::inspect_input( $module, $values ); | |
| 340 | + if ( ! empty( $report['unknown'] ) || ! empty( $report['invalid'] ) ) { | |
| 341 | + $lines = array(); | |
| 342 | + foreach ( $report['unknown'] as $key ) { | |
| 343 | + $line = sprintf( ' %s — not a setting of module "%s"', $key, $module ); | |
| 344 | + $hint = Settings_Manager::hint_for_unknown_key( $key ); | |
| 345 | + if ( '' !== $hint ) { | |
| 346 | + $line .= "\n " . $hint; | |
| 347 | + } else { | |
| 348 | + $near = Settings_Manager::did_you_mean( $module, $key ); | |
| 349 | + if ( ! empty( $near ) ) { | |
| 350 | + $line .= "\n did you mean: " . implode( ', ', $near ) . '?'; | |
| 351 | + } | |
| 352 | + } | |
| 353 | + $lines[] = $line; | |
| 354 | + } | |
| 355 | + foreach ( $report['invalid'] as $key ) { | |
| 356 | + $lines[] = sprintf( ' %s — value rejected by the schema (wrong type, or outside the allowed range/options)', $key ); | |
| 357 | + } | |
| 358 | + | |
| 359 | + $applied = empty( $report['applied'] ) | |
| 360 | + ? 'Nothing was written.' | |
| 361 | + : sprintf( 'Nothing was written — the valid keys (%s) were not applied either, so the whole payload can be corrected and re-sent.', implode( ', ', $report['applied'] ) ); | |
| 362 | + | |
| 363 | + \WP_CLI::error( | |
| 364 | + sprintf( | |
| 365 | + "Refused to update %s:\n%s\n\n%s", | |
| 366 | + $module, | |
| 367 | + implode( "\n", $lines ), | |
| 368 | + $applied | |
| 304 | 369 | ) |
| 305 | 370 | ); |
| 306 | 371 | } |
| 307 | 372 | |