@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.
- package/LICENSE +21 -0
- package/README.md +381 -0
- package/client-config.json +13 -0
- package/dist/elementor-handler.d.ts +51 -0
- package/dist/elementor-handler.d.ts.map +1 -0
- package/dist/elementor-handler.js +358 -0
- package/dist/elementor-handler.js.map +1 -0
- package/dist/helpers.d.ts +24 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +107 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index-backup.d.ts +3 -0
- package/dist/index-backup.d.ts.map +1 -0
- package/dist/index-backup.js +3752 -0
- package/dist/index-backup.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +187 -0
- package/dist/index.js.map +1 -0
- package/dist/server-config.d.ts +80 -0
- package/dist/server-config.d.ts.map +1 -0
- package/dist/server-config.js +137 -0
- package/dist/server-config.js.map +1 -0
- package/dist/tool-handlers.d.ts +44 -0
- package/dist/tool-handlers.d.ts.map +1 -0
- package/dist/tool-handlers.js +1682 -0
- package/dist/tool-handlers.js.map +1 -0
- package/dist/tool-schemas.d.ts +859 -0
- package/dist/tool-schemas.d.ts.map +1 -0
- package/dist/tool-schemas.js +870 -0
- package/dist/tool-schemas.js.map +1 -0
- package/dist/types.d.ts +19 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +10 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +142 -0
- package/dist/utils.js.map +1 -0
- package/dist/wordpress-client.d.ts +21 -0
- package/dist/wordpress-client.d.ts.map +1 -0
- package/dist/wordpress-client.js +151 -0
- package/dist/wordpress-client.js.map +1 -0
- 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
|