@adminforth/markdown 1.3.0 → 1.4.0
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/index.js +126 -36
- package/index.ts +169 -54
- package/package.json +1 -1
- package/types.ts +14 -0
package/dist/index.js
CHANGED
|
@@ -54,6 +54,18 @@ export default class MarkdownPlugin extends AdminForthPlugin {
|
|
|
54
54
|
if (!field) {
|
|
55
55
|
throw new Error(`Field '${this.options.attachments.attachmentFieldName}' not found in resource '${this.options.attachments.attachmentResource}'`);
|
|
56
56
|
}
|
|
57
|
+
if (this.options.attachments.attachmentTitleFieldName) {
|
|
58
|
+
const titleField = yield resource.columns.find(c => c.name === this.options.attachments.attachmentTitleFieldName);
|
|
59
|
+
if (!titleField) {
|
|
60
|
+
throw new Error(`Field '${this.options.attachments.attachmentTitleFieldName}' not found in resource '${this.options.attachments.attachmentResource}'`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (this.options.attachments.attachmentAltFieldName) {
|
|
64
|
+
const altField = yield resource.columns.find(c => c.name === this.options.attachments.attachmentAltFieldName);
|
|
65
|
+
if (!altField) {
|
|
66
|
+
throw new Error(`Field '${this.options.attachments.attachmentAltFieldName}' not found in resource '${this.options.attachments.attachmentResource}'`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
57
69
|
const plugin = yield adminforth.activatedPlugins.find(p => p.resourceConfig.resourceId === this.options.attachments.attachmentResource &&
|
|
58
70
|
p.pluginOptions.pathColumnName === this.options.attachments.attachmentFieldName);
|
|
59
71
|
if (!plugin) {
|
|
@@ -99,36 +111,60 @@ export default class MarkdownPlugin extends AdminForthPlugin {
|
|
|
99
111
|
};
|
|
100
112
|
const editorRecordPkField = resourceConfig.columns.find(c => c.primaryKey);
|
|
101
113
|
if (this.options.attachments) {
|
|
102
|
-
|
|
114
|
+
const extractKeyFromUrl = (url) => url.replace(/^https:\/\/[^\/]+\/+/, '');
|
|
115
|
+
function getAttachmentMetas(markdown) {
|
|
116
|
+
var _a, _b, _c;
|
|
103
117
|
if (!markdown) {
|
|
104
118
|
return [];
|
|
105
119
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
120
|
+
// Minimal image syntax:  or  or 
|
|
121
|
+
// We only track https URLs and only those that look like S3/AWS public URLs.
|
|
122
|
+
const imageRegex = /!\[([^\]]*)\]\(\s*(https:\/\/[^\s)]+)\s*(?:\s+(?:"([^"]*)"|'([^']*)'))?\s*\)/g;
|
|
123
|
+
const byKey = new Map();
|
|
124
|
+
for (const match of markdown.matchAll(imageRegex)) {
|
|
125
|
+
const altRaw = (_a = match[1]) !== null && _a !== void 0 ? _a : '';
|
|
126
|
+
const srcRaw = match[2];
|
|
127
|
+
const titleRaw = (_c = ((_b = match[3]) !== null && _b !== void 0 ? _b : match[4])) !== null && _c !== void 0 ? _c : null;
|
|
128
|
+
const srcNoQuery = srcRaw.split('?')[0];
|
|
129
|
+
if (!srcNoQuery.includes('s3') && !srcNoQuery.includes('amazonaws')) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const key = extractKeyFromUrl(srcNoQuery);
|
|
133
|
+
byKey.set(key, {
|
|
134
|
+
key,
|
|
135
|
+
alt: altRaw,
|
|
136
|
+
title: titleRaw,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
return [...byKey.values()];
|
|
111
140
|
}
|
|
112
|
-
const createAttachmentRecords = (adminforth, options, recordId,
|
|
113
|
-
|
|
141
|
+
const createAttachmentRecords = (adminforth, options, recordId, metas, adminUser) => __awaiter(this, void 0, void 0, function* () {
|
|
142
|
+
if (!metas.length) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
114
145
|
process.env.HEAVY_DEBUG && console.log('📸 Creating attachment records', JSON.stringify(recordId));
|
|
115
146
|
try {
|
|
116
|
-
yield Promise.all(
|
|
117
|
-
console.log('Processing path:', s3Path);
|
|
147
|
+
yield Promise.all(metas.map((meta) => __awaiter(this, void 0, void 0, function* () {
|
|
118
148
|
try {
|
|
149
|
+
const recordToCreate = {
|
|
150
|
+
[options.attachments.attachmentFieldName]: meta.key,
|
|
151
|
+
[options.attachments.attachmentRecordIdFieldName]: recordId,
|
|
152
|
+
[options.attachments.attachmentResourceIdFieldName]: resourceConfig.resourceId,
|
|
153
|
+
};
|
|
154
|
+
if (options.attachments.attachmentTitleFieldName) {
|
|
155
|
+
recordToCreate[options.attachments.attachmentTitleFieldName] = meta.title;
|
|
156
|
+
}
|
|
157
|
+
if (options.attachments.attachmentAltFieldName) {
|
|
158
|
+
recordToCreate[options.attachments.attachmentAltFieldName] = meta.alt;
|
|
159
|
+
}
|
|
119
160
|
yield adminforth.createResourceRecord({
|
|
120
161
|
resource: this.attachmentResource,
|
|
121
|
-
record:
|
|
122
|
-
[options.attachments.attachmentFieldName]: extractKey(s3Path),
|
|
123
|
-
[options.attachments.attachmentRecordIdFieldName]: recordId,
|
|
124
|
-
[options.attachments.attachmentResourceIdFieldName]: resourceConfig.resourceId,
|
|
125
|
-
},
|
|
162
|
+
record: recordToCreate,
|
|
126
163
|
adminUser,
|
|
127
164
|
});
|
|
128
|
-
console.log('Successfully created record for:', s3Path);
|
|
129
165
|
}
|
|
130
166
|
catch (err) {
|
|
131
|
-
console.error('Error creating record for',
|
|
167
|
+
console.error('Error creating record for', meta.key, err);
|
|
132
168
|
}
|
|
133
169
|
})));
|
|
134
170
|
}
|
|
@@ -136,12 +172,16 @@ export default class MarkdownPlugin extends AdminForthPlugin {
|
|
|
136
172
|
console.error('Error in Promise.all', err);
|
|
137
173
|
}
|
|
138
174
|
});
|
|
139
|
-
const deleteAttachmentRecords = (adminforth, options,
|
|
140
|
-
if (!
|
|
175
|
+
const deleteAttachmentRecords = (adminforth, options, recordId, keys, adminUser) => __awaiter(this, void 0, void 0, function* () {
|
|
176
|
+
if (!keys.length) {
|
|
141
177
|
return;
|
|
142
178
|
}
|
|
143
179
|
const attachmentPrimaryKeyField = this.attachmentResource.columns.find(c => c.primaryKey);
|
|
144
|
-
const attachments = yield adminforth.resource(options.attachments.attachmentResource).list(
|
|
180
|
+
const attachments = yield adminforth.resource(options.attachments.attachmentResource).list([
|
|
181
|
+
Filters.EQ(options.attachments.attachmentRecordIdFieldName, recordId),
|
|
182
|
+
Filters.EQ(options.attachments.attachmentResourceIdFieldName, resourceConfig.resourceId),
|
|
183
|
+
Filters.IN(options.attachments.attachmentFieldName, keys),
|
|
184
|
+
]);
|
|
145
185
|
yield Promise.all(attachments.map((a) => __awaiter(this, void 0, void 0, function* () {
|
|
146
186
|
yield adminforth.deleteResourceRecord({
|
|
147
187
|
resource: this.attachmentResource,
|
|
@@ -151,12 +191,58 @@ export default class MarkdownPlugin extends AdminForthPlugin {
|
|
|
151
191
|
});
|
|
152
192
|
})));
|
|
153
193
|
});
|
|
194
|
+
const updateAttachmentRecordsMetadata = (adminforth, options, recordId, metas, adminUser) => __awaiter(this, void 0, void 0, function* () {
|
|
195
|
+
if (!metas.length) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (!options.attachments.attachmentTitleFieldName && !options.attachments.attachmentAltFieldName) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const attachmentPrimaryKeyField = this.attachmentResource.columns.find(c => c.primaryKey);
|
|
202
|
+
const metaByKey = new Map(metas.map(m => [m.key, m]));
|
|
203
|
+
const existingAparts = yield adminforth.resource(options.attachments.attachmentResource).list([
|
|
204
|
+
Filters.EQ(options.attachments.attachmentRecordIdFieldName, recordId),
|
|
205
|
+
Filters.EQ(options.attachments.attachmentResourceIdFieldName, resourceConfig.resourceId)
|
|
206
|
+
]);
|
|
207
|
+
yield Promise.all(existingAparts.map((a) => __awaiter(this, void 0, void 0, function* () {
|
|
208
|
+
var _a, _b, _c, _d;
|
|
209
|
+
const key = a[options.attachments.attachmentFieldName];
|
|
210
|
+
const meta = metaByKey.get(key);
|
|
211
|
+
if (!meta) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
const patch = {};
|
|
215
|
+
if (options.attachments.attachmentTitleFieldName) {
|
|
216
|
+
const field = options.attachments.attachmentTitleFieldName;
|
|
217
|
+
if (((_a = a[field]) !== null && _a !== void 0 ? _a : null) !== ((_b = meta.title) !== null && _b !== void 0 ? _b : null)) {
|
|
218
|
+
patch[field] = meta.title;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (options.attachments.attachmentAltFieldName) {
|
|
222
|
+
const field = options.attachments.attachmentAltFieldName;
|
|
223
|
+
if (((_c = a[field]) !== null && _c !== void 0 ? _c : null) !== ((_d = meta.alt) !== null && _d !== void 0 ? _d : null)) {
|
|
224
|
+
patch[field] = meta.alt;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (!Object.keys(patch).length) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
yield adminforth.updateResourceRecord({
|
|
231
|
+
resource: this.attachmentResource,
|
|
232
|
+
recordId: a[attachmentPrimaryKeyField.name],
|
|
233
|
+
record: patch,
|
|
234
|
+
oldRecord: a,
|
|
235
|
+
adminUser,
|
|
236
|
+
});
|
|
237
|
+
})));
|
|
238
|
+
});
|
|
154
239
|
(resourceConfig.hooks.create.afterSave).push((_a) => __awaiter(this, [_a], void 0, function* ({ record, adminUser }) {
|
|
155
240
|
// find all s3Paths in the html
|
|
156
|
-
const
|
|
157
|
-
|
|
241
|
+
const metas = getAttachmentMetas(record[this.options.fieldName]);
|
|
242
|
+
const keys = metas.map(m => m.key);
|
|
243
|
+
process.env.HEAVY_DEBUG && console.log('📸 Found attachment keys', keys);
|
|
158
244
|
// create attachment records
|
|
159
|
-
yield createAttachmentRecords(adminforth, this.options, record[editorRecordPkField.name],
|
|
245
|
+
yield createAttachmentRecords(adminforth, this.options, record[editorRecordPkField.name], metas, adminUser);
|
|
160
246
|
return { ok: true };
|
|
161
247
|
}));
|
|
162
248
|
// after edit we need to delete attachments that are not in the html anymore
|
|
@@ -172,18 +258,22 @@ export default class MarkdownPlugin extends AdminForthPlugin {
|
|
|
172
258
|
Filters.EQ(this.options.attachments.attachmentRecordIdFieldName, recordId),
|
|
173
259
|
Filters.EQ(this.options.attachments.attachmentResourceIdFieldName, resourceConfig.resourceId)
|
|
174
260
|
]);
|
|
175
|
-
const
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
process.env.HEAVY_DEBUG && console.log('📸
|
|
179
|
-
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
process.env.HEAVY_DEBUG && console.log('📸 Found
|
|
261
|
+
const existingKeys = existingAparts.map((a) => a[this.options.attachments.attachmentFieldName]);
|
|
262
|
+
const metas = getAttachmentMetas(record[this.options.fieldName]);
|
|
263
|
+
const newKeys = metas.map(m => m.key);
|
|
264
|
+
process.env.HEAVY_DEBUG && console.log('📸 Existing keys (from db)', existingKeys);
|
|
265
|
+
process.env.HEAVY_DEBUG && console.log('📸 Found new keys (from text)', newKeys);
|
|
266
|
+
const toDelete = existingKeys.filter(key => !newKeys.includes(key));
|
|
267
|
+
const toAdd = newKeys.filter(key => !existingKeys.includes(key));
|
|
268
|
+
process.env.HEAVY_DEBUG && console.log('📸 Found keys to delete', toDelete);
|
|
269
|
+
process.env.HEAVY_DEBUG && console.log('📸 Found keys to add', toAdd);
|
|
270
|
+
const metasToAdd = metas.filter(m => toAdd.includes(m.key));
|
|
183
271
|
yield Promise.all([
|
|
184
|
-
deleteAttachmentRecords(adminforth, this.options, toDelete, adminUser),
|
|
185
|
-
createAttachmentRecords(adminforth, this.options, recordId,
|
|
272
|
+
deleteAttachmentRecords(adminforth, this.options, recordId, toDelete, adminUser),
|
|
273
|
+
createAttachmentRecords(adminforth, this.options, recordId, metasToAdd, adminUser)
|
|
186
274
|
]);
|
|
275
|
+
// Keep alt/title in sync for existing attachments too
|
|
276
|
+
yield updateAttachmentRecordsMetadata(adminforth, this.options, recordId, metas, adminUser);
|
|
187
277
|
return { ok: true };
|
|
188
278
|
}));
|
|
189
279
|
// after delete we need to delete all attachments
|
|
@@ -192,9 +282,9 @@ export default class MarkdownPlugin extends AdminForthPlugin {
|
|
|
192
282
|
Filters.EQ(this.options.attachments.attachmentRecordIdFieldName, record[editorRecordPkField.name]),
|
|
193
283
|
Filters.EQ(this.options.attachments.attachmentResourceIdFieldName, resourceConfig.resourceId)
|
|
194
284
|
]);
|
|
195
|
-
const
|
|
196
|
-
process.env.HEAVY_DEBUG && console.log('📸 Found
|
|
197
|
-
yield deleteAttachmentRecords(adminforth, this.options,
|
|
285
|
+
const existingKeys = existingAparts.map((a) => a[this.options.attachments.attachmentFieldName]);
|
|
286
|
+
process.env.HEAVY_DEBUG && console.log('📸 Found keys to delete', existingKeys);
|
|
287
|
+
yield deleteAttachmentRecords(adminforth, this.options, record[editorRecordPkField.name], existingKeys, adminUser);
|
|
198
288
|
return { ok: true };
|
|
199
289
|
}));
|
|
200
290
|
}
|
package/index.ts
CHANGED
|
@@ -52,6 +52,20 @@ export default class MarkdownPlugin extends AdminForthPlugin {
|
|
|
52
52
|
if (!field) {
|
|
53
53
|
throw new Error(`Field '${this.options.attachments!.attachmentFieldName}' not found in resource '${this.options.attachments!.attachmentResource}'`);
|
|
54
54
|
}
|
|
55
|
+
|
|
56
|
+
if (this.options.attachments.attachmentTitleFieldName) {
|
|
57
|
+
const titleField = await resource.columns.find(c => c.name === this.options.attachments!.attachmentTitleFieldName);
|
|
58
|
+
if (!titleField) {
|
|
59
|
+
throw new Error(`Field '${this.options.attachments!.attachmentTitleFieldName}' not found in resource '${this.options.attachments!.attachmentResource}'`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (this.options.attachments.attachmentAltFieldName) {
|
|
64
|
+
const altField = await resource.columns.find(c => c.name === this.options.attachments!.attachmentAltFieldName);
|
|
65
|
+
if (!altField) {
|
|
66
|
+
throw new Error(`Field '${this.options.attachments!.attachmentAltFieldName}' not found in resource '${this.options.attachments!.attachmentResource}'`);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
55
69
|
|
|
56
70
|
const plugin = await adminforth.activatedPlugins.find(p =>
|
|
57
71
|
p.resourceConfig!.resourceId === this.options.attachments!.attachmentResource &&
|
|
@@ -106,43 +120,74 @@ export default class MarkdownPlugin extends AdminForthPlugin {
|
|
|
106
120
|
const editorRecordPkField = resourceConfig.columns.find(c => c.primaryKey);
|
|
107
121
|
if (this.options.attachments) {
|
|
108
122
|
|
|
109
|
-
|
|
123
|
+
type AttachmentMeta = { key: string; alt: string | null; title: string | null };
|
|
124
|
+
|
|
125
|
+
const extractKeyFromUrl = (url: string) => url.replace(/^https:\/\/[^\/]+\/+/, '');
|
|
126
|
+
|
|
127
|
+
function getAttachmentMetas(markdown: string): AttachmentMeta[] {
|
|
110
128
|
if (!markdown) {
|
|
111
129
|
return [];
|
|
112
130
|
}
|
|
113
131
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const
|
|
132
|
+
// Minimal image syntax:  or  or 
|
|
133
|
+
// We only track https URLs and only those that look like S3/AWS public URLs.
|
|
134
|
+
const imageRegex = /!\[([^\]]*)\]\(\s*(https:\/\/[^\s)]+)\s*(?:\s+(?:"([^"]*)"|'([^']*)'))?\s*\)/g;
|
|
135
|
+
|
|
136
|
+
const byKey = new Map<string, AttachmentMeta>();
|
|
137
|
+
for (const match of markdown.matchAll(imageRegex)) {
|
|
138
|
+
const altRaw = match[1] ?? '';
|
|
139
|
+
const srcRaw = match[2];
|
|
140
|
+
const titleRaw = (match[3] ?? match[4]) ?? null;
|
|
141
|
+
|
|
142
|
+
const srcNoQuery = srcRaw.split('?')[0];
|
|
143
|
+
if (!srcNoQuery.includes('s3') && !srcNoQuery.includes('amazonaws')) {
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
117
146
|
|
|
118
|
-
|
|
119
|
-
.
|
|
120
|
-
|
|
147
|
+
const key = extractKeyFromUrl(srcNoQuery);
|
|
148
|
+
byKey.set(key, {
|
|
149
|
+
key,
|
|
150
|
+
alt: altRaw,
|
|
151
|
+
title: titleRaw,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
return [...byKey.values()];
|
|
121
155
|
}
|
|
122
156
|
|
|
123
157
|
const createAttachmentRecords = async (
|
|
124
|
-
adminforth: IAdminForth,
|
|
158
|
+
adminforth: IAdminForth,
|
|
159
|
+
options: PluginOptions,
|
|
160
|
+
recordId: any,
|
|
161
|
+
metas: AttachmentMeta[],
|
|
162
|
+
adminUser: AdminUser
|
|
125
163
|
) => {
|
|
126
|
-
|
|
164
|
+
if (!metas.length) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
127
167
|
process.env.HEAVY_DEBUG && console.log('📸 Creating attachment records', JSON.stringify(recordId))
|
|
128
168
|
try {
|
|
129
|
-
await Promise.all(
|
|
130
|
-
console.log('Processing path:', s3Path);
|
|
169
|
+
await Promise.all(metas.map(async (meta) => {
|
|
131
170
|
try {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
171
|
+
const recordToCreate: any = {
|
|
172
|
+
[options.attachments.attachmentFieldName]: meta.key,
|
|
173
|
+
[options.attachments.attachmentRecordIdFieldName]: recordId,
|
|
174
|
+
[options.attachments.attachmentResourceIdFieldName]: resourceConfig.resourceId,
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
if (options.attachments.attachmentTitleFieldName) {
|
|
178
|
+
recordToCreate[options.attachments.attachmentTitleFieldName] = meta.title;
|
|
179
|
+
}
|
|
180
|
+
if (options.attachments.attachmentAltFieldName) {
|
|
181
|
+
recordToCreate[options.attachments.attachmentAltFieldName] = meta.alt;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
await adminforth.createResourceRecord({
|
|
185
|
+
resource: this.attachmentResource,
|
|
186
|
+
record: recordToCreate,
|
|
187
|
+
adminUser,
|
|
188
|
+
});
|
|
144
189
|
} catch (err) {
|
|
145
|
-
console.error('Error creating record for',
|
|
190
|
+
console.error('Error creating record for', meta.key, err);
|
|
146
191
|
}
|
|
147
192
|
}));
|
|
148
193
|
} catch (err) {
|
|
@@ -151,35 +196,95 @@ export default class MarkdownPlugin extends AdminForthPlugin {
|
|
|
151
196
|
}
|
|
152
197
|
|
|
153
198
|
const deleteAttachmentRecords = async (
|
|
154
|
-
adminforth: IAdminForth,
|
|
199
|
+
adminforth: IAdminForth,
|
|
200
|
+
options: PluginOptions,
|
|
201
|
+
recordId: any,
|
|
202
|
+
keys: string[],
|
|
203
|
+
adminUser: AdminUser
|
|
155
204
|
) => {
|
|
156
|
-
if (!
|
|
205
|
+
if (!keys.length) {
|
|
157
206
|
return;
|
|
158
207
|
}
|
|
159
208
|
const attachmentPrimaryKeyField = this.attachmentResource.columns.find(c => c.primaryKey);
|
|
160
|
-
const attachments = await adminforth.resource(options.attachments.attachmentResource).list(
|
|
161
|
-
Filters.
|
|
162
|
-
|
|
209
|
+
const attachments = await adminforth.resource(options.attachments.attachmentResource).list([
|
|
210
|
+
Filters.EQ(options.attachments.attachmentRecordIdFieldName, recordId),
|
|
211
|
+
Filters.EQ(options.attachments.attachmentResourceIdFieldName, resourceConfig.resourceId),
|
|
212
|
+
Filters.IN(options.attachments.attachmentFieldName, keys),
|
|
213
|
+
]);
|
|
214
|
+
|
|
163
215
|
await Promise.all(attachments.map(async (a: any) => {
|
|
164
|
-
await adminforth.deleteResourceRecord(
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
)
|
|
216
|
+
await adminforth.deleteResourceRecord({
|
|
217
|
+
resource: this.attachmentResource,
|
|
218
|
+
recordId: a[attachmentPrimaryKeyField.name],
|
|
219
|
+
adminUser,
|
|
220
|
+
record: a,
|
|
221
|
+
})
|
|
172
222
|
}))
|
|
173
223
|
}
|
|
224
|
+
|
|
225
|
+
const updateAttachmentRecordsMetadata = async (
|
|
226
|
+
adminforth: IAdminForth,
|
|
227
|
+
options: PluginOptions,
|
|
228
|
+
recordId: any,
|
|
229
|
+
metas: AttachmentMeta[],
|
|
230
|
+
adminUser: AdminUser
|
|
231
|
+
) => {
|
|
232
|
+
if (!metas.length) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
if (!options.attachments.attachmentTitleFieldName && !options.attachments.attachmentAltFieldName) {
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const attachmentPrimaryKeyField = this.attachmentResource.columns.find(c => c.primaryKey);
|
|
239
|
+
const metaByKey = new Map(metas.map(m => [m.key, m] as const));
|
|
240
|
+
|
|
241
|
+
const existingAparts = await adminforth.resource(options.attachments.attachmentResource).list([
|
|
242
|
+
Filters.EQ(options.attachments.attachmentRecordIdFieldName, recordId),
|
|
243
|
+
Filters.EQ(options.attachments.attachmentResourceIdFieldName, resourceConfig.resourceId)
|
|
244
|
+
]);
|
|
245
|
+
|
|
246
|
+
await Promise.all(existingAparts.map(async (a: any) => {
|
|
247
|
+
const key = a[options.attachments.attachmentFieldName];
|
|
248
|
+
const meta = metaByKey.get(key);
|
|
249
|
+
if (!meta) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const patch: any = {};
|
|
254
|
+
if (options.attachments.attachmentTitleFieldName) {
|
|
255
|
+
const field = options.attachments.attachmentTitleFieldName;
|
|
256
|
+
if ((a[field] ?? null) !== (meta.title ?? null)) {
|
|
257
|
+
patch[field] = meta.title;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (options.attachments.attachmentAltFieldName) {
|
|
261
|
+
const field = options.attachments.attachmentAltFieldName;
|
|
262
|
+
if ((a[field] ?? null) !== (meta.alt ?? null)) {
|
|
263
|
+
patch[field] = meta.alt;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
if (!Object.keys(patch).length) {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
await adminforth.updateResourceRecord({
|
|
271
|
+
resource: this.attachmentResource,
|
|
272
|
+
recordId: a[attachmentPrimaryKeyField.name],
|
|
273
|
+
record: patch,
|
|
274
|
+
oldRecord: a,
|
|
275
|
+
adminUser,
|
|
276
|
+
});
|
|
277
|
+
}));
|
|
278
|
+
}
|
|
174
279
|
|
|
175
280
|
(resourceConfig.hooks.create.afterSave).push(async ({ record, adminUser }: { record: any, adminUser: AdminUser }) => {
|
|
176
281
|
// find all s3Paths in the html
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
process.env.HEAVY_DEBUG && console.log('📸 Found
|
|
282
|
+
const metas = getAttachmentMetas(record[this.options.fieldName]);
|
|
283
|
+
const keys = metas.map(m => m.key);
|
|
284
|
+
process.env.HEAVY_DEBUG && console.log('📸 Found attachment keys', keys);
|
|
180
285
|
// create attachment records
|
|
181
286
|
await createAttachmentRecords(
|
|
182
|
-
adminforth, this.options, record[editorRecordPkField.name],
|
|
287
|
+
adminforth, this.options, record[editorRecordPkField.name], metas, adminUser);
|
|
183
288
|
|
|
184
289
|
return { ok: true };
|
|
185
290
|
});
|
|
@@ -198,19 +303,29 @@ export default class MarkdownPlugin extends AdminForthPlugin {
|
|
|
198
303
|
Filters.EQ(this.options.attachments.attachmentRecordIdFieldName, recordId),
|
|
199
304
|
Filters.EQ(this.options.attachments.attachmentResourceIdFieldName, resourceConfig.resourceId)
|
|
200
305
|
]);
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
process.env.HEAVY_DEBUG && console.log('📸 Found
|
|
208
|
-
|
|
306
|
+
const existingKeys = existingAparts.map((a: any) => a[this.options.attachments.attachmentFieldName]);
|
|
307
|
+
|
|
308
|
+
const metas = getAttachmentMetas(record[this.options.fieldName]);
|
|
309
|
+
const newKeys = metas.map(m => m.key);
|
|
310
|
+
|
|
311
|
+
process.env.HEAVY_DEBUG && console.log('📸 Existing keys (from db)', existingKeys)
|
|
312
|
+
process.env.HEAVY_DEBUG && console.log('📸 Found new keys (from text)', newKeys);
|
|
313
|
+
|
|
314
|
+
const toDelete = existingKeys.filter(key => !newKeys.includes(key));
|
|
315
|
+
const toAdd = newKeys.filter(key => !existingKeys.includes(key));
|
|
316
|
+
|
|
317
|
+
process.env.HEAVY_DEBUG && console.log('📸 Found keys to delete', toDelete)
|
|
318
|
+
process.env.HEAVY_DEBUG && console.log('📸 Found keys to add', toAdd);
|
|
319
|
+
|
|
320
|
+
const metasToAdd = metas.filter(m => toAdd.includes(m.key));
|
|
209
321
|
await Promise.all([
|
|
210
|
-
deleteAttachmentRecords(adminforth, this.options, toDelete, adminUser),
|
|
211
|
-
createAttachmentRecords(adminforth, this.options, recordId,
|
|
322
|
+
deleteAttachmentRecords(adminforth, this.options, recordId, toDelete, adminUser),
|
|
323
|
+
createAttachmentRecords(adminforth, this.options, recordId, metasToAdd, adminUser)
|
|
212
324
|
]);
|
|
213
325
|
|
|
326
|
+
// Keep alt/title in sync for existing attachments too
|
|
327
|
+
await updateAttachmentRecordsMetadata(adminforth, this.options, recordId, metas, adminUser);
|
|
328
|
+
|
|
214
329
|
return { ok: true };
|
|
215
330
|
|
|
216
331
|
}
|
|
@@ -225,9 +340,9 @@ export default class MarkdownPlugin extends AdminForthPlugin {
|
|
|
225
340
|
Filters.EQ(this.options.attachments.attachmentResourceIdFieldName, resourceConfig.resourceId)
|
|
226
341
|
]
|
|
227
342
|
);
|
|
228
|
-
const
|
|
229
|
-
process.env.HEAVY_DEBUG && console.log('📸 Found
|
|
230
|
-
await deleteAttachmentRecords(adminforth, this.options,
|
|
343
|
+
const existingKeys = existingAparts.map((a: any) => a[this.options.attachments.attachmentFieldName]);
|
|
344
|
+
process.env.HEAVY_DEBUG && console.log('📸 Found keys to delete', existingKeys);
|
|
345
|
+
await deleteAttachmentRecords(adminforth, this.options, record[editorRecordPkField.name], existingKeys, adminUser);
|
|
231
346
|
|
|
232
347
|
return { ok: true };
|
|
233
348
|
}
|
package/package.json
CHANGED
package/types.ts
CHANGED
|
@@ -38,5 +38,19 @@ export interface PluginOptions {
|
|
|
38
38
|
* Why we force to store and ask for resource id if we already have record id? Because in amny use cases attachments resource is shared between multiple resources, and record id might be not be unique across resources, but resource id + record id will be always unique.
|
|
39
39
|
*/
|
|
40
40
|
attachmentResourceIdFieldName: string; // e.g. 'apartment_resource_id',
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Optional: field name in attachment resource where title of image will be stored.
|
|
44
|
+
* When in markdown title of image is mentioned e.g. , it will be parsed and stored in attachment resource.
|
|
45
|
+
* If you will update title in markdown, it will be updated in attachment resource as well.
|
|
46
|
+
*/
|
|
47
|
+
attachmentTitleFieldName?: string; // e.g. 'title',
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Optional: field name in attachment resource where alt of image will be stored.
|
|
51
|
+
* When in markdown alt of image is mentioned e.g. , it will be parsed and stored in attachment resource.
|
|
52
|
+
* If you will update alt in markdown, it will be updated in attachment resource as well.
|
|
53
|
+
*/
|
|
54
|
+
attachmentAltFieldName?: string; // e.g. 'alt',
|
|
41
55
|
},
|
|
42
56
|
}
|