PluginProbe
Elementor Website Builder – more than just a page builder / 4.3.1
Elementor Website Builder – more than just a page builder v4.3.1
4.3.1 4.3.0 4.3.0-beta3 4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 All 454 releases
← All changes | modules/mcp/abilities/build-composition-ability.php +115 -13 4.3.0-beta2 → 4.3.1 View file →
@@ -8,8 +8,10 @@
8 8 use Elementor\Modules\Mcp\Abilities\Build_Composition\Xml_Parser;
9 9 use Elementor\Modules\Mcp\Abilities\Utils\Composition_Compiler;
10 10 use Elementor\Modules\Mcp\Abilities\Utils\Document_Mutation_Links;
11 11 use Elementor\Modules\Mcp\Abilities\Utils\Prompt_Loader;
12 +use Elementor\Modules\Mcp\Abilities\Utils\Tool_Performance_Metrics;
13 +use Elementor\Modules\Mcp\Events\Mcp_Event_Dispatcher;
12 14 use Elementor\Plugin;
13 15
14 16 if ( ! defined( 'ABSPATH' ) ) {
15 17 exit;
@@ -54,30 +56,35 @@
54 56 );
55 57 }
56 58
57 59 public function execute( $input = [] ) {
58 - $input = is_array( $input ) ? $input : [];
60 + $started_at = hrtime( true );
61 + $input = is_array( $input ) ? $input : [];
59 62
63 + $post_id = isset( $input['post_id'] ) ? (int) $input['post_id'] : 0;
64 + $parent_id = $input['parent_id'] ?? self::DEFAULT_PARENT_ID;
65 + $dry_run = ! empty( $input['dry_run'] );
66 + $mode = is_string( $input['mode'] ?? null ) ? $input['mode'] : self::MODE_APPEND;
67 +
60 68 $validation_error = $this->validate_input( $input );
61 69 if ( $validation_error ) {
70 + $this->emit_mcp_build_composition_executed( $started_at, $post_id, $mode, $dry_run, $validation_error );
62 71 return $validation_error;
63 72 }
64 73
65 - $post_id = (int) $input['post_id'];
66 - $parent_id = $input['parent_id'] ?? self::DEFAULT_PARENT_ID;
67 - $dry_run = ! empty( $input['dry_run'] );
68 - $mode = $input['mode'] ?? self::MODE_APPEND;
69 -
70 74 if ( ! current_user_can( 'edit_post', $post_id ) ) {
71 - return new \WP_Error(
75 + $error = new \WP_Error(
72 76 'elementor_forbidden',
73 77 __( 'You do not have permission to edit this post.', 'elementor' ),
74 78 [ 'status' => \WP_Http::FORBIDDEN ]
75 79 );
80 + $this->emit_mcp_build_composition_executed( $started_at, $post_id, $mode, $dry_run, $error );
81 + return $error;
76 82 }
77 83
78 84 $document = $this->resolve_document( $post_id );
79 85 if ( is_wp_error( $document ) ) {
86 + $this->emit_mcp_build_composition_executed( $started_at, $post_id, $mode, $dry_run, $document );
80 87 return $document;
81 88 }
82 89
83 90 $elements_data = $document->get_elements_data();
@@ -87,29 +94,124 @@
87 94 is_array( $elements_data ) ? $elements_data : [],
88 95 $parent_id
89 96 );
90 97 if ( is_wp_error( $compiled ) ) {
98 + $this->emit_mcp_build_composition_executed( $started_at, $post_id, $mode, $dry_run, $compiled, null, [], $document );
91 99 return $compiled;
92 100 }
93 101
94 - $subtrees = $compiled['elements'];
95 - $warnings = $compiled['warnings'];
96 - $dom = $compiled['dom'];
97 - $xml_parser = $compiled['xml_parser'];
102 + $subtrees = $compiled['elements'];
103 + $warnings = $compiled['warnings'];
104 + $warning_codes = $compiled['warning_codes'] ?? [];
105 + $dom = $compiled['dom'];
106 + $xml_parser = $compiled['xml_parser'];
98 107
99 108 if ( $dry_run ) {
100 - return $this->build_response( $post_id, $document, $xml_parser, $dom, [], $warnings, $mode, [] );
109 + $response = $this->build_response( $post_id, $document, $xml_parser, $dom, [], $warnings, $mode, [] );
110 + $this->emit_mcp_build_composition_executed( $started_at, $post_id, $mode, $dry_run, null, $subtrees, $warning_codes, $document, $response, [] );
111 + return $response;
101 112 }
102 113
103 114 $persister = new Composition_Persister( $this->get_mutator(), $xml_parser );
104 115 $persisted = $persister->insert_and_save( $document, $subtrees, $parent_id, $mode );
105 116 if ( is_wp_error( $persisted ) ) {
117 + $this->emit_mcp_build_composition_executed( $started_at, $post_id, $mode, $dry_run, $persisted, $subtrees, $warning_codes, $document );
106 118 return $persisted;
107 119 }
108 120
109 121 $persister->embed_ids_into_dom( $dom, $persisted['tree'], $parent_id, $persisted['root_ids'] );
110 122
111 - return $this->build_response( $post_id, $document, $xml_parser, $dom, $persisted['root_ids'], $warnings, $mode, $persisted['removed_ids'] );
123 + $response = $this->build_response( $post_id, $document, $xml_parser, $dom, $persisted['root_ids'], $warnings, $mode, $persisted['removed_ids'] );
124 + $this->emit_mcp_build_composition_executed( $started_at, $post_id, $mode, $dry_run, null, $subtrees, $warning_codes, $document, $response, $persisted['removed_ids'] );
125 +
126 + return $response;
127 + }
128 +
129 + private function emit_mcp_build_composition_executed(
130 + int $started_at,
131 + int $post_id,
132 + string $mode,
133 + bool $dry_run,
134 + ?\WP_Error $error = null,
135 + ?array $subtrees = null,
136 + array $warning_codes = [],
137 + ?Document $document = null,
138 + array $response = [],
139 + array $removed_ids = []
140 + ): void {
141 + $duration_ms = Tool_Performance_Metrics::duration_ms_since( $started_at );
142 +
143 + $status = null === $error ? 'success' : 'error';
144 + $error_code = null !== $error ? $error->get_error_code() : null;
145 +
146 + $operations_count = 0;
147 + $operations_by_type = [];
148 + $class_attachments = 0;
149 + $interactions_count = 0;
150 + $removed_count = count( $removed_ids );
151 +
152 + if ( null !== $subtrees ) {
153 + $this->collect_composition_counts( $subtrees, $operations_count, $operations_by_type, $class_attachments, $interactions_count );
154 + }
155 +
156 + $style_input = [];
157 + $vars_referenced = 0;
158 + $document_type = null !== $document ? $this->resolve_document_type( $document ) : null;
159 +
160 + $payload = [
161 + 'tool_name' => $this->get_ability_id(),
162 + 'status' => $status,
163 + 'duration_ms' => $duration_ms,
164 + 'post_id' => $post_id,
165 + 'mode' => $mode,
166 + 'dry_run' => $dry_run,
167 + 'operations_count' => $operations_count,
168 + 'operations_by_type' => $operations_by_type,
169 + 'class_attachments_count' => $class_attachments,
170 + 'interactions_applied_count' => $interactions_count,
171 + 'removed_count' => $removed_count,
172 + 'warning_count' => count( $warning_codes ),
173 + 'warning_types' => array_values( array_unique( $warning_codes ) ),
174 + ];
175 +
176 + if ( null !== $document_type ) {
177 + $payload['document_type'] = $document_type;
178 + }
179 +
180 + if ( null !== $error_code ) {
181 + $payload['error_code'] = $error_code;
182 + }
183 +
184 + Mcp_Event_Dispatcher::emit( 'mcp_build_composition_executed', $payload );
185 + }
186 +
187 + private function collect_composition_counts( array $subtrees, int &$count, array &$by_type, int &$class_attachments, int &$interactions ): void {
188 + $stack = $subtrees;
189 +
190 + while ( ! empty( $stack ) ) {
191 + $node = array_pop( $stack );
192 + $type = $node['widgetType'] ?? $node['elType'] ?? '';
193 +
194 + if ( '' !== $type ) {
195 + ++$count;
196 + $by_type[ $type ] = ( $by_type[ $type ] ?? 0 ) + 1;
197 + }
198 +
199 + $classes = $node['settings']['classes']['value'] ?? [];
200 + $class_attachments += count( array_filter( (array) $classes, fn( $c ) => is_string( $c ) && str_starts_with( $c, 'g-' ) ) );
201 +
202 + if ( ! empty( $node['interactions']['items'] ) && is_array( $node['interactions']['items'] ) ) {
203 + $interactions += count( $node['interactions']['items'] );
204 + }
205 +
206 + foreach ( $node['elements'] ?? [] as $child ) {
207 + $stack[] = $child;
208 + }
209 + }
210 + }
211 +
212 + private function resolve_document_type( Document $document ): string {
213 + return $document->get_name();
112 214 }
113 215
114 216 private function get_ability_description(): string {
115 217 return Prompt_Loader::load( 'build-composition' );