UNPKG

9.03 kBJavaScriptView Raw
1var _ = require('lodash');
2var async = require('async');
3
4module.exports = blocks;
5
6function blocks(options, callback) {
7 return new blocks.Blocks(options, callback);
8}
9
10function Blocks(options, callback) {
11 var self = this;
12 self._app = options.app;
13 self._apos = options.apos;
14 self._pages = options.pages;
15 self._types = options.types;
16 self._action = '/apos-blocks';
17
18 // Mix in the ability to serve assets and templates
19 self._apos.mixinModuleAssets(self, 'blocks', __dirname, options);
20
21 self.pushAsset('script', 'editor', { when: 'user' });
22 self.pushAsset('stylesheet', 'editor', { when: 'user' });
23
24 // Typical syntax: aposBlocks(page, 'groupname', [ 'typeone', 'typetwo'])
25
26 // Also allowed: aposBlocks(page, 'groupname', { types: [ 'typeone', 'typetwo' ] })
27
28 // And if you must: aposBlocks({ page: page, group: 'groupname', types: [ 'typeone', 'typetwo'] })
29
30 // You may pass additional data which is visible to the blocks.html template:
31 //
32 // aposBlocks(page, 'groupname', [ 'typeone', 'typetwo'], { user: user, permissions: permissions })
33 //
34 // Newly added blocks do NOT see such additional data, however they DO see "user" and "permissions"
35 // because these are explicitly passed to new blocks. Currently newly added blocks don't see any
36 // joins or custom page loader results in the page object either.
37
38 self._apos.addLocal('aposBlocks', function() {
39 var options = {};
40 if (!arguments.length) {
41 throw new Error('You must pass at least one argument to aposBlocks. Typical usage is aposBlocks(page, "body", [ "blockTypeOne", "blockTypeTwo"]).');
42 }
43 if (arguments.length === 1) {
44 options = arguments[0];
45 } else {
46 options.page = arguments[0];
47 options.group = arguments[1];
48 if (arguments[2]) {
49 if (Array.isArray(arguments[2])) {
50 options.types = arguments[2];
51 if (arguments[3]) {
52 _.merge(options, arguments[3]);
53 }
54 } else {
55 _.merge(options, arguments[2]);
56 }
57 }
58 }
59 var data = {};
60 _.merge(data, options);
61 data.blocks = (options.page.blockGroups && options.page.blockGroups[options.group] && options.page.blockGroups[options.group].blocks) || [];
62 data.types = _.filter(self._types, function(type) {
63 return _.contains(options.types, type.name);
64 });
65 return self.render('blocks', data);
66 });
67
68 self._app.post(self._action + '/new', function(req, res) {
69 var page;
70 var result;
71 var group = self._apos.sanitizeString(req.body.group);
72 var type = self._apos.sanitizeString(req.body.type);
73 var id = self._apos.generateId();
74 var slug = self._apos.sanitizeString(req.body.slug);
75 if (!type) {
76 return res.send({ status: 'invalid' });
77 }
78 var typeConfig = _.find(self._types, function(typeConfig) {
79 return typeConfig.name === type;
80 });
81 if (!typeConfig) {
82 return res.send({ status: 'nosuchblock' });
83 }
84 return async.series({
85 get: function(callback) {
86 return self._apos.getPage(req, slug, {}, function(err, _page) {
87 if (err) {
88 console.log(err);
89 return callback('error');
90 }
91 if (!_page) {
92 return callback('notfound');
93 }
94 page = _page;
95 return callback(null);
96 });
97 },
98 render: function(callback) {
99 result = {
100 status: 'ok',
101 type: type,
102 id: id,
103 html: self.render(type, {
104 page: page,
105 type: type,
106 group: group,
107 // Make basic user and permissions info available to ease the pain of
108 // not having proper page loaders running for new blocks yet
109 user: req.user,
110 permissions: (req.user && req.user.permissions) || {},
111 id: id,
112 prefix: group + '_' + id + '_'
113 }, req)
114 };
115 return callback(null);
116 },
117 insert: function(callback) {
118 if (!page.blockGroups) {
119 page.blockGroups = {};
120 }
121 if (!page.blockGroups[group]) {
122 page.blockGroups[group] = {
123 blocks: []
124 };
125 }
126 page.blockGroups[group].blocks.push({
127 type: type,
128 id: id
129 });
130 return self._apos.putPage(req, slug, page, callback);
131 }
132 }, function(err) {
133 if (err) {
134 return res.send({ status: err });
135 }
136 return res.send(result);
137 });
138 });
139
140 self._app.post(self._action + '/switch', function(req, res) {
141 var page;
142 var result;
143 var group = self._apos.sanitizeString(req.body.group);
144 var type = self._apos.sanitizeString(req.body.type);
145 var id = self._apos.sanitizeId(req.body.id);
146 var slug = self._apos.sanitizeString(req.body.slug);
147 if (!type) {
148 return res.send({ status: 'invalid' });
149 }
150 var typeConfig = _.find(self._types, function(typeConfig) {
151 return typeConfig.name === type;
152 });
153 if (!typeConfig) {
154 return res.send({ status: 'nosuchblock' });
155 }
156 return async.series({
157 get: function(callback) {
158 return self._apos.getPage(req, slug, {}, function(err, _page) {
159 if (err) {
160 console.log(err);
161 return callback('error');
162 }
163 if (!_page) {
164 return callback('notfound');
165 }
166 page = _page;
167 return callback(null);
168 });
169 },
170 render: function(callback) {
171 result = {
172 status: 'ok',
173 type: type,
174 id: id,
175 html: self.render(type, {
176 page: page,
177 type: type,
178 group: group,
179 id: id,
180 prefix: group + '_' + id + '_'
181 }, req)
182 };
183 return callback(null);
184 },
185 update: function(callback) {
186 _.each(page.blockGroups[group].blocks, function(block) {
187 if (block.id === id) {
188 block.type = type;
189 }
190 });
191 return self._apos.putPage(req, slug, page, callback);
192 }
193 }, function(err) {
194 if (err) {
195 return res.send({ status: err });
196 }
197 return res.send(result);
198 });
199 });
200
201 self._app.post(self._action + '/remove', function(req, res) {
202 var group = self._apos.sanitizeString(req.body.group);
203 var id = self._apos.sanitizeId(req.body.id);
204 var slug = self._apos.sanitizeString(req.body.slug);
205 return async.series({
206 get: function(callback) {
207 return self._apos.getPage(req, slug, {}, function(err, _page) {
208 if (err) {
209 console.log(err);
210 return callback('error');
211 }
212 if (!_page) {
213 return callback('notfound');
214 }
215 page = _page;
216 return callback(null);
217 });
218 },
219 remove: function(callback) {
220 if (!page.blockGroups) {
221 return callback('notfound');
222 }
223 if (!page.blockGroups[group]) {
224 return callback('notfound');
225 }
226 // Keep only the blocks that DON'T match the id
227 page.blockGroups[group].blocks = _.filter(page.blockGroups[group].blocks, function(block) {
228 return (block.id !== id);
229 });
230 return self._apos.putPage(req, slug, page, callback);
231 }
232 }, function(err) {
233 if (err) {
234 return res.send({ status: err });
235 }
236 return res.send({ status: 'ok' });
237 });
238 });
239
240 self._app.post(self._action + '/order', function(req, res) {
241 var group = self._apos.sanitizeString(req.body.group);
242 var ids = self._apos.sanitizeIds(req.body.ids);
243 var slug = self._apos.sanitizeString(req.body.slug);
244 return async.series({
245 get: function(callback) {
246 return self._apos.getPage(req, slug, {}, function(err, _page) {
247 if (err) {
248 console.log(err);
249 return callback('error');
250 }
251 if (!_page) {
252 return callback('notfound');
253 }
254 page = _page;
255 return callback(null);
256 });
257 },
258 update: function(callback) {
259 if (!page.blockGroups) {
260 return callback('notfound');
261 }
262 if (!page.blockGroups[group]) {
263 return callback('notfound');
264 }
265 page.blockGroups[group].blocks = self._apos.orderById(ids, page.blockGroups[group].blocks, 'id');
266 return self._apos.putPage(req, slug, page, callback);
267 }
268 }, function(err) {
269 if (err) {
270 return res.send({ status: err });
271 }
272 return res.send({ status: 'ok' });
273 });
274 });
275
276 // Allows the "page versions" dialog to say something about
277 // blocks coming and going
278 self._apos.on('diff', function(page, lines) {
279 _.each(page.blockGroups || {}, function(value, group) {
280 var prefix = group + ' block: ';
281 _.each(value.blocks, function(block) {
282 lines.push(prefix + block.type);
283 });
284 });
285 });
286
287 return process.nextTick(function() {
288 return callback(null);
289 });
290}
291
292blocks.Blocks = Blocks;