UNPKG

jsharmony-cms

Version:
511 lines (451 loc) 23.9 kB
/* Copyright 2019 apHarmony This file is part of jsHarmony. jsHarmony is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. jsHarmony is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this package. If not, see <http://www.gnu.org/licenses/>. */ var Helper = require('jsharmony/Helper'); var HelperFS = require('jsharmony/HelperFS'); var _ = require('lodash'); var path = require('path'); var fs = require('fs'); var async = require('async'); module.exports = exports = function(module, funcs){ var exports = {}; var _t = module._t; exports.getMenuFile = function(menu_file_id){ if(!menu_file_id) throw new Error('Invalid menu_file_id'); return path.join(path.join(module.jsh.Config.datadir,'menu'),menu_file_id.toString()+'.json'); }; exports.getClientMenu = function(menu, cb){ var menu_file_id = menu.menu_file_id; //Load Menu Content from disk module.jsh.ParseJSON(funcs.getMenuFile(menu_file_id), module.name, 'Menu File ID#'+menu_file_id, function(err, menu_content){ if(err && !HelperFS.fileNotFound(err)) return cb(err); menu_content = menu_content || { menu_items: [] }; return cb(null,menu_content); }, { fatalError: false }); }; exports.getMenuUrl = function(menu_item, branchData){ if((menu_item.menu_item_link_type||'').toString()=='PAGE'){ var page_key = parseInt(menu_item.menu_item_link_dest); if(!branchData) return '#'; if(!(page_key in branchData.page_keys)) throw new Error('Menu '+(menu_item.menu_tag||'')+' item '+menu_item.menu_item_path+' :: '+menu_item.menu_item_text+' links to missing Page ID # '+page_key.toString()); return branchData.page_keys[page_key]; } else if((menu_item.menu_item_link_type||'').toString()=='MEDIA'){ var media_key = parseInt(menu_item.menu_item_link_dest); if(!branchData) return '#'; if(!(media_key in branchData.media_keys)) throw new Error('Menu '+(menu_item.menu_tag||'')+' item '+menu_item.menu_item_path+' :: '+menu_item.menu_item_text+' links to missing Media ID # '+media_key.toString()); return branchData.media_keys[media_key]; } return menu_item.menu_item_link_dest; }; exports.getMenuImageUrl = function(menu_item, branchData){ if (menu_item.menu_item_image) { var media_key = parseInt(menu_item.menu_item_image); if(!branchData) return '#'; if(!(media_key in branchData.media_keys)) throw new Error('Menu '+(menu_item.menu_tag||'')+' item '+menu_item.menu_item_path+' :: '+menu_item.menu_item_text+' image links to missing Media ID # '+media_key.toString()); return branchData.media_keys[media_key]; } return menu_item.menu_item_image; }; exports.createMenuTree = function(menu, branchData){ menu.menu_items = JSON.parse(JSON.stringify(menu.menu_items||[])); //Generate menu item tree var menu_item_ids = {}; for(var i=0;i<menu.menu_items.length;i++){ var menu_item = menu.menu_items[i]; menu_item.menu_item_children = []; menu_item_ids[menu_item.menu_item_id] = menu_item; menu_item.menu_item_type = (menu_item.menu_item_type||'TEXT').toUpperCase(); menu_item.menu_item_link_type = (menu_item.menu_item_link_type||'').toUpperCase(); menu_item.menu_item_link_target = (menu_item.menu_item_link_target||'').toUpperCase(); menu_item.id = menu_item.menu_item_id; menu_item.children = menu_item.menu_item_children; //html menu_item.html = menu_item.menu_item_text; if(menu_item.menu_item_type != 'HTML') menu_item.html = Helper.escapeHTML(menu_item.html); menu_item.text = Helper.StripTags(menu_item.html); menu_item.tag = menu_item.menu_item_tag || ''; menu_item.menu_tag = menu.menu_tag || ''; menu_item.class = menu_item.menu_item_class || ''; menu_item.style = menu_item.menu_item_style || ''; menu_item.href = ''; menu_item.onclick = ''; if(menu_item.menu_item_link_type){ if(menu_item.menu_item_link_type=='JS'){ menu_item.href = '#'; menu_item.onclick = menu_item.menu_item_link_dest + '; return false;'; } else { menu_item.href = funcs.getMenuUrl(menu_item, branchData); } } if(menu_item.menu_item_image){ menu_item.menu_item_image_path = funcs.getMenuImageUrl(menu_item, branchData); } menu_item.target = ((menu_item.menu_item_link_type != 'JS') && (menu_item.menu_item_link_target == 'NEWWIN')) ? '_blank' : ''; menu_item.selected = false; } var menu_item_tree = []; _.each(menu.menu_items, function(menu_item){ if(!menu_item.menu_item_parent_id){ menu_item_tree.push(menu_item); menu_item.parent = null; } else { menu_item_ids[menu_item.menu_item_parent_id].children.push(menu_item); menu_item.parent = menu_item_ids[menu_item.menu_item_parent_id]; } }); function resolveParent(menu_item){ if(menu_item.parents) return; if(!menu_item.parent){ menu_item.parents = []; menu_item.depth = 1; return; } if(!menu_item.parent.parents) resolveParent(menu_item.parent); menu_item.parents = menu_item.parent.parents.concat(menu_item.parent); menu_item.depth = menu_item.parent.depth + 1; } _.each(menu.menu_items, function(menu_item){ resolveParent(menu_item); menu_item.getSiblings = function(){ var siblings = menu_item.parent ? menu_item.parent.children : menu_item_tree; return _.filter(siblings, function(sibling){ return sibling.id != menu_item.id; }); }; }); //Add properties to menu menu.menu_item_tree = menu_item_tree; menu.tree = menu_item_tree; menu.items = menu.menu_items; menu.tag = menu.menu_tag; menu.currentItem = null; //Aliases menu.topItems = menu.tree; menu.allItems = menu.items; }; exports.menu = function (req, res, next) { var verb = req.method.toLowerCase(); var Q = req.query; var P = req.body; var appsrv = this; var jsh = module.jsh; var XValidate = jsh.XValidate; var sql = ''; var sql_ptypes = []; var sql_params = {}; var verrors = {}; var dbtypes = appsrv.DB.types; var validate = null; var model = jsh.getModel(req, module.namespace + 'Menu_Tree'); if(!req.params || !req.params.menu_key) return next(); var menu_key = req.params.menu_key; //Get menu sql_ptypes = [dbtypes.BigInt]; sql_params = { 'menu_key': menu_key }; validate = new XValidate(); verrors = {}; validate.AddValidator('_obj.menu_key', 'Menu Key', 'B', [XValidate._v_IsNumeric(), XValidate._v_Required()]); validate.AddValidator('_obj.branch_id', 'Branch ID', 'B', [XValidate._v_IsNumeric()]); sql = 'select menu_key,menu_file_id,menu_tag'; if(Q.menu_id){ sql_ptypes.push(dbtypes.BigInt); sql_params.menu_id = Q.menu_id; validate.AddValidator('_obj.menu_id', 'Menu ID', 'B', [XValidate._v_IsNumeric()]); sql += ' from {schema}.menu where menu_key=@menu_key and menu_id=@menu_id and site_id={schema}.my_current_site_id()'; } else sql += ' from {schema}.v_my_menu where menu_key=@menu_key'; var fields = []; var datalockstr = ''; appsrv.getDataLockSQL(req, model, fields, sql_ptypes, sql_params, verrors, function (datalockquery) { datalockstr += ' and ' + datalockquery; }); sql = Helper.ReplaceAll(sql, '%%%DATALOCKS%%%', datalockstr); verrors = _.merge(verrors, validate.Validate('B', sql_params)); if (!_.isEmpty(verrors)) { Helper.GenError(req, res, -2, verrors[''].join('\n')); return; } appsrv.ExecRecordset(req._DBContext, funcs.replaceSchema(sql), sql_ptypes, sql_params, function (err, rslt) { if (err != null) { err.sql = sql; err.model = model; appsrv.AppDBError(req, res, err); return; } if(!rslt || !rslt.length || !rslt[0] || (rslt[0].length != 1)){ return Helper.GenError(req, res, -4, 'Invalid Menu ID'); } var menu = rslt[0][0]; if (verb == 'get'){ if (!Helper.hasModelAction(req, model, 'B')) { Helper.GenError(req, res, -11, _t('Invalid Model Access')); return; } if (!appsrv.ParamCheck('P', P, [])) { Helper.GenError(req, res, -4, 'Invalid Parameters'); return; } if (!appsrv.ParamCheck('Q', Q, ['|menu_id','|branch_id'])) { Helper.GenError(req, res, -4, 'Invalid Parameters'); return; } //Return menu funcs.getClientMenu(menu, function(err, clientMenu){ if(err) { Helper.GenError(req, res, -99999, err.toString()); return; } var page_keys = {}; var media_keys = {}; var branch_id = null; var menu_config = {}; async.waterfall([ //Validate branch access function(cb){ if(!Q.branch_id) return cb(); sql = "select v_my_branch_access.branch_id,branch_name,site_id from {schema}.v_my_branch_access inner join {schema}.branch_menu on branch_menu.branch_id=v_my_branch_access.branch_id where v_my_branch_access.branch_id=@branch_id and branch_access like 'R%' and (branch_menu.menu_key=@menu_key)"; appsrv.ExecRecordset(req._DBContext, funcs.replaceSchema(sql), [dbtypes.BigInt,dbtypes.BigInt], { menu_key: menu.menu_key, branch_id: Q.branch_id }, function (err, rslt) { if (err != null) { err.sql = sql; err.model = model; appsrv.AppDBError(req, res, err); return; } if(!rslt || !rslt.length || !rslt[0] || !rslt[0].length){ return Helper.GenError(req, res, -12, 'Could not access Site Revision or Menu'); } branch_id = rslt[0][0].branch_id; return cb(); }); }, //Get site config function(cb){ funcs.getSiteConfig(req._DBContext, req.gdata.site_id, { continueOnConfigError: true }, function(err, siteConfig){ if(err) return cb(err); var site_config = siteConfig; if(site_config && site_config.menus){ _.each(site_config.menus, function(site_menu_config){ if(site_menu_config && site_menu_config.tag == menu.menu_tag) menu_config = _.pick(site_menu_config, ['max_depth']); }); } return cb(); }); }, //Get list of all page_keys function (menu_cb){ var sql = 'select page.page_key,page.page_path from {schema}.page inner join {schema}.branch_page on branch_page.page_id = page.page_id where branch_id = $ifnull(@branch_id, {schema}.my_current_branch_id()) and page_file_id is not null and page_path is not null'; appsrv.ExecRecordset(req._DBContext, funcs.replaceSchema(sql), [dbtypes.BigInt], { branch_id: branch_id }, function (err, rslt) { if (err != null) { err.sql = sql; return menu_cb(err); } if(!rslt || !rslt.length || !rslt[0]){ return menu_cb(new Error('Error loading pages')); } _.each(rslt[0], function(page){ page_keys[page.page_key] = page.page_path; }); return menu_cb(); }); }, //Get list of all media_keys function (menu_cb){ var sql = 'select media.media_key,media.media_path from {schema}.media inner join {schema}.branch_media on branch_media.media_id = media.media_id where branch_id = $ifnull(@branch_id, {schema}.my_current_branch_id()) and media_file_id is not null and media_path is not null'; appsrv.ExecRecordset(req._DBContext, funcs.replaceSchema(sql), [dbtypes.BigInt], { branch_id: branch_id }, function (err, rslt) { if (err != null) { err.sql = sql; return menu_cb(err); } if(!rslt || !rslt.length || !rslt[0]){ return menu_cb(new Error('Error loading media')); } _.each(rslt[0], function(media){ media_keys[media.media_key] = media.media_path; }); return menu_cb(); }); }, //Set menu_item_link_page for PAGE link types //Set menu_item_link_media for MEDIA link types function(menu_cb){ for(var i=0;i<clientMenu.menu_items.length;i++){ var menu_item = clientMenu.menu_items[i]; var menu_item_link_type = (menu_item.menu_item_link_type||'').toUpperCase(); if(menu_item_link_type=='PAGE'){ var page_key = parseInt(menu_item.menu_item_link_dest); if(page_key in page_keys){ menu_item.menu_item_link_page = page_keys[page_key]; } else { menu_item.menu_item_link_page = 'PAGE :: '+page_key+' :: Not found'; } } else if(menu_item_link_type=='MEDIA'){ var media_key = parseInt(menu_item.menu_item_link_dest); if(media_key in media_keys){ menu_item.menu_item_link_media = media_keys[media_key]; } else { menu_item.menu_item_link_media = 'MEDIA :: '+media_key+' :: Not found'; } } } return menu_cb(null); }, //Set menu_item_image function(menu_cb){ for(var i=0;i<clientMenu.menu_items.length;i++){ var menu_item = clientMenu.menu_items[i]; var media_key = parseInt(menu_item.menu_item_image); if(!media_key){ menu_item.menu_item_image_path = ''; } else if(media_key in media_keys){ menu_item.menu_item_image_path = media_keys[media_key]; } else { menu_item.menu_item_image_path = 'MEDIA :: '+media_key+' :: Not found'; } } return menu_cb(null); }, ], function(err){ if(err) { Helper.GenError(req, res, -99999, err.toString()); return; } res.type('json'); res.end(JSON.stringify({ _success: 1, menu: clientMenu, menu_config: menu_config || {}, })); }); }); } else if (verb == 'post'){ if (!Helper.hasModelAction(req, model, 'U')) { Helper.GenError(req, res, -11, _t('Invalid Model Access')); return; } //Validate parameters if (!appsrv.ParamCheck('P', P, ['&menu_items'])) { Helper.GenError(req, res, -4, 'Invalid Parameters'); return; } if (!appsrv.ParamCheck('Q', Q, [])) { Helper.GenError(req, res, -4, 'Invalid Parameters'); return; } //XValidate var menu_content = P; if (_.isString(menu_content.menu_items)){ try{ menu_content.menu_items = JSON.parse(menu_content.menu_items); } catch(ex){ Helper.GenError(req, res, -4, 'Invalid Parameters'); return; } } if (!_.isArray(menu_content.menu_items)) menu_content.menu_items = []; validate = new XValidate(); verrors = {}; validate.AddValidator('_obj.menu_item_id', 'Item ID', 'B', [XValidate._v_Required(), XValidate._v_IsNumeric(true)]); validate.AddValidator('_obj.menu_item_parent_id', 'Parent Item ID', 'B', [XValidate._v_IsNumeric(true)]); validate.AddValidator('_obj.menu_item_path', 'Path', 'B', [XValidate._v_Required(), XValidate._v_MaxLength(2048)]); validate.AddValidator('_obj.menu_item_text', 'Text', 'B', [XValidate._v_MaxLength(2048)]); validate.AddValidator('_obj.menu_item_type', 'Item Type', 'B', [XValidate._v_Required(), XValidate._v_InArray(['TEXT','HTML'])]); validate.AddValidator('_obj.menu_item_tag', 'Tag', 'B', [XValidate._v_MaxLength(2048)]); validate.AddValidator('_obj.menu_item_style', 'CSS Style', 'B', [XValidate._v_MaxLength(2048)]); validate.AddValidator('_obj.menu_item_class', 'CSS Class', 'B', [XValidate._v_MaxLength(2048)]); validate.AddValidator('_obj.menu_item_link_type', 'Link Type', 'B', [XValidate._v_InArray(['PAGE','MEDIA','URL','JS'])]); validate.AddValidator('_obj.menu_item_link_dest', 'Link Destination', 'B', [XValidate._v_MaxLength(2048)]); validate.AddValidator('_obj.menu_item_link_target', 'Link Target', 'B', [XValidate._v_InArray(['NEWWIN'])]); validate.AddValidator('_obj.menu_item_image', 'Image', 'B', [XValidate._v_MaxLength(2048)]); validate.AddValidator('_obj.menu_item_image_path', 'Image Path', 'B', [XValidate._v_MaxLength(2048)]); for(var i=0;i<menu_content.menu_items.length;i++){ var menu_item = menu_content.menu_items[i]; verrors = _.merge(verrors, validate.Validate('B', menu_item)); if (!_.isEmpty(verrors)) { Helper.GenError(req, res, -2, 'Error on item #'+(i+1).toString()+'\n'+verrors[''].join('\n')); return; } } //Save to database sql_ptypes = [dbtypes.BigInt]; sql_params = {menu_key: menu_key}; sql = 'update '+(module.schema?module.schema+'.':'')+'v_my_menu set menu_file_id=null where menu_key=@menu_key;'; sql += 'select menu_file_id from '+(module.schema?module.schema+'.':'')+'v_my_menu where menu_key=@menu_key;'; fields = []; datalockstr = ''; verrors = {}; appsrv.getDataLockSQL(req, model, fields, sql_ptypes, sql_params, verrors, function (datalockquery) { datalockstr += ' and ' + datalockquery; }); sql = Helper.ReplaceAll(sql, '%%%DATALOCKS%%%', datalockstr); if (!_.isEmpty(verrors)) { Helper.GenError(req, res, -2, verrors[''].join('\n')); return; } appsrv.ExecRecordset(req._DBContext, sql, sql_ptypes, sql_params, function (err, rslt) { if (err != null) { err.sql = sql; err.model = model; appsrv.AppDBError(req, res, err); return; } if(!rslt || !rslt.length || !rslt[0] || !rslt[0].length || !rslt[0][0]) return Helper.GenError(req, res, -99999, 'Invalid database result'); menu.menu_file_id = rslt[0][0].menu_file_id; //Save to disk fs.writeFile(funcs.getMenuFile(menu.menu_file_id), JSON.stringify(menu_content), 'utf8', function(err){ res.type('json'); res.end(JSON.stringify({ '_success': 1 })); }); }); return; } else return next(); }); }; exports.prettyMenu = function(menu_items, page_keys, media_keys, options){ options = _.extend({ text: true }, options); var rslt = '[\n'; var rsltarr = []; //Generate indexed list of menu items by ID var menu_items_by_id = {}; for(var i=0;i<menu_items.length;i++){ var menu_item = menu_items[i]; menu_item.collection_index = i; menu_items_by_id[menu_item.menu_item_id] = menu_item; if(!menu_item.menu_item_parent_id){ menu_item.menu_item_path_text = '/' + Helper.StripTags(menu_item.menu_item_text).trim() + '/'; menu_item.menu_item_collection_index_array = [menu_item.collection_index]; } } //Generate text path function getTextPath(menu_item){ if(!menu_item) return ''; if(!menu_item.menu_item_path_text){ menu_item.menu_item_parents = getTextPath(menu_items_by_id[menu_item.menu_item_parent_id]); menu_item.menu_item_path_text = menu_item.menu_item_parents + Helper.StripTags(menu_item.menu_item_text).trim() + '/'; if(!menu_item.menu_item_collection_index_array){ menu_item.menu_item_collection_index_array = menu_items_by_id[menu_item.menu_item_parent_id].menu_item_collection_index_array.concat([menu_item.collection_index]); } } menu_item.menu_item_path_array = (menu_item.menu_item_path_text||'').split('/'); return menu_item.menu_item_path_text; } //Clone menu_items menu_items = JSON.parse(JSON.stringify(menu_items||[])); //Get text paths _.each(menu_items, function(menu_item){ getTextPath(menu_item); }); if(options.text){ //Sort menu items menu_items.sort(function(a,b){ for(var i=0;i<a.menu_item_collection_index_array.length;i++){ if(b.menu_item_collection_index_array.length <= i) return 1; if(a.menu_item_collection_index_array[i] > b.menu_item_collection_index_array[i]) return 1; if(a.menu_item_collection_index_array[i] < b.menu_item_collection_index_array[i]) return -1; } if(a.menu_item_collection_index_array.length < b.menu_item_collection_index_array.length) return -1; return 0; }); } _.each(menu_items, function(menu_item){ //Delete non-essential info var menu_item_path = menu_item.menu_item_path_text; if(menu_item_path && (menu_item_path[menu_item_path.length-1]=='/')) menu_item_path = menu_item_path.substr(0, menu_item_path.length-1); var print_item = { menu_item: menu_item_path, type: menu_item.menu_item_type, }; var link_type = (menu_item.menu_item_link_type||'').toUpperCase(); var link_text = ''; if(link_type){ link_text = link_type + ' :: ' + menu_item.menu_item_link_dest; if(link_type=='PAGE'){ var page_key = parseInt(menu_item.menu_item_link_dest); if(page_key in page_keys) link_text = 'PAGE :: ' + page_keys[page_key].page_path; } else if(link_type=='MEDIA'){ let media_key = parseInt(menu_item.menu_item_link_dest); if(media_key in media_keys) link_text = 'MEDIA :: ' + media_keys[media_key].media_path; } } if (menu_item.menu_item_image) { let media_key = parseInt(menu_item.menu_item_image); if(media_key in media_keys) print_item.image = 'MEDIA :: ' + media_keys[media_key].media_path; } if(link_text){ print_item.link = link_text; if(menu_item.menu_item_link_target) print_item.link_target = menu_item.menu_item_link_target; } if(menu_item.menu_item_tag) print_item.tag = menu_item.menu_item_tag; if(menu_item.menu_item_style) print_item.css_style = menu_item.menu_item_style; if(menu_item.menu_item_class) print_item.css_class = menu_item.menu_item_class; //Add to array rsltarr.push(print_item); //Generate text rslt += ' { '; var first_key = true; for(var key in print_item){ if(!first_key) rslt += ', '; rslt += key + ': ' + JSON.stringify(print_item[key]); first_key = false; } rslt += ' },\n'; }); rslt += ']'; if(options.text) return rslt; return rsltarr; }; return exports; };