diff --git a/examples/testimonials/modules/@apostrophecms/form/index.js b/examples/testimonials/modules/@apostrophecms/form/index.js new file mode 100644 index 0000000..743199a --- /dev/null +++ b/examples/testimonials/modules/@apostrophecms/form/index.js @@ -0,0 +1,13 @@ +// Extends form object to capture submission events and +// automagically create other piece types +module.exports = { + handlers (self) { + return { + submission: { + async createTestimonial (req, form, data) { + self.apos.modules.testimonial.addTestimony(req, data) + } + } + } + } +} \ No newline at end of file diff --git a/examples/testimonials/modules/testimonial/index.js b/examples/testimonials/modules/testimonial/index.js new file mode 100644 index 0000000..2c1bc7e --- /dev/null +++ b/examples/testimonials/modules/testimonial/index.js @@ -0,0 +1,180 @@ +// modules/article/index.js +module.exports = { + extend: "@apostrophecms/piece-type", + options: { + label: "Testimonial", + pluralLabel: "Testimonials", + }, + fields: { + add: { + names: { + label: "Names", + type: "string", + }, + emailaddress: { + label: "Email address", + type: "string", + }, + phonenumber: { + label: "Phone Number", + type: "string", + }, + testimonial: { + label: "Testimonial", + type: "string", + }, + helpneeded: { + label: "Help needed", + type: "string", + }, + photo: { + // Photos are not uploaded via the website form to avoid upload issues, but can be added later + label: "Photo", + type: "area", + options: { + max: 1, + widgets: { + "@apostrophecms/image": {}, + }, + }, + required: false, + }, + active: { + label: "Active", + type: "boolean", + help: "Active testimonials can be viewed on the public website", + }, + }, + group: { + basics: { + label: "Basics", + fields: [ + "title", + "names", + "emailaddress", + "phonenumber", + "testimonial", + "helpneeded", + ], + }, + advanced: { + label: "Photo", + fields: ["photo"], + }, + utility: { + fields: ["active"], + }, + }, + }, + columns: { + add: { + names: { + label: "Names", + }, + emailaddress: { + label: "Email", + }, + phonenumber: { + label: "Phone", + }, + active: { + label: "Active", + }, + }, + }, + filters: { + add: { + active: { + label: "Active", + inputType: "select", + def: false, + }, + }, + }, + queries(self, query) { + builders: { + return { + builders: { + active: { + // This is our filter to be able to see active or inactive in the manager + def: null, + safeFor: "public", + finalize() { + const active = query.get("active"); + if (active === null) { + return; + } + + if (active) { + query.and({ + active: true, + }); + } else { + query.and({ + active: false, + }); + } + }, + launder(value) { + return self.apos.launder.booleanOrNull(value); + }, + choices() { + return [ + { + value: true, + label: "Active", + }, + { + value: false, + label: "NOT active", + }, + { + value: null, + label: "All items", + }, + ]; + }, + }, + }, + }; + } + }, + handlers(self, options) {}, + components(self) { + return { + // Returning the five most recently created testimonies. + async latest(req, data) { + const articles = await self + .find(req) + .active(true) + .sort({ createdAt: -1 }) + .limit(data.max || 5) + .toArray(); + return { + articles, + }; + }, + }; + }, + methods(self) { + return { + async addTestimony(req, initialInfo) { + // Generate a blank testimony data object. + let newTestimony = self.newInstance(); + // Add our initial information to the object. + newTestimony = { + ...newTestimony, + ...initialInfo, + }; + // Assign some arbitrary title to this piece + newTestimony.title = initialInfo["names"]; + // Set "active" to false - testimonies must be approved before uploading + newTestimony.active = false; + Object.assign(newTestimony, { ...initialInfo }); + // Insert the testimonial with the asynchronous `self.insert` method + const insertResult = await self.insert(req, newTestimony); + return insertResult; + }, + }; + }, +}; diff --git a/examples/testimonials/modules/testimonial/views/latest.html b/examples/testimonials/modules/testimonial/views/latest.html new file mode 100644 index 0000000..7165d2d --- /dev/null +++ b/examples/testimonials/modules/testimonial/views/latest.html @@ -0,0 +1,15 @@ +
+
+ {% set cls = cycler("", "bg-gray-200") %} {% for testimony in + data.testimonials %} +
+
+ {{ testimony.createdAt | date('D MMM') }}: {{ testimony.title }} +
+

{{ testimony.testimonial }}

+
+ {% endfor %} +
+
diff --git a/examples/testimonials/readme.md b/examples/testimonials/readme.md new file mode 100644 index 0000000..0ad8176 --- /dev/null +++ b/examples/testimonials/readme.md @@ -0,0 +1,15 @@ +# Forms example - Testimonial +This is an example of using a form submission to automatically create a piece type in your Apostrophe 3 project. +## Installation +* Copy the `modules/@apostrophe/form` folder into your project +* Copy the `modules/testimonial` folder into your project +* Add the testimonial module to app.js as `testimonial: {},` +## Extending the form module +* `form/index.js` adds the async function `createTestimonial`, which is called on form submission. This calls the `testimonial.addTestimony()` function to create our new testimonial piece. +## The testimonial piece +### `index.js` +The fields `names`, `emailaddress`, `phonenumber`, `testimonial` and `helpneeded` need to be matched by fields in the created form. When the form is submitted, that data is passed to `addTestimony`, these fields build our new piece. There are two extra fields in the `testimonial` piece type: +* `photo` - we want to be able to add a photograph of the person submitting the testimonial, but we don't want to expose our site to file uploads on this first implementation. +* `active` - this is a flag that defaults to false when the testimonial is created. When approved by an editor or admin, changing this flag to true will allow the testimonial to appear in the display component. + +Finally in this piece, we have the `latest` component which returns a list of the active testimonials. \ No newline at end of file