@sweepypanda/wp-elementor-mcp 1.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +381 -0
  3. package/client-config.json +13 -0
  4. package/dist/elementor-handler.d.ts +51 -0
  5. package/dist/elementor-handler.d.ts.map +1 -0
  6. package/dist/elementor-handler.js +358 -0
  7. package/dist/elementor-handler.js.map +1 -0
  8. package/dist/helpers.d.ts +24 -0
  9. package/dist/helpers.d.ts.map +1 -0
  10. package/dist/helpers.js +107 -0
  11. package/dist/helpers.js.map +1 -0
  12. package/dist/index-backup.d.ts +3 -0
  13. package/dist/index-backup.d.ts.map +1 -0
  14. package/dist/index-backup.js +3752 -0
  15. package/dist/index-backup.js.map +1 -0
  16. package/dist/index.d.ts +3 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +187 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/server-config.d.ts +80 -0
  21. package/dist/server-config.d.ts.map +1 -0
  22. package/dist/server-config.js +137 -0
  23. package/dist/server-config.js.map +1 -0
  24. package/dist/tool-handlers.d.ts +44 -0
  25. package/dist/tool-handlers.d.ts.map +1 -0
  26. package/dist/tool-handlers.js +1682 -0
  27. package/dist/tool-handlers.js.map +1 -0
  28. package/dist/tool-schemas.d.ts +859 -0
  29. package/dist/tool-schemas.d.ts.map +1 -0
  30. package/dist/tool-schemas.js +870 -0
  31. package/dist/tool-schemas.js.map +1 -0
  32. package/dist/types.d.ts +19 -0
  33. package/dist/types.d.ts.map +1 -0
  34. package/dist/types.js +2 -0
  35. package/dist/types.js.map +1 -0
  36. package/dist/utils.d.ts +10 -0
  37. package/dist/utils.d.ts.map +1 -0
  38. package/dist/utils.js +142 -0
  39. package/dist/utils.js.map +1 -0
  40. package/dist/wordpress-client.d.ts +21 -0
  41. package/dist/wordpress-client.d.ts.map +1 -0
  42. package/dist/wordpress-client.js +151 -0
  43. package/dist/wordpress-client.js.map +1 -0
  44. package/package.json +74 -0
@@ -0,0 +1,1682 @@
1
+ import { ElementorDataHandler } from './elementor-handler.js';
2
+ import { getServerConfig } from './server-config.js';
3
+ import { ResponseHelpers, ElementorHelpers } from './helpers.js';
4
+ import FormData from 'form-data';
5
+ export class ToolHandlers {
6
+ wordPressClient;
7
+ elementorHandler;
8
+ serverConfig;
9
+ constructor(wordPressClient) {
10
+ this.wordPressClient = wordPressClient;
11
+ this.elementorHandler = new ElementorDataHandler(wordPressClient);
12
+ this.serverConfig = getServerConfig();
13
+ }
14
+ async handleToolCall(name, args) {
15
+ try {
16
+ switch (name) {
17
+ // WordPress Basic Operations
18
+ case 'get_posts':
19
+ return await this.getPosts(args);
20
+ case 'get_post':
21
+ return await this.getPost(args);
22
+ case 'create_post':
23
+ return await this.createPost(args);
24
+ case 'update_post':
25
+ return await this.updatePost(args);
26
+ case 'get_pages':
27
+ return await this.getPages(args);
28
+ case 'get_page':
29
+ return await this.getPage(args);
30
+ case 'list_all_content':
31
+ return await this.listAllContent(args);
32
+ case 'create_page':
33
+ return await this.createPage(args);
34
+ case 'update_page':
35
+ return await this.updatePage(args);
36
+ // Media Operations
37
+ case 'get_media':
38
+ return await this.getMedia(args);
39
+ case 'upload_media':
40
+ return await this.uploadMedia(args);
41
+ // Elementor Basic Operations
42
+ case 'get_elementor_templates':
43
+ return await this.getElementorTemplates(args);
44
+ case 'get_elementor_data':
45
+ return await this.getElementorData(args);
46
+ case 'update_elementor_data':
47
+ return await this.updateElementorData(args);
48
+ case 'update_elementor_widget':
49
+ return await this.updateElementorWidget(args);
50
+ case 'get_elementor_widget':
51
+ return await this.getElementorWidget(args);
52
+ case 'get_elementor_elements':
53
+ return await this.getElementorElements(args);
54
+ case 'update_elementor_section':
55
+ return await this.updateElementorSection(args);
56
+ case 'get_elementor_data_smart':
57
+ return await this.getElementorDataSmart(args);
58
+ case 'get_elementor_structure_summary':
59
+ return await this.getElementorStructureSummary(args);
60
+ case 'backup_elementor_data':
61
+ return await this.backupElementorData(args);
62
+ // NEW: Temp file operations
63
+ case 'get_elementor_data_to_file':
64
+ return await this.elementorHandler.getElementorDataToFile(args);
65
+ case 'get_page_structure_to_file':
66
+ return await this.elementorHandler.getPageStructureToFile(args);
67
+ case 'backup_elementor_data_to_file':
68
+ return await this.elementorHandler.backupElementorDataToFile(args);
69
+ // Section and Container Creation Tools
70
+ case 'create_elementor_section':
71
+ return await this.createElementorSection(args);
72
+ case 'create_elementor_container':
73
+ return await this.createElementorContainer(args);
74
+ case 'add_column_to_section':
75
+ return await this.addColumnToSection(args);
76
+ case 'duplicate_section':
77
+ return await this.duplicateSection(args);
78
+ // Widget Addition Tools
79
+ case 'add_widget_to_section':
80
+ return await this.addWidgetToSection(args);
81
+ case 'insert_widget_at_position':
82
+ return await this.insertWidgetAtPosition(args);
83
+ case 'clone_widget':
84
+ return await this.cloneWidget(args);
85
+ case 'move_widget':
86
+ return await this.moveWidget(args);
87
+ // Element Management Tools
88
+ case 'delete_elementor_element':
89
+ return await this.deleteElementorElement(args);
90
+ case 'reorder_elements':
91
+ return await this.reorderElements(args);
92
+ case 'copy_element_settings':
93
+ return await this.copyElementSettings(args);
94
+ // Page Structure Tools
95
+ case 'get_page_structure':
96
+ return await this.getPageStructure(args);
97
+ case 'rebuild_page_structure':
98
+ return await this.rebuildPageStructure(args);
99
+ case 'validate_elementor_data':
100
+ return await this.validateElementorData(args);
101
+ default:
102
+ throw new Error(`Unknown tool: ${name}`);
103
+ }
104
+ }
105
+ catch (error) {
106
+ console.error(`❌ Tool execution failed for ${name}: ${error.message}`);
107
+ return {
108
+ content: [{
109
+ type: 'text',
110
+ text: JSON.stringify({
111
+ status: 'error',
112
+ data: {
113
+ message: error.message,
114
+ code: 'TOOL_EXECUTION_ERROR',
115
+ tool: name
116
+ }
117
+ }, null, 2)
118
+ }],
119
+ isError: true
120
+ };
121
+ }
122
+ }
123
+ // WordPress Basic Operations
124
+ async getPosts(args) {
125
+ const authCheck = this.wordPressClient.ensureAuthenticated();
126
+ if (authCheck)
127
+ return authCheck;
128
+ const params = {
129
+ per_page: args.per_page || 10,
130
+ status: args.status || 'publish',
131
+ context: 'view' // Use 'view' instead of 'edit' for lighter responses
132
+ };
133
+ if (args.search) {
134
+ params.search = args.search;
135
+ }
136
+ try {
137
+ console.error(`Fetching posts with params: ${JSON.stringify(params)}`);
138
+ const axios = this.wordPressClient.getAxiosInstance();
139
+ const response = await axios.get('posts', { params });
140
+ const posts = response.data;
141
+ let debugInfo = `Found ${posts.length} posts\n`;
142
+ posts.forEach((post, index) => {
143
+ const hasElementorData = post.meta && post.meta._elementor_data;
144
+ const hasElementorEditMode = post.meta && post.meta._elementor_edit_mode;
145
+ debugInfo += `${index + 1}. ID: ${post.id}, Title: "${post.title.rendered}", Status: ${post.status}`;
146
+ if (hasElementorData) {
147
+ debugInfo += ` ✅ Elementor`;
148
+ }
149
+ else if (hasElementorEditMode) {
150
+ debugInfo += ` ⚠️ Elementor (no data)`;
151
+ }
152
+ debugInfo += `\n`;
153
+ });
154
+ return ResponseHelpers.createSuccessResponse({
155
+ posts: posts,
156
+ summary: debugInfo,
157
+ total_found: posts.length
158
+ }, `Successfully retrieved ${posts.length} posts`);
159
+ }
160
+ catch (error) {
161
+ console.error(`Error fetching posts: ${error.response?.status} - ${error.response?.statusText}`);
162
+ return ResponseHelpers.createErrorResponse(`Failed to fetch posts: ${error.response?.status} ${error.response?.statusText} - ${error.response?.data?.message || error.message}`, 'FETCH_POSTS_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
163
+ }
164
+ }
165
+ async getPost(args) {
166
+ const authCheck = this.wordPressClient.ensureAuthenticated();
167
+ if (authCheck)
168
+ return authCheck;
169
+ try {
170
+ console.error(`Fetching post with ID: ${args.id}`);
171
+ const axios = this.wordPressClient.getAxiosInstance();
172
+ const response = await axios.get(`posts/${args.id}`, {
173
+ params: { context: 'edit' }
174
+ });
175
+ return ResponseHelpers.createSuccessResponse(response.data, `Successfully retrieved post ${args.id}`);
176
+ }
177
+ catch (error) {
178
+ console.error(`Error fetching post ${args.id}: ${error.response?.status} - ${error.response?.statusText}`);
179
+ if (error.response?.status === 404) {
180
+ return ResponseHelpers.createErrorResponse(`Post with ID ${args.id} not found. The post may have been deleted, be in trash, or may not exist.`, 'POST_NOT_FOUND', 'NOT_FOUND_ERROR', `HTTP 404: Post ${args.id} does not exist`);
181
+ }
182
+ return ResponseHelpers.createErrorResponse(`Failed to fetch post ${args.id}: ${error.response?.status} ${error.response?.statusText} - ${error.response?.data?.message || error.message}`, 'FETCH_POST_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
183
+ }
184
+ }
185
+ async createPost(args) {
186
+ const authCheck = this.wordPressClient.ensureAuthenticated();
187
+ if (authCheck)
188
+ return authCheck;
189
+ const postData = {
190
+ title: args.title,
191
+ content: args.content,
192
+ status: args.status || 'draft',
193
+ ...(args.excerpt && { excerpt: args.excerpt }),
194
+ };
195
+ try {
196
+ console.error(`Creating post with title: "${args.title}"`);
197
+ const axios = this.wordPressClient.getAxiosInstance();
198
+ const response = await axios.post('posts', postData);
199
+ await ElementorHelpers.clearElementorCache(response.data.id);
200
+ return ResponseHelpers.createSuccessResponse({
201
+ post: response.data,
202
+ cache_cleared: true
203
+ }, `Post created successfully! ID: ${response.data.id}, Status: ${response.data.status}`);
204
+ }
205
+ catch (error) {
206
+ console.error(`Error creating post: ${error.response?.status} - ${error.response?.statusText}`);
207
+ return ResponseHelpers.createErrorResponse(`Failed to create post: ${error.response?.status} ${error.response?.statusText} - ${error.response?.data?.message || error.message}`, 'CREATE_POST_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
208
+ }
209
+ }
210
+ async updatePost(args) {
211
+ const authCheck = this.wordPressClient.ensureAuthenticated();
212
+ if (authCheck)
213
+ return authCheck;
214
+ const updateData = {};
215
+ if (args.title)
216
+ updateData.title = args.title;
217
+ if (args.content)
218
+ updateData.content = args.content;
219
+ if (args.status)
220
+ updateData.status = args.status;
221
+ if (args.excerpt)
222
+ updateData.excerpt = args.excerpt;
223
+ try {
224
+ console.error(`Updating post ID: ${args.id}`);
225
+ const axios = this.wordPressClient.getAxiosInstance();
226
+ const response = await axios.post(`posts/${args.id}`, updateData);
227
+ await ElementorHelpers.clearElementorCache(args.id);
228
+ return ResponseHelpers.createSuccessResponse({
229
+ post: response.data,
230
+ cache_cleared: true
231
+ }, `Post updated successfully! ID: ${response.data.id}, Status: ${response.data.status}`);
232
+ }
233
+ catch (error) {
234
+ console.error(`Error updating post ${args.id}: ${error.response?.status} - ${error.response?.statusText}`);
235
+ if (error.response?.status === 404) {
236
+ return ResponseHelpers.createErrorResponse(`Post with ID ${args.id} not found. The post may have been deleted, be in trash, or may not exist.`, 'POST_NOT_FOUND', 'NOT_FOUND_ERROR', `HTTP 404: Post ${args.id} does not exist`);
237
+ }
238
+ return ResponseHelpers.createErrorResponse(`Failed to update post ${args.id}: ${error.response?.status} ${error.response?.statusText} - ${error.response?.data?.message || error.message}`, 'UPDATE_POST_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
239
+ }
240
+ }
241
+ async getPages(args) {
242
+ const authCheck = this.wordPressClient.ensureAuthenticated();
243
+ if (authCheck)
244
+ return authCheck;
245
+ const params = {
246
+ per_page: args.per_page || 10,
247
+ status: args.status || 'publish',
248
+ context: 'view' // Use 'view' instead of 'edit' for lighter responses
249
+ };
250
+ try {
251
+ console.error(`Fetching pages with params: ${JSON.stringify(params)}`);
252
+ const axios = this.wordPressClient.getAxiosInstance();
253
+ const response = await axios.get('pages', { params });
254
+ const pages = response.data;
255
+ let debugInfo = `Found ${pages.length} pages\n`;
256
+ pages.forEach((page, index) => {
257
+ const hasElementorData = page.meta && page.meta._elementor_data;
258
+ const hasElementorEditMode = page.meta && page.meta._elementor_edit_mode;
259
+ debugInfo += `${index + 1}. ID: ${page.id}, Title: "${page.title.rendered}", Status: ${page.status}`;
260
+ if (hasElementorData) {
261
+ debugInfo += ` ✅ Elementor`;
262
+ }
263
+ else if (hasElementorEditMode) {
264
+ debugInfo += ` ⚠️ Elementor (no data)`;
265
+ }
266
+ debugInfo += `\n`;
267
+ });
268
+ return ResponseHelpers.createSuccessResponse({
269
+ pages: pages,
270
+ summary: debugInfo,
271
+ total_found: pages.length
272
+ }, `Successfully retrieved ${pages.length} pages`);
273
+ }
274
+ catch (error) {
275
+ console.error(`Error fetching pages: ${error.response?.status} - ${error.response?.statusText}`);
276
+ return ResponseHelpers.createErrorResponse(`Failed to fetch pages: ${error.response?.status} ${error.response?.statusText} - ${error.response?.data?.message || error.message}`, 'FETCH_PAGES_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
277
+ }
278
+ }
279
+ async getPage(args) {
280
+ const authCheck = this.wordPressClient.ensureAuthenticated();
281
+ if (authCheck)
282
+ return authCheck;
283
+ try {
284
+ console.error(`Fetching page with ID: ${args.id}`);
285
+ const axios = this.wordPressClient.getAxiosInstance();
286
+ const response = await axios.get(`pages/${args.id}`, {
287
+ params: { context: 'view' }
288
+ });
289
+ const page = response.data;
290
+ return ResponseHelpers.createSuccessResponse({
291
+ id: page.id,
292
+ title: page.title.rendered,
293
+ content: page.content.rendered,
294
+ excerpt: page.excerpt.rendered,
295
+ status: page.status,
296
+ slug: page.slug,
297
+ date: page.date,
298
+ modified: page.modified,
299
+ link: page.link,
300
+ author: page.author,
301
+ parent: page.parent,
302
+ menu_order: page.menu_order,
303
+ featured_media: page.featured_media,
304
+ comment_status: page.comment_status,
305
+ ping_status: page.ping_status,
306
+ template: page.template,
307
+ meta: page.meta,
308
+ elementor_status: page.meta && page.meta._elementor_edit_mode ? 'full' : 'none'
309
+ }, `Successfully retrieved page ID ${args.id}: "${page.title.rendered}"`);
310
+ }
311
+ catch (error) {
312
+ return ResponseHelpers.createErrorResponse(`Failed to fetch page ID ${args.id}: ${error.response?.data?.message || error.message}`, 'FETCH_PAGE_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
313
+ }
314
+ }
315
+ async listAllContent(args) {
316
+ const authCheck = this.wordPressClient.ensureAuthenticated();
317
+ if (authCheck)
318
+ return authCheck;
319
+ try {
320
+ const perPage = args.per_page || 50;
321
+ const statuses = args.include_all_statuses ? ['publish', 'draft', 'private', 'trash'] : ['publish'];
322
+ console.error(`Listing all content with per_page: ${perPage}, statuses: ${statuses.join(', ')}`);
323
+ let allContent = [];
324
+ const axios = this.wordPressClient.getAxiosInstance();
325
+ // Fetch posts for each status
326
+ for (const status of statuses) {
327
+ try {
328
+ const postsResponse = await axios.get('posts', {
329
+ params: {
330
+ per_page: perPage,
331
+ status,
332
+ context: 'edit'
333
+ }
334
+ });
335
+ postsResponse.data.forEach((post) => {
336
+ const hasElementorData = post.meta?._elementor_data;
337
+ const hasElementorEditMode = post.meta?._elementor_edit_mode;
338
+ let elementorStatus = 'none';
339
+ if (hasElementorData) {
340
+ elementorStatus = 'full';
341
+ }
342
+ else if (hasElementorEditMode === 'builder') {
343
+ elementorStatus = 'partial';
344
+ }
345
+ allContent.push({
346
+ id: post.id,
347
+ title: post.title.rendered || '(No title)',
348
+ type: 'post',
349
+ status: post.status,
350
+ elementor_status: elementorStatus,
351
+ url: post.link
352
+ });
353
+ });
354
+ }
355
+ catch (error) {
356
+ console.error(`Failed to fetch posts with status ${status}: ${error.message}`);
357
+ }
358
+ }
359
+ // Fetch pages for each status
360
+ for (const status of statuses) {
361
+ try {
362
+ const pagesResponse = await axios.get('pages', {
363
+ params: {
364
+ per_page: perPage,
365
+ status,
366
+ context: 'edit'
367
+ }
368
+ });
369
+ pagesResponse.data.forEach((page) => {
370
+ const hasElementorData = page.meta?._elementor_data;
371
+ const hasElementorEditMode = page.meta?._elementor_edit_mode;
372
+ let elementorStatus = 'none';
373
+ if (hasElementorData) {
374
+ elementorStatus = 'full';
375
+ }
376
+ else if (hasElementorEditMode === 'builder') {
377
+ elementorStatus = 'partial';
378
+ }
379
+ allContent.push({
380
+ id: page.id,
381
+ title: page.title.rendered || '(No title)',
382
+ type: 'page',
383
+ status: page.status,
384
+ elementor_status: elementorStatus,
385
+ url: page.link
386
+ });
387
+ });
388
+ }
389
+ catch (error) {
390
+ console.error(`Failed to fetch pages with status ${status}: ${error.message}`);
391
+ }
392
+ }
393
+ allContent.sort((a, b) => a.id - b.id);
394
+ const summary = {
395
+ total: allContent.length,
396
+ by_type: {
397
+ posts: allContent.filter(item => item.type === 'post').length,
398
+ pages: allContent.filter(item => item.type === 'page').length
399
+ },
400
+ by_elementor_status: {
401
+ full: allContent.filter(item => item.elementor_status === 'full').length,
402
+ partial: allContent.filter(item => item.elementor_status === 'partial').length,
403
+ none: allContent.filter(item => item.elementor_status === 'none').length
404
+ },
405
+ by_status: {}
406
+ };
407
+ allContent.forEach(item => {
408
+ summary.by_status[item.status] = (summary.by_status[item.status] || 0) + 1;
409
+ });
410
+ return ResponseHelpers.createSuccessResponse({
411
+ summary,
412
+ content: allContent
413
+ }, `Successfully retrieved ${allContent.length} content items`);
414
+ }
415
+ catch (error) {
416
+ console.error(`Error listing all content: ${error.message}`);
417
+ return ResponseHelpers.createErrorResponse(`Failed to list content: ${error.response?.status} ${error.response?.statusText} - ${error.response?.data?.message || error.message}`, "LIST_CONTENT_ERROR", "API_ERROR", `HTTP ${error.response?.status}: ${error.response?.statusText}`);
418
+ }
419
+ }
420
+ async createPage(args) {
421
+ const authCheck = this.wordPressClient.ensureAuthenticated();
422
+ if (authCheck)
423
+ return authCheck;
424
+ const pageData = {
425
+ title: args.title,
426
+ content: args.content,
427
+ status: args.status || 'draft',
428
+ ...(args.excerpt && { excerpt: args.excerpt }),
429
+ ...(args.parent && { parent: args.parent }),
430
+ };
431
+ try {
432
+ console.error(`Creating page with title: "${args.title}"`);
433
+ const axios = this.wordPressClient.getAxiosInstance();
434
+ const response = await axios.post('pages', pageData);
435
+ await ElementorHelpers.clearElementorCache(response.data.id);
436
+ return ResponseHelpers.createSuccessResponse({
437
+ page: response.data,
438
+ cache_cleared: true
439
+ }, `Page created successfully! ID: ${response.data.id}, Status: ${response.data.status}`);
440
+ }
441
+ catch (error) {
442
+ console.error(`Error creating page: ${error.response?.status} - ${error.response?.statusText}`);
443
+ return ResponseHelpers.createErrorResponse(`Failed to create page: ${error.response?.status} ${error.response?.statusText} - ${error.response?.data?.message || error.message}`, 'CREATE_PAGE_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
444
+ }
445
+ }
446
+ async updatePage(args) {
447
+ const authCheck = this.wordPressClient.ensureAuthenticated();
448
+ if (authCheck)
449
+ return authCheck;
450
+ const updateData = {};
451
+ if (args.title)
452
+ updateData.title = args.title;
453
+ if (args.content)
454
+ updateData.content = args.content;
455
+ if (args.status)
456
+ updateData.status = args.status;
457
+ if (args.excerpt)
458
+ updateData.excerpt = args.excerpt;
459
+ if (args.parent !== undefined)
460
+ updateData.parent = args.parent;
461
+ try {
462
+ console.error(`Updating page ID: ${args.id}`);
463
+ const axios = this.wordPressClient.getAxiosInstance();
464
+ const response = await axios.post(`pages/${args.id}`, updateData);
465
+ await ElementorHelpers.clearElementorCache(args.id);
466
+ return ResponseHelpers.createSuccessResponse({
467
+ page: response.data,
468
+ cache_cleared: true
469
+ }, `Page updated successfully! ID: ${response.data.id}, Status: ${response.data.status}`);
470
+ }
471
+ catch (error) {
472
+ console.error(`Error updating page ${args.id}: ${error.response?.status} - ${error.response?.statusText}`);
473
+ if (error.response?.status === 404) {
474
+ return ResponseHelpers.createErrorResponse(`Page with ID ${args.id} not found. The page may have been deleted, be in trash, or may not exist.`, 'PAGE_NOT_FOUND', 'NOT_FOUND', `HTTP 404: Page ID ${args.id} not accessible`);
475
+ }
476
+ return ResponseHelpers.createErrorResponse(`Failed to update page ${args.id}: ${error.response?.status} ${error.response?.statusText} - ${error.response?.data?.message || error.message}`, 'UPDATE_PAGE_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
477
+ }
478
+ }
479
+ async getMedia(args) {
480
+ const authCheck = this.wordPressClient.ensureAuthenticated();
481
+ if (authCheck)
482
+ return authCheck;
483
+ const params = {
484
+ per_page: args.per_page || 10,
485
+ };
486
+ if (args.media_type) {
487
+ params.media_type = args.media_type;
488
+ }
489
+ try {
490
+ console.error(`Fetching media with params: ${JSON.stringify(params)}`);
491
+ const axios = this.wordPressClient.getAxiosInstance();
492
+ const response = await axios.get('media', { params });
493
+ return ResponseHelpers.createSuccessResponse({
494
+ media: response.data,
495
+ count: response.data.length,
496
+ filter: args.media_type || 'all'
497
+ }, `Retrieved ${response.data.length} media items`);
498
+ }
499
+ catch (error) {
500
+ console.error(`Error fetching media: ${error.response?.status} - ${error.response?.statusText}`);
501
+ return ResponseHelpers.createErrorResponse(`Failed to fetch media: ${error.response?.status} ${error.response?.statusText} - ${error.response?.data?.message || error.message}`, 'GET_MEDIA_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
502
+ }
503
+ }
504
+ async uploadMedia(args) {
505
+ const authCheck = this.wordPressClient.ensureAuthenticated();
506
+ if (authCheck)
507
+ return authCheck;
508
+ try {
509
+ const fs = await import('fs');
510
+ const path = await import('path');
511
+ if (!fs.existsSync(args.file_path)) {
512
+ return ResponseHelpers.createErrorResponse(`Failed to upload media: File not found: ${args.file_path}`, 'FILE_NOT_FOUND', 'VALIDATION_ERROR', 'The specified file path does not exist or is not accessible');
513
+ }
514
+ const formData = new FormData();
515
+ const fileStream = fs.createReadStream(args.file_path);
516
+ const fileName = path.basename(args.file_path);
517
+ formData.append('file', fileStream, fileName);
518
+ if (args.title) {
519
+ formData.append('title', args.title);
520
+ }
521
+ if (args.alt_text) {
522
+ formData.append('alt_text', args.alt_text);
523
+ }
524
+ const axios = this.wordPressClient.getAxiosInstance();
525
+ const response = await axios.post('media', formData, {
526
+ headers: {
527
+ ...formData.getHeaders(),
528
+ },
529
+ });
530
+ return ResponseHelpers.createSuccessResponse({
531
+ operation_type: "upload_media",
532
+ media_id: response.data.id,
533
+ url: response.data.source_url,
534
+ title: response.data.title.rendered,
535
+ file_path: args.file_path,
536
+ file_name: fileName,
537
+ mime_type: response.data.mime_type,
538
+ file_size: response.data.media_details?.filesize || null,
539
+ alt_text: args.alt_text || null,
540
+ upload_date: response.data.date
541
+ }, `Media uploaded successfully! Media ID: ${response.data.id} - ${response.data.title.rendered}`);
542
+ }
543
+ catch (error) {
544
+ return ResponseHelpers.createErrorResponse(`Failed to upload media: ${error.response?.data?.message || error.message}`, 'UPLOAD_FAILED', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText} - ${error.response?.data || error.message}`);
545
+ }
546
+ }
547
+ // Elementor Operations
548
+ async getElementorTemplates(args) {
549
+ const authCheck = this.wordPressClient.ensureAuthenticated();
550
+ if (authCheck)
551
+ return authCheck;
552
+ const params = {
553
+ per_page: args.per_page || 10,
554
+ meta_key: '_elementor_template_type',
555
+ };
556
+ if (args.type) {
557
+ params.meta_value = args.type;
558
+ }
559
+ try {
560
+ const axios = this.wordPressClient.getAxiosInstance();
561
+ const response = await axios.get('elementor_library', { params });
562
+ return ResponseHelpers.createSuccessResponse({
563
+ templates: response.data,
564
+ count: response.data.length
565
+ }, `Retrieved ${response.data.length} Elementor templates`);
566
+ }
567
+ catch (error) {
568
+ if (error.response?.status === 404) {
569
+ return ResponseHelpers.createErrorResponse('Elementor templates endpoint not found. Make sure Elementor Pro is installed and activated.', 'TEMPLATES_NOT_FOUND', 'NOT_FOUND', 'Elementor Pro required for template access');
570
+ }
571
+ return ResponseHelpers.createErrorResponse(`Failed to get Elementor templates: ${error.message}`, 'GET_TEMPLATES_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
572
+ }
573
+ }
574
+ async getElementorData(args) {
575
+ return await this.elementorHandler.getElementorData(args);
576
+ }
577
+ async updateElementorData(args) {
578
+ const authCheck = this.wordPressClient.ensureAuthenticated();
579
+ if (authCheck)
580
+ return authCheck;
581
+ try {
582
+ const updateData = {
583
+ meta: {
584
+ _elementor_data: args.elementor_data,
585
+ _elementor_edit_mode: 'builder',
586
+ },
587
+ };
588
+ const axios = this.wordPressClient.getAxiosInstance();
589
+ let response;
590
+ let postType = 'post';
591
+ try {
592
+ response = await axios.post(`posts/${args.post_id}`, updateData);
593
+ }
594
+ catch (postError) {
595
+ if (postError.response?.status === 404) {
596
+ try {
597
+ response = await axios.post(`pages/${args.post_id}`, updateData);
598
+ postType = 'page';
599
+ }
600
+ catch (pageError) {
601
+ return ResponseHelpers.createErrorResponse(`Post/Page ID ${args.post_id} not found in posts or pages`, 'POST_PAGE_NOT_FOUND', 'NOT_FOUND', 'Failed to update both post and page endpoints');
602
+ }
603
+ }
604
+ else {
605
+ return ResponseHelpers.createErrorResponse(`Failed to update post: ${postError.response?.data?.message || postError.message}`, 'UPDATE_POST_ERROR', 'API_ERROR', `HTTP ${postError.response?.status}: ${postError.response?.statusText}`);
606
+ }
607
+ }
608
+ await ElementorHelpers.clearElementorCache(args.post_id);
609
+ return ResponseHelpers.createSuccessResponse({
610
+ post_type: postType,
611
+ post_id: args.post_id,
612
+ cache_cleared: true,
613
+ updated_data: true
614
+ }, `Elementor data updated successfully for ${postType} ID: ${args.post_id}`);
615
+ }
616
+ catch (error) {
617
+ return ResponseHelpers.createErrorResponse(`Failed to update Elementor data: ${error.response?.data?.message || error.message}`, 'UPDATE_ELEMENTOR_DATA_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
618
+ }
619
+ }
620
+ async updateElementorWidget(args) {
621
+ const authCheck = this.wordPressClient.ensureAuthenticated();
622
+ if (authCheck)
623
+ return authCheck;
624
+ try {
625
+ const parsedResult = await this.elementorHandler.safeGetElementorData(args.post_id);
626
+ if (!parsedResult.success || !parsedResult.data) {
627
+ return ResponseHelpers.createErrorResponse(`Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, 'ELEMENTOR_DATA_ERROR', 'DATA_ERROR', 'Could not retrieve or parse Elementor data for widget update');
628
+ }
629
+ const elementorData = parsedResult.data;
630
+ const updateWidgetRecursive = (elements) => {
631
+ for (let i = 0; i < elements.length; i++) {
632
+ const element = elements[i];
633
+ if (element.id === args.widget_id) {
634
+ if (args.widget_settings) {
635
+ element.settings = { ...element.settings, ...args.widget_settings };
636
+ }
637
+ if (args.widget_content) {
638
+ ElementorHelpers.updateWidgetContent(element, args.widget_content);
639
+ }
640
+ return true;
641
+ }
642
+ if (element.elements && element.elements.length > 0) {
643
+ if (updateWidgetRecursive(element.elements)) {
644
+ return true;
645
+ }
646
+ }
647
+ }
648
+ return false;
649
+ };
650
+ const widgetFound = updateWidgetRecursive(elementorData);
651
+ if (!widgetFound) {
652
+ return ResponseHelpers.createErrorResponse(`Widget ID ${args.widget_id} not found in Elementor data`, 'WIDGET_NOT_FOUND', 'NOT_FOUND', `Could not locate widget with ID ${args.widget_id} in the page structure`);
653
+ }
654
+ const updateData = {
655
+ meta: {
656
+ _elementor_data: JSON.stringify(elementorData),
657
+ _elementor_edit_mode: 'builder',
658
+ },
659
+ };
660
+ const axios = this.wordPressClient.getAxiosInstance();
661
+ let response;
662
+ let postType = 'post';
663
+ try {
664
+ response = await axios.post(`posts/${args.post_id}`, updateData);
665
+ }
666
+ catch (postError) {
667
+ if (postError.response?.status === 404) {
668
+ try {
669
+ response = await axios.post(`pages/${args.post_id}`, updateData);
670
+ postType = 'page';
671
+ }
672
+ catch (pageError) {
673
+ return ResponseHelpers.createErrorResponse(`Post/Page ID ${args.post_id} not found in posts or pages`, 'POST_PAGE_NOT_FOUND', 'NOT_FOUND', 'Failed to update both post and page endpoints for widget update');
674
+ }
675
+ }
676
+ else {
677
+ return ResponseHelpers.createErrorResponse(`Failed to update post: ${postError.response?.data?.message || postError.message}`, 'UPDATE_POST_ERROR', 'API_ERROR', `HTTP ${postError.response?.status}: ${postError.response?.statusText}`);
678
+ }
679
+ }
680
+ await ElementorHelpers.clearElementorCache(args.post_id);
681
+ return ResponseHelpers.createSuccessResponse({
682
+ widget_id: args.widget_id,
683
+ post_type: postType,
684
+ post_id: args.post_id,
685
+ cache_cleared: true,
686
+ updated_settings: !!args.widget_settings,
687
+ updated_content: !!args.widget_content
688
+ }, `Elementor widget ${args.widget_id} updated successfully for ${postType} ID: ${args.post_id}`);
689
+ }
690
+ catch (error) {
691
+ return ResponseHelpers.createErrorResponse(`Failed to update Elementor widget: ${error.response?.data?.message || error.message}`, 'UPDATE_WIDGET_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
692
+ }
693
+ }
694
+ async getElementorWidget(args) {
695
+ const authCheck = this.wordPressClient.ensureAuthenticated();
696
+ if (authCheck)
697
+ return authCheck;
698
+ try {
699
+ console.error(`🔍 Getting widget ${args.widget_id} from post ID: ${args.post_id}`);
700
+ const parsedResult = await this.elementorHandler.safeGetElementorData(args.post_id);
701
+ if (!parsedResult.success || !parsedResult.data) {
702
+ console.error(`❌ Failed to get Elementor data: ${parsedResult.error}`);
703
+ return ResponseHelpers.createErrorResponse(`Failed to retrieve Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, 'ELEMENTOR_DATA_ERROR', 'DATA_ERROR', 'Could not retrieve or parse Elementor data for widget retrieval');
704
+ }
705
+ const elementorData = parsedResult.data;
706
+ console.error(`✅ Successfully parsed data, searching through ${elementorData.length} top-level elements for widget ${args.widget_id}`);
707
+ const widget = ElementorHelpers.findElementRecursive(elementorData, args.widget_id);
708
+ if (!widget) {
709
+ return ResponseHelpers.createErrorResponse(`Widget ID ${args.widget_id} not found in Elementor data`, 'WIDGET_NOT_FOUND', 'NOT_FOUND', `Could not locate widget with ID ${args.widget_id} in the page structure`);
710
+ }
711
+ return ResponseHelpers.createSuccessResponse({
712
+ widget: widget,
713
+ widget_id: args.widget_id,
714
+ post_id: args.post_id
715
+ }, `Widget ${args.widget_id} retrieved successfully`);
716
+ }
717
+ catch (error) {
718
+ return ResponseHelpers.createErrorResponse(`Failed to get Elementor widget: ${error.response?.data?.message || error.message}`, 'GET_WIDGET_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
719
+ }
720
+ }
721
+ async getElementorElements(args) {
722
+ const authCheck = this.wordPressClient.ensureAuthenticated();
723
+ if (authCheck)
724
+ return authCheck;
725
+ try {
726
+ console.error(`🔍 Getting Elementor elements for ID: ${args.post_id}`);
727
+ const parsedResult = await this.elementorHandler.safeGetElementorData(args.post_id);
728
+ if (!parsedResult.success || !parsedResult.data) {
729
+ return ResponseHelpers.createErrorResponse(`No Elementor data found for post/page ID ${args.post_id}`, 'NO_ELEMENTOR_DATA', 'DATA_NOT_FOUND', `Post/page does not contain Elementor data or is not built with Elementor: ${parsedResult.error}`);
730
+ }
731
+ const elementorData = parsedResult.data;
732
+ console.error(`✅ Successfully parsed ${elementorData.length} top-level elements`);
733
+ const extractElementsRecursive = (elements, level = 0) => {
734
+ const result = [];
735
+ for (const element of elements) {
736
+ const elementInfo = {
737
+ id: element.id,
738
+ type: element.elType,
739
+ level: level,
740
+ };
741
+ if (element.widgetType) {
742
+ elementInfo.widgetType = element.widgetType;
743
+ }
744
+ if (args.include_content && element.settings) {
745
+ if (element.widgetType === 'html' && element.settings.html) {
746
+ elementInfo.contentPreview = element.settings.html.substring(0, 100) + (element.settings.html.length > 100 ? '...' : '');
747
+ }
748
+ else if (element.widgetType === 'text-editor' && element.settings.editor) {
749
+ elementInfo.contentPreview = element.settings.editor.substring(0, 100) + (element.settings.editor.length > 100 ? '...' : '');
750
+ }
751
+ else if (element.widgetType === 'heading' && element.settings.title) {
752
+ elementInfo.contentPreview = element.settings.title.substring(0, 100) + (element.settings.title.length > 100 ? '...' : '');
753
+ }
754
+ }
755
+ result.push(elementInfo);
756
+ if (element.elements && element.elements.length > 0) {
757
+ result.push(...extractElementsRecursive(element.elements, level + 1));
758
+ }
759
+ }
760
+ return result;
761
+ };
762
+ const elements = extractElementsRecursive(elementorData);
763
+ return ResponseHelpers.createSuccessResponse({
764
+ post_id: args.post_id,
765
+ total_elements: elements.length,
766
+ elements: elements
767
+ }, `Retrieved ${elements.length} Elementor elements from post/page ${args.post_id}`);
768
+ }
769
+ catch (error) {
770
+ return ResponseHelpers.createErrorResponse(`Failed to get Elementor elements: ${error.response?.data?.message || error.message}`, 'GET_ELEMENTS_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
771
+ }
772
+ }
773
+ async updateElementorSection(args) {
774
+ const authCheck = this.wordPressClient.ensureAuthenticated();
775
+ if (authCheck)
776
+ return authCheck;
777
+ try {
778
+ const parsedResult = await this.elementorHandler.safeGetElementorData(args.post_id);
779
+ if (!parsedResult.success || !parsedResult.data) {
780
+ return ResponseHelpers.createErrorResponse(`Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, 'ELEMENTOR_DATA_ERROR', 'DATA_ERROR', 'Could not retrieve or parse Elementor data for section update');
781
+ }
782
+ const elementorData = parsedResult.data;
783
+ let sectionFound = false;
784
+ let updatedWidgets = [];
785
+ const updateSectionWidgets = (elements) => {
786
+ for (let i = 0; i < elements.length; i++) {
787
+ const element = elements[i];
788
+ if (element.id === args.section_id) {
789
+ sectionFound = true;
790
+ for (const widgetUpdate of args.widgets_updates) {
791
+ const updated = updateWidgetInElements(element.elements || [], widgetUpdate);
792
+ if (updated) {
793
+ updatedWidgets.push(widgetUpdate.widget_id);
794
+ }
795
+ }
796
+ return true;
797
+ }
798
+ if (element.elements && element.elements.length > 0) {
799
+ if (updateSectionWidgets(element.elements)) {
800
+ return true;
801
+ }
802
+ }
803
+ }
804
+ return false;
805
+ };
806
+ const updateWidgetInElements = (elements, widgetUpdate) => {
807
+ for (const element of elements) {
808
+ if (element.id === widgetUpdate.widget_id) {
809
+ if (widgetUpdate.widget_settings) {
810
+ element.settings = { ...element.settings, ...widgetUpdate.widget_settings };
811
+ }
812
+ if (widgetUpdate.widget_content) {
813
+ ElementorHelpers.updateWidgetContent(element, widgetUpdate.widget_content);
814
+ }
815
+ return true;
816
+ }
817
+ if (element.elements && element.elements.length > 0) {
818
+ if (updateWidgetInElements(element.elements, widgetUpdate)) {
819
+ return true;
820
+ }
821
+ }
822
+ }
823
+ return false;
824
+ };
825
+ updateSectionWidgets(elementorData);
826
+ if (!sectionFound) {
827
+ return ResponseHelpers.createErrorResponse(`Section ID ${args.section_id} not found in Elementor data`, 'SECTION_NOT_FOUND', 'NOT_FOUND', `Could not locate section with ID ${args.section_id} in the page structure`);
828
+ }
829
+ const updateData = {
830
+ meta: {
831
+ _elementor_data: JSON.stringify(elementorData),
832
+ _elementor_edit_mode: 'builder',
833
+ },
834
+ };
835
+ const axios = this.wordPressClient.getAxiosInstance();
836
+ let response;
837
+ let postType = 'post';
838
+ try {
839
+ response = await axios.post(`posts/${args.post_id}`, updateData);
840
+ }
841
+ catch (postError) {
842
+ if (postError.response?.status === 404) {
843
+ try {
844
+ response = await axios.post(`pages/${args.post_id}`, updateData);
845
+ postType = 'page';
846
+ }
847
+ catch (pageError) {
848
+ return ResponseHelpers.createErrorResponse(`Post/Page ID ${args.post_id} not found in posts or pages`, 'POST_PAGE_NOT_FOUND', 'NOT_FOUND', 'Failed to update both post and page endpoints for section update');
849
+ }
850
+ }
851
+ else {
852
+ throw postError;
853
+ }
854
+ }
855
+ await ElementorHelpers.clearElementorCache(args.post_id);
856
+ return ResponseHelpers.createSuccessResponse({
857
+ operation_type: "update_section_widgets",
858
+ section_id: args.section_id,
859
+ post_id: args.post_id,
860
+ post_type: postType,
861
+ updated_widgets: updatedWidgets,
862
+ widgets_not_found: args.widgets_updates.filter(w => !updatedWidgets.includes(w.widget_id)).map(w => w.widget_id),
863
+ total_updates_requested: args.widgets_updates.length,
864
+ successful_updates: updatedWidgets.length,
865
+ cache_cleared: true
866
+ }, `Elementor section ${args.section_id} updated successfully with ${updatedWidgets.length}/${args.widgets_updates.length} widget updates`);
867
+ }
868
+ catch (error) {
869
+ return ResponseHelpers.createErrorResponse(`Failed to update Elementor section: ${error.response?.data?.message || error.message}`, 'UPDATE_SECTION_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
870
+ }
871
+ }
872
+ async getElementorDataSmart(args) {
873
+ const authCheck = this.wordPressClient.ensureAuthenticated();
874
+ if (authCheck)
875
+ return authCheck;
876
+ try {
877
+ console.error(`🔍 Getting smart Elementor data for ID: ${args.post_id}, element index: ${args.element_index || 0}`);
878
+ const parsedResult = await this.elementorHandler.safeGetElementorData(args.post_id);
879
+ if (!parsedResult.success || !parsedResult.data) {
880
+ return ResponseHelpers.createErrorResponse(`Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, 'ELEMENTOR_DATA_ERROR', 'DATA_ERROR', 'Could not retrieve or parse Elementor data for smart chunking');
881
+ }
882
+ const elementorData = parsedResult.data;
883
+ const maxDepth = args.max_depth || 2;
884
+ const elementIndex = args.element_index || 0;
885
+ const includeWidgetPreviews = args.include_widget_previews || false;
886
+ if (elementIndex >= elementorData.length) {
887
+ return ResponseHelpers.createErrorResponse(`Element index ${elementIndex} is out of range. Total top-level elements: ${elementorData.length}`, 'INDEX_OUT_OF_RANGE', 'VALIDATION_ERROR', 'Requested element index exceeds available elements');
888
+ }
889
+ // Function to limit depth and content of elements
890
+ const limitElementDepth = (element, currentDepth) => {
891
+ const limited = {
892
+ id: element.id,
893
+ elType: element.elType,
894
+ widgetType: element.widgetType || null,
895
+ isInner: element.isInner || false,
896
+ depth: currentDepth
897
+ };
898
+ // Add limited settings (exclude heavy content)
899
+ if (element.settings) {
900
+ limited.settings = {};
901
+ // Include only lightweight settings, exclude heavy content
902
+ const lightweightKeys = ['_column_size', '_inline_size', 'background_background', 'content_width', 'flex_direction', 'gap'];
903
+ lightweightKeys.forEach(key => {
904
+ if (element.settings[key] !== undefined) {
905
+ limited.settings[key] = element.settings[key];
906
+ }
907
+ });
908
+ // Add widget content preview if requested and if it's a widget
909
+ if (includeWidgetPreviews && element.widgetType && element.settings) {
910
+ if (element.settings.title) {
911
+ limited.content_preview = String(element.settings.title).substring(0, 100);
912
+ }
913
+ else if (element.settings.editor) {
914
+ limited.content_preview = String(element.settings.editor).replace(/<[^>]*>/g, '').substring(0, 100);
915
+ }
916
+ else if (element.settings.html) {
917
+ limited.content_preview = String(element.settings.html).replace(/<[^>]*>/g, '').substring(0, 100);
918
+ }
919
+ }
920
+ }
921
+ // Recursively process children up to max depth
922
+ if (element.elements && element.elements.length > 0 && currentDepth < maxDepth) {
923
+ limited.elements = element.elements.map((child) => limitElementDepth(child, currentDepth + 1));
924
+ limited.child_count = element.elements.length;
925
+ }
926
+ else if (element.elements && element.elements.length > 0) {
927
+ // If we've reached max depth, just show summary
928
+ limited.child_count = element.elements.length;
929
+ limited.child_types = [...new Set(element.elements.map((child) => child.elType || child.widgetType))];
930
+ }
931
+ return limited;
932
+ };
933
+ const targetElement = elementorData[elementIndex];
934
+ const limitedElement = limitElementDepth(targetElement, 0);
935
+ // Calculate approximate token size (rough estimate: 4 chars per token)
936
+ const estimatedTokens = Math.ceil(JSON.stringify(limitedElement).length / 4);
937
+ return ResponseHelpers.createSuccessResponse({
938
+ post_id: args.post_id,
939
+ element_index: elementIndex,
940
+ max_depth: maxDepth,
941
+ include_widget_previews: includeWidgetPreviews,
942
+ total_top_level_elements: elementorData.length,
943
+ element_data: limitedElement,
944
+ navigation: {
945
+ has_previous: elementIndex > 0,
946
+ has_next: elementIndex < elementorData.length - 1,
947
+ previous_index: elementIndex > 0 ? elementIndex - 1 : null,
948
+ next_index: elementIndex < elementorData.length - 1 ? elementIndex + 1 : null
949
+ },
950
+ estimated_tokens: estimatedTokens
951
+ }, `Retrieved smart element ${elementIndex + 1} of ${elementorData.length} with max depth ${maxDepth} (estimated ${estimatedTokens} tokens)`);
952
+ }
953
+ catch (error) {
954
+ return ResponseHelpers.createErrorResponse(`Failed to get smart Elementor data: ${error.message}`, 'GET_SMART_DATA_ERROR', 'API_ERROR', 'Operation failed');
955
+ }
956
+ }
957
+ async getElementorStructureSummary(args) {
958
+ const authCheck = this.wordPressClient.ensureAuthenticated();
959
+ if (authCheck)
960
+ return authCheck;
961
+ try {
962
+ console.error(`📊 Getting structure summary for ID: ${args.post_id}`);
963
+ const parsedResult = await this.elementorHandler.safeGetElementorData(args.post_id);
964
+ if (!parsedResult.success || !parsedResult.data) {
965
+ return ResponseHelpers.createErrorResponse(`Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, 'ELEMENTOR_DATA_ERROR', 'DATA_ERROR', 'Could not retrieve or parse Elementor data for structure summary');
966
+ }
967
+ const elementorData = parsedResult.data;
968
+ const maxDepth = args.max_depth || 4;
969
+ // Function to create structure summary
970
+ const createStructureSummary = (elements, currentDepth = 0) => {
971
+ return elements.map(element => {
972
+ const summary = {
973
+ id: element.id,
974
+ type: element.elType,
975
+ depth: currentDepth
976
+ };
977
+ if (element.widgetType) {
978
+ summary.widget_type = element.widgetType;
979
+ }
980
+ if (element.settings) {
981
+ // Add basic layout info
982
+ if (element.settings._column_size) {
983
+ summary.column_size = element.settings._column_size;
984
+ }
985
+ if (element.settings.content_width) {
986
+ summary.content_width = element.settings.content_width;
987
+ }
988
+ }
989
+ if (element.elements && element.elements.length > 0) {
990
+ summary.child_count = element.elements.length;
991
+ if (currentDepth < maxDepth) {
992
+ summary.children = createStructureSummary(element.elements, currentDepth + 1);
993
+ }
994
+ else {
995
+ summary.child_types = [...new Set(element.elements.map((child) => child.elType || child.widgetType))];
996
+ }
997
+ }
998
+ return summary;
999
+ });
1000
+ };
1001
+ const structureSummary = createStructureSummary(elementorData);
1002
+ return ResponseHelpers.createSuccessResponse({
1003
+ post_id: args.post_id,
1004
+ max_depth: maxDepth,
1005
+ total_top_level_elements: elementorData.length,
1006
+ structure: structureSummary
1007
+ }, `Retrieved structure summary for post ID ${args.post_id} with ${elementorData.length} top-level elements`);
1008
+ }
1009
+ catch (error) {
1010
+ return ResponseHelpers.createErrorResponse(`Failed to get structure summary: ${error.message}`, 'GET_STRUCTURE_SUMMARY_ERROR', 'API_ERROR', 'Operation failed');
1011
+ }
1012
+ }
1013
+ async backupElementorData(args) {
1014
+ const authCheck = this.wordPressClient.ensureAuthenticated();
1015
+ if (authCheck)
1016
+ return authCheck;
1017
+ try {
1018
+ const parsedResult = await this.elementorHandler.safeGetElementorData(args.post_id);
1019
+ if (!parsedResult.success || !parsedResult.data) {
1020
+ return ResponseHelpers.createErrorResponse(`Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, 'ELEMENTOR_DATA_ERROR', 'DATA_ERROR', 'Could not retrieve or parse Elementor data for backup');
1021
+ }
1022
+ const timestamp = new Date().toISOString();
1023
+ const backupName = args.backup_name || `backup_${timestamp}`;
1024
+ const axios = this.wordPressClient.getAxiosInstance();
1025
+ let postInfo;
1026
+ let postType = 'post';
1027
+ try {
1028
+ postInfo = await axios.get(`posts/${args.post_id}`, {
1029
+ params: { context: 'edit' }
1030
+ });
1031
+ }
1032
+ catch (postError) {
1033
+ if (postError.response?.status === 404) {
1034
+ try {
1035
+ postInfo = await axios.get(`pages/${args.post_id}`, {
1036
+ params: { context: 'edit' }
1037
+ });
1038
+ postType = 'page';
1039
+ }
1040
+ catch (pageError) {
1041
+ return ResponseHelpers.createErrorResponse(`Post/Page ID ${args.post_id} not found in posts or pages`, 'POST_PAGE_NOT_FOUND', 'NOT_FOUND', 'Failed to access both post and page endpoints for backup');
1042
+ }
1043
+ }
1044
+ else {
1045
+ throw postError;
1046
+ }
1047
+ }
1048
+ const backupKey = `_elementor_data_backup_${Date.now()}`;
1049
+ const backupMeta = {
1050
+ meta: {
1051
+ [backupKey]: JSON.stringify({
1052
+ backup_name: backupName,
1053
+ timestamp: timestamp,
1054
+ post_id: args.post_id,
1055
+ post_type: postType,
1056
+ post_title: postInfo.data.title.rendered || postInfo.data.title.raw,
1057
+ elementor_data: JSON.stringify(parsedResult.data)
1058
+ })
1059
+ }
1060
+ };
1061
+ let response;
1062
+ if (postType === 'page') {
1063
+ response = await axios.post(`pages/${args.post_id}`, backupMeta);
1064
+ }
1065
+ else {
1066
+ response = await axios.post(`posts/${args.post_id}`, backupMeta);
1067
+ }
1068
+ return ResponseHelpers.createSuccessResponse({
1069
+ operation_type: "backup_elementor_data",
1070
+ post_id: args.post_id,
1071
+ post_type: postType,
1072
+ post_title: postInfo.data.title.rendered || postInfo.data.title.raw,
1073
+ backup_name: backupName,
1074
+ backup_key: backupKey,
1075
+ timestamp: timestamp,
1076
+ backup_storage: "wordpress_meta",
1077
+ data_size: JSON.stringify(parsedResult.data).length
1078
+ }, `Elementor data backup created successfully! Backup key: ${backupKey}`);
1079
+ }
1080
+ catch (error) {
1081
+ return ResponseHelpers.createErrorResponse(`Failed to backup Elementor data: ${error.response?.data?.message || error.message}`, 'BACKUP_ERROR', 'API_ERROR', `HTTP ${error.response?.status}: ${error.response?.statusText}`);
1082
+ }
1083
+ }
1084
+ // Section and Container Creation
1085
+ async createElementorSection(args) {
1086
+ const authCheck = this.wordPressClient.ensureAuthenticated();
1087
+ if (authCheck)
1088
+ return authCheck;
1089
+ try {
1090
+ const parsedResult = await this.elementorHandler.safeGetElementorData(args.post_id);
1091
+ let elementorData = [];
1092
+ if (parsedResult.success && parsedResult.data) {
1093
+ elementorData = parsedResult.data;
1094
+ }
1095
+ const newSection = ElementorHelpers.createElementorSection(args.columns || 1, args.section_settings || {});
1096
+ if (args.position !== undefined && args.position >= 0 && args.position < elementorData.length) {
1097
+ elementorData.splice(args.position, 0, newSection);
1098
+ }
1099
+ else {
1100
+ elementorData.push(newSection);
1101
+ }
1102
+ await this.updateElementorData({
1103
+ post_id: args.post_id,
1104
+ elementor_data: JSON.stringify(elementorData)
1105
+ });
1106
+ return ResponseHelpers.createSuccessResponse({
1107
+ operation_type: "create_section",
1108
+ section_id: newSection.id,
1109
+ columns: args.columns || 1,
1110
+ position: args.position || elementorData.length - 1,
1111
+ post_id: args.post_id,
1112
+ section_settings: args.section_settings || {},
1113
+ column_ids: newSection.elements.map((col) => col.id)
1114
+ }, `Section created successfully! Section ID: ${newSection.id} with ${args.columns || 1} columns`);
1115
+ }
1116
+ catch (error) {
1117
+ return ResponseHelpers.createErrorResponse(`Failed to create section: ${error.message}`, 'CREATE_SECTION_ERROR', 'API_ERROR', `Section creation failed: ${error.message}`);
1118
+ }
1119
+ }
1120
+ async createElementorContainer(args) {
1121
+ const authCheck = this.wordPressClient.ensureAuthenticated();
1122
+ if (authCheck)
1123
+ return authCheck;
1124
+ try {
1125
+ const parsedResult = await this.elementorHandler.safeGetElementorData(args.post_id);
1126
+ let elementorData = [];
1127
+ if (parsedResult.success && parsedResult.data) {
1128
+ elementorData = parsedResult.data;
1129
+ }
1130
+ const containerId = ElementorHelpers.generateElementorId();
1131
+ const newContainer = {
1132
+ id: containerId,
1133
+ elType: 'container',
1134
+ isInner: false,
1135
+ settings: args.container_settings || {},
1136
+ elements: [],
1137
+ widgetType: null
1138
+ };
1139
+ if (args.position !== undefined && args.position >= 0 && args.position < elementorData.length) {
1140
+ elementorData.splice(args.position, 0, newContainer);
1141
+ }
1142
+ else {
1143
+ elementorData.push(newContainer);
1144
+ }
1145
+ await this.updateElementorData({
1146
+ post_id: args.post_id,
1147
+ elementor_data: JSON.stringify(elementorData)
1148
+ });
1149
+ return ResponseHelpers.createSuccessResponse({
1150
+ operation_type: "create_container",
1151
+ container_id: containerId,
1152
+ position: args.position || elementorData.length - 1,
1153
+ post_id: args.post_id,
1154
+ container_settings: args.container_settings || {}
1155
+ }, `Container created successfully! Container ID: ${containerId}`);
1156
+ }
1157
+ catch (error) {
1158
+ return ResponseHelpers.createErrorResponse(`Failed to create container: ${error.message}`, 'CREATE_CONTAINER_ERROR', 'API_ERROR', `Container creation failed: ${error.message}`);
1159
+ }
1160
+ }
1161
+ async addColumnToSection(args) {
1162
+ const authCheck = this.wordPressClient.ensureAuthenticated();
1163
+ if (authCheck)
1164
+ return authCheck;
1165
+ try {
1166
+ const parsedResult = await this.elementorHandler.safeGetElementorData(args.post_id);
1167
+ if (!parsedResult.success || !parsedResult.data) {
1168
+ return ResponseHelpers.createErrorResponse(`Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, 'ELEMENTOR_DATA_ERROR', 'DATA_ERROR', 'Could not retrieve or parse Elementor data for adding columns');
1169
+ }
1170
+ const elementorData = parsedResult.data;
1171
+ const section = ElementorHelpers.findElementRecursive(elementorData, args.section_id);
1172
+ if (!section) {
1173
+ return ResponseHelpers.createErrorResponse(`Section ID ${args.section_id} not found in Elementor data`, 'SECTION_NOT_FOUND', 'NOT_FOUND', `Could not locate section with ID ${args.section_id}`);
1174
+ }
1175
+ const columnsToAdd = args.columns_to_add || 1;
1176
+ const currentColumns = section.elements.length;
1177
+ const newColumnCount = currentColumns + columnsToAdd;
1178
+ const newColumnSize = Math.floor(100 / newColumnCount);
1179
+ // Adjust existing columns
1180
+ section.elements.forEach((column) => {
1181
+ column.settings._column_size = newColumnSize;
1182
+ });
1183
+ // Add new columns
1184
+ for (let i = 0; i < columnsToAdd; i++) {
1185
+ section.elements.push(ElementorHelpers.createElementorColumn(newColumnSize));
1186
+ }
1187
+ await this.updateElementorData({
1188
+ post_id: args.post_id,
1189
+ elementor_data: JSON.stringify(elementorData)
1190
+ });
1191
+ return ResponseHelpers.createSuccessResponse({
1192
+ operation_type: "add_columns_to_section",
1193
+ section_id: args.section_id,
1194
+ columns_added: columnsToAdd,
1195
+ total_columns: newColumnCount,
1196
+ post_id: args.post_id
1197
+ }, `Added ${columnsToAdd} column(s) to section ${args.section_id}. Total columns: ${newColumnCount}`);
1198
+ }
1199
+ catch (error) {
1200
+ return ResponseHelpers.createErrorResponse(`Failed to add columns to section: ${error.message}`, 'ADD_COLUMNS_ERROR', 'API_ERROR', `Failed to add columns: ${error.message}`);
1201
+ }
1202
+ }
1203
+ async duplicateSection(args) {
1204
+ const authCheck = this.wordPressClient.ensureAuthenticated();
1205
+ if (authCheck)
1206
+ return authCheck;
1207
+ try {
1208
+ const parsedResult = await this.elementorHandler.safeGetElementorData(args.post_id);
1209
+ if (!parsedResult.success || !parsedResult.data) {
1210
+ return ResponseHelpers.createErrorResponse(`Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, 'ELEMENTOR_DATA_ERROR', 'DATA_ERROR', 'Could not retrieve or parse Elementor data for duplicating section');
1211
+ }
1212
+ const elementorData = parsedResult.data;
1213
+ const section = ElementorHelpers.findElementRecursive(elementorData, args.section_id);
1214
+ if (!section) {
1215
+ return ResponseHelpers.createErrorResponse(`Section ID ${args.section_id} not found in Elementor data`, 'SECTION_NOT_FOUND', 'NOT_FOUND', `Could not locate section with ID ${args.section_id}`);
1216
+ }
1217
+ // Deep clone the section and generate new IDs
1218
+ const duplicatedSection = JSON.parse(JSON.stringify(section));
1219
+ duplicatedSection.id = ElementorHelpers.generateElementorId();
1220
+ // Recursively update all element IDs
1221
+ const updateIds = (element) => {
1222
+ element.id = ElementorHelpers.generateElementorId();
1223
+ if (element.elements) {
1224
+ element.elements.forEach(updateIds);
1225
+ }
1226
+ };
1227
+ duplicatedSection.elements.forEach(updateIds);
1228
+ // Find the original section index
1229
+ const originalIndex = elementorData.findIndex((el) => el.id === args.section_id);
1230
+ const insertIndex = args.position !== undefined ? args.position : originalIndex + 1;
1231
+ elementorData.splice(insertIndex, 0, duplicatedSection);
1232
+ await this.updateElementorData({
1233
+ post_id: args.post_id,
1234
+ elementor_data: JSON.stringify(elementorData)
1235
+ });
1236
+ return ResponseHelpers.createSuccessResponse({
1237
+ operation_type: "duplicate_section",
1238
+ original_section_id: args.section_id,
1239
+ duplicated_section_id: duplicatedSection.id,
1240
+ position: insertIndex,
1241
+ post_id: args.post_id
1242
+ }, `Section ${args.section_id} duplicated successfully! New section ID: ${duplicatedSection.id}`);
1243
+ }
1244
+ catch (error) {
1245
+ return ResponseHelpers.createErrorResponse(`Failed to duplicate section: ${error.message}`, 'DUPLICATE_SECTION_ERROR', 'API_ERROR', `Failed to duplicate section: ${error.message}`);
1246
+ }
1247
+ }
1248
+ // Widget Addition and Management
1249
+ async addWidgetToSection(args) {
1250
+ const authCheck = this.wordPressClient.ensureAuthenticated();
1251
+ if (authCheck)
1252
+ return authCheck;
1253
+ try {
1254
+ const parsedResult = await this.elementorHandler.safeGetElementorData(args.post_id);
1255
+ if (!parsedResult.success || !parsedResult.data) {
1256
+ return ResponseHelpers.createErrorResponse(`Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, 'ELEMENTOR_DATA_ERROR', 'DATA_ERROR', 'Could not retrieve or parse Elementor data for adding widget');
1257
+ }
1258
+ const elementorData = parsedResult.data;
1259
+ let targetColumn = null;
1260
+ if (args.column_id) {
1261
+ targetColumn = ElementorHelpers.findElementRecursive(elementorData, args.column_id);
1262
+ }
1263
+ else if (args.section_id) {
1264
+ const section = ElementorHelpers.findElementRecursive(elementorData, args.section_id);
1265
+ if (section && section.elements && section.elements.length > 0) {
1266
+ targetColumn = section.elements[0]; // Use first column
1267
+ }
1268
+ }
1269
+ if (!targetColumn) {
1270
+ return ResponseHelpers.createErrorResponse(`Target column not found. Please specify a valid section_id or column_id`, 'TARGET_COLUMN_NOT_FOUND', 'NOT_FOUND', 'Could not locate target column for widget placement');
1271
+ }
1272
+ const newWidget = {
1273
+ id: ElementorHelpers.generateElementorId(),
1274
+ elType: 'widget',
1275
+ isInner: false,
1276
+ settings: args.widget_settings || {},
1277
+ elements: [],
1278
+ widgetType: args.widget_type
1279
+ };
1280
+ if (args.position !== undefined && args.position >= 0 && args.position < targetColumn.elements.length) {
1281
+ targetColumn.elements.splice(args.position, 0, newWidget);
1282
+ }
1283
+ else {
1284
+ targetColumn.elements.push(newWidget);
1285
+ }
1286
+ await this.updateElementorData({
1287
+ post_id: args.post_id,
1288
+ elementor_data: JSON.stringify(elementorData)
1289
+ });
1290
+ return ResponseHelpers.createSuccessResponse({
1291
+ operation_type: "add_widget_to_section",
1292
+ widget_id: newWidget.id,
1293
+ widget_type: args.widget_type,
1294
+ target_column_id: targetColumn.id,
1295
+ position: args.position || targetColumn.elements.length - 1,
1296
+ post_id: args.post_id,
1297
+ widget_settings: args.widget_settings || {}
1298
+ }, `Widget ${args.widget_type} added successfully! Widget ID: ${newWidget.id}`);
1299
+ }
1300
+ catch (error) {
1301
+ return ResponseHelpers.createErrorResponse(`Failed to add widget to section: ${error.message}`, 'ADD_WIDGET_ERROR', 'API_ERROR', `Failed to add widget: ${error.message}`);
1302
+ }
1303
+ }
1304
+ async insertWidgetAtPosition(args) {
1305
+ const authCheck = this.wordPressClient.ensureAuthenticated();
1306
+ if (authCheck)
1307
+ return authCheck;
1308
+ try {
1309
+ const parsedResult = await this.elementorHandler.safeGetElementorData(args.post_id);
1310
+ if (!parsedResult.success || !parsedResult.data) {
1311
+ return ResponseHelpers.createErrorResponse(`Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, 'ELEMENTOR_DATA_ERROR', 'DATA_ERROR', 'Could not retrieve or parse Elementor data for inserting widget');
1312
+ }
1313
+ const elementorData = parsedResult.data;
1314
+ const targetElement = ElementorHelpers.findElementRecursive(elementorData, args.target_element_id);
1315
+ if (!targetElement) {
1316
+ return ResponseHelpers.createErrorResponse(`Target element ID ${args.target_element_id} not found`, 'TARGET_ELEMENT_NOT_FOUND', 'NOT_FOUND', `Could not locate element with ID ${args.target_element_id}`);
1317
+ }
1318
+ const newWidget = {
1319
+ id: ElementorHelpers.generateElementorId(),
1320
+ elType: 'widget',
1321
+ isInner: false,
1322
+ settings: args.widget_settings || {},
1323
+ elements: [],
1324
+ widgetType: args.widget_type
1325
+ };
1326
+ const insertPosition = args.insert_position || 'after';
1327
+ // Find parent container
1328
+ const findParentContainer = (elements, elementId) => {
1329
+ for (const element of elements) {
1330
+ if (element.elements) {
1331
+ const childIndex = element.elements.findIndex((child) => child.id === elementId);
1332
+ if (childIndex !== -1) {
1333
+ return { parent: element, childIndex };
1334
+ }
1335
+ const result = findParentContainer(element.elements, elementId);
1336
+ if (result)
1337
+ return result;
1338
+ }
1339
+ }
1340
+ return null;
1341
+ };
1342
+ const parentInfo = findParentContainer(elementorData, args.target_element_id);
1343
+ if (parentInfo) {
1344
+ const { parent, childIndex } = parentInfo;
1345
+ switch (insertPosition) {
1346
+ case 'before':
1347
+ parent.elements.splice(childIndex, 0, newWidget);
1348
+ break;
1349
+ case 'after':
1350
+ parent.elements.splice(childIndex + 1, 0, newWidget);
1351
+ break;
1352
+ case 'inside':
1353
+ targetElement.elements.push(newWidget);
1354
+ break;
1355
+ }
1356
+ }
1357
+ else {
1358
+ // Target is a top-level element
1359
+ const topIndex = elementorData.findIndex((el) => el.id === args.target_element_id);
1360
+ if (topIndex !== -1) {
1361
+ if (insertPosition === 'before') {
1362
+ elementorData.splice(topIndex, 0, newWidget);
1363
+ }
1364
+ else {
1365
+ elementorData.splice(topIndex + 1, 0, newWidget);
1366
+ }
1367
+ }
1368
+ }
1369
+ await this.updateElementorData({
1370
+ post_id: args.post_id,
1371
+ elementor_data: JSON.stringify(elementorData)
1372
+ });
1373
+ return ResponseHelpers.createSuccessResponse({
1374
+ operation_type: "insert_widget_at_position",
1375
+ widget_id: newWidget.id,
1376
+ widget_type: args.widget_type,
1377
+ target_element_id: args.target_element_id,
1378
+ insert_position: insertPosition,
1379
+ post_id: args.post_id
1380
+ }, `Widget ${args.widget_type} inserted ${insertPosition} element ${args.target_element_id}! Widget ID: ${newWidget.id}`);
1381
+ }
1382
+ catch (error) {
1383
+ return ResponseHelpers.createErrorResponse(`Failed to insert widget at position: ${error.message}`, 'INSERT_WIDGET_ERROR', 'API_ERROR', `Failed to insert widget: ${error.message}`);
1384
+ }
1385
+ }
1386
+ async cloneWidget(args) {
1387
+ const authCheck = this.wordPressClient.ensureAuthenticated();
1388
+ if (authCheck)
1389
+ return authCheck;
1390
+ try {
1391
+ const parsedResult = await this.elementorHandler.safeGetElementorData(args.post_id);
1392
+ if (!parsedResult.success || !parsedResult.data) {
1393
+ return ResponseHelpers.createErrorResponse(`Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, 'ELEMENTOR_DATA_ERROR', 'DATA_ERROR', 'Could not retrieve or parse Elementor data for cloning widget');
1394
+ }
1395
+ const elementorData = parsedResult.data;
1396
+ const sourceWidget = ElementorHelpers.findElementRecursive(elementorData, args.widget_id);
1397
+ if (!sourceWidget) {
1398
+ return ResponseHelpers.createErrorResponse(`Widget ID ${args.widget_id} not found`, 'WIDGET_NOT_FOUND', 'NOT_FOUND', `Could not locate widget with ID ${args.widget_id}`);
1399
+ }
1400
+ // Deep clone the widget and generate new ID
1401
+ const clonedWidget = JSON.parse(JSON.stringify(sourceWidget));
1402
+ clonedWidget.id = ElementorHelpers.generateElementorId();
1403
+ // Clone to specific position or after original
1404
+ if (args.target_element_id) {
1405
+ return await this.insertWidgetAtPosition({
1406
+ post_id: args.post_id,
1407
+ widget_type: clonedWidget.widgetType,
1408
+ target_element_id: args.target_element_id,
1409
+ insert_position: args.insert_position || 'after',
1410
+ widget_settings: clonedWidget.settings
1411
+ });
1412
+ }
1413
+ else {
1414
+ // Clone after original widget
1415
+ return await this.insertWidgetAtPosition({
1416
+ post_id: args.post_id,
1417
+ widget_type: clonedWidget.widgetType,
1418
+ target_element_id: args.widget_id,
1419
+ insert_position: 'after',
1420
+ widget_settings: clonedWidget.settings
1421
+ });
1422
+ }
1423
+ }
1424
+ catch (error) {
1425
+ return ResponseHelpers.createErrorResponse(`Failed to clone widget: ${error.message}`, 'CLONE_WIDGET_ERROR', 'API_ERROR', `Failed to clone widget: ${error.message}`);
1426
+ }
1427
+ }
1428
+ async moveWidget(args) {
1429
+ const authCheck = this.wordPressClient.ensureAuthenticated();
1430
+ if (authCheck)
1431
+ return authCheck;
1432
+ try {
1433
+ const parsedResult = await this.elementorHandler.safeGetElementorData(args.post_id);
1434
+ if (!parsedResult.success || !parsedResult.data) {
1435
+ return ResponseHelpers.createErrorResponse(`Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, 'ELEMENTOR_DATA_ERROR', 'DATA_ERROR', 'Could not retrieve or parse Elementor data for moving widget');
1436
+ }
1437
+ const elementorData = parsedResult.data;
1438
+ // Find and remove the widget from its current location
1439
+ let widgetToMove = null;
1440
+ const removeWidget = (elements) => {
1441
+ for (let i = 0; i < elements.length; i++) {
1442
+ const element = elements[i];
1443
+ if (element.id === args.widget_id) {
1444
+ widgetToMove = elements.splice(i, 1)[0];
1445
+ return true;
1446
+ }
1447
+ if (element.elements && removeWidget(element.elements)) {
1448
+ return true;
1449
+ }
1450
+ }
1451
+ return false;
1452
+ };
1453
+ if (!removeWidget(elementorData)) {
1454
+ return ResponseHelpers.createErrorResponse(`Widget ID ${args.widget_id} not found`, 'WIDGET_NOT_FOUND', 'NOT_FOUND', `Could not locate widget with ID ${args.widget_id}`);
1455
+ }
1456
+ // Find target location
1457
+ let targetColumn = null;
1458
+ if (args.target_column_id) {
1459
+ targetColumn = ElementorHelpers.findElementRecursive(elementorData, args.target_column_id);
1460
+ }
1461
+ else if (args.target_section_id) {
1462
+ const section = ElementorHelpers.findElementRecursive(elementorData, args.target_section_id);
1463
+ if (section && section.elements && section.elements.length > 0) {
1464
+ targetColumn = section.elements[0]; // Use first column
1465
+ }
1466
+ }
1467
+ if (!targetColumn) {
1468
+ return ResponseHelpers.createErrorResponse(`Target location not found. Please specify a valid target_section_id or target_column_id`, 'TARGET_LOCATION_NOT_FOUND', 'NOT_FOUND', 'Could not locate target location for widget move');
1469
+ }
1470
+ // Insert widget at target location
1471
+ if (args.position !== undefined && args.position >= 0 && args.position < targetColumn.elements.length) {
1472
+ targetColumn.elements.splice(args.position, 0, widgetToMove);
1473
+ }
1474
+ else {
1475
+ targetColumn.elements.push(widgetToMove);
1476
+ }
1477
+ await this.updateElementorData({
1478
+ post_id: args.post_id,
1479
+ elementor_data: JSON.stringify(elementorData)
1480
+ });
1481
+ return ResponseHelpers.createSuccessResponse({
1482
+ operation_type: "move_widget",
1483
+ widget_id: args.widget_id,
1484
+ target_column_id: targetColumn.id,
1485
+ position: args.position || targetColumn.elements.length - 1,
1486
+ post_id: args.post_id
1487
+ }, `Widget ${args.widget_id} moved successfully to column ${targetColumn.id}`);
1488
+ }
1489
+ catch (error) {
1490
+ return ResponseHelpers.createErrorResponse(`Failed to move widget: ${error.message}`, 'MOVE_WIDGET_ERROR', 'API_ERROR', `Failed to move widget: ${error.message}`);
1491
+ }
1492
+ }
1493
+ // Element Management
1494
+ async deleteElementorElement(args) {
1495
+ const authCheck = this.wordPressClient.ensureAuthenticated();
1496
+ if (authCheck)
1497
+ return authCheck;
1498
+ try {
1499
+ const parsedResult = await this.elementorHandler.safeGetElementorData(args.post_id);
1500
+ if (!parsedResult.success || !parsedResult.data) {
1501
+ return ResponseHelpers.createErrorResponse(`Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, 'ELEMENTOR_DATA_ERROR', 'DATA_ERROR', 'Could not retrieve or parse Elementor data for deleting element');
1502
+ }
1503
+ const elementorData = parsedResult.data;
1504
+ // Find and remove the element
1505
+ const removeElement = (elements) => {
1506
+ for (let i = 0; i < elements.length; i++) {
1507
+ const element = elements[i];
1508
+ if (element.id === args.element_id) {
1509
+ elements.splice(i, 1);
1510
+ return true;
1511
+ }
1512
+ if (element.elements && removeElement(element.elements)) {
1513
+ return true;
1514
+ }
1515
+ }
1516
+ return false;
1517
+ };
1518
+ if (!removeElement(elementorData)) {
1519
+ return ResponseHelpers.createErrorResponse(`Element ID ${args.element_id} not found`, 'ELEMENT_NOT_FOUND', 'NOT_FOUND', `Could not locate element with ID ${args.element_id}`);
1520
+ }
1521
+ await this.updateElementorData({
1522
+ post_id: args.post_id,
1523
+ elementor_data: JSON.stringify(elementorData)
1524
+ });
1525
+ return ResponseHelpers.createSuccessResponse({
1526
+ operation_type: "delete_element",
1527
+ element_id: args.element_id,
1528
+ post_id: args.post_id
1529
+ }, `Element ${args.element_id} deleted successfully`);
1530
+ }
1531
+ catch (error) {
1532
+ return ResponseHelpers.createErrorResponse(`Failed to delete element: ${error.message}`, 'DELETE_ELEMENT_ERROR', 'API_ERROR', `Failed to delete element: ${error.message}`);
1533
+ }
1534
+ }
1535
+ async reorderElements(args) {
1536
+ const authCheck = this.wordPressClient.ensureAuthenticated();
1537
+ if (authCheck)
1538
+ return authCheck;
1539
+ try {
1540
+ const parsedResult = await this.elementorHandler.safeGetElementorData(args.post_id);
1541
+ if (!parsedResult.success || !parsedResult.data) {
1542
+ return ResponseHelpers.createErrorResponse(`Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, 'ELEMENTOR_DATA_ERROR', 'DATA_ERROR', 'Could not retrieve or parse Elementor data for reordering elements');
1543
+ }
1544
+ const elementorData = parsedResult.data;
1545
+ const container = ElementorHelpers.findElementRecursive(elementorData, args.container_id);
1546
+ if (!container) {
1547
+ return ResponseHelpers.createErrorResponse(`Container ID ${args.container_id} not found`, 'CONTAINER_NOT_FOUND', 'NOT_FOUND', `Could not locate container with ID ${args.container_id}`);
1548
+ }
1549
+ // Create a map of current elements
1550
+ const elementMap = new Map();
1551
+ container.elements.forEach((element) => {
1552
+ elementMap.set(element.id, element);
1553
+ });
1554
+ // Reorder elements according to provided order
1555
+ const reorderedElements = [];
1556
+ for (const elementId of args.element_ids) {
1557
+ if (elementMap.has(elementId)) {
1558
+ reorderedElements.push(elementMap.get(elementId));
1559
+ elementMap.delete(elementId);
1560
+ }
1561
+ }
1562
+ // Add any remaining elements that weren't specified
1563
+ elementMap.forEach(element => {
1564
+ reorderedElements.push(element);
1565
+ });
1566
+ container.elements = reorderedElements;
1567
+ await this.updateElementorData({
1568
+ post_id: args.post_id,
1569
+ elementor_data: JSON.stringify(elementorData)
1570
+ });
1571
+ return ResponseHelpers.createSuccessResponse({
1572
+ operation_type: "reorder_elements",
1573
+ container_id: args.container_id,
1574
+ element_ids: args.element_ids,
1575
+ total_elements: reorderedElements.length,
1576
+ post_id: args.post_id
1577
+ }, `Elements in container ${args.container_id} reordered successfully`);
1578
+ }
1579
+ catch (error) {
1580
+ return ResponseHelpers.createErrorResponse(`Failed to reorder elements: ${error.message}`, 'REORDER_ELEMENTS_ERROR', 'API_ERROR', `Failed to reorder elements: ${error.message}`);
1581
+ }
1582
+ }
1583
+ async copyElementSettings(args) {
1584
+ const authCheck = this.wordPressClient.ensureAuthenticated();
1585
+ if (authCheck)
1586
+ return authCheck;
1587
+ try {
1588
+ const parsedResult = await this.elementorHandler.safeGetElementorData(args.post_id);
1589
+ if (!parsedResult.success || !parsedResult.data) {
1590
+ return ResponseHelpers.createErrorResponse(`Failed to get Elementor data for post/page ID ${args.post_id}: ${parsedResult.error || 'Unknown error'}`, 'ELEMENTOR_DATA_ERROR', 'DATA_ERROR', 'Could not retrieve or parse Elementor data for copying settings');
1591
+ }
1592
+ const elementorData = parsedResult.data;
1593
+ const sourceElement = ElementorHelpers.findElementRecursive(elementorData, args.source_element_id);
1594
+ const targetElement = ElementorHelpers.findElementRecursive(elementorData, args.target_element_id);
1595
+ if (!sourceElement) {
1596
+ return ResponseHelpers.createErrorResponse(`Source element ID ${args.source_element_id} not found`, 'SOURCE_ELEMENT_NOT_FOUND', 'NOT_FOUND', `Could not locate source element with ID ${args.source_element_id}`);
1597
+ }
1598
+ if (!targetElement) {
1599
+ return ResponseHelpers.createErrorResponse(`Target element ID ${args.target_element_id} not found`, 'TARGET_ELEMENT_NOT_FOUND', 'NOT_FOUND', `Could not locate target element with ID ${args.target_element_id}`);
1600
+ }
1601
+ // Copy settings
1602
+ if (args.settings_to_copy && args.settings_to_copy.length > 0) {
1603
+ // Copy specific settings
1604
+ args.settings_to_copy.forEach(setting => {
1605
+ if (sourceElement.settings && sourceElement.settings[setting] !== undefined) {
1606
+ targetElement.settings = targetElement.settings || {};
1607
+ targetElement.settings[setting] = sourceElement.settings[setting];
1608
+ }
1609
+ });
1610
+ }
1611
+ else {
1612
+ // Copy all settings
1613
+ targetElement.settings = { ...sourceElement.settings };
1614
+ }
1615
+ await this.updateElementorData({
1616
+ post_id: args.post_id,
1617
+ elementor_data: JSON.stringify(elementorData)
1618
+ });
1619
+ return ResponseHelpers.createSuccessResponse({
1620
+ operation_type: "copy_element_settings",
1621
+ source_element_id: args.source_element_id,
1622
+ target_element_id: args.target_element_id,
1623
+ settings_copied: args.settings_to_copy || 'all',
1624
+ post_id: args.post_id
1625
+ }, `Settings copied from ${args.source_element_id} to ${args.target_element_id} successfully`);
1626
+ }
1627
+ catch (error) {
1628
+ return ResponseHelpers.createErrorResponse(`Failed to copy element settings: ${error.message}`, 'COPY_SETTINGS_ERROR', 'API_ERROR', `Failed to copy settings: ${error.message}`);
1629
+ }
1630
+ }
1631
+ async getPageStructure(args) {
1632
+ return await this.elementorHandler.getPageStructure(args);
1633
+ }
1634
+ async rebuildPageStructure(args) {
1635
+ const authCheck = this.wordPressClient.ensureAuthenticated();
1636
+ if (authCheck)
1637
+ return authCheck;
1638
+ return ResponseHelpers.createErrorResponse('Rebuild page structure not yet implemented. Please use WordPress admin interface to rebuild page structure.', 'NOT_IMPLEMENTED', 'FEATURE_NOT_AVAILABLE', 'Use WordPress admin interface for page structure rebuilding');
1639
+ }
1640
+ async validateElementorData(args) {
1641
+ const authCheck = this.wordPressClient.ensureAuthenticated();
1642
+ if (authCheck)
1643
+ return authCheck;
1644
+ try {
1645
+ const parsedResult = await this.elementorHandler.safeGetElementorData(args.post_id);
1646
+ if (!parsedResult.success) {
1647
+ return ResponseHelpers.createErrorResponse(`Validation failed: ${parsedResult.error}`, 'VALIDATION_FAILED', 'DATA_ERROR', 'Elementor data could not be parsed or validated');
1648
+ }
1649
+ const elementorData = parsedResult.data;
1650
+ const issues = [];
1651
+ // Basic validation checks
1652
+ const validateElement = (element, path) => {
1653
+ if (!element.id) {
1654
+ issues.push(`Missing ID at ${path}`);
1655
+ }
1656
+ if (!element.elType) {
1657
+ issues.push(`Missing elType at ${path}`);
1658
+ }
1659
+ if (element.elements) {
1660
+ element.elements.forEach((child, index) => {
1661
+ validateElement(child, `${path}.elements[${index}]`);
1662
+ });
1663
+ }
1664
+ };
1665
+ elementorData.forEach((element, index) => {
1666
+ validateElement(element, `root[${index}]`);
1667
+ });
1668
+ return ResponseHelpers.createSuccessResponse({
1669
+ operation_type: "validate_elementor_data",
1670
+ post_id: args.post_id,
1671
+ is_valid: issues.length === 0,
1672
+ total_elements: elementorData.length,
1673
+ issues: issues,
1674
+ validation_passed: issues.length === 0
1675
+ }, `Elementor data validation ${issues.length === 0 ? 'passed' : 'failed'} for post ID ${args.post_id}`);
1676
+ }
1677
+ catch (error) {
1678
+ return ResponseHelpers.createErrorResponse(`Failed to validate Elementor data: ${error.message}`, 'VALIDATION_ERROR', 'API_ERROR', `Validation process failed: ${error.message}`);
1679
+ }
1680
+ }
1681
+ }
1682
+ //# sourceMappingURL=tool-handlers.js.map