@nakedcreativity/cee-ms-node 0.3.39 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js DELETED
@@ -1,742 +0,0 @@
1
- var express = require('express');
2
- const path = require('path');
3
- const fs = require('fs');
4
- const _ = require('underscore');
5
- var Liquid = require('nakedcreativity-liquid');
6
- var cheerio = require('cheerio');
7
- var bodyParser = require("body-parser");
8
-
9
- var lexical = Liquid.lexical;
10
-
11
- var site;
12
- var engine;
13
-
14
- function ceems(config) {
15
-
16
- this.app = express()
17
-
18
- // process config and set defaults
19
- // // set up static file hosting
20
- this.app.use(express.static(config.assets))
21
- this.app.use(bodyParser.json({
22
- limit: config.parseLimit || '250kb'
23
- }));
24
- this.app.use(bodyParser.urlencoded({
25
- extended:false,
26
- limit: config.parseLimit || '250kb'
27
- }));
28
-
29
- this.engine = Liquid({
30
- root: path.resolve('./views/'), // for layouts and partials
31
- extname: '.liquid',
32
- cache: true
33
- });
34
- engine = this.engine
35
-
36
- this.config = config;
37
-
38
- // register liquid engine
39
- this.app.engine('liquid', this.engine.express({
40
- strict_variables: false,
41
- strict_filters: true
42
- }));
43
- this.app.set('views', this.viewsPath());
44
- this.app.set('view engine', 'liquid');
45
-
46
-
47
-
48
- // register region and module tags
49
- this.engine.registerTag('region', {
50
- parse: function(tagToken, remainTokens) {
51
- this.name = tagToken.args; // name
52
- },
53
- render: function(scope, hash) {
54
- console.log('render region',this.name)
55
- var str = Liquid.evalValue('regions.'+this.name, scope);
56
- return Promise.resolve(str);
57
- }
58
- });
59
-
60
- this.engine.registerTag('module', {
61
- parse: function(tagToken, remainTokens) {
62
- this.name = tagToken.args; // name
63
- },
64
- render: function(scope, hash) {
65
- var str = Liquid.evalValue('modules.'+this.name, scope);
66
- return Promise.resolve(str);
67
- }
68
- });
69
-
70
- this.engine.registerTag('navigation', {
71
- parse: function(tagToken, remainTokens) {
72
- this.match = tagToken.args.split(' '); // name // path[0] == navigation name
73
- },
74
- render: function(scope, hash){
75
-
76
- var value = Liquid.evalValue(this.match[0], scope)
77
- if(!value){
78
- value = Liquid.lexical.parseLiteral(this.match[0])
79
- }
80
- var page = scope.scopes[0]
81
-
82
- if(page.path && this.match[0] == 'page.path[0]' && site.navigation[page.path[0]]){
83
- scope.push(site.navigation[page.path[0]]);
84
- }else if(page.path && this.match[0] == 'page.path[1]' && site.navigation[page.path[1]]){
85
- scope.push(site.navigation[page.path[1]]);
86
- }else if(site.navigation[value]){
87
- scope.push(site.navigation[value]);
88
- }else {
89
- return
90
- }
91
- var template = Liquid.lexical.parseLiteral(this.match[1])
92
- if(!template){
93
- template = this.match[1]
94
- }
95
-
96
- return engine.handleCache(template+'.liquid')
97
- .then((templates) => {
98
- return engine.renderer.renderTemplates(templates, scope);
99
- })
100
- .then((html) => {
101
- scope.pop();
102
- return html;
103
- });
104
- }
105
- });
106
-
107
- this.engine.registerTag('page', {
108
- parse: function(tagToken, remainTokens) {
109
- this.match = tagToken.args.split(' '); // name // path[0] == navigation name
110
- },
111
- render: function(scope, hash){
112
- // var filepath = Liquid.evalValue(this.match[1]+'.liquid', scope);
113
-
114
- var path = Liquid.evalValue(this.match[0], scope)
115
- if(!path){
116
- path = Liquid.lexical.parseLiteral(this.match[0])
117
- }
118
- if(!path){
119
- path = this.match[0]
120
- }
121
-
122
- if(site.pages[path]){
123
- var page = site.pages[path]
124
-
125
- if(page.redirect){
126
- res.redirect(parseInt(page.redirect.type), page.redirect.to)
127
- }
128
-
129
- var page = JSON.parse(fs.readFileSync('./pages/'+page._id+'.json', 'utf8'));
130
- page.fullpath = site.pages[path].url
131
- page.path = page.fullpath.split('/')
132
-
133
-
134
- scope.push(page)
135
-
136
- var template = Liquid.lexical.parseLiteral(this.match[1])
137
- if(!template){
138
- template = this.match[1]
139
- }
140
-
141
- return engine.handleCache(template+'.liquid')
142
- .then((templates) => {
143
- return engine.renderer.renderTemplates(templates, scope);
144
- })
145
- .then((html) => {
146
- scope.pop();
147
- return html;
148
- });
149
-
150
- }else {
151
- return
152
- }
153
-
154
-
155
-
156
- }
157
- })
158
-
159
- this.engine.registerTag('data', {
160
- parse: function(tagToken, remainTokens) {
161
- this.match = tagToken.args.split(' '); // name // path[0] == navigation name
162
- },
163
- render: function(scope, hash){
164
- // var filepath = Liquid.evalValue(this.match[1]+'.liquid', scope);
165
-
166
- if(this.match[2]){
167
- var itemsPerPage = this.match[2]
168
- }else{
169
- var itemsPerPage = 10
170
- }
171
-
172
- var collectionName = Liquid.evalValue(this.match[0], scope)
173
-
174
- if(site.collections[collectionName]){
175
- //console.log(site.collections[collection]._id)
176
- var collection = JSON.parse(fs.readFileSync('./data/'+site.collections[collectionName]._id+'.json', 'utf8'));
177
-
178
- var scopeObj = {}
179
- scopeObj['collection'] = collection
180
-
181
- if(scope.scopes[0].page && scope.scopes[0].page.pagination){
182
- scopeObj.pagination = scope.scopes[0].page.pagination
183
- }else {
184
- scopeObj.pagination = 1
185
- }
186
-
187
- scopeObj.itemsPerPage = parseInt(itemsPerPage);
188
-
189
- scopeObj.start = itemsPerPage * (scopeObj.pagination - 1)
190
- scopeObj.end = scopeObj.start + itemsPerPage
191
-
192
- scopeObj.lastPage = Math.ceil(collection.data.length / itemsPerPage)
193
-
194
- scope.push(scopeObj)
195
-
196
- //console.log(scope)
197
- var template = Liquid.lexical.parseLiteral(this.match[1])
198
- if (!template) {
199
- template = this.match[1]
200
- }
201
-
202
- return engine.handleCache(template+'.liquid')
203
- .then((templates) => {
204
- return engine.renderer.renderTemplates(templates, scope);
205
- })
206
- .then((html) => {
207
- scope.pop();
208
- return html;
209
- });
210
-
211
- }else {
212
- return
213
- }
214
- }
215
- })
216
-
217
- this.engine.registerTag('external', {
218
- parse: function (tagToken, remainTokens) {
219
- this.match = tagToken.args.split(' '); // name // path[0] == navigation name
220
- },
221
- render: function (scope, hash) {
222
-
223
- var filepath = Liquid.evalValue(this.match[0], scope)
224
- if(!filepath){
225
- filepath = Liquid.lexical.parseLiteral(this.match[0])
226
- }
227
- var jsonData = JSON.parse(fs.readFileSync(filepath, 'utf8'))
228
-
229
- var scopeObj = {}
230
- scopeObj['external'] = jsonData
231
- scope.push(scopeObj)
232
-
233
- //console.log(scope)
234
- var template = Liquid.lexical.parseLiteral(this.match[1])
235
- if (!template) {
236
- template = this.match[1]
237
- }
238
-
239
- return engine.handleCache(template + '.liquid')
240
- .then((templates) => {
241
- return engine.renderer.renderTemplates(templates, scope);
242
- })
243
- .then((html) => {
244
- scope.pop();
245
- return html;
246
- });
247
-
248
- }
249
- })
250
-
251
-
252
- this.engine.registerFilter('active', (path, match, cssClass) => {
253
-
254
- var regex = new RegExp("^"+match+"(?!([a-z0-9\-_]+))")
255
-
256
- if(regex.test(path)){
257
- return cssClass
258
- }else {
259
- return ''
260
- }
261
-
262
- })
263
-
264
-
265
- this.engine.registerFilter('url', (url) => {
266
-
267
- return this.site.site.url+url
268
-
269
- })
270
-
271
- this.engine.registerTag('image', {
272
- parse: function(tagToken, remainTokens) {
273
- this.args = tagToken.args.split(' ');
274
- },
275
- render: function(scope, hash) {
276
- var image = Liquid.evalExp(this.args[0], scope);
277
- //console.log(image)
278
-
279
- if(!image){
280
- return Promise.resolve('')
281
- }
282
-
283
- if(this.args[1] && image.variants && image.variants[this.args[1]]){
284
- var src = image.variants[this.args[1]]
285
- }else {
286
- var src = image.src
287
- }
288
- var alt = image.alt || ''
289
- return Promise.resolve('<img src="'+src+'" alt="'+alt+'"/>');
290
- }
291
- });
292
-
293
- this.engine.registerTag('imageurl', {
294
- parse: function(tagToken, remainTokens) {
295
- this.args = tagToken.args.split(' ');
296
- },
297
- render: function(scope, hash) {
298
- var image = Liquid.evalExp(this.args[0], scope);
299
-
300
- if(!image){
301
- return Promise.resolve('')
302
- }
303
-
304
- //console.log(image)
305
- if(this.args[1] && image.variants && image.variants[this.args[1]]){
306
- var src = image.variants[this.args[1]]
307
- }else {
308
- var src = image.src
309
- }
310
- return Promise.resolve(src);
311
- }
312
- });
313
-
314
- this.engine.registerTag('breadcrumb', {
315
- parse: function(tagToken, remainTokens) {
316
- this.template = tagToken.args
317
- },
318
- render: function(scope, hash){
319
-
320
- var breadcrumbArray = []
321
- var path = ''
322
- if(!scope.scopes[0].path){
323
- return
324
- }
325
- scope.scopes[0].path.forEach(handle => {
326
- path += handle
327
- if(site.pages[path]){
328
- breadcrumbArray.push(site.pages[path])
329
- path += '/'
330
- }
331
- })
332
-
333
- scope.push({
334
- breadcrumb: breadcrumbArray
335
- })
336
-
337
- return engine.handleCache(this.template+'.liquid')
338
- .then((templates) => {
339
- return engine.renderer.renderTemplates(templates, scope);
340
- })
341
- .then((html) => {
342
- scope.pop();
343
- return html;
344
- });
345
- }
346
- })
347
-
348
- this.engine.registerTag('blog', {
349
- parse: function (tagToken, remainTokens) {
350
- this.match = tagToken.args.split(' ')
351
- },
352
- render: function (scope, hash) {
353
- // get the blog set
354
- var siteblog = Liquid.evalValue(this.match[0], scope);
355
- if (!siteblog) {
356
- siteblog = this.match[0]
357
- }
358
- if (!site.blogs[siteblog]) {
359
- return
360
- }
361
- // loop over articles, push articles that match tag or pagination, into array
362
- var blog = site.blogs[siteblog]
363
- var articles = []
364
- // if tag set, create filtered array of articles
365
- if (this.match[3]) {
366
- var tag = Liquid.evalValue(this.match[3], scope);
367
- if (!tag) {
368
- tag = Liquid.lexical.parseLiteral(this.match[3])
369
- }
370
- blog.articles.forEach((article) => {
371
- if (article.tags && article.tags.indexOf(tag) > -1) {
372
- articles.push(article)
373
- }
374
- })
375
- } else {
376
- articles = blog.articles
377
- }
378
- //check if articles per page is set
379
- if (this.match[2]) {
380
- var articlesPerPage = this.match[2]
381
- } else {
382
- var articlesPerPage = 10
383
- }
384
- var pagination = 1
385
- if (scope.scopes[0].page && scope.scopes[0].page.pagination) {
386
- pagination = scope.scopes[0].page.pagination
387
- }
388
- articlesPerPage = parseInt(articlesPerPage)
389
- var start = articlesPerPage * (pagination - 1)
390
- var end = start + articlesPerPage
391
- var lastPage = Math.ceil(articles.length / articlesPerPage)
392
- // is page greater than the number of articles?
393
- // throw a 404
394
- if (start >= articles.length) {
395
- throw new Error('This page does not exist')
396
- }
397
- // slice original blog array to send just current page of articles to liquid
398
- blog.articles = articles.slice(start, end)
399
- blog.pagination = pagination
400
- blog.lastPage = lastPage
401
- scope.push(blog)
402
- return engine.handleCache(this.match[1] + '.liquid')
403
- .then((templates) => {
404
- return engine.renderer.renderTemplates(templates, scope);
405
- })
406
- .then((html) => {
407
- scope.pop();
408
- return html;
409
- })
410
- .catch((error) => {
411
- throw error
412
- })
413
- }
414
- })
415
-
416
-
417
- this.app.listen(config.port, function () {});
418
-
419
- this.app.use((req, res, next)=>{
420
-
421
- if(config.response && config.response.headers){
422
- _.each(config.response.headers, header=>{
423
- res.header(header[0], header[1])
424
- })
425
- }
426
-
427
- next();
428
- });
429
-
430
-
431
- if(!config.customHandler){
432
- this.app.get(/(.*)/, (req, res)=>{
433
- this.render(req,res);
434
- })
435
- }
436
-
437
- this.app.post(/draft\/(.*)/, (req, res)=>{
438
-
439
- // validate authorisation JWT here
440
-
441
- //console.log(req.body.page)
442
- req.query.edit = true;
443
-
444
- // if this was sent by the iframe, handle it differently
445
- if(req.query.iframe != null){
446
- this.site = JSON.parse(decodeURIComponent(req.body.site))
447
- site = this.site
448
- this.render(req, res, JSON.parse(decodeURIComponent(req.body.page)));
449
- }else {
450
- this.site = req.body.site
451
- site = this.site
452
- this.render(req, res, req.body.page);
453
- }
454
-
455
- })
456
-
457
- }
458
-
459
- ceems.prototype.load = function(req, res){
460
-
461
- var path = req._parsedUrl.pathname.substring(1)
462
- if(path == '')
463
- path = 'home'
464
-
465
- this.site = JSON.parse(fs.readFileSync('./pages/site.json', 'utf8'));
466
- //this.pages_map = _.indexBy(this.site.pages, 'url');
467
-
468
- site = this.site //????
469
-
470
- // this may be better in middleware?
471
- // test if path matches a pagination url
472
- var regex = new RegExp("(.+)(\/[0-9]{1,})$")
473
- if(regex.test(path)){
474
- // looks like a blog url
475
- var matches = path.match(regex)
476
- var path = matches[1]
477
- var pagination = parseInt(matches[2].substring(1))
478
- }
479
-
480
- if(this.site.pages[path]){
481
- var page = this.site.pages[path]
482
-
483
- if(page.redirect){
484
- res.redirect(301, page.redirect)
485
- }
486
-
487
- var page = JSON.parse(fs.readFileSync('./pages/'+page._id+'.json', 'utf8'));
488
- page.fullpath = this.site.pages[path].url
489
- page.path = page.fullpath.split('/')
490
-
491
- if(pagination)
492
- page.pagination = pagination
493
-
494
- return page
495
-
496
- }
497
- // 404
498
- else{
499
- console.log('404 for ',path)
500
- res.status(404)
501
- // .send('Sorry, this page does not exist');
502
- var notFound = this.site.pages['404']
503
- return JSON.parse(fs.readFileSync('./pages/'+notFound._id+'.json', 'utf8'))
504
- }
505
-
506
-
507
- }
508
-
509
- ceems.prototype.render = function(req, res, page=null) {
510
-
511
- if(!page){
512
- page = this.load(req, res)
513
- }
514
- // get any external data, and save this into the page
515
- // if(page.external){
516
- // _.each(page.external, function(ext, key){
517
-
518
- // try {
519
- // page.external[key].data = JSON.parse(fs.readFileSync('./'+ext.file, 'utf8'));
520
- // }catch(error){
521
- // console.log(error)
522
- // }
523
-
524
- // })
525
- // }
526
-
527
- // if there are whitelisted cookies, add these to the page so they can be rendered
528
- if(this.config.whitelistedCookies){
529
- page.cookies = []
530
- _.each(this.config.whitelistedCookies, cookieName=>{
531
- if(req.cookies && req.cookies[cookieName]){
532
- page.cookies[cookieName] = req.cookies[cookieName]
533
- }
534
- })
535
- }
536
-
537
- var renderedRegions = {}
538
- var renderedColumns = {}
539
- var renderedModules = {}
540
-
541
- var jobs = []
542
-
543
- // load page data
544
- _.each(page.regions, function(region, key){
545
-
546
- renderedRegions[key] = []
547
-
548
- if(region.containers){
549
-
550
- region.containers.forEach((container, index)=>{
551
-
552
- if(container.module){
553
-
554
- var html = `{% ${container.module} %}`
555
-
556
- }else {
557
-
558
-
559
- if(!container.class) {
560
- container.class = 'container'
561
- }
562
-
563
- console.log(container)
564
-
565
- var html = ''
566
- if(container.wrapper){
567
- html += `<div class="${container.wrapper}" data-container-id="${container._id}"><div class="${container.class}">`
568
- }else {
569
- html += `<div class="${container.class}" data-container-id="${container._id}">`
570
- }
571
-
572
-
573
- container.rows.forEach(row=>{
574
-
575
- html += `<div class="row ${row.class}" data-row-id="${row._id}">`
576
- row.columns.forEach(column=>{
577
-
578
- console.log(column)
579
-
580
- var modules = _.reduce(column.modules, (modules, id)=>{
581
- return modules + '{% '+id+' %}'
582
- }, '')
583
- html += `<div data-column-id="${column._id}" class="col ${column.class}">${modules}</div>` // inserts placeholder in format {% id %}
584
-
585
- })
586
-
587
- html += `</div>`
588
-
589
- })
590
- if(container.wrapper){
591
- html += `</div>`
592
- }
593
- html += `</div>`
594
-
595
- }
596
-
597
- renderedRegions[key][index] = html
598
-
599
- })
600
- }
601
-
602
- _.each(region.modules, function(module, index){
603
-
604
- var moduleTemplate = fs.readFileSync('./views/'+module.type+'.liquid');
605
-
606
- jobs.push(new Promise((resolve,reject) => {
607
-
608
- // to-do: we need to give module access to entire page object, and therefore the 'sitemap' and other site information
609
- var scope = module.content
610
- scope.page = page
611
- if (this.site){
612
- scope.site = this.site.site
613
- scope.pages = this.site.pages
614
- scope.navigation = this.site.navigation
615
- }
616
- this.engine.parseAndRender(moduleTemplate, scope)
617
- .then(html => {
618
-
619
- if(req.query.edit != null){
620
- var $ = cheerio.load(html);
621
-
622
- $.root().children().attr('data-module-type', module.type);
623
- if(module._id){
624
- $.root().children().attr('data-module-id', module._id);
625
- }
626
- html = $.html()
627
- }
628
-
629
- if(region.containers){
630
- renderedModules[module._id] = html
631
- }else {
632
- renderedRegions[key][index] = html
633
- }
634
- return resolve();
635
-
636
- })
637
- .catch(error=>{
638
- reject(error)
639
- })
640
- }))
641
-
642
- }, this)
643
-
644
-
645
- }, this)
646
-
647
- // loop through each module not in a region and generate HTML
648
- _.each(page.modules, function(module, key){
649
-
650
- var moduleTemplate = fs.readFileSync('./views/'+module.type+'.liquid');
651
-
652
- jobs.push(new Promise((resolve,reject) => {
653
-
654
- // we need to give module access to entire page object, and therefore the 'sitemap' and other site information
655
- var scope = module.content
656
- scope.page = page
657
- scope.site = this.site.site
658
- scope.pages = this.site.pages
659
-
660
- this.engine.parseAndRender(moduleTemplate, scope)
661
- .then(function(html){
662
- //console.log(html)
663
-
664
- if(req.query.edit != null){
665
- var $ = cheerio.load(html);
666
- $.root().children().attr('data-module-type', module.type);
667
- $.root().children().attr('data-module-id', module._id);
668
- //console.log($.root().children())
669
-
670
- html = $.html()
671
- }
672
-
673
- page.modules[key] = html
674
- return resolve();
675
-
676
- })
677
- .catch(error=>{
678
- reject(error)
679
- })
680
- }))
681
-
682
- }, this)
683
-
684
- Promise.all(jobs).then(html =>{
685
-
686
- // map / reduce? look at _
687
- _.each(renderedRegions, function(region, key){
688
- page.regions[key] = ''
689
- _.each(region, function(html){
690
-
691
- // items in rendered columns to place
692
- if(Object.keys(renderedModules).length > 0){
693
- _.each(renderedModules, (moduleHtml, key)=>{
694
- html = html.replace(new RegExp('{%\\s'+key+'\\s%}','g'), moduleHtml)
695
- })
696
-
697
- }
698
-
699
- page.regions[key] += html
700
- })
701
- })
702
-
703
-
704
- var scope = page
705
- if(this.site && this.site.navigation)
706
- scope.navigation = this.site.navigation
707
-
708
- //console.log(scope)
709
- res.render(page.template, scope, function(err, html){
710
- if(err){
711
- console.log(err)
712
- }
713
-
714
- if(req.query.edit != null){
715
- var $ = cheerio.load(html);
716
- $('body').addClass('cee-ms-edit');
717
- html = $.html()
718
- }
719
-
720
- //res.header('X-FRAME-OPTIONS', 'allow-from http://localhost:3002');
721
- //console.log(res)
722
- res.send(html)
723
- })
724
-
725
- }).catch(error=>{
726
- console.log('error in')
727
- console.log(error)
728
- res.status(500).send(error.message);
729
- })
730
- }
731
-
732
- ceems.prototype.init = function(){
733
-
734
- // needs to be dealt with on each request, or results cache in someway
735
-
736
- }
737
-
738
- ceems.prototype.viewsPath = function(){
739
- return './views/'
740
- }
741
-
742
- module.exports = ceems;