json-object-editor 0.10.654 → 0.10.660

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.
@@ -555,6 +555,85 @@ var routeWithoutSlash = route.startsWith('/') ? route.slice(1) : route;
555
555
  logit(`sent html ${new Date().getTime() - routeStartTimer}`);
556
556
  return;
557
557
  }
558
+
559
+ /*HANDLE PAGES WITHOUT LAYOUT - render content directly*/
560
+ if(!layout && page.content_type && (page.content_type == 'code' || page.content_type == 'wysiwyg')){
561
+ var html = page.content || '';
562
+
563
+ // Debug: log content length and first 200 chars
564
+ console.log(modulename+' [NO LAYOUT] content length:', html.length);
565
+ console.log(modulename+' [NO LAYOUT] content preview:', html.substring(0, 200));
566
+
567
+ // Process includes
568
+ includes = Array.from(new Set(includes));
569
+ var INC = '',incobj;
570
+ includes.map(function(i){
571
+ incobj = JOE.Data.include.where({_id:i})[0]||{filetype:'notfound'};
572
+ var url='/_include/'+incobj._id;
573
+ switch(incobj.filetype){
574
+ case 'css':
575
+ INC += '<link rel="Stylesheet" type="text/css" href="'+url+'"/>';
576
+ break;
577
+ case 'js':
578
+ INC += '<script src="'+url+'" type="application/javascript"></script>';
579
+ break;
580
+ }
581
+ });
582
+
583
+ // Check if this is a complete HTML document
584
+ var isCompleteHTML = html.indexOf('<!doctype') != -1 || html.indexOf('<html') != -1;
585
+
586
+ // Only process template variables if:
587
+ // 1. Content is NOT a complete HTML document, OR
588
+ // 2. Content explicitly uses JOE template syntax (${this.PAGE.}, ${this.SITE.}, etc.)
589
+ var hasJoeTemplateSyntax = html.indexOf('${this.') != -1 ||
590
+ html.indexOf('${PAGE.') != -1 ||
591
+ html.indexOf('${SITE.') != -1 ||
592
+ html.indexOf('${DATA.') != -1 ||
593
+ html.indexOf('${INCLUDES}') != -1;
594
+
595
+ if(!isCompleteHTML || hasJoeTemplateSyntax){
596
+ // Process template variables if content uses them
597
+ var content = {
598
+ DYNAMIC:DYNAMIC,
599
+ INCLUDES:INC,
600
+ PAGE:page,
601
+ SITE:site,
602
+ JOEPATH:JOE.webconfig.joepath,
603
+ DATA:datasets,
604
+ WEBCONFIG:JOE.webconfig,
605
+ SECTION:section_content||{},
606
+ REQUEST:req,
607
+ ORIGIN:origin
608
+ };
609
+ if(hasJoeTemplateSyntax || html.indexOf('${') != -1){
610
+ console.log(modulename+' [NO LAYOUT] processing template variables');
611
+ html = fillTemplate(html, content);
612
+ }
613
+ } else {
614
+ console.log(modulename+' [NO LAYOUT] skipping template processing for complete HTML document');
615
+ }
616
+
617
+ // If content doesn't already have HTML structure, wrap it
618
+ if(!isCompleteHTML){
619
+ html = '<!doctype html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">' + INC + '</head><body>' + html + '</body></html>';
620
+ } else {
621
+ // Insert includes into existing HTML
622
+ if(html.indexOf('</head>') != -1){
623
+ html = html.replace('</head>', INC + '</head>');
624
+ } else if(html.indexOf('<head>') != -1){
625
+ html = html.replace('<head>', '<head>' + INC);
626
+ } else {
627
+ // No head tag, prepend to html
628
+ html = html.replace(/<html[^>]*>/, function(match){ return match + '<head>' + INC + '</head>'; });
629
+ }
630
+ }
631
+
632
+ console.log(modulename+' [NO LAYOUT] final HTML length:', html.length);
633
+ res.send(html);
634
+ logit(`sent html (no layout) ${new Date().getTime() - routeStartTimer}`);
635
+ return;
636
+ }
558
637
  }
559
638
 
560
639
  res.send(payload);
@@ -129,6 +129,11 @@ function Storage(specs){
129
129
  }
130
130
  var event_specs = {timestamp:ts};
131
131
 
132
+ // Build a history payload for auditing. We sanitize a copy of
133
+ // the document before diffing so that the craydent Object.equals
134
+ // helper never sees raw `null`/`undefined` values (its internal
135
+ // comparison calls `.toString()` on both sides, which will throw
136
+ // on `null`/`undefined` when they are equal).
132
137
  var history_payload = {
133
138
  itemid:data._id,
134
139
  collection:collection,
@@ -138,7 +143,29 @@ function Storage(specs){
138
143
  };
139
144
  //var cached = JOE.Cache.findByID(data.itemtype,data._id);
140
145
  if(cached){
141
- history_payload.changes = $c.changes(cached,data);
146
+ // Use sanitized clones for history diffing to avoid
147
+ // craydent-object's equals() calling `.toString()` on
148
+ // `null`/`undefined` and throwing. This does not affect
149
+ // what gets saved to Mongo, only what is recorded in
150
+ // the history "changes" payload.
151
+ var _sanitizeForHistory = function(input){
152
+ if (input === null || input === undefined){ return ''; }
153
+ if (Array.isArray(input)){
154
+ return input.map(function(v){ return _sanitizeForHistory(v); });
155
+ }
156
+ if (typeof input === 'object'){
157
+ var out = {};
158
+ for (var k in input){
159
+ if (!input.hasOwnProperty(k)){ continue; }
160
+ out[k] = _sanitizeForHistory(input[k]);
161
+ }
162
+ return out;
163
+ }
164
+ return input;
165
+ };
166
+ var cachedSafe = _sanitizeForHistory(cached);
167
+ var dataSafe = _sanitizeForHistory(data);
168
+ history_payload.changes = $c.changes(cachedSafe,dataSafe);
142
169
  //sanitize
143
170
  for(var hvar in history_payload.changes){
144
171
  if(hvar.indexOf('$') != -1){