nextjs-cms 0.5.9 → 0.5.11
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/dist/api/axios/axiosInstance.d.ts +1 -1
- package/dist/api/axios/axiosInstance.js +8 -8
- package/dist/api/index.d.ts +855 -855
- package/dist/api/index.d.ts.map +1 -1
- package/dist/api/index.js +12 -12
- package/dist/api/lib/serverActions.d.ts +239 -239
- package/dist/api/lib/serverActions.d.ts.map +1 -1
- package/dist/api/lib/serverActions.js +834 -834
- package/dist/api/root.d.ts +828 -828
- package/dist/api/root.js +30 -30
- package/dist/api/routers/accountSettings.d.ts +60 -60
- package/dist/api/routers/accountSettings.js +108 -108
- package/dist/api/routers/admins.d.ts +105 -105
- package/dist/api/routers/admins.js +219 -219
- package/dist/api/routers/auth.d.ts +47 -47
- package/dist/api/routers/auth.js +25 -25
- package/dist/api/routers/categorySection.d.ts +103 -103
- package/dist/api/routers/categorySection.js +38 -38
- package/dist/api/routers/cmsSettings.d.ts +48 -48
- package/dist/api/routers/cmsSettings.js +51 -51
- package/dist/api/routers/cpanel.d.ts +83 -83
- package/dist/api/routers/cpanel.js +216 -216
- package/dist/api/routers/files.d.ts +47 -47
- package/dist/api/routers/files.js +23 -23
- package/dist/api/routers/gallery.d.ts +35 -35
- package/dist/api/routers/gallery.js +62 -62
- package/dist/api/routers/googleAnalytics.d.ts +30 -30
- package/dist/api/routers/googleAnalytics.js +7 -7
- package/dist/api/routers/hasItemsSection.d.ts +139 -139
- package/dist/api/routers/hasItemsSection.js +34 -34
- package/dist/api/routers/navigation.d.ts +51 -51
- package/dist/api/routers/navigation.js +11 -11
- package/dist/api/routers/simpleSection.d.ts +57 -57
- package/dist/api/routers/simpleSection.js +12 -12
- package/dist/api/trpc.d.ts +106 -106
- package/dist/api/trpc.js +72 -72
- package/dist/auth/axios/axiosInstance.d.ts +1 -1
- package/dist/auth/axios/axiosInstance.js +8 -8
- package/dist/auth/csrf.d.ts +29 -29
- package/dist/auth/csrf.js +76 -76
- package/dist/auth/hooks/index.d.ts +3 -3
- package/dist/auth/hooks/index.d.ts.map +1 -1
- package/dist/auth/hooks/index.js +3 -3
- package/dist/auth/hooks/useAxiosPrivate.d.ts +4 -4
- package/dist/auth/hooks/useAxiosPrivate.js +74 -74
- package/dist/auth/hooks/useRefreshToken.d.ts +6 -6
- package/dist/auth/hooks/useRefreshToken.js +79 -79
- package/dist/auth/index.d.ts +22 -22
- package/dist/auth/index.js +44 -44
- package/dist/auth/jwt.d.ts +5 -5
- package/dist/auth/jwt.js +25 -25
- package/dist/auth/lib/actions.d.ts +32 -32
- package/dist/auth/lib/actions.d.ts.map +1 -1
- package/dist/auth/lib/actions.js +209 -209
- package/dist/auth/lib/client.d.ts +3 -3
- package/dist/auth/lib/client.js +46 -46
- package/dist/auth/lib/index.d.ts +2 -2
- package/dist/auth/lib/index.d.ts.map +1 -1
- package/dist/auth/lib/index.js +2 -2
- package/dist/auth/react.d.ts +105 -105
- package/dist/auth/react.d.ts.map +1 -1
- package/dist/auth/react.js +347 -347
- package/dist/auth/trpc.d.ts +5 -5
- package/dist/auth/trpc.d.ts.map +1 -1
- package/dist/auth/trpc.js +81 -81
- package/dist/core/config/config-loader.d.ts +91 -91
- package/dist/core/config/config-loader.js +230 -230
- package/dist/core/config/index.d.ts +2 -2
- package/dist/core/config/index.d.ts.map +1 -1
- package/dist/core/config/index.js +1 -1
- package/dist/core/config/loader.d.ts +1 -1
- package/dist/core/config/loader.js +42 -42
- package/dist/core/db/index.d.ts +1 -1
- package/dist/core/db/index.d.ts.map +1 -1
- package/dist/core/db/index.js +1 -1
- package/dist/core/db/table-checker/DbTable.d.ts +5 -5
- package/dist/core/db/table-checker/DbTable.js +5 -5
- package/dist/core/db/table-checker/MysqlTable.d.ts +33 -33
- package/dist/core/db/table-checker/MysqlTable.d.ts.map +1 -1
- package/dist/core/db/table-checker/MysqlTable.js +94 -94
- package/dist/core/db/table-checker/index.d.ts +1 -1
- package/dist/core/db/table-checker/index.d.ts.map +1 -1
- package/dist/core/db/table-checker/index.js +1 -1
- package/dist/core/factories/FieldFactory.d.ts +123 -123
- package/dist/core/factories/FieldFactory.d.ts.map +1 -1
- package/dist/core/factories/FieldFactory.js +411 -411
- package/dist/core/factories/SectionFactory.d.ts +109 -109
- package/dist/core/factories/SectionFactory.d.ts.map +1 -1
- package/dist/core/factories/SectionFactory.js +415 -415
- package/dist/core/factories/index.d.ts +2 -2
- package/dist/core/factories/index.d.ts.map +1 -1
- package/dist/core/factories/index.js +2 -2
- package/dist/core/fields/checkbox.d.ts +62 -62
- package/dist/core/fields/checkbox.d.ts.map +1 -1
- package/dist/core/fields/checkbox.js +62 -62
- package/dist/core/fields/color.d.ts +83 -83
- package/dist/core/fields/color.d.ts.map +1 -1
- package/dist/core/fields/color.js +91 -91
- package/dist/core/fields/date.d.ts +99 -99
- package/dist/core/fields/date.d.ts.map +1 -1
- package/dist/core/fields/date.js +108 -108
- package/dist/core/fields/document.d.ts +179 -179
- package/dist/core/fields/document.d.ts.map +1 -1
- package/dist/core/fields/document.js +277 -277
- package/dist/core/fields/field-group.d.ts +17 -17
- package/dist/core/fields/field-group.d.ts.map +1 -1
- package/dist/core/fields/field-group.js +6 -6
- package/dist/core/fields/field.d.ts +125 -125
- package/dist/core/fields/field.d.ts.map +1 -1
- package/dist/core/fields/field.js +148 -148
- package/dist/core/fields/fileField.d.ts +14 -14
- package/dist/core/fields/fileField.d.ts.map +1 -1
- package/dist/core/fields/fileField.js +5 -5
- package/dist/core/fields/index.d.ts +64 -64
- package/dist/core/fields/index.d.ts.map +1 -1
- package/dist/core/fields/index.js +18 -18
- package/dist/core/fields/map.d.ts +166 -166
- package/dist/core/fields/map.d.ts.map +1 -1
- package/dist/core/fields/map.js +152 -152
- package/dist/core/fields/number.d.ts +185 -185
- package/dist/core/fields/number.d.ts.map +1 -1
- package/dist/core/fields/number.js +241 -241
- package/dist/core/fields/password.d.ts +108 -108
- package/dist/core/fields/password.d.ts.map +1 -1
- package/dist/core/fields/password.js +133 -133
- package/dist/core/fields/photo.d.ts +288 -288
- package/dist/core/fields/photo.d.ts.map +1 -1
- package/dist/core/fields/photo.js +410 -410
- package/dist/core/fields/richText.d.ts +294 -294
- package/dist/core/fields/richText.d.ts.map +1 -1
- package/dist/core/fields/richText.js +338 -338
- package/dist/core/fields/select.d.ts +365 -365
- package/dist/core/fields/select.d.ts.map +1 -1
- package/dist/core/fields/select.js +499 -499
- package/dist/core/fields/selectMultiple.d.ts +235 -235
- package/dist/core/fields/selectMultiple.d.ts.map +1 -1
- package/dist/core/fields/selectMultiple.js +417 -417
- package/dist/core/fields/tags.d.ts +130 -130
- package/dist/core/fields/tags.d.ts.map +1 -1
- package/dist/core/fields/tags.js +105 -105
- package/dist/core/fields/text.d.ts +135 -135
- package/dist/core/fields/text.d.ts.map +1 -1
- package/dist/core/fields/text.js +157 -157
- package/dist/core/fields/textArea.d.ts +106 -106
- package/dist/core/fields/textArea.d.ts.map +1 -1
- package/dist/core/fields/textArea.js +126 -126
- package/dist/core/fields/video.d.ts +147 -147
- package/dist/core/fields/video.d.ts.map +1 -1
- package/dist/core/fields/video.js +248 -248
- package/dist/core/helpers/entity.d.ts +7 -7
- package/dist/core/helpers/entity.js +27 -27
- package/dist/core/helpers/index.d.ts +4 -4
- package/dist/core/helpers/index.d.ts.map +1 -1
- package/dist/core/helpers/index.js +3 -3
- package/dist/core/index.d.ts +7 -7
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +7 -7
- package/dist/core/sections/category.d.ts +282 -282
- package/dist/core/sections/category.d.ts.map +1 -1
- package/dist/core/sections/category.js +147 -147
- package/dist/core/sections/hasItems.d.ts +631 -631
- package/dist/core/sections/hasItems.d.ts.map +1 -1
- package/dist/core/sections/hasItems.js +144 -144
- package/dist/core/sections/index.d.ts +4 -4
- package/dist/core/sections/index.d.ts.map +1 -1
- package/dist/core/sections/index.js +4 -4
- package/dist/core/sections/section.d.ts +225 -225
- package/dist/core/sections/section.d.ts.map +1 -1
- package/dist/core/sections/section.js +341 -341
- package/dist/core/sections/simple.d.ts +98 -98
- package/dist/core/sections/simple.d.ts.map +1 -1
- package/dist/core/sections/simple.js +95 -95
- package/dist/core/security/dom.d.ts +10 -10
- package/dist/core/security/dom.js +92 -92
- package/dist/core/submit/ItemEditSubmit.d.ts +75 -75
- package/dist/core/submit/ItemEditSubmit.js +186 -186
- package/dist/core/submit/NewItemSubmit.d.ts +13 -13
- package/dist/core/submit/NewItemSubmit.js +93 -93
- package/dist/core/submit/SimpleSectionSubmit.d.ts +12 -12
- package/dist/core/submit/SimpleSectionSubmit.js +93 -93
- package/dist/core/submit/index.d.ts +4 -4
- package/dist/core/submit/index.js +4 -4
- package/dist/core/submit/submit.d.ts +115 -115
- package/dist/core/submit/submit.js +479 -479
- package/dist/core/types/index.d.ts +279 -279
- package/dist/core/types/index.d.ts.map +1 -1
- package/dist/core/types/index.js +1 -1
- package/dist/db/client.d.ts +8 -8
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +19 -19
- package/dist/db/config.d.ts +5 -5
- package/dist/db/config.js +22 -22
- package/dist/db/drizzle.config.d.ts +5 -5
- package/dist/db/drizzle.config.js +18 -18
- package/dist/db/index.d.ts +2 -2
- package/dist/db/index.js +3 -3
- package/dist/db/schema.d.ts +638 -638
- package/dist/db/schema.js +73 -73
- package/dist/index.d.ts +7 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -7
- package/dist/translations/index.d.ts +2 -2
- package/dist/translations/index.js +15 -15
- package/dist/utils/CpanelApi.d.ts +24 -24
- package/dist/utils/CpanelApi.js +64 -64
- package/dist/utils/constants.d.ts +13 -13
- package/dist/utils/constants.js +61 -61
- package/dist/utils/index.d.ts +4 -4
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +4 -4
- package/dist/utils/utils.d.ts +59 -59
- package/dist/utils/utils.js +132 -132
- package/dist/validators/checkbox.d.ts +3 -3
- package/dist/validators/checkbox.d.ts.map +1 -1
- package/dist/validators/checkbox.js +12 -12
- package/dist/validators/color.d.ts +3 -3
- package/dist/validators/color.d.ts.map +1 -1
- package/dist/validators/color.js +7 -7
- package/dist/validators/date.d.ts +3 -3
- package/dist/validators/date.d.ts.map +1 -1
- package/dist/validators/date.js +5 -5
- package/dist/validators/document.d.ts +3 -3
- package/dist/validators/document.d.ts.map +1 -1
- package/dist/validators/document.js +57 -57
- package/dist/validators/index.d.ts +14 -14
- package/dist/validators/index.d.ts.map +1 -1
- package/dist/validators/index.js +14 -14
- package/dist/validators/map.d.ts +3 -3
- package/dist/validators/map.d.ts.map +1 -1
- package/dist/validators/map.js +5 -5
- package/dist/validators/number.d.ts +3 -3
- package/dist/validators/number.d.ts.map +1 -1
- package/dist/validators/number.js +20 -20
- package/dist/validators/password.d.ts +3 -3
- package/dist/validators/password.d.ts.map +1 -1
- package/dist/validators/password.js +11 -11
- package/dist/validators/photo.d.ts +3 -3
- package/dist/validators/photo.d.ts.map +1 -1
- package/dist/validators/photo.js +100 -100
- package/dist/validators/richText.d.ts +3 -3
- package/dist/validators/richText.d.ts.map +1 -1
- package/dist/validators/richText.js +8 -8
- package/dist/validators/select-multiple.d.ts +9 -9
- package/dist/validators/select-multiple.d.ts.map +1 -1
- package/dist/validators/select-multiple.js +20 -20
- package/dist/validators/select.d.ts +3 -3
- package/dist/validators/select.d.ts.map +1 -1
- package/dist/validators/select.js +5 -5
- package/dist/validators/text.d.ts +3 -3
- package/dist/validators/text.d.ts.map +1 -1
- package/dist/validators/text.js +7 -7
- package/dist/validators/textarea.d.ts +3 -3
- package/dist/validators/textarea.d.ts.map +1 -1
- package/dist/validators/textarea.js +7 -7
- package/dist/validators/video.d.ts +3 -3
- package/dist/validators/video.d.ts.map +1 -1
- package/dist/validators/video.js +57 -57
- package/package.json +4 -5
|
@@ -1,479 +1,479 @@
|
|
|
1
|
-
import { sql } from 'drizzle-orm';
|
|
2
|
-
import { db } from
|
|
3
|
-
import { SectionFactory } from
|
|
4
|
-
import { entityKind, is } from
|
|
5
|
-
import { NumberField, PhotoField, SelectField, TextField } from
|
|
6
|
-
import { MysqlTableChecker } from
|
|
7
|
-
export class Submit {
|
|
8
|
-
static [entityKind] = 'Submit';
|
|
9
|
-
user;
|
|
10
|
-
_itemId = undefined;
|
|
11
|
-
_error = false;
|
|
12
|
-
_errorMessage = '';
|
|
13
|
-
_notices = null;
|
|
14
|
-
sectionName;
|
|
15
|
-
_postData;
|
|
16
|
-
_sectionInfo;
|
|
17
|
-
preSubmit = false;
|
|
18
|
-
sqlNamesAndValues = {};
|
|
19
|
-
fields = [];
|
|
20
|
-
/**
|
|
21
|
-
* Constructor
|
|
22
|
-
*/
|
|
23
|
-
constructor({ preSubmit, sectionName, user, postData }) {
|
|
24
|
-
this.sectionName = sectionName;
|
|
25
|
-
this.user = user;
|
|
26
|
-
this._postData = postData;
|
|
27
|
-
this.preSubmit = preSubmit ?? false;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Run custom hooks after the item is updated
|
|
31
|
-
* @protected
|
|
32
|
-
*/
|
|
33
|
-
async runPostSubmitHooks() {
|
|
34
|
-
try {
|
|
35
|
-
await this._sectionInfo.hooks?.afterUpdate?.({
|
|
36
|
-
itemId: this._itemId,
|
|
37
|
-
values: this.sqlNamesAndValues,
|
|
38
|
-
section: this._sectionInfo,
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
catch (e) {
|
|
42
|
-
// Do not break the submit flow if a hook throws
|
|
43
|
-
console.error('afterUpdate hook failed:', e);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Log operation to the database
|
|
48
|
-
*/
|
|
49
|
-
async logOperation({ operation, message }) {
|
|
50
|
-
// TODO: Log the operation to the database,
|
|
51
|
-
// get the heading input name and its value to identify the item,
|
|
52
|
-
// then generate the log message
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Must be called after the constructor
|
|
56
|
-
*/
|
|
57
|
-
async initialize(requiredRole = 'C') {
|
|
58
|
-
await this.initializeSectionInfo(this.user, requiredRole);
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Gets the section info and assigns it to the `sectionInfo` property
|
|
62
|
-
* @param user
|
|
63
|
-
* @param requiredRole
|
|
64
|
-
* @private
|
|
65
|
-
*/
|
|
66
|
-
async initializeSectionInfo(user, requiredRole) {
|
|
67
|
-
/**
|
|
68
|
-
* First, let's get the section details, and then get the section input fields
|
|
69
|
-
*/
|
|
70
|
-
const s = await SectionFactory.getSectionForAdmin({
|
|
71
|
-
name: this.sectionName,
|
|
72
|
-
admin: {
|
|
73
|
-
id: user.id,
|
|
74
|
-
requiredRole: requiredRole,
|
|
75
|
-
},
|
|
76
|
-
});
|
|
77
|
-
if (!s || !s.name) {
|
|
78
|
-
this._error = true;
|
|
79
|
-
this._errorMessage = 'Section not found or you do not have access to this operation';
|
|
80
|
-
return undefined;
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Build the section from config when we need to use it
|
|
84
|
-
*/
|
|
85
|
-
this._sectionInfo = s.build();
|
|
86
|
-
/**
|
|
87
|
-
* Build the fields from the field configs
|
|
88
|
-
*/
|
|
89
|
-
this._sectionInfo.buildFields();
|
|
90
|
-
}
|
|
91
|
-
/**
|
|
92
|
-
* Perform post submit operations
|
|
93
|
-
* This will initiate the post submit operations for each field (if any)
|
|
94
|
-
* @private
|
|
95
|
-
*/
|
|
96
|
-
async postSubmit() {
|
|
97
|
-
try {
|
|
98
|
-
/**
|
|
99
|
-
* Perform post submit operations
|
|
100
|
-
*/
|
|
101
|
-
for (const field of this._sectionInfo?.fields ?? []) {
|
|
102
|
-
let variable = undefined;
|
|
103
|
-
/**
|
|
104
|
-
* Figure out the variable to pass to the post submit function
|
|
105
|
-
*/
|
|
106
|
-
switch (field.type) {
|
|
107
|
-
case 'photo':
|
|
108
|
-
case 'document':
|
|
109
|
-
case 'video':
|
|
110
|
-
/**
|
|
111
|
-
* File fields need the section name to save the file in the correct folder
|
|
112
|
-
*/
|
|
113
|
-
variable = this._sectionInfo?.name;
|
|
114
|
-
break;
|
|
115
|
-
case 'select_multiple':
|
|
116
|
-
case 'select':
|
|
117
|
-
/**
|
|
118
|
-
* Select multiple fields need the itemId,
|
|
119
|
-
* to save the selected values in the correct table with the correct item id
|
|
120
|
-
*/
|
|
121
|
-
variable = this._itemId;
|
|
122
|
-
break;
|
|
123
|
-
case 'rich_text':
|
|
124
|
-
/**
|
|
125
|
-
* Rich text fields need the section name to save the file in the correct folder,
|
|
126
|
-
* and the itemId to save inline images with the correct item id in the `editor_photos` table
|
|
127
|
-
*/
|
|
128
|
-
variable = {
|
|
129
|
-
sectionName: this._sectionInfo?.name,
|
|
130
|
-
itemId: this._itemId,
|
|
131
|
-
};
|
|
132
|
-
break;
|
|
133
|
-
default:
|
|
134
|
-
break;
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Call the post submit function
|
|
138
|
-
*/
|
|
139
|
-
await field.postSubmit(variable);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
catch (e) {
|
|
143
|
-
this._error = true;
|
|
144
|
-
this._errorMessage = e.message;
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Rollback post submit operations.<br>
|
|
150
|
-
* This will rollback the post submit operations for each field (if any)
|
|
151
|
-
* @private
|
|
152
|
-
*/
|
|
153
|
-
async postSubmitRollback() {
|
|
154
|
-
for (const field of this._sectionInfo?.fields ?? []) {
|
|
155
|
-
await field.postSubmitRollback();
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
/**
|
|
159
|
-
* Check if the conditional field conditions are met for a field.
|
|
160
|
-
* @param field
|
|
161
|
-
* @private
|
|
162
|
-
*/
|
|
163
|
-
checkConditionalField(field) {
|
|
164
|
-
/**
|
|
165
|
-
* Check if the conditions are met
|
|
166
|
-
*/
|
|
167
|
-
let status = false;
|
|
168
|
-
field.conditionalRules?.map((rule) => {
|
|
169
|
-
switch (rule.condition) {
|
|
170
|
-
case 'equals':
|
|
171
|
-
status = this._postData.get(rule.field.name)?.toString() === `${rule.value}`;
|
|
172
|
-
break;
|
|
173
|
-
case 'notEquals':
|
|
174
|
-
status = this._postData.get(rule.field.name)?.toString() !== `${rule.value}`;
|
|
175
|
-
break;
|
|
176
|
-
default:
|
|
177
|
-
break;
|
|
178
|
-
}
|
|
179
|
-
});
|
|
180
|
-
return status;
|
|
181
|
-
}
|
|
182
|
-
/**
|
|
183
|
-
* Check if the field is required and perform the required checks.
|
|
184
|
-
* @param field
|
|
185
|
-
* @protected
|
|
186
|
-
*/
|
|
187
|
-
checkRequired(field) {
|
|
188
|
-
field.checkRequired();
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* Set the field value.
|
|
192
|
-
* @param field
|
|
193
|
-
* @protected
|
|
194
|
-
*/
|
|
195
|
-
setFieldValue(field) {
|
|
196
|
-
let value;
|
|
197
|
-
/**
|
|
198
|
-
* Check if the field is a select field that has depth (selecting from category section with depth > 1)
|
|
199
|
-
*/
|
|
200
|
-
if (is(field, SelectField)) {
|
|
201
|
-
/**
|
|
202
|
-
* If true, value can be an array of values
|
|
203
|
-
*/
|
|
204
|
-
value = this._postData.getAll(field.name).filter(Boolean);
|
|
205
|
-
field.setValue(value.map((v) => {
|
|
206
|
-
return { value: v.toString(), label: '' };
|
|
207
|
-
}));
|
|
208
|
-
}
|
|
209
|
-
else {
|
|
210
|
-
value = this._postData.get(field.name);
|
|
211
|
-
if (value)
|
|
212
|
-
field.setValue(value);
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
updateSqlNamesAndValues(field) {
|
|
216
|
-
if (field.hasSqlNameAndValue()) {
|
|
217
|
-
this.sqlNamesAndValues[field.name] = field.getSubmitValue();
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
assignItemIdValue(field) {
|
|
221
|
-
if (!this._itemId &&
|
|
222
|
-
field.name === this._sectionInfo.db.identifier.name &&
|
|
223
|
-
(is(field, NumberField) || is(field, TextField))) {
|
|
224
|
-
this._itemId = field.getValue();
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
/**
|
|
228
|
-
* Handle the field
|
|
229
|
-
* @param field
|
|
230
|
-
* @protected
|
|
231
|
-
*/
|
|
232
|
-
async handleField(field) {
|
|
233
|
-
/**
|
|
234
|
-
* Get the value from the post request,
|
|
235
|
-
* and assign it to the field object
|
|
236
|
-
*/
|
|
237
|
-
this.setFieldValue(field);
|
|
238
|
-
/**
|
|
239
|
-
* Check if field is conditional, and check if the conditions are met.
|
|
240
|
-
* If the conditions are met, continue with the field.
|
|
241
|
-
* If the field is not conditional, skip the field, because it will not be included in the sql query.
|
|
242
|
-
*/
|
|
243
|
-
if (field.isConditional()) {
|
|
244
|
-
/**
|
|
245
|
-
* Check if the conditions are met
|
|
246
|
-
*/
|
|
247
|
-
if (!this.checkConditionalField(field)) {
|
|
248
|
-
/**
|
|
249
|
-
* Conditions are not met, skip the field
|
|
250
|
-
*/
|
|
251
|
-
return;
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
try {
|
|
255
|
-
/**
|
|
256
|
-
* Check if the field is required,
|
|
257
|
-
* This will throw an error if the field is required and the value is not present
|
|
258
|
-
*/
|
|
259
|
-
this.checkRequired(field);
|
|
260
|
-
/**
|
|
261
|
-
* Prepare the field for submission,
|
|
262
|
-
* this will throw an error if the checks fail
|
|
263
|
-
*/
|
|
264
|
-
await field.prepareForSubmission();
|
|
265
|
-
}
|
|
266
|
-
catch (e) {
|
|
267
|
-
this._error = true;
|
|
268
|
-
this._errorMessage = e.message;
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
/**
|
|
272
|
-
* Add the sql name and value to the sqlNamesAndValues object
|
|
273
|
-
*/
|
|
274
|
-
this.updateSqlNamesAndValues(field);
|
|
275
|
-
/**
|
|
276
|
-
* Assign the itemId value
|
|
277
|
-
*/
|
|
278
|
-
this.assignItemIdValue(field);
|
|
279
|
-
}
|
|
280
|
-
async handleFields() {
|
|
281
|
-
if (this._error)
|
|
282
|
-
return;
|
|
283
|
-
if (!this._sectionInfo)
|
|
284
|
-
return;
|
|
285
|
-
for (const field of this._sectionInfo.fields ?? []) {
|
|
286
|
-
if (field) {
|
|
287
|
-
await this.handleField(field);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
/**
|
|
292
|
-
* Execute the sql query.
|
|
293
|
-
* @param sqlQuery
|
|
294
|
-
* @private
|
|
295
|
-
*/
|
|
296
|
-
async executeQuery(sqlQuery) {
|
|
297
|
-
if (this._error)
|
|
298
|
-
return;
|
|
299
|
-
/**
|
|
300
|
-
* If there is a sql query, let's execute it
|
|
301
|
-
*/
|
|
302
|
-
if (!sqlQuery) {
|
|
303
|
-
this._error = true;
|
|
304
|
-
this._errorMessage = 'SQL query is not defined';
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
try {
|
|
308
|
-
const [submit, fields] = await db.execute(sqlQuery);
|
|
309
|
-
/**
|
|
310
|
-
* If there is no itemId,
|
|
311
|
-
* there should be an auto increment field in the table
|
|
312
|
-
* assign the insertId to the itemId property
|
|
313
|
-
*/
|
|
314
|
-
if (!this._itemId) {
|
|
315
|
-
this._itemId = submit.insertId;
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
catch (e) {
|
|
319
|
-
this._error = true;
|
|
320
|
-
this._errorMessage = e.message;
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
/**
|
|
325
|
-
* Try to submit the form.
|
|
326
|
-
*/
|
|
327
|
-
async submit() {
|
|
328
|
-
/**
|
|
329
|
-
* Assign the values to the fields
|
|
330
|
-
*/
|
|
331
|
-
await this.handleFields();
|
|
332
|
-
/**
|
|
333
|
-
* If this is a pre-submit operation, don't submit the form
|
|
334
|
-
*/
|
|
335
|
-
if (this.preSubmit) {
|
|
336
|
-
return;
|
|
337
|
-
}
|
|
338
|
-
/**
|
|
339
|
-
* Build the sql query
|
|
340
|
-
*/
|
|
341
|
-
const sqlQuery = this.buildSqlQuery();
|
|
342
|
-
/**
|
|
343
|
-
* Execute the sql query
|
|
344
|
-
*/
|
|
345
|
-
await this.executeQuery(sqlQuery);
|
|
346
|
-
/**
|
|
347
|
-
* Just return if there is an error,
|
|
348
|
-
* because post submit operations did not run yet
|
|
349
|
-
*/
|
|
350
|
-
if (this._error) {
|
|
351
|
-
return;
|
|
352
|
-
}
|
|
353
|
-
/**
|
|
354
|
-
* Perform post submit operations
|
|
355
|
-
*/
|
|
356
|
-
await this.postSubmit();
|
|
357
|
-
/**
|
|
358
|
-
* If there is an error in any of the post submit operations,
|
|
359
|
-
* rollback the submit and post submit operations
|
|
360
|
-
*/
|
|
361
|
-
if (this._error) {
|
|
362
|
-
try {
|
|
363
|
-
/**
|
|
364
|
-
* Rollback the submit operation
|
|
365
|
-
*/
|
|
366
|
-
await this.submitRollback();
|
|
367
|
-
/**
|
|
368
|
-
* Rollback the post submit operations for each field (if any)
|
|
369
|
-
*/
|
|
370
|
-
await this.postSubmitRollback();
|
|
371
|
-
}
|
|
372
|
-
catch (e) {
|
|
373
|
-
/**
|
|
374
|
-
* Those errors can be ignored
|
|
375
|
-
*/
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
// Only when there was no error, trigger post submit hooks
|
|
379
|
-
if (!this._error) {
|
|
380
|
-
// console.log('Running post submit hooks...')
|
|
381
|
-
await this.runPostSubmitHooks();
|
|
382
|
-
}
|
|
383
|
-
/**
|
|
384
|
-
* Handle gallery photos
|
|
385
|
-
*/
|
|
386
|
-
await this.handleGallery();
|
|
387
|
-
return true;
|
|
388
|
-
}
|
|
389
|
-
async handleGallery() {
|
|
390
|
-
if (!this._sectionInfo.gallery ||
|
|
391
|
-
!this._sectionInfo.gallery.db ||
|
|
392
|
-
!this._sectionInfo.gallery.db.tableName ||
|
|
393
|
-
!this._sectionInfo.gallery.db.referenceIdentifierField ||
|
|
394
|
-
!this._sectionInfo.gallery.db.metaField) {
|
|
395
|
-
/**
|
|
396
|
-
* The gallery table is not set up correctly, add it to the notices array
|
|
397
|
-
*/
|
|
398
|
-
this.addNotice('Gallery table is not set up correctly, gallery photos were not saved.');
|
|
399
|
-
return;
|
|
400
|
-
}
|
|
401
|
-
const columns = await MysqlTableChecker.getColumns(this._sectionInfo.gallery.db.tableName);
|
|
402
|
-
if (!columns.includes(this._sectionInfo.gallery.db.referenceIdentifierField) ||
|
|
403
|
-
!columns.includes(this._sectionInfo.gallery.db.metaField) ||
|
|
404
|
-
!columns.includes(this._sectionInfo.gallery.db.photoNameField)) {
|
|
405
|
-
/**
|
|
406
|
-
* The gallery table is not set up correctly, add it to the notices array
|
|
407
|
-
*/
|
|
408
|
-
this.addNotice('Gallery table is not set up correctly, gallery photos were not saved.');
|
|
409
|
-
return;
|
|
410
|
-
}
|
|
411
|
-
/**
|
|
412
|
-
* Gallery photos are sent as `dropzoneFiles` in the post request
|
|
413
|
-
*/
|
|
414
|
-
const galleryPhotos = this._postData.getAll('dropzoneFiles');
|
|
415
|
-
/**
|
|
416
|
-
* Loop through the gallery photos and create a new PhotoField for each photo
|
|
417
|
-
*/
|
|
418
|
-
for (const photo of galleryPhotos) {
|
|
419
|
-
/**
|
|
420
|
-
* Create a photo field instance for the gallery photo
|
|
421
|
-
*/
|
|
422
|
-
const photoField = new PhotoField({
|
|
423
|
-
name: 'dropzonePhoto',
|
|
424
|
-
label: 'Photo',
|
|
425
|
-
required: false,
|
|
426
|
-
order: 0,
|
|
427
|
-
type: ['jpg', 'jpeg', 'png'],
|
|
428
|
-
watermark: this._sectionInfo.gallery.watermark,
|
|
429
|
-
thumbnail: this._sectionInfo.gallery.thumbnail,
|
|
430
|
-
});
|
|
431
|
-
/**
|
|
432
|
-
* Set the value of the photo field
|
|
433
|
-
*/
|
|
434
|
-
photoField.setValue(photo);
|
|
435
|
-
try {
|
|
436
|
-
/**
|
|
437
|
-
* Prepare the photo field for submission
|
|
438
|
-
*/
|
|
439
|
-
await photoField.prepareForSubmission();
|
|
440
|
-
/**
|
|
441
|
-
* Perform post submit operations for the photo field
|
|
442
|
-
*/
|
|
443
|
-
await photoField.postSubmit(this._sectionInfo.name);
|
|
444
|
-
/**
|
|
445
|
-
* Prepare the sql statement to insert the photo into the gallery table
|
|
446
|
-
*/
|
|
447
|
-
const sqlChunks = [];
|
|
448
|
-
sqlChunks.push(sql `INSERT INTO \`${sql.raw(this._sectionInfo.gallery.db.tableName)}\` (\`${sql.raw(this._sectionInfo.gallery.db.referenceIdentifierField)}\`, \`${sql.raw(this._sectionInfo.gallery.db.photoNameField)}\`, \`created_by\`) VALUES (${this._itemId}, ${photoField.getValue()}, ${this.user.name})`);
|
|
449
|
-
const sqlQuery = sql.join(sqlChunks);
|
|
450
|
-
/**
|
|
451
|
-
* Execute the sql query
|
|
452
|
-
*/
|
|
453
|
-
await db.execute(sqlQuery);
|
|
454
|
-
}
|
|
455
|
-
catch (e) {
|
|
456
|
-
// It's not logical to roll back the whole operation.
|
|
457
|
-
// The admin can try again in the `edit` page.
|
|
458
|
-
// Add the error message to the notices array
|
|
459
|
-
this.addNotice(e.message);
|
|
460
|
-
console.log(e);
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
get errorMessage() {
|
|
465
|
-
return this._errorMessage;
|
|
466
|
-
}
|
|
467
|
-
get error() {
|
|
468
|
-
return this._error;
|
|
469
|
-
}
|
|
470
|
-
get notices() {
|
|
471
|
-
return this._notices;
|
|
472
|
-
}
|
|
473
|
-
addNotice(notice) {
|
|
474
|
-
if (!this._notices) {
|
|
475
|
-
this._notices = [];
|
|
476
|
-
}
|
|
477
|
-
this._notices.push(notice);
|
|
478
|
-
}
|
|
479
|
-
}
|
|
1
|
+
import { sql } from 'drizzle-orm';
|
|
2
|
+
import { db } from '../../db/client';
|
|
3
|
+
import { SectionFactory } from '../factories';
|
|
4
|
+
import { entityKind, is } from '../helpers';
|
|
5
|
+
import { NumberField, PhotoField, SelectField, TextField } from '../fields';
|
|
6
|
+
import { MysqlTableChecker } from '../db';
|
|
7
|
+
export class Submit {
|
|
8
|
+
static [entityKind] = 'Submit';
|
|
9
|
+
user;
|
|
10
|
+
_itemId = undefined;
|
|
11
|
+
_error = false;
|
|
12
|
+
_errorMessage = '';
|
|
13
|
+
_notices = null;
|
|
14
|
+
sectionName;
|
|
15
|
+
_postData;
|
|
16
|
+
_sectionInfo;
|
|
17
|
+
preSubmit = false;
|
|
18
|
+
sqlNamesAndValues = {};
|
|
19
|
+
fields = [];
|
|
20
|
+
/**
|
|
21
|
+
* Constructor
|
|
22
|
+
*/
|
|
23
|
+
constructor({ preSubmit, sectionName, user, postData }) {
|
|
24
|
+
this.sectionName = sectionName;
|
|
25
|
+
this.user = user;
|
|
26
|
+
this._postData = postData;
|
|
27
|
+
this.preSubmit = preSubmit ?? false;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Run custom hooks after the item is updated
|
|
31
|
+
* @protected
|
|
32
|
+
*/
|
|
33
|
+
async runPostSubmitHooks() {
|
|
34
|
+
try {
|
|
35
|
+
await this._sectionInfo.hooks?.afterUpdate?.({
|
|
36
|
+
itemId: this._itemId,
|
|
37
|
+
values: this.sqlNamesAndValues,
|
|
38
|
+
section: this._sectionInfo,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
catch (e) {
|
|
42
|
+
// Do not break the submit flow if a hook throws
|
|
43
|
+
console.error('afterUpdate hook failed:', e);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Log operation to the database
|
|
48
|
+
*/
|
|
49
|
+
async logOperation({ operation, message }) {
|
|
50
|
+
// TODO: Log the operation to the database,
|
|
51
|
+
// get the heading input name and its value to identify the item,
|
|
52
|
+
// then generate the log message
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Must be called after the constructor
|
|
56
|
+
*/
|
|
57
|
+
async initialize(requiredRole = 'C') {
|
|
58
|
+
await this.initializeSectionInfo(this.user, requiredRole);
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Gets the section info and assigns it to the `sectionInfo` property
|
|
62
|
+
* @param user
|
|
63
|
+
* @param requiredRole
|
|
64
|
+
* @private
|
|
65
|
+
*/
|
|
66
|
+
async initializeSectionInfo(user, requiredRole) {
|
|
67
|
+
/**
|
|
68
|
+
* First, let's get the section details, and then get the section input fields
|
|
69
|
+
*/
|
|
70
|
+
const s = await SectionFactory.getSectionForAdmin({
|
|
71
|
+
name: this.sectionName,
|
|
72
|
+
admin: {
|
|
73
|
+
id: user.id,
|
|
74
|
+
requiredRole: requiredRole,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
if (!s || !s.name) {
|
|
78
|
+
this._error = true;
|
|
79
|
+
this._errorMessage = 'Section not found or you do not have access to this operation';
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Build the section from config when we need to use it
|
|
84
|
+
*/
|
|
85
|
+
this._sectionInfo = s.build();
|
|
86
|
+
/**
|
|
87
|
+
* Build the fields from the field configs
|
|
88
|
+
*/
|
|
89
|
+
this._sectionInfo.buildFields();
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Perform post submit operations
|
|
93
|
+
* This will initiate the post submit operations for each field (if any)
|
|
94
|
+
* @private
|
|
95
|
+
*/
|
|
96
|
+
async postSubmit() {
|
|
97
|
+
try {
|
|
98
|
+
/**
|
|
99
|
+
* Perform post submit operations
|
|
100
|
+
*/
|
|
101
|
+
for (const field of this._sectionInfo?.fields ?? []) {
|
|
102
|
+
let variable = undefined;
|
|
103
|
+
/**
|
|
104
|
+
* Figure out the variable to pass to the post submit function
|
|
105
|
+
*/
|
|
106
|
+
switch (field.type) {
|
|
107
|
+
case 'photo':
|
|
108
|
+
case 'document':
|
|
109
|
+
case 'video':
|
|
110
|
+
/**
|
|
111
|
+
* File fields need the section name to save the file in the correct folder
|
|
112
|
+
*/
|
|
113
|
+
variable = this._sectionInfo?.name;
|
|
114
|
+
break;
|
|
115
|
+
case 'select_multiple':
|
|
116
|
+
case 'select':
|
|
117
|
+
/**
|
|
118
|
+
* Select multiple fields need the itemId,
|
|
119
|
+
* to save the selected values in the correct table with the correct item id
|
|
120
|
+
*/
|
|
121
|
+
variable = this._itemId;
|
|
122
|
+
break;
|
|
123
|
+
case 'rich_text':
|
|
124
|
+
/**
|
|
125
|
+
* Rich text fields need the section name to save the file in the correct folder,
|
|
126
|
+
* and the itemId to save inline images with the correct item id in the `editor_photos` table
|
|
127
|
+
*/
|
|
128
|
+
variable = {
|
|
129
|
+
sectionName: this._sectionInfo?.name,
|
|
130
|
+
itemId: this._itemId,
|
|
131
|
+
};
|
|
132
|
+
break;
|
|
133
|
+
default:
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Call the post submit function
|
|
138
|
+
*/
|
|
139
|
+
await field.postSubmit(variable);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch (e) {
|
|
143
|
+
this._error = true;
|
|
144
|
+
this._errorMessage = e.message;
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Rollback post submit operations.<br>
|
|
150
|
+
* This will rollback the post submit operations for each field (if any)
|
|
151
|
+
* @private
|
|
152
|
+
*/
|
|
153
|
+
async postSubmitRollback() {
|
|
154
|
+
for (const field of this._sectionInfo?.fields ?? []) {
|
|
155
|
+
await field.postSubmitRollback();
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Check if the conditional field conditions are met for a field.
|
|
160
|
+
* @param field
|
|
161
|
+
* @private
|
|
162
|
+
*/
|
|
163
|
+
checkConditionalField(field) {
|
|
164
|
+
/**
|
|
165
|
+
* Check if the conditions are met
|
|
166
|
+
*/
|
|
167
|
+
let status = false;
|
|
168
|
+
field.conditionalRules?.map((rule) => {
|
|
169
|
+
switch (rule.condition) {
|
|
170
|
+
case 'equals':
|
|
171
|
+
status = this._postData.get(rule.field.name)?.toString() === `${rule.value}`;
|
|
172
|
+
break;
|
|
173
|
+
case 'notEquals':
|
|
174
|
+
status = this._postData.get(rule.field.name)?.toString() !== `${rule.value}`;
|
|
175
|
+
break;
|
|
176
|
+
default:
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
return status;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Check if the field is required and perform the required checks.
|
|
184
|
+
* @param field
|
|
185
|
+
* @protected
|
|
186
|
+
*/
|
|
187
|
+
checkRequired(field) {
|
|
188
|
+
field.checkRequired();
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Set the field value.
|
|
192
|
+
* @param field
|
|
193
|
+
* @protected
|
|
194
|
+
*/
|
|
195
|
+
setFieldValue(field) {
|
|
196
|
+
let value;
|
|
197
|
+
/**
|
|
198
|
+
* Check if the field is a select field that has depth (selecting from category section with depth > 1)
|
|
199
|
+
*/
|
|
200
|
+
if (is(field, SelectField)) {
|
|
201
|
+
/**
|
|
202
|
+
* If true, value can be an array of values
|
|
203
|
+
*/
|
|
204
|
+
value = this._postData.getAll(field.name).filter(Boolean);
|
|
205
|
+
field.setValue(value.map((v) => {
|
|
206
|
+
return { value: v.toString(), label: '' };
|
|
207
|
+
}));
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
value = this._postData.get(field.name);
|
|
211
|
+
if (value)
|
|
212
|
+
field.setValue(value);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
updateSqlNamesAndValues(field) {
|
|
216
|
+
if (field.hasSqlNameAndValue()) {
|
|
217
|
+
this.sqlNamesAndValues[field.name] = field.getSubmitValue();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
assignItemIdValue(field) {
|
|
221
|
+
if (!this._itemId &&
|
|
222
|
+
field.name === this._sectionInfo.db.identifier.name &&
|
|
223
|
+
(is(field, NumberField) || is(field, TextField))) {
|
|
224
|
+
this._itemId = field.getValue();
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Handle the field
|
|
229
|
+
* @param field
|
|
230
|
+
* @protected
|
|
231
|
+
*/
|
|
232
|
+
async handleField(field) {
|
|
233
|
+
/**
|
|
234
|
+
* Get the value from the post request,
|
|
235
|
+
* and assign it to the field object
|
|
236
|
+
*/
|
|
237
|
+
this.setFieldValue(field);
|
|
238
|
+
/**
|
|
239
|
+
* Check if field is conditional, and check if the conditions are met.
|
|
240
|
+
* If the conditions are met, continue with the field.
|
|
241
|
+
* If the field is not conditional, skip the field, because it will not be included in the sql query.
|
|
242
|
+
*/
|
|
243
|
+
if (field.isConditional()) {
|
|
244
|
+
/**
|
|
245
|
+
* Check if the conditions are met
|
|
246
|
+
*/
|
|
247
|
+
if (!this.checkConditionalField(field)) {
|
|
248
|
+
/**
|
|
249
|
+
* Conditions are not met, skip the field
|
|
250
|
+
*/
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
try {
|
|
255
|
+
/**
|
|
256
|
+
* Check if the field is required,
|
|
257
|
+
* This will throw an error if the field is required and the value is not present
|
|
258
|
+
*/
|
|
259
|
+
this.checkRequired(field);
|
|
260
|
+
/**
|
|
261
|
+
* Prepare the field for submission,
|
|
262
|
+
* this will throw an error if the checks fail
|
|
263
|
+
*/
|
|
264
|
+
await field.prepareForSubmission();
|
|
265
|
+
}
|
|
266
|
+
catch (e) {
|
|
267
|
+
this._error = true;
|
|
268
|
+
this._errorMessage = e.message;
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Add the sql name and value to the sqlNamesAndValues object
|
|
273
|
+
*/
|
|
274
|
+
this.updateSqlNamesAndValues(field);
|
|
275
|
+
/**
|
|
276
|
+
* Assign the itemId value
|
|
277
|
+
*/
|
|
278
|
+
this.assignItemIdValue(field);
|
|
279
|
+
}
|
|
280
|
+
async handleFields() {
|
|
281
|
+
if (this._error)
|
|
282
|
+
return;
|
|
283
|
+
if (!this._sectionInfo)
|
|
284
|
+
return;
|
|
285
|
+
for (const field of this._sectionInfo.fields ?? []) {
|
|
286
|
+
if (field) {
|
|
287
|
+
await this.handleField(field);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Execute the sql query.
|
|
293
|
+
* @param sqlQuery
|
|
294
|
+
* @private
|
|
295
|
+
*/
|
|
296
|
+
async executeQuery(sqlQuery) {
|
|
297
|
+
if (this._error)
|
|
298
|
+
return;
|
|
299
|
+
/**
|
|
300
|
+
* If there is a sql query, let's execute it
|
|
301
|
+
*/
|
|
302
|
+
if (!sqlQuery) {
|
|
303
|
+
this._error = true;
|
|
304
|
+
this._errorMessage = 'SQL query is not defined';
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
try {
|
|
308
|
+
const [submit, fields] = await db.execute(sqlQuery);
|
|
309
|
+
/**
|
|
310
|
+
* If there is no itemId,
|
|
311
|
+
* there should be an auto increment field in the table
|
|
312
|
+
* assign the insertId to the itemId property
|
|
313
|
+
*/
|
|
314
|
+
if (!this._itemId) {
|
|
315
|
+
this._itemId = submit.insertId;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
catch (e) {
|
|
319
|
+
this._error = true;
|
|
320
|
+
this._errorMessage = e.message;
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Try to submit the form.
|
|
326
|
+
*/
|
|
327
|
+
async submit() {
|
|
328
|
+
/**
|
|
329
|
+
* Assign the values to the fields
|
|
330
|
+
*/
|
|
331
|
+
await this.handleFields();
|
|
332
|
+
/**
|
|
333
|
+
* If this is a pre-submit operation, don't submit the form
|
|
334
|
+
*/
|
|
335
|
+
if (this.preSubmit) {
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Build the sql query
|
|
340
|
+
*/
|
|
341
|
+
const sqlQuery = this.buildSqlQuery();
|
|
342
|
+
/**
|
|
343
|
+
* Execute the sql query
|
|
344
|
+
*/
|
|
345
|
+
await this.executeQuery(sqlQuery);
|
|
346
|
+
/**
|
|
347
|
+
* Just return if there is an error,
|
|
348
|
+
* because post submit operations did not run yet
|
|
349
|
+
*/
|
|
350
|
+
if (this._error) {
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Perform post submit operations
|
|
355
|
+
*/
|
|
356
|
+
await this.postSubmit();
|
|
357
|
+
/**
|
|
358
|
+
* If there is an error in any of the post submit operations,
|
|
359
|
+
* rollback the submit and post submit operations
|
|
360
|
+
*/
|
|
361
|
+
if (this._error) {
|
|
362
|
+
try {
|
|
363
|
+
/**
|
|
364
|
+
* Rollback the submit operation
|
|
365
|
+
*/
|
|
366
|
+
await this.submitRollback();
|
|
367
|
+
/**
|
|
368
|
+
* Rollback the post submit operations for each field (if any)
|
|
369
|
+
*/
|
|
370
|
+
await this.postSubmitRollback();
|
|
371
|
+
}
|
|
372
|
+
catch (e) {
|
|
373
|
+
/**
|
|
374
|
+
* Those errors can be ignored
|
|
375
|
+
*/
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
// Only when there was no error, trigger post submit hooks
|
|
379
|
+
if (!this._error) {
|
|
380
|
+
// console.log('Running post submit hooks...')
|
|
381
|
+
await this.runPostSubmitHooks();
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Handle gallery photos
|
|
385
|
+
*/
|
|
386
|
+
await this.handleGallery();
|
|
387
|
+
return true;
|
|
388
|
+
}
|
|
389
|
+
async handleGallery() {
|
|
390
|
+
if (!this._sectionInfo.gallery ||
|
|
391
|
+
!this._sectionInfo.gallery.db ||
|
|
392
|
+
!this._sectionInfo.gallery.db.tableName ||
|
|
393
|
+
!this._sectionInfo.gallery.db.referenceIdentifierField ||
|
|
394
|
+
!this._sectionInfo.gallery.db.metaField) {
|
|
395
|
+
/**
|
|
396
|
+
* The gallery table is not set up correctly, add it to the notices array
|
|
397
|
+
*/
|
|
398
|
+
this.addNotice('Gallery table is not set up correctly, gallery photos were not saved.');
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
const columns = await MysqlTableChecker.getColumns(this._sectionInfo.gallery.db.tableName);
|
|
402
|
+
if (!columns.includes(this._sectionInfo.gallery.db.referenceIdentifierField) ||
|
|
403
|
+
!columns.includes(this._sectionInfo.gallery.db.metaField) ||
|
|
404
|
+
!columns.includes(this._sectionInfo.gallery.db.photoNameField)) {
|
|
405
|
+
/**
|
|
406
|
+
* The gallery table is not set up correctly, add it to the notices array
|
|
407
|
+
*/
|
|
408
|
+
this.addNotice('Gallery table is not set up correctly, gallery photos were not saved.');
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Gallery photos are sent as `dropzoneFiles` in the post request
|
|
413
|
+
*/
|
|
414
|
+
const galleryPhotos = this._postData.getAll('dropzoneFiles');
|
|
415
|
+
/**
|
|
416
|
+
* Loop through the gallery photos and create a new PhotoField for each photo
|
|
417
|
+
*/
|
|
418
|
+
for (const photo of galleryPhotos) {
|
|
419
|
+
/**
|
|
420
|
+
* Create a photo field instance for the gallery photo
|
|
421
|
+
*/
|
|
422
|
+
const photoField = new PhotoField({
|
|
423
|
+
name: 'dropzonePhoto',
|
|
424
|
+
label: 'Photo',
|
|
425
|
+
required: false,
|
|
426
|
+
order: 0,
|
|
427
|
+
type: ['jpg', 'jpeg', 'png'],
|
|
428
|
+
watermark: this._sectionInfo.gallery.watermark,
|
|
429
|
+
thumbnail: this._sectionInfo.gallery.thumbnail,
|
|
430
|
+
});
|
|
431
|
+
/**
|
|
432
|
+
* Set the value of the photo field
|
|
433
|
+
*/
|
|
434
|
+
photoField.setValue(photo);
|
|
435
|
+
try {
|
|
436
|
+
/**
|
|
437
|
+
* Prepare the photo field for submission
|
|
438
|
+
*/
|
|
439
|
+
await photoField.prepareForSubmission();
|
|
440
|
+
/**
|
|
441
|
+
* Perform post submit operations for the photo field
|
|
442
|
+
*/
|
|
443
|
+
await photoField.postSubmit(this._sectionInfo.name);
|
|
444
|
+
/**
|
|
445
|
+
* Prepare the sql statement to insert the photo into the gallery table
|
|
446
|
+
*/
|
|
447
|
+
const sqlChunks = [];
|
|
448
|
+
sqlChunks.push(sql `INSERT INTO \`${sql.raw(this._sectionInfo.gallery.db.tableName)}\` (\`${sql.raw(this._sectionInfo.gallery.db.referenceIdentifierField)}\`, \`${sql.raw(this._sectionInfo.gallery.db.photoNameField)}\`, \`created_by\`) VALUES (${this._itemId}, ${photoField.getValue()}, ${this.user.name})`);
|
|
449
|
+
const sqlQuery = sql.join(sqlChunks);
|
|
450
|
+
/**
|
|
451
|
+
* Execute the sql query
|
|
452
|
+
*/
|
|
453
|
+
await db.execute(sqlQuery);
|
|
454
|
+
}
|
|
455
|
+
catch (e) {
|
|
456
|
+
// It's not logical to roll back the whole operation.
|
|
457
|
+
// The admin can try again in the `edit` page.
|
|
458
|
+
// Add the error message to the notices array
|
|
459
|
+
this.addNotice(e.message);
|
|
460
|
+
console.log(e);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
get errorMessage() {
|
|
465
|
+
return this._errorMessage;
|
|
466
|
+
}
|
|
467
|
+
get error() {
|
|
468
|
+
return this._error;
|
|
469
|
+
}
|
|
470
|
+
get notices() {
|
|
471
|
+
return this._notices;
|
|
472
|
+
}
|
|
473
|
+
addNotice(notice) {
|
|
474
|
+
if (!this._notices) {
|
|
475
|
+
this._notices = [];
|
|
476
|
+
}
|
|
477
|
+
this._notices.push(notice);
|
|
478
|
+
}
|
|
479
|
+
}
|