UNPKG

apostrophe

Version:

The Apostrophe Content Management System.

290 lines (271 loc) 8.37 kB
// `apostrophe-pieces` provides a "base class" you can extend to create new content // types for your project. Just use the `addFields` option to create a schema and // you'll get a user interface for managing your content for free. Add in the // `apostrophe-pieces-pages` module to display an index page and permalink pages // for your pieces, and use `apostrophe-pieces-widgets` to allow them to be sprinkled // into pages all over the site. To learn more, see: // // [Reusable content with pieces](/core-concepts/reusable-content-pieces/) // // ## Options // // ### `slugPrefix` // // If set this string, which typically should end with `-`, will be prepended // to the slugs of all pieces of this type in order to prevent needless // conflicts with the slugs of other piece types. // // ### `addToListProjection` // // A MongoDB-style projection object indicating which additional properties of a piece will be returned // by the query that populates the list view in the "Manage Pieces" dialog box. This was added // for security reasons. Note that if you are simply using addColumns then this should happen automatically // for you. You would mainly need this option if you are overriding the list view template // altogether and displaying information in a custom way. Negative projections (exclusions) are // not supported. // // ## More Options // // See [reusable content with pieces](/core-concepts/reusable-content-pieces/) // for many additional options. var _ = require('@sailshq/lodash'); module.exports = { extend: 'apostrophe-doc-type-manager', manageViews: [ 'list' ], perPage: 10, listProjection: { _url: 1, title: 1, published: 1, trash: 1, updatedAt: 1, createdAt: 1 }, addToListProjection: {}, afterConstruct: function(self) { self.composeFilters(); self.composeColumns(); self.pushAssets(); self.pushDefineSingleton(); self.createRoutes(); self.addPermissions(); self.addToAdminBar(); self.finalizeControls(); self.addTasks(); }, beforeConstruct: function(self, options) { self.contextual = options.contextual; if (self.contextual) { // If the piece is edited contextually, default the published state to false options.addFields = [ { type: 'boolean', name: 'published', label: 'Published', // Patched later with a final ruling of true or false // if it is not overridden at project level, see // patchPublished method def: 'contextual' } ].concat(options.addFields || []); } options.defaultColumns = options.defaultColumns || [ { name: 'title', label: 'Title', sort: { title: 1 } }, { name: 'updatedAt', label: 'Last Updated', sort: { updatedAt: 1 }, partial: function(value) { if (!value) { // Don't crash if updatedAt is missing, for instance due to a dodgy import process return ''; } return self.partial('manageUpdatedAt.html', { value: value }); } }, { name: 'published', label: 'Published', partial: function(value) { return self.partial('managePublished', { value: value }); } } ]; if (self.contextual) { options.defaultColumns.push({ name: '_url', label: 'Link', partial: function(value) { return self.partial('manageLink', { value: value }); } }); } options.addColumns = options.defaultColumns.concat(options.addColumns || []); options.addFilters = [ { name: 'published', choices: [ { value: true, label: 'Published' }, { value: false, label: 'Draft' }, { value: null, label: 'Both' } ], allowedInChooser: false, def: true, style: 'pill' }, { name: 'trash', choices: [ { value: false, label: 'Live' }, { value: true, label: 'Trash' } ], allowedInChooser: false, def: false, style: 'pill' } ].concat(options.addFilters || []); options.batchOperations = [ { name: 'trash', label: 'Trash', unlessFilter: { trash: true } }, { name: 'rescue', label: 'Rescue', unlessFilter: { trash: false } }, { name: 'publish', label: 'Publish', unlessFilter: { published: true }, requiredField: 'published' }, { name: 'unpublish', label: 'Unpublish', unlessFilter: { published: false }, requiredField: 'published' }, { name: 'tag', label: 'Add Tag to', buttonLabel: 'Add Tag', // The schema *of this piece type* must have a field called tags. // This has nothing to do with the schema for the batch form. -Tom requiredField: 'tags', schema: [ { type: 'tags', name: 'tags', label: 'Tag', required: true } ] }, { name: 'untag', label: 'Remove Tag from', buttonLabel: 'Remove Tag', // The schema *of this piece type* must have a field called tags. // This has nothing to do with the schema for the batch form. -Tom requiredField: 'tags', schema: [ { type: 'tags', name: 'tags', label: 'Tag', required: true } ] }, { // Batch permissions, but only if permissions fields are present name: 'permissions', label: 'Set Permissions for', buttonLabel: 'Permissions', requiredField: 'loginRequired' } ].concat(options.addBatchOperations || []); if (options.removeBatchOperations) { options.batchOperations = _.filter(options.batchOperations, function(batchOperation) { return (!_.contains(options.removeBatchOperations, batchOperation.name)); }); } }, construct: function(self, options) { if (!options.name) { throw new Error('apostrophe-pieces require name option'); } if (!options.label) { // Englishify it options.label = _.startCase(options.name); } options.pluralLabel = options.pluralLabel || options.label + 's'; self.name = options.name; self.label = options.label; self.pluralLabel = options.pluralLabel; self.manageViews = options.manageViews; require('./lib/api.js')(self, options); require('./lib/routes.js')(self, options); require('./lib/browser.js')(self, options); require('./lib/helpers.js')(self, options); require('./lib/tasks.js')(self, options); // As a doc manager, we can provide default templates for use when // choosing docs of our type. With this code in place, subclasses of // pieces can just provide custom chooserChoice.html and chooserChoices.html // templates with no additional plumbing. -Tom self.choiceTemplate = self.__meta.name + ':chooserChoice.html'; self.choicesTemplate = self.__meta.name + ':chooserChoices.html'; self.relationshipTemplate = self.__meta.name + ':relationshipEditor.html'; if (self.options.piecesFilters) { self.apos.utils.warnDev('⚠️ The piecesFilters option was passed to ' + self.__meta.name + ', which\nextends apostrophe-pieces. This option should be passed to the corresponding\nmodule that extends apostrophe-pieces-pages.'); } self.on('apostrophe:modulesReady', 'patchPublished', function() { var published = _.find(self.schema, { name: 'published' }); if (!published) { return; } if (published.def !== 'contextual') { return; } var workflow = self.apos.modules['apostrophe-workflow']; if (workflow && workflow.includeType(self.name)) { published.def = true; } else { published.def = false; } }); } };