json-object-editor
Version:
JOE the Json Object Editor | Platform Edition
297 lines (276 loc) • 13.6 kB
JavaScript
var page = {
title : '${name}',
info:"A page is a layout and include combination that displays content at a specific site URL.",
hideNumbers:true,
menuicon:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="-6 -6 44 44"><path d="M6 3L6 4 6 28 6 29 7 29 25 29 26 29 26 28 26 10 26 9.6 25.7 9.3 19.7 3.3 19.4 3 19 3 7 3 6 3zM8 5L18 5 18 10 18 11 19 11 24 11 24 27 8 27 8 5zM20 6.4L22.6 9 20 9 20 6.4zM16 13L14 25 16 25 18 13 16 13zM12.2 15.4L9.7 18.4 9.2 19 9.7 19.6 12.2 22.6 13.8 21.4 11.8 19 13.8 16.6 12.2 15.4zM19.8 15.4L18.2 16.6 20.2 19 18.2 21.4 19.8 22.6 22.3 19.6 22.8 19 22.3 18.4 19.8 15.4z"/></svg>',
// Curated summary for agents
summary:{
description:'Renderable web page bound to a Site, with layout, blocks, and optional dynamic content.',
purpose:'Pages define route paths and content; they render layouts and includes and can pull dynamic items based on URL parameters.',
labelField:'name',
defaultSort:{ field:'path', dir:'asc' },
searchableFields:['name','info','path','_id'],
allowedSorts:['path','name','joeUpdated','created'],
relationships:{
outbound:[
{ field:'site', targetSchema:'site', cardinality:'one' },
{ field:'layout', targetSchema:'layout', cardinality:'one' },
{ field:'includes', targetSchema:'include', cardinality:'many' },
{ field:'blocks', targetSchema:'block', cardinality:'many' },
{ field:'permissions', targetSchema:'user|group', cardinality:'many' }
],
inbound:{ graphRef:'server/relationships.graph.json' }
},
joeManagedFields:['created','joeUpdated'],
fields:[
{ name:'_id', type:'string', required:true },
{ name:'itemtype', type:'string', required:true, const:'page' },
{ name:'name', type:'string', required:true },
{ name:'info', type:'string' },
{ name:'site', type:'string', isReference:true, targetSchema:'site' },
{ name:'path', type:'string' },
{ name:'content_type', type:'string', enumValues:['wysiwyg','code','plugin','module'] },
{ name:'content', type:'string' },
{ name:'plugin', type:'string' },
{ name:'plugin_method', type:'string' },
{ name:'plugin_params', type:'objectList' },
{ name:'blocks', type:'string', isArray:true, isReference:true, targetSchema:'block' },
{ name:'layout', type:'string', isReference:true, targetSchema:'layout' },
{ name:'includes', type:'string', isArray:true, isReference:true, targetSchema:'include' },
{ name:'dynamic', type:'boolean' },
{ name:'content_items', type:'objectList' },
{ name:'datasets', type:'string', isArray:true },
{ name:'meta', type:'object' },
{ name:'permissions', type:'string', isArray:true, isReference:true, targetSchema:'user|group' },
{ name:'joeUpdated', type:'string', format:'date-time', required:true },
{ name:'created', type:'string', format:'date-time', required:true }
]
},
sorter:['path','name'],
listView:{
title:
'<joe-subtext>${RUN[_joe.getDataItemProp;\'${site}\';\'site\';\'url\']}${path}</joe-subtext>'+
'<joe-title>${name}</joe-title>' +
'<joe-subtitle>${info}</joe-subtitle>'+
'<joe-subtext><b><small>layout</b></small> ${RUN[_joe.getDataItemProp;\'${layout}\';\'layout\']}</joe-subtext>',
listWindowTitle: 'Pages'
},
methods:{
buildURL:function(item,fullURL,mixins){
var path = item.path || '/';
// Handle dynamic mixin substitution (e.g., :campaign → guid)
if (mixins && typeof mixins === 'object') {
Object.entries(mixins).forEach(([key, val]) => {
path = path.replace(`:${key}`, val);
});
}
// Normalize site prefix
if(item.site){
var site = $J.get(item.site);
path = `/${site.url}/${path}`.replace(/\/+/g, '/');
}
// Optional full URL
if(fullURL){
path = location.origin + path;
}
return path;
}
},
itemMenu:function(item){
var menu = [];
if(item.site){
// var site = _joe.Data.site.where({_id:item.site})[0]||{};
// var path = (site.url+'/'+item.path).replace('//','/');
var fullURL = _joe.schemas.page.methods.buildURL(item,true)
menu.push({name:'<joe-icon class="joe-view-button" title="preview"> </joe-icon>',
action:`window.open('${fullURL}');`});
}
return menu;
},
subsets:function(){
var subs = [{group_start:'sites'}];
_joe.Data.site.sortBy('name').map(function(site){
subs.push({name:site.name,filter:{site:site._id}});
});
subs.push({group_end:'sites'});
return subs;
},
bgColor:function(item){
if(!item.site){
return '';
}
var siteObj = _joe.Data.site.where({_id:item.site})[0]||false;
if(!siteObj){return '';}
if(siteObj.homepage == item._id){
return '#9ef';
}
},
fields:function(page){
var layout = _joe.getDataItem(page.layout,'layout');
var layout_fields = [];
if(layout){
var specific_fields = layout.layout_fields || [];
specific_fields.map(function(f){
var fspecs = {};
try{
if(f.specs){
fspecs = eval('('+f.specs+')');
}
}catch(e){
logit('error with layout field specs '+ f.field_name);
}
layout_fields.push({extend:f.field_name,
specs: $.extend({type: f.field_type},fspecs)
});
});
}
var fieldset = [
'name',
'info',
{section_start:'Content',collapsed:function(page){
return !page.content;
}},
{name:'content_type',type:'select',values:['wysiwyg','code','plugin','module'], rerender:'content,plugin,plugin_method,plugin_params'},
{extend:'content',specs:{
hidden:function(item){
return item.content_type =='plugin';
},
language:function(item){
return 'javascript';
},
comment:'${this.DATA.dataset_name}<br/>'+
`also available: INCLUDES,PAGE,LAYOUT,SITE,JOEPATH,DATA,WEBCONFIG
<div>for module: export function(data) that returns an html string.</div>
<h4>Example:</h4>
<div class="pad10">
<pre>
module.exports = async function(data){
// data contains all the page data, including dynamic items
return "html as string";
};
</pre>
</div>
</div>`
,
type:function(item){
if(!item.content_type){
return 'code';
}
if(["code","module"].indexOf(item.content_type) != -1){
return 'code';
}
return item.content_type;
}
}},
// {label:'Plugin Config', condition:function(item){
// return item.content_type =='plugin';
// }},
{name:"plugin",type:'select', width:'50%',comment:'Name of the JOE plugin to use',
values:function(){return __pluginNames},
hidden:function(item){
return item.content_type != 'plugin';
}
},
{name:"plugin_method", width:'50%',comment:'if blank, uses \'default\'',
hidden:function(item){
return item.content_type != 'plugin';
}
},
{name:"plugin_params", type:'objectList',comment:'these params are passed to the plugin',
hidden:function(item){
return item.content_type != 'plugin';
},
properties:['param','value']
},
{section_end:'Content'},
{section_label:'Blocks', section_start:'blocks_info'},
{name:'syncLayoutBlocks',value:true,type:'boolean',display:'layout sync',label:'automatically keep blocks synced to layout',width:'60%'},
{name:'createBlock', type:'create',schema:'block',display:'create new blocks', width:'40%'},
'blocks',
{section_end:'blocks_info'},
{section_start:'Layout'}
];
fieldset = fieldset.concat(layout_fields,[
{section_end:'Layout'},
{section_start:'Dynamic',collapsed:function(page){
return !page.dynamic;
}},
{name:'dynamic',type:'boolean',display:'dynamic',label:'the following items from the url path will be available in the template as ${this.DYNAMIC.[reference]}'},
// {name:'dynamic_hash',display:'Hash Routing',comment:'ie #/content_reference/content_reference'},
{name:'content_items',display:'Content Items',comment:'itemtype is the dataset to look at, reference is path param to use for an ID in that dataset', type:'objectList',
properties:['itemtype','reference']
},
'datasets',
{section_end:'Dynamic'},
'meta',
{section_start:'system',collapsed:true},
'_id','created','itemtype',
{section_end:'system'},
{sidebar_start:'right',collapsed:function(item){return false; return (item.site && item.layout && item.path)}},
'updated',
{name:'preview',type:'content',run:function(item){
if(item.site){
var site = $J.get(_joe.current.object.site);
//_joe.Data.site.where({_id:item.site})[0]||{};
//var path = __jsc.PORT?('/'+site.url+'/'+item.path).replace('//','/'):'//'+location.hostname+':'+__jsc.sitesPort+'/'+(site.url+'/'+item.path).replace('//','/');
var path = _joe.schemas.page.methods.buildURL(item,true)
return `
<joe-subtext>${location.origin}</joe-subtext>
<joe-subtitle>${(site.path||'')+item.path}</joe-subtitle>
<joe-button class="joe-button joe-iconed-button joe-preview-button full-width"
onclick="window.open('${path}');">Preview</joe-button>
<joe-button class="joe-button joe-iconed-button full-width"
onclick="window.open('${path}?editor=true');">Editor</joe-button>`
;
}else{
return 'no site selected';
}
}},
{section_start:'Adv'},
{extend:'site',specs:{rerender:'preview'}},
{name:'path',
// onblur:'if(this.value[0] != \'/\'){this.value = \'/\'+this.value;}'
// +'_joe.Fields.rerender(\'preview\');'/*rerender:'preview'*/,
// onblur:function(a,b,c){
// console.log(a,b,c);
// if(this.value[0] != "/"){this.value = "/"+this.value;};
// return this.value;
// //+'_joe.Fields.rerender(\'preview\');'
// },
rerender:'preview',
comment:'use /:reference for dynamic pages'
},
{label:'layout'},
{name:'layout',icon:'layout',dispaly:'current page layout',type:'select',values:'layout',idprop:'_id',goto:'layout'},
{name:'createLayout', type:'create',schema:'layout',label:false},
{label:'includes'},
'includes',
{name:'createInclude', type:'create',schema:'include',label:false},
{section_end:'Adv'},
{section_start:'Access'},
{name:'permissions',display:'Grant Permission to: ',type:'objectReference',values:function(){
var opts = [].concat(_joe.Data.group,_joe.Data.user);
return opts;
},_idprop:'_id',template:function(i){
var t =
'<joe-icon class="fleft" title="${itemtype}">'+_joe.schemas[i.itemtype].menuicon+'</joe-icon>';
switch(i.itemtype){
case 'user':
t+= '<joe-title>${name} <span class="joe-subtext">${itemtype}</span></joe-title>';
t+= '<joe-subtitle>'+((i.first && i.first+' '+i.last)|| i.fullname)+'</joe-subtitle>';
break;
case 'group':
t+= '<joe-title>${name} <span class="joe-subtext">${itemtype}</span></joe-title>';
break;
}
return t;
}
},
{section_end:'Access'},
{sidebar_end:'right'}
]);
return fieldset;
},
idprop : "_id"
};
module.exports = page;