@nakedcreativity/cee-ms-node 0.3.38 → 1.0.2

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,739 +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.lexical.parseLiteral(this.match[0])
224
- var jsonData = JSON.parse(fs.readFileSync(filepath, 'utf8'))
225
-
226
- var scopeObj = {}
227
- scopeObj['external'] = jsonData
228
- scope.push(scopeObj)
229
-
230
- //console.log(scope)
231
- var template = Liquid.lexical.parseLiteral(this.match[1])
232
- if (!template) {
233
- template = this.match[1]
234
- }
235
-
236
- return engine.handleCache(template + '.liquid')
237
- .then((templates) => {
238
- return engine.renderer.renderTemplates(templates, scope);
239
- })
240
- .then((html) => {
241
- scope.pop();
242
- return html;
243
- });
244
-
245
- }
246
- })
247
-
248
-
249
- this.engine.registerFilter('active', (path, match, cssClass) => {
250
-
251
- var regex = new RegExp("^"+match+"(?!([a-z0-9\-_]+))")
252
-
253
- if(regex.test(path)){
254
- return cssClass
255
- }else {
256
- return ''
257
- }
258
-
259
- })
260
-
261
-
262
- this.engine.registerFilter('url', (url) => {
263
-
264
- return this.site.site.url+url
265
-
266
- })
267
-
268
- this.engine.registerTag('image', {
269
- parse: function(tagToken, remainTokens) {
270
- this.args = tagToken.args.split(' ');
271
- },
272
- render: function(scope, hash) {
273
- var image = Liquid.evalExp(this.args[0], scope);
274
- //console.log(image)
275
-
276
- if(!image){
277
- return Promise.resolve('')
278
- }
279
-
280
- if(this.args[1] && image.variants && image.variants[this.args[1]]){
281
- var src = image.variants[this.args[1]]
282
- }else {
283
- var src = image.src
284
- }
285
- var alt = image.alt || ''
286
- return Promise.resolve('<img src="'+src+'" alt="'+alt+'"/>');
287
- }
288
- });
289
-
290
- this.engine.registerTag('imageurl', {
291
- parse: function(tagToken, remainTokens) {
292
- this.args = tagToken.args.split(' ');
293
- },
294
- render: function(scope, hash) {
295
- var image = Liquid.evalExp(this.args[0], scope);
296
-
297
- if(!image){
298
- return Promise.resolve('')
299
- }
300
-
301
- //console.log(image)
302
- if(this.args[1] && image.variants && image.variants[this.args[1]]){
303
- var src = image.variants[this.args[1]]
304
- }else {
305
- var src = image.src
306
- }
307
- return Promise.resolve(src);
308
- }
309
- });
310
-
311
- this.engine.registerTag('breadcrumb', {
312
- parse: function(tagToken, remainTokens) {
313
- this.template = tagToken.args
314
- },
315
- render: function(scope, hash){
316
-
317
- var breadcrumbArray = []
318
- var path = ''
319
- if(!scope.scopes[0].path){
320
- return
321
- }
322
- scope.scopes[0].path.forEach(handle => {
323
- path += handle
324
- if(site.pages[path]){
325
- breadcrumbArray.push(site.pages[path])
326
- path += '/'
327
- }
328
- })
329
-
330
- scope.push({
331
- breadcrumb: breadcrumbArray
332
- })
333
-
334
- return engine.handleCache(this.template+'.liquid')
335
- .then((templates) => {
336
- return engine.renderer.renderTemplates(templates, scope);
337
- })
338
- .then((html) => {
339
- scope.pop();
340
- return html;
341
- });
342
- }
343
- })
344
-
345
- this.engine.registerTag('blog', {
346
- parse: function (tagToken, remainTokens) {
347
- this.match = tagToken.args.split(' ')
348
- },
349
- render: function (scope, hash) {
350
- // get the blog set
351
- var siteblog = Liquid.evalValue(this.match[0], scope);
352
- if (!siteblog) {
353
- siteblog = this.match[0]
354
- }
355
- if (!site.blogs[siteblog]) {
356
- return
357
- }
358
- // loop over articles, push articles that match tag or pagination, into array
359
- var blog = site.blogs[siteblog]
360
- var articles = []
361
- // if tag set, create filtered array of articles
362
- if (this.match[3]) {
363
- var tag = Liquid.evalValue(this.match[3], scope);
364
- if (!tag) {
365
- tag = Liquid.lexical.parseLiteral(this.match[3])
366
- }
367
- blog.articles.forEach((article) => {
368
- if (article.tags && article.tags.indexOf(tag) > -1) {
369
- articles.push(article)
370
- }
371
- })
372
- } else {
373
- articles = blog.articles
374
- }
375
- //check if articles per page is set
376
- if (this.match[2]) {
377
- var articlesPerPage = this.match[2]
378
- } else {
379
- var articlesPerPage = 10
380
- }
381
- var pagination = 1
382
- if (scope.scopes[0].page && scope.scopes[0].page.pagination) {
383
- pagination = scope.scopes[0].page.pagination
384
- }
385
- articlesPerPage = parseInt(articlesPerPage)
386
- var start = articlesPerPage * (pagination - 1)
387
- var end = start + articlesPerPage
388
- var lastPage = Math.ceil(articles.length / articlesPerPage)
389
- // is page greater than the number of articles?
390
- // throw a 404
391
- if (start >= articles.length) {
392
- throw new Error('This page does not exist')
393
- }
394
- // slice original blog array to send just current page of articles to liquid
395
- blog.articles = articles.slice(start, end)
396
- blog.pagination = pagination
397
- blog.lastPage = lastPage
398
- scope.push(blog)
399
- return engine.handleCache(this.match[1] + '.liquid')
400
- .then((templates) => {
401
- return engine.renderer.renderTemplates(templates, scope);
402
- })
403
- .then((html) => {
404
- scope.pop();
405
- return html;
406
- })
407
- .catch((error) => {
408
- throw error
409
- })
410
- }
411
- })
412
-
413
-
414
- this.app.listen(config.port, function () {});
415
-
416
- this.app.use((req, res, next)=>{
417
-
418
- if(config.response && config.response.headers){
419
- _.each(config.response.headers, header=>{
420
- res.header(header[0], header[1])
421
- })
422
- }
423
-
424
- next();
425
- });
426
-
427
-
428
- if(!config.customHandler){
429
- this.app.get(/(.*)/, (req, res)=>{
430
- this.render(req,res);
431
- })
432
- }
433
-
434
- this.app.post(/draft\/(.*)/, (req, res)=>{
435
-
436
- // validate authorisation JWT here
437
-
438
- //console.log(req.body.page)
439
- req.query.edit = true;
440
-
441
- // if this was sent by the iframe, handle it differently
442
- if(req.query.iframe != null){
443
- this.site = JSON.parse(decodeURIComponent(req.body.site))
444
- site = this.site
445
- this.render(req, res, JSON.parse(decodeURIComponent(req.body.page)));
446
- }else {
447
- this.site = req.body.site
448
- site = this.site
449
- this.render(req, res, req.body.page);
450
- }
451
-
452
- })
453
-
454
- }
455
-
456
- ceems.prototype.load = function(req, res){
457
-
458
- var path = req._parsedUrl.pathname.substring(1)
459
- if(path == '')
460
- path = 'home'
461
-
462
- this.site = JSON.parse(fs.readFileSync('./pages/site.json', 'utf8'));
463
- //this.pages_map = _.indexBy(this.site.pages, 'url');
464
-
465
- site = this.site //????
466
-
467
- // this may be better in middleware?
468
- // test if path matches a pagination url
469
- var regex = new RegExp("(.+)(\/[0-9]{1,})$")
470
- if(regex.test(path)){
471
- // looks like a blog url
472
- var matches = path.match(regex)
473
- var path = matches[1]
474
- var pagination = parseInt(matches[2].substring(1))
475
- }
476
-
477
- if(this.site.pages[path]){
478
- var page = this.site.pages[path]
479
-
480
- if(page.redirect){
481
- res.redirect(301, page.redirect)
482
- }
483
-
484
- var page = JSON.parse(fs.readFileSync('./pages/'+page._id+'.json', 'utf8'));
485
- page.fullpath = this.site.pages[path].url
486
- page.path = page.fullpath.split('/')
487
-
488
- if(pagination)
489
- page.pagination = pagination
490
-
491
- return page
492
-
493
- }
494
- // 404
495
- else{
496
- console.log('404 for ',path)
497
- res.status(404)
498
- // .send('Sorry, this page does not exist');
499
- var notFound = this.site.pages['404']
500
- return JSON.parse(fs.readFileSync('./pages/'+notFound._id+'.json', 'utf8'))
501
- }
502
-
503
-
504
- }
505
-
506
- ceems.prototype.render = function(req, res, page=null) {
507
-
508
- if(!page){
509
- page = this.load(req, res)
510
- }
511
- // get any external data, and save this into the page
512
- // if(page.external){
513
- // _.each(page.external, function(ext, key){
514
-
515
- // try {
516
- // page.external[key].data = JSON.parse(fs.readFileSync('./'+ext.file, 'utf8'));
517
- // }catch(error){
518
- // console.log(error)
519
- // }
520
-
521
- // })
522
- // }
523
-
524
- // if there are whitelisted cookies, add these to the page so they can be rendered
525
- if(this.config.whitelistedCookies){
526
- page.cookies = []
527
- _.each(this.config.whitelistedCookies, cookieName=>{
528
- if(req.cookies && req.cookies[cookieName]){
529
- page.cookies[cookieName] = req.cookies[cookieName]
530
- }
531
- })
532
- }
533
-
534
- var renderedRegions = {}
535
- var renderedColumns = {}
536
- var renderedModules = {}
537
-
538
- var jobs = []
539
-
540
- // load page data
541
- _.each(page.regions, function(region, key){
542
-
543
- renderedRegions[key] = []
544
-
545
- if(region.containers){
546
-
547
- region.containers.forEach((container, index)=>{
548
-
549
- if(container.module){
550
-
551
- var html = `{% ${container.module} %}`
552
-
553
- }else {
554
-
555
-
556
- if(!container.class) {
557
- container.class = 'container'
558
- }
559
-
560
- console.log(container)
561
-
562
- var html = ''
563
- if(container.wrapper){
564
- html += `<div class="${container.wrapper}" data-container-id="${container._id}"><div class="${container.class}">`
565
- }else {
566
- html += `<div class="${container.class}" data-container-id="${container._id}">`
567
- }
568
-
569
-
570
- container.rows.forEach(row=>{
571
-
572
- html += `<div class="row ${row.class}" data-row-id="${row._id}">`
573
- row.columns.forEach(column=>{
574
-
575
- console.log(column)
576
-
577
- var modules = _.reduce(column.modules, (modules, id)=>{
578
- return modules + '{% '+id+' %}'
579
- }, '')
580
- html += `<div data-column-id="${column._id}" class="col ${column.class}">${modules}</div>` // inserts placeholder in format {% id %}
581
-
582
- })
583
-
584
- html += `</div>`
585
-
586
- })
587
- if(container.wrapper){
588
- html += `</div>`
589
- }
590
- html += `</div>`
591
-
592
- }
593
-
594
- renderedRegions[key][index] = html
595
-
596
- })
597
- }
598
-
599
- _.each(region.modules, function(module, index){
600
-
601
- var moduleTemplate = fs.readFileSync('./views/'+module.type+'.liquid');
602
-
603
- jobs.push(new Promise((resolve,reject) => {
604
-
605
- // to-do: we need to give module access to entire page object, and therefore the 'sitemap' and other site information
606
- var scope = module.content
607
- scope.page = page
608
- if (this.site){
609
- scope.site = this.site.site
610
- scope.pages = this.site.pages
611
- scope.navigation = this.site.navigation
612
- }
613
- this.engine.parseAndRender(moduleTemplate, scope)
614
- .then(html => {
615
-
616
- if(req.query.edit != null){
617
- var $ = cheerio.load(html);
618
-
619
- $.root().children().attr('data-module-type', module.type);
620
- if(module._id){
621
- $.root().children().attr('data-module-id', module._id);
622
- }
623
- html = $.html()
624
- }
625
-
626
- if(region.containers){
627
- renderedModules[module._id] = html
628
- }else {
629
- renderedRegions[key][index] = html
630
- }
631
- return resolve();
632
-
633
- })
634
- .catch(error=>{
635
- reject(error)
636
- })
637
- }))
638
-
639
- }, this)
640
-
641
-
642
- }, this)
643
-
644
- // loop through each module not in a region and generate HTML
645
- _.each(page.modules, function(module, key){
646
-
647
- var moduleTemplate = fs.readFileSync('./views/'+module.type+'.liquid');
648
-
649
- jobs.push(new Promise((resolve,reject) => {
650
-
651
- // we need to give module access to entire page object, and therefore the 'sitemap' and other site information
652
- var scope = module.content
653
- scope.page = page
654
- scope.site = this.site.site
655
- scope.pages = this.site.pages
656
-
657
- this.engine.parseAndRender(moduleTemplate, scope)
658
- .then(function(html){
659
- //console.log(html)
660
-
661
- if(req.query.edit != null){
662
- var $ = cheerio.load(html);
663
- $.root().children().attr('data-module-type', module.type);
664
- $.root().children().attr('data-module-id', module._id);
665
- //console.log($.root().children())
666
-
667
- html = $.html()
668
- }
669
-
670
- page.modules[key] = html
671
- return resolve();
672
-
673
- })
674
- .catch(error=>{
675
- reject(error)
676
- })
677
- }))
678
-
679
- }, this)
680
-
681
- Promise.all(jobs).then(html =>{
682
-
683
- // map / reduce? look at _
684
- _.each(renderedRegions, function(region, key){
685
- page.regions[key] = ''
686
- _.each(region, function(html){
687
-
688
- // items in rendered columns to place
689
- if(Object.keys(renderedModules).length > 0){
690
- _.each(renderedModules, (moduleHtml, key)=>{
691
- html = html.replace(new RegExp('{%\\s'+key+'\\s%}','g'), moduleHtml)
692
- })
693
-
694
- }
695
-
696
- page.regions[key] += html
697
- })
698
- })
699
-
700
-
701
- var scope = page
702
- if(this.site && this.site.navigation)
703
- scope.navigation = this.site.navigation
704
-
705
- //console.log(scope)
706
- res.render(page.template, scope, function(err, html){
707
- if(err){
708
- console.log(err)
709
- }
710
-
711
- if(req.query.edit != null){
712
- var $ = cheerio.load(html);
713
- $('body').addClass('cee-ms-edit');
714
- html = $.html()
715
- }
716
-
717
- //res.header('X-FRAME-OPTIONS', 'allow-from http://localhost:3002');
718
- //console.log(res)
719
- res.send(html)
720
- })
721
-
722
- }).catch(error=>{
723
- console.log('error in')
724
- console.log(error)
725
- res.status(500).send(error.message);
726
- })
727
- }
728
-
729
- ceems.prototype.init = function(){
730
-
731
- // needs to be dealt with on each request, or results cache in someway
732
-
733
- }
734
-
735
- ceems.prototype.viewsPath = function(){
736
- return './views/'
737
- }
738
-
739
- module.exports = ceems;