@maxisoft/instantcms-mcp 1.2.3
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 +308 -0
- package/dist/__tests__/artifact-tool.test.js +196 -0
- package/dist/__tests__/db-tool.test.js +152 -0
- package/dist/__tests__/define-tool.test.js +106 -0
- package/dist/__tests__/find-tool.test.js +185 -0
- package/dist/__tests__/get-component-api.test.js +144 -0
- package/dist/__tests__/hardening.test.js +73 -0
- package/dist/__tests__/hooks-tool.test.js +173 -0
- package/dist/__tests__/mcp-integration-extra.test.js +166 -0
- package/dist/__tests__/mcp-integration.test.js +53 -0
- package/dist/__tests__/pagination.test.js +114 -0
- package/dist/__tests__/performance-baseline.test.js +56 -0
- package/dist/__tests__/project-patch.test.js +144 -0
- package/dist/__tests__/project-source.test.js +44 -0
- package/dist/__tests__/project-workflows-extra.test.js +165 -0
- package/dist/__tests__/project-workflows.test.js +51 -0
- package/dist/__tests__/scaffold-addon-roundtrip.test.js +287 -0
- package/dist/__tests__/serialization.test.js +170 -0
- package/dist/__tests__/source-knowledge-parsers.test.js +42 -0
- package/dist/__tests__/template-development.test.js +78 -0
- package/dist/__tests__/template-productivity.test.js +87 -0
- package/dist/__tests__/tools.test.js +2143 -0
- package/dist/data/components.js +2818 -0
- package/dist/data/controllers-map.js +7571 -0
- package/dist/data/core-api.js +9356 -0
- package/dist/data/database-schema.js +4555 -0
- package/dist/data/events-map.js +1339 -0
- package/dist/data/fields-map.js +3692 -0
- package/dist/data/hooks.js +2257 -0
- package/dist/data/js-api.js +123 -0
- package/dist/data/libs-api.js +264 -0
- package/dist/data/routes-map.js +203 -0
- package/dist/data/schemas-validation.js +255 -0
- package/dist/data/schemas.js +1404 -0
- package/dist/data/traits-map.js +1004 -0
- package/dist/data/version-profiles.js +41 -0
- package/dist/data/widgets-map.js +131 -0
- package/dist/data/wysiwyg-map.js +486 -0
- package/dist/generated/components-source.js +14748 -0
- package/dist/generated/hooks-source.js +3399 -0
- package/dist/generated/knowledge-meta.js +101 -0
- package/dist/index.js +13 -0
- package/dist/registry/database-tools.js +112 -0
- package/dist/registry/extension-tools.js +473 -0
- package/dist/registry/generator-tools.js +506 -0
- package/dist/registry/knowledge-tools.js +441 -0
- package/dist/registry/language-tools.js +99 -0
- package/dist/registry/meta-tools.js +152 -0
- package/dist/registry/project-tools.js +48 -0
- package/dist/registry/resources.js +98 -0
- package/dist/registry/source-tools.js +211 -0
- package/dist/registry/template-development-tools.js +79 -0
- package/dist/server.js +32 -0
- package/dist/tools/addon-tool.js +1437 -0
- package/dist/tools/admin-partial-tool.js +498 -0
- package/dist/tools/api-tool.js +294 -0
- package/dist/tools/artifact-tool.js +95 -0
- package/dist/tools/cache-tool.js +360 -0
- package/dist/tools/component-tool.js +415 -0
- package/dist/tools/controllers-tool.js +125 -0
- package/dist/tools/cron-tool.js +197 -0
- package/dist/tools/crud-tool.js +659 -0
- package/dist/tools/db-tool.js +198 -0
- package/dist/tools/email-tool.js +222 -0
- package/dist/tools/external-api-tool.js +596 -0
- package/dist/tools/filter-tool.js +341 -0
- package/dist/tools/form-tool.js +275 -0
- package/dist/tools/grid-tool.js +189 -0
- package/dist/tools/hooks-tool.js +104 -0
- package/dist/tools/import-export-tool.js +548 -0
- package/dist/tools/lang-tool.js +251 -0
- package/dist/tools/layout-override-tool.js +125 -0
- package/dist/tools/layout-tool.js +548 -0
- package/dist/tools/maria-tool.js +127 -0
- package/dist/tools/mariadb.js +201 -0
- package/dist/tools/migration-tool.js +329 -0
- package/dist/tools/oauth-tool.js +520 -0
- package/dist/tools/parser/components-parser.js +76 -0
- package/dist/tools/parser/controllers-parser.js +313 -0
- package/dist/tools/parser/core-parser.js +294 -0
- package/dist/tools/parser/coverage-generator.js +424 -0
- package/dist/tools/parser/events-parser.js +127 -0
- package/dist/tools/parser/fields-parser.js +382 -0
- package/dist/tools/parser/hooks-parser.js +106 -0
- package/dist/tools/parser/sql-parser.js +197 -0
- package/dist/tools/parser/traits-parser.js +161 -0
- package/dist/tools/parser/widgets-parser.js +150 -0
- package/dist/tools/permission-tool.js +348 -0
- package/dist/tools/project-patch-tool.js +73 -0
- package/dist/tools/project-source-tool.js +188 -0
- package/dist/tools/project-workflow-tool.js +186 -0
- package/dist/tools/requirement-tool.js +212 -0
- package/dist/tools/scaffold-tool.js +815 -0
- package/dist/tools/seo-tool.js +412 -0
- package/dist/tools/source-tool.js +155 -0
- package/dist/tools/template-development-tool.js +271 -0
- package/dist/tools/template-overrides-tool.js +294 -0
- package/dist/tools/template-productivity-tool.js +319 -0
- package/dist/tools/template-tool.js +665 -0
- package/dist/tools/test-tool.js +183 -0
- package/dist/tools/webhook-tool.js +427 -0
- package/dist/tools/widget-tool.js +331 -0
- package/dist/tools/wysiwyg-tool.js +137 -0
- package/dist/types/scaffold.js +68 -0
- package/dist/utils/define-tool.js +37 -0
- package/dist/utils/find-tool.js +97 -0
- package/dist/utils/mcp-result.js +18 -0
- package/dist/utils/pagination.js +27 -0
- package/dist/utils/serialization.js +27 -0
- package/package.json +94 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.introspectDatabase = introspectDatabase;
|
|
4
|
+
exports.listContentTypes = listContentTypes;
|
|
5
|
+
exports.listDatabaseEvents = listDatabaseEvents;
|
|
6
|
+
exports.describeTable = describeTable;
|
|
7
|
+
const database_schema_js_1 = require("../data/database-schema.js");
|
|
8
|
+
const events_map_js_1 = require("../data/events-map.js");
|
|
9
|
+
function introspectDatabase(tableName) {
|
|
10
|
+
if (tableName) {
|
|
11
|
+
const table = database_schema_js_1.databaseSchema.tables.find(t => t.name === tableName || t.name.endsWith(`_${tableName}`));
|
|
12
|
+
if (!table) {
|
|
13
|
+
const similar = database_schema_js_1.databaseSchema.tables.filter(t => t.name.includes(tableName) || (t.comment && t.comment.includes(tableName)));
|
|
14
|
+
return {
|
|
15
|
+
error: `Таблица "${tableName}" не найдена`,
|
|
16
|
+
suggestions: similar.slice(0, 5).map(t => ({
|
|
17
|
+
name: t.name,
|
|
18
|
+
comment: t.comment
|
|
19
|
+
}))
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
table: table.name,
|
|
24
|
+
comment: table.comment,
|
|
25
|
+
fieldCount: table.fields.length,
|
|
26
|
+
indexCount: table.indexes.length,
|
|
27
|
+
fields: table.fields.map(f => ({
|
|
28
|
+
name: f.name,
|
|
29
|
+
type: f.type,
|
|
30
|
+
nullable: f.nullable,
|
|
31
|
+
key: f.key,
|
|
32
|
+
default: f.default,
|
|
33
|
+
comment: f.comment
|
|
34
|
+
})),
|
|
35
|
+
indexes: table.indexes.map(i => ({
|
|
36
|
+
name: i.name,
|
|
37
|
+
type: i.type,
|
|
38
|
+
fields: i.fields
|
|
39
|
+
})),
|
|
40
|
+
relationships: findRelationships(table.name)
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
totalTables: database_schema_js_1.databaseSchema.tableCount,
|
|
45
|
+
tables: database_schema_js_1.databaseSchema.tables.map(t => ({
|
|
46
|
+
name: t.name,
|
|
47
|
+
comment: t.comment,
|
|
48
|
+
fieldCount: t.fields.length,
|
|
49
|
+
hasPrimaryKey: t.indexes.some(i => i.name === 'PRIMARY')
|
|
50
|
+
})),
|
|
51
|
+
summary: generateSummary()
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function listContentTypes() {
|
|
55
|
+
const contentTable = database_schema_js_1.databaseSchema.tables.find(t => t.name === 'cms_content_types');
|
|
56
|
+
if (!contentTable) {
|
|
57
|
+
return { error: 'Таблица content_types не найдена' };
|
|
58
|
+
}
|
|
59
|
+
const usersTable = database_schema_js_1.databaseSchema.tables.find(t => t.name === 'cms_users');
|
|
60
|
+
return {
|
|
61
|
+
table: contentTable.name,
|
|
62
|
+
fields: contentTable.fields.map(f => ({
|
|
63
|
+
name: f.name,
|
|
64
|
+
type: f.type,
|
|
65
|
+
comment: f.comment
|
|
66
|
+
})),
|
|
67
|
+
keyFields: contentTable.fields.filter(f => f.key === 'PRI' || f.key === 'UNI'),
|
|
68
|
+
systemTables: {
|
|
69
|
+
users: usersTable ? {
|
|
70
|
+
name: usersTable.name,
|
|
71
|
+
comment: usersTable.comment,
|
|
72
|
+
keyFields: usersTable.fields.filter(f => f.key === 'PRI').map(f => f.name)
|
|
73
|
+
} : null
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function listDatabaseEvents() {
|
|
78
|
+
return {
|
|
79
|
+
totalEvents: events_map_js_1.eventsMap.eventCount,
|
|
80
|
+
byController: Object.entries(events_map_js_1.eventsMap.byController).map(([controller, events]) => ({
|
|
81
|
+
controller,
|
|
82
|
+
eventCount: events.length,
|
|
83
|
+
events: events.slice(0, 10)
|
|
84
|
+
})),
|
|
85
|
+
allEvents: events_map_js_1.eventsMap.events.map(e => ({
|
|
86
|
+
event: e.event,
|
|
87
|
+
listener: e.listener,
|
|
88
|
+
isEnabled: e.isEnabled
|
|
89
|
+
}))
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function describeTable(tableName) {
|
|
93
|
+
const table = database_schema_js_1.databaseSchema.tables.find(t => t.name === tableName || t.name === `cms_${tableName}` || t.name.endsWith(`_${tableName}`));
|
|
94
|
+
if (!table) {
|
|
95
|
+
return { error: `Таблица "${tableName}" не найдена` };
|
|
96
|
+
}
|
|
97
|
+
const fieldTypes = {};
|
|
98
|
+
for (const field of table.fields) {
|
|
99
|
+
const baseType = field.type.replace(/[()].*/, '');
|
|
100
|
+
fieldTypes[baseType] = (fieldTypes[baseType] || 0) + 1;
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
table: table.name,
|
|
104
|
+
comment: table.comment,
|
|
105
|
+
statistics: {
|
|
106
|
+
totalFields: table.fields.length,
|
|
107
|
+
totalIndexes: table.indexes.length,
|
|
108
|
+
fieldTypes
|
|
109
|
+
},
|
|
110
|
+
fields: table.fields.map(f => ({
|
|
111
|
+
name: f.name,
|
|
112
|
+
type: f.type,
|
|
113
|
+
nullable: f.nullable,
|
|
114
|
+
key: f.key,
|
|
115
|
+
default: f.default,
|
|
116
|
+
comment: f.comment,
|
|
117
|
+
extra: f.extra
|
|
118
|
+
})),
|
|
119
|
+
indexes: table.indexes.map(i => ({
|
|
120
|
+
name: i.name,
|
|
121
|
+
type: i.type,
|
|
122
|
+
fields: i.fields
|
|
123
|
+
})),
|
|
124
|
+
relationships: findRelationships(table.name),
|
|
125
|
+
sampleQuery: generateSampleQuery(table)
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
function findRelationships(tableName) {
|
|
129
|
+
const relationships = [];
|
|
130
|
+
const baseName = tableName.replace(/^cms_/, '');
|
|
131
|
+
for (const table of database_schema_js_1.databaseSchema.tables) {
|
|
132
|
+
for (const field of table.fields) {
|
|
133
|
+
if (field.name === baseName || field.name === `${baseName}_id`) {
|
|
134
|
+
if (table.name !== tableName) {
|
|
135
|
+
relationships.push({
|
|
136
|
+
type: 'has_many',
|
|
137
|
+
target: table.name,
|
|
138
|
+
via: field.name
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (table.name.includes(baseName) && table.name !== tableName) {
|
|
144
|
+
for (const index of table.indexes) {
|
|
145
|
+
if (index.fields.some(f => f.includes('id') || f.includes(baseName))) {
|
|
146
|
+
relationships.push({
|
|
147
|
+
type: 'related',
|
|
148
|
+
target: table.name,
|
|
149
|
+
on: index.fields
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return relationships.slice(0, 10);
|
|
156
|
+
}
|
|
157
|
+
function generateSummary() {
|
|
158
|
+
const categories = {
|
|
159
|
+
'users': 0,
|
|
160
|
+
'content': 0,
|
|
161
|
+
'widgets': 0,
|
|
162
|
+
'system': 0,
|
|
163
|
+
'other': 0
|
|
164
|
+
};
|
|
165
|
+
for (const table of database_schema_js_1.databaseSchema.tables) {
|
|
166
|
+
if (table.name.startsWith('cms_users'))
|
|
167
|
+
categories.users++;
|
|
168
|
+
else if (table.name.startsWith('cms_con_') || table.name.includes('content'))
|
|
169
|
+
categories.content++;
|
|
170
|
+
else if (table.name.startsWith('cms_widgets'))
|
|
171
|
+
categories.widgets++;
|
|
172
|
+
else if (['cms_sessions', 'cms_jobs', 'cms_scheduler'].includes(table.name))
|
|
173
|
+
categories.system++;
|
|
174
|
+
else
|
|
175
|
+
categories.other++;
|
|
176
|
+
}
|
|
177
|
+
return categories;
|
|
178
|
+
}
|
|
179
|
+
function generateSampleQuery(table) {
|
|
180
|
+
const primaryKey = table.indexes.find(i => i.name === 'PRIMARY');
|
|
181
|
+
const pk = primaryKey?.fields[0] || 'id';
|
|
182
|
+
return [
|
|
183
|
+
`-- Получить запись по ID`,
|
|
184
|
+
`SELECT * FROM ${table.name} WHERE ${pk} = 1;`,
|
|
185
|
+
``,
|
|
186
|
+
`-- Получить список с пагинацией`,
|
|
187
|
+
`SELECT * FROM ${table.name} ORDER BY ${pk} DESC LIMIT 10 OFFSET 0;`,
|
|
188
|
+
``,
|
|
189
|
+
`-- Фильтрация по полю`,
|
|
190
|
+
table.fields.find(f => f.name === 'is_pub')
|
|
191
|
+
? `SELECT * FROM ${table.name} WHERE is_pub = 1 ORDER BY date_pub DESC;`
|
|
192
|
+
: null,
|
|
193
|
+
`-- JOIN с пользователями`,
|
|
194
|
+
table.fields.some(f => f.name === 'user_id')
|
|
195
|
+
? `SELECT t.*, u.nickname as user_nickname \nFROM ${table.name} t\nLEFT JOIN cms_users u ON u.id = t.user_id\nWHERE t.id = 1;`
|
|
196
|
+
: null
|
|
197
|
+
].filter(Boolean).join('\n');
|
|
198
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.scaffoldEmail = scaffoldEmail;
|
|
4
|
+
function scaffoldEmail(opts) {
|
|
5
|
+
const name = opts.addon_name.toLowerCase().replace(/[^a-z0-9_]/g, '_');
|
|
6
|
+
const NAME = name.toUpperCase();
|
|
7
|
+
const files = {};
|
|
8
|
+
const baseDir = `package/system/languages/ru/controllers/${name}`;
|
|
9
|
+
for (const template of opts.templates) {
|
|
10
|
+
const templateFileName = `${name}_${template.name}.email.php`;
|
|
11
|
+
files[`${baseDir}/${templateFileName}`] = generateEmailTemplate(name, NAME, template, opts.options);
|
|
12
|
+
}
|
|
13
|
+
files[`${baseDir}/${name}_emails.php`] = generateEmailIndex(name, NAME, opts.templates);
|
|
14
|
+
return {
|
|
15
|
+
addon_name: name,
|
|
16
|
+
templates_count: opts.templates.length,
|
|
17
|
+
files,
|
|
18
|
+
templates: opts.templates.map(t => ({
|
|
19
|
+
name: t.name,
|
|
20
|
+
file: `${name}_${t.name}.email.php`,
|
|
21
|
+
subject: t.subject,
|
|
22
|
+
variables_count: t.variables?.length || 0,
|
|
23
|
+
})),
|
|
24
|
+
structure_notes: [
|
|
25
|
+
`Email шаблоны: ${baseDir}/`,
|
|
26
|
+
`Использование в коде: $this->controller->emailTask('${name}', $template, $data)`,
|
|
27
|
+
`Отправка: cmsEventsManager::hook('send_email', ['to' => $email, 'template' => '${name}:${opts.templates[0]?.name || 'default'}', 'vars' => $data])`,
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function generateEmailTemplate(name, NAME, template, options) {
|
|
32
|
+
const useHtml = options?.use_html ?? true;
|
|
33
|
+
const baseTemplate = options?.base_template || 'default';
|
|
34
|
+
const header = generateEmailHeader(template.subject, baseTemplate, useHtml);
|
|
35
|
+
const body = generateEmailBody(template.body, name, NAME, template.variables);
|
|
36
|
+
const footer = generateEmailFooter(baseTemplate, useHtml);
|
|
37
|
+
let code = `<?php
|
|
38
|
+
/**
|
|
39
|
+
* Email шаблон: ${template.name}
|
|
40
|
+
* Для отправки используйте метод emailTask() контроллера
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
`;
|
|
44
|
+
if (useHtml) {
|
|
45
|
+
code += `$${name}_template = <<<'HTML'
|
|
46
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
47
|
+
<html xmlns="http://www.w3.org/1999/xhtml">
|
|
48
|
+
<head>
|
|
49
|
+
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
50
|
+
<title>${escapeHtml(template.subject)}</title>
|
|
51
|
+
${header}
|
|
52
|
+
</head>
|
|
53
|
+
<body>
|
|
54
|
+
${body}
|
|
55
|
+
${footer}
|
|
56
|
+
</body>
|
|
57
|
+
</html>
|
|
58
|
+
HTML;
|
|
59
|
+
|
|
60
|
+
`;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
code += `$${name}_template = <<<'TEXT'
|
|
64
|
+
${template.body}
|
|
65
|
+
TEXT;
|
|
66
|
+
|
|
67
|
+
`;
|
|
68
|
+
}
|
|
69
|
+
code += `
|
|
70
|
+
$${name}_subject = '${escapeHtml(template.subject)}';
|
|
71
|
+
`;
|
|
72
|
+
if (template.variables && template.variables.length > 0) {
|
|
73
|
+
code += `
|
|
74
|
+
// Доступные переменные:
|
|
75
|
+
`;
|
|
76
|
+
for (const v of template.variables) {
|
|
77
|
+
const example = v.example ? ` // Пример: ${v.example}` : '';
|
|
78
|
+
code += `// \${${v.name}}${example}
|
|
79
|
+
`;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return code;
|
|
83
|
+
}
|
|
84
|
+
function generateEmailHeader(subject, baseTemplate, useHtml) {
|
|
85
|
+
if (!useHtml) {
|
|
86
|
+
return '';
|
|
87
|
+
}
|
|
88
|
+
const baseStyles = `
|
|
89
|
+
body {
|
|
90
|
+
margin: 0;
|
|
91
|
+
padding: 0;
|
|
92
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
93
|
+
font-size: 14px;
|
|
94
|
+
line-height: 1.6;
|
|
95
|
+
color: #333;
|
|
96
|
+
}
|
|
97
|
+
.email-container {
|
|
98
|
+
max-width: 600px;
|
|
99
|
+
margin: 0 auto;
|
|
100
|
+
padding: 20px;
|
|
101
|
+
}
|
|
102
|
+
.email-header {
|
|
103
|
+
background: #4a90d9;
|
|
104
|
+
color: white;
|
|
105
|
+
padding: 20px;
|
|
106
|
+
text-align: center;
|
|
107
|
+
}
|
|
108
|
+
.email-body {
|
|
109
|
+
padding: 30px 20px;
|
|
110
|
+
background: #ffffff;
|
|
111
|
+
}
|
|
112
|
+
.email-footer {
|
|
113
|
+
padding: 20px;
|
|
114
|
+
text-align: center;
|
|
115
|
+
font-size: 12px;
|
|
116
|
+
color: #666;
|
|
117
|
+
background: #f5f5f5;
|
|
118
|
+
}
|
|
119
|
+
.button {
|
|
120
|
+
display: inline-block;
|
|
121
|
+
padding: 10px 20px;
|
|
122
|
+
background: #4a90d9;
|
|
123
|
+
color: white;
|
|
124
|
+
text-decoration: none;
|
|
125
|
+
border-radius: 4px;
|
|
126
|
+
}
|
|
127
|
+
`;
|
|
128
|
+
switch (baseTemplate) {
|
|
129
|
+
case 'minimal':
|
|
130
|
+
return `
|
|
131
|
+
<style type="text/css">
|
|
132
|
+
body { background: #fafafa; }
|
|
133
|
+
.email-container { background: #ffffff; padding: 30px; }
|
|
134
|
+
</style>
|
|
135
|
+
`;
|
|
136
|
+
case 'notifications':
|
|
137
|
+
return `
|
|
138
|
+
<style type="text/css">
|
|
139
|
+
${baseStyles}
|
|
140
|
+
.email-header { background: #e74c3c; }
|
|
141
|
+
</style>
|
|
142
|
+
`;
|
|
143
|
+
default:
|
|
144
|
+
return `
|
|
145
|
+
<style type="text/css">
|
|
146
|
+
${baseStyles}
|
|
147
|
+
</style>
|
|
148
|
+
`;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
function generateEmailBody(body, name, NAME, variables) {
|
|
152
|
+
let processedBody = body;
|
|
153
|
+
if (variables) {
|
|
154
|
+
for (const v of variables) {
|
|
155
|
+
processedBody = processedBody.replace(new RegExp(`{${v.name}}`, 'g'), `\${\${${v.name}}}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
processedBody = processedBody.replace(/\{site_name\}/g, '<?php echo \\$site_name; ?>');
|
|
159
|
+
processedBody = processedBody.replace(/\{site_url\}/g, '<?php echo \\$site_url; ?>');
|
|
160
|
+
processedBody = processedBody.replace(/\{user_name\}/g, '<?php echo \\$user_name; ?>');
|
|
161
|
+
processedBody = processedBody.replace(/\{user_email\}/g, '<?php echo \\$user_email; ?>');
|
|
162
|
+
const subjectPreview = escapeHtml(body.slice(0, 50));
|
|
163
|
+
return `
|
|
164
|
+
<div class="email-container">
|
|
165
|
+
<div class="email-header">
|
|
166
|
+
<h1><?php echo \\$subject ?: '${subjectPreview}'; ?></h1>
|
|
167
|
+
</div>
|
|
168
|
+
<div class="email-body">
|
|
169
|
+
${processedBody
|
|
170
|
+
.split('\n')
|
|
171
|
+
.map(line => ` <p>${line}</p>`)
|
|
172
|
+
.join('\n')}
|
|
173
|
+
</div>
|
|
174
|
+
<div class="email-footer">
|
|
175
|
+
<p>С уважением, команда <?php echo \\$site_name; ?></p>
|
|
176
|
+
<p><a href="<?php echo \\$site_url; ?>"><?php echo \\$site_url; ?></a></p>
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
`;
|
|
180
|
+
}
|
|
181
|
+
function generateEmailFooter(baseTemplate, useHtml) {
|
|
182
|
+
if (!useHtml) {
|
|
183
|
+
return '';
|
|
184
|
+
}
|
|
185
|
+
switch (baseTemplate) {
|
|
186
|
+
case 'minimal':
|
|
187
|
+
return '';
|
|
188
|
+
default:
|
|
189
|
+
return '';
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function generateEmailIndex(name, NAME, templates) {
|
|
193
|
+
let code = `<?php
|
|
194
|
+
/**
|
|
195
|
+
* Email шаблоны для ${name}
|
|
196
|
+
* Генерируется автоматически
|
|
197
|
+
*/
|
|
198
|
+
|
|
199
|
+
`;
|
|
200
|
+
for (const template of templates) {
|
|
201
|
+
code += `/**
|
|
202
|
+
* ${template.name} - ${template.subject}
|
|
203
|
+
*/
|
|
204
|
+
define('LANG_${NAME}_EMAIL_${template.name.toUpperCase()}_SUBJECT', '${escapeHtml(template.subject)}');
|
|
205
|
+
define('LANG_${NAME}_EMAIL_${template.name.toUpperCase()}_BODY', '${escapeHtml(template.body.slice(0, 100))}...');
|
|
206
|
+
|
|
207
|
+
`;
|
|
208
|
+
}
|
|
209
|
+
code += `
|
|
210
|
+
// Отправка email
|
|
211
|
+
// $this->controller->emailTask('${name}', '${templates[0]?.name || 'default'}', $data);
|
|
212
|
+
`;
|
|
213
|
+
return code;
|
|
214
|
+
}
|
|
215
|
+
function escapeHtml(text) {
|
|
216
|
+
return text
|
|
217
|
+
.replace(/&/g, '&')
|
|
218
|
+
.replace(/</g, '<')
|
|
219
|
+
.replace(/>/g, '>')
|
|
220
|
+
.replace(/"/g, '"')
|
|
221
|
+
.replace(/'/g, ''');
|
|
222
|
+
}
|