UNPKG

9.1 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, groupLabel: 'Lego Block' })
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 _.extend(options, arguments[3]);
53 }
54 } else {
55 _.extend(options, arguments[2]);
56 }
57 }
58 }
59 var data = {};
60 _.extend(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 data.groupLabel = options.groupLabel;
66 return self.render('blocks', data);
67 });
68
69 self._app.post(self._action + '/new', function(req, res) {
70 var page;
71 var result;
72 var group = self._apos.sanitizeString(req.body.group);
73 var type = self._apos.sanitizeString(req.body.type);
74 var id = self._apos.generateId();
75 var slug = self._apos.sanitizeString(req.body.slug);
76 if (!type) {
77 return res.send({ status: 'invalid' });
78 }
79 var typeConfig = _.find(self._types, function(typeConfig) {
80 return typeConfig.name === type;
81 });
82 if (!typeConfig) {
83 return res.send({ status: 'nosuchblock' });
84 }
85 return async.series({
86 get: function(callback) {
87 return self._apos.getPage(req, slug, {}, function(err, _page) {
88 if (err) {
89 console.log(err);
90 return callback('error');
91 }
92 if (!_page) {
93 return callback('notfound');
94 }
95 page = _page;
96 return callback(null);
97 });
98 },
99 render: function(callback) {
100 result = {
101 status: 'ok',
102 type: type,
103 id: id,
104 html: self.render(type, {
105 page: page,
106 type: type,
107 group: group,
108 // Make basic user and permissions info available to ease the pain of
109 // not having proper page loaders running for new blocks yet
110 user: req.user,
111 permissions: (req.user && req.user.permissions) || {},
112 id: id,
113 prefix: group + '_' + id + '_'
114 }, req)
115 };
116 return callback(null);
117 },
118 insert: function(callback) {
119 if (!page.blockGroups) {
120 page.blockGroups = {};
121 }
122 if (!page.blockGroups[group]) {
123 page.blockGroups[group] = {
124 blocks: []
125 };
126 }
127 page.blockGroups[group].blocks.push({
128 type: type,
129 id: id
130 });
131 return self._apos.putPage(req, slug, page, callback);
132 }
133 }, function(err) {
134 if (err) {
135 return res.send({ status: err });
136 }
137 return res.send(result);
138 });
139 });
140
141 self._app.post(self._action + '/switch', function(req, res) {
142 var page;
143 var result;
144 var group = self._apos.sanitizeString(req.body.group);
145 var type = self._apos.sanitizeString(req.body.type);
146 var id = self._apos.sanitizeId(req.body.id);
147 var slug = self._apos.sanitizeString(req.body.slug);
148 if (!type) {
149 return res.send({ status: 'invalid' });
150 }
151 var typeConfig = _.find(self._types, function(typeConfig) {
152 return typeConfig.name === type;
153 });
154 if (!typeConfig) {
155 return res.send({ status: 'nosuchblock' });
156 }
157 return async.series({
158 get: function(callback) {
159 return self._apos.getPage(req, slug, {}, function(err, _page) {
160 if (err) {
161 console.log(err);
162 return callback('error');
163 }
164 if (!_page) {
165 return callback('notfound');
166 }
167 page = _page;
168 return callback(null);
169 });
170 },
171 render: function(callback) {
172 result = {
173 status: 'ok',
174 type: type,
175 id: id,
176 html: self.render(type, {
177 page: page,
178 type: type,
179 group: group,
180 id: id,
181 prefix: group + '_' + id + '_'
182 }, req)
183 };
184 return callback(null);
185 },
186 update: function(callback) {
187 _.each(page.blockGroups[group].blocks, function(block) {
188 if (block.id === id) {
189 block.type = type;
190 }
191 });
192 return self._apos.putPage(req, slug, page, callback);
193 }
194 }, function(err) {
195 if (err) {
196 return res.send({ status: err });
197 }
198 return res.send(result);
199 });
200 });
201
202 self._app.post(self._action + '/remove', function(req, res) {
203 var group = self._apos.sanitizeString(req.body.group);
204 var id = self._apos.sanitizeId(req.body.id);
205 var slug = self._apos.sanitizeString(req.body.slug);
206 return async.series({
207 get: function(callback) {
208 return self._apos.getPage(req, slug, {}, function(err, _page) {
209 if (err) {
210 console.log(err);
211 return callback('error');
212 }
213 if (!_page) {
214 return callback('notfound');
215 }
216 page = _page;
217 return callback(null);
218 });
219 },
220 remove: function(callback) {
221 if (!page.blockGroups) {
222 return callback('notfound');
223 }
224 if (!page.blockGroups[group]) {
225 return callback('notfound');
226 }
227 // Keep only the blocks that DON'T match the id
228 page.blockGroups[group].blocks = _.filter(page.blockGroups[group].blocks, function(block) {
229 return (block.id !== id);
230 });
231 return self._apos.putPage(req, slug, page, callback);
232 }
233 }, function(err) {
234 if (err) {
235 return res.send({ status: err });
236 }
237 return res.send({ status: 'ok' });
238 });
239 });
240
241 self._app.post(self._action + '/order', function(req, res) {
242 var group = self._apos.sanitizeString(req.body.group);
243 var ids = self._apos.sanitizeIds(req.body.ids);
244 var slug = self._apos.sanitizeString(req.body.slug);
245 return async.series({
246 get: function(callback) {
247 return self._apos.getPage(req, slug, {}, function(err, _page) {
248 if (err) {
249 console.log(err);
250 return callback('error');
251 }
252 if (!_page) {
253 return callback('notfound');
254 }
255 page = _page;
256 return callback(null);
257 });
258 },
259 update: function(callback) {
260 if (!page.blockGroups) {
261 return callback('notfound');
262 }
263 if (!page.blockGroups[group]) {
264 return callback('notfound');
265 }
266 page.blockGroups[group].blocks = self._apos.orderById(ids, page.blockGroups[group].blocks, 'id');
267 return self._apos.putPage(req, slug, page, callback);
268 }
269 }, function(err) {
270 if (err) {
271 return res.send({ status: err });
272 }
273 return res.send({ status: 'ok' });
274 });
275 });
276
277 // Allows the "page versions" dialog to say something about
278 // blocks coming and going
279 self._apos.on('diff', function(page, lines) {
280 _.each(page.blockGroups || {}, function(value, group) {
281 var prefix = group + ' block: ';
282 _.each(value.blocks, function(block) {
283 lines.push(prefix + block.type);
284 });
285 });
286 });
287
288 return process.nextTick(function() {
289 return callback(null);
290 });
291}
292
293blocks.Blocks = Blocks;