UNPKG

jsharmony-cms

Version:
532 lines (461 loc) 24.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 _ = require('lodash'); var async = require('async'); module.exports = exports = function(module, funcs){ var exports = {}; var _t = module._t; exports.validate_req = function (req, res, next) { var verb = req.method.toLowerCase(); if (!req.body) req.body = {}; var Q = req.query; var P = {}; if (req.body && ('data' in req.body)){ try{ P = JSON.parse(req.body.data); } catch(ex){ Helper.GenError(req, res, -4, 'Invalid Parameters'); return; } } var jsh = module.jsh; var appsrv = jsh.AppSrv; var XValidate = jsh.XValidate; var model = jsh.getModel(req, module.namespace + 'Branch_Validate'); if (!Helper.hasModelAction(req, model, 'B')) { Helper.GenError(req, res, -11, _t('Invalid Model Access')); return; } if (verb == 'get') { if (!appsrv.ParamCheck('Q', Q, ['&branch_id'])) { Helper.GenError(req, res, -4, 'Invalid Parameters'); return; } if (!appsrv.ParamCheck('P', P, [])) { Helper.GenError(req, res, -4, 'Invalid Parameters'); return; } var branch_id = Q.branch_id; //Check if item is defined var sql_params = { 'branch_id': branch_id }; var validate = new XValidate(); var verrors = {}; validate.AddValidator('_obj.branch_id', 'Branch ID', 'B', [XValidate._v_IsNumeric(), XValidate._v_Required()]); verrors = _.merge(verrors, validate.Validate('B', sql_params)); if (!_.isEmpty(verrors)) { Helper.GenError(req, res, -2, verrors[''].join('\n')); return; } funcs.validateBranchAccess(req, res, branch_id, 'R%', ['AUTHOR','PUBLISHER','WEBMASTER'], function(){ funcs.validate(req._DBContext, branch_id, function(err, rslt){ if(err){ if(err.sql || err.frontend_visible){ if(err.sql) err.model = model; return appsrv.AppDBError(req, res, err); } else{ rslt = { _success: 1, error_count: 1, branch_validate: { system: { system: { errors: [err.toString()] } } } }; } } rslt._success = 1; res.end(JSON.stringify(rslt)); }); }); } else { return next(); } }; exports.validate_logError = function(item_errors, branch_item_type, item, errtxt){ var key = item[branch_item_type+'_key']; var error_columns = []; if(module.BranchItems[branch_item_type] && module.BranchItems[branch_item_type].validate && module.BranchItems[branch_item_type].validate.error_columns) error_columns = module.BranchItems[branch_item_type].validate.error_columns; if(!(key in item_errors)) item_errors[key] = _.extend(_.pick(item, error_columns), { errors: [] }); item_errors[key].errors.push(errtxt); }; exports.validate_logSystemError = function(branchData, errtxt){ if(!branchData.branch_validate.system) branchData.branch_validate.system = { system: { errors: [] } }; branchData.branch_validate.system.system.errors.push(errtxt); }; exports.validate = function (dbcontext, branch_id, callback) { var jsh = module.jsh; var cms = module; var appsrv = jsh.AppSrv; var dbtypes = appsrv.DB.types; var sql_ptypes = [dbtypes.BigInt]; var sql_params = { 'branch_id': branch_id }; var branch_validate = {}; var branchData = { _DBContext: dbcontext, page_keys: {}, page_templates: null, component_templates: null, media_keys: {}, branch_id: branch_id, site_id: null, site_config: {}, template_variables: null, branch_validate: branch_validate, deployment: {}, }; async.waterfall([ //Get template_variables for branch function(cb){ var sql = 'select site_editor deployment_target_id, v_my_site.deployment_target_template_variables, v_my_branch_desc.site_id, deployment_target_publish_config, deployment_target_publish_path from {schema}.v_my_branch_desc left outer join {schema}.v_my_site on v_my_site.site_id = v_my_branch_desc.site_id left outer join {schema}.deployment_target on deployment_target.deployment_target_id = v_my_site.deployment_target_id where v_my_branch_desc.branch_id=@branch_id'; appsrv.ExecRow(dbcontext, funcs.replaceSchema(sql), sql_ptypes, sql_params, function (err, rslt) { if (err != null) { err.sql = sql; return cb(err); } if(!rslt || !rslt.length || !rslt[0]) return cb(Helper.NewError('No access to target revision', -11)); branchData.deployment_target_id = rslt[0].deployment_target_id; branchData.site_id = rslt[0].site_id; branchData.deployment.deployment_target_publish_path = rslt[0].deployment_target_publish_path || ''; //Template Variables var template_variables = {}; try{ if(rslt[0].deployment_target_template_variables) template_variables = _.extend(template_variables, JSON.parse(rslt[0].deployment_target_template_variables)); } catch(ex){ return cb('Publish Target has invalid deployment_target_template_variables: '+rslt[0].deployment_target_template_variables); } var deployment_target_publish_config = {}; try{ deployment_target_publish_config = funcs.parseDeploymentTargetPublishConfig(branchData.site_id, rslt[0].deployment_target_publish_config, 'publish'); } catch(ex){ return cb('Error parsing deployment_target_publish_config: '+ex.toString()); } //Get site config funcs.getSiteConfig('deployment', branchData.site_id, { }, function(err, siteConfig){ if(err) return cb(err); branchData.site_config = siteConfig || {}; funcs.parseTemplateVariables('publish', 'deployment', branchData.site_id, branchData.site_config, template_variables, deployment_target_publish_config, function(err, parsed_template_variables){ if(err) return cb(err); template_variables = parsed_template_variables; branchData.template_variables = template_variables; //Deployment Target Publish Params var publish_params = _.extend(JSON.parse(JSON.stringify(template_variables)), deployment_target_publish_config); branchData.publish_params = publish_params; return cb(); }); }); }); }, //Run onBeforeValidate functions function(cb){ async.eachOfSeries(cms.BranchItems, function(branch_item, branch_item_type, branch_item_cb){ if(!branch_item.validate) return branch_item_cb(); //Initialize errors array for this item branch_validate[branch_item_type] = {}; Helper.execif(branch_item.validate.onBeforeValidate, function(f){ branch_item.validate.onBeforeValidate(branch_validate[branch_item_type], branchData, f); }, branch_item_cb ); }, cb); }, //Load Custom Branch Data function (cb){ if(!module.Config.onValidate_LoadData) return cb(); return module.Config.onValidate_LoadData(jsh, branchData, branchData.template_variables, cb); }, //Get all branch data function(cb){ async.eachOfSeries(cms.BranchItems, function(branch_item, branch_item_type, branch_item_cb){ if(!branch_item.validate) return branch_item_cb(); //Run onValidate function Helper.execif(branch_item.validate.onValidate, function(f){ branch_item.validate.onValidate(branch_validate[branch_item_type], branchData, f); }, branch_item_cb ); }, cb); }, ], function(err){ if(err) return callback(err); var error_count = 0; _.map(branch_validate, function(item_errors){ error_count += _.keys(item_errors).length; }); var rslt = { _success: 1, error_count: error_count, branch_validate: _.pickBy(branch_validate, function(branch_items){ return !_.isEmpty(branch_items); }) }; return callback(null, rslt); }); }; exports.validate_getPages = function(item_errors, branchData, callback){ var jsh = module.jsh; var appsrv = jsh.AppSrv; var dbtypes = appsrv.DB.types; //Get all pages var sql = '\ select page_id,page_key,page_file_id,page_title,page_path,page_tags,page_author,page_template_id,page_template_path,page_filename,page_seo_title,page_seo_canonical_url,page_seo_metadesc,page_seo_keywords,page_review_sts,page_lang \ from '+(module.schema?module.schema+'.':'')+'page page where page_is_folder = 0 and page.page_id in (select page_id from '+(module.schema?module.schema+'.':'')+'branch_page where branch_id=@branch_id)\ '; var sql_ptypes = [dbtypes.BigInt]; var sql_params = { 'branch_id': branchData.branch_id }; appsrv.ExecRecordset(branchData._DBContext, sql, sql_ptypes, sql_params, function (err, rslt) { if (err != null) { err.sql = sql; return callback(err); } if(rslt && rslt[0]){ var page_paths = {}; _.each(rslt[0], function(page){ branchData.page_keys[page.page_key] = page; //Find duplicate page_path var page_path_upper = (page.page_path||'').trim().toUpperCase(); if(!page_path_upper) funcs.validate_logError(item_errors, 'page', page, 'Page ID '+page.page_id+' missing page_path'); else if(page_path_upper in page_paths) funcs.validate_logError(item_errors, 'page', page, 'Duplicate page_path: '+page.page_path); else page_paths[page_path_upper] = page; }); } return callback(); }); }; exports.validate_getMedia = function(item_errors, branchData, callback){ var jsh = module.jsh; var appsrv = jsh.AppSrv; var dbtypes = appsrv.DB.types; //Get all media var sql = '\ select media_id,media_key,media_file_id,media_desc,media_path,media_ext,media_width,media_height \ from '+(module.schema?module.schema+'.':'')+'media media where media_is_folder=0 and media.media_id in (select media_id from '+(module.schema?module.schema+'.':'')+'branch_media where branch_id=@branch_id)\ '; var sql_ptypes = [dbtypes.BigInt]; var sql_params = { 'branch_id': branchData.branch_id }; appsrv.ExecRecordset(branchData._DBContext, sql, sql_ptypes, sql_params, function (err, rslt) { if (err != null) { err.sql = sql; return callback(err); } if(rslt && rslt[0]){ var media_paths = {}; _.each(rslt[0], function(media){ branchData.media_keys[media.media_key] = media; //Find duplicate media_path var media_path_upper = (media.media_path||'').trim().toUpperCase(); if(!media_path_upper) funcs.validate_logError(item_errors, 'media', media, 'Media ID '+media.media_id+' missing media_path'); else if(media_path_upper in media_paths) funcs.validate_logError(item_errors, 'media', media, 'Duplicate media_path: '+media.media_path); else media_paths[media_path_upper] = media; }); } return callback(); }); }; exports.validate_page = function(item_errors, branchData, callback){ //Validate templates _.each(branchData.page_templates, function(page_template, page_template_id){ _.each(page_template.components, function(component, componentId){ if(componentId in branchData.component_templates){ funcs.validate_logSystemError(branchData, 'Page template "' + page_template.title + '" has an inline component "' + componentId + '" that is already defined as a site component.'); } }); }); //Get page file content async.eachOfSeries(branchData.page_keys, function(page, page_id, page_cb){ funcs.getClientPage(branchData._DBContext, page, null, branchData.site_id, { pageTemplates: branchData.page_templates }, function(err, clientPage){ if(err){ funcs.validate_logError(item_errors, 'page', page, err.toString()); return page_cb(); } if(!clientPage) return page_cb(null); page.compiled = clientPage.page; page.template = clientPage.template; var page_path = null; try{ page_path = funcs.getPageRelativePath(page); } catch(ex){ if(ex) funcs.validate_logError(item_errors, 'page', page, ex.toString()); } if(!page_path) funcs.validate_logError(item_errors, 'page', page, 'Page does not have a valid path'); var allContent = []; _.each(['css','header','footer','js'], function(key){ if(!page.template[key]) return; allContent['template '+key] = page.template[key]; }); _.each(['css','header','footer'], function(key){ if(!page.compiled[key]) return; allContent[key] = page.template[key]; }); if(page.compiled.content) for(let key in page.compiled.content) allContent[key + ' content'] = page.compiled.content[key]; for(let key in allContent){ funcs.replaceBranchURLs(allContent[key], { getMediaURL: function(media_key, thumbnail_id, branchData, getLinkContent){ if(!(media_key in branchData.media_keys)) throw new Error('<' + key + '>: Link to missing Media ID #'+media_key.toString()+': ...'+getLinkContent()+'...'); if(thumbnail_id){ if(!(branchData.site_config.media_thumbnails && branchData.site_config.media_thumbnails[thumbnail_id])) throw new Error('<' + key + '>: Media #'+media_key.toString()+' links to invalid Thumbnail ID: '+thumbnail_id+' ...'+getLinkContent()+'...'); } return ''; }, getPageURL: function(page_key, branchData, getLinkContent){ if(!(page_key in branchData.page_keys)) throw new Error('<' + key + '>: Link to missing Page ID #'+page_key.toString()+': ...'+getLinkContent()+'...'); return ''; }, onError: function(err){ funcs.validate_logError(item_errors, 'page', page, err.toString()); }, branchData: branchData, replaceComponents: true, removeClass: true }); } return page_cb(null); }); }, callback); }; exports.validate_media = function(item_errors, branchData, callback){ //Validate Media Paths async.eachOfSeries(branchData.media_keys, function(media, media_id, media_cb){ var media_path = null; try{ media_path = funcs.getMediaRelativePath(media); } catch(ex){ if(ex) funcs.validate_logError(item_errors, 'media', media, ex.toString()); } if(!media_path) funcs.validate_logError(item_errors, 'media', media, 'Media does not have a valid path'); return media_cb(null); }, callback); }; exports.validate_menu = function(item_errors, branchData, callback){ var jsh = module.jsh; var appsrv = jsh.AppSrv; var dbtypes = appsrv.DB.types; var menus = {}; var menu_configs = {}; async.waterfall([ //Get all menus function(cb){ var sql = 'select menu_id,menu_key,menu_file_id,menu_name,menu_tag \ from '+(module.schema?module.schema+'.':'')+'menu menu \ where menu.menu_id in (select menu_id from '+(module.schema?module.schema+'.':'')+'branch_menu where branch_id=@branch_id)'; var sql_ptypes = [dbtypes.BigInt]; var sql_params = { 'branch_id': branchData.branch_id }; appsrv.ExecRecordset(branchData._DBContext, sql, sql_ptypes, sql_params, function (err, rslt) { if (err != null) { err.sql = sql; return cb(err); } if(rslt && rslt[0]){ var menu_tags = {}; _.each(rslt[0], function(menu){ menus[menu.menu_id] = menu; //Find duplicate menu_tag var menu_tag_upper = (menu.menu_tag||'').trim().toUpperCase(); if(!menu_tag_upper) funcs.validate_logError(item_errors, 'menu', menu, 'Menu ID '+menu.menu_id+' missing menu_tag'); else if(menu_tag_upper in menu_tags) funcs.validate_logError(item_errors, 'menu', menu, 'Duplicate menu_tag: '+menu.menu_tag); else menu_tags[menu_tag_upper] = menu; }); } return cb(); }); }, //Check for missing menus function(cb){ if(branchData.site_config && branchData.site_config.menus){ _.each(branchData.site_config.menus, function(site_menu_config){ if(site_menu_config && site_menu_config.tag) menu_configs[site_menu_config.tag] = site_menu_config; }); } var menu_tags = {}; for(var key in menus){ var menu = menus[key]; menu_tags[menu.menu_tag] = menu; } _.each(menu_configs, function(menu_config, menu_tag){ if(!(menu_tag in menu_tags)) funcs.validate_logSystemError(branchData, 'A menu with menu tag "' + menu_tag + '" is required by the Site Config. Please add this missing menu to the site.'); }); return cb(); }, //Get menu file content function(cb){ async.eachOfSeries(menus, function(menu, menu_id, menu_cb){ funcs.getClientMenu(menu, function(err, menu_content){ if(err){ funcs.validate_logError(item_errors, 'menu', menu, err.toString()); return menu_cb(); } menu.menu_items = menu_content.menu_items||[]; //For each menu item var menu_config = menu_configs[menu.menu_tag]; var pretty_menu_items = funcs.prettyMenu(menu_content.menu_items, branchData.page_keys, branchData.media_keys, { text: false }); for(var i=0;i<menu.menu_items.length;i++){ var menu_item = menu.menu_items[i]; var pretty_menu_item = pretty_menu_items[i]; //Validate URLs if((menu_item.menu_item_link_type||'').toString()=='PAGE'){ var page_key = parseInt(menu_item.menu_item_link_dest); if(!(page_key in branchData.page_keys)) funcs.validate_logError(item_errors, 'menu', menu, 'Menu item "' + pretty_menu_item.menu_item + '" links to missing Page ID #'+page_key.toString()); else menu_item.menu_item_link_dest = branchData.page_keys[page_key]; } else if((menu_item.menu_item_link_type||'').toString()=='MEDIA'){ let media_key = parseInt(menu_item.menu_item_link_dest); if(!(media_key in branchData.media_keys)) funcs.validate_logError(item_errors, 'menu', menu, 'Menu item "' + pretty_menu_item.menu_item + '" links to missing Media ID #'+media_key.toString()); else menu_item.menu_item_link_dest = branchData.media_keys[media_key]; } //Validate Image URLs if (menu_item.menu_item_image) { let media_key = parseInt(menu_item.menu_item_image); if(!(media_key in branchData.media_keys)) funcs.validate_logError(item_errors, 'menu', menu, 'Menu item "' + pretty_menu_item.menu_item + '" image links to missing Media ID #'+media_key.toString()); else menu_item.menu_item_image = branchData.media_keys[media_key]; } //Validate Max Depth if(menu_config && menu_config.max_depth){ var menu_path = menu_item.menu_item_path.split('/'); var menu_item_depth = 0; _.each(menu_path, function(val){ if(val) menu_item_depth++; }); if(menu_item_depth > menu_config.max_depth) funcs.validate_logError(item_errors, 'menu', menu, 'Menu item "' + pretty_menu_item.menu_item + '" exceeds maximum menu depth of '+menu_config.max_depth.toString()); } } return menu_cb(); }); }, cb); }, ], callback); }; exports.validate_sitemap = function(item_errors, branchData, callback){ var jsh = module.jsh; var appsrv = jsh.AppSrv; var dbtypes = appsrv.DB.types; var sitemaps = {}; async.waterfall([ //Get all sitemaps function(cb){ var sql = 'select sitemap_id,sitemap_key,sitemap_file_id,sitemap_name,sitemap_type \ from '+(module.schema?module.schema+'.':'')+'sitemap sitemap \ where sitemap.sitemap_id in (select sitemap_id from '+(module.schema?module.schema+'.':'')+'branch_sitemap where branch_id=@branch_id)'; var sql_ptypes = [dbtypes.BigInt]; var sql_params = { 'branch_id': branchData.branch_id }; appsrv.ExecRecordset(branchData._DBContext, sql, sql_ptypes, sql_params, function (err, rslt) { if (err != null) { err.sql = sql; return cb(err); } if(rslt && rslt[0]){ var sitemap_types = {}; _.each(rslt[0], function(sitemap){ sitemaps[sitemap.sitemap_id] = sitemap; //Find duplicate sitemap_type var sitemap_type_upper = (sitemap.sitemap_type||'').trim().toUpperCase(); if(!sitemap_type_upper) funcs.validate_logError(item_errors, 'sitemap', sitemap, 'Sitemap ID '+sitemap.sitemap_id+' missing sitemap_type'); else if(sitemap_type_upper in sitemap_types) funcs.validate_logError(item_errors, 'sitemap', sitemap, 'Duplicate sitemap_type: '+sitemap.sitemap_type); else sitemap_types[sitemap_type_upper] = sitemap; }); } return cb(); }); }, //Get sitemap file content function(cb){ async.eachOfSeries(sitemaps, function(sitemap, sitemap_id, sitemap_cb){ funcs.getClientSitemap(sitemap, function(err, sitemap_content){ if(err){ funcs.validate_logError(item_errors, 'sitemap', sitemap, err.toString()); return sitemap_cb(); } if(!sitemap_content) return sitemap_cb(null); //Validate URLs sitemap.sitemap_items = sitemap_content.sitemap_items||[]; var pretty_sitemap_items = funcs.prettySitemap(sitemap_content.sitemap_items, branchData.page_keys, branchData.media_keys, { text: false }); for(var i=0;i<sitemap.sitemap_items.length;i++){ var sitemap_item = sitemap.sitemap_items[i]; var pretty_sitemap_item = pretty_sitemap_items[i]; if((sitemap_item.sitemap_item_link_type||'').toString()=='PAGE'){ var page_key = parseInt(sitemap_item.sitemap_item_link_dest); if(!(page_key in branchData.page_keys)){ funcs.validate_logError(item_errors, 'sitemap', sitemap, 'Sitemap item "' + pretty_sitemap_item.sitemap_item + '" links to missing Page ID #'+page_key.toString()); } else sitemap_item.sitemap_item_link_dest = branchData.page_keys[page_key]; } else if((sitemap_item.sitemap_item_link_type||'').toString()=='MEDIA'){ var media_key = parseInt(sitemap_item.sitemap_item_link_dest); if(!(media_key in branchData.media_keys)) funcs.validate_logError(item_errors, 'sitemap', sitemap, 'Sitemap item "' + pretty_sitemap_item.sitemap_item + '" links to missing Media ID #'+media_key.toString()); else sitemap_item.sitemap_item_link_dest = branchData.media_keys[media_key]; } } return sitemap_cb(); }); }, cb); }, ], callback); }; return exports; };