diff --git a/inc/Abilities/AgentPingAbilities.php b/inc/Abilities/AgentPingAbilities.php index efddb3a59..da9a6b55c 100644 --- a/inc/Abilities/AgentPingAbilities.php +++ b/inc/Abilities/AgentPingAbilities.php @@ -13,6 +13,7 @@ use DataMachine\Abilities\PermissionHelper; use DataMachine\Abilities\AgentPing\SendPingAbility; +use DataMachine\Abilities\Traits\HasCheckPermission; defined( 'ABSPATH' ) || exit; @@ -23,6 +24,7 @@ class AgentPingAbilities { private SendPingAbility $send_ping; public function __construct() { + add_action('wp_abilities_api_init', array( $this, 'abilities_api_init' )); if ( ! class_exists( 'WP_Ability' ) || self::$registered ) { return; } diff --git a/inc/Abilities/AgentTokenAbilities.php b/inc/Abilities/AgentTokenAbilities.php index de36844d9..aed2581d7 100644 --- a/inc/Abilities/AgentTokenAbilities.php +++ b/inc/Abilities/AgentTokenAbilities.php @@ -13,6 +13,7 @@ use DataMachine\Core\Database\Agents\Agents; use DataMachine\Core\Database\Agents\AgentTokens; +use DataMachine\Abilities\PermissionHelper; defined( 'ABSPATH' ) || exit; @@ -234,11 +235,11 @@ public function executeCreateToken( array $input ): array { 'info', 'Agent token created', array( - 'agent_id' => $agent_id, - 'agent_slug' => $agent['agent_slug'], - 'token_id' => $result['token_id'], - 'label' => $label, - 'has_expiry' => null !== $expires_at, + 'agent_id' => $agent_id, + 'agent_slug' => $agent['agent_slug'], + 'token_id' => $result['token_id'], + 'label' => $label, + 'has_expiry' => null !== $expires_at, 'has_cap_restriction' => null !== $capabilities, ) ); diff --git a/inc/Abilities/Analytics/BingWebmasterAbilities.php b/inc/Abilities/Analytics/BingWebmasterAbilities.php index c211fcf28..b0320427a 100644 --- a/inc/Abilities/Analytics/BingWebmasterAbilities.php +++ b/inc/Abilities/Analytics/BingWebmasterAbilities.php @@ -13,6 +13,8 @@ use DataMachine\Abilities\PermissionHelper; use DataMachine\Core\HttpClient; +use DataMachine\Abilities\Analytics\Traits\HasGetConfig; +use DataMachine\Abilities\Media\ImageGenerationAbilities; defined( 'ABSPATH' ) || exit; diff --git a/inc/Abilities/Analytics/GoogleAnalyticsAbilities.php b/inc/Abilities/Analytics/GoogleAnalyticsAbilities.php index a1244eb90..56a282d95 100644 --- a/inc/Abilities/Analytics/GoogleAnalyticsAbilities.php +++ b/inc/Abilities/Analytics/GoogleAnalyticsAbilities.php @@ -19,6 +19,7 @@ use DataMachine\Abilities\PermissionHelper; use DataMachine\Core\HttpClient; +use DataMachine\Abilities\Analytics\Traits\HasGetConfig; defined( 'ABSPATH' ) || exit; diff --git a/inc/Abilities/Analytics/GoogleSearchConsoleAbilities.php b/inc/Abilities/Analytics/GoogleSearchConsoleAbilities.php index 58372c9e7..e12e3dc88 100644 --- a/inc/Abilities/Analytics/GoogleSearchConsoleAbilities.php +++ b/inc/Abilities/Analytics/GoogleSearchConsoleAbilities.php @@ -13,6 +13,8 @@ use DataMachine\Abilities\PermissionHelper; use DataMachine\Core\HttpClient; +use DataMachine\Abilities\Analytics\GoogleAnalyticsAbilities; +use DataMachine\Abilities\Analytics\Traits\HasGetConfig; defined( 'ABSPATH' ) || exit; diff --git a/inc/Abilities/Analytics/PageSpeedAbilities.php b/inc/Abilities/Analytics/PageSpeedAbilities.php index 92f1419ca..fc89b970b 100644 --- a/inc/Abilities/Analytics/PageSpeedAbilities.php +++ b/inc/Abilities/Analytics/PageSpeedAbilities.php @@ -18,6 +18,8 @@ use DataMachine\Abilities\PermissionHelper; use DataMachine\Core\HttpClient; +use DataMachine\Abilities\Analytics\Traits\HasGetConfig; +use DataMachine\Engine\AI\Tools\Global\Traits\HasIsConfigured; defined( 'ABSPATH' ) || exit; diff --git a/inc/Abilities/AuthAbilities.php b/inc/Abilities/AuthAbilities.php index 38a67d1cb..899db1bcc 100644 --- a/inc/Abilities/AuthAbilities.php +++ b/inc/Abilities/AuthAbilities.php @@ -12,6 +12,8 @@ namespace DataMachine\Abilities; use DataMachine\Abilities\PermissionHelper; +use DataMachine\Abilities\Traits\HasCheckPermission; +use DataMachine\Core\NetworkSettings; defined( 'ABSPATH' ) || exit; diff --git a/inc/Abilities/ChatAbilities.php b/inc/Abilities/ChatAbilities.php index 82d47d785..70256791a 100644 --- a/inc/Abilities/ChatAbilities.php +++ b/inc/Abilities/ChatAbilities.php @@ -27,6 +27,7 @@ class ChatAbilities { private CreateChatSessionAbility $create_session; public function __construct() { + add_action('wp_abilities_api_init', array( $this, 'abilities_api_init' )); if ( ! class_exists( 'WP_Ability' ) || self::$registered ) { return; } diff --git a/inc/Abilities/Content/EditPostBlocksAbility.php b/inc/Abilities/Content/EditPostBlocksAbility.php index 953f6d7f4..3af507b34 100644 --- a/inc/Abilities/Content/EditPostBlocksAbility.php +++ b/inc/Abilities/Content/EditPostBlocksAbility.php @@ -46,32 +46,32 @@ private function registerAbility(): void { 'type' => 'integer', 'description' => __( 'Post ID to edit', 'data-machine' ), ), - 'edits' => array( - 'type' => 'array', - 'description' => __( 'Array of edit operations', 'data-machine' ), - 'items' => array( - 'type' => 'object', - 'required' => array( 'block_index', 'find', 'replace' ), - 'properties' => array( - 'block_index' => array( - 'type' => 'integer', - 'description' => __( 'Zero-based block index to edit', 'data-machine' ), - ), - 'find' => array( - 'type' => 'string', - 'description' => __( 'Text to find within the block', 'data-machine' ), - ), - 'replace' => array( - 'type' => 'string', - 'description' => __( 'Replacement text', 'data-machine' ), + 'edits' => array( + 'type' => 'array', + 'description' => __( 'Array of edit operations', 'data-machine' ), + 'items' => array( + 'type' => 'object', + 'required' => array( 'block_index', 'find', 'replace' ), + 'properties' => array( + 'block_index' => array( + 'type' => 'integer', + 'description' => __( 'Zero-based block index to edit', 'data-machine' ), + ), + 'find' => array( + 'type' => 'string', + 'description' => __( 'Text to find within the block', 'data-machine' ), + ), + 'replace' => array( + 'type' => 'string', + 'description' => __( 'Replacement text', 'data-machine' ), + ), ), ), ), - ), - 'preview' => array( - 'type' => 'boolean', - 'description' => __( 'When true, return diff preview without applying changes', 'data-machine' ), - ), + 'preview' => array( + 'type' => 'boolean', + 'description' => __( 'When true, return diff preview without applying changes', 'data-machine' ), + ), ), ), 'output_schema' => array( @@ -301,11 +301,11 @@ function ( $content ) use ( $find, $replace ) { ) ); return array( - 'success' => true, - 'preview' => true, - 'post_id' => $post_id, - 'diff_id' => $diff_id, - 'diff' => array( + 'success' => true, + 'preview' => true, + 'post_id' => $post_id, + 'diff_id' => $diff_id, + 'diff' => array( 'diffId' => $diff_id, 'diffType' => 'edit', 'originalContent' => implode( "\n", array_column( $diffs, 'originalContent' ) ), diff --git a/inc/Abilities/Content/ReplacePostBlocksAbility.php b/inc/Abilities/Content/ReplacePostBlocksAbility.php index 5772e8cab..e4de491c2 100644 --- a/inc/Abilities/Content/ReplacePostBlocksAbility.php +++ b/inc/Abilities/Content/ReplacePostBlocksAbility.php @@ -46,27 +46,27 @@ private function registerAbility(): void { 'description' => __( 'Post ID to edit', 'data-machine' ), ), 'replacements' => array( - 'type' => 'array', - 'description' => __( 'Array of block replacement operations', 'data-machine' ), - 'items' => array( - 'type' => 'object', - 'required' => array( 'block_index', 'new_content' ), - 'properties' => array( - 'block_index' => array( - 'type' => 'integer', - 'description' => __( 'Zero-based block index to replace', 'data-machine' ), - ), - 'new_content' => array( - 'type' => 'string', - 'description' => __( 'New innerHTML for the block', 'data-machine' ), + 'type' => 'array', + 'description' => __( 'Array of block replacement operations', 'data-machine' ), + 'items' => array( + 'type' => 'object', + 'required' => array( 'block_index', 'new_content' ), + 'properties' => array( + 'block_index' => array( + 'type' => 'integer', + 'description' => __( 'Zero-based block index to replace', 'data-machine' ), + ), + 'new_content' => array( + 'type' => 'string', + 'description' => __( 'New innerHTML for the block', 'data-machine' ), + ), ), ), ), - ), - 'preview' => array( - 'type' => 'boolean', - 'description' => __( 'When true, return diff preview without applying changes', 'data-machine' ), - ), + 'preview' => array( + 'type' => 'boolean', + 'description' => __( 'When true, return diff preview without applying changes', 'data-machine' ), + ), ), ), 'output_schema' => array( @@ -288,11 +288,11 @@ public static function execute( array $input ): array { }, $changes ); return array( - 'success' => true, - 'preview' => true, - 'post_id' => $post_id, - 'diff_id' => $diff_id, - 'diff' => array( + 'success' => true, + 'preview' => true, + 'post_id' => $post_id, + 'diff_id' => $diff_id, + 'diff' => array( 'diffId' => $diff_id, 'diffType' => 'replace', 'originalContent' => implode( "\n", array_column( $diffs, 'originalContent' ) ), diff --git a/inc/Abilities/Email/EmailAbilities.php b/inc/Abilities/Email/EmailAbilities.php index 3cb952d89..5c954a427 100644 --- a/inc/Abilities/Email/EmailAbilities.php +++ b/inc/Abilities/Email/EmailAbilities.php @@ -47,28 +47,28 @@ private function registerAbilities(): void { 'type' => 'object', 'required' => array( 'to', 'subject', 'body', 'in_reply_to' ), 'properties' => array( - 'to' => array( + 'to' => array( 'type' => 'string', 'description' => __( 'Recipient email address', 'data-machine' ), ), - 'subject' => array( + 'subject' => array( 'type' => 'string', 'description' => __( 'Reply subject (typically Re: original subject)', 'data-machine' ), ), - 'body' => array( + 'body' => array( 'type' => 'string', 'description' => __( 'Reply body content', 'data-machine' ), ), - 'in_reply_to' => array( + 'in_reply_to' => array( 'type' => 'string', 'description' => __( 'Message-ID of the email being replied to', 'data-machine' ), ), - 'references' => array( + 'references' => array( 'type' => 'string', 'default' => '', 'description' => __( 'References header chain for threading', 'data-machine' ), ), - 'cc' => array( + 'cc' => array( 'type' => 'string', 'default' => '', ), @@ -187,8 +187,8 @@ private function registerAbilities(): void { 'description' => __( 'IMAP flag: Seen, Flagged, Answered, Deleted, Draft', 'data-machine' ), ), 'action' => array( - 'type' => 'string', - 'default' => 'set', + 'type' => 'string', + 'default' => 'set', 'description' => __( 'set or clear the flag', 'data-machine' ), ), 'folder' => array( @@ -277,8 +277,8 @@ private function registerAbilities(): void { 'description' => __( 'Flag: Seen, Flagged, Answered, Deleted, Draft', 'data-machine' ), ), 'action' => array( - 'type' => 'string', - 'default' => 'set', + 'type' => 'string', + 'default' => 'set', 'description' => __( 'set or clear', 'data-machine' ), ), 'folder' => array( @@ -294,11 +294,11 @@ private function registerAbilities(): void { 'output_schema' => array( 'type' => 'object', 'properties' => array( - 'success' => array( 'type' => 'boolean' ), - 'message' => array( 'type' => 'string' ), - 'flagged_count' => array( 'type' => 'integer' ), - 'total_matches' => array( 'type' => 'integer' ), - 'error' => array( 'type' => 'string' ), + 'success' => array( 'type' => 'boolean' ), + 'message' => array( 'type' => 'string' ), + 'flagged_count' => array( 'type' => 'integer' ), + 'total_matches' => array( 'type' => 'integer' ), + 'error' => array( 'type' => 'string' ), ), ), 'execute_callback' => array( $this, 'executeBatchFlag' ), @@ -336,11 +336,11 @@ private function registerAbilities(): void { 'output_schema' => array( 'type' => 'object', 'properties' => array( - 'success' => array( 'type' => 'boolean' ), - 'message' => array( 'type' => 'string' ), - 'deleted_count' => array( 'type' => 'integer' ), - 'total_matches' => array( 'type' => 'integer' ), - 'error' => array( 'type' => 'string' ), + 'success' => array( 'type' => 'boolean' ), + 'message' => array( 'type' => 'string' ), + 'deleted_count' => array( 'type' => 'integer' ), + 'total_matches' => array( 'type' => 'integer' ), + 'error' => array( 'type' => 'string' ), ), ), 'execute_callback' => array( $this, 'executeBatchDelete' ), @@ -414,12 +414,12 @@ private function registerAbilities(): void { 'output_schema' => array( 'type' => 'object', 'properties' => array( - 'success' => array( 'type' => 'boolean' ), - 'message' => array( 'type' => 'string' ), - 'results' => array( 'type' => 'array' ), + 'success' => array( 'type' => 'boolean' ), + 'message' => array( 'type' => 'string' ), + 'results' => array( 'type' => 'array' ), 'unsubscribed' => array( 'type' => 'integer' ), - 'failed' => array( 'type' => 'integer' ), - 'no_header' => array( 'type' => 'integer' ), + 'failed' => array( 'type' => 'integer' ), + 'no_header' => array( 'type' => 'integer' ), ), ), 'execute_callback' => array( $this, 'executeBatchUnsubscribe' ), @@ -442,10 +442,10 @@ private function registerAbilities(): void { 'output_schema' => array( 'type' => 'object', 'properties' => array( - 'success' => array( 'type' => 'boolean' ), - 'message' => array( 'type' => 'string' ), - 'mailbox_info' => array( 'type' => 'object' ), - 'error' => array( 'type' => 'string' ), + 'success' => array( 'type' => 'boolean' ), + 'message' => array( 'type' => 'string' ), + 'mailbox_info' => array( 'type' => 'object' ), + 'error' => array( 'type' => 'string' ), ), ), 'execute_callback' => array( $this, 'executeTestConnection' ), @@ -520,7 +520,7 @@ public function executeReply( array $input ): array { global $phpmailer; $error = 'wp_mail() returned false'; if ( isset( $phpmailer ) && $phpmailer instanceof \PHPMailer\PHPMailer\PHPMailer ) { - $error = $phpmailer->ErrorInfo ?: $error; + $error = $phpmailer->ErrorInfo ? $phpmailer->ErrorInfo : $error; } return array( @@ -619,7 +619,7 @@ public function executeFlag( array $input ): array { return array( 'success' => true, - 'message' => sprintf( 'Flag %s %s on UID %d', $flag, $action === 'clear' ? 'cleared' : 'set', $uid ), + 'message' => sprintf( 'Flag %s %s on UID %d', $flag, 'clear' === $action ? 'cleared' : 'set', $uid ), ); } @@ -627,6 +627,7 @@ public function executeFlag( array $input ): array { * Test the IMAP connection with stored credentials. */ public function executeTestConnection( array $input ): array { + unset( $input ); if ( ! function_exists( 'imap_open' ) ) { return array( 'success' => false, @@ -807,7 +808,7 @@ public function executeBatchUnsubscribe( array $input ): array { continue; } - $from = $header->from[0]; + $from = $header->from[0]; $sender = $from->mailbox . '@' . $from->host; if ( isset( $seen_senders[ $sender ] ) ) { @@ -817,7 +818,7 @@ public function executeBatchUnsubscribe( array $input ): array { $seen_senders[ $sender ] = true; // Fetch unsubscribe headers. - $raw = imap_fetchheader( $connection, $uid, FT_UID ); + $raw = imap_fetchheader( $connection, $uid, FT_UID ); $parsed = $this->parseUnsubscribeHeaders( $raw ); $to_process[] = array( @@ -1195,10 +1196,10 @@ public function executeBatchDelete( array $input ): array { if ( false === $uids || empty( $uids ) ) { imap_close( $connection ); return array( - 'success' => true, - 'message' => 'No messages matching search criteria', - 'deleted_count' => 0, - 'total_matches' => 0, + 'success' => true, + 'message' => 'No messages matching search criteria', + 'deleted_count' => 0, + 'total_matches' => 0, ); } @@ -1216,10 +1217,10 @@ public function executeBatchDelete( array $input ): array { } return array( - 'success' => true, - 'message' => $message, - 'deleted_count' => count( $to_delete ), - 'total_matches' => $total, + 'success' => true, + 'message' => $message, + 'deleted_count' => count( $to_delete ), + 'total_matches' => $total, ); } diff --git a/inc/Abilities/Engine/ExecuteStepAbility.php b/inc/Abilities/Engine/ExecuteStepAbility.php index 37767fd0d..b1112e28c 100644 --- a/inc/Abilities/Engine/ExecuteStepAbility.php +++ b/inc/Abilities/Engine/ExecuteStepAbility.php @@ -681,7 +681,7 @@ private function markCompletedItemProcessed( int $job_id ): void { 'Deferred mark-as-processed on pipeline completion', array( 'job_id' => $job_id, - 'item_identifier' => $item_identifier, + 'item_identifier' => $item_identifier, 'source_type' => $source_type, 'fetch_flow_step_id' => $fetch_flow_step_id, ) diff --git a/inc/Abilities/Engine/PipelineBatchScheduler.php b/inc/Abilities/Engine/PipelineBatchScheduler.php index 34103a103..eafe2ec40 100644 --- a/inc/Abilities/Engine/PipelineBatchScheduler.php +++ b/inc/Abilities/Engine/PipelineBatchScheduler.php @@ -318,8 +318,8 @@ private function createChildJob( // its last step, ExecuteStepAbility reads these to mark the item as // processed. Previously set by FetchHandler::onItemProcessed() on the // parent, but now the fetch step no longer marks items eagerly. - $item_identifier = $single_packet['metadata']['item_identifier'] ?? null; - $source_type = $single_packet['metadata']['source_type'] ?? null; + $item_identifier = $single_packet['metadata']['item_identifier'] ?? null; + $source_type = $single_packet['metadata']['source_type'] ?? null; if ( ! empty( $item_identifier ) ) { $child_engine['item_identifier'] = $item_identifier; } diff --git a/inc/Abilities/EngineAbilities.php b/inc/Abilities/EngineAbilities.php index 5bd39c997..db004fc82 100644 --- a/inc/Abilities/EngineAbilities.php +++ b/inc/Abilities/EngineAbilities.php @@ -29,6 +29,7 @@ class EngineAbilities { private ScheduleFlowAbility $schedule_flow; public function __construct() { + add_action('wp_abilities_api_init', array( $this, 'abilities_api_init' )); if ( ! class_exists( 'WP_Ability' ) || self::$registered ) { return; } diff --git a/inc/Abilities/Fetch/FetchEmailAbility.php b/inc/Abilities/Fetch/FetchEmailAbility.php index 800e90b23..d3d0fdc05 100644 --- a/inc/Abilities/Fetch/FetchEmailAbility.php +++ b/inc/Abilities/Fetch/FetchEmailAbility.php @@ -19,6 +19,7 @@ namespace DataMachine\Abilities\Fetch; use DataMachine\Abilities\PermissionHelper; +use DataMachine\Abilities\Traits\HasCheckPermission; defined( 'ABSPATH' ) || exit; @@ -118,8 +119,8 @@ private function registerAbilities(): void { 'output_schema' => array( 'type' => 'object', 'properties' => array( - 'success' => array( 'type' => 'boolean' ), - 'data' => array( + 'success' => array( 'type' => 'boolean' ), + 'data' => array( 'type' => 'object', 'properties' => array( 'items' => array( 'type' => 'array' ), @@ -129,8 +130,8 @@ private function registerAbilities(): void { 'has_more' => array( 'type' => 'boolean' ), ), ), - 'error' => array( 'type' => 'string' ), - 'logs' => array( 'type' => 'array' ), + 'error' => array( 'type' => 'string' ), + 'logs' => array( 'type' => 'array' ), ), ), 'execute_callback' => array( $this, 'execute' ), @@ -225,7 +226,7 @@ public function execute( array $input ): array { 'offset' => 0, 'has_more' => false, ), - 'logs' => $logs, + 'logs' => $logs, ); } @@ -243,7 +244,7 @@ public function execute( array $input ): array { 'offset' => 0, 'has_more' => false, ), - 'logs' => $logs, + 'logs' => $logs, ); } @@ -312,7 +313,7 @@ public function execute( array $input ): array { 'offset' => $offset, 'has_more' => $has_more, ), - 'logs' => $logs, + 'logs' => $logs, ); } @@ -365,7 +366,7 @@ private function fetchHeaders( $connection, int $uid ): ?array { 'metadata' => array( 'uid' => $uid, 'message_id' => $message_id, - 'item_identifier' => $message_id, + 'item_identifier' => $message_id, 'from' => $from_email, 'from_name' => $from_name, 'to' => $to_address, @@ -439,7 +440,7 @@ private function fetchMessage( $connection, int $uid, array $config ): ?array { 'metadata' => array( 'uid' => $uid, 'message_id' => $message_id, - 'item_identifier' => $message_id, + 'item_identifier' => $message_id, 'from' => $from_email, 'from_name' => $from_name, 'to' => $to_address, @@ -536,7 +537,7 @@ private function fetchBody( $connection, int $uid ): string { private function decodeBody( string $body, int $encoding ): string { switch ( $encoding ) { case 3: // BASE64. - return base64_decode( $body, true ) ?: $body; + return base64_decode( $body, true ) ? base64_decode( $body, true ) : $body; case 4: // QUOTED-PRINTABLE. return quoted_printable_decode( $body ); case 1: // 8BIT. diff --git a/inc/Abilities/Fetch/FetchFilesAbility.php b/inc/Abilities/Fetch/FetchFilesAbility.php index d947f4cd5..f78cf4981 100644 --- a/inc/Abilities/Fetch/FetchFilesAbility.php +++ b/inc/Abilities/Fetch/FetchFilesAbility.php @@ -12,6 +12,7 @@ namespace DataMachine\Abilities\Fetch; use DataMachine\Abilities\PermissionHelper; +use DataMachine\Abilities\Traits\HasCheckPermission; defined( 'ABSPATH' ) || exit; @@ -172,12 +173,12 @@ public function execute( array $input ): array { ); $metadata = array( - 'source_type' => 'files', - 'item_identifier' => $file_identifier, - 'original_id' => $file_identifier, - 'original_title' => $file['original_name'], - 'original_date_gmt' => $file['uploaded_at'] ?? gmdate( 'Y-m-d H:i:s' ), - 'source_url' => '', + 'source_type' => 'files', + 'item_identifier' => $file_identifier, + 'original_id' => $file_identifier, + 'original_title' => $file['original_name'], + 'original_date_gmt' => $file['uploaded_at'] ?? gmdate( 'Y-m-d H:i:s' ), + 'source_url' => '', ); if ( $is_image ) { diff --git a/inc/Abilities/Fetch/FetchRssAbility.php b/inc/Abilities/Fetch/FetchRssAbility.php index 13c51f895..dec80cb62 100644 --- a/inc/Abilities/Fetch/FetchRssAbility.php +++ b/inc/Abilities/Fetch/FetchRssAbility.php @@ -11,6 +11,8 @@ namespace DataMachine\Abilities\Fetch; use DataMachine\Abilities\PermissionHelper; +use DataMachine\Abilities\Fetch\Traits\HasApplyKeywordSearch; +use DataMachine\Abilities\Traits\HasCheckPermission; defined( 'ABSPATH' ) || exit; @@ -254,15 +256,15 @@ function ( $error ) { $enclosure_url = $this->extractItemEnclosure( $item ); $metadata = array( - 'source_type' => 'rss', - 'item_identifier' => $guid, - 'original_id' => $guid, - 'original_title' => $title, - 'original_date_gmt' => $pub_date ? gmdate( 'Y-m-d\TH:i:s\Z', strtotime( $pub_date ) ) : null, - 'author' => $author, - 'categories' => $categories, - 'guid' => $guid, - 'source_url' => $link ? $link : '', + 'source_type' => 'rss', + 'item_identifier' => $guid, + 'original_id' => $guid, + 'original_title' => $title, + 'original_date_gmt' => $pub_date ? gmdate( 'Y-m-d\TH:i:s\Z', strtotime( $pub_date ) ) : null, + 'author' => $author, + 'categories' => $categories, + 'guid' => $guid, + 'source_url' => $link ? $link : '', ); $file_info = null; diff --git a/inc/Abilities/Fetch/FetchWordPressApiAbility.php b/inc/Abilities/Fetch/FetchWordPressApiAbility.php index 4feae750f..f1dc25d0a 100644 --- a/inc/Abilities/Fetch/FetchWordPressApiAbility.php +++ b/inc/Abilities/Fetch/FetchWordPressApiAbility.php @@ -12,6 +12,10 @@ namespace DataMachine\Abilities\Fetch; use DataMachine\Abilities\PermissionHelper; +use DataMachine\Abilities\Fetch\Traits\HasApplyKeywordSearch; +use DataMachine\Abilities\Fetch\FetchRssAbility; +use DataMachine\Abilities\Traits\HasCheckPermission; +use DataMachine\Abilities\Fetch\FetchRssAbility; defined( 'ABSPATH' ) || exit; @@ -280,13 +284,13 @@ public function execute( array $input ): array { 'title' => $title, 'content' => wp_strip_all_tags( $content ), 'metadata' => array( - 'source_type' => 'rest_api', - 'item_identifier' => $unique_id, - 'original_id' => $item_id, - 'original_title' => $title, - 'original_date_gmt' => $item_date, - 'site_name' => $site_name, - 'source_url' => $source_link, + 'source_type' => 'rest_api', + 'item_identifier' => $unique_id, + 'original_id' => $item_id, + 'original_title' => $title, + 'original_date_gmt' => $item_date, + 'site_name' => $site_name, + 'source_url' => $source_link, ), ); diff --git a/inc/Abilities/Fetch/FetchWordPressMediaAbility.php b/inc/Abilities/Fetch/FetchWordPressMediaAbility.php index bc430fddd..922cfdd5a 100644 --- a/inc/Abilities/Fetch/FetchWordPressMediaAbility.php +++ b/inc/Abilities/Fetch/FetchWordPressMediaAbility.php @@ -11,6 +11,8 @@ namespace DataMachine\Abilities\Fetch; use DataMachine\Abilities\PermissionHelper; +use DataMachine\Abilities\Fetch\Traits\HasApplyKeywordSearch; +use DataMachine\Abilities\Traits\HasCheckPermission; defined( 'ABSPATH' ) || exit; @@ -262,18 +264,18 @@ public function execute( array $input ): array { 'title' => $content_data['title'] ?? '', 'content' => $content_data['content'] ?? '', 'metadata' => array( - 'source_type' => 'wordpress_media', - 'item_identifier' => $post->ID, - 'original_id' => $post->ID, - 'parent_post_id' => $post->post_parent, - 'original_title' => $title, - 'original_date_gmt' => $post->post_date_gmt, - 'mime_type' => $file_type, - 'file_size' => $file_size, - 'site_name' => $site_name, - 'source_url' => $source_url, - 'image_file_path' => strpos( $file_type, 'video/' ) !== 0 ? $file_path : '', - '_engine_data' => $engine_data, + 'source_type' => 'wordpress_media', + 'item_identifier' => $post->ID, + 'original_id' => $post->ID, + 'parent_post_id' => $post->post_parent, + 'original_title' => $title, + 'original_date_gmt' => $post->post_date_gmt, + 'mime_type' => $file_type, + 'file_size' => $file_size, + 'site_name' => $site_name, + 'source_url' => $source_url, + 'image_file_path' => strpos( $file_type, 'video/' ) !== 0 ? $file_path : '', + '_engine_data' => $engine_data, ), 'file_info' => $file_info, ); diff --git a/inc/Abilities/Fetch/GetWordPressPostAbility.php b/inc/Abilities/Fetch/GetWordPressPostAbility.php index 85f4474a9..268d9b143 100644 --- a/inc/Abilities/Fetch/GetWordPressPostAbility.php +++ b/inc/Abilities/Fetch/GetWordPressPostAbility.php @@ -11,6 +11,7 @@ namespace DataMachine\Abilities\Fetch; use DataMachine\Abilities\PermissionHelper; +use DataMachine\Abilities\Traits\HasCheckPermission; defined( 'ABSPATH' ) || exit; diff --git a/inc/Abilities/Fetch/QueryWordPressPostsAbility.php b/inc/Abilities/Fetch/QueryWordPressPostsAbility.php index 5e2cb6deb..e8a8a14a1 100644 --- a/inc/Abilities/Fetch/QueryWordPressPostsAbility.php +++ b/inc/Abilities/Fetch/QueryWordPressPostsAbility.php @@ -11,6 +11,8 @@ namespace DataMachine\Abilities\Fetch; use DataMachine\Abilities\PermissionHelper; +use DataMachine\Abilities\Fetch\Traits\HasApplyKeywordSearch; +use DataMachine\Abilities\Traits\HasCheckPermission; defined( 'ABSPATH' ) || exit; @@ -273,17 +275,17 @@ public function execute( array $input ): array { 'title' => $title, 'content' => $content, 'metadata' => array( - 'source_type' => 'wordpress_local', - 'item_identifier' => $post_id, - 'original_id' => $post_id, - 'original_title' => $title, - 'original_date_gmt' => $post->post_date_gmt, - 'post_type' => $post->post_type, - 'post_status' => $post->post_status, - 'site_name' => $site_name, - 'permalink' => get_permalink( $post_id ) ?? '', - 'excerpt' => $post->post_excerpt, - 'author' => get_the_author_meta( 'display_name', (int) $post->post_author ), + 'source_type' => 'wordpress_local', + 'item_identifier' => $post_id, + 'original_id' => $post_id, + 'original_title' => $title, + 'original_date_gmt' => $post->post_date_gmt, + 'post_type' => $post->post_type, + 'post_status' => $post->post_status, + 'site_name' => $site_name, + 'permalink' => get_permalink( $post_id ) ?? '', + 'excerpt' => $post->post_excerpt, + 'author' => get_the_author_meta( 'display_name', (int) $post->post_author ), ), ); diff --git a/inc/Abilities/File/AgentFileAbilities.php b/inc/Abilities/File/AgentFileAbilities.php index 135dc5c20..ac3511323 100644 --- a/inc/Abilities/File/AgentFileAbilities.php +++ b/inc/Abilities/File/AgentFileAbilities.php @@ -21,6 +21,8 @@ use DataMachine\Core\FilesRepository\DirectoryManager; use DataMachine\Core\FilesRepository\FilesystemHelper; use DataMachine\Engine\AI\MemoryFileRegistry; +use DataMachine\Abilities\Traits\HasCheckPermission; +use DataMachine\Abilities\File\FlowFileAbilities; defined( 'ABSPATH' ) || exit; @@ -348,23 +350,23 @@ public function executeListAgentFiles( array $input ): array { 'agent_id' => (int) ( $input['agent_id'] ?? 0 ), 'user_id' => $user_id, ); - $contexts_dir = $dm->get_contexts_directory( $agent_context ); + $contexts_dir = $dm->get_contexts_directory( $agent_context ); if ( is_dir( $contexts_dir ) ) { foreach ( glob( trailingslashit( $contexts_dir ) . '*.md' ) as $filepath ) { $filename = basename( $filepath ); $slug = pathinfo( $filename, PATHINFO_FILENAME ); $files[] = array( - 'filename' => $filename, - 'size' => filesize( $filepath ), - 'modified' => gmdate( 'c', filemtime( $filepath ) ), - 'type' => 'context', - 'layer' => 'context', - 'protected' => false, - 'editable' => true, - 'registered' => false, - 'label' => ucfirst( $slug ) . ' Context', - 'description' => "Context-scoped instructions loaded when execution context is '{$slug}'.", + 'filename' => $filename, + 'size' => filesize( $filepath ), + 'modified' => gmdate( 'c', filemtime( $filepath ) ), + 'type' => 'context', + 'layer' => 'context', + 'protected' => false, + 'editable' => true, + 'registered' => false, + 'label' => ucfirst( $slug ) . ' Context', + 'description' => "Context-scoped instructions loaded when execution context is '{$slug}'.", 'context_slug' => $slug, ); } diff --git a/inc/Abilities/File/FlowFileAbilities.php b/inc/Abilities/File/FlowFileAbilities.php index 9f48c4082..a082c782c 100644 --- a/inc/Abilities/File/FlowFileAbilities.php +++ b/inc/Abilities/File/FlowFileAbilities.php @@ -15,6 +15,7 @@ use DataMachine\Core\Database\Flows\Flows; use DataMachine\Core\FilesRepository\FileCleanup; use DataMachine\Core\FilesRepository\FileStorage; +use DataMachine\Abilities\Traits\HasCheckPermission; defined( 'ABSPATH' ) || exit; diff --git a/inc/Abilities/Flow/FlowHelpers.php b/inc/Abilities/Flow/FlowHelpers.php index b06fa871a..47116f10d 100644 --- a/inc/Abilities/Flow/FlowHelpers.php +++ b/inc/Abilities/Flow/FlowHelpers.php @@ -19,6 +19,7 @@ use DataMachine\Core\Database\Flows\Flows; use DataMachine\Core\Database\Jobs\Jobs; use DataMachine\Core\Database\Pipelines\Pipelines; +use DataMachine\Abilities\Traits\HasCheckPermission; defined( 'ABSPATH' ) || exit; diff --git a/inc/Abilities/Flow/PauseFlowAbility.php b/inc/Abilities/Flow/PauseFlowAbility.php index 577ad7149..aeac6d8ba 100644 --- a/inc/Abilities/Flow/PauseFlowAbility.php +++ b/inc/Abilities/Flow/PauseFlowAbility.php @@ -116,8 +116,8 @@ public function execute( array $input ): array { $details = array(); foreach ( $flows as $flow ) { - $fid = (int) $flow['flow_id']; - $scheduling = $flow['scheduling_config'] ?? array(); + $fid = (int) $flow['flow_id']; + $scheduling = $flow['scheduling_config'] ?? array(); // Already paused — skip. if ( isset( $scheduling['enabled'] ) && false === $scheduling['enabled'] ) { diff --git a/inc/Abilities/Flow/ResumeFlowAbility.php b/inc/Abilities/Flow/ResumeFlowAbility.php index b04cfd72a..9905b4cb8 100644 --- a/inc/Abilities/Flow/ResumeFlowAbility.php +++ b/inc/Abilities/Flow/ResumeFlowAbility.php @@ -23,6 +23,7 @@ class ResumeFlowAbility { use FlowHelpers; + use DataMachine\Abilities\Flow\PauseFlowAbility; public function __construct() { $this->initDatabases(); diff --git a/inc/Abilities/FlowAbilities.php b/inc/Abilities/FlowAbilities.php index 87fc7df4e..cb68da1a3 100644 --- a/inc/Abilities/FlowAbilities.php +++ b/inc/Abilities/FlowAbilities.php @@ -22,6 +22,7 @@ use DataMachine\Abilities\Flow\ResumeFlowAbility; use DataMachine\Abilities\Flow\QueueAbility; use DataMachine\Abilities\Flow\WebhookTriggerAbility; +use DataMachine\Abilities\Traits\HasCheckPermission; defined( 'ABSPATH' ) || exit; @@ -40,6 +41,7 @@ class FlowAbilities { private WebhookTriggerAbility $webhook_trigger; public function __construct() { + add_action('wp_abilities_api_init', array( $this, 'abilities_api_init' )); if ( ! class_exists( 'WP_Ability' ) || self::$registered ) { return; } diff --git a/inc/Abilities/FlowStep/FlowStepHelpers.php b/inc/Abilities/FlowStep/FlowStepHelpers.php index 8fe218c07..1327241a5 100644 --- a/inc/Abilities/FlowStep/FlowStepHelpers.php +++ b/inc/Abilities/FlowStep/FlowStepHelpers.php @@ -17,6 +17,7 @@ use DataMachine\Core\Database\Flows\Flows; use DataMachine\Core\Database\Pipelines\Pipelines; use DataMachine\Core\Steps\FlowStepConfig; +use DataMachine\Abilities\Traits\HasCheckPermission; defined( 'ABSPATH' ) || exit; diff --git a/inc/Abilities/FlowStepAbilities.php b/inc/Abilities/FlowStepAbilities.php index 6b6a3e487..6794bcaf8 100644 --- a/inc/Abilities/FlowStepAbilities.php +++ b/inc/Abilities/FlowStepAbilities.php @@ -17,6 +17,7 @@ use DataMachine\Abilities\FlowStep\UpdateFlowStepAbility; use DataMachine\Abilities\FlowStep\ConfigureFlowStepsAbility; use DataMachine\Abilities\FlowStep\ValidateFlowStepsConfigAbility; +use DataMachine\Abilities\Traits\HasCheckPermission; defined( 'ABSPATH' ) || exit; @@ -30,6 +31,7 @@ class FlowStepAbilities { private ValidateFlowStepsConfigAbility $validate_flow_steps_config; public function __construct() { + add_action('wp_abilities_api_init', array( $this, 'abilities_api_init' )); if ( ! class_exists( 'WP_Ability' ) || self::$registered ) { return; } diff --git a/inc/Abilities/Handler/TestHandlerAbility.php b/inc/Abilities/Handler/TestHandlerAbility.php index 97b9a588f..a9e2e7254 100644 --- a/inc/Abilities/Handler/TestHandlerAbility.php +++ b/inc/Abilities/Handler/TestHandlerAbility.php @@ -68,15 +68,15 @@ private function registerAbility(): void { 'output_schema' => array( 'type' => 'object', 'properties' => array( - 'success' => array( 'type' => 'boolean' ), - 'handler_slug' => array( 'type' => 'string' ), - 'handler_label' => array( 'type' => 'string' ), - 'config_used' => array( 'type' => 'object' ), - 'packets' => array( 'type' => 'array' ), - 'packet_count' => array( 'type' => 'integer' ), - 'warnings' => array( 'type' => 'array' ), - 'execution_time_ms' => array( 'type' => 'number' ), - 'error' => array( 'type' => 'string' ), + 'success' => array( 'type' => 'boolean' ), + 'handler_slug' => array( 'type' => 'string' ), + 'handler_label' => array( 'type' => 'string' ), + 'config_used' => array( 'type' => 'object' ), + 'packets' => array( 'type' => 'array' ), + 'packet_count' => array( 'type' => 'integer' ), + 'warnings' => array( 'type' => 'array' ), + 'execution_time_ms' => array( 'type' => 'number' ), + 'error' => array( 'type' => 'string' ), ), ), 'execute_callback' => array( $this, 'execute' ), diff --git a/inc/Abilities/HandlerAbilities.php b/inc/Abilities/HandlerAbilities.php index 14e895b97..0ecf53784 100644 --- a/inc/Abilities/HandlerAbilities.php +++ b/inc/Abilities/HandlerAbilities.php @@ -12,6 +12,7 @@ namespace DataMachine\Abilities; use DataMachine\Abilities\PermissionHelper; +use DataMachine\Abilities\Traits\HasCheckPermission; defined( 'ABSPATH' ) || exit; diff --git a/inc/Abilities/InternalLinkingAbilities.php b/inc/Abilities/InternalLinkingAbilities.php index 11d819440..b210ea942 100644 --- a/inc/Abilities/InternalLinkingAbilities.php +++ b/inc/Abilities/InternalLinkingAbilities.php @@ -344,8 +344,6 @@ private function registerAbilities(): void { 'meta' => array( 'show_in_rest' => true ), ) ); - - }; if ( doing_action( 'wp_abilities_api_init' ) ) { @@ -1018,7 +1016,7 @@ public static function getLinkOpportunities( array $input = array() ): array { 'error' => "Category '{$category}' not found.", ); } - $cat_posts = get_posts( + $cat_posts = get_posts( array( 'post_type' => 'post', 'post_status' => 'publish', @@ -1059,8 +1057,8 @@ public static function getLinkOpportunities( array $input = array() ): array { } $traffic_by_post[ $post_id ]['clicks'] += (int) $clicks; - $traffic_by_post[ $post_id ]['impressions'] += (int) ( $row['impressions'] ?? 0 ); - $traffic_by_post[ $post_id ]['position'] += (float) ( $row['position'] ?? 0 ); + $traffic_by_post[ $post_id ]['impressions'] += (int) ( $row['impressions'] ?? 0 ); + $traffic_by_post[ $post_id ]['position'] += (float) ( $row['position'] ?? 0 ); ++$traffic_by_post[ $post_id ]['row_count']; } diff --git a/inc/Abilities/Job/JobHelpers.php b/inc/Abilities/Job/JobHelpers.php index 0ae90102a..8740273d2 100644 --- a/inc/Abilities/Job/JobHelpers.php +++ b/inc/Abilities/Job/JobHelpers.php @@ -18,6 +18,7 @@ use DataMachine\Core\Database\Jobs\Jobs; use DataMachine\Core\Database\Pipelines\Pipelines; use DataMachine\Core\Database\ProcessedItems\ProcessedItems; +use DataMachine\Abilities\Traits\HasCheckPermission; defined( 'ABSPATH' ) || exit; diff --git a/inc/Abilities/JobAbilities.php b/inc/Abilities/JobAbilities.php index 0d03b6326..21b62c94d 100644 --- a/inc/Abilities/JobAbilities.php +++ b/inc/Abilities/JobAbilities.php @@ -22,6 +22,7 @@ use DataMachine\Abilities\Job\JobsSummaryAbility; use DataMachine\Abilities\Job\FailJobAbility; use DataMachine\Abilities\Job\RetryJobAbility; +use DataMachine\Abilities\Traits\HasCheckPermission; defined( 'ABSPATH' ) || exit; @@ -40,6 +41,7 @@ class JobAbilities { private RetryJobAbility $retry_job; public function __construct() { + add_action('wp_abilities_api_init', array( $this, 'abilities_api_init' )); if ( ! class_exists( 'WP_Ability' ) || self::$registered ) { return; } diff --git a/inc/Abilities/Media/ImageGenerationAbilities.php b/inc/Abilities/Media/ImageGenerationAbilities.php index e372a9a45..3062e7789 100644 --- a/inc/Abilities/Media/ImageGenerationAbilities.php +++ b/inc/Abilities/Media/ImageGenerationAbilities.php @@ -20,6 +20,7 @@ use DataMachine\Core\PluginSettings; use DataMachine\Engine\AI\RequestBuilder; use DataMachine\Engine\Tasks\TaskScheduler; +use DataMachine\Abilities\Analytics\Traits\HasGetConfig; defined( 'ABSPATH' ) || exit; diff --git a/inc/Abilities/Pipeline/PipelineHelpers.php b/inc/Abilities/Pipeline/PipelineHelpers.php index 7add46936..6085b6c00 100644 --- a/inc/Abilities/Pipeline/PipelineHelpers.php +++ b/inc/Abilities/Pipeline/PipelineHelpers.php @@ -18,6 +18,7 @@ use DataMachine\Core\Admin\DateFormatter; use DataMachine\Core\Database\Flows\Flows; use DataMachine\Core\Database\Pipelines\Pipelines; +use DataMachine\Abilities\Traits\HasCheckPermission; defined( 'ABSPATH' ) || exit; diff --git a/inc/Abilities/PipelineAbilities.php b/inc/Abilities/PipelineAbilities.php index 52a2df19f..6ad80f5be 100644 --- a/inc/Abilities/PipelineAbilities.php +++ b/inc/Abilities/PipelineAbilities.php @@ -19,6 +19,7 @@ use DataMachine\Abilities\Pipeline\DeletePipelineAbility; use DataMachine\Abilities\Pipeline\DuplicatePipelineAbility; use DataMachine\Abilities\Pipeline\ImportExportAbility; +use DataMachine\Abilities\Traits\HasCheckPermission; defined( 'ABSPATH' ) || exit; @@ -34,6 +35,7 @@ class PipelineAbilities { private ImportExportAbility $import_export; public function __construct() { + add_action('wp_abilities_api_init', array( $this, 'abilities_api_init' )); if ( ! class_exists( 'WP_Ability' ) || self::$registered ) { return; } diff --git a/inc/Abilities/PipelineStepAbilities.php b/inc/Abilities/PipelineStepAbilities.php index da83a9f2a..6efc50b69 100644 --- a/inc/Abilities/PipelineStepAbilities.php +++ b/inc/Abilities/PipelineStepAbilities.php @@ -16,6 +16,7 @@ use DataMachine\Core\Database\Pipelines\Pipelines; use DataMachine\Core\Database\ProcessedItems\ProcessedItems; use DataMachine\Core\PluginSettings; +use DataMachine\Abilities\Traits\HasCheckPermission; defined( 'ABSPATH' ) || exit; diff --git a/inc/Abilities/ProcessedItemsAbilities.php b/inc/Abilities/ProcessedItemsAbilities.php index 01c90c9dd..16aad9044 100644 --- a/inc/Abilities/ProcessedItemsAbilities.php +++ b/inc/Abilities/ProcessedItemsAbilities.php @@ -15,6 +15,7 @@ use DataMachine\Abilities\PermissionHelper; use DataMachine\Core\Database\ProcessedItems\ProcessedItems; +use DataMachine\Abilities\Traits\HasCheckPermission; defined( 'ABSPATH' ) || exit; diff --git a/inc/Abilities/Publish/PublishWordPressAbility.php b/inc/Abilities/Publish/PublishWordPressAbility.php index 7c5fe8c6f..8d8bf15ec 100644 --- a/inc/Abilities/Publish/PublishWordPressAbility.php +++ b/inc/Abilities/Publish/PublishWordPressAbility.php @@ -13,6 +13,7 @@ use DataMachine\Abilities\PermissionHelper; use DataMachine\Core\WordPress\PostTracking; use DataMachine\Core\WordPress\WordPressSettingsResolver; +use DataMachine\Abilities\Traits\HasCheckPermission; defined( 'ABSPATH' ) || exit; diff --git a/inc/Abilities/Publish/SendEmailAbility.php b/inc/Abilities/Publish/SendEmailAbility.php index 86998bfc9..5c92e655a 100644 --- a/inc/Abilities/Publish/SendEmailAbility.php +++ b/inc/Abilities/Publish/SendEmailAbility.php @@ -16,6 +16,7 @@ namespace DataMachine\Abilities\Publish; use DataMachine\Abilities\PermissionHelper; +use DataMachine\Abilities\Traits\HasCheckPermission; defined( 'ABSPATH' ) || exit; @@ -170,8 +171,8 @@ public function execute( array $input ): array { $headers[] = "Content-Type: {$content_type}; charset=UTF-8"; // From. - $from_name = $config['from_name'] ?: get_bloginfo( 'name' ); - $from_email = $config['from_email'] ?: get_option( 'admin_email' ); + $from_name = $config['from_name'] ? $config['from_name'] : get_bloginfo( 'name' ); + $from_email = $config['from_email'] ? $config['from_email'] : get_option( 'admin_email' ); if ( $from_name && $from_email ) { $headers[] = sprintf( 'From: %s <%s>', $from_name, $from_email ); } @@ -262,7 +263,7 @@ public function execute( array $input ): array { global $phpmailer; $error_msg = 'wp_mail() returned false'; if ( isset( $phpmailer ) && $phpmailer instanceof \PHPMailer\PHPMailer\PHPMailer ) { - $error_msg = $phpmailer->ErrorInfo ?: $error_msg; + $error_msg = $phpmailer->ErrorInfo ? $phpmailer->ErrorInfo : $error_msg; } $logs[] = array( diff --git a/inc/Abilities/SettingsAbilities.php b/inc/Abilities/SettingsAbilities.php index 06e43e515..b95a23f5b 100644 --- a/inc/Abilities/SettingsAbilities.php +++ b/inc/Abilities/SettingsAbilities.php @@ -14,6 +14,7 @@ use DataMachine\Core\NetworkSettings; use DataMachine\Core\PluginSettings; +use DataMachine\Abilities\Traits\HasCheckPermission; defined( 'ABSPATH' ) || exit; diff --git a/inc/Abilities/StepTypeAbilities.php b/inc/Abilities/StepTypeAbilities.php index a0778ef55..8979b6575 100644 --- a/inc/Abilities/StepTypeAbilities.php +++ b/inc/Abilities/StepTypeAbilities.php @@ -12,6 +12,8 @@ namespace DataMachine\Abilities; use DataMachine\Abilities\PermissionHelper; +use DataMachine\Abilities\Traits\HasCheckPermission; +use DataMachine\Core\NetworkSettings; defined( 'ABSPATH' ) || exit; diff --git a/inc/Abilities/Taxonomy/CreateTaxonomyTermAbility.php b/inc/Abilities/Taxonomy/CreateTaxonomyTermAbility.php index 1c43ed666..dc1bc4404 100644 --- a/inc/Abilities/Taxonomy/CreateTaxonomyTermAbility.php +++ b/inc/Abilities/Taxonomy/CreateTaxonomyTermAbility.php @@ -13,6 +13,7 @@ use DataMachine\Abilities\PermissionHelper; use DataMachine\Core\WordPress\TaxonomyHandler; +use DataMachine\Abilities\Traits\HasCheckPermission; defined( 'ABSPATH' ) || exit; diff --git a/inc/Abilities/Taxonomy/DeleteTaxonomyTermAbility.php b/inc/Abilities/Taxonomy/DeleteTaxonomyTermAbility.php index ec352f480..7c314d1af 100644 --- a/inc/Abilities/Taxonomy/DeleteTaxonomyTermAbility.php +++ b/inc/Abilities/Taxonomy/DeleteTaxonomyTermAbility.php @@ -13,6 +13,7 @@ use DataMachine\Abilities\PermissionHelper; use DataMachine\Core\WordPress\TaxonomyHandler; +use DataMachine\Abilities\Traits\HasCheckPermission; defined( 'ABSPATH' ) || exit; diff --git a/inc/Abilities/Taxonomy/GetTaxonomyTermsAbility.php b/inc/Abilities/Taxonomy/GetTaxonomyTermsAbility.php index b928f391e..30d512bca 100644 --- a/inc/Abilities/Taxonomy/GetTaxonomyTermsAbility.php +++ b/inc/Abilities/Taxonomy/GetTaxonomyTermsAbility.php @@ -13,6 +13,7 @@ use DataMachine\Abilities\PermissionHelper; use DataMachine\Core\WordPress\TaxonomyHandler; +use DataMachine\Abilities\Traits\HasCheckPermission; defined( 'ABSPATH' ) || exit; diff --git a/inc/Abilities/Taxonomy/ResolveTermAbility.php b/inc/Abilities/Taxonomy/ResolveTermAbility.php index 841a5aab1..8a106378c 100644 --- a/inc/Abilities/Taxonomy/ResolveTermAbility.php +++ b/inc/Abilities/Taxonomy/ResolveTermAbility.php @@ -13,6 +13,7 @@ use DataMachine\Abilities\PermissionHelper; use DataMachine\Core\WordPress\TaxonomyHandler; +use DataMachine\Abilities\Traits\HasCheckPermission; if ( ! defined( 'ABSPATH' ) ) { exit; diff --git a/inc/Abilities/Taxonomy/UpdateTaxonomyTermAbility.php b/inc/Abilities/Taxonomy/UpdateTaxonomyTermAbility.php index c872cab4d..3eb8ccb51 100644 --- a/inc/Abilities/Taxonomy/UpdateTaxonomyTermAbility.php +++ b/inc/Abilities/Taxonomy/UpdateTaxonomyTermAbility.php @@ -13,6 +13,7 @@ use DataMachine\Abilities\PermissionHelper; use DataMachine\Core\WordPress\TaxonomyHandler; +use DataMachine\Abilities\Traits\HasCheckPermission; defined( 'ABSPATH' ) || exit; diff --git a/inc/Abilities/TaxonomyAbilities.php b/inc/Abilities/TaxonomyAbilities.php index d2da8bf47..35f71d4f0 100644 --- a/inc/Abilities/TaxonomyAbilities.php +++ b/inc/Abilities/TaxonomyAbilities.php @@ -18,6 +18,7 @@ use DataMachine\Abilities\Taxonomy\UpdateTaxonomyTermAbility; use DataMachine\Abilities\Taxonomy\DeleteTaxonomyTermAbility; use DataMachine\Abilities\Taxonomy\ResolveTermAbility; +use DataMachine\Abilities\Traits\HasCheckPermission; defined( 'ABSPATH' ) || exit; @@ -32,6 +33,7 @@ class TaxonomyAbilities { private ResolveTermAbility $resolve_term; public function __construct() { + add_action('wp_abilities_api_init', array( $this, 'abilities_api_init' )); if ( ! class_exists( 'WP_Ability' ) || self::$registered ) { return; } diff --git a/inc/Api/AgentFiles.php b/inc/Api/AgentFiles.php index 01292c931..b5978af8a 100644 --- a/inc/Api/AgentFiles.php +++ b/inc/Api/AgentFiles.php @@ -18,6 +18,7 @@ use WP_Error; use WP_REST_Request; use WP_REST_Server; +use DataMachine\Api\Traits\HasRegister; if ( ! defined( 'WPINC' ) ) { die; @@ -525,7 +526,7 @@ public static function delete_daily_file( WP_REST_Request $request ) { * @return string Full path to the contexts directory. */ private static function resolve_contexts_dir( WP_REST_Request $request ): string { - $dm = new \DataMachine\Core\FilesRepository\DirectoryManager(); + $dm = new \DataMachine\Core\FilesRepository\DirectoryManager(); $user_id = $dm->get_effective_user_id( self::resolve_scoped_user_id( $request ) ); $context = array( 'user_id' => $user_id ); diff --git a/inc/Api/AgentPing.php b/inc/Api/AgentPing.php index 67e863310..b16f4a2b5 100644 --- a/inc/Api/AgentPing.php +++ b/inc/Api/AgentPing.php @@ -14,6 +14,7 @@ use WP_REST_Server; use WP_REST_Request; use WP_REST_Response; +use DataMachine\Api\Traits\HasRegister; if ( ! defined( 'WPINC' ) ) { die; diff --git a/inc/Api/Agents.php b/inc/Api/Agents.php index a44fdabb8..453b09c18 100644 --- a/inc/Api/Agents.php +++ b/inc/Api/Agents.php @@ -19,6 +19,7 @@ use WP_REST_Request; use WP_REST_Server; use WP_Error; +use DataMachine\Api\Email; if ( ! defined( 'WPINC' ) ) { die; diff --git a/inc/Api/Analytics.php b/inc/Api/Analytics.php index 881cca0bb..75ab933a2 100644 --- a/inc/Api/Analytics.php +++ b/inc/Api/Analytics.php @@ -22,6 +22,7 @@ use DataMachine\Abilities\PermissionHelper; use WP_REST_Server; +use DataMachine\Api\Traits\HasRegister; if ( ! defined( 'ABSPATH' ) ) { exit; diff --git a/inc/Api/Auth.php b/inc/Api/Auth.php index b07f99b79..ee3777b33 100644 --- a/inc/Api/Auth.php +++ b/inc/Api/Auth.php @@ -14,6 +14,7 @@ use DataMachine\Abilities\PermissionHelper; use DataMachine\Abilities\AuthAbilities; use WP_REST_Server; +use DataMachine\Api\Traits\HasRegister; if ( ! defined( 'WPINC' ) ) { die; @@ -277,8 +278,8 @@ private static function ability_to_response( array $result, string $error_code ) return rest_ensure_response( $result ); } - $error = $result['error'] ?? 'Unknown error'; - $status = 400; + $error = $result['error'] ?? 'Unknown error'; + $status = 400; if ( str_contains( $error, 'not found' ) ) { $status = 404; diff --git a/inc/Api/Chat/Chat.php b/inc/Api/Chat/Chat.php index f6e5e7c5d..ea67807df 100644 --- a/inc/Api/Chat/Chat.php +++ b/inc/Api/Chat/Chat.php @@ -20,6 +20,7 @@ use WP_REST_Server; use WP_REST_Request; use WP_Error; +use DataMachine\Api\Traits\HasRegister; require_once __DIR__ . '/ChatPipelinesDirective.php'; @@ -108,9 +109,9 @@ public static function register_routes() { 'sanitize_callback' => array( self::class, 'sanitize_attachments' ), ), 'client_context' => array( - 'type' => 'object', - 'required' => false, - 'description' => __( 'Client-side context for the AI agent. Arbitrary key-value pairs describing what the user is currently doing (active tab, draft ID, screen, etc). Injected as a system message.', 'data-machine' ), + 'type' => 'object', + 'required' => false, + 'description' => __( 'Client-side context for the AI agent. Arbitrary key-value pairs describing what the user is currently doing (active tab, draft ID, screen, etc). Injected as a system message.', 'data-machine' ), ), ), ) diff --git a/inc/Api/Chat/ChatOrchestrator.php b/inc/Api/Chat/ChatOrchestrator.php index b8a0488bc..1521347f9 100644 --- a/inc/Api/Chat/ChatOrchestrator.php +++ b/inc/Api/Chat/ChatOrchestrator.php @@ -123,7 +123,10 @@ public static function processChat( if ( ! empty( $attachments ) ) { $content = ConversationManager::buildMultiModalContent( $message, $attachments ); - $metadata = array( 'type' => 'multimodal', 'attachments' => $attachments ); + $metadata = array( + 'type' => 'multimodal', + 'attachments' => $attachments, + ); $messages[] = ConversationManager::buildConversationMessage( 'user', $content, $metadata ); } else { $messages[] = ConversationManager::buildConversationMessage( 'user', $message, array( 'type' => 'text' ) ); @@ -610,8 +613,8 @@ public static function executeConversationTurn( $agent_id = datamachine_resolve_or_create_agent_id( $user_id ); } - $resolver = new ToolPolicyResolver(); - $all_tools = $resolver->resolve( array( + $resolver = new ToolPolicyResolver(); + $all_tools = $resolver->resolve( array( 'context' => ToolPolicyResolver::CONTEXT_CHAT, 'agent_id' => $agent_id, ) ); diff --git a/inc/Api/Chat/Tools/AddPipelineStep.php b/inc/Api/Chat/Tools/AddPipelineStep.php index 5e29788a7..0da3c766d 100644 --- a/inc/Api/Chat/Tools/AddPipelineStep.php +++ b/inc/Api/Chat/Tools/AddPipelineStep.php @@ -17,6 +17,7 @@ use DataMachine\Abilities\StepTypeAbilities; use DataMachine\Engine\AI\Tools\BaseTool; +use DataMachine\Api\Chat\Tools\CreatePipeline; class AddPipelineStep extends BaseTool { diff --git a/inc/Api/Chat/Tools/CreatePipeline.php b/inc/Api/Chat/Tools/CreatePipeline.php index eb31ae3f8..268ad06dd 100644 --- a/inc/Api/Chat/Tools/CreatePipeline.php +++ b/inc/Api/Chat/Tools/CreatePipeline.php @@ -17,6 +17,7 @@ use DataMachine\Abilities\StepTypeAbilities; use DataMachine\Engine\AI\Tools\BaseTool; +use DataMachine\Api\Chat\Tools\CreateFlow; class CreatePipeline extends BaseTool { diff --git a/inc/Api/Email.php b/inc/Api/Email.php index 2b97188da..758d3dbe9 100644 --- a/inc/Api/Email.php +++ b/inc/Api/Email.php @@ -32,16 +32,46 @@ public static function register_routes(): void { 'callback' => array( self::class, 'handle_send' ), 'permission_callback' => array( self::class, 'check_permission' ), 'args' => array( - 'to' => array( 'type' => 'string', 'required' => true ), - 'subject' => array( 'type' => 'string', 'required' => true ), - 'body' => array( 'type' => 'string', 'required' => true ), - 'cc' => array( 'type' => 'string', 'default' => '' ), - 'bcc' => array( 'type' => 'string', 'default' => '' ), - 'from_name' => array( 'type' => 'string', 'default' => '' ), - 'from_email' => array( 'type' => 'string', 'default' => '' ), - 'reply_to' => array( 'type' => 'string', 'default' => '' ), - 'content_type' => array( 'type' => 'string', 'default' => 'text/html' ), - 'attachments' => array( 'type' => 'array', 'default' => array() ), + 'to' => array( + 'type' => 'string', + 'required' => true, + ), + 'subject' => array( + 'type' => 'string', + 'required' => true, + ), + 'body' => array( + 'type' => 'string', + 'required' => true, + ), + 'cc' => array( + 'type' => 'string', + 'default' => '', + ), + 'bcc' => array( + 'type' => 'string', + 'default' => '', + ), + 'from_name' => array( + 'type' => 'string', + 'default' => '', + ), + 'from_email' => array( + 'type' => 'string', + 'default' => '', + ), + 'reply_to' => array( + 'type' => 'string', + 'default' => '', + ), + 'content_type' => array( + 'type' => 'string', + 'default' => 'text/html', + ), + 'attachments' => array( + 'type' => 'array', + 'default' => array(), + ), ), ) ); @@ -55,13 +85,34 @@ public static function register_routes(): void { 'callback' => array( self::class, 'handle_fetch' ), 'permission_callback' => array( self::class, 'check_permission' ), 'args' => array( - 'folder' => array( 'type' => 'string', 'default' => 'INBOX' ), - 'search' => array( 'type' => 'string', 'default' => 'UNSEEN' ), - 'max' => array( 'type' => 'integer', 'default' => 10 ), - 'offset' => array( 'type' => 'integer', 'default' => 0 ), - 'headers_only' => array( 'type' => 'boolean', 'default' => false ), - 'mark_as_read' => array( 'type' => 'boolean', 'default' => false ), - 'download_attachments' => array( 'type' => 'boolean', 'default' => false ), + 'folder' => array( + 'type' => 'string', + 'default' => 'INBOX', + ), + 'search' => array( + 'type' => 'string', + 'default' => 'UNSEEN', + ), + 'max' => array( + 'type' => 'integer', + 'default' => 10, + ), + 'offset' => array( + 'type' => 'integer', + 'default' => 0, + ), + 'headers_only' => array( + 'type' => 'boolean', + 'default' => false, + ), + 'mark_as_read' => array( + 'type' => 'boolean', + 'default' => false, + ), + 'download_attachments' => array( + 'type' => 'boolean', + 'default' => false, + ), ), ) ); @@ -75,8 +126,14 @@ public static function register_routes(): void { 'callback' => array( self::class, 'handle_read' ), 'permission_callback' => array( self::class, 'check_permission' ), 'args' => array( - 'uid' => array( 'type' => 'integer', 'required' => true ), - 'folder' => array( 'type' => 'string', 'default' => 'INBOX' ), + 'uid' => array( + 'type' => 'integer', + 'required' => true, + ), + 'folder' => array( + 'type' => 'string', + 'default' => 'INBOX', + ), ), ) ); @@ -90,13 +147,34 @@ public static function register_routes(): void { 'callback' => array( self::class, 'handle_reply' ), 'permission_callback' => array( self::class, 'check_permission' ), 'args' => array( - 'to' => array( 'type' => 'string', 'required' => true ), - 'subject' => array( 'type' => 'string', 'required' => true ), - 'body' => array( 'type' => 'string', 'required' => true ), - 'in_reply_to' => array( 'type' => 'string', 'required' => true ), - 'references' => array( 'type' => 'string', 'default' => '' ), - 'cc' => array( 'type' => 'string', 'default' => '' ), - 'content_type' => array( 'type' => 'string', 'default' => 'text/html' ), + 'to' => array( + 'type' => 'string', + 'required' => true, + ), + 'subject' => array( + 'type' => 'string', + 'required' => true, + ), + 'body' => array( + 'type' => 'string', + 'required' => true, + ), + 'in_reply_to' => array( + 'type' => 'string', + 'required' => true, + ), + 'references' => array( + 'type' => 'string', + 'default' => '', + ), + 'cc' => array( + 'type' => 'string', + 'default' => '', + ), + 'content_type' => array( + 'type' => 'string', + 'default' => 'text/html', + ), ), ) ); @@ -110,8 +188,14 @@ public static function register_routes(): void { 'callback' => array( self::class, 'handle_delete' ), 'permission_callback' => array( self::class, 'check_permission' ), 'args' => array( - 'uid' => array( 'type' => 'integer', 'required' => true ), - 'folder' => array( 'type' => 'string', 'default' => 'INBOX' ), + 'uid' => array( + 'type' => 'integer', + 'required' => true, + ), + 'folder' => array( + 'type' => 'string', + 'default' => 'INBOX', + ), ), ) ); @@ -125,9 +209,18 @@ public static function register_routes(): void { 'callback' => array( self::class, 'handle_move' ), 'permission_callback' => array( self::class, 'check_permission' ), 'args' => array( - 'uid' => array( 'type' => 'integer', 'required' => true ), - 'destination' => array( 'type' => 'string', 'required' => true ), - 'folder' => array( 'type' => 'string', 'default' => 'INBOX' ), + 'uid' => array( + 'type' => 'integer', + 'required' => true, + ), + 'destination' => array( + 'type' => 'string', + 'required' => true, + ), + 'folder' => array( + 'type' => 'string', + 'default' => 'INBOX', + ), ), ) ); @@ -141,10 +234,22 @@ public static function register_routes(): void { 'callback' => array( self::class, 'handle_flag' ), 'permission_callback' => array( self::class, 'check_permission' ), 'args' => array( - 'uid' => array( 'type' => 'integer', 'required' => true ), - 'flag' => array( 'type' => 'string', 'required' => true ), - 'action' => array( 'type' => 'string', 'default' => 'set' ), - 'folder' => array( 'type' => 'string', 'default' => 'INBOX' ), + 'uid' => array( + 'type' => 'integer', + 'required' => true, + ), + 'flag' => array( + 'type' => 'string', + 'required' => true, + ), + 'action' => array( + 'type' => 'string', + 'default' => 'set', + ), + 'folder' => array( + 'type' => 'string', + 'default' => 'INBOX', + ), ), ) ); @@ -158,10 +263,22 @@ public static function register_routes(): void { 'callback' => array( self::class, 'handle_batch_move' ), 'permission_callback' => array( self::class, 'check_permission' ), 'args' => array( - 'search' => array( 'type' => 'string', 'required' => true ), - 'destination' => array( 'type' => 'string', 'required' => true ), - 'folder' => array( 'type' => 'string', 'default' => 'INBOX' ), - 'max' => array( 'type' => 'integer', 'default' => 500 ), + 'search' => array( + 'type' => 'string', + 'required' => true, + ), + 'destination' => array( + 'type' => 'string', + 'required' => true, + ), + 'folder' => array( + 'type' => 'string', + 'default' => 'INBOX', + ), + 'max' => array( + 'type' => 'integer', + 'default' => 500, + ), ), ) ); @@ -175,11 +292,26 @@ public static function register_routes(): void { 'callback' => array( self::class, 'handle_batch_flag' ), 'permission_callback' => array( self::class, 'check_permission' ), 'args' => array( - 'search' => array( 'type' => 'string', 'required' => true ), - 'flag' => array( 'type' => 'string', 'required' => true ), - 'action' => array( 'type' => 'string', 'default' => 'set' ), - 'folder' => array( 'type' => 'string', 'default' => 'INBOX' ), - 'max' => array( 'type' => 'integer', 'default' => 500 ), + 'search' => array( + 'type' => 'string', + 'required' => true, + ), + 'flag' => array( + 'type' => 'string', + 'required' => true, + ), + 'action' => array( + 'type' => 'string', + 'default' => 'set', + ), + 'folder' => array( + 'type' => 'string', + 'default' => 'INBOX', + ), + 'max' => array( + 'type' => 'integer', + 'default' => 500, + ), ), ) ); @@ -193,9 +325,18 @@ public static function register_routes(): void { 'callback' => array( self::class, 'handle_batch_delete' ), 'permission_callback' => array( self::class, 'check_permission' ), 'args' => array( - 'search' => array( 'type' => 'string', 'required' => true ), - 'folder' => array( 'type' => 'string', 'default' => 'INBOX' ), - 'max' => array( 'type' => 'integer', 'default' => 100 ), + 'search' => array( + 'type' => 'string', + 'required' => true, + ), + 'folder' => array( + 'type' => 'string', + 'default' => 'INBOX', + ), + 'max' => array( + 'type' => 'integer', + 'default' => 100, + ), ), ) ); @@ -209,8 +350,14 @@ public static function register_routes(): void { 'callback' => array( self::class, 'handle_unsubscribe' ), 'permission_callback' => array( self::class, 'check_permission' ), 'args' => array( - 'uid' => array( 'type' => 'integer', 'required' => true ), - 'folder' => array( 'type' => 'string', 'default' => 'INBOX' ), + 'uid' => array( + 'type' => 'integer', + 'required' => true, + ), + 'folder' => array( + 'type' => 'string', + 'default' => 'INBOX', + ), ), ) ); @@ -224,9 +371,18 @@ public static function register_routes(): void { 'callback' => array( self::class, 'handle_batch_unsubscribe' ), 'permission_callback' => array( self::class, 'check_permission' ), 'args' => array( - 'search' => array( 'type' => 'string', 'required' => true ), - 'folder' => array( 'type' => 'string', 'default' => 'INBOX' ), - 'max' => array( 'type' => 'integer', 'default' => 20 ), + 'search' => array( + 'type' => 'string', + 'required' => true, + ), + 'folder' => array( + 'type' => 'string', + 'default' => 'INBOX', + ), + 'max' => array( + 'type' => 'integer', + 'default' => 20, + ), ), ) ); @@ -478,6 +634,7 @@ public static function handle_batch_unsubscribe( \WP_REST_Request $request ): \W } public static function handle_test_connection( \WP_REST_Request $request ): \WP_REST_Response|\WP_Error { + unset( $request ); $ability = wp_get_ability( 'datamachine/email-test-connection' ); if ( ! $ability ) { return new \WP_Error( 'ability_not_found', 'Email test connection ability not available', array( 'status' => 500 ) ); diff --git a/inc/Api/Execute.php b/inc/Api/Execute.php index cb15fe02e..2a083123d 100644 --- a/inc/Api/Execute.php +++ b/inc/Api/Execute.php @@ -10,6 +10,7 @@ namespace DataMachine\Api; use DataMachine\Abilities\PermissionHelper; +use DataMachine\Api\Traits\HasRegister; if ( ! defined( 'ABSPATH' ) ) { exit; diff --git a/inc/Api/FlowFiles.php b/inc/Api/FlowFiles.php index ea4a7d01d..e0cde89c4 100644 --- a/inc/Api/FlowFiles.php +++ b/inc/Api/FlowFiles.php @@ -16,6 +16,7 @@ use WP_Error; use WP_REST_Request; use WP_REST_Server; +use DataMachine\Api\Traits\HasRegister; if ( ! defined( 'WPINC' ) ) { die; diff --git a/inc/Api/Flows/FlowQueue.php b/inc/Api/Flows/FlowQueue.php index d1b720542..9d11fa39f 100644 --- a/inc/Api/Flows/FlowQueue.php +++ b/inc/Api/Flows/FlowQueue.php @@ -12,6 +12,7 @@ use DataMachine\Abilities\PermissionHelper; use WP_REST_Server; +use DataMachine\Api\Traits\HasRegister; if ( ! defined( 'WPINC' ) ) { die; diff --git a/inc/Api/Flows/FlowScheduling.php b/inc/Api/Flows/FlowScheduling.php index 86aaaebaa..9a0d0841b 100644 --- a/inc/Api/Flows/FlowScheduling.php +++ b/inc/Api/Flows/FlowScheduling.php @@ -470,4 +470,8 @@ private static function schedule_cron( int $flow_id, string $cron_expression, $d return true; } + + public function __construct() { + add_action('rest_api_init', array( $this, 'rest_api_init' )); + } } diff --git a/inc/Api/Flows/FlowSteps.php b/inc/Api/Flows/FlowSteps.php index 9ae91620e..95125903b 100644 --- a/inc/Api/Flows/FlowSteps.php +++ b/inc/Api/Flows/FlowSteps.php @@ -15,6 +15,7 @@ use DataMachine\Abilities\FlowStepAbilities; use DataMachine\Abilities\HandlerAbilities; use DataMachine\Abilities\StepTypeAbilities; +use DataMachine\Api\Traits\HasRegister; if ( ! defined( 'WPINC' ) ) { diff --git a/inc/Api/Flows/Flows.php b/inc/Api/Flows/Flows.php index 0a88bbc6a..daed44388 100644 --- a/inc/Api/Flows/Flows.php +++ b/inc/Api/Flows/Flows.php @@ -12,6 +12,7 @@ use DataMachine\Abilities\PermissionHelper; use WP_REST_Server; +use DataMachine\Api\Traits\HasRegister; if ( ! defined( 'WPINC' ) ) { die; diff --git a/inc/Api/Handlers.php b/inc/Api/Handlers.php index 35d9d606b..e595f18b9 100644 --- a/inc/Api/Handlers.php +++ b/inc/Api/Handlers.php @@ -16,6 +16,7 @@ use DataMachine\Abilities\StepTypeAbilities; use WP_REST_Server; use WP_REST_Request; +use DataMachine\Api\Traits\HasRegister; if ( ! defined( 'ABSPATH' ) ) { exit; diff --git a/inc/Api/InternalLinks.php b/inc/Api/InternalLinks.php index f6f96ce08..08f2e57b0 100644 --- a/inc/Api/InternalLinks.php +++ b/inc/Api/InternalLinks.php @@ -19,6 +19,7 @@ use DataMachine\Abilities\PermissionHelper; use WP_REST_Server; +use DataMachine\Api\Traits\HasRegister; if ( ! defined( 'ABSPATH' ) ) { exit; diff --git a/inc/Api/Jobs.php b/inc/Api/Jobs.php index ac531eb41..e104d6441 100644 --- a/inc/Api/Jobs.php +++ b/inc/Api/Jobs.php @@ -18,6 +18,7 @@ use DataMachine\Abilities\PermissionHelper; use DataMachine\Abilities\JobAbilities; +use DataMachine\Api\Traits\HasRegister; if ( ! defined( 'WPINC' ) ) { die; @@ -55,20 +56,20 @@ public static function register_routes() { 'callback' => array( self::class, 'handle_get_jobs' ), 'permission_callback' => array( self::class, 'check_permission' ), 'args' => array( - 'orderby' => array( + 'orderby' => array( 'required' => false, 'type' => 'string', 'default' => 'job_id', 'description' => __( 'Order jobs by field', 'data-machine' ), ), - 'order' => array( + 'order' => array( 'required' => false, 'type' => 'string', 'default' => 'DESC', 'enum' => array( 'ASC', 'DESC' ), 'description' => __( 'Sort order', 'data-machine' ), ), - 'per_page' => array( + 'per_page' => array( 'required' => false, 'type' => 'integer', 'default' => 50, @@ -76,46 +77,46 @@ public static function register_routes() { 'maximum' => 100, 'description' => __( 'Number of jobs per page', 'data-machine' ), ), - 'offset' => array( + 'offset' => array( 'required' => false, 'type' => 'integer', 'default' => 0, 'minimum' => 0, 'description' => __( 'Offset for pagination', 'data-machine' ), ), - 'pipeline_id' => array( + 'pipeline_id' => array( 'required' => false, 'type' => 'integer', 'description' => __( 'Filter by pipeline ID', 'data-machine' ), ), - 'flow_id' => array( + 'flow_id' => array( 'required' => false, 'type' => 'integer', 'description' => __( 'Filter by flow ID', 'data-machine' ), ), - 'status' => array( + 'status' => array( 'required' => false, 'type' => 'string', 'description' => __( 'Filter by job status', 'data-machine' ), ), - 'user_id' => array( - 'required' => false, - 'type' => 'integer', - 'description' => __( 'Filter by user ID (admin only, non-admins always see own data)', 'data-machine' ), - 'sanitize_callback' => 'absint', - ), - 'parent_job_id' => array( - 'required' => false, - 'type' => 'integer', - 'description' => __( 'Filter by parent job ID (for batch child jobs)', 'data-machine' ), - ), - 'hide_children' => array( - 'required' => false, - 'type' => 'boolean', - 'default' => false, - 'description' => __( 'Hide child jobs from top-level list', 'data-machine' ), + 'user_id' => array( + 'required' => false, + 'type' => 'integer', + 'description' => __( 'Filter by user ID (admin only, non-admins always see own data)', 'data-machine' ), + 'sanitize_callback' => 'absint', + ), + 'parent_job_id' => array( + 'required' => false, + 'type' => 'integer', + 'description' => __( 'Filter by parent job ID (for batch child jobs)', 'data-machine' ), + ), + 'hide_children' => array( + 'required' => false, + 'type' => 'boolean', + 'default' => false, + 'description' => __( 'Hide child jobs from top-level list', 'data-machine' ), + ), ), - ), ) ); diff --git a/inc/Api/Logs.php b/inc/Api/Logs.php index e26ba5aa8..cd4b0be5a 100644 --- a/inc/Api/Logs.php +++ b/inc/Api/Logs.php @@ -17,6 +17,7 @@ use DataMachine\Abilities\PermissionHelper; use DataMachine\Abilities\LogAbilities; +use DataMachine\Api\Traits\HasRegister; if ( ! defined( 'WPINC' ) ) { die; diff --git a/inc/Api/Pipelines/PipelineFlows.php b/inc/Api/Pipelines/PipelineFlows.php index c9cb64f24..6a30c5451 100644 --- a/inc/Api/Pipelines/PipelineFlows.php +++ b/inc/Api/Pipelines/PipelineFlows.php @@ -11,6 +11,7 @@ namespace DataMachine\Api\Pipelines; use DataMachine\Abilities\PermissionHelper; +use DataMachine\Api\Traits\HasRegister; if ( ! defined( 'WPINC' ) ) { die; diff --git a/inc/Api/Pipelines/PipelineSteps.php b/inc/Api/Pipelines/PipelineSteps.php index 7cd314f1e..96e7d93a5 100644 --- a/inc/Api/Pipelines/PipelineSteps.php +++ b/inc/Api/Pipelines/PipelineSteps.php @@ -15,6 +15,7 @@ use DataMachine\Abilities\PipelineStepAbilities; use DataMachine\Abilities\StepTypeAbilities; use WP_REST_Server; +use DataMachine\Api\Traits\HasRegister; if ( ! defined( 'WPINC' ) ) { die; diff --git a/inc/Api/Pipelines/Pipelines.php b/inc/Api/Pipelines/Pipelines.php index e6dbf4ead..f1b21a87a 100644 --- a/inc/Api/Pipelines/Pipelines.php +++ b/inc/Api/Pipelines/Pipelines.php @@ -15,6 +15,7 @@ use DataMachine\Abilities\PipelineAbilities; use DataMachine\Core\Admin\DateFormatter; use WP_REST_Server; +use DataMachine\Api\Traits\HasRegister; if ( ! defined( 'WPINC' ) ) { die; diff --git a/inc/Api/ProcessedItems.php b/inc/Api/ProcessedItems.php index e8a4ef406..74c372faa 100644 --- a/inc/Api/ProcessedItems.php +++ b/inc/Api/ProcessedItems.php @@ -16,6 +16,7 @@ use DataMachine\Abilities\PermissionHelper; use DataMachine\Abilities\ProcessedItemsAbilities; +use DataMachine\Api\Traits\HasRegister; if ( ! defined( 'WPINC' ) ) { die; diff --git a/inc/Api/Providers.php b/inc/Api/Providers.php index 62c4d6c63..d8ae1a843 100644 --- a/inc/Api/Providers.php +++ b/inc/Api/Providers.php @@ -13,6 +13,7 @@ use DataMachine\Core\PluginSettings; use WP_REST_Server; +use DataMachine\Api\Traits\HasRegister; if ( ! defined( 'ABSPATH' ) ) { exit; diff --git a/inc/Api/Settings.php b/inc/Api/Settings.php index c42816e96..ca98c7a15 100644 --- a/inc/Api/Settings.php +++ b/inc/Api/Settings.php @@ -15,6 +15,7 @@ use DataMachine\Abilities\SettingsAbilities; use WP_REST_Response; use WP_REST_Server; +use DataMachine\Api\Traits\HasRegister; if ( ! defined( 'WPINC' ) ) { die; diff --git a/inc/Api/StepTypes.php b/inc/Api/StepTypes.php index 73009d9a0..c4aadfaab 100644 --- a/inc/Api/StepTypes.php +++ b/inc/Api/StepTypes.php @@ -14,6 +14,7 @@ use DataMachine\Abilities\HandlerAbilities; use DataMachine\Abilities\StepTypeAbilities; use WP_REST_Server; +use DataMachine\Api\Traits\HasRegister; if ( ! defined( 'ABSPATH' ) ) { exit; diff --git a/inc/Api/Tools.php b/inc/Api/Tools.php index 346300271..ae93c5f54 100644 --- a/inc/Api/Tools.php +++ b/inc/Api/Tools.php @@ -12,6 +12,7 @@ namespace DataMachine\Api; use WP_REST_Server; +use DataMachine\Api\Traits\HasRegister; if ( ! defined( 'ABSPATH' ) ) { exit; diff --git a/inc/Api/Users.php b/inc/Api/Users.php index 7fdf228b8..c86c8457f 100644 --- a/inc/Api/Users.php +++ b/inc/Api/Users.php @@ -13,6 +13,7 @@ use WP_REST_Request; use WP_REST_Server; use WP_Error; +use DataMachine\Api\Email; if ( ! defined( 'WPINC' ) ) { die; diff --git a/inc/Api/WebhookTrigger.php b/inc/Api/WebhookTrigger.php index 73f3c7a3a..7c37c7f89 100644 --- a/inc/Api/WebhookTrigger.php +++ b/inc/Api/WebhookTrigger.php @@ -18,6 +18,7 @@ use DataMachine\Abilities\PermissionHelper; use DataMachine\Core\Database\Flows\Flows; +use DataMachine\Api\Traits\HasRegister; if ( ! defined( 'ABSPATH' ) ) { exit; diff --git a/inc/Cli/Commands/AgentsCommand.php b/inc/Cli/Commands/AgentsCommand.php index 7aeb8021c..9ceca7712 100644 --- a/inc/Cli/Commands/AgentsCommand.php +++ b/inc/Cli/Commands/AgentsCommand.php @@ -691,12 +691,12 @@ private function tokenList( $abilities, int $agent_id, array $assoc_args ): void } $items[] = array( - 'token_id' => $token['token_id'], - 'prefix' => $token['token_prefix'] . '...', - 'label' => $token['label'] ?: '(none)', - 'last_used' => $token['last_used_at'] ?? 'never', - 'expires' => $token['expires_at'] ?? 'never', - 'status' => $expired ? 'expired' : 'active', + 'token_id' => $token['token_id'], + 'prefix' => $token['token_prefix'] . '...', + 'label' => $token['label'] ? $token['label'] : '(none)', + 'last_used' => $token['last_used_at'] ?? 'never', + 'expires' => $token['expires_at'] ?? 'never', + 'status' => $expired ? 'expired' : 'active', ); } @@ -863,7 +863,7 @@ public function config( array $args, array $assoc_args ): void { $value = ( null !== $decoded || 'null' === $raw_value ) ? $decoded : $raw_value; $config[ $key ] = $value; - $display = is_array( $value ) ? wp_json_encode( $value, JSON_UNESCAPED_SLASHES ) : (string) $value; + $display = is_array( $value ) ? wp_json_encode( $value, JSON_UNESCAPED_SLASHES ) : (string) $value; WP_CLI::log( sprintf( ' %s → %s', $key, $display ) ); } } diff --git a/inc/Cli/Commands/EmailCommand.php b/inc/Cli/Commands/EmailCommand.php index 03209ab99..ccb2ecfaf 100644 --- a/inc/Cli/Commands/EmailCommand.php +++ b/inc/Cli/Commands/EmailCommand.php @@ -205,18 +205,18 @@ public function fetch( array $args, array $assoc_args ): void { foreach ( $items as $item ) { $meta = $item['metadata'] ?? array(); $rows[] = array( - 'uid' => $meta['uid'] ?? '', - 'from' => $meta['from'] ?? '', - 'from_name' => $meta['from_name'] ?? '', - 'to' => $meta['to'] ?? '', - 'subject' => $item['title'] ?? '', - 'date' => $meta['date'] ?? '', - 'seen' => ( $meta['seen'] ?? false ) ? 'Y' : 'N', - 'flagged' => ( $meta['flagged'] ?? false ) ? '*' : '', - 'size' => $meta['size'] ?? '', - 'attachments' => $meta['attachment_count'] ?? '', - 'message_id' => $meta['message_id'] ?? '', - 'in_reply_to' => $meta['in_reply_to'] ?? '', + 'uid' => $meta['uid'] ?? '', + 'from' => $meta['from'] ?? '', + 'from_name' => $meta['from_name'] ?? '', + 'to' => $meta['to'] ?? '', + 'subject' => $item['title'] ?? '', + 'date' => $meta['date'] ?? '', + 'seen' => ( $meta['seen'] ?? false ) ? 'Y' : 'N', + 'flagged' => ( $meta['flagged'] ?? false ) ? '*' : '', + 'size' => $meta['size'] ?? '', + 'attachments' => $meta['attachment_count'] ?? '', + 'message_id' => $meta['message_id'] ?? '', + 'in_reply_to' => $meta['in_reply_to'] ?? '', ); } diff --git a/inc/Cli/Commands/Flows/FlowsCommand.php b/inc/Cli/Commands/Flows/FlowsCommand.php index 7739a8808..3455bba51 100644 --- a/inc/Cli/Commands/Flows/FlowsCommand.php +++ b/inc/Cli/Commands/Flows/FlowsCommand.php @@ -338,7 +338,7 @@ public function __invoke( array $args, array $assoc_args ): void { // Use 'full' mode for single-flow detail views. $output_mode = $flow_id ? 'full' : 'list'; - $result = $ability->executeAbility( + $result = $ability->executeAbility( array_merge( $scoping, array( @@ -857,8 +857,8 @@ private function updateFlow( int $flow_id, array $assoc_args ): void { if ( null !== $handler_config ) { // --handler-config accepts handler-keyed JSON, e.g. {"reddit":{"subreddit":"test"}}. // Unwrap: the key is the handler slug, the value is the config. - $handler_slug = null; - $unwrapped_config = $handler_config; + $handler_slug = null; + $unwrapped_config = $handler_config; $handler_config_keys = array_keys( $handler_config ); // If the top-level keys look like handler slugs (single key wrapping a config object), @@ -954,8 +954,8 @@ private function extractConfigSummary( array $flow ): string { // Coordinates (location field with lat,lon). if ( ! empty( $hconfig['location'] ) && strpos( $hconfig['location'], ',' ) !== false ) { - $loc = $hconfig['location']; - $rad = $hconfig['radius'] ?? ''; + $loc = $hconfig['location']; + $rad = $hconfig['radius'] ?? ''; $parts[] = $loc . ( $rad ? " r={$rad}" : '' ); } @@ -966,8 +966,8 @@ private function extractConfigSummary( array $flow ): string { // Source URL — show domain only. if ( ! empty( $hconfig['source_url'] ) ) { - $host = wp_parse_url( $hconfig['source_url'], PHP_URL_HOST ); - $parts[] = $host ?: $hconfig['source_url']; + $host = wp_parse_url( $hconfig['source_url'], PHP_URL_HOST ); + $parts[] = $host ? $host : $hconfig['source_url']; } // Venue/source name. @@ -978,8 +978,8 @@ private function extractConfigSummary( array $flow ): string { // Feed URL — show domain only. $feed_url = $hconfig['feed_url'] ?? $hconfig['url'] ?? ''; if ( $feed_url && empty( $hconfig['source_url'] ) ) { - $host = wp_parse_url( $feed_url, PHP_URL_HOST ); - $parts[] = $host ?: $feed_url; + $host = wp_parse_url( $feed_url, PHP_URL_HOST ); + $parts[] = $host ? $host : $feed_url; } // Taxonomy term selections (any taxonomy_*_selection key). @@ -987,7 +987,7 @@ private function extractConfigSummary( array $flow ): string { if ( strpos( $key, 'taxonomy_' ) === 0 && strpos( $key, '_selection' ) !== false ) { if ( ! empty( $val ) && 'skip' !== $val && 'ai_decides' !== $val ) { $tax_name = str_replace( array( 'taxonomy_', '_selection' ), '', $key ); - $parts[] = "{$tax_name}={$val}"; + $parts[] = "{$tax_name}={$val}"; } } } @@ -1000,7 +1000,7 @@ private function extractConfigSummary( array $flow ): string { $summary = mb_substr( $summary, 0, 57 ) . '...'; } - return $summary ?: '—'; + return $summary ? $summary : '—'; } /** diff --git a/inc/Cli/Commands/PipelinesCommand.php b/inc/Cli/Commands/PipelinesCommand.php index 62825ef3d..fb7e78e42 100644 --- a/inc/Cli/Commands/PipelinesCommand.php +++ b/inc/Cli/Commands/PipelinesCommand.php @@ -417,7 +417,7 @@ private function extractPipelineLocation( array $flows ): string { } $summary = implode( ' | ', array_unique( $parts ) ); - return $summary ?: '—'; + return $summary ? $summary : '—'; } /** diff --git a/inc/Cli/Commands/ProcessedItemsCommand.php b/inc/Cli/Commands/ProcessedItemsCommand.php index f44208237..b9d3c2484 100644 --- a/inc/Cli/Commands/ProcessedItemsCommand.php +++ b/inc/Cli/Commands/ProcessedItemsCommand.php @@ -84,6 +84,7 @@ public function audit( array $args, array $assoc_args ): void { $where_sql = ! empty( $where_clauses ) ? 'WHERE ' . implode( ' AND ', $where_clauses ) : ''; // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared + // phpcs:disable WordPress.DB.PreparedSQL -- Table name from $wpdb->prefix, not user input. $results = $wpdb->get_results( $wpdb->prepare( "SELECT @@ -101,6 +102,7 @@ public function audit( array $args, array $assoc_args ): void { ), ARRAY_A ); + // phpcs:enable WordPress.DB.PreparedSQL if ( empty( $results ) ) { WP_CLI::log( 'No processed items found.' ); @@ -131,13 +133,13 @@ public function audit( array $args, array $assoc_args ): void { } $rows[] = array( - 'flow_id' => $flow_id, - 'flow_name' => $flow['flow_name'] ?? '?', - 'pipeline_id' => $flow['pipeline_id'] ?? '?', - 'handler' => $row['source_type'], - 'processed' => $processed, - 'first_seen' => $row['first_processed'], - 'last_seen' => $row['last_processed'], + 'flow_id' => $flow_id, + 'flow_name' => $flow['flow_name'] ?? '?', + 'pipeline_id' => $flow['pipeline_id'] ?? '?', + 'handler' => $row['source_type'], + 'processed' => $processed, + 'first_seen' => $row['first_processed'], + 'last_seen' => $row['last_processed'], ); } @@ -298,7 +300,7 @@ private function clear_with_filters( array $assoc_args ): void { $flow_patterns = array(); foreach ( $flows as $flow ) { - $flow_patterns[] = "flow_step_id LIKE %s"; + $flow_patterns[] = 'flow_step_id LIKE %s'; $values[] = '%_' . $flow['flow_id']; } $where_parts[] = '(' . implode( ' OR ', $flow_patterns ) . ')'; @@ -323,9 +325,11 @@ private function clear_with_filters( array $assoc_args ): void { // Count first. // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared + // phpcs:disable WordPress.DB.PreparedSQL -- Table name from $wpdb->prefix, not user input. $count = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM %i {$where_sql}", ...$values ) ); + // phpcs:enable WordPress.DB.PreparedSQL if ( 0 === $count ) { WP_CLI::log( 'No processed items match the criteria.' ); @@ -368,9 +372,11 @@ private function clear_with_filters( array $assoc_args ): void { // Delete. // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared + // phpcs:disable WordPress.DB.PreparedSQL -- Table name from $wpdb->prefix, not user input. $deleted = $wpdb->query( $wpdb->prepare( "DELETE FROM %i {$where_sql}", ...$values ) ); + // phpcs:enable WordPress.DB.PreparedSQL if ( false === $deleted ) { WP_CLI::error( 'Database error during deletion: ' . $wpdb->last_error ); diff --git a/inc/Cli/Commands/RetentionCommand.php b/inc/Cli/Commands/RetentionCommand.php index 11cdb2a6e..59ab633a2 100644 --- a/inc/Cli/Commands/RetentionCommand.php +++ b/inc/Cli/Commands/RetentionCommand.php @@ -69,7 +69,7 @@ public function show( array $args, array $assoc_args ): void { $policy_items = array(); foreach ( $policies as $domain => $policy ) { - $size_info = $sizes[ $domain ] ?? array(); + $size_info = $sizes[ $domain ] ?? array(); $policy_items[] = array( 'domain' => $domain, 'retention' => $policy['retention'], @@ -158,8 +158,8 @@ public function run( array $args, array $assoc_args ): void { } // 3. Logs. - $log_days = (int) apply_filters( 'datamachine_log_max_age_days', 7 ); - $count = $this->count_old_logs( $log_days ); + $log_days = (int) apply_filters( 'datamachine_log_max_age_days', 7 ); + $count = $this->count_old_logs( $log_days ); $results[] = array( 'domain' => 'Pipeline logs', 'threshold' => $log_days . ' days', @@ -186,8 +186,8 @@ public function run( array $args, array $assoc_args ): void { } // 5. Action Scheduler actions + logs. - $as_days = (int) apply_filters( 'datamachine_as_actions_max_age_days', 7 ); - $as_count = $this->count_old_as_actions( $as_days ); + $as_days = (int) apply_filters( 'datamachine_as_actions_max_age_days', 7 ); + $as_count = $this->count_old_as_actions( $as_days ); $results[] = array( 'domain' => 'AS actions + logs', 'threshold' => $as_days . ' days', @@ -286,6 +286,7 @@ private function get_table_sizes(): array { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching $results = $wpdb->get_results( // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared + // phpcs:disable WordPress.DB.PreparedSQL, WordPress.DB.PreparedSQLPlaceholders -- Table name from $wpdb->prefix, not user input. $wpdb->prepare( "SELECT table_name, table_rows, ROUND((data_length + index_length) / 1024 / 1024, 1) AS size_mb @@ -295,6 +296,7 @@ private function get_table_sizes(): array { ...$unique_tables ) ); + // phpcs:enable WordPress.DB.PreparedSQL, WordPress.DB.PreparedSQLPlaceholders $table_data = array(); if ( $results ) { @@ -351,6 +353,7 @@ private function count_old_as_actions( int $older_than_days ): int { $logs_table = $wpdb->prefix . 'actionscheduler_logs'; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching + // phpcs:disable WordPress.DB.PreparedSQL -- Table name from $wpdb->prefix, not user input. $actions_count = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$actions_table} @@ -359,8 +362,10 @@ private function count_old_as_actions( int $older_than_days ): int { $cutoff ) ); + // phpcs:enable WordPress.DB.PreparedSQL // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching + // phpcs:disable WordPress.DB.PreparedSQL -- Table name from $wpdb->prefix, not user input. $logs_count = (int) $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(*) FROM {$logs_table} l @@ -370,6 +375,7 @@ private function count_old_as_actions( int $older_than_days ): int { $cutoff ) ); + // phpcs:enable WordPress.DB.PreparedSQL return $actions_count + $logs_count; } diff --git a/inc/Cli/Commands/TestCommand.php b/inc/Cli/Commands/TestCommand.php index f7cc79314..c97b07246 100644 --- a/inc/Cli/Commands/TestCommand.php +++ b/inc/Cli/Commands/TestCommand.php @@ -140,7 +140,7 @@ private function listHandlers( array $assoc_args ): void { } $items[] = array( - 'slug' => $slug, + 'slug' => $slug, 'label' => $handler['label'] ?? '', 'type' => $handler_type, ); @@ -229,10 +229,10 @@ private function describeHandler( string $handler_slug, array $assoc_args ): voi * @param array $assoc_args Command arguments. */ private function runTest( ?string $handler_slug, array $assoc_args ): void { - $format = $assoc_args['format'] ?? 'table'; + $format = $assoc_args['format'] ?? 'table'; $config_json = $assoc_args['config'] ?? null; - $flow_id = isset( $assoc_args['flow'] ) ? (int) $assoc_args['flow'] : null; - $limit = (int) ( $assoc_args['limit'] ?? 5 ); + $flow_id = isset( $assoc_args['flow'] ) ? (int) $assoc_args['flow'] : null; + $limit = (int) ( $assoc_args['limit'] ?? 5 ); $config = array(); if ( $config_json ) { @@ -331,7 +331,7 @@ private function renderTableOutput( array $result ): void { WP_CLI::log( '' ); // Build table rows. - $items = array(); + $items = array(); $all_metadata_keys = array(); foreach ( $packets as $index => $packet ) { @@ -342,7 +342,7 @@ private function renderTableOutput( array $result ): void { // Extract domain from source_url. $source_display = ''; if ( $source_url ) { - $parsed = wp_parse_url( $source_url ); + $parsed = wp_parse_url( $source_url ); $source_display = $parsed['host'] ?? $source_url; } diff --git a/inc/Core/ActionScheduler/ActionsCleanup.php b/inc/Core/ActionScheduler/ActionsCleanup.php index d8ea4765f..efb4377f3 100644 --- a/inc/Core/ActionScheduler/ActionsCleanup.php +++ b/inc/Core/ActionScheduler/ActionsCleanup.php @@ -48,6 +48,7 @@ function () { // Delete AS log entries for old completed/failed/canceled actions first (FK-safe order). // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching + // phpcs:disable WordPress.DB.PreparedSQL -- Table name from $wpdb->prefix, not user input. $logs_deleted = $wpdb->query( $wpdb->prepare( "DELETE l FROM {$logs_table} l @@ -57,9 +58,11 @@ function () { $cutoff_datetime ) ); + // phpcs:enable WordPress.DB.PreparedSQL // Delete the completed/failed/canceled actions themselves. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching + // phpcs:disable WordPress.DB.PreparedSQL -- Table name from $wpdb->prefix, not user input. $actions_deleted = $wpdb->query( $wpdb->prepare( "DELETE FROM {$actions_table} @@ -68,6 +71,7 @@ function () { $cutoff_datetime ) ); + // phpcs:enable WordPress.DB.PreparedSQL $total_deleted = ( false !== $logs_deleted ? $logs_deleted : 0 ) + ( false !== $actions_deleted ? $actions_deleted : 0 ); diff --git a/inc/Core/Admin/FlowFormatter.php b/inc/Core/Admin/FlowFormatter.php index aba71fd72..712c8c720 100644 --- a/inc/Core/Admin/FlowFormatter.php +++ b/inc/Core/Admin/FlowFormatter.php @@ -29,7 +29,7 @@ class FlowFormatter { * Cached service instances to avoid re-creation per flow in batch formatting. */ private static ?HandlerAbilities $handler_abilities_cache = null; - private static ?object $settings_display_cache = null; + private static ?object $settings_display_cache = null; public static function format_flow_for_response( array $flow, ?array $latest_job = null, ?array $next_runs = null ): array { $flow_config = $flow['flow_config'] ?? array(); diff --git a/inc/Core/Auth/AgentAuthCallback.php b/inc/Core/Auth/AgentAuthCallback.php index 68edbe912..3640edfb2 100644 --- a/inc/Core/Auth/AgentAuthCallback.php +++ b/inc/Core/Auth/AgentAuthCallback.php @@ -169,6 +169,7 @@ public function handle_callback( \WP_REST_Request $request ) { * @return \WP_REST_Response */ public function list_external_tokens( \WP_REST_Request $request ): \WP_REST_Response { + unset( $request ); $tokens = get_option( self::OPTION_KEY, array() ); $result = array(); diff --git a/inc/Core/Auth/AgentAuthMiddleware.php b/inc/Core/Auth/AgentAuthMiddleware.php index 91e068d6f..6ecd7765a 100644 --- a/inc/Core/Auth/AgentAuthMiddleware.php +++ b/inc/Core/Auth/AgentAuthMiddleware.php @@ -42,6 +42,7 @@ class AgentAuthMiddleware { * Register the authentication filter. */ public function __construct() { + add_action('rest_api_init', array( $this, 'rest_api_init' )); add_filter( 'rest_authentication_errors', array( $this, 'authenticate' ), 90 ); } @@ -147,11 +148,11 @@ public function authenticate( $result ) { 'debug', 'Agent auth: token authenticated', array( - 'agent_id' => $agent_id, - 'agent_slug' => $agent['agent_slug'], - 'owner_id' => $owner_id, - 'token_id' => $token_id, - 'token_label' => $token_record['label'] ?? '', + 'agent_id' => $agent_id, + 'agent_slug' => $agent['agent_slug'], + 'owner_id' => $owner_id, + 'token_id' => $token_id, + 'token_label' => $token_record['label'] ?? '', 'has_cap_restrictions' => null !== $token_capabilities, ) ); diff --git a/inc/Core/Auth/AgentAuthorize.php b/inc/Core/Auth/AgentAuthorize.php index 53bf34eb0..e247d163b 100644 --- a/inc/Core/Auth/AgentAuthorize.php +++ b/inc/Core/Auth/AgentAuthorize.php @@ -190,7 +190,7 @@ public function handle_authorize_post( \WP_REST_Request $request ) { $nonce = $request->get_param( '_wpnonce' ); // Look up agent for redirect URI validation. - $agents_repo = new Agents(); + $agents_repo = new Agents(); $agent_for_uri = $agents_repo->get_by_slug( $agent_slug ); if ( $agent_for_uri ) { @@ -256,11 +256,11 @@ public function handle_authorize_post( \WP_REST_Request $request ) { 'info', 'Agent token issued via authorize flow', array( - 'agent_id' => (int) $agent['agent_id'], - 'agent_slug' => $agent['agent_slug'], - 'user_id' => $user_id, - 'token_id' => $result['token_id'], - 'label' => $token_label, + 'agent_id' => (int) $agent['agent_id'], + 'agent_slug' => $agent['agent_slug'], + 'user_id' => $user_id, + 'token_id' => $result['token_id'], + 'label' => $token_label, ) ); @@ -410,7 +410,7 @@ private function render_consent_screen( array $agent, string $redirect_uri, stri $owner_name = $owner ? esc_html( $owner->display_name ) : 'Unknown'; $user_name = esc_html( $user->display_name ); - $parsed_uri = wp_parse_url( $redirect_uri ); + $parsed_uri = wp_parse_url( $redirect_uri ); $uri_display = esc_html( ( $parsed_uri['host'] ?? '' ) . ( isset( $parsed_uri['port'] ) ? ':' . $parsed_uri['port'] : '' ) ); header( 'Content-Type: text/html; charset=utf-8' ); diff --git a/inc/Core/Database/Agents/AgentAccess.php b/inc/Core/Database/Agents/AgentAccess.php index aa57c1560..d75c45fe9 100644 --- a/inc/Core/Database/Agents/AgentAccess.php +++ b/inc/Core/Database/Agents/AgentAccess.php @@ -12,6 +12,7 @@ namespace DataMachine\Core\Database\Agents; use DataMachine\Core\Database\BaseRepository; +use DataMachine\Core\Database\Agents\Agents; if ( ! defined( 'ABSPATH' ) ) { exit; diff --git a/inc/Core/Database/Agents/AgentTokens.php b/inc/Core/Database/Agents/AgentTokens.php index e22d98933..8030c3194 100644 --- a/inc/Core/Database/Agents/AgentTokens.php +++ b/inc/Core/Database/Agents/AgentTokens.php @@ -16,6 +16,7 @@ namespace DataMachine\Core\Database\Agents; use DataMachine\Core\Database\BaseRepository; +use DataMachine\Core\Database\Agents\Agents; if ( ! defined( 'ABSPATH' ) ) { exit; diff --git a/inc/Core/Database/Logs/LogRepository.php b/inc/Core/Database/Logs/LogRepository.php index 8387886d7..e837ce3ed 100644 --- a/inc/Core/Database/Logs/LogRepository.php +++ b/inc/Core/Database/Logs/LogRepository.php @@ -198,7 +198,7 @@ public function get_logs( array $filters = array() ): array { // Fetch items. $query_params = array_merge( $params, array( $per_page, $offset ) ); // phpcs:disable WordPress.DB.PreparedSQLPlaceholders -- Dynamic query construction with safe values. - $items = $this->wpdb->get_results( + $items = $this->wpdb->get_results( $this->wpdb->prepare( "SELECT * FROM {$this->table_name} WHERE {$where_sql} ORDER BY created_at DESC, id DESC LIMIT %d OFFSET %d", ...$query_params @@ -305,10 +305,12 @@ public function get_metadata( ?int $agent_id = null ): array { ); // phpcs:enable WordPress.DB.PreparedSQLPlaceholders } else { + // phpcs:disable WordPress.DB.PreparedSQL -- Table name from $wpdb->prefix, not user input. $row = $this->wpdb->get_row( "SELECT COUNT(*) AS total_entries, MIN(created_at) AS oldest, MAX(created_at) AS newest FROM {$this->table_name} WHERE {$where}", ARRAY_A ); + // phpcs:enable WordPress.DB.PreparedSQL } // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared @@ -346,10 +348,12 @@ public function get_level_counts( ?int $agent_id = null ): array { ); // phpcs:enable WordPress.DB.PreparedSQLPlaceholders } else { + // phpcs:disable WordPress.DB.PreparedSQL -- Table name from $wpdb->prefix, not user input. $rows = $this->wpdb->get_results( "SELECT level, COUNT(*) AS cnt FROM {$this->table_name} WHERE {$where} GROUP BY level", ARRAY_A ); + // phpcs:enable WordPress.DB.PreparedSQL } // phpcs:enable WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared diff --git a/inc/Core/Database/PostIdentityIndex/PostIdentityIndex.php b/inc/Core/Database/PostIdentityIndex/PostIdentityIndex.php index 1e04dd58f..636fa8ed5 100644 --- a/inc/Core/Database/PostIdentityIndex/PostIdentityIndex.php +++ b/inc/Core/Database/PostIdentityIndex/PostIdentityIndex.php @@ -123,6 +123,7 @@ public function upsert( int $post_id, array $fields ): bool { $placeholders = implode( ', ', $formats ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching + // phpcs:disable WordPress.DB.PreparedSQL, WordPress.DB.PreparedSQLPlaceholders -- Table name from $wpdb->prefix, not user input. $result = $this->wpdb->query( // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared $this->wpdb->prepare( @@ -130,6 +131,7 @@ public function upsert( int $post_id, array $fields ): bool { ...array_values( $data ) ) ); + // phpcs:enable WordPress.DB.PreparedSQL, WordPress.DB.PreparedSQLPlaceholders if ( false === $result ) { $this->log_db_error( 'PostIdentityIndex::upsert', array( 'post_id' => $post_id ) ); @@ -181,6 +183,7 @@ public function find_by_date_and_venue( string $event_date, ?int $venue_term_id if ( null !== $venue_term_id && $venue_term_id > 0 ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared + // phpcs:disable WordPress.DB.PreparedSQL -- Table name from $wpdb->prefix, not user input. return $this->wpdb->get_results( $this->wpdb->prepare( "SELECT * FROM {$this->table_name} WHERE event_date = %s AND venue_term_id = %d LIMIT %d", @@ -189,10 +192,20 @@ public function find_by_date_and_venue( string $event_date, ?int $venue_term_id $limit ), ARRAY_A - ) ?: array(); + ) ? $this->wpdb->get_results( + $this->wpdb->prepare( + "SELECT * FROM {$this->table_name} WHERE event_date = %s AND venue_term_id = %d LIMIT %d", + $event_date, + $venue_term_id, + $limit + ), + ARRAY_A + ) : array(); + // phpcs:enable WordPress.DB.PreparedSQL } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared + // phpcs:disable WordPress.DB.PreparedSQL -- Table name from $wpdb->prefix, not user input. return $this->wpdb->get_results( $this->wpdb->prepare( "SELECT * FROM {$this->table_name} WHERE event_date = %s LIMIT %d", @@ -200,7 +213,15 @@ public function find_by_date_and_venue( string $event_date, ?int $venue_term_id $limit ), ARRAY_A - ) ?: array(); + ) ? $this->wpdb->get_results( + $this->wpdb->prepare( + "SELECT * FROM {$this->table_name} WHERE event_date = %s LIMIT %d", + $event_date, + $limit + ), + ARRAY_A + ) : array(); + // phpcs:enable WordPress.DB.PreparedSQL } /** @@ -218,6 +239,7 @@ public function find_by_date_and_title_hash( string $event_date, string $title_h } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared + // phpcs:disable WordPress.DB.PreparedSQL -- Table name from $wpdb->prefix, not user input. $row = $this->wpdb->get_row( $this->wpdb->prepare( "SELECT * FROM {$this->table_name} WHERE event_date = %s AND title_hash = %s LIMIT 1", @@ -226,8 +248,9 @@ public function find_by_date_and_title_hash( string $event_date, string $title_h ), ARRAY_A ); + // phpcs:enable WordPress.DB.PreparedSQL - return $row ?: null; + return $row ? $row : null; } /** @@ -246,6 +269,7 @@ public function find_by_ticket_url_and_date( string $ticket_url, string $event_d } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared + // phpcs:disable WordPress.DB.PreparedSQL -- Table name from $wpdb->prefix, not user input. $row = $this->wpdb->get_row( $this->wpdb->prepare( "SELECT * FROM {$this->table_name} WHERE ticket_url = %s AND event_date = %s LIMIT 1", @@ -254,8 +278,9 @@ public function find_by_ticket_url_and_date( string $ticket_url, string $event_d ), ARRAY_A ); + // phpcs:enable WordPress.DB.PreparedSQL - return $row ?: null; + return $row ? $row : null; } /** @@ -272,6 +297,7 @@ public function find_by_source_url( string $source_url ): ?array { } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared + // phpcs:disable WordPress.DB.PreparedSQL -- Table name from $wpdb->prefix, not user input. $row = $this->wpdb->get_row( $this->wpdb->prepare( "SELECT * FROM {$this->table_name} WHERE source_url = %s LIMIT 1", @@ -279,8 +305,9 @@ public function find_by_source_url( string $source_url ): ?array { ), ARRAY_A ); + // phpcs:enable WordPress.DB.PreparedSQL - return $row ?: null; + return $row ? $row : null; } /** @@ -298,6 +325,7 @@ public function find_with_ticket_url_on_date( string $event_date, int $limit = 5 } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared + // phpcs:disable WordPress.DB.PreparedSQL -- Table name from $wpdb->prefix, not user input. return $this->wpdb->get_results( $this->wpdb->prepare( "SELECT * FROM {$this->table_name} WHERE event_date = %s AND ticket_url IS NOT NULL AND ticket_url != '' LIMIT %d", @@ -305,7 +333,15 @@ public function find_with_ticket_url_on_date( string $event_date, int $limit = 5 $limit ), ARRAY_A - ) ?: array(); + ) ? $this->wpdb->get_results( + $this->wpdb->prepare( + "SELECT * FROM {$this->table_name} WHERE event_date = %s AND ticket_url IS NOT NULL AND ticket_url != '' LIMIT %d", + $event_date, + $limit + ), + ARRAY_A + ) : array(); + // phpcs:enable WordPress.DB.PreparedSQL } /** @@ -323,6 +359,7 @@ public function find_by_date( string $event_date, int $limit = 50 ): array { } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared + // phpcs:disable WordPress.DB.PreparedSQL -- Table name from $wpdb->prefix, not user input. return $this->wpdb->get_results( $this->wpdb->prepare( "SELECT * FROM {$this->table_name} WHERE event_date = %s LIMIT %d", @@ -330,7 +367,15 @@ public function find_by_date( string $event_date, int $limit = 50 ): array { $limit ), ARRAY_A - ) ?: array(); + ) ? $this->wpdb->get_results( + $this->wpdb->prepare( + "SELECT * FROM {$this->table_name} WHERE event_date = %s LIMIT %d", + $event_date, + $limit + ), + ARRAY_A + ) : array(); + // phpcs:enable WordPress.DB.PreparedSQL } // ----------------------------------------------------------------------- @@ -366,6 +411,7 @@ public function find_missing_post_ids( string $post_type, int $limit = 500, int // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared $results = $wpdb->get_col( $wpdb->prepare( + // phpcs:disable WordPress.DB.PreparedSQL -- Table name from $wpdb->prefix, not user input. "SELECT p.ID FROM {$wpdb->posts} p LEFT JOIN {$this->table_name} idx ON p.ID = idx.post_id WHERE p.post_type = %s @@ -378,8 +424,9 @@ public function find_missing_post_ids( string $post_type, int $limit = 500, int $offset ) ); + // phpcs:enable WordPress.DB.PreparedSQL - return array_map( 'intval', $results ?: array() ); + return array_map( 'intval', $results ? $results : array() ); } /** @@ -390,12 +437,14 @@ public function find_missing_post_ids( string $post_type, int $limit = 500, int */ public function delete_by_post_type( string $post_type ): int { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared + // phpcs:disable WordPress.DB.PreparedSQL -- Table name from $wpdb->prefix, not user input. $result = $this->wpdb->query( $this->wpdb->prepare( "DELETE FROM {$this->table_name} WHERE post_type = %s", $post_type ) ); + // phpcs:enable WordPress.DB.PreparedSQL return false === $result ? 0 : $result; } diff --git a/inc/Core/OAuth/OAuth2Handler.php b/inc/Core/OAuth/OAuth2Handler.php index f8ed74aac..599c19490 100644 --- a/inc/Core/OAuth/OAuth2Handler.php +++ b/inc/Core/OAuth/OAuth2Handler.php @@ -13,6 +13,8 @@ namespace DataMachine\Core\OAuth; use DataMachine\Core\HttpClient; +use DataMachine\Core\OAuth\OAuth1Handler; +use DataMachine\Core\OAuth\OAuth1Handler; if ( ! defined( 'WPINC' ) ) { die; diff --git a/inc/Core/Steps/Fetch/Handlers/WordPress/WordPress.php b/inc/Core/Steps/Fetch/Handlers/WordPress/WordPress.php index 7e7bb74df..8eee80bf6 100644 --- a/inc/Core/Steps/Fetch/Handlers/WordPress/WordPress.php +++ b/inc/Core/Steps/Fetch/Handlers/WordPress/WordPress.php @@ -95,15 +95,15 @@ private function fetch_single_post( string $source_url, ExecutionContext $contex 'title' => $data['title'], 'content' => $data['content'], 'metadata' => array( - 'source_type' => 'wordpress_local', - 'item_identifier' => (string) $post_id, - 'original_id' => $post_id, - 'original_title' => $data['title'], - 'original_date_gmt' => $data['publish_date'], - 'post_type' => $data['post_type'], - 'post_status' => $data['post_status'], - 'site_name' => $data['site_name'], - '_engine_data' => array( + 'source_type' => 'wordpress_local', + 'item_identifier' => (string) $post_id, + 'original_id' => $post_id, + 'original_title' => $data['title'], + 'original_date_gmt' => $data['publish_date'], + 'post_type' => $data['post_type'], + 'post_status' => $data['post_status'], + 'site_name' => $data['site_name'], + '_engine_data' => array( 'source_url' => $data['permalink'] ?? '', 'image_file_path' => $image_file_path, ), diff --git a/inc/Core/Steps/Fetch/Handlers/WordPressMedia/WordPressMedia.php b/inc/Core/Steps/Fetch/Handlers/WordPressMedia/WordPressMedia.php index 62271d0b8..16ecfafb7 100644 --- a/inc/Core/Steps/Fetch/Handlers/WordPressMedia/WordPressMedia.php +++ b/inc/Core/Steps/Fetch/Handlers/WordPressMedia/WordPressMedia.php @@ -22,6 +22,7 @@ class WordPressMedia extends FetchHandler { use HandlerRegistrationTrait; + use DataMachine\Core\Steps\Fetch\Handlers\Files\Files; public function __construct() { parent::__construct( 'wordpress_media' ); diff --git a/inc/Core/Steps/Fetch/Tools/SkipItemTool.php b/inc/Core/Steps/Fetch/Tools/SkipItemTool.php index 0dfda8849..d3a56e73c 100644 --- a/inc/Core/Steps/Fetch/Tools/SkipItemTool.php +++ b/inc/Core/Steps/Fetch/Tools/SkipItemTool.php @@ -65,7 +65,7 @@ public function handle_tool_call( array $parameters, array $tool_def = array() ) // Get item identifier and source type from engine data (set by fetch handler) $item_identifier = $engine->get( 'item_identifier' ); $source_type = $engine->get( 'source_type' ); - $flow_step_id = $parameters['flow_step_id'] ?? $engine->get( 'flow_step_id' ); + $flow_step_id = $parameters['flow_step_id'] ?? $engine->get( 'flow_step_id' ); // Mark item as processed so it won't be refetched if ( $flow_step_id && $item_identifier && $source_type ) { @@ -82,11 +82,11 @@ public function handle_tool_call( array $parameters, array $tool_def = array() ) 'info', 'SkipItemTool: Item marked as processed (skipped)', array( - 'job_id' => $job_id, - 'flow_step_id' => $flow_step_id, - 'item_identifier' => $item_identifier, - 'source_type' => $source_type, - 'reason' => $reason, + 'job_id' => $job_id, + 'flow_step_id' => $flow_step_id, + 'item_identifier' => $item_identifier, + 'source_type' => $source_type, + 'reason' => $reason, ) ); } else { @@ -95,11 +95,11 @@ public function handle_tool_call( array $parameters, array $tool_def = array() ) 'warning', 'SkipItemTool: Could not mark item as processed - missing identifiers', array( - 'job_id' => $job_id, - 'flow_step_id' => $flow_step_id, - 'item_identifier' => $item_identifier, - 'source_type' => $source_type, - 'reason' => $reason, + 'job_id' => $job_id, + 'flow_step_id' => $flow_step_id, + 'item_identifier' => $item_identifier, + 'source_type' => $source_type, + 'reason' => $reason, ) ); } @@ -120,11 +120,11 @@ public function handle_tool_call( array $parameters, array $tool_def = array() ) ); return array( - 'success' => true, - 'message' => "Item skipped: {$reason}", - 'status' => $status->toString(), - 'item_identifier' => $item_identifier, - 'tool_name' => 'skip_item', + 'success' => true, + 'message' => "Item skipped: {$reason}", + 'status' => $status->toString(), + 'item_identifier' => $item_identifier, + 'tool_name' => 'skip_item', ); } } diff --git a/inc/Core/Steps/Publish/Handlers/PublishHandler.php b/inc/Core/Steps/Publish/Handlers/PublishHandler.php index 07bb0ad95..86579f5f7 100644 --- a/inc/Core/Steps/Publish/Handlers/PublishHandler.php +++ b/inc/Core/Steps/Publish/Handlers/PublishHandler.php @@ -18,6 +18,7 @@ use DataMachine\Core\EngineData; use DataMachine\Core\HttpClient; use DataMachine\Core\WordPress\PostTracking; +use DataMachine\Core\Steps\Fetch\Handlers\FetchHandler; defined( 'ABSPATH' ) || exit; diff --git a/inc/Core/Steps/Update/UpdateStep.php b/inc/Core/Steps/Update/UpdateStep.php index 213ae4e32..49e285169 100644 --- a/inc/Core/Steps/Update/UpdateStep.php +++ b/inc/Core/Steps/Update/UpdateStep.php @@ -367,5 +367,4 @@ private function findSuccessfulHandlerResultsBySlug( array $handler_slugs ): arr return $results; } - } diff --git a/inc/Engine/AI/System/SystemAgentServiceProvider.php b/inc/Engine/AI/System/SystemAgentServiceProvider.php index 541e67e97..27408e350 100644 --- a/inc/Engine/AI/System/SystemAgentServiceProvider.php +++ b/inc/Engine/AI/System/SystemAgentServiceProvider.php @@ -60,9 +60,9 @@ private function registerTaskHandlers(): void { * @return array Task handlers including built-in ones. */ public function getBuiltInTasks( array $tasks ): array { - $tasks['image_generation'] = ImageGenerationTask::class; - $tasks['image_optimization'] = ImageOptimizationTask::class; - $tasks['alt_text_generation'] = AltTextTask::class; + $tasks['image_generation'] = ImageGenerationTask::class; + $tasks['image_optimization'] = ImageOptimizationTask::class; + $tasks['alt_text_generation'] = AltTextTask::class; // github_create_issue moved to data-machine-code extension. $tasks['internal_linking'] = InternalLinkingTask::class; $tasks['daily_memory_generation'] = DailyMemoryTask::class; diff --git a/inc/Engine/AI/System/Tasks/AltTextTask.php b/inc/Engine/AI/System/Tasks/AltTextTask.php index c8cd94c7c..d4b6a4e14 100644 --- a/inc/Engine/AI/System/Tasks/AltTextTask.php +++ b/inc/Engine/AI/System/Tasks/AltTextTask.php @@ -16,6 +16,7 @@ use DataMachine\Core\PluginSettings; use DataMachine\Engine\AI\RequestBuilder; +use DataMachine\Engine\AI\System\Tasks\Traits\HasSupportsUndo; class AltTextTask extends SystemTask { diff --git a/inc/Engine/AI/System/Tasks/ImageGenerationTask.php b/inc/Engine/AI/System/Tasks/ImageGenerationTask.php index 199255001..f017c32b0 100644 --- a/inc/Engine/AI/System/Tasks/ImageGenerationTask.php +++ b/inc/Engine/AI/System/Tasks/ImageGenerationTask.php @@ -14,6 +14,7 @@ defined( 'ABSPATH' ) || exit; use DataMachine\Core\HttpClient; +use DataMachine\Engine\AI\System\Tasks\Traits\HasSupportsUndo; class ImageGenerationTask extends SystemTask { diff --git a/inc/Engine/AI/System/Tasks/ImageOptimizationTask.php b/inc/Engine/AI/System/Tasks/ImageOptimizationTask.php index 8a07d5830..e48b812b0 100644 --- a/inc/Engine/AI/System/Tasks/ImageOptimizationTask.php +++ b/inc/Engine/AI/System/Tasks/ImageOptimizationTask.php @@ -12,6 +12,7 @@ * @since 0.42.0 */ +use DataMachine\Engine\AI\System\Tasks\Traits\HasSupportsUndo; namespace DataMachine\Engine\AI\System\Tasks; defined( 'ABSPATH' ) || exit; @@ -71,7 +72,7 @@ public function execute( int $jobId, array $params ): void { $file_path = get_attached_file( $attachment_id ); if ( empty( $file_path ) || ! file_exists( $file_path ) ) { - $this->failJob( $jobId, 'Attachment file not found: ' . ( $file_path ?: 'empty path' ) ); + $this->failJob( $jobId, 'Attachment file not found: ' . ( $file_path ? $file_path : 'empty path' ) ); return; } @@ -90,19 +91,19 @@ public function execute( int $jobId, array $params ): void { $compress_result = $this->compressImage( $file_path, $mime_type, $quality, $attachment_id ); if ( $compress_result['success'] ) { - $results['compressed'] = true; - $results['new_size'] = $compress_result['new_size']; - $results['savings'] = $original_size - $compress_result['new_size']; - $results['savings_pct'] = $original_size > 0 ? round( ( $results['savings'] / $original_size ) * 100, 1 ) : 0; + $results['compressed'] = true; + $results['new_size'] = $compress_result['new_size']; + $results['savings'] = $original_size - $compress_result['new_size']; + $results['savings_pct'] = $original_size > 0 ? round( ( $results['savings'] / $original_size ) * 100, 1 ) : 0; $effects[] = array( - 'type' => 'attachment_file_modified', - 'target' => array( + 'type' => 'attachment_file_modified', + 'target' => array( 'attachment_id' => $attachment_id, 'file_path' => $file_path, ), - 'previous_size' => $original_size, - 'new_size' => $compress_result['new_size'], + 'previous_size' => $original_size, + 'new_size' => $compress_result['new_size'], ); // Update attachment metadata with new file size. diff --git a/inc/Engine/AI/System/Tasks/InternalLinkingTask.php b/inc/Engine/AI/System/Tasks/InternalLinkingTask.php index 57571175b..f9c7b9987 100644 --- a/inc/Engine/AI/System/Tasks/InternalLinkingTask.php +++ b/inc/Engine/AI/System/Tasks/InternalLinkingTask.php @@ -18,6 +18,7 @@ use DataMachine\Abilities\Content\ReplacePostBlocksAbility; use DataMachine\Core\PluginSettings; use DataMachine\Engine\AI\RequestBuilder; +use DataMachine\Engine\AI\System\Tasks\Traits\HasSupportsUndo; class InternalLinkingTask extends SystemTask { diff --git a/inc/Engine/AI/System/Tasks/MetaDescriptionTask.php b/inc/Engine/AI/System/Tasks/MetaDescriptionTask.php index d8c7a79e4..b68eadb1c 100644 --- a/inc/Engine/AI/System/Tasks/MetaDescriptionTask.php +++ b/inc/Engine/AI/System/Tasks/MetaDescriptionTask.php @@ -20,6 +20,7 @@ use DataMachine\Core\PluginSettings; use DataMachine\Engine\AI\RequestBuilder; +use DataMachine\Engine\AI\System\Tasks\Traits\HasSupportsUndo; class MetaDescriptionTask extends SystemTask { diff --git a/inc/Engine/AI/Tools/Global/AgentDailyMemory.php b/inc/Engine/AI/Tools/Global/AgentDailyMemory.php index 21d9d9265..23144d415 100644 --- a/inc/Engine/AI/Tools/Global/AgentDailyMemory.php +++ b/inc/Engine/AI/Tools/Global/AgentDailyMemory.php @@ -17,6 +17,8 @@ use DataMachine\Engine\AI\Tools\BaseTool; use DataMachine\Core\FilesRepository\DirectoryManager; +use DataMachine\Engine\AI\Tools\Global\Traits\HasIsConfigured; +use DataMachine\Engine\AI\Tools\Global\AgentMemory; class AgentDailyMemory extends BaseTool { diff --git a/inc/Engine/AI/Tools/Global/AgentMemory.php b/inc/Engine/AI/Tools/Global/AgentMemory.php index 708a744ee..875406b5c 100644 --- a/inc/Engine/AI/Tools/Global/AgentMemory.php +++ b/inc/Engine/AI/Tools/Global/AgentMemory.php @@ -16,6 +16,7 @@ use DataMachine\Engine\AI\Tools\BaseTool; use DataMachine\Core\FilesRepository\DirectoryManager; +use DataMachine\Engine\AI\Tools\Global\Traits\HasIsConfigured; class AgentMemory extends BaseTool { diff --git a/inc/Engine/AI/Tools/Global/AmazonAffiliateLink.php b/inc/Engine/AI/Tools/Global/AmazonAffiliateLink.php index 138fb84c4..90e48734b 100644 --- a/inc/Engine/AI/Tools/Global/AmazonAffiliateLink.php +++ b/inc/Engine/AI/Tools/Global/AmazonAffiliateLink.php @@ -15,6 +15,7 @@ use DataMachine\Core\HttpClient; use DataMachine\Engine\AI\Tools\BaseTool; +use DataMachine\Abilities\Analytics\Traits\HasGetConfig; class AmazonAffiliateLink extends BaseTool { diff --git a/inc/Engine/AI/Tools/Global/InternalLinkAudit.php b/inc/Engine/AI/Tools/Global/InternalLinkAudit.php index 5392443d0..c1b09b3cc 100644 --- a/inc/Engine/AI/Tools/Global/InternalLinkAudit.php +++ b/inc/Engine/AI/Tools/Global/InternalLinkAudit.php @@ -19,6 +19,7 @@ defined( 'ABSPATH' ) || exit; use DataMachine\Engine\AI\Tools\BaseTool; +use DataMachine\Engine\AI\Tools\Global\Traits\HasIsConfigured; class InternalLinkAudit extends BaseTool { diff --git a/inc/Engine/AI/Tools/Global/LocalSearch.php b/inc/Engine/AI/Tools/Global/LocalSearch.php index 6241d1faa..cc27ffead 100644 --- a/inc/Engine/AI/Tools/Global/LocalSearch.php +++ b/inc/Engine/AI/Tools/Global/LocalSearch.php @@ -13,6 +13,7 @@ defined( 'ABSPATH' ) || exit; use DataMachine\Engine\AI\Tools\BaseTool; +use DataMachine\Engine\AI\Tools\Global\Traits\HasIsConfigured; class LocalSearch extends BaseTool { diff --git a/inc/Engine/AI/Tools/Global/WebFetch.php b/inc/Engine/AI/Tools/Global/WebFetch.php index e008665ad..8faaa0f07 100644 --- a/inc/Engine/AI/Tools/Global/WebFetch.php +++ b/inc/Engine/AI/Tools/Global/WebFetch.php @@ -10,6 +10,7 @@ use DataMachine\Core\HttpClient; use DataMachine\Engine\AI\Tools\BaseTool; +use DataMachine\Engine\AI\Tools\Global\Traits\HasIsConfigured; class WebFetch extends BaseTool { diff --git a/inc/Engine/AI/Tools/Global/WordPressPostReader.php b/inc/Engine/AI/Tools/Global/WordPressPostReader.php index a15d3a8d4..aec6f5b8e 100644 --- a/inc/Engine/AI/Tools/Global/WordPressPostReader.php +++ b/inc/Engine/AI/Tools/Global/WordPressPostReader.php @@ -13,6 +13,7 @@ use DataMachine\Abilities\Fetch\GetWordPressPostAbility; use DataMachine\Engine\AI\Tools\BaseTool; +use DataMachine\Engine\AI\Tools\Global\Traits\HasIsConfigured; class WordPressPostReader extends BaseTool { diff --git a/inc/Engine/AI/Tools/ToolPolicyResolver.php b/inc/Engine/AI/Tools/ToolPolicyResolver.php index 1c081a07b..a9f5e7c44 100644 --- a/inc/Engine/AI/Tools/ToolPolicyResolver.php +++ b/inc/Engine/AI/Tools/ToolPolicyResolver.php @@ -88,9 +88,23 @@ public function resolve( array $context ): array { $deny = $context['deny'] ?? array(); $agent_id = isset( $context['agent_id'] ) ? (int) $context['agent_id'] : 0; - // 0. Baseline gate: if the acting user lacks datamachine_use_tools, return no tools. - // Pipeline and system contexts bypass this (they run as admin/cron). - if ( self::CONTEXT_CHAT === $context_type && ! PermissionHelper::can( 'use_tools' ) ) { + // 0. Optional legacy baseline gate. + // + // Historically chat tool resolution short-circuited to zero tools when the + // acting user lacked datamachine_use_tools. That made chat tool access an + // all-or-nothing switch and blocked lower-privilege tool tiers for regular + // authenticated users. + // + // Going forward, chat tool visibility should be resolved per-tool via: + // - linked ability permission callbacks + // - explicit access_level metadata + // - agent tool policy + // + // The legacy behavior is still available behind a filter for installs that + // want to preserve the coarse gate during migration. + $require_use_tools_for_chat = apply_filters( 'datamachine_require_use_tools_for_chat_tools', false, $context ); + + if ( self::CONTEXT_CHAT === $context_type && $require_use_tools_for_chat && ! PermissionHelper::can( 'use_tools' ) ) { return array(); } diff --git a/inc/migrations.php b/inc/migrations.php index 3467b6a47..f285da215 100644 --- a/inc/migrations.php +++ b/inc/migrations.php @@ -404,9 +404,9 @@ function datamachine_get_site_scaffold_content(): string { $post_types = get_post_types( array( 'public' => true ), 'objects' ); $post_type_lines = array(); foreach ( $post_types as $pt ) { - $count = wp_count_posts( $pt->name ); - $published = isset( $count->publish ) ? (int) $count->publish : 0; - $hier = $pt->hierarchical ? 'hierarchical' : 'flat'; + $count = wp_count_posts( $pt->name ); + $published = isset( $count->publish ) ? (int) $count->publish : 0; + $hier = $pt->hierarchical ? 'hierarchical' : 'flat'; $post_type_lines[] = sprintf( '| %s | %s | %d | %s |', $pt->label, $pt->name, $published, $hier ); } @@ -421,8 +421,8 @@ function datamachine_get_site_scaffold_content(): string { if ( is_wp_error( $term_count ) ) { $term_count = 0; } - $hier = $tax->hierarchical ? 'hierarchical' : 'flat'; - $associated = implode( ', ', $tax->object_type ?? array() ); + $hier = $tax->hierarchical ? 'hierarchical' : 'flat'; + $associated = implode( ', ', $tax->object_type ?? array() ); $taxonomy_lines[] = sprintf( '| %s | %s | %d | %s | %s |', $tax->label, $tax->name, (int) $term_count, $hier, $associated ); } @@ -462,21 +462,25 @@ function datamachine_get_site_scaffold_content(): string { } // --- User roles --- - $wp_roles = wp_roles(); - $role_names = $wp_roles->get_names(); - $default_roles = array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' ); - $custom_roles = array_diff( array_keys( $role_names ), $default_roles ); - $role_lines = array(); + $wp_roles = wp_roles(); + $role_names = $wp_roles->get_names(); + $default_roles = array( 'administrator', 'editor', 'author', 'contributor', 'subscriber' ); + $custom_roles = array_diff( array_keys( $role_names ), $default_roles ); + $role_lines = array(); foreach ( $role_names as $slug => $name ) { - $user_count = count( get_users( array( 'role' => $slug, 'fields' => 'ID', 'number' => 1 ) ) ); + $user_count = count( get_users( array( + 'role' => $slug, + 'fields' => 'ID', + 'number' => 1, + ) ) ); $is_custom = in_array( $slug, $custom_roles, true ) ? ' (custom)' : ''; $role_lines[] = sprintf( '- %s (`%s`)%s', translate_user_role( $name ), $slug, $is_custom ); } // --- REST API namespaces (custom only) --- - $rest_namespaces = array(); - $builtin_prefixes = array( 'wp/', 'oembed/', 'wp-site-health/' ); + $rest_namespaces = array(); + $builtin_prefixes = array( 'wp/', 'oembed/', 'wp-site-health/' ); if ( function_exists( 'rest_get_server' ) && did_action( 'rest_api_init' ) ) { $routes = rest_get_server()->get_namespaces(); @@ -1088,8 +1092,14 @@ function datamachine_ensure_default_memory_files() { $default_user_id = \DataMachine\Core\FilesRepository\DirectoryManager::get_default_agent_user_id(); - $ability->execute( array( 'layer' => 'agent', 'user_id' => $default_user_id ) ); - $ability->execute( array( 'layer' => 'user', 'user_id' => $default_user_id ) ); + $ability->execute( array( + 'layer' => 'agent', + 'user_id' => $default_user_id, + ) ); + $ability->execute( array( + 'layer' => 'user', + 'user_id' => $default_user_id, + ) ); // Scaffold default context memory files (contexts/{context}.md). datamachine_ensure_default_context_files( $default_user_id ); @@ -1106,7 +1116,7 @@ function datamachine_ensure_default_memory_files() { * @param int $user_id Default agent user ID. */ function datamachine_ensure_default_context_files( int $user_id ): void { - $dm = new \DataMachine\Core\FilesRepository\DirectoryManager(); + $dm = new \DataMachine\Core\FilesRepository\DirectoryManager(); $contexts_dir = $dm->get_contexts_directory( array( 'user_id' => $user_id ) ); if ( ! $dm->ensure_directory_exists( $contexts_dir ) ) { @@ -1431,6 +1441,7 @@ function datamachine_scaffold_soul_content( string $content, string $filename, a * @return string */ function datamachine_scaffold_memory_content( string $content, string $filename, array $context ): string { + unset( $context ); if ( 'MEMORY.md' !== $filename || '' !== $content ) { return $content; } @@ -1453,11 +1464,12 @@ function datamachine_scaffold_memory_content( string $content, string $filename, * @return string */ function datamachine_scaffold_rules_content( string $content, string $filename, array $context ): string { + unset( $context ); if ( 'RULES.md' !== $filename || '' !== $content ) { return $content; } - $site_name = get_bloginfo( 'name' ) ?: 'this site'; + $site_name = get_bloginfo( 'name' ) ? get_bloginfo( 'name' ) : 'this site'; return <<base_prefix . 'datamachine_agents'; - $network_access_table = $wpdb->base_prefix . 'datamachine_agent_access'; - $network_tokens_table = $wpdb->base_prefix . 'datamachine_agent_tokens'; - $migrated_agents = 0; - $migrated_access = 0; + $network_agents_table = $wpdb->base_prefix . 'datamachine_agents'; + $network_access_table = $wpdb->base_prefix . 'datamachine_agent_access'; + $network_tokens_table = $wpdb->base_prefix . 'datamachine_agent_tokens'; + $migrated_agents = 0; + $migrated_access = 0; $sites = get_sites( array( 'fields' => 'ids' ) ); @@ -2087,11 +2099,13 @@ function datamachine_migrate_agents_to_network_scope() { // Set site_scope on existing main-site agents that don't have one yet. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared $wpdb->query( + // phpcs:disable WordPress.DB.PreparedSQL -- Table name from $wpdb->prefix, not user input. $wpdb->prepare( "UPDATE `{$network_agents_table}` SET site_scope = %d WHERE site_scope IS NULL", (int) $blog_id ) ); + // phpcs:enable WordPress.DB.PreparedSQL continue; } @@ -2119,6 +2133,7 @@ function datamachine_migrate_agents_to_network_scope() { // Check if slug already exists in network table. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared + // phpcs:disable WordPress.DB.PreparedSQL -- Table name from $wpdb->prefix, not user input. $existing = $wpdb->get_row( $wpdb->prepare( "SELECT agent_id FROM `{$network_agents_table}` WHERE agent_slug = %s", @@ -2126,6 +2141,7 @@ function datamachine_migrate_agents_to_network_scope() { ), ARRAY_A ); + // phpcs:enable WordPress.DB.PreparedSQL if ( $existing ) { // Slug already exists in network table — skip this agent. @@ -2159,10 +2175,12 @@ function datamachine_migrate_agents_to_network_scope() { // Migrate access grants for this agent. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared + // phpcs:disable WordPress.DB.PreparedSQL -- Table name from $wpdb->prefix, not user input. $site_access = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM `{$site_access_table}` WHERE agent_id = %d", $old_agent_id ), ARRAY_A ); + // phpcs:enable WordPress.DB.PreparedSQL foreach ( $site_access as $access ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery @@ -2184,10 +2202,12 @@ function datamachine_migrate_agents_to_network_scope() { $token_table_exists = $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $site_tokens_table ) ); if ( $token_table_exists ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared + // phpcs:disable WordPress.DB.PreparedSQL -- Table name from $wpdb->prefix, not user input. $site_tokens = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM `{$site_tokens_table}` WHERE agent_id = %d", $old_agent_id ), ARRAY_A ); + // phpcs:enable WordPress.DB.PreparedSQL foreach ( $site_tokens as $token ) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery diff --git a/tests/Unit/AI/Tools/ToolPolicyResolverTest.php b/tests/Unit/AI/Tools/ToolPolicyResolverTest.php index 69c9049db..67edd4359 100644 --- a/tests/Unit/AI/Tools/ToolPolicyResolverTest.php +++ b/tests/Unit/AI/Tools/ToolPolicyResolverTest.php @@ -60,6 +60,74 @@ public function test_chat_tools_pass_availability_check(): void { } } + public function test_chat_does_not_require_use_tools_cap_by_default(): void { + add_filter( 'user_has_cap', array( $this, 'deny_all_datamachine_caps' ), 10, 4 ); + + add_filter( 'datamachine_tools', function ( $tools ) { + $tools['test_authenticated_tool'] = array( + 'label' => 'Test Authenticated Tool', + 'description' => 'Visible to authenticated users.', + 'class' => 'NonExistentClass', + 'method' => 'handle_tool_call', + 'parameters' => array(), + 'contexts' => array( 'chat' ), + 'access_level' => 'authenticated', + ); + return $tools; + } ); + + ToolManager::clearCache(); + + $user_id = self::factory()->user->create( array( 'role' => 'subscriber' ) ); + wp_set_current_user( $user_id ); + + $tools = $this->resolver->resolve( array( + 'context' => ToolPolicyResolver::CONTEXT_CHAT, + ) ); + + $this->assertArrayHasKey( 'test_authenticated_tool', $tools ); + + remove_filter( 'user_has_cap', array( $this, 'deny_all_datamachine_caps' ), 10 ); + remove_all_filters( 'datamachine_tools' ); + wp_set_current_user( 0 ); + ToolManager::clearCache(); + } + + public function test_chat_can_restore_legacy_use_tools_gate_via_filter(): void { + add_filter( 'user_has_cap', array( $this, 'deny_all_datamachine_caps' ), 10, 4 ); + add_filter( 'datamachine_require_use_tools_for_chat_tools', '__return_true' ); + + add_filter( 'datamachine_tools', function ( $tools ) { + $tools['test_authenticated_tool'] = array( + 'label' => 'Test Authenticated Tool', + 'description' => 'Visible to authenticated users.', + 'class' => 'NonExistentClass', + 'method' => 'handle_tool_call', + 'parameters' => array(), + 'contexts' => array( 'chat' ), + 'access_level' => 'authenticated', + ); + return $tools; + } ); + + ToolManager::clearCache(); + + $user_id = self::factory()->user->create( array( 'role' => 'subscriber' ) ); + wp_set_current_user( $user_id ); + + $tools = $this->resolver->resolve( array( + 'context' => ToolPolicyResolver::CONTEXT_CHAT, + ) ); + + $this->assertEmpty( $tools ); + + remove_filter( 'user_has_cap', array( $this, 'deny_all_datamachine_caps' ), 10 ); + remove_filter( 'datamachine_require_use_tools_for_chat_tools', '__return_true' ); + remove_all_filters( 'datamachine_tools' ); + wp_set_current_user( 0 ); + ToolManager::clearCache(); + } + // ============================================ // PIPELINE CONTEXT // ============================================ @@ -371,6 +439,21 @@ private function createAgentWithPolicy( ?array $tool_policy ): int { return $agents_repo->create_if_missing( $slug, 'Test Agent', 1, $config ); } + /** + * Deny all Data Machine caps while leaving normal login intact. + */ + public function deny_all_datamachine_caps( array $allcaps, array $caps, array $args, $user ): array { + unset( $args, $user ); + + foreach ( array_keys( $allcaps ) as $cap ) { + if ( str_starts_with( $cap, 'datamachine_' ) ) { + $allcaps[ $cap ] = false; + } + } + + return $allcaps; + } + public function test_no_agent_id_means_no_restrictions(): void { $tools_without = $this->resolver->resolve( array( 'context' => ToolPolicyResolver::CONTEXT_CHAT,