domma-cms 1.6.37 → 1.6.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/admin/dist/domma/domma-tools.css +3 -3
  2. package/admin/dist/domma/domma-tools.min.js +3 -3
  3. package/package.json +2 -2
  4. package/plugins/blog/CLAUDE.md +11 -1
  5. package/plugins/blog/lib/cache.js +43 -0
  6. package/plugins/blog/lib/page.js +28 -9
  7. package/plugins/blog/plugin.js +14 -13
  8. package/plugins/blog/plugin.json +1 -1
  9. package/plugins/blog/plugin.public.js +1 -1
  10. package/plugins/blog/tests/cache.test.js +65 -0
  11. package/plugins/booking/CLAUDE.md +14 -2
  12. package/plugins/booking/admin/lib/diary-layout.js +18 -0
  13. package/plugins/booking/admin/views/diary.js +153 -152
  14. package/plugins/booking/admin/views/people.js +64 -66
  15. package/plugins/booking/admin/views/settings.js +31 -32
  16. package/plugins/booking/plugin.json +1 -1
  17. package/plugins/booking/tests/admin-diary.test.js +9 -1
  18. package/plugins/feedback/CLAUDE.md +55 -0
  19. package/plugins/feedback/admin/css/index.css +16 -1
  20. package/plugins/feedback/admin/lib/kit.js +106 -0
  21. package/plugins/feedback/admin/lib/settings.js +107 -0
  22. package/plugins/feedback/admin/views/feedback.js +4 -1
  23. package/plugins/feedback/admin/views/inbox.js +11 -2
  24. package/plugins/feedback/admin/views/send.js +74 -7
  25. package/plugins/feedback/docs/guide.md +9 -9
  26. package/plugins/feedback/lib/attachments.js +133 -0
  27. package/plugins/feedback/lib/cache.js +43 -0
  28. package/plugins/feedback/lib/receiver.js +36 -6
  29. package/plugins/feedback/lib/sender.js +70 -6
  30. package/plugins/feedback/lib/shape.js +111 -6
  31. package/plugins/feedback/plugin.js +171 -17
  32. package/plugins/feedback/plugin.json +2 -2
  33. package/plugins/feedback/tests/api.test.js +125 -3
  34. package/plugins/feedback/tests/settings.test.js +59 -0
  35. package/plugins/feedback/tests/shape.test.js +64 -0
  36. package/plugins/free-tier.lock.json +73 -61
  37. package/plugins/mail-reader/CLAUDE.md +15 -4
  38. package/plugins/mail-reader/plugin.json +1 -1
  39. package/plugins/shopping-cart/CLAUDE.md +28 -2
  40. package/plugins/shopping-cart/admin/css/index.css +3 -0
  41. package/plugins/shopping-cart/admin/views/orders.js +42 -10
  42. package/plugins/shopping-cart/admin/views/shop.js +1 -1
  43. package/plugins/shopping-cart/docs/guide.md +7 -3
  44. package/plugins/shopping-cart/lib/payments.js +15 -2
  45. package/plugins/shopping-cart/lib/refunds.js +51 -0
  46. package/plugins/shopping-cart/lib/sales.js +15 -8
  47. package/plugins/shopping-cart/lib/shop.js +38 -3
  48. package/plugins/shopping-cart/plugin.js +47 -19
  49. package/plugins/shopping-cart/plugin.json +2 -2
  50. package/plugins/shopping-cart/tests/api.test.js +52 -0
  51. package/plugins/shopping-cart/tests/commission.test.js +1 -1
  52. package/plugins/shopping-cart/tests/refunds.test.js +42 -0
  53. package/plugins/support/CLAUDE.md +51 -0
  54. package/plugins/support/admin/css/index.css +16 -1
  55. package/plugins/support/admin/lib/kit.js +192 -8
  56. package/plugins/support/admin/lib/settings.js +123 -0
  57. package/plugins/support/admin/views/desk.js +55 -12
  58. package/plugins/support/admin/views/inbox.js +17 -4
  59. package/plugins/support/admin/views/support.js +4 -1
  60. package/plugins/support/docs/guide.md +7 -9
  61. package/plugins/support/lib/attachments.js +133 -0
  62. package/plugins/support/lib/cache.js +43 -0
  63. package/plugins/support/lib/receiver.js +45 -10
  64. package/plugins/support/lib/sender.js +81 -10
  65. package/plugins/support/lib/shape.js +153 -12
  66. package/plugins/support/plugin.js +159 -29
  67. package/plugins/support/plugin.json +2 -2
  68. package/plugins/support/tests/api.test.js +125 -3
  69. package/plugins/support/tests/settings.test.js +68 -0
  70. package/plugins/support/tests/shape.test.js +51 -1
@@ -18,7 +18,7 @@ test('a request needs a subject and a message; unknown choices fall back', () =>
18
18
  assert.equal(cleanRequest({body: 'x'}).error, 'Give it a short subject.');
19
19
  assert.equal(cleanRequest({subject: 'x'}).error, 'Tell us what you need.');
20
20
  const {request} = cleanRequest({subject: ' Site\n down ', body: ' help\r\nplease ', category: 'nope', priority: 'panic', where: '/shop'});
21
- assert.deepEqual(request, {category: 'help', priority: 'normal', subject: 'Site down', body: 'help\nplease', where: '/shop'});
21
+ assert.deepEqual(request, {category: 'help', priority: 'normal', subject: 'Site down', body: 'help\nplease', where: '/shop', attachments: []});
22
22
  assert.equal(cleanRequest({subject: 's', body: 'b', priority: 'urgent'}).request.priority, 'urgent');
23
23
  });
24
24
 
@@ -187,3 +187,53 @@ test('review: a request removed while a send is in flight is not half-deleted, a
187
187
  assert.ok(!posted.includes('T3') && !posted.includes('M3'));
188
188
  } finally { fs.rmSync(dir, {recursive: true, force: true}); }
189
189
  });
190
+
191
+ // ---- Attachments (1.1.4) ----
192
+
193
+ import {ATTACHMENTS, attachmentsOf, cleanAttachment, cleanAttachments, cleanMessage} from '../lib/shape.js';
194
+
195
+ const FID = '0f8fad5b-d9cb-469f-a165-70867728950e';
196
+ const file = (n = 1, extra = {}) => ({id: `${FID.slice(0, -1)}${n}`, name: `shot${n}.png`, type: 'image/png', size: 10, ...extra});
197
+
198
+ test('an attachment: a known type, a server-made id, a size within the cap', () => {
199
+ assert.deepEqual(cleanAttachment(file(1, {junk: 1})), {id: file(1).id, name: 'shot1.png', type: 'image/png', size: 10});
200
+ assert.equal(cleanAttachment(file(1, {type: 'image/svg+xml'})), null, 'SVG can carry script');
201
+ assert.equal(cleanAttachment(file(1, {type: 'text/html'})), null);
202
+ assert.equal(cleanAttachment(file(1, {id: '../../x'})), null);
203
+ assert.equal(cleanAttachment(file(1, {size: ATTACHMENTS.maxBytes + 1})), null);
204
+ assert.equal(cleanAttachment(file(1, {name: '/etc/"passwd'})).name, '_passwd');
205
+ assert.equal(cleanAttachments([file(1), file(2), file(3), file(4), file(1)]).length, 3, 'three at most, each once');
206
+ });
207
+
208
+ test('files ride on a message: announced not stored, carried by a follow-up, shown to the site', () => {
209
+ const {request} = cleanRequest({subject: 's', body: 'b', attachments: [file(1), {id: 'nope'}]});
210
+ assert.equal(request.attachments.length, 1);
211
+ const {data} = receivedTicket(buildTicketPayload({request, user: {name: 'Ann'}, ref: 'r1'}), 'acme', T0);
212
+ assert.equal(data.messages[0].attachments[0].stored, false);
213
+ assert.equal(cleanMessage({body: '', attachments: [file(2)]}).error, 'Write a message first.', 'files go with words');
214
+ const msg = buildMessagePayload({ticketRef: 'r1', ref: 'm1', body: 'and this', attachments: [file(2)]});
215
+ assert.equal(msg.attachments.length, 1);
216
+ assert.equal(buildMessagePayload({ticketRef: 'r1', ref: 'm2', close: true, attachments: [file(3)]}).attachments, undefined, 'a close carries none');
217
+ const next = addTurn(data, {from: 'customer', name: 'Ann', body: 'and this', id: 'm1', at: T1, attachments: msg.attachments}).data;
218
+ assert.deepEqual(attachmentsOf(next).map(a => [a.name, a.messageId, a.stored]), [['shot1.png', 'r1', false], ['shot2.png', 'm1', false]]);
219
+ const view = senderView({id: 'e1', data: next});
220
+ assert.deepEqual(view.messages[1].attachments, [{id: file(2).id, name: 'shot2.png', type: 'image/png', size: 10, stored: false}]);
221
+ assert.equal(view.messages[0].attachments[0].messageId, undefined);
222
+ });
223
+
224
+ test('a sent message stays in the outbox until its files have gone', () => {
225
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'sp-files-'));
226
+ try {
227
+ const ob = createOutbox(dir);
228
+ ob.add('ticket', {ref: 'r1', subject: 's', body: 'b', attachments: [file(1)]});
229
+ ob.mark('r1', {state: 'sent'});
230
+ const remote = [{ref: 'r1', subject: 's', status: 'open', messages: [{id: 'r1', from: 'customer', body: 'b', at: T0}]}];
231
+ ob.remember(remote);
232
+ assert.equal(ob.all().length, 1, 'kept: its file has not gone');
233
+ assert.ok(ob.fileIds().has(file(1).id));
234
+ ob.fileSent('r1', file(1).id);
235
+ ob.remember(remote);
236
+ assert.equal(ob.all().length, 0, 'now done with');
237
+ assert.ok(ob.fileIds().has(file(1).id), 'the file itself is still wanted: the thread shows it');
238
+ } finally { fs.rmSync(dir, {recursive: true, force: true}); }
239
+ });