New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

apostrophe-blog

Package Overview
Dependencies
Maintainers
1
Versions
75
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

apostrophe-blog - npm Package Compare versions

Comparing version 0.0.2 to 0.0.3

LICENSE

378

index.js
var async = require('async');
var _ = require('underscore');
var extend = require('extend');
var snippets = require('apostrophe-snippets');
var util = require('util');
var moment = require('moment');
module.exports = function(options, callback) {
return new Blog(options, callback);
};
// Creating an instance of the blog module is easy:
// var blog = require('apostrophe-blog')(options, callback);
//
// If you want to access the constructor function for use in the
// constructor of a module that extends this one, consider:
//
// var blog = require('apostrophe-blog');
// ... Inside the constructor for the new object ...
// blog.Blog.call(this, options, null);
//
// In fact, this module does exactly that to extend the snippets module
// (see below). Something similar happens on the browser side in
// main.js.
function Blog(options, callback) {
var apos = options.apos;
var pages = options.pages;
var app = options.app;
var aposBlog = self = this;
module.exports = blog;
// For visibility in other scopes
aposBlog.options = options;
function blog(options, callback) {
return new blog.Blog(options, callback);
}
// Make sure that aposScripts and aposStylesheets summon our
// browser-side UI assets for managing pages
apos.scripts.push('/apos-blog/js/blog.js');
apos.stylesheets.push('/apos-blog/css/blog.css');
apos.templates.push(__dirname + '/views/newPost');
apos.templates.push(__dirname + '/views/editPost');
apos.templates.push(__dirname + '/views/editPosts');
apos.templates.push(__dirname + '/views/pageSettings');
pages.addGroup('blog', {
settings: {
sanitize: function(data, callback) {
var ok = {};
ok.tags = apos.sanitizeTags(data.tags);
return callback(null, ok);
}
}
blog.Blog = function(options, callback) {
var self = this;
_.defaults(options, {
instance: 'blogPost',
name: options.name || 'blog',
label: options.name || 'Blog',
// Overridden separately so that one can have types that just
// override the templates and don't mess with replacing
// all of the javascript and CSS
webAssetDir: __dirname + '/public',
// The default would be aposBlogPostMenu, this is more natural
menuName: 'aposBlogMenu'
});
app.post('/apos-blog/new', function(req, res) {
var post;
var title;
var content;
var slug;
var tags;
// Find our templates before the snippet templates (a chain of overrides)
options.dirs = (options.dirs || []).concat([ __dirname ]);
title = req.body.title.trim();
// Validation is annoying, automatic cleanup is awesome
if (!title.length) {
title = 'New Post';
}
slug = apos.slugify(title);
// Call the base class constructor. Don't pass the callback, we want to invoke it
// ourselves after constructing more stuff
snippets.Snippets.call(this, options, null);
content = JSON.parse(req.body.content);
apos.sanitizeItems(content);
// The snippet dispatcher is almost perfect for our needs, except that
// we expect the publication date of the blog post to appear before the slug
// of the blog post in the URL. So spot that situation, change req.remainder
// to just the slug of the blog post, and invoke the original version of
// "dispatch."
tags = req.body.tags;
// Grab the "superclass" version of the dispatch method so we can call it
var superDispatch = self.dispatch;
async.series([ permissions, insertPost ], sendPost);
function permissions(callback) {
return apos.permissions(req, 'edit-post', null, function(err) {
// If there is no permissions error then we are cool
// enough to create a post
return callback(err);
});
}
function insertPost(callback) {
post = { title: title, type: 'blogPost', tags: tags, areas: { body: { items: content } }, slug: slug, createdAt: new Date(), publishedAt: new Date() };
apos.putPage(slug, post, callback);
}
function sendPost(err) {
if (err) {
res.statusCode = 500;
return res.send('error');
self.dispatch = function(req, callback) {
if (req.remainder.length) {
var matches = req.remainder.match(/^\/\d+\/\d+\/\d+\/(.*)$/);
if (matches) {
req.remainder = '/' + matches[1];
}
return res.send(JSON.stringify(post));
}
});
superDispatch.call(this, req, callback);
};
app.post('/apos-blog/edit', function(req, res) {
var post;
var title;
var content;
var originalSlug;
var slug;
var tags;
title = req.body.title.trim();
// Validation is annoying, automatic cleanup is awesome
if (!title.length) {
title = 'Updated Post';
}
tags = req.body.tags;
var originalSlug = req.body.originalSlug;
slug = apos.slugify(req.body.slug);
if (!slug.length) {
slug = originalSlug;
}
content = JSON.parse(req.body.content);
apos.sanitizeItems(content);
async.series([ getPost, permissions, updatePost, redirect ], sendPost);
function getPost(callback) {
apos.getPage(originalSlug, function(err, page) {
if (err) {
return callback(err);
}
if (!page) {
return callback('No such blog post');
}
if (page.type !== 'blogPost') {
return callback('Not a blog post');
}
post = page;
return callback(null);
});
}
function permissions(callback) {
return apos.permissions(req, 'edit-post', post, function(err) {
// If there is no permissions error then we are cool
// enough to create a post
return callback(err);
});
}
function updatePost(callback) {
post.title = title;
post.slug = slug;
post.tags = tags;
post.areas = { body: { items: content } };
apos.putPage(originalSlug, post, callback);
}
function redirect(callback) {
apos.updateRedirect(originalSlug, slug, callback);
}
function sendPost(err) {
if (err) {
res.statusCode = 500;
return res.send('error');
}
return res.send(JSON.stringify(post));
}
});
app.post('/apos-blog/delete', function(req, res) {
async.series([ getPost, permissions, deletePost], respond);
var slug;
var post;
function getPost(callback) {
slug = req.body.slug;
return apos.getPage(slug, function(err, postArg) {
post = postArg;
if(!post) {
return callback('Not Found');
}
if (post.type !== 'blogPost') {
return callback('Not a blog post');
}
return callback(err);
});
}
function permissions(callback) {
return apos.permissions(req, 'delete-post', post, function(err) {
// If there is no permissions error then we are cool
// enough to delete the post
return callback(err);
});
}
function deletePost(callback) {
apos.pages.remove({slug: post.slug}, callback);
}
function respond(err) {
if (err) {
return res.send(JSON.stringify({
status: err
}));
}
return res.send(JSON.stringify({
status: 'ok'
}));
}
});
app.get('/apos-blog/get-posts', function(req, res) {
self.getPosts(req, req.query, function(err, posts) {
return res.send(JSON.stringify(posts));
});
});
app.get('/apos-blog/get-post', function(req, res) {
self.getPosts(req, req.query, function(err, posts) {
if (posts && posts.length) {
res.send(JSON.stringify(posts[0]));
} else {
res.send(JSON.stringify(null));
}
});
});
// Serve our assets. This is the final route so it doesn't
// beat out the rest
app.get('/apos-blog/*', apos.static(__dirname + '/public'));
apos.addLocal('aposEditBlog', function(options) {
return apos.partial('editBlog.html', options, __dirname + '/views');
});
// Returns recent posts the current user is permitted to read, in
// blog order (reverse chrono). If criteria.editable is true, only
// posts this user can edit are returned. All other properties of
// criteria are merged with the MongoDB criteria object used to
// select the relevant posts.
self.getPosts = function(req, criteriaArg, callback) {
if (!callback) {
callback = options;
options = {};
}
var criteria = extend({}, criteriaArg);
var editable = criteria.editable;
if (criteria.editable !== undefined) {
delete criteria['editable'];
}
criteria.type = 'blogPost';
if (req.query.slug) {
criteria.slug = req.query.slug;
}
apos.pages.find(criteria).sort({ publishedAt: -1 }).toArray(function(err, posts) {
if (err) {
return callback(err);
}
async.filter(posts, function(post, callback) {
apos.permissions(req, editable ? 'edit-post' : 'view-post', post, function(err) {
return callback(!err);
});
}, function(posts) {
return callback(null, posts);
});
});
self.getDefaultTitle = function() {
return 'My Article';
};
// This is a loader function, for use with the `load` option of
// the pages module's `serve` method.
// Returns a "permalink" URL to the snippet, beginning with the
// slug of the specified page. See findBestPage for a good way to
// choose a page beneath which to link this snippet.
//
// If the page type does not begin with "blog," this loader does nothing.
//
// Otherwise, if the page type begins with "blog" and the page matches the URL
// exactly, this function serves up the "main index" page of the blog (a list of
// posts in blog order).
//
// If the page is an inexact match, this function looks at the remainder of the
// URL to decide what to do. If the remainder is of the form
// /YYYY/MM/DD/slug, then the blog post with that slug is served (a blog
// post permalink page).
//
// TODO: make this easier to extend for use in other modules like the
// events module. Pay attention to attributes of the page to decide which
// blog posts are relevant (like categories for blog pages in A1.5). Move
// the default versions of the blog macros and templates into this module, with ways
// to override them.
// It is commonplace to override this function. For instance,
// blog posts add the publication date to the URL.
self.loader = function(req, callback) {
async.series([permissions, blog], callback);
function permissions(callback) {
// Load additional permissions so we know whether this person is a blogger etc.
apos.permissions(req, 'blogger', null, function(err) {
req.extras.blogger = !err;
return callback(null);
});
}
function blog(callback) {
if (!req.bestPage) {
return callback(null);
}
// If the page type isn't part of the blog group,
// then this is outside our purview
var type = pages.getType(req.bestPage.type);
if (type.group !== 'blog') {
return callback(null);
}
// We consider a partial match to be good enough, depending on the
// remainder of the URL
req.page = req.bestPage;
if (req.remainder.length) {
// Is it a blog post permalink?
var matches = req.remainder.match(/^\/\d+\/\d+\/\d+\/(.*)$/);
if (matches) {
// Yep, change templates and filter to retrieve just that one blog post
req.query.slug = matches[1];
req.type = 'blogPost';
} else {
req.type = 'notfound';
}
} else {
// No additional URL: index page
}
var criteria = {};
if (req.page.blog && req.page.blog.tags.length) {
criteria.tags = { $in: req.page.blog.tags };
}
self.getPosts(req, criteria, function(err, posts) {
if (req.type === 'blogPost') {
if (!posts.length) {
req.type = 'notfound';
} else {
req.extras.post = posts[0];
}
} else {
req.extras.posts = posts;
}
return callback(err);
});
}
self.permalink = function(snippet, page) {
return page.slug + '/' + moment(snippet.publishedAt).format('YYYY/MM/DD') + '/' + snippet.slug;
};

@@ -343,3 +87,3 @@

process.nextTick(function() { return callback(null); });
}
};
{
"name": "apostrophe-blog",
"version": "0.0.2",
"version": "0.0.3",
"description": "Blogging for the Apostrophe content management system",

@@ -25,4 +25,6 @@ "main": "index.js",

"underscore": "~1.4.4",
"extend": "~1.1.3"
"extend": "~1.1.3",
"apostrophe-snippets": "0.0.x",
"moment": "~2.0.0"
}
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc