text: // # Posts API
  // RESTful API for the Post resource
  var Promise         = require('bluebird'),
      _               = require('lodash'),
      dataProvider    = require('../models'),
      canThis         = require('../permissions').canThis,
      errors          = require('../errors'),
      utils           = require('./utils'),
      pipeline        = require('../utils/pipeline'),
  
      docName         = 'posts',
      allowedIncludes = ['created_by', 'updated_by', 'published_by', 'author', 'tags', 'fields', 'next', 'previous'],
      posts;
  
  /**
   * ### Posts API Methods
   *
   * **See:** [API Methods](index.js.html#api%20methods)
   */
  
  posts = {
      /**
       * ## Browse
       * Find a paginated set of posts
       *
       * Will only return published posts unless we have an authenticated user and an alternative status
       * parameter.
       *
       * Will return without static pages unless told otherwise
       *
       *
       * @public
       * @param {{context, page, limit, status, staticPages, tag, featured}} options (optional)
       * @returns {Promise<Posts>} Posts Collection with Meta
       */
      browse: function browse(options) {
          var extraOptions = ['tag', 'author', 'status', 'staticPages', 'featured'],
              permittedOptions = utils.browseDefaultOptions.concat(extraOptions),
              tasks;
  
          /**
           * ### Model Query
           *  Make the call to the Model layer
           * @param {Object} options
           * @returns {Object} options
           */
          function modelQuery(options) {
              return dataProvider.Post.findPage(options);
          }
  
          // Push all of our tasks into a `tasks` array in the correct order
          tasks = [
              utils.validate(docName, {opts: permittedOptions}),
              utils.handlePublicPermissions(docName, 'browse'),
              utils.convertOptions(allowedIncludes),
              modelQuery
          ];
  
          // Pipeline calls each task passing the result of one to be the arguments for the next
          return pipeline(tasks, options);
      },
  
      /**
       * ## Read
       * Find a post, by ID, UUID, or Slug
       *
       * @public
       * @param {Object} options
       * @return {Promise<Post>} Post
       */
      read: function read(options) {
          var attrs = ['id', 'slug', 'status', 'uuid'],
              tasks;
  
          /**
           * ### Model Query
           * Make the call to the Model layer
           * @param {Object} options
           * @returns {Object} options
           */
          function modelQuery(options) {
              return dataProvider.Post.findOne(options.data, _.omit(options, ['data']));
          }
  
          // Push all of our tasks into a `tasks` array in the correct order
          tasks = [
              utils.validate(docName, {attrs: attrs}),
              utils.handlePublicPermissions(docName, 'read'),
              utils.convertOptions(allowedIncludes),
              modelQuery
          ];
  
          // Pipeline calls each task passing the result of one to be the arguments for the next
          return pipeline(tasks, options).then(function formatResponse(result) {
              // @TODO make this a formatResponse task?
              if (result) {
                  return {posts: [result.toJSON(options)]};
              }
  
              return Promise.reject(new errors.NotFoundError('Post not found.'));
          });
      },
  
      /**
       * ## Edit
       * Update properties of a post
       *
       * @public
       * @param {Post} object Post or specific properties to update
       * @param {{id (required), context, include,...}} options
       * @return {Promise(Post)} Edited Post
       */
      edit: function edit(object, options) {
          var tasks;
  
          /**
           * ### Handle Permissions
           * We need to be an authorised user to perform this action
           * @param {Object} options
           * @returns {Object} options
           */
          function handlePermissions(options) {
              return canThis(options.context).edit.post(options.id).then(function permissionGranted() {
                  return options;
              }).catch(function handleError(error) {
                  return errors.handleAPIError(error, 'You do not have permission to edit posts.');
              });
          }
  
          /**
           * ### Model Query
           * Make the call to the Model layer
           * @param {Object} options
           * @returns {Object} options
           */
          function modelQuery(options) {
              return dataProvider.Post.edit(options.data.posts[0], _.omit(options, ['data']));
          }
  
          // Push all of our tasks into a `tasks` array in the correct order
          tasks = [
              utils.validate(docName, {opts: utils.idDefaultOptions}),
              handlePermissions,
              utils.convertOptions(allowedIncludes),
              modelQuery
          ];
  
          // Pipeline calls each task passing the result of one to be the arguments for the next
          return pipeline(tasks, object, options).then(function formatResponse(result) {
              if (result) {
                  var post = result.toJSON(options);
  
                  // If previously was not published and now is (or vice versa), signal the change
                  post.statusChanged = false;
                  if (result.updated('status') !== result.get('status')) {
                      post.statusChanged = true;
                  }
                  return {posts: [post]};
              }
  
              return Promise.reject(new errors.NotFoundError('Post not found.'));
          });
      },
  
      /**
       * ## Add
       * Create a new post along with any tags
       *
       * @public
       * @param {Post} object
       * @param {{context, include,...}} options
       * @return {Promise(Post)} Created Post
       */
      add: function add(object, options) {
          var tasks;
  
          /**
           * ### Handle Permissions
           * We need to be an authorised user to perform this action
           * @param {Object} options
           * @returns {Object} options
           */
          function handlePermissions(options) {
              return canThis(options.context).add.post().then(function permissionGranted() {
                  return options;
              }).catch(function () {
                  return Promise.reject(new errors.NoPermissionError('You do not have permission to add posts.'));
              });
          }
  
          /**
           * ### Model Query
           * Make the call to the Model layer
           * @param {Object} options
           * @returns {Object} options
           */
          function modelQuery(options) {
              return dataProvider.Post.add(options.data.posts[0], _.omit(options, ['data']));
          }
  
          // Push all of our tasks into a `tasks` array in the correct order
          tasks = [
              utils.validate(docName),
              handlePermissions,
              utils.convertOptions(allowedIncludes),
              modelQuery
          ];
  
          // Pipeline calls each task passing the result of one to be the arguments for the next
          return pipeline(tasks, object, options).then(function formatResponse(result) {
              var post = result.toJSON(options);
  
              if (post.status === 'published') {
                  // When creating a new post that is published right now, signal the change
                  post.statusChanged = true;
              }
              return {posts: [post]};
          });
      },
  
      /**
       * ## Destroy
       * Delete a post, cleans up tag relations, but not unused tags
       *
       * @public
       * @param {{id (required), context,...}} options
       * @return {Promise(Post)} Deleted Post
       */
      destroy: function destroy(options) {
          var tasks;
  
          /**
           * ### Handle Permissions
           * We need to be an authorised user to perform this action
           * @param {Object} options
           * @returns {Object} options
           */
          function handlePermissions(options) {
              return canThis(options.context).destroy.post(options.id).then(function permissionGranted() {
                  options.status = 'all';
                  return options;
              }).catch(function handleError(error) {
                  return errors.handleAPIError(error, 'You do not have permission to remove posts.');
              });
          }
  
          /**
           * ### Model Query
           * Make the call to the Model layer
           * @param {Object} options
           * @returns {Object} options
           */
          function modelQuery(options) {
              return posts.read(options).then(function (result) {
                  return dataProvider.Post.destroy(options).then(function () {
                      return result;
                  });
              });
          }
  
          // Push all of our tasks into a `tasks` array in the correct order
          tasks = [
              utils.validate(docName, {opts: utils.idDefaultOptions}),
              handlePermissions,
              utils.convertOptions(allowedIncludes),
              modelQuery
          ];
  
          // Pipeline calls each task passing the result of one to be the arguments for the next
          return pipeline(tasks, options).then(function formatResponse(result) {
              var deletedObj = result;
  
              if (deletedObj.posts) {
                  _.each(deletedObj.posts, function (post) {
                      post.statusChanged = true;
                  });
              }
  
              return deletedObj;
          });
      }
  };
  
  module.exports = posts;
  