Name
sn_ace.ACEAppBuilderUtilV2
Description
No description available
Script
var ACEAppBuilderUtilV2 = Class.create();
ACEAppBuilderUtilV2.prototype = {
initialize: function() {
this.CONSTANTS = new ACEAppBuilderMetaData();
this.TABLE = this.CONSTANTS.TABLES;
this.UXF_TABLE = this.CONSTANTS.UXF_TABLES;
this.GLOBAL = new global.ACEAppBuilderGlobalScopedUtil();
this.CREATE_DEMO_DATA = false;
this.demoUtils = new ACEDemoDataUtils();
this.AIS = this.CONSTANTS.AIS;
this.EXPERIENCE_ID = null;
this.VIRTUAL_GR = null;
this.gr_counter = 0;
this.CAN_READ = this.hasReadAccess();
this.DEFAULT_FETCH_MODE = 'dfs';
this.SUPPORTED_FETCH_MODES = ['dfs', 'bfs'];
this.ACE_DEFAULT_APP_CONFIG = "f0d91c8a87650110a4a2c077f6cb0b3e";
this.ACE_CONTROL_TYPE = "c70afc5453db5110c733ddeeff7b1281";
this.GENERIC_CONTENT_TYPE = "4cb9f85453db5110c733ddeeff7b12b4";
this.REUSABLE_CONTENT_TYPE = "27ea3c9453db5110c733ddeeff7b126a";
this.REUSABLE_DEFINTION_CONTENT_TYPE = "5db4a12153975110c733ddeeff7b1277";
this.MODAL_CONTAINER_TYPE = "6aee588753931110c733ddeeff7b126a";
this.CONTENT_SLOT_TYPE = "306b292d53006110c733ddeeff7b1237";
this.DATA_BLOCK_CONTENT_TYPE = 'a886aedfc3dde11044104fb9c840ddb7';
this.BLOCK_CONTEXT_AS_LABEL = "$(blockcontext";
this.TRANSLATED_TEXT_PREFIX = 'TRT: ';
},
upsertReusableBlock: function(blockName, blockParameters, appConfigId) {
var reusableBlocKGr = new GlideRecord(this.TABLE.CONTENT_BLOCK);
var blockId = null;
reusableBlocKGr.addQuery("reusable_block", true);
reusableBlocKGr.addQuery("name", blockName);
reusableBlocKGr.query();
if (reusableBlocKGr.next()) {
reusableBlocKGr.variable["block_variables"] = JSON.stringify(blockParameters);
blockId = reusableBlocKGr.getUniqueValue();
reusableBlocKGr.update();
} else {
reusableBlocKGr.reusable_block = true;
reusableBlocKGr.name = blockName;
reusableBlocKGr.type = this.REUSABLE_DEFINTION_CONTENT_TYPE;
reusableBlocKGr.document_table = this.TABLE.APP_CONFIG;
reusableBlocKGr.document_id = appConfigId || this.TABLE.ACE_DEFAULT_APP_CONFIG;
reusableBlocKGr.active = true;
reusableBlocKGr.visibility = "answer=true;"
reusableBlocKGr.variable["block_variables"] = JSON.stringify(blockParameters);
blockId = reusableBlocKGr.insert();
}
return blockId;
},
_updateACEContentBlocks: function(contentTree, documentTable, documentId) {
var resuableBlockInstanceToDefBlockIdMap = this._getActualBlockIdMap(contentTree);
var contentBlocksResp = this._getAllContentBlockData(contentTree);
var allContentBlockData = contentBlocksResp.blockData;
var reusableBlockIds = contentBlocksResp.reusableBlockIds;
var allContentBlockIds = Object.keys(allContentBlockData);
/* Remove all content blocks that are existing with in the documentId (acepage) and not in the input block ids */
var contentBlocksTobeDeletedGr = new GlideRecord(this.TABLE.CONTENT_BLOCK);
contentBlocksTobeDeletedGr.addEncodedQuery('document_id=' + documentId +
'^document_table=' + documentTable +
'^NQdocument_table=sn_ace_content_block^document_idIN' + reusableBlockIds.join(','));
contentBlocksTobeDeletedGr.addQuery("sys_id", 'NOT IN', allContentBlockIds);
contentBlocksTobeDeletedGr.query();
var resuableBlockInstancesToDelete = [];
while (contentBlocksTobeDeletedGr.next()) {
if (contentBlocksTobeDeletedGr.getValue("type") == this.REUSABLE_CONTENT_TYPE) {
resuableBlockInstancesToDelete.push(contentBlocksTobeDeletedGr.getUniqueValue());
}
}
contentBlocksTobeDeletedGr.query();
contentBlocksTobeDeletedGr.deleteMultiple();
if (resuableBlockInstancesToDelete && resuableBlockInstancesToDelete.length > 0) {
var resuableBlockInstancesToDeleteGr = new GlideRecord(this.TABLE.CONTENT_BLOCK);
resuableBlockInstancesToDeleteGr.addQuery("document_table", this.TABLE.CONTENT_BLOCK);
resuableBlockInstancesToDeleteGr.addQuery("document_id", "IN", resuableBlockInstancesToDelete.join(","));
resuableBlockInstancesToDeleteGr.query();
resuableBlockInstancesToDeleteGr.deleteMultiple();
}
/* Upsert the remaining content blocks based on the input content block data */
var visitedBlockIds = [],
blockData = {};
var allContentBlocksGr = new GlideRecord(this.TABLE.CONTENT_BLOCK);
allContentBlocksGr.addEncodedQuery('document_id=' + documentId +
'^document_table=' + documentTable +
'^NQdocument_table=sn_ace_content_block^document_idIN' + reusableBlockIds.join(','));
allContentBlocksGr.query();
while (allContentBlocksGr.next()) {
visitedBlockIds.push(allContentBlocksGr.getUniqueValue());
}
/* Create new Block Ids that are not available in the above query */
var remainingBlockIds = [];
allContentBlockIds.forEach(function(cbId) {
if (visitedBlockIds.indexOf(cbId) == -1) {
remainingBlockIds.push(cbId);
}
});
var _self = this;
remainingBlockIds.forEach(function(cbId) {
var cbGr = new GlideRecord(_self.TABLE.CONTENT_BLOCK);
cbGr.document_id = documentId;
cbGr.document_table = documentTable;
cbGr.name = allContentBlockData[cbId].name;
cbGr.label = allContentBlockData[cbId].label || "";
cbGr.active = allContentBlockData[cbId].active;
cbGr.title = allContentBlockData[cbId].title;
cbGr.subtitle = allContentBlockData[cbId].subtitle;
cbGr.post_script = allContentBlockData[cbId].post_script;
cbGr.lazy_script = allContentBlockData[cbId].lazy_script;
cbGr.lazy_load = allContentBlockData[cbId].lazy_load;
cbGr.visibility = allContentBlockData[cbId].visibility;
cbGr.order = allContentBlockData[cbId].order;
cbGr.style = JSON.stringify(allContentBlockData[cbId].style);
cbGr.element_id = allContentBlockData[cbId].elementId;
cbGr.slotname = allContentBlockData[cbId].slotname;
if (allContentBlockData[cbId].hasOwnProperty('enableLazyLoad'))
cbGr.lazy_load = allContentBlockData[cbId].enableLazyLoad;
cbGr.props_overrides = JSON.stringify(allContentBlockData[cbId].props_overrides);
if (allContentBlockData[cbId].reusableBlockId) {
var persistedBlockId = allContentBlockData[allContentBlockData[cbId].reusableBlockId].newBlockId || allContentBlockData[cbId].reusableBlockId;
cbGr.document_id = persistedBlockId;
cbGr.document_table = _self.TABLE.CONTENT_BLOCK;
}
if (!allContentBlockData[cbId].type.id) {
switch (allContentBlockData[cbId].type.route) {
case "ace-control":
cbGr.type = _self.ACE_CONTROL_TYPE;
break;
case "generic":
cbGr.type = _self.GENERIC_CONTENT_TYPE;
break;
case "reusable":
cbGr.type = _self.REUSABLE_CONTENT_TYPE;
break;
case "modal-container":
cbGr.type = _self.MODAL_CONTAINER_TYPE;
break;
case "content-slot":
cbGr.type = _self.CONTENT_SLOT_TYPE;
break;
}
} else {
cbGr.type = allContentBlockData[cbId].type.id;
}
var parentBlockId = resuableBlockInstanceToDefBlockIdMap[allContentBlockData[cbId].parent_block_id] || allContentBlockData[cbId].parent_block_id;
cbGr.parent = parentBlockId
if (allContentBlockData[cbId].props_details) {
for (var gVariable in allContentBlockData[cbId].props_details) {
cbGr.variable[gVariable] = (gVariable == 'component_properties' ||
gVariable == 'block_parameters' || gVariable == "aria_properties" ||
gVariable == 'items' || gVariable == 'output_map') &&
typeof allContentBlockData[cbId].props_details[gVariable] != 'string' ?
JSON.stringify(allContentBlockData[cbId].props_details[gVariable]) : allContentBlockData[cbId].props_details[gVariable];
}
}
var grId = cbGr.insert();
allContentBlockData[cbId].newBlockId = grId;
allContentBlockData[grId] = allContentBlockData[cbId];
});
/*Again query all the blocks for the given document and update the blocks along with the new parent block ids where necessary */
var translatableStrings = [];
var allComponentProperties = this.GLOBAL.getCachedMacroponentCBProperties() || '';
if (allComponentProperties && allComponentProperties.componentProperties) {
allComponentProperties = allComponentProperties.componentProperties;
var componentKeys = Object.keys(allComponentProperties);
if (componentKeys && componentKeys.length > 0) {
var firstComponentProp = allComponentProperties[componentKeys[0]];
if (!firstComponentProp.translatableStringMap) {
allComponentProperties = this.getCachedProperties(true).componentProperties;
}
}
} else {
allComponentProperties = this.getCachedProperties(true).componentProperties;
}
allContentBlocksGr.query();
while (allContentBlocksGr.next()) {
var blockId = allContentBlocksGr.getUniqueValue();
blockData = allContentBlockData[blockId];
allContentBlocksGr.name = blockData.name;
allContentBlocksGr.active = blockData.active;
allContentBlocksGr.order = blockData.order;
allContentBlocksGr.label = blockData.label || "";
allContentBlocksGr.title = blockData.title;
allContentBlocksGr.subtitle = blockData.subtitle;
allContentBlocksGr.post_script = blockData.post_script;
allContentBlocksGr.lazy_script = blockData.lazy_script;
allContentBlocksGr.lazy_load = blockData.lazy_load;
allContentBlocksGr.visibility = blockData.visibility;
allContentBlocksGr.style = JSON.stringify(blockData.style);
allContentBlocksGr.element_id = blockData.elementId;
allContentBlocksGr.slotname = blockData.slotname;
if (blockData.hasOwnProperty('enableLazyLoad'))
allContentBlocksGr.lazy_load = blockData.enableLazyLoad;
allContentBlocksGr.props_overrides = JSON.stringify(blockData.props_overrides);
if (blockData.parent_block_id && allContentBlockData[blockData.parent_block_id] && allContentBlockData[blockData.parent_block_id].newBlockId) {
blockData.parent_block_id = allContentBlockData[blockData.parent_block_id].newBlockId;
}
if (blockData.type) {
if (!blockData.type.id) {
switch (blockData.type.route) {
case "ace-control":
allContentBlocksGr.type = this.ACE_CONTROL_TYPE;
break;
case "generic":
allContentBlocksGr.type = this.GENERIC_CONTENT_TYPE;
break;
case "reusable":
allContentBlocksGr.type = this.REUSABLE_CONTENT_TYPE;
break;
case "modal-container":
allContentBlocksGr.type = this.MODAL_CONTAINER_TYPE;
break;
case "content-slot":
allContentBlocksGr.type = this.CONTENT_SLOT_TYPE;
break;
}
} else {
allContentBlocksGr.type = blockData.type.id;
}
}
var parentBlockId = resuableBlockInstanceToDefBlockIdMap[blockData.parent_block_id] || blockData.parent_block_id;
allContentBlocksGr.parent = parentBlockId;
// Change For RB translation
if (blockData.type.route == 'reusable') {
var reusableBlockData = this.getReusableBlockDataForTranslations(blockData.props_details.block_id);
if (reusableBlockData.block_variables && blockData.props_details.block_parameters) {
var parsedComponentProps;
if (typeof blockData.props_details.block_parameters == 'object') {
parsedComponentProps = blockData.props_details.block_parameters;
} else {
parsedComponentProps = JSON.parse(blockData.props_details.block_parameters);
}
var translatableStringMap = this.getReusableBlockTranslatableFields(reusableBlockData.block_variables);
var translatableStringKeys = Object.keys(translatableStringMap);
if (translatableStringKeys && translatableStringKeys.length > 0) {
var propTranslatableStrings = this.fetchTranslatableStringsFromProps(translatableStringKeys, parsedComponentProps, translatableStringMap);
this.mergeTranslatableStrings(translatableStrings, propTranslatableStrings);
}
}
if (reusableBlockData && reusableBlockData.required_translation) {
var reusableBlockTranslatableString = JSON.parse(reusableBlockData.required_translation) || [];
if (reusableBlockTranslatableString.length > 0) {
for (var key in reusableBlockTranslatableString) {
if (translatableStrings.indexOf(reusableBlockTranslatableString[key]) === -1)
translatableStrings.push(reusableBlockTranslatableString[key]);
}
}
}
}
if (blockData.title && translatableStrings.indexOf(blockData.title) === -1) {
blockData.title = this.removeTranslationPrefix(blockData.title);
allContentBlocksGr.title = blockData.title;
translatableStrings.push(blockData.title);
}
if (blockData.subtitle && translatableStrings.indexOf(blockData.subtitle) === -1) {
blockData.subtitle = this.removeTranslationPrefix(blockData.subtitle);
allContentBlocksGr.subtitle = blockData.subtitle;
translatableStrings.push(blockData.subtitle);
}
if (blockData && blockData.type && blockData.type.props && blockData.props_details) {
blockData.type.props.forEach(function(prop) {
if (prop.type == 'translated_text') {
if (translatableStrings.indexOf(blockData.props_details[prop.element]) === -1) {
blockData.props_details[prop.element] = _self.removeTranslationPrefix(blockData.props_details[prop.element])
translatableStrings.push(blockData.props_details[prop.element]);
}
}
})
}
if (blockData.props_details) {
for (var gVariable in blockData.props_details) {
allContentBlocksGr.variable[gVariable] = (gVariable == 'component_properties' ||
gVariable == 'block_parameters' ||
gVariable == "aria_properties" ||
gVariable == "items" ||
gVariable == 'output_map') &&
typeof blockData.props_details[gVariable] != 'string' ?
JSON.stringify(blockData.props_details[gVariable]) : blockData.props_details[gVariable];
}
if (blockData.props_details.component_name) {
if (allComponentProperties && allComponentProperties[blockData.props_details.component_name] && allComponentProperties[blockData.props_details.component_name].translatableStringMap) {
var translatableProps = allComponentProperties[blockData.props_details.component_name].translatableStringMap;
if (translatableProps) {
var parsedComponentProps = '';
if (typeof blockData.props_details.component_properties == 'object') {
parsedComponentProps = blockData.props_details.component_properties;
} else {
parsedComponentProps = JSON.parse(blockData.props_details.component_properties);
}
var translatableStringKeys = Object.keys(translatableProps);
if (translatableStringKeys && translatableStringKeys.length > 0) {
var propTranslatableStrings = this.fetchTranslatableStringsFromProps(translatableStringKeys, parsedComponentProps, translatableProps);
this.mergeTranslatableStrings(translatableStrings, propTranslatableStrings);
}
}
}
}
}
if (blockData.reusableBlockId) {
var persistedBlockId = allContentBlockData[blockData.reusableBlockId].newBlockId || blockData.reusableBlockId;
allContentBlocksGr.document_id = persistedBlockId;
allContentBlocksGr.document_table = _self.TABLE.CONTENT_BLOCK;
}
allContentBlocksGr.update();
}
if (translatableStrings && translatableStrings.length > 0) {
var pageMacroponentTranslationUpdateInfo = {};
if (documentTable == "sn_ace_page") {
pageMacroponentTranslationUpdateInfo = this.GLOBAL.updatePageMacroponentTransLation(allContentBlocksGr.document_id, translatableStrings)
}
this.updateReusableBlockAndPageTranslations(translatableStrings, documentId, documentTable, pageMacroponentTranslationUpdateInfo);
}
var pageId = documentTable == "sn_ace_page" ? documentId : "";
var blockId = documentTable == "sn_ace_page" ? "" : documentId;
return this._getContentBlockMetaData(pageId, blockId);
},
updateReusableBlockAndPageTranslations: function(translatableStrings, documentId, documentTable, pageMacroponentTranslationUpdateInfo) {
if (documentTable == this.TABLE.CONTENT_BLOCK) {
var gr = new GlideRecord(this.TABLE.CONTENT_BLOCK);
gr.get(documentId);
gr.required_translations = JSON.stringify(translatableStrings);
gr.update();
} else if (pageMacroponentTranslationUpdateInfo && pageMacroponentTranslationUpdateInfo.requireUpdate) {
var cbGr = new GlideRecord('sn_ace_page');
cbGr.get(documentId);
cbGr.required_translations = pageMacroponentTranslationUpdateInfo.required_translations;
cbGr.update();
}
},
getReusableBlockDataForTranslations: function(blockId) {
var result = {};
var gr = new GlideRecord(this.TABLE.CONTENT_BLOCK);
gr.get('sys_id', blockId);
result.block_variables = JSON.parse(gr.variable["block_variables"])
result.required_translation = gr.getValue('required_translations')
return result;
},
getReusableBlockTranslatableFields: function(block_variables) {
var _self = this;
var translatableStringMap = {};
if (block_variables && block_variables.length) {
block_variables.forEach(function(block_variable, index) {
if (block_variable.typeMetadata && block_variable.fieldType == 'json') {
var translatableProp = _self.getTranslatableProp(block_variable.typeMetadata, 'translatable', '');
if (translatableProp && translatableProp.length > 0) {
translatableStringMap[block_variable.name] = translatableProp;
}
} else if (block_variable && block_variable.typeMetadata && block_variable.typeMetadata.translatable) {
translatableStringMap[block_variable.name] = block_variable.defaultValue;
}
});
}
return translatableStringMap;
},
fetchTranslatableStringsFromProps: function(translatableStringKeys, parsedComponentProps, translatableProps) {
var translatableStrings = [];
var _self = this;
translatableStringKeys.forEach(function(translatableStringKey) {
if (translatableStringKey in parsedComponentProps) {
if (typeof parsedComponentProps[translatableStringKey] == 'object' &&
typeof translatableProps[translatableStringKey] == 'object') {
var allKeys = translatableProps[translatableStringKey];
if (allKeys && parsedComponentProps[translatableStringKey] && Array.isArray(parsedComponentProps[translatableStringKey])) {
parsedComponentProps[translatableStringKey].forEach(function(props) {
allKeys.forEach(function(key) {
if (key in props) {
if (translatableStrings.indexOf(props[key]) === -1 && !props[key].includes(_self.BLOCK_CONTEXT_AS_LABEL)) {
translatableStrings.push(props[key]);
}
}
})
});
} else {
allKeys.forEach(function(key) {
if (key in parsedComponentProps[translatableStringKey]) {
if (translatableStrings.indexOf(parsedComponentProps[translatableStringKey][key]) === -1 && !parsedComponentProps[translatableStringKey][key].includes(_self.BLOCK_CONTEXT_AS_LABEL)) {
translatableStrings.push(parsedComponentProps[translatableStringKey][key]);
}
}
})
}
} else if (translatableStrings.indexOf(parsedComponentProps[translatableStringKey]) === -1 && !parsedComponentProps[translatableStringKey].includes(_self.BLOCK_CONTEXT_AS_LABEL)) {
translatableStrings.push(parsedComponentProps[translatableStringKey]);
}
}
});
return translatableStrings;
},
mergeTranslatableStrings: function(translatableStrings, propTranslatableStrings) {
if (propTranslatableStrings && propTranslatableStrings.length > 0) {
propTranslatableStrings.forEach(function(param) {
if (translatableStrings.indexOf(param) === -1) {
translatableStrings.push(param);
}
});
}
return translatableStrings;
},
_getContentSlottedNodes: function(contentTree, contentSlottedNodes, reusableBlockId) {
var self = this;
if (contentTree.children) {
contentTree.children.forEach(function(node) {
if (node.type && node.type.route == "content-slot" && node.children) {
node.children.forEach(function(childItem) {
childItem.reusableBlockId = reusableBlockId;
contentSlottedNodes.push(childItem);
});
} else {
self._getContentSlottedNodes(node, contentSlottedNodes, reusableBlockId);
}
});
}
},
_getActualBlockIdMap: function(contentTree) {
var result = {};
var children = contentTree;
while (children && children.length > 0) {
var nextChildren = [];
children.forEach(function(childItem) {
result[childItem.block_id] = childItem.actual_block_id || childItem.block_id;
nextChildren = nextChildren.concat(childItem.children);
});
children = nextChildren;
}
return result;
},
_getReusableBlockChildren: function(contentTree, existingChildren) {
var children = existingChildren || [];
if (contentTree.props_details && contentTree.props_details.clone + "" == "true") {
children = children.concat(contentTree.children);
} else {
var contentSlottedNodes = [];
this._getContentSlottedNodes(contentTree, contentSlottedNodes, contentTree.block_id);
children = children.concat(contentSlottedNodes);
}
return children;
},
updateBlockData: function(childItem, blockData) {
cbData = {};
Object.keys(childItem).forEach(function(cbKey) {
if (cbKey != "children") {
cbData[cbKey] = childItem[cbKey];
}
});
blockData[childItem.block_id] = cbData;
if (childItem.reusableBlockId) {
blockData[childItem.block_id].reusableBlockId = childItem.reusableBlockId;
}
},
_getAllContentBlockData: function(contentTree, updatedBlocks) {
var blockData = {};
var contentTreeArr = [];
var children = [],
nextlevelChildren = [],
reusableBlockIds = [];
var self = this;
cbData = {};
if (!contentTree) {
return {
"blockData": {},
"reusableBlockIds": []
};
}
if (!Array.isArray(contentTree))
contentTreeArr.push(contentTree);
else
contentTreeArr = contentTree;
contentTreeArr.forEach(function(cb) {
if ((!cb.type || cb.type.route != "reusable")) {
children = children.concat(cb.children);
} else if (cb.type && cb.type.route == "reusable") {
children = self._getReusableBlockChildren(cb, children);
reusableBlockIds.push(cb.block_id);
}
self.updateBlockData(cb, blockData);
});
while (children && children.length > 0) {
nextlevelChildren = [];
children.forEach(function(childItem) {
if (updatedBlocks && updatedBlocks[childItem.block_id]) {
blockData[childItem.block_id] = updatedBlocks[childItem.block_id];
} else {
self.updateBlockData(childItem, blockData);
}
if (childItem.children) {
if (childItem.type && childItem.type.route && childItem.type.route == "reusable") {
reusableBlockIds.push(childItem.block_id);
var reusbaleBlockChildren = self._getReusableBlockChildren(childItem, []);
if (reusbaleBlockChildren && reusbaleBlockChildren.length)
nextlevelChildren = nextlevelChildren.concat(reusbaleBlockChildren);
} else if (childItem.reusableBlockId) {
childItem.children.forEach(function(chItem) {
chItem.reusableBlockId = childItem.reusableBlockId;
});
nextlevelChildren = nextlevelChildren.concat(childItem.children);
} else {
nextlevelChildren = nextlevelChildren.concat(childItem.children);
}
}
});
children = nextlevelChildren;
}
return {
"blockData": blockData,
"reusableBlockIds": reusableBlockIds
};
},
_getVirtualGrInstance: function(cbId) {
if (!cbId) return null;
var gr = new GlideRecord(this.TABLE.CONTENT_BLOCK);
if (gr.get(cbId)) {
return gr;
}
return null;
},
_cachedScriptEvaluator: function(grInstance, scriptField, scriptValue, context) {
try {
if (!grInstance || !scriptValue) return context ? context : true;
grInstance.setValue(scriptField, scriptValue);
var evaluator = new GlideScopedEvaluator();
if (context) {
evaluator.putVariable('context', context);
evaluator.evaluateScript(grInstance, scriptField, null);
} else {
evaluator.putVariable('answer', null);
evaluator.putVariable('current', grInstance);
evaluator.evaluateScript(grInstance, scriptField, null);
return evaluator.getVariable('answer');
}
} catch (e) {
gs.log('ACEAppBuilder: Exception executing script of cached content block, ' + e);
}
},
_scriptEvaluator: function(grInstance, context, fieldName, pageParams, blockcontext, dataParams) {
try {
if (!fieldName || !grInstance.getValue(fieldName))
return context;
var evaluator = new GlideScopedEvaluator();
evaluator.putVariable('context', context);
evaluator.putVariable('pageParams', pageParams);
evaluator.putVariable('blockcontext', blockcontext);
evaluator.putVariable('dataParams', dataParams);
return evaluator.evaluateScript(grInstance, fieldName, null);
} catch (e) {
gs.error('ACEAppBuilder: Exception executing script of content block, ' + e.toString());
}
},
_evaluatePostScript: function(grInstance, context, pageParams, blockcontext, dataParams) {
this._scriptEvaluator(grInstance, context, 'post_script', pageParams, blockcontext, dataParams);
},
_evaluateLazyScript: function(grInstance, context, pageParams, blockcontext, dataParams) {
return this._scriptEvaluator(grInstance, context, 'lazy_script', pageParams, blockcontext, dataParams);
},
_visibilityEvaluator: function(grInstance, pageParams, blockcontext, dataParams) {
try {
if (!grInstance.getValue('visibility'))
return true;
var evaluator = new GlideScopedEvaluator();
evaluator.putVariable('answer', null);
evaluator.putVariable('current', grInstance);
evaluator.putVariable('pageParams', pageParams);
evaluator.putVariable('blockcontext', blockcontext);
evaluator.putVariable('dataParams', dataParams);
evaluator.evaluateScript(grInstance, 'visibility', null);
return evaluator.getVariable('answer');
} catch (e) {
gs.error('ACEAppBuilder: Exception executing script of content block, ' + e.toString());
return false;
}
},
_getValuePairsFromClonedRecord: function(table, sysId, fields) {
try {
if (!table || !sysId || !this.CAN_READ) return {};
var valuePairs = {};
var gr = new GlideRecord(table);
if (gr.get(sysId)) {
fields.forEach(function(field) {
valuePairs[field] = gr.getValue(field);
});
return valuePairs;
}
return {};
} catch (e) {
return {};
}
},
_grRecordCreator: function(table, valuePairs, cloneInfo) {
try {
var clonedValuePairs = {},
field;
if (cloneInfo && cloneInfo.sys_id && Array.isArray(cloneInfo.fields) && cloneInfo.fields.length > 0) {
clonedValuePairs = this._getValuePairsFromClonedRecord(table, cloneInfo.sys_id, cloneInfo.fields);
}
var gr = new GlideRecord(table);
gr.initialize();
gr.sys_scope = gs.getCurrentApplicationId();
for (field in clonedValuePairs) {
gr[field] = clonedValuePairs[field];
}
for (field in valuePairs) {
if (field == 'props_details' && table == this.TABLE.CONTENT_BLOCK) {
for (var i in valuePairs.props_details) {
gr.variable[i] = valuePairs.props_details[i];
}
} else
gr[field] = valuePairs[field];
}
return gr.insert();
} catch (e) {
gs.error("ACEAppBuilderUtil: Error in Glide Record Creation: " + e.toString());
return null;
}
},
_updateMacroponentProp: function(macroponentId, field, element, inputValueProp, newValue) {
try {
var _self = this;
var gr = new GlideRecord(this.UXF_TABLE.MACROPONENT);
if (gr.get(macroponentId)) {
var fieldData = gr.getValue(field);
fieldData = JSON.parse(fieldData);
if (Array.isArray(fieldData)) {
for (var i = 0; i < fieldData.length; i++) {
var dataBroker = fieldData[i];
if (dataBroker.elementId == element && dataBroker.inputValues && dataBroker.inputValues[inputValueProp]) {
dataBroker.inputValues[inputValueProp].value = newValue.toString();
var input = {};
input[field] = JSON.stringify(fieldData, null, 1);
_self.GLOBAL.updateRecord(_self.UXF_TABLE.MACROPONENT, gr.getUniqueValue(), input);
}
}
}
return true;
}
} catch (e) {
return null;
}
},
createPage: function(appConfigId, name, aceAppSysId, template, pageId) {
var screenTypeId = this.GLOBAL._grRecordCreator(this.UXF_TABLE.SCREEN_TYPE, {
'name': name
}, {
'sys_id': this._getConstantsForPage(template).SCREEN_TYPE,
'fields': this.CONSTANTS.CLONE_FIELDS.SCREEN_TYPE
});
var macroponentId = this._createMacroponent(name, template);
var screenId = this._createScreen(macroponentId, appConfigId, screenTypeId, name, template);
var appRouteId = this._createRoute(appConfigId, screenTypeId, name, template);
// TODO: Have segregation between methods that create UXF artifacts and ACE artifacts
if (pageId) {
if (!this._updateMacroponentProp(macroponentId, 'data', this.CONSTANTS.BASE_UXF_RECORDS.SECTIONS_DATA_BROKER_NAME, 'page_route', pageId)) {
gs.addErrorMessage(gs.getMessage('Please update the macroponent prop value via UIB'));
}
if (!this._updateMacroponentProp(macroponentId, 'data', this.CONSTANTS.BASE_UXF_RECORDS.ACE_PAGE_INIT_DATA_BROKER_NAME, 'page_route', pageId)) {
gs.addErrorMessage(gs.getMessage('Please update the macroponent prop value via UIB'));
}
// FIXME: Remove references to hardcoded template names
if (template != 'Home')
this._createContentBlocks(pageId, this.CONSTANTS.DEFAULT_CONTENT_BLOCKS);
}
return {
route: appRouteId,
screenType: screenTypeId,
screen: screenId,
macroponent: macroponentId
};
},
_createRoute: function(appConfig, screenType, name, template) {
var cloneInfo = {
'sys_id': this._getConstantsForPage(template).ROUTE,
'fields': this.CONSTANTS.CLONE_FIELDS.APP_ROUTE
};
var payload = {
'name': name,
'app_config': appConfig,
'screen_type': screenType,
'route_type': name.toLowerCase()
};
return this.GLOBAL._grRecordCreator(this.UXF_TABLE.APP_ROUTE, payload, cloneInfo);
},
_cloneClientScripts: function(baseMacroponentId, newMacroponentId) {
var clientScriptMaps = [];
try {
var _self = this;
var gr = new GlideRecord(_self.UXF_TABLE.CLIENT_SCRIPT);
gr.addQuery('macroponent', baseMacroponentId);
gr.query();
while (gr.next()) {
var newClientScriptId = this.GLOBAL._grRecordCreator(_self.UXF_TABLE.CLIENT_SCRIPT, {
'macroponent': newMacroponentId,
'name': gr.getValue('name'),
'type': gr.getValue('type'),
'script': gr.getValue('script'),
});
clientScriptMaps.push({
'findId': gr.getValue('sys_id'),
'replaceWithId': newClientScriptId
});
}
return clientScriptMaps;
} catch (e) {
return clientScriptMaps;
}
},
_replaceAll: function(str, find, replace) {
return str.replace(new RegExp(find, 'g'), replace);
},
_createMacroponent: function(name, template) {
var _self = this;
var cloneInfo = {
'sys_id': _self._getConstantsForPage(template).MACROPONENT,
'fields': _self.CONSTANTS.CLONE_FIELDS.MACROPONENT
};
var macroponentId = this.GLOBAL._grRecordCreator(this.UXF_TABLE.MACROPONENT, {
'name': name
}, cloneInfo);
var clientScriptMaps = _self._cloneClientScripts(this._getConstantsForPage(template).MACROPONENT, macroponentId);
//postCleanUp
var gr = new GlideRecord(_self.UXF_TABLE.MACROPONENT);
if (gr.get(macroponentId)) {
var postUpdateComposition = {
'composition': gr.getValue('composition'),
'data': gr.getValue('data'),
'state_properties': gr.getValue('state_properties'),
'internal_event_mappings': gr.getValue('internal_event_mappings'),
};
clientScriptMaps.forEach(function(clientScriptMap) {
postUpdateComposition.composition = _self._replaceAll(postUpdateComposition.composition, clientScriptMap.findId, clientScriptMap.replaceWithId);
postUpdateComposition.data = _self._replaceAll(postUpdateComposition.data, clientScriptMap.findId, clientScriptMap.replaceWithId);
postUpdateComposition.state_properties = _self._replaceAll(postUpdateComposition.state_properties, clientScriptMap.findId, clientScriptMap.replaceWithId);
postUpdateComposition.internal_event_mappings = _self._replaceAll(postUpdateComposition.internal_event_mappings, clientScriptMap.findId, clientScriptMap.replaceWithId);
});
//gr.update();
_self.GLOBAL.updateRecord(_self.UXF_TABLE.MACROPONENT, macroponentId, postUpdateComposition);
}
return macroponentId;
},
_createScreen: function(macroponent, appConfig, screenType, name, template) {
var cloneInfo = {
'sys_id': this._getConstantsForPage(template).SCREEN,
'fields': this.CONSTANTS.CLONE_FIELDS.UX_SCREEN
};
var payload = {
'name': name,
'macroponent': macroponent,
'app_config': appConfig,
'screen_type': screenType
};
return this.GLOBAL._grRecordCreator(this.UXF_TABLE.SCREEN, payload, cloneInfo);
},
_getConstantsForPage: function(name) {
switch (name) {
case 'Home':
return this.CONSTANTS.BASE_UXF_RECORDS.HOME_PAGE;
case 'Settings':
return this.CONSTANTS.BASE_UXF_RECORDS.SETTINGS_PAGE;
case 'Search':
return this.CONSTANTS.BASE_UXF_RECORDS.SEARCH_PAGE;
case 'List':
return this.CONSTANTS.BASE_UXF_RECORDS.LIST_PAGE;
case 'Record':
return this.CONSTANTS.BASE_UXF_RECORDS.RECORD_PAGE;
case 'Content Block':
return this.CONSTANTS.BASE_UXF_RECORDS.CONTENT_BLOCK;
default:
return this.CONSTANTS.BASE_UXF_RECORDS.BLANK_PAGE;
}
},
_createContentBlocks: function(pageId, contentBlocks) {
var _self = this;
function createContentBlock(pageId, block, parent) {
try {
var payload = JSON.parse(JSON.stringify(block));
payload.type = block.type.id;
payload.parent = parent;
payload.document_table = _self.TABLE.PAGE;
payload.document_id = pageId;
var blockId = _self._grRecordCreator(_self.TABLE.CONTENT_BLOCK, payload);
block.children && block.children.forEach(function(child) {
createContentBlock(pageId, child, blockId);
});
} catch (e) {
gs.error('ACEAppBuilderUtil: error creating content blocks: ' + e.toString());
}
}
contentBlocks.forEach(function(block) {
createContentBlock(pageId, block, null);
});
},
addPageToExperience: function(pageName, pageTemplate, path, aceAppSysId) {
var pageId = this._grRecordCreator(this.TABLE.PAGE, {
'name': pageName,
'ace_app': aceAppSysId,
'template': pageTemplate,
'path': path
});
return pageId;
},
createExperience: function(name, urlPath, landingPath, aceAppSysId, createDemoData, current) {
var _self = this;
if (createDemoData) this.CREATE_DEMO_DATA = true;
var appConfig = this.GLOBAL._grRecordCreator(_self.UXF_TABLE.APP_CONFIG, {
'name': name,
'landing_path': landingPath
});
var experience = this.GLOBAL._grRecordCreator(_self.UXF_TABLE.PAGE_REGISTRY, {
'title': name,
'admin_panel_table': _self.UXF_TABLE.APP_CONFIG,
'admin_panel': appConfig,
'path': urlPath,
'parent_app': _self.CONSTANTS.BASE_UXF_RECORDS.PARENT_APP,
'root_macroponent': _self.CONSTANTS.BASE_UXF_RECORDS.APP_SHELL_UI
});
var pagePropertyPayload = {
'page': experience,
'name': 'ace_application_id',
'type': 'string',
'value': aceAppSysId,
'description': 'ACE Record id for this Experience'
};
var chromeHeaderPagePropertyPayload = {
'page': experience,
'name': 'chrome_header',
'type': 'json',
'value': JSON.stringify(_self.CONSTANTS.CHROME_HEADER_VALUE),
};
this.GLOBAL._grRecordCreator(_self.UXF_TABLE.PAGE_PROPERTY, pagePropertyPayload);
this.GLOBAL._grRecordCreator(_self.UXF_TABLE.PAGE_PROPERTY, chromeHeaderPagePropertyPayload);
// Update the current variable directly here with required info
var context = this._createFactories(appConfig, aceAppSysId, name);
if (current) {
current.app_config = appConfig;
current.context = JSON.stringify(context);
current.update();
}
var shippedPages = _self.CONSTANTS.DEFAULT_PAGES;
shippedPages.forEach(function(pageInfo) {
_self.addPageToExperience(pageInfo.name, pageInfo.template, pageInfo.path, aceAppSysId);
});
return appConfig;
},
constructNavMenuItems: function(menuItems, parentId) {
var _self = this;
menuItems.forEach(function(menuItem) {
menuItem.label = menuItem.title;
menuItem.id = menuItem.block_id;
_self.constructNavMenuItems(menuItem.children);
});
},
/*_getPageIdFromRoute: function(aceAppSysId, pageRoute) {
if (!aceAppSysId || !pageRoute) return null;
var gr = new GlideRecord(this.TABLE.PAGE);
gr.addQuery('name', pageRoute);
gr.addQuery('ace_app', aceAppSysId);
gr.query();
if (gr.next()) {
return gr.getValue('sys_id');
}
return null;
},*/
_buildLink: function(gr) {
var link = '';
switch (gr.getValue('link_type')) {
case 'guided_setup':
link = '/$guided_setup.do#/content/' + gr.getValue('guided_setup_id');
var focus_id = gr.getValue('foucus_id');
if (focus_id != '') {
link += '?focus=' + focus_id;
}
break;
case 'record':
link = '/' + gr.getValue('list_table') + '.do?sys_id=' + gr.getValue('record');
break;
case 'list':
link = '/' + gr.getValue('list_table') + '_list.do?sysparm_query=' + gr.getValue('list_query');
break;
case 'custom':
default:
link = gr.getValue('link_url');
}
return link;
},
_getPluginsInfo: function(plugins) {
if (!plugins) return [];
var pluginList = plugins.split(',');
return pluginList.map(function(plugin) {
var grPlugin = new GlideRecord('v_plugin');
grPlugin.get(plugin);
return {
"id": plugin,
"label": grPlugin.getValue('name'),
"selected": true,
"type": "plugin",
"use_demo_data": false
};
});
},
_areAllPluginsActive: function(plugins) {
if (!plugins) return false;
var pluginList = plugins.split(',');
return !pluginList.some(function(plugin) {
var grPlugin = new GlideRecord('v_plugin');
grPlugin.get(plugin);
return grPlugin.getValue('active') !== 'active';
});
},
_mapCards: function(registry) {
var mappedCards = [];
try {
registry.card_order.forEach(function(parentCardId) {
var screens = [];
screens.push(registry.definitions[parentCardId]);
registry.maps[parentCardId].forEach(function(childCardId) {
screens.push(registry.definitions[childCardId]);
});
mappedCards.push(screens);
});
return mappedCards;
} catch (e) {
return mappedCards;
}
},
_parseSysProperties: function(propsSysIdList) {
var props = [];
try {
var gr = new GlideRecord('sys_properties');
gr.addQuery('sys_id', 'IN', propsSysIdList);
gr.query();
while (gr.next()) {
props.push({
'name': gr.getValue('name'),
'value': gr.getValue('value') == 'true',
'description': gr.getValue('description')
});
}
return props;
} catch (e) {
return props;
}
},
syncSysProperties: function(propsList) {
var _self = this;
if (!propsList) return false;
if (!Array.isArray(propsList)) return false;
if (propsList.length == 0) return false;
try {
propsList.forEach(function(property) {
if (_self.GLOBAL._getSysProperty(property.name) != property.value.toString())
_self.GLOBAL._updateSysProperty(property.name, property.value);
});
return true;
} catch (e) {
return false;
}
},
_getChartConfig: function(gr) {
try {
var config = JSON.parse(gr.getValue('chart_config'));
config.chart_type = gr.getValue('chart_type');
return config;
} catch (e) {
return {
"chart_type": "donut",
"title": "Basic Chart",
"table": "problem",
"query": "active=true"
};
}
},
_isParentChildTypeEqual: function(gr) {
if (!gr.parent) return false;
return gr.parent.category.toString() == gr.category.toString() &&
gr.parent.type.toString() == gr.type.toString();
},
_getGlideVarsForType: function(type) {
var variables = [];
var gr = new GlideRecord(this.TABLE.VARIABLE);
gr.addQuery('model', type);
gr.query();
while (gr.next()) {
variables.push({
'element': gr.getValue('element'),
'type': gr.getValue('internal_type')
});
}
return variables;
},
_setGlideVars: function(table, recordId, glideVariableObject) {
try {
var gr = new GlideRecord(table);
if (gr.get(recordId)) {
for (var gVariable in glideVariableObject) {
gr.variable[gVariable] = glideVariableObject[gVariable];
}
return gr.update();
}
return false;
} catch (e) {
return false;
}
},
_getContentBlocksRowCountOnly: function(customQuery) {
try {
return this._getContentBlocks('', customQuery).length;
} catch (e) {
return 0;
}
},
getAllContentBlockTypes: function(table, experienceMetadata) {
var types = {};
var _self = this;
var gr = new GlideRecord(table);
gr.query();
while (gr.next()) {
var typeId = gr.getUniqueValue();
var route = gr.route.toString();
var props = (experienceMetadata && experienceMetadata['typeVarsMap'] && experienceMetadata['typeVarsMap'][typeId]) ? experienceMetadata['typeVarsMap'][typeId] : _self._getGlideVarsForType(typeId);
types[typeId] = {
'group': gr.category.toString(),
'name': gr.name.toString(),
'id': typeId,
'route': route,
'props': props
};
experienceMetadata && experienceMetadata[route] && (types[typeId]['macroponent'] = experienceMetadata[route].macroponent);
}
return types;
},
_getDetailedType: function(typeSysId, experienceMetadata) {
return this.getAllContentBlockTypes(this.TABLE.TYPE, experienceMetadata)[typeSysId];
},
_variableScriptEvaluator: function(gr, cbScriptVariables, cbData) {
var _self = this;
try {
var evaluator = new GlideScopedEvaluator();
evaluator.putVariable('pageId', cbScriptVariables.pageId);
evaluator.putVariable('pageParams', cbScriptVariables.pageParams);
evaluator.putVariable('blockcontext', cbScriptVariables.blockcontext);
var aceVarGr = new GlideRecord(this.TABLE.VARIABLE);
aceVarGr.addQuery('model', cbData.type.id);
aceVarGr.query();
if (aceVarGr.next()) {
var sysVarGr = new GlideRecord('sys_variable_value');
sysVarGr.addQuery('document_key', cbData.block_id);
sysVarGr.addQuery('variable', aceVarGr.getValue('sys_id'));
sysVarGr.query();
try {
if (sysVarGr.next())
return evaluator.evaluateScript(sysVarGr, 'value', null);
} catch (e) {
gs.error('Error while evaluating Script: ' + e.toString());
}
}
} catch (e) {
gs.error('Error while evaluating Script: ' + e.toString());
}
return [];
},
_evaluateCBScriptVariables: function(gr, cbData, cbScriptVariables) {
var _self = this;
var typeObject = cbData.type;
if (typeObject && typeObject.props && Array.isArray(typeObject.props) > 0) {
typeObject.props.forEach(function(propItem) {
if (propItem.type === "script")
cbData.props_details[propItem.element] = _self._variableScriptEvaluator(gr, cbScriptVariables, cbData);
});
}
},
_sanitize: function(value, parseBoolean) {
if (value == null || value == undefined) return '';
if (parseBoolean) {
if (value.toString() == 'true') return '1';
if (value.toString() == 'false') return '0';
}
return value.toString();
},
_sanitizeScript: function(scriptValue) {
if (scriptValue == null || scriptValue == undefined ||
(typeof scriptValue === 'object' && JSON.stringify(scriptValue) === '{}')) {
return '';
}
return scriptValue.toString();
},
/* dataSource = GlideRecord instance [OR] Cached Data*/
_getContentBlockInfo: function(dataSource, cbId, pageParams, experienceMetadata, fetchOnyMetaData) {
var _self = this;
try {
var typeObject = _self._getDetailedType(_self._sanitize(dataSource.type), experienceMetadata);
//id and label property is required for content tree component
var style;
try {
style = JSON.parse(dataSource.style);
} catch (err) {
style = {};
}
var propsOverrides = {};
try {
propsOverrides = dataSource.props_overrides ? JSON.parse(dataSource.props_overrides) : {};
} catch (err) {
}
var parentBlock = dataSource.parent;
var parentBlockRoute = parentBlock && parentBlock.type && parentBlock.type.route ?
parentBlock.type.route.toString() : '';
var contentBlockInfo = {
'block_id': cbId,
'id': cbId,
'parent_block_id': parentBlock.toString(),
'parent_block_route': parentBlockRoute,
'active': dataSource.active.toString() === 'true' ? true : false,
'name': _self._sanitize(dataSource.name),
'label': _self._sanitize(dataSource.name),
'title': _self._sanitize(dataSource.title),
'subtitle': _self._sanitize(dataSource.subtitle),
'order': _self._sanitize(dataSource.order),
'type': typeObject,
'lazyLoad': _self._sanitize(dataSource.lazy_load, true),
'children': [],
'props_details': {},
'props_details_display': {},
'props_overrides': propsOverrides,
'pageParams': pageParams,
'style': style,
'elementId': _self._sanitize(dataSource.element_id),
'slotname': _self._sanitize(dataSource.slotname),
'post_script': _self._sanitizeScript(dataSource.getValue('post_script')),
'visibility': _self._sanitizeScript(dataSource.getValue('visibility')),
'lazy_script': _self._sanitizeScript(dataSource.getValue('lazy_script')),
'scope': _self._sanitize(dataSource.sys_scope),
'package': _self._sanitize(dataSource.sys_package)
};
if (typeObject && typeObject.props && Array.isArray(typeObject.props) > 0) {
typeObject.props.forEach(function(propItem) {
contentBlockInfo.props_details[propItem.element] = dataSource.variable[propItem.element].toString();
if (propItem.type == 'reference') {
var dictionary = new GlideRecord('sys_dictionary');
dictionary.addQuery('sys_id', _self._sanitize(dataSource.variable[propItem.element]));
dictionary.query();
if (dictionary.next()) {
contentBlockInfo.props_details_display[propItem.element] = dictionary.getValue('element');
}
}
});
}
if (contentBlockInfo.type && contentBlockInfo.type.route && contentBlockInfo.type.route == 'reusable') {
if (contentBlockInfo.props_details && contentBlockInfo.props_details.block_id)
var reusableBlockInfo = this.getReusableBlockDataForTranslations(contentBlockInfo.props_details.block_id);
if (reusableBlockInfo && reusableBlockInfo.block_variables && reusableBlockInfo.block_variables.length > 0)
contentBlockInfo.block_variables = reusableBlockInfo.block_variables || {};
}
if (fetchOnyMetaData) {
contentBlockInfo['post_script'] = dataSource.getValue("post_script");
contentBlockInfo['visibility'] = dataSource.getValue("visibility");
contentBlockInfo['lazy_script'] = dataSource.getValue("lazy_script");
}
return contentBlockInfo;
} catch (e) {
gs.error('ACEAppBuilderUtil: Error while fetching content block info: ' + e.toString());
return contentBlockInfo;
}
},
_getExperienceIdFromCBId: function(cbId) {
if (!cbId) return null;
var gr = new GlideRecord(this.TABLE.CONTENT_BLOCK);
if (gr.get(cbId)) {
return gr.getValue('ace_config');
}
return null;
},
_getExperienceIdFromCBQuery: function(cbQuery) {
if (!cbQuery || !cbQuery.document_table || !cbQuery.document_id) return null;
var _self = this;
switch (cbQuery.document_table) {
case _self.TABLE.PAGE:
var gr = new GlideRecord(_self.TABLE.PAGE);
if (gr.get(cbQuery.document_id)) {
return gr.getValue('ace_app');
}
return null;
case _self.TABLE.APP_CONFIG:
return cbQuery.document_id;
case _self.TABLE.CONTENT_BLOCK:
return cbQuery.document_id;
default:
return null;
}
},
_buildCachedContentBlocks: function(experienceId, cbQuery, parentId, cbScriptVariables) {
var cachedContentBlocks = [],
_self = this;
//check for user roles access sn_ace.ace_user
if (!this.CAN_READ) return cachedContentBlocks;
//attach the parent query to the existing query. NOTE: For cached content, parent == '' IS_NOT_EQUAL parent == null
if (!cbQuery) cbQuery = {};
cbQuery['parent'] = parentId;
cbQuery['active'] = '1';
var cachedCBs = this.GLOBAL.getContentBlocksFromCache(experienceId, cbQuery);
if (!cachedCBs) return null;
//Try creating a Virtual GlideRecord instance using the parentId required for GlideScopeEvaluator. Re-use this for evaluation to reduce queries.
var virtualGr = _self.VIRTUAL_GR || _self._getVirtualGrInstance(parentId);
if (!_self.VIRTUAL_GR && virtualGr) _self.VIRTUAL_GR = virtualGr;
cachedCBs.forEach(function(cachedCB) {
var cbId = cachedCB.sys_id;
//Re-try the Virtual GR creation again with cbId (since parentId is null) and register it in app level
virtualGr = _self.VIRTUAL_GR || _self._getVirtualGrInstance(cbId);
if (!_self.VIRTUAL_GR && virtualGr) _self.VIRTUAL_GR = virtualGr;
if (_self._cachedScriptEvaluator(virtualGr, 'visibility', cachedCB.visibility, null)) {
var cachedCBData = _self._getContentBlockInfo(cachedCB, cbId);
_self._evaluateCBScriptVariables(virtualGr, cachedCBData, cbScriptVariables);
cachedCBData.children = _self._buildCachedContentBlocks(experienceId, cbQuery, cbId, cbScriptVariables);
_self._cachedScriptEvaluator(virtualGr, 'post_script', cachedCB.post_script, cachedCBData);
cachedContentBlocks.push(cachedCBData);
}
});
return cachedContentBlocks;
},
_getContentBlocksFromCache: function(parentId, cbQuery, cbScriptVariables) {
try {
//get and register the experience id in app level to reduce queries
var experienceId = this.EXPERIENCE_ID || this._getExperienceIdFromCBId(parentId) || this._getExperienceIdFromCBQuery(cbQuery);
if (!experienceId) return null;
if (!this.EXPERIENCE_ID) this.EXPERIENCE_ID = experienceId;
//try to fetch content blocks from cache
var cachedContentBlocks = this._buildCachedContentBlocks(experienceId, cbQuery, parentId, cbScriptVariables);
//if not present, try to build the cache for this experience
if (!cachedContentBlocks || (Array.isArray(cachedContentBlocks) && cachedContentBlocks.length == 0)) {
this.GLOBAL.buildExperienceCache(experienceId);
//retry fetching the content blocks from the recenly built cache
cachedContentBlocks = this._buildCachedContentBlocks(experienceId, cbQuery, parentId, cbScriptVariables);
}
return cachedContentBlocks;
} catch (e) {
return null;
}
},
_getContentBlocks: function(parentId, customQuery, cbScriptVariables, experienceMetadata, includeFilterOnEmptyParent, skipScriptEvaluation) {
var contentBlocks = [];
cbScriptVariables = cbScriptVariables || {};
var _self = this;
try {
this.gr_counter += 1;
var gr = new GlideRecord(this.TABLE.CONTENT_BLOCK);
if (!includeFilterOnEmptyParent) {
gr.addQuery('parent', parentId);
}
gr.addQuery('active', true);
for (var q in customQuery) {
gr.addQuery(q, customQuery[q]);
}
gr.addQuery('type', '!=', _self.DATA_BLOCK_CONTENT_TYPE);
gr.orderBy('order');
gr.query();
while (gr.next()) {
var cbId = gr.getUniqueValue().toString();
if (gr.getValue("type") == _self.REUSABLE_CONTENT_TYPE) {
//todo
var reusableContentBlocks = null;
var cbData = _self._getContentBlockInfo(gr, cbId, cbScriptVariables.pageParams, experienceMetadata, false);
var blockParams = {};
try {
blockParams = gr.variable && gr.variable.block_parameters ?
JSON.parse(gr.variable.block_parameters + '') : {};
} catch (err) {
gs.error('ACEAppBuilderUtil._getContentBlocks - error in parsing blockParams')
}
reusableContentBlocks = _self.getContentBlocksByBlockId(gr.variable.block_id, blockParams, cbId, false, skipScriptEvaluation);
cbData.children = reusableContentBlocks;
contentBlocks.push(cbData);
} else {
if (!skipScriptEvaluation && !_self._visibilityEvaluator(gr, cbScriptVariables.pageParams, cbScriptVariables.blockcontext, cbScriptVariables.dataParams)) continue;
var cbData = _self._getContentBlockInfo(gr, cbId, cbScriptVariables.pageParams, experienceMetadata);
// content block variable script evaluator
_self._evaluateCBScriptVariables(gr, cbData, cbScriptVariables);
cbData.children = _self._getContentBlocks(cbId, customQuery, cbScriptVariables, experienceMetadata);
if (!skipScriptEvaluation) {
//post script evaluator
_self._evaluatePostScript(gr, cbData, cbScriptVariables.pageParams, cbScriptVariables.blockcontext, cbScriptVariables.dataParams);
}
contentBlocks.push(cbData);
}
}
} catch (e) {
gs.error('ACEAppBuilderUtil: Error while fetching content blocks: ' + e.toString());
}
return contentBlocks;
},
_getFetchMode: function(cbQuery) {
//Experience level System Property value (ace.content_blocks.fetch.mode.<EXPERIENCE ID>)
var experienceId = this._getExperienceIdFromCBQuery(cbQuery);
if (!experienceId) return this.DEFAULT_FETCH_MODE;
var mode = gs.getProperty('ace.content_blocks.fetch.mode.' + experienceId, 'dfs');
mode = mode.toLowerCase();
if (this.SUPPORTED_FETCH_MODES.indexOf(mode) != -1) return mode;
return this.DEFAULT_FETCH_MODE;
},
// getContentBlocksById: function(blockId, pageId, pageParams, reloadCache) {
// var _self = this;
// if (!this.CAN_READ) return [];
// var cbQuery = {
// 'parent': blockId,
// 'or': {
// 'sys_id': blockId
// }
// };
// var cbPageParams = {
// 'pageId': pageId,
// 'pageParams': pageParams
// };
// var contentBlockTypeMetadata = this.GLOBAL.getCachedContentTypeMetaData(reloadCache);
// var experienceMetadata = {};
// if (contentBlockTypeMetadata) {
// experienceMetadata = contentBlockTypeMetadata[appConfig];
// experienceMetadata['typeVarsMap'] = contentBlockTypeMetadata['typeVarsMap'];
// }
// return this._getContentBlocksBFS(null, cbQuery, cbPageParams, experienceMetadata);
// },
getContentBlocksByPageId: function(pageId, pageParams, reloadCache) {
var appConfig = "";
var pageGr = new GlideRecord(this.TABLE.PAGE);
pageGr.get(pageId);
if (pageGr.isValidRecord()) {
appConfig = pageGr.ace_app.app_config.toString();
}
return this.getContentBlocks(pageId, pageParams, appConfig, reloadCache, false);
},
getContentBlocksByBlockId: function(defBlockId, params, instanceBlockId, reloadCache, skipScriptEvaluation) {
var self = this;
var appConfig = "",
aceAppConfigGr;
var blockGr = new GlideRecord(this.TABLE.CONTENT_BLOCK);
if (instanceBlockId) {
blockGr.addQuery("sys_id", "IN", defBlockId + "," + instanceBlockId);
blockGr.query();
} else {
blockGr.get(defBlockId);
}
var defContentTree = [];
var instanceContentTree = [];
while (blockGr.next()) {
if (blockGr.getUniqueValue() == defBlockId) {
aceAppConfigGr = new GlideRecord(this.TABLE.APP_CONFIG);
aceAppConfigGr.get(blockGr.document_id.toString());
if (aceAppConfigGr.isValidRecord()) {
appConfig = aceAppConfigGr.app_config.toString();
}
defContentTree = this.getContentBlocks(defBlockId, params, appConfig, reloadCache, true, null, skipScriptEvaluation);
} else {
var pageId = blockGr.getValue('document_id');
var pageGr = new GlideRecord(this.TABLE.PAGE);
if (pageGr.get(pageId)) {
appConfig = pageGr.ace_app.app_config.toString();
}
instanceContentTree = this.getContentBlocks(instanceBlockId, params, appConfig, reloadCache, true, true, skipScriptEvaluation);
}
}
self._mergeReusableBlocks(defContentTree, instanceContentTree);
return defContentTree;
},
_mergeReusableBlocks: function(defContentTree, instanceContentTree) {
var self = this;
var children = defContentTree;
while (children && children.length > 0) {
var nextlevelChildren = [];
children.forEach(function(node) {
if (node.type && node.type.route == "content-slot") {
node.children = self._findNodesHavingParent(instanceContentTree, node.block_id);
}
nextlevelChildren = nextlevelChildren.concat(node.children);
});
children = nextlevelChildren;
}
},
_findNodesHavingParent: function(contents, parentBlockId) {
var children = contents;
var result = [];
while (children && children.length > 0) {
var nextlevelChildren = [];
children.forEach(function(node) {
if (node.parent_block_id == parentBlockId) {
result = result.concat(node);
} else {
nextlevelChildren = nextlevelChildren.concat(node.children);
}
});
children = nextlevelChildren;
}
return result;
},
getExperienceMetadata: function(reloadCache, appConfig) {
var contentBlockTypeMetadata = this.GLOBAL.getCachedContentTypeMetaData(reloadCache);
var baseExpConfigId = this.CONSTANTS.BASE_UXF_RECORDS.ACE_BASE_EXPERIENCE_APP_CONFIG;
var experienceMetadata = {};
if (!appConfig) {
experienceMetadata = contentBlockTypeMetadata[baseExpConfigId];
experienceMetadata['typeVarsMap'] = contentBlockTypeMetadata['typeVarsMap'];
return experienceMetadata;
}
if (!contentBlockTypeMetadata[appConfig])
contentBlockTypeMetadata = this.GLOBAL.getCachedContentTypeMetaData(true);
experienceMetadata = contentBlockTypeMetadata[appConfig] || contentBlockTypeMetadata[baseExpConfigId];
experienceMetadata['typeVarsMap'] = contentBlockTypeMetadata['typeVarsMap'];
return experienceMetadata;
},
getContentBlocks: function(artifactId, pageParams, appConfig, reloadCache, isBlock, includeParent, skipScriptEvaluation) {
var _self = this;
if (!this.CAN_READ) return [];
var experienceMetadata = this.getExperienceMetadata(reloadCache, appConfig);
var cbQuery = {
'document_table': isBlock ? _self.TABLE.CONTENT_BLOCK : _self.TABLE.PAGE,
'document_id': artifactId
};
var cbPageParams = {
'pageId': isBlock ? "" : artifactId,
'pageParams': pageParams
};
// Confirm the content blocks are being fetched for a page and evaluate data blocks.
if (cbPageParams.pageId != "") {
// TODO: get the data blocks for this page.
var dataBlockGr = new GlideRecord('sn_ace_content_block');
dataBlockGr.addQuery('type', _self.DATA_BLOCK_CONTENT_TYPE);
dataBlockGr.query();
var dataBlocks = [];
while (dataBlockGr.next()) {
var cbData = _self._getContentBlockInfo(dataBlockGr, dataBlockGr.getUniqueValue().toString(), cbPageParams.pageParams, experienceMetadata);
dataBlocks.push(cbData);
}
dataBlocks = _self.eagerlyEvaluateDataBlocks(dataBlocks, cbPageParams);
cbPageParams = _self.addDataBlockParamsToScriptVariables(dataBlocks, cbPageParams);
}
var fetchMode = this._getFetchMode(cbQuery);
if (fetchMode === this.DEFAULT_FETCH_MODE) {
return this._getContentBlocks(null, cbQuery, cbPageParams, experienceMetadata, includeParent, skipScriptEvaluation).concat(dataBlocks);
}
return this._getContentBlocksBFS(null, cbQuery, cbPageParams, experienceMetadata, includeParent, skipScriptEvaluation).concat(dataBlocks);
},
getAllContentBlocksBFS: function() {
return this._getContentBlocksBFS(null, {}, {});
},
_appendContentBlockschildren: function(contentBlocksMap, cbId, cbContextObj) {
if (!contentBlocksMap[cbId]) return [];
var result = [];
contentBlocksMap[cbId].forEach(function(cbChildId) {
if (cbContextObj[cbChildId]) {
result.push(JSON.parse(JSON.stringify(cbContextObj[cbChildId])));
//delete cbContextObj[cbChildId];
}
});
return result;
},
_processContentBlocksFromIds: function(contentBlocksMap, contentBlockParentIds, cbContextObj, cbScriptVariables, cbLevel, nextCBLevel, experienceMetadata, fetchOnlyMetaData) {
var _self = this;
this.gr_counter += 1;
if (!contentBlockParentIds && (!cbLevel || cbLevel[nextCBLevel].length == 0)) return;
var gr = new GlideRecord(this.TABLE.CONTENT_BLOCK);
if (contentBlockParentIds) {
gr.addQuery('parent', 'IN', contentBlockParentIds.toString());
} else {
gr.addQuery('sys_id', "IN", cbLevel[nextCBLevel].join(","));
}
gr.orderBy('order');
gr.query();
while (gr.next()) {
var cbId = gr.getUniqueValue().toString();
if (cbLevel[nextCBLevel] && cbLevel[nextCBLevel].indexOf(cbId) == -1) continue;
if (gr.getValue("type") == _self.REUSABLE_CONTENT_TYPE) {
//todo
var reusableContentBlocks = null;
var cbData = _self._getContentBlockInfo(gr, cbId, cbScriptVariables.pageParams, experienceMetadata, fetchOnlyMetaData);
if (!fetchOnlyMetaData) {
var blockParams = gr.variable.block_parameters;
if (typeof blockParams == "string") {
try {
blockParams = JSON.parse(blockParams);
} catch (err) {}
}
reusableContentBlocks = _self.getContentBlocksByBlockId(gr.variable.block_id, blockParams, cbId, false)
} else {
reusableContentBlocks = _self._getContentBlockMetaData("", gr.variable.block_id, cbId, false, gr.variable.clone + "" == "true");
reusableContentBlocks = reusableContentBlocks ? reusableContentBlocks.contentBlocks : [];
}
cbData.children = reusableContentBlocks;
cbContextObj[cbId] = cbData;
} else {
var cbData = _self._getContentBlockInfo(gr, cbId, cbScriptVariables.pageParams, experienceMetadata, fetchOnlyMetaData);
if (fetchOnlyMetaData) {
//append children
cbData.children = _self._appendContentBlockschildren(contentBlocksMap, cbId, cbContextObj);
} else {
// content block variable script evaluator
_self._evaluateCBScriptVariables(gr, cbData, cbScriptVariables);
//append children
cbData.children = _self._appendContentBlockschildren(contentBlocksMap, cbId, cbContextObj);
//post script evaluator
_self._evaluatePostScript(gr, cbData, cbScriptVariables.pageParams, cbScriptVariables.blockcontext, cbScriptVariables.dataParams);
}
cbContextObj[cbId] = cbData;
}
}
},
_buildContentBlockTreeFromLevels: function(contentBlocksMap, cbLevel, cbScriptVariables, experienceMetadata, fetchOnlyMetaData) {
//loop through last-but-one level to 0
var _self = this;
var cbContextObj = {};
var contentBlocks = [];
for (var level = (cbLevel.length - 2); level >= 0; level--) {
var contentBlockParentIds = cbLevel[level];
_self._processContentBlocksFromIds(contentBlocksMap, contentBlockParentIds, cbContextObj, cbScriptVariables, cbLevel, (level + 1), experienceMetadata, fetchOnlyMetaData);
}
_self._processContentBlocksFromIds(contentBlocksMap, '', cbContextObj, cbScriptVariables, cbLevel, 0, experienceMetadata, fetchOnlyMetaData);
//if only root level content blocks are present
if (!contentBlockParentIds) contentBlockParentIds = cbLevel[0];
contentBlockParentIds.forEach(function(contentBlockParentId) {
contentBlocks.push(cbContextObj[contentBlockParentId]);
});
return contentBlocks;
},
_buildContentBlockLevels: function(cbMap, cbIds, cbLevelObject, level) {
if (!cbLevelObject[level]) cbLevelObject[level] = [];
var _self = this;
cbIds.forEach(function(blockId) {
cbLevelObject[level].push(blockId);
if (cbMap[blockId] && cbMap[blockId].length > 0) {
_self._buildContentBlockLevels(cbMap, cbMap[blockId], cbLevelObject, (level + 1));
}
});
},
_getValidCBIds: function(base, blacklist) {
var validIds = [];
base.forEach(function(cbId) {
if (blacklist.indexOf(cbId) == -1) validIds.push(cbId);
});
return validIds;
},
_visibilityCheck: function(cbLevels, cbLevelIndex, blacklistedCBIds, cbScriptVariables) {
if (cbLevelIndex == cbLevels.length && cbLevelIndex >= 0) {
return cbLevels;
}
this.gr_counter += 1;
var gr = new GlideRecord(this.TABLE.CONTENT_BLOCK);
gr.addQuery('active', true);
if (cbLevelIndex < 0) {
gr.addQuery('sys_id', 'IN', cbLevels[0]);
} else {
var validIds = this._getValidCBIds(cbLevels[cbLevelIndex], blacklistedCBIds);
if (validIds.length === 0) return cbLevels;
gr.addQuery('parent', 'IN', validIds.toString());
}
gr.orderBy('order');
gr.query();
var cbs = [];
while (gr.next()) {
var cbId = gr.getUniqueValue().toString();
if (!this._visibilityEvaluator(gr, cbScriptVariables.pageParams, cbScriptVariables.blockcontext, cbScriptVariables.dataParams)) {
blacklistedCBIds.push(cbId);
} else {
cbs.push(cbId);
}
}
if (cbs.length)
cbLevels[cbLevelIndex + 1] = cbs;
return this._visibilityCheck(cbLevels, (cbLevelIndex + 1), blacklistedCBIds, cbScriptVariables);
},
/**
* BFS Approach:
* Step 1: Get ALL the content blocks of a page orderby 'order'
* Step 2: Build the parent-child sys_id relationship map. Example:
* {
* '_root': ['cb_id#1', 'cb_id#2', 'cb_id#3'],
* 'cb_id#1': ['cb_id#1.child#1', 'cb_id#1.child#2', 'cb_id#1.child#3'],
* 'cb_id#2': ['cb_id#2.child#1', 'cb_id#2.child#2'],
* 'cb_id#3': ['cb_id#3.child#1', 'cb_id#3.child#2', 'cb_id#3.child#3', 'cb_id#3.child#4'],
* 'cb_id#1.child#1': ['cb_id#1.child#1.child#1', ...],
* ...
* }
* Step 3: Build the step-level hierarchy of the map. Example:
* [
* ['cb_id#1', 'cb_id#2', 'cb_id#3'],
* ['cb_id#1.child#1', 'cb_id#1.child#2', 'cb_id#1.child#3', 'cb_id#2.child#1', 'cb_id#2.child#2', 'cb_id#3.child#1', 'cb_id#3.child#2', 'cb_id#3.child#3', 'cb_id#3.child#4'],
* ['cb_id#1.child#1.child#1', ...],
* ...
* ]
* Step 4: Using a top-down approach evaluate the visibility script and modify the step-level hierarchy.
* Example: For, 'cb_id#2' = false
* [
* ['cb_id#1', 'cb_id#3'],
* ['cb_id#1.child#1', 'cb_id#1.child#2', 'cb_id#1.child#3', 'cb_id#3.child#1', 'cb_id#3.child#2', 'cb_id#3.child#3', 'cb_id#3.child#4'],
* ['cb_id#1.child#1.child#1', ...],
* ...
* ]
* Step 5: Using a bottom-up approach evaluate the post script and build the Content Block contract
*/
_getContentBlocksBFS: function(parentId, customQuery, cbScriptVariables, experienceMetadata, includeFilterOnEmptyParent) {
try {
var contentBlocksMap = {
'_root': []
};
this.gr_counter += 1;
//Step 1,2: get all content blocks and build the parent-child relationship map of their sys_ids
var gr = new GlideRecord(this.TABLE.CONTENT_BLOCK);
for (var q in customQuery) {
gr.addQuery(q, customQuery[q]);
}
if (parentId || includeFilterOnEmptyParent) {
gr.addQuery('parent', parentId);
}
gr.addQuery('type', '!=', this.DATA_BLOCK_CONTENT_TYPE);
gr.orderBy('order');
gr.query();
while (gr.next()) {
var blockId = gr.getUniqueValue();
var parentBlockId = gr.getValue('parent');
if (!parentBlockId) contentBlocksMap._root.push(blockId);
else {
if (!contentBlocksMap[parentBlockId]) contentBlocksMap[parentBlockId] = [];
contentBlocksMap[parentBlockId].push(blockId);
}
}
var contentBlocksLevel = [];
//Step 3: build a 2D array of the step-level hierarchy
this._buildContentBlockLevels(contentBlocksMap, contentBlocksMap._root, contentBlocksLevel, 0);
//Step 4: modify the same 2D array of the step-level hierarchy after removing visible false items
contentBlocksLevel = this._visibilityCheck(contentBlocksLevel, -1, [], cbScriptVariables);
//Step 5: build the content block contract
var contentBlocks = this._buildContentBlockTreeFromLevels(contentBlocksMap, contentBlocksLevel, cbScriptVariables, experienceMetadata);
return contentBlocks;
} catch (e) {
gs.error('ACEAppBuilderUtil: Error while fetching content blocks: ' + e.toString());
return [];
}
},
getContentBlockPayload: function(cbId, pageParams, includeChildBlocks) {
var cbData = {};
var cbScriptVariables = {
'pageParams': pageParams
};
if (!this.CAN_READ) return cbData;
var gr = new GlideRecord(this.TABLE.CONTENT_BLOCK);
if (cbId && gr.get(cbId)) {
// TODO: Should we check for visibility in lazy load again?
var app_config = gr.ace_config.app_config.toString();
var experienceMetadata = this.getExperienceMetadata(false, app_config);
cbData = this._getContentBlockInfo(gr, cbId, pageParams, experienceMetadata);
this._evaluateCBScriptVariables(gr, cbData, cbScriptVariables);
if (includeChildBlocks) {
// content block variable script evaluator
cbData.children = this._getContentBlocks(cbId, {}, cbScriptVariables, experienceMetadata);
//post script evaluator
_self._evaluatePostScript(gr, cbData, pageParams);
}
this._evaluateLazyScript(gr, cbData, pageParams);
}
return cbData;
},
_getUrlPath: function(urlPath, route) {
return '/now' + '/' + urlPath + '/' + route.toLowerCase();
},
getMenuItems: function(aceAppId, moduleSysId, routeParams) {
var _self = this;
var menuItems = [];
try {
menuItems = _self._getContentBlocks(moduleSysId, {
'document_table': _self.TABLE.APP_CONFIG,
'document_id': aceAppId,
'type': _self.CONSTANTS.CONTENT_BLOCK_TYPES.NAV
}, {
pageParams: routeParams
});
_self.constructNavMenuItems(menuItems);
return menuItems;
} catch (e) {
return menuItems;
}
},
getFieldsAndParams: function(routeId) {
var routeGr = new GlideRecord('sys_ux_app_route');
var result = {
fields: [],
params: []
};
if (routeGr.get(routeId)) {
var fields = routeGr.getValue('fields') || '';
result.fields = fields.split(',').filter(function(field) {
return field;
}).map(function(field) {
return {
name: field
};
});
var params = routeGr.getValue('optional_parameters') || '';
result.params = params.split(',').filter(function(param) {
return param;
}).map(function(param) {
return {
name: param
};
});
}
return result;
},
getModulePath: function(urlPath, modulePath, routeId, page_parameters) {
var _self = this;
var path = _self._getUrlPath(urlPath, modulePath);
var uxfPageParams = _self.getFieldsAndParams(routeId);
if (page_parameters) {
page_parameters = JSON.parse(page_parameters);
uxfPageParams.fields.concat(uxfPageParams.params).forEach(function(param) {
var name = param.name;
if (page_parameters[name])
path = path + '/' + page_parameters[name].toString();
});
}
return path;
},
getModules: function(aceAppId, urlPath, routeParams) {
var _self = this;
if (!this.CAN_READ) return [];
var modules = this._getContentBlocks(null, {
'document_table': this.TABLE.APP_CONFIG,
'document_id': aceAppId,
'parent': null,
'type': this.CONSTANTS.CONTENT_BLOCK_TYPES.MODULE
});
//massage modules blocks to support UI
//TODO: Make UI in sync with content_blocks contract
modules.forEach(function(module) {
module.isActive = false;
var containedPages = module.props_details.contained_pages;
module.props_details.contained_pages = containedPages ? _self.getContainedPagesRoutes(containedPages) : containedPages;
module.menu = _self.getMenuItems(aceAppId, module.block_id, routeParams);
// Module Page Route Should be atken from prop details
var pageId = module.props_details.ace_page;
var gr = new GlideRecord(_self.TABLE.PAGE);
if (pageId && gr.get(pageId)) {
module.page_route = gr.getValue('path');
module.path = _self.getModulePath(urlPath, module.page_route, gr.route.toString(), module.props_details.page_parameters);
} else if (module.props_details.link) module.path = module.props_details.link;
else gs.addErrorMessage(gs.getMessage("Please provide input for either acepage or link fields"));
});
return modules;
},
getContainedPagesRoutes: function(containedPages) {
var _self = this;
var contained_pages_routes = [];
var ace_page_gr = new GlideRecord(_self.TABLE.PAGE);
ace_page_gr.addQuery('sys_id', 'IN', containedPages);
ace_page_gr.query();
while (ace_page_gr.next()) {
contained_pages_routes.push(ace_page_gr.getValue('path'));
}
return contained_pages_routes;
},
getURLPath: function(uxAppConfigId) {
var url_path = '';
var sys_ux_page_registry_gr = new GlideRecord(this.CONSTANTS.UXF_TABLES.PAGE_REGISTRY);
sys_ux_page_registry_gr.addQuery('admin_panel', uxAppConfigId);
sys_ux_page_registry_gr.query();
if (sys_ux_page_registry_gr.next()) {
url_path = sys_ux_page_registry_gr.getValue('path');
}
return url_path;
},
getAceAppData: function(aceAppConfig, routeParams) {
var _self = this;
var onLoadData = {
'modules': []
};
try {
var gr = new GlideRecord(this.TABLE.APP_CONFIG);
if (gr.get(aceAppConfig)) {
onLoadData.modules = _self.getModules(aceAppConfig, _self.getURLPath(gr.getValue('app_config')), routeParams);
onLoadData.isSearchConfigured = _self.fetchSearchConfig(aceAppConfig) ? true : false;
}
return onLoadData;
} catch (e) {
return onLoadData;
}
},
_createDemoBlocksInPage: function(pageId) {
var _self = this;
this._createContentBlocks(pageId, this.demoUtils.getDemoTemplate());
},
_createDemoData: function(pageName, pageId, moduleId) {
// TODO: Add this demo data while creating the app
if (!this.CREATE_DEMO_DATA) return;
},
_createFactories: function(appConfigId, aceAppSysId, appName) {
var contentOutput = this.createPage(appConfigId, appName + ' - ACE Content Factory', aceAppSysId, 'Content Factory');
var containerOutput = this.createPage(appConfigId, appName + ' - ACE Container Factory', aceAppSysId, 'Container Factory');
return this._patchFactories(contentOutput.macroponent, containerOutput.macroponent);
},
_deleteUXFPage: function(routeId) {
// TODO: Replace this method with UXF apis in app builder
var routeGr = new GlideRecord(this.UXF_TABLE.APP_ROUTE);
if (!routeGr.get(routeId)) return;
var screenTypeGr = new GlideRecord(this.UXF_TABLE.SCREEN_TYPE);
if (!screenTypeGr.get(routeGr.getValue('screen_type'))) return;
var screenGr = new GlideRecord(this.UXF_TABLE.SCREEN);
screenGr.addQuery('screen_type', routeGr.getValue('screen_type'));
screenGr.query();
while (screenGr.next()) {
this.GLOBAL.deleteRecord(this.UXF_TABLE.SCREEN, screenGr.getUniqueValue());
}
var macroponentGr = new GlideRecord(this.UXF_TABLE.MACROPONENT);
if (!macroponentGr.get(screenGr.getValue('macroponent'))) return;
this.GLOBAL.deleteRecord(this.UXF_TABLE.APP_ROUTE, routeGr.getUniqueValue());
this.GLOBAL.deleteRecord(this.UXF_TABLE.SCREEN_TYPE, screenTypeGr.getUniqueValue());
this.GLOBAL.deleteRecord(this.UXF_TABLE.MACROPONENT, macroponentGr.getUniqueValue());
},
_createContentBlock: function(configId, inputDocId, templateId, documentTable, searchPageId) {
var _self = this;
var cbGr = new GlideRecord(_self.TABLE.CONTENT_BLOCK);
cbGr.addQuery('document_id', templateId);
cbGr.addQuery('document_table', documentTable);
cbGr.query();
while (cbGr.next()) {
var props_details = {};
var props = _self._getGlideVarsForType(cbGr.type.toString());
if (props && Array.isArray(props) > 0) {
props.forEach(function(propItem) {
props_details[propItem.element] = cbGr.variable[propItem.element].toString();
});
}
var cbId = _self._grRecordCreator(_self.TABLE.CONTENT_BLOCK, {
name: cbGr.getValue('name'),
title: cbGr.getValue('title'),
subtitle: cbGr.getValue('subtitle'),
order: cbGr.getValue('order'),
type: cbGr.type,
parent: cbGr.parent,
post_script: cbGr.getValue('post_script'),
visibility: cbGr.getValue('visibility'),
active: cbGr.getValue('active'),
ace_config: configId,
document_id: inputDocId,
document_table: documentTable
});
if (searchPageId && cbGr.getValue('name') === _self.CONSTANTS.SEARCH_CONTENT_BLOCK) {
props_details['page'] = searchPageId;
}
_self._setGlideVars(_self.TABLE.CONTENT_BLOCK, cbId, props_details);
}
},
createContentBlock: function(configId, pageId) {
var _self = this;
if (!this.CAN_READ) return;
var baseGr = new GlideRecord(_self.TABLE.PAGE);
if (pageId && baseGr.get(pageId)) {
var templatePageId = baseGr.getValue('template');
_self._createContentBlock(configId, pageId, templatePageId, _self.TABLE.PAGE);
}
},
_patchPage: function(aceAppId, pageId, macroponentId, screenType, sourceMacroponentId, isNewPage) {
if (!this.CAN_READ) return null;
if (!this._updateMacroponentProp(macroponentId, 'data', this.CONSTANTS.BASE_UXF_RECORDS.SECTIONS_DATA_BROKER_NAME, 'page_route', pageId)) {
//code logic
}
if (!this._updateMacroponentProp(macroponentId, 'data', this.CONSTANTS.BASE_UXF_RECORDS.ACE_PAGE_INIT_DATA_BROKER_NAME, 'page_route', pageId)) {
//code logic
}
try {
// Update route field on ace_page record if screenType is supplied
if (screenType) {
var routeGr = new GlideRecord(this.UXF_TABLE.APP_ROUTE);
routeGr.addQuery('screen_type', screenType);
routeGr.query();
if (routeGr.next()) {
var pageGr = new GlideRecord(this.TABLE.PAGE);
if (pageGr.get(pageId) && pageGr.canWrite()) {
pageGr.setValue('route', routeGr.getUniqueValue());
pageGr.update();
}
}
}
// FIXME: These record updates maynot work from a different scope. Try to use Global update script include
if (isNewPage) {
this.fixClientScriptBindings(sourceMacroponentId, macroponentId);
}
} catch (e) {
gs.error(e);
return null;
}
},
fixClientScriptBindings: function(sourceMacroponentId, targetMacroponentId) {
var targetMacroponentGr = new GlideRecord(this.UXF_TABLE.MACROPONENT);
targetMacroponentGr.get(targetMacroponentId);
var clientScriptSrcTargetMap = {};
var sourceClientScriptsGr = new GlideRecord("sys_ux_client_script");
sourceClientScriptsGr.addQuery("macroponent", sourceMacroponentId);
sourceClientScriptsGr.query();
while (sourceClientScriptsGr.next()) {
clientScriptSrcTargetMap[sourceClientScriptsGr.getValue("name")] = clientScriptSrcTargetMap[sourceClientScriptsGr.getValue("name")] || {};
clientScriptSrcTargetMap[sourceClientScriptsGr.getValue("name")].srcId = sourceClientScriptsGr.getUniqueValue();
}
var targetClientScriptsGr = new GlideRecord("sys_ux_client_script");
targetClientScriptsGr.addQuery("macroponent", targetMacroponentId);
targetClientScriptsGr.query();
while (targetClientScriptsGr.next()) {
clientScriptSrcTargetMap[targetClientScriptsGr.getValue("name")] = clientScriptSrcTargetMap[targetClientScriptsGr.getValue("name")] || {};
clientScriptSrcTargetMap[targetClientScriptsGr.getValue("name")].targetId = targetClientScriptsGr.getUniqueValue();
}
var targetMacroponentUpdatedProps = {};
for (var clientScriptName in clientScriptSrcTargetMap) {
if (clientScriptSrcTargetMap[clientScriptName].srcId && clientScriptSrcTargetMap[clientScriptName].targetId) {
if (targetMacroponentGr.getValue("composition")) {
targetMacroponentUpdatedProps.composition = targetMacroponentUpdatedProps.composition || targetMacroponentGr.getValue("composition") || "";
targetMacroponentUpdatedProps.composition = targetMacroponentUpdatedProps.composition.replace(new RegExp(clientScriptSrcTargetMap[clientScriptName].srcId, 'g'), clientScriptSrcTargetMap[clientScriptName].targetId);
}
if (targetMacroponentGr.getValue("internal_event_mappings")) {
targetMacroponentUpdatedProps.internal_event_mappings = targetMacroponentUpdatedProps.internal_event_mappings || targetMacroponentGr.getValue("internal_event_mappings") || "";
targetMacroponentUpdatedProps.internal_event_mappings = targetMacroponentUpdatedProps.internal_event_mappings.replace(new RegExp(clientScriptSrcTargetMap[clientScriptName].srcId, 'g'), clientScriptSrcTargetMap[clientScriptName].targetId);
}
if (targetMacroponentGr.getValue("data")) {
targetMacroponentUpdatedProps.data = targetMacroponentUpdatedProps.data || targetMacroponentGr.getValue("data") || "";
targetMacroponentUpdatedProps.data = targetMacroponentUpdatedProps.data.replace(new RegExp(clientScriptSrcTargetMap[clientScriptName].srcId, 'g'), clientScriptSrcTargetMap[clientScriptName].targetId);
}
}
}
this.GLOBAL.updateRecord(this.UXF_TABLE.MACROPONENT, targetMacroponentId, targetMacroponentUpdatedProps);
},
updateAppConfigForSubPagesRoutes: function(screenType, contentBlockId) {
var _self = this;
var screen = new GlideRecord(this.UXF_TABLE.SCREEN);
screen.get('screen_type', screenType);
var appConfig = screen.app_config;
var route_gr = new GlideRecord(this.UXF_TABLE.APP_ROUTE);
route_gr.addQuery('parent_macroponent', contentBlockId);
route_gr.addQuery('app_config', this.CONSTANTS.BASE_UXF_RECORDS.ACE_BASE_EXPERIENCE_APP_CONFIG);
route_gr.query();
while (route_gr.next()) {
var route_id = route_gr.sys_id;
this.GLOBAL.updateRecord(_self.UXF_TABLE.APP_ROUTE, route_id, {
'app_config': appConfig
});
}
},
getContentBlockSubPages: function(appName) {
var self = this;
var sub_pages_list = [];
var clone_macroponent_routes = self.CONSTANTS.CLONE_ROUTES;
var content_block_routes_gr = new GlideRecord(self.UXF_TABLE.APP_ROUTE);
content_block_routes_gr.addQuery('app_config', self.CONSTANTS.BASE_UXF_RECORDS.ACE_BASE_EXPERIENCE_APP_CONFIG);
content_block_routes_gr.addQuery('parent_macroponent', self.CONSTANTS.BASE_UXF_RECORDS.CONTENT_BLOCK.MACROPONENT);
content_block_routes_gr.query();
while (content_block_routes_gr.next()) {
var cloneMacroponent = clone_macroponent_routes.some(function(route) {
return route === content_block_routes_gr.getValue('route_type');
});
var screen_gr = new GlideRecord(self.UXF_TABLE.SCREEN);
screen_gr.get('screen_type', content_block_routes_gr.getValue('screen_type'));
sub_pages_list.push({
MACROPONENT: screen_gr.getValue('macroponent'),
SCREEN: screen_gr.getValue('sys_id'),
SCREEN_TYPE: content_block_routes_gr.getValue('screen_type'),
NAME: appName + " - " + content_block_routes_gr.getValue('name'),
ROUTE: content_block_routes_gr.getValue('route_type'),
CLONE_MACROPONENT: cloneMacroponent
});
}
return sub_pages_list;
},
_cloneMacroponentForSubpage: function(baseMacroponentId, subPageName, appContentBlockId) {
var self = this;
var content_block = self._getConstantsForPage('Content Block');
var macroponentCloneInfo = {
'sys_id': baseMacroponentId,
'fields': self.CONSTANTS.CLONE_FIELDS.MACROPONENT
};
// create new subpage macroponent
var newMacroponentId = self.GLOBAL._grRecordCreator(self.UXF_TABLE.MACROPONENT, {
'name': subPageName
}, macroponentCloneInfo);
var macroponentGr = new GlideRecord(self.UXF_TABLE.MACROPONENT);
if (!macroponentGr.get(newMacroponentId)) {
return;
}
// patch it with base composition
self.GLOBAL.updateRecord(self.UXF_TABLE.MACROPONENT, newMacroponentId, {
'composition': self._replaceAll(macroponentGr.getValue('composition'), content_block.MACROPONENT, appContentBlockId)
});
// ensure success
macroponentGr = new GlideRecord(self.UXF_TABLE.MACROPONENT);
if (!macroponentGr.get(newMacroponentId)) {
return;
}
return macroponentGr;
},
_updateNewMacroponentWithScripts: function(newMacroponentGr, clientScriptMaps, newMacroponentId) {
var self = this;
var postUpdate = {
'composition': newMacroponentGr.getValue('composition'),
'data': newMacroponentGr.getValue('data'),
'state_properties': newMacroponentGr.getValue('state_properties'),
'internal_event_mappings': newMacroponentGr.getValue('internal_event_mappings'),
};
clientScriptMaps.forEach(function(clientScriptMap) {
postUpdate.composition = self._replaceAll(
postUpdate.composition,
clientScriptMap.findId,
clientScriptMap.replaceWithId
);
postUpdate.data = self._replaceAll(
postUpdate.data,
clientScriptMap.findId,
clientScriptMap.replaceWithId
);
postUpdate.state_properties = self._replaceAll(
postUpdate.state_properties,
clientScriptMap.findId,
clientScriptMap.replaceWithId
);
postUpdate.internal_event_mappings = self._replaceAll(
postUpdate.internal_event_mappings,
clientScriptMap.findId,
clientScriptMap.replaceWithId
);
});
self.GLOBAL.updateRecord(self.UXF_TABLE.MACROPONENT, newMacroponentId, postUpdate);
},
_createScreenTypeForSubPage: function(subPage) {
var self = this;
var screenTypeCloneInfo = {
'sys_id': subPage.SCREEN_TYPE,
'fields': self.CONSTANTS.CLONE_FIELDS.SCREEN_TYPE
};
var newScreenTypeId = self.GLOBAL._grRecordCreator(self.UXF_TABLE.SCREEN_TYPE, {
'name': subPage.NAME
},
screenTypeCloneInfo);
return newScreenTypeId;
},
_createScreenForSubPage: function(subPage, newMacroponentId, appConfig, newScreenTypeId, appContentBlockId) {
var self = this;
var screenCloneInfo = {
'sys_id': subPage.SCREEN,
'fields': self.CONSTANTS.CLONE_FIELDS.UX_SCREEN
};
var screenPayload = {
'name': subPage.NAME,
'macroponent': newMacroponentId,
'app_config': appConfig,
'screen_type': newScreenTypeId,
'parent_macroponent': appContentBlockId
};
self.GLOBAL._grRecordCreator(self.UXF_TABLE.SCREEN, screenPayload, screenCloneInfo);
},
_patchScreenCollectionToRoute: function(subPage, appContentBlockId, appConfig, newScreenTypeId) {
var self = this;
var route_gr = new GlideRecord(self.UXF_TABLE.APP_ROUTE);
route_gr.addQuery('route_type', subPage.ROUTE);
route_gr.addQuery('parent_macroponent', appContentBlockId);
route_gr.addQuery('app_config', appConfig);
route_gr.query();
if (route_gr.next()) {
var route_id = route_gr.sys_id;
self.GLOBAL.updateRecord(self.UXF_TABLE.APP_ROUTE, route_id, {
'screen_type': newScreenTypeId
});
}
},
_cloneMacroponentAndScripts: function(baseMacroponent, subPageName, appContentBlockId) {
var self = this;
// MACROPONENT CREATION
var newMacroponentGr = self._cloneMacroponentForSubpage(baseMacroponent, subPageName, appContentBlockId);
if (!newMacroponentGr) {
return;
}
var newMacroponentSysId = newMacroponentGr.getValue('sys_id');
// CLONE CLIENT SCRIPTS
var clientScriptMaps = self._cloneClientScripts(baseMacroponent, newMacroponentSysId);
// POST UPDATE MACROPONENT
self._updateNewMacroponentWithScripts(newMacroponentGr, clientScriptMaps, newMacroponentSysId);
return newMacroponentSysId;
},
cloneSubPages: function(appName, appConfig, appContentBlockId, subPages) {
// TODO: appName is not used in this method but do not remove it here unless you are going to refactor the
// various callers using this method signature through UIB/UX tranform script, etc.
var self = this;
try {
subPages.forEach(function(subPage) {
if (!subPage) {
return;
}
// OPTIONAL MACROPONENT CREATION
var newMacroponentId = subPage.MACROPONENT; // initialize to base macroponent
if (subPage.CLONE_MACROPONENT) {
newMacroponentId = self._cloneMacroponentAndScripts(subPage.MACROPONENT, subPage.NAME, appContentBlockId);
}
//SCREEN_TYPE CREATION
var newScreenTypeId = self._createScreenTypeForSubPage(subPage);
//SCREEN CREATION
self._createScreenForSubPage(subPage, newMacroponentId, appConfig, newScreenTypeId, appContentBlockId);
// PATCH SCREEN COLLECTION TO ROUTE
self._patchScreenCollectionToRoute(subPage, appContentBlockId, appConfig, newScreenTypeId);
});
} catch (e) {
gs.error("ACEAppBuilderUtil: Failed to create content block subpages: " + e.toString());
}
},
fixMissingSubPages: function(aceBaseCbSubpages, appCbSubpages, appContentBlockId, appConfigSysId) {
try {
var self = this;
var appSubpagesMap = {};
for (var i = 0; i < appCbSubpages.length; i++) {
appSubpagesMap[appCbSubpages[i].ROUTE] = appCbSubpages[i];
}
var subpagesWithMissingMacroponent = aceBaseCbSubpages.filter(function(baseSubpage) {
var appSubpage = appSubpagesMap[baseSubpage.ROUTE];
if (!appSubpage) {
return false;
}
// for subpages that are flagged to have their macroponents cloned,
// if both base and app subpage point to the same macroponent, then the macroponent is missing
return appSubpage.CLONE_MACROPONENT && baseSubpage.MACROPONENT === appSubpage.MACROPONENT;
});
subpagesWithMissingMacroponent.forEach(function(baseSubpage) {
var newMacroponentId = self._cloneMacroponentAndScripts(baseSubpage.MACROPONENT, baseSubpage.NAME, appContentBlockId);
var appSubpage = appSubpagesMap[baseSubpage.ROUTE];
appSubpage.MACROPONENT = newMacroponentId;
self.syncScreenWithBase(baseSubpage, appSubpage, appConfigSysId);
});
} catch (e) {
gs.error('ACEAppBuilderUtil: Failed to fix missing content block subpages: ' + e.toString());
}
},
getPropertiesPayload: function(propertyIds) {
var supportedTypes = this.CONSTANTS.SUPPORTED_PROPERTIES_TYPES;
return this.GLOBAL.getPropertiesPayload(propertyIds, supportedTypes);
},
getControlCardQuery: function(table) {
//Example query: name=incident^internal_type=string^ORinternal_type=string_full_utf8^ORinternal_type=integer^ORinternal_type=choice^ORinternal_type=boolean
var query = 'name=' + table + '^';
var supportedFields = this.CONSTANTS.SUPPORTED_PROPERTIES_TYPES;
query += supportedFields.map(function(f, index) {
return (index > 0 ? '^OR' : '') + 'internal_type=' + f;
}).join('');
return query;
},
getAnalyticsCardQuery: function(tableName) {
var query = 'name=' + tableName + '^';
var tableGr = new GlideRecord('sys_db_object');
tableGr.addQuery('name', tableName);
tableGr.query();
if (tableGr.next()) {
var superClass = tableGr.getValue('super_class');
if (superClass) {
var superClassTableGr = new GlideRecord('sys_db_object');
if (superClassTableGr.get(superClass)) {
query += 'OR' + 'name=' + superClassTableGr.getValue('name') + '^';
}
}
}
query += 'internal_type!=collection^ORinternal_type=NULL';
return query;
},
getControl: function(contentBlockPayload) {
var result = [];
var _self = this;
try {
contentBlockPayload.forEach(function(control) {
if (!control.props_details) {
return;
}
var controlType = control.props_details.control_type;
if (controlType === 'button') {
var buttonLabel = control.props_details.button_label;
var buttonEventName = control.props_details.button_event_name;
var payload = {
'title': control.title,
'subtitle': control.subtitle,
'type': control.props_details.control_type,
'button_data': {
label: buttonLabel,
event_name: buttonEventName
}
};
result.push(payload);
} else if (controlType === 'record') {
if (control.props_details.table && control.props_details.record && control.props_details.field) {
var table = control.props_details.table;
var recordId = control.props_details.record;
var fieldName = control.props_details.field;
var helperText = control.props_details.helper_text;
var placeHolderText = control.props_details.placeholder_text || '';
var isDisabled = (control.props_details.disabled === true);
var payload = {
'title': control.title,
'subtitle': control.subtitle,
'type': control.props_details.control_type,
'record_data': {}
};
if (fieldName) {
payload.record_data.field_name = fieldName;
var recordDetails = _self.GLOBAL.getRecord(table, recordId, [payload.record_data.field_name]);
if (recordDetails[payload.record_data.field_name] != undefined) {
payload.record_data.field_type = recordDetails.__internal_type;
payload.record_data.sys_id = recordId;
payload.record_data.table = table;
payload.record_data.old_value = recordDetails[payload.record_data.field_name];
payload.record_data.read_only = isDisabled || !(recordDetails.__canWrite && recordDetails.__isInSelectedScope);
if (helperText) {
payload.record_data.helper_text = helperText;
}
if (placeHolderText) {
payload.record_data.placeholder_text = placeHolderText;
}
if (recordDetails.__choices)
payload.record_data.choices = recordDetails.__choices;
}
}
result.push(payload);
}
}
});
return result;
} catch (e) {
return result;
}
},
_updateRecord: function(table, recordId, input) {
try {
return this.GLOBAL.updateRecord(table, recordId, input);
} catch (e) {
gs.error('ACEAppBuilderUtil: Error: ' + e.toString());
return false;
}
},
setControl: function(payload) {
try {
var updated = true,
_self = this;
for (var recordId in payload) {
var record = payload[recordId];
if (record.new_value != record.old_value) {
var input = {};
input[record.field_name] = record.new_value;
updated = _self._updateRecord(record.table, recordId, input) && updated;
}
}
return updated;
} catch (e) {
return false;
}
},
createAiSearchConfig: function(appConfigId, searchConfigBlockId) {
var searchSourceId, searchProfileId, searchApplicationId, appName, searchContextContentBlockId;
//fetching app name
if (!this.CAN_READ) return 3;
var gr = new GlideRecord(this.TABLE.APP_CONFIG);
if (gr.get(appConfigId)) {
appName = gr.getValue('name');
}
if (this.GLOBAL._isAisEnabled()) {
var dataSource = this.GLOBAL._grGetKeyDataFromTable('ais_datasource', this.AIS.searchIndex, 'source');
if (!this.GLOBAL.AISAlreadyExists(appName)) {
if (dataSource != null) {
searchSourceId = this.GLOBAL._grRecordCreator('ais_search_source', {
'name': appName + this.AIS.source,
'datasource': dataSource,
'condition': 'ace_config=' + appConfigId + ''
}, null, true);
}
if (searchSourceId != null) {
searchProfileId = this.GLOBAL._grRecordCreator('ais_search_profile', {
'label': appName + this.AIS.profile
}, null, true);
//map search source to profile
this.GLOBAL._grRecordCreator('ais_search_profile_ais_search_source_m2m', {
'profile': searchProfileId,
'search_source': searchSourceId
});
//publishing search profile
var searchProfileName = this.GLOBAL._grGetKeyDataFromTable('ais_search_profile', searchProfileId, 'name');
this.GLOBAL._aisPublishProfile(searchProfileName);
}
//creating search application
if (searchProfileId != null) {
searchApplicationId = this.GLOBAL._grRecordCreator('sys_search_context_config', {
'name': appName + this.AIS.application,
'search_profile': searchProfileId,
'genius_results_limit': 3
}, null, true);
}
// creating search context block
if (searchApplicationId != null) {
searchContextContentBlockId = this._grRecordCreator(this.TABLE.CONTENT_BLOCK, {
'type': this.CONSTANTS.CONTENT_BLOCK_TYPES.SEARCH_CONTEXT,
'name': appName + ' - Default Search Context',
'ace_config': appConfigId,
'parent': searchConfigBlockId,
'order': 10,
'document_id': appConfigId,
'document_table': this.TABLE.APP_CONFIG
});
this._setGlideVars(this.TABLE.CONTENT_BLOCK, searchContextContentBlockId, {
'search_application': searchApplicationId,
'evam_configuration': this.AIS.evam_config
});
}
if (searchContextContentBlockId != null) {
return 2;
}
} else {
return 1;
}
} else {
//zing search configuration
return 0;
}
return 3;
},
fetchRouteSearchResult: function(aceAppId, contentBlockId) {
var _self = this;
if (!this.CAN_READ) return null;
var contentBlockGr = new GlideRecord(this.TABLE.CONTENT_BLOCK);
if (contentBlockGr.get(contentBlockId)) {
if (contentBlockGr.getValue('document_table') == this.TABLE.PAGE) {
var pageId = contentBlockGr.getValue('document_id');
var gr = new GlideRecord(this.TABLE.PAGE);
if (gr.get(pageId)) {
var routeId = gr.getValue('route');
var route = this.GLOBAL._grGetKeyDataFromTable(_self.UXF_TABLE.APP_ROUTE, routeId, 'route_type');
return getURL(aceAppId, route);
}
} else {
return getURL(aceAppId, '');
}
}
function getURL(aceAppId, route) {
var gr = new GlideRecord(_self.TABLE.APP_CONFIG);
var url = '';
if (gr.get(aceAppId)) {
url = '/now/' + _self.getURLPath(gr.getValue('app_config')) + '/' + route;
}
return url;
}
},
fetchSearchConfig: function(aceAppId) {
var _self = this;
function getURL(route, contentBlockId) {
var gr = new GlideRecord(_self.TABLE.APP_CONFIG);
var url = '';
if (gr.get(aceAppId)) {
url = '/now/' + _self.getURLPath(gr.getValue('app_config')) + '/' + route + '/' + contentBlockId;
}
return url;
}
var result = {};
if (!this.CAN_READ) return result;
var searchConfigBlocks = this._getContentBlocks('', {
'document_table': this.TABLE.APP_CONFIG,
'document_id': aceAppId,
'parent': '',
'type': this.CONSTANTS.CONTENT_BLOCK_TYPES.SEARCH_CONFIG
});
if (searchConfigBlocks.length > 0) {
var pageId = searchConfigBlocks[0].props_details.page;
var gr = new GlideRecord(this.TABLE.PAGE);
if (gr.get(pageId)) {
var routeId = gr.getValue('route');
var route = this.GLOBAL._grGetKeyDataFromTable(_self.UXF_TABLE.APP_ROUTE, routeId, 'route_type');
result = {
page_route: route,
mode: searchConfigBlocks[0].props_details.mode,
sys_id: searchConfigBlocks[0].block_id,
url: getURL(route, searchConfigBlocks[0].block_id)
};
}
return result;
}
},
getSearchContexts: function(aceAppId, searchConfigContentBlockId) {
var result = [];
if (!this.CAN_READ) return result;
var searchContexts = this._getContentBlocks(searchConfigContentBlockId, {
'ace_config': aceAppId,
'type': this.CONSTANTS.CONTENT_BLOCK_TYPES.SEARCH_CONTEXT
});
searchContexts.forEach(function(context) {
if (context.props_details.evam_configuration && context.props_details.search_application) {
result.push({
'search_config': context.props_details.search_application,
'evam_config': context.props_details.evam_configuration
});
}
});
return result;
},
activateSearchConfig: function(aceAppId, searchConfigContentBlockId) {
var result = this.getSearchContexts(aceAppId, searchConfigContentBlockId);
if (result.length > 0) {
var gr = new GlideRecord(this.TABLE.CONTENT_BLOCK);
if (gr.get(searchConfigContentBlockId) && gr.canWrite()) {
gr.setValue('active', true);
gr.update();
return true;
}
}
return false;
},
getSubPageRoutes: function(appConfig, contentBlockId, appName) {
var self = this;
var routes = [];
var clone_macroponent_routes = self.CONSTANTS.CLONE_ROUTES;
var contentBlockGr = new GlideRecord(self.UXF_TABLE.APP_ROUTE);
contentBlockGr.addQuery('app_config', appConfig);
contentBlockGr.addQuery('parent_macroponent', contentBlockId);
contentBlockGr.query();
while (contentBlockGr.next()) {
var cloneMacroponent = clone_macroponent_routes.some(function(route) {
return route === contentBlockGr.getValue('route_type');
});
var screen_gr = new GlideRecord(self.UXF_TABLE.SCREEN);
screen_gr.addQuery('screen_type', contentBlockGr.getValue('screen_type'));
screen_gr.orderBy('sys_created_on');
screen_gr.setLimit(1);
screen_gr.query();
while (screen_gr.next()) {
var screen_id = screen_gr.getValue('sys_id');
}
screen_gr.get('screen_type', contentBlockGr.getValue('screen_type'));
routes.push({
MACROPONENT: screen_gr.getValue('macroponent'),
SCREEN: screen_id,
SCREEN_TYPE: contentBlockGr.getValue('screen_type'),
NAME: appName + " - " + contentBlockGr.getValue('name'),
ROUTE: contentBlockGr.getValue('route_type'),
ROUTE_ID: contentBlockGr.getValue('sys_id'),
VIEWPORT_ID: contentBlockGr.getValue('parent_macroponent_composition_element_id'),
CLONE_MACROPONENT: cloneMacroponent
});
}
return routes;
},
getClientScriptDetails: function(macroponentSysId) {
var client_scripts = [];
var client_script_gr = new GlideRecord('sys_ux_client_script');
client_script_gr.addQuery('macroponent', macroponentSysId);
client_script_gr.query();
while (client_script_gr.next()) {
client_scripts.push({
sys_id: client_script_gr.getValue('sys_id').toString(),
name: client_script_gr.getValue('name').toString(),
script: client_script_gr.getValue('script').toString(),
macroponent_id: macroponentSysId
});
}
return client_scripts;
},
getAppConfigDetails: function(aceAppConfig) {
var self = this;
if (!this.CAN_READ) return {};
var app_config_gr = new GlideRecord(self.TABLE.APP_CONFIG);
if (app_config_gr.get(aceAppConfig)) {
return {
app_config: app_config_gr.getValue('app_config').toString(),
context: JSON.parse(app_config_gr.getValue('context')),
app_name: app_config_gr.getValue('name'),
version: app_config_gr.getValue('version'),
compatibility: app_config_gr.getValue('compatibility'),
scope: app_config_gr.getValue('sys_scope')
};
}
},
updateAppConfigWithVersion: function(aceAppConfig, version, compatibleVersion) {
var self = this;
var app_config_gr = new GlideRecord(self.TABLE.APP_CONFIG);
if (app_config_gr.get(aceAppConfig)) {
app_config_gr.setValue('version', version);
app_config_gr.setValue('compatibility', compatibleVersion);
}
app_config_gr.update();
},
syncNewSubPages: function(appName, aceBaseCbRoutes, appCbRoutes, appConfig) {
var self = this;
var content_block_id = appConfig.context.content_block_id;
var routes_diff = aceBaseCbRoutes.filter(function(acebase_route) {
return !appCbRoutes.some(function(route) {
return acebase_route.ROUTE === route.ROUTE;
});
});
routes_diff.forEach(function(route) {
var cloneInfo = {
'sys_id': route.ROUTE_ID,
'fields': self.CONSTANTS.CLONE_FIELDS.APP_ROUTE
};
var payload = {
'name': route.NAME,
'app_config': appConfig.app_config,
'parent_macroponent': content_block_id,
'parent_macroponent_composition_element_id': route.VIEWPORT_ID
};
self.GLOBAL._grRecordCreator(self.UXF_TABLE.APP_ROUTE, payload, cloneInfo);
});
self.cloneSubPages(appName, appConfig.app_config, content_block_id, routes_diff);
},
syncClientScriptsWithBase: function(aceBaseCbSubpage, appCbSubpage) {
var self = this;
var ace_base_client_scripts = self.getClientScriptDetails(aceBaseCbSubpage.MACROPONENT);
var app_client_scripts = self.getClientScriptDetails(appCbSubpage.MACROPONENT);
app_client_scripts.forEach(function(app_client_script) {
var ace_base_client_script = ace_base_client_scripts.filter(function(ace_base_client_script) {
return ace_base_client_script.name === app_client_script.name;
});
if (ace_base_client_script.length > 0) {
self.GLOBAL.updateRecord(self.UXF_TABLE.CLIENT_SCRIPT, app_client_script.sys_id, {
'script': ace_base_client_script[0].script
});
}
});
//Fetch client scripts that are part of a subpage in ace base content block and do not exist in app's content block su page
var client_script_diff = ace_base_client_scripts.filter(function(ace_base_client_script) {
return !app_client_scripts.some(function(app_client_script) {
return ace_base_client_script.name === app_client_script.name;
});
});
if (client_script_diff.length > 0) {
var macroponent = {};
var macroponent_gr = new GlideRecord(self.UXF_TABLE.MACROPONENT);
if (macroponent_gr.get(appCbSubpage.MACROPONENT)) {
macroponent.composition = macroponent_gr.getValue('composition'),
macroponent.data = macroponent_gr.getValue('data'),
macroponent.state_properties = macroponent_gr.getValue('state_properties'),
macroponent.internal_event_mappings = macroponent_gr.getValue('internal_event_mappings');
}
client_script_diff.forEach(function(client_script) {
var client_script_id = self.GLOBAL._grRecordCreator(self.UXF_TABLE.CLIENT_SCRIPT, {
'macroponent': appCbSubpage.MACROPONENT,
'name': client_script.name,
'script': client_script.script,
});
macroponent.composition = self._replaceAll(macroponent.composition, client_script.sys_id, client_script_id);
macroponent.data = self._replaceAll(macroponent.data, client_script.sys_id, client_script_id);
macroponent.state_properties = self._replaceAll(macroponent.state_properties, client_script.sys_id, client_script_id);
macroponent.internal_event_mappings = self._replaceAll(macroponent.internal_event_mappings, client_script.sys_id, client_script_id);
});
self.GLOBAL.updateRecord(self.UXF_TABLE.MACROPONENT, appCbSubpage.MACROPONENT, {
'composition': macroponent.composition,
'data': macroponent.data,
'state_properties': macroponent.state_properties,
'internal_event_mappings': macroponent.internal_event_mappings,
});
}
},
syncMacroponentWithBase: function(aceBaseCbSubpage, appCbSubpage, appConfig) {
var self = this;
var macroponent = {};
var ace_base_macroponent_gr = new GlideRecord(self.UXF_TABLE.MACROPONENT);
if (ace_base_macroponent_gr.get(aceBaseCbSubpage.MACROPONENT)) {
macroponent.composition = ace_base_macroponent_gr.getValue('composition'),
macroponent.data = ace_base_macroponent_gr.getValue('data'),
macroponent.layout = ace_base_macroponent_gr.getValue('layout'),
macroponent.properties = ace_base_macroponent_gr.getValue('props'),
macroponent.state_properties = ace_base_macroponent_gr.getValue('state_properties'),
macroponent.internal_event_mappings = ace_base_macroponent_gr.getValue('internal_event_mappings'),
macroponent.dispatched_events = ace_base_macroponent_gr.getValue('dispatched_events'),
macroponent.handled_events = ace_base_macroponent_gr.getValue('handled_events');
}
macroponent.composition = self._replaceAll(macroponent.composition, self.CONSTANTS.BASE_UXF_RECORDS.CONTENT_BLOCK.MACROPONENT, appConfig.context.content_block_id);
var ace_base_client_scripts = self.getClientScriptDetails(aceBaseCbSubpage.MACROPONENT);
var app_client_scripts = self.getClientScriptDetails(appCbSubpage.MACROPONENT);
app_client_scripts.forEach(function(app_client_script) {
var client_script = ace_base_client_scripts.filter(function(ace_base_client_script) {
return app_client_script.name === ace_base_client_script.name;
});
if (client_script.length > 0) {
macroponent.composition = self._replaceAll(
macroponent.composition,
client_script[0].sys_id,
app_client_script.sys_id
);
macroponent.data = self._replaceAll(
macroponent.data,
client_script[0].sys_id,
app_client_script.sys_id
);
macroponent.state_properties = self._replaceAll(
macroponent.state_properties,
client_script[0].sys_id,
app_client_script.sys_id
);
macroponent.internal_event_mappings = self._replaceAll(
macroponent.internal_event_mappings,
client_script[0].sys_id,
app_client_script.sys_id
);
}
});
self.GLOBAL.updateRecord(self.UXF_TABLE.MACROPONENT, appCbSubpage.MACROPONENT, {
'composition': macroponent.composition,
'data': macroponent.data,
'layout': macroponent.layout,
'props': macroponent.properties,
'state_properties': macroponent.state_properties,
'internal_event_mappings': macroponent.internal_event_mappings,
'dispatched_events': macroponent.dispatched_events,
'handled_events': macroponent.handled_events
});
},
syncRouteWithBase: function(aceBaseCbSubpage, appCbSubpage, appConfig) {
var self = this;
var route = {};
var ace_base_route_gr = new GlideRecord(self.UXF_TABLE.APP_ROUTE);
if (ace_base_route_gr.get(aceBaseCbSubpage.ROUTE_ID)) {
route.name = ace_base_route_gr.getValue('name'),
route.fields = ace_base_route_gr.getValue('fields'),
route.optional_parameters = ace_base_route_gr.getValue('optional_parameters'),
route.parent_macroponent_composition_element_id = ace_base_route_gr.getValue('parent_macroponent_composition_element_id');
}
self.GLOBAL.updateRecord(self.UXF_TABLE.APP_ROUTE, appCbSubpage.ROUTE_ID, {
'name': appConfig.app_name + "-" + route.name,
'parent_macroponent': appConfig.context.content_block_id,
'fields': route.fields,
'optional_parameters': route.optional_parameters,
'parent_macroponent_composition_element_id': route.parent_macroponent_composition_element_id,
'screen_type': appCbSubpage.SCREEN_TYPE,
'app_config': appConfig.app_config,
});
},
syncScreenWithBase: function(aceBaseCbSubpage, appCbSubpage, appConfig) {
var self = this;
var screen = {};
var ace_base_screen_gr = new GlideRecord(self.UXF_TABLE.SCREEN);
if (ace_base_screen_gr.get(aceBaseCbSubpage.SCREEN)) {
var content_block_routes_gr = new GlideRecord(self.UXF_TABLE.APP_ROUTE);
content_block_routes_gr.addQuery('app_config', self.CONSTANTS.BASE_UXF_RECORDS.ACE_BASE_EXPERIENCE_APP_CONFIG);
content_block_routes_gr.addQuery('parent_macroponent', self.CONSTANTS.BASE_UXF_RECORDS.CONTENT_BLOCK.MACROPONENT);
content_block_routes_gr.addQuery('screen_type', ace_base_screen_gr.getValue('screen_type'));
content_block_routes_gr.query();
if (!content_block_routes_gr.next())
return;
screen.name = appConfig.app_name + ' - ' + content_block_routes_gr.getValue('name');
screen.macroponent_config = ace_base_screen_gr.getValue('macroponent_config');
screen.event_mappings = ace_base_screen_gr.getValue('event_mappings');
screen.page_definition = (appCbSubpage.CLONE_MACROPONENT) ? appCbSubpage.MACROPONENT : aceBaseCbSubpage.MACROPONENT;
}
self.GLOBAL.updateRecord(self.UXF_TABLE.SCREEN, appCbSubpage.SCREEN, {
'name': screen.name,
'screen_type': appCbSubpage.SCREEN_TYPE,
'macroponent': screen.page_definition,
'parent_macroponent': appConfig.context.content_block_id,
'macroponent_config': screen.macroponent_config,
'event_mappings': screen.event_mappings,
'app_config': appConfig.app_config
});
},
hardSyncWithBase: function(aceAppConfig) {
var self = this;
var app_config = self.getAppConfigDetails(aceAppConfig);
var app_name = app_config.app_name;
var app_content_block_id = app_config.context.content_block_id;
var ace_base_cb_subpages = self.getSubPageRoutes(self.CONSTANTS.BASE_UXF_RECORDS.ACE_BASE_EXPERIENCE_APP_CONFIG, self.CONSTANTS.BASE_UXF_RECORDS.CONTENT_BLOCK.MACROPONENT, app_name);
var app_cb_subpages = self.getSubPageRoutes(app_config.app_config, app_content_block_id, app_name);
self.syncNewSubPages(app_name, ace_base_cb_subpages, app_cb_subpages, app_config);
app_cb_subpages.forEach(function(subpage) {
var ace_base_cb_subpage = ace_base_cb_subpages.filter(function(acebase_subpage) {
return acebase_subpage.ROUTE === subpage.ROUTE;
});
if (ace_base_cb_subpage.length > 0) {
if (subpage.CLONE_MACROPONENT) {
self.syncMacroponentWithBase(ace_base_cb_subpage[0], subpage, app_config);
self.syncClientScriptsWithBase(ace_base_cb_subpage[0], subpage);
}
self.syncRouteWithBase(ace_base_cb_subpage[0], subpage, app_config);
self.syncScreenWithBase(ace_base_cb_subpage[0], subpage, app_config);
}
});
self.fixMissingSubPages(ace_base_cb_subpages, app_cb_subpages, app_content_block_id, app_config.app_config);
//sync content block macroponent and client scripts
self.syncMacroponentWithBase({
MACROPONENT: self.CONSTANTS.BASE_UXF_RECORDS.CONTENT_BLOCK.MACROPONENT
}, {
MACROPONENT: app_content_block_id
}, app_config);
self.syncClientScriptsWithBase({
MACROPONENT: self.CONSTANTS.BASE_UXF_RECORDS.CONTENT_BLOCK.MACROPONENT
}, {
MACROPONENT: app_content_block_id
});
},
hasReadAccess: function() {
return gs.hasRole('sn_ace.ace_user');
},
getRoutesForAcePage: function(aceAppConfig) {
var routeSysIds = [];
var sn_ace_page_gr = new GlideRecord(this.TABLE.PAGE);
sn_ace_page_gr.addQuery('ace_app', aceAppConfig);
sn_ace_page_gr.query();
while (sn_ace_page_gr.next()) {
routeSysIds.push(sn_ace_page_gr.getValue('route'));
}
return routeSysIds;
},
getMacroponentsFromRoute: function(routes) {
var self = this;
var macroponentSysIds = [];
routes.forEach(function(route) {
var gr_route = new GlideRecord(self.UXF_TABLE.APP_ROUTE);
gr_route.get(route);
gr_route.query();
if (gr_route.next()) {
var gr_screen = new GlideRecord(self.UXF_TABLE.SCREEN);
gr_screen.addQuery('screen_type', gr_route.screen_type);
gr_screen.query();
if (gr_screen.next()) {
macroponentSysIds.push(gr_screen.macroponent.toString());
}
}
})
return macroponentSysIds;
},
appendAceContentTreeProperty: function(composition, aceRenderer) {
var self = this;
if (!composition.length) {
return;
}
composition.forEach(function(child) {
if (child.definition && child.definition.id == aceRenderer && child.propertyValues.payload) {
child.propertyValues['contentTree'] = child.propertyValues.payload;
delete child.propertyValues.payload;
}
if (child.overrides && child.overrides.composition) {
self.appendAceContentTreeProperty(child.overrides.composition, aceRenderer);
}
});
},
updateMacroponentComposition: function(macroponents, contentBlockId, aceRendererId) {
var self = this;
macroponents.forEach(function(macroponentId) {
var macroGr = new GlideRecord(self.UXF_TABLE.MACROPONENT);
if (macroGr.get(macroponentId)) {
var composition = macroGr.getValue('composition');
var newComposition = JSON.parse((self._replaceAll(composition, contentBlockId, aceRendererId)));
self.appendAceContentTreeProperty(newComposition, aceRendererId);
self.GLOBAL.updateRecord(self.UXF_TABLE.MACROPONENT, macroponentId, {
composition: JSON.stringify(newComposition)
});
}
});
},
migrateToAceRenderer: function(aceAppConfig) {
var self = this;
var app_config = self.getAppConfigDetails(aceAppConfig);
var migrationStatus = gs.getMessage("Experience {0} migration to ACE v2 initiated", [app_config.app_name]);
if (gs.getCurrentApplicationId() != app_config.scope) {
migrationStatus = gs.getMessage("Cross Scope Alert! Failed to migrate experience {0} to ACE v2", [app_config.app_name]);;
return migrationStatus;
}
if (app_config.version != 'v2' && app_config.context && app_config.context.content_block_id) {
var app_content_block_id = app_config.context.content_block_id;
var routeSysIds = self.getRoutesForAcePage(aceAppConfig);
var macroponents = self.getMacroponentsFromRoute(routeSysIds);
try {
self.updateMacroponentComposition(macroponents, app_content_block_id, self.CONSTANTS.ACE_CONTENT_MANAGER_ID);
self.updateAppConfigWithVersion(aceAppConfig, 'v2', 'v1');
migrationStatus = gs.getMessage("Experience {0} has been migrated to ACE v2 successfully", [app_config.app_name]);
} catch (e) {
migrationStatus = gs.getMessage("Failed to migrate experience {0} to ACE v2", [app_config.app_name]);
gs.error("Failed to migrate experience to ACE v2" + e.toString());
}
} else {
migrationStatus = gs.getMessage("Experience {0} cannot be migrated to ACE v2", [app_config.app_name]);
}
return migrationStatus;
},
_getContentBlockMetaData: function(pageId, defBlockId, instanceBlockId, reloadCache, isClonedBlock) {
try {
var blockParameters = [];
this.gr_counter += 1;
var encodedQuery = "";
var contentBlocks = [];
var appConfig = ""; //Default App Config of ACE
var screenType = "";
var aceAppConfigGr;
var pageMacroponentId = "";
var experienceMetadata = {};
if (pageId) {
var pageGr = new GlideRecord('sn_ace_page');
pageGr.get(pageId);
if (pageGr.isValidRecord()) {
appConfig = pageGr.ace_app.app_config.toString();
screenType = pageGr.route.screen_type.toString();
}
experienceMetadata = this.getExperienceMetadata(reloadCache, appConfig);
var screenGr = new GlideRecord("sys_ux_screen");
screenGr.addQuery("screen_type", screenType);
screenGr.query();
if (screenGr.next()) {
pageMacroponentId = screenGr.macroponent.toString();
}
encodedQuery += "document_table=sn_ace_page^document_id=" + pageId;
contentBlocks = this._getContentBlockMetaDataForBlockId(pageId, encodedQuery, experienceMetadata);
} else if (defBlockId) {
var blockGr = new GlideRecord(this.TABLE.CONTENT_BLOCK);
if (instanceBlockId) {
blockGr.addQuery("sys_id", "IN", [defBlockId, instanceBlockId].join(","));
blockGr.query();
var defContentBlocks = [],
instanceContentBlocks = [];
while (blockGr.next()) {
if (blockGr.getUniqueValue() == defBlockId) {
aceAppConfigGr = new GlideRecord(this.TABLE.APP_CONFIG);
aceAppConfigGr.get(blockGr.document_id.toString());
if (aceAppConfigGr.isValidRecord()) {
appConfig = aceAppConfigGr.app_config.toString();
}
if (blockGr.variable["block_variables"]) {
blockParameters = JSON.parse(blockGr.variable["block_variables"]);
}
experienceMetadata = this.getExperienceMetadata(reloadCache, appConfig);
encodedQuery = "document_table=sn_ace_content_block^document_id=" + defBlockId;
defContentBlocks = isClonedBlock ? [] : this._getContentBlockMetaDataForBlockId(defBlockId, encodedQuery, experienceMetadata);
} else {
var parentPageId = blockGr.getValue('document_id');
var parentPageGr = new GlideRecord(this.TABLE.PAGE);
if (parentPageGr.get(parentPageId)) {
appConfig = parentPageGr.ace_app.app_config.toString();
}
experienceMetadata = this.getExperienceMetadata(reloadCache, appConfig);
encodedQuery = "document_table=sn_ace_content_block^document_id=" + instanceBlockId;
instanceContentBlocks = this._getContentBlockMetaDataForBlockId(instanceBlockId, encodedQuery, experienceMetadata);
}
}
if (isClonedBlock) {
contentBlocks = instanceContentBlocks;
} else {
this._mergeReusableBlocks(defContentBlocks, instanceContentBlocks);
contentBlocks = defContentBlocks;
}
} else {
blockGr.get(defBlockId);
if (blockGr.isValidRecord()) {
aceAppConfigGr = new GlideRecord(this.TABLE.APP_CONFIG);
aceAppConfigGr.get(blockGr.document_id.toString());
if (aceAppConfigGr.isValidRecord()) {
appConfig = aceAppConfigGr.app_config.toString();
}
if (blockGr.variable["block_variables"]) {
blockParameters = JSON.parse(blockGr.variable["block_variables"]);
}
}
experienceMetadata = this.getExperienceMetadata(reloadCache, appConfig);
encodedQuery += "document_table=sn_ace_content_block^document_id=" + defBlockId;
contentBlocks = this._getContentBlockMetaDataForBlockId(defBlockId, encodedQuery, experienceMetadata);
}
}
return {
contentBlocks: contentBlocks,
blockParameters: blockParameters,
pageMacroponentId: pageMacroponentId
};
} catch (e) {
gs.error('ACEAppBuilderUtil: Error while fetching content blocks: ' + e.toString());
return {
contentBlocks: []
};
}
},
_getContentBlockMetaDataForBlockId: function(artifactId, encodedQuery, experienceMetadata) {
var contentBlocksMap = {
'_root': []
};
//Step 1,2: get all content blocks and build the parent-child relationship map of their sys_ids
var gr = new GlideRecord(this.TABLE.CONTENT_BLOCK);
gr.addEncodedQuery(encodedQuery);
gr.orderBy('order');
gr.query();
var blockIdsPartOfQuery = [];
while (gr.next()) {
var blockId = gr.getUniqueValue();
blockIdsPartOfQuery.push(blockId);
var blockParentId = gr.getValue('parent');
var parentBlockId = (artifactId != blockId) ? blockParentId : null;
if (!parentBlockId) {
contentBlocksMap._root.push(blockId);
} else {
contentBlocksMap[parentBlockId] = contentBlocksMap[parentBlockId] || [];
contentBlocksMap[parentBlockId].push(blockId);
}
}
var parentBlockIds = Object.keys(contentBlocksMap);
parentBlockIds.forEach(function(parentId) {
if (parentId != "_root" && blockIdsPartOfQuery.indexOf(parentId) == -1) {
//parent block is not part of the instance of reusable block's content slot
if (contentBlocksMap[parentId] && contentBlocksMap[parentId].length > 0) {
contentBlocksMap._root.push(contentBlocksMap[parentId][0]);
delete contentBlocksMap[parentId];
}
}
});
var contentBlocksLevel = [];
//Step 3: build a 2D array of the step-level hierarchy
this._buildContentBlockLevels(contentBlocksMap, contentBlocksMap._root, contentBlocksLevel, 0);
//Step 4: build the content block contract
return this._buildContentBlockTreeFromLevels(contentBlocksMap, contentBlocksLevel, {}, experienceMetadata, true);
},
getDefaultAppConfigId: function() {
var gr = new GlideRecord('sn_ace_app_config');
gr.get(this.ACE_DEFAULT_APP_CONFIG);
return gr.getValue('app_config');
},
getAppConfigId: function(inputParam) {
var blockId = inputParam.blockId;
var pageId = inputParam.pageId;
var result = "";
if (pageId) {
var gr = new GlideRecord('sn_ace_page');
result = gr.get(pageId) ? gr.ace_app.app_config + '' : this.getDefaultAppConfigId();
} else if (blockId) {
var blockGr = new GlideRecord(this.TABLE.CONTENT_BLOCK);
blockGr.get(blockId);
if (blockGr.isValidRecord()) {
var aceAppConfigGr = new GlideRecord(this.TABLE.APP_CONFIG);
aceAppConfigGr.get(blockGr.document_id.toString());
if (aceAppConfigGr.isValidRecord()) {
result = aceAppConfigGr.app_config.toString();
}
}
}
return result;
},
aceEvent: function() {
return {
name: "ACE_ACTION_PERFORMED",
label: "ACE EVENT",
properties: [{
"name": "eventName",
"label": "Event Name",
"fieldType": "string",
"typeMetadata": {
"schema": {
"type": "string"
}
},
"valueType": "string"
},
{
"name": "eventPayload",
"label": "Event Payload",
"fieldType": "json"
}
]
};
},
getVariableChoices: function() {
var choiceGr = new GlideRecord('sys_choice');
choiceGr.addEncodedQuery('nameSTARTSWITHvar__m_sn_ace_variable_');
choiceGr.query();
var choices = {};
while (choiceGr.next()) {
var choice = {};
choice.language = choiceGr.getValue('language');
choice.label = choiceGr.getValue('label');
choice.value = choiceGr.getValue('value');
choice.id = choiceGr.getValue('value');
choice.sequence = choiceGr.getValue('sequence');
choice.element = choiceGr.getValue('element');
var tableName = choiceGr.getValue('name');
if (!choices.hasOwnProperty(tableName))
choices[tableName] = [];
choices[tableName].push(choice);
}
return choices;
},
getVariables: function() {
var choices = this.getVariableChoices();
var variableGr = new GlideRecord(this.TABLE.VARIABLE);
variableGr.orderBy('order');
variableGr.query();
var varObj = {};
while (variableGr.next()) {
var _variable = {};
_variable.label = variableGr.getValue('label');
_variable.name = variableGr.getValue('element');
_variable.fieldType = variableGr.internal_type.name + '';
var defaultValue = variableGr.getValue('default_value');
_variable.defaultValue = defaultValue == null ? "" : defaultValue;
var fieldChoices = choices[variableGr.getValue('name')];
if (fieldChoices && fieldChoices.length) {
var newChoices = fieldChoices.filter(function(choice) {
return variableGr.getValue('element') == choice.element;
});
if (newChoices.length) {
_variable.typeMetadata = {};
var enumArr = [];
_variable.typeMetadata.choices = newChoices;
newChoices.forEach(function(choice) {
enumArr.push(choice.value);
});
_variable.typeMetadata.schema = {
"enum": enumArr,
"type": "string"
};
}
}
var modelId = variableGr.getValue('model');
if (varObj.hasOwnProperty(modelId))
varObj[modelId].push(_variable);
else
varObj[modelId] = [_variable];
}
return varObj;
},
getContentBlockProperties: function(inputParam) {
var configId = this.getAppConfigId(inputParam);
var varObj = this.getVariables();
var cbTypes = {};
var contentTypes = this.GLOBAL.getCachedContentTypeMetaData();
var cbTypeGr = new GlideRecord(this.TABLE.TYPE);
cbTypeGr.addEncodedQuery('route!=NULL');
cbTypeGr.orderBy('name');
cbTypeGr.query();
while (cbTypeGr.next()) {
var cbType = {};
cbType.variables = varObj[cbTypeGr.getUniqueValue()];
cbType.typeId = cbTypeGr.getUniqueValue();
cbType.name = cbTypeGr.getValue('name');
var route = cbTypeGr.getValue('route');
cbType.nested = cbTypeGr.getValue('nested') == 1 ? true : false;
cbType.macroponent = "";
if (configId && contentTypes[configId] && contentTypes[configId][route]) {
var macroponent = contentTypes[configId][route]['macroponent'];
cbType.macroponent = !macroponent ? "" : macroponent;
} else {
// Fallback to ACE Base Experience if Macroponent ID is not available
// for a route in an app config / experience.
var baseExpConfigId = this.CONSTANTS.BASE_UXF_RECORDS.ACE_BASE_EXPERIENCE_APP_CONFIG;
if (contentTypes[baseExpConfigId] && contentTypes[baseExpConfigId][route]) {
var macroponentId = contentTypes[baseExpConfigId][route]['macroponent'];
cbType.macroponent = !macroponentId ? "" : macroponentId;
}
}
var allowedBlocks = cbTypeGr.getValue('allowed_blocks');
cbType.allowed_blocks = !allowedBlocks ? "" : allowedBlocks;
cbType.category = cbTypeGr.getValue('category');
cbType.events = [];
cbType.icon = cbTypeGr.getValue('icon');
cbType.events.push(this.aceEvent());
cbType.sysId = cbTypeGr.getUniqueValue();
cbType.slots = [];
var slots = cbTypeGr.getValue('slots') || "";
if (slots.length) {
cbType.slots = slots.split(',');
}
if (cbTypes.hasOwnProperty(route)) {
var sysId = cbTypeGr.getUniqueValue();
cbTypes[route + '||' + sysId] = cbType;
} else {
cbTypes[route] = cbType;
}
}
return cbTypes;
},
tryParse: function(obj) {
var resp = {};
try {
resp.value = JSON.parse(obj);
resp.status = true;
} catch (e) {
resp.status = false;
resp.message = "Error in parsing the object"
}
return resp;
},
uxEvents: function() {
var eventGr = new GlideRecord('sys_ux_event');
eventGr.query();
var events = {};
while (eventGr.next()) {
var event = {};
event.label = eventGr.getValue('label');
event.name = eventGr.getValue('event_name');
var eventPropValues = eventGr.getValue('props') ? eventGr.getValue('props').replace(/\n/g, " ") : "[]";
try {
event.properties = JSON.parse(eventPropValues);
} catch (e) {
event.properties = {};
gs.error("ACEAppBuilderUtilV2: Unable to parse event prop values, exception: " + e);
}
events[eventGr.getUniqueValue()] = event;
}
return events;
},
getMacroponentIcons: function() {
var icons = {};
var toolboxCompGr = new GlideRecord('sys_uib_toolbox_component');
toolboxCompGr.query();
while (toolboxCompGr.next()) {
icons[toolboxCompGr.getValue('macroponent')] = toolboxCompGr.getValue('icon');
}
return icons;
},
getMacroponentProperties: function() {
var events = this.uxEvents();
var icons = this.getMacroponentIcons();
var compGr = new GlideRecord('sys_ux_macroponent');
compGr.addEncodedQuery('root_component!=NULL^category=component^ORcategory=primitives^ORcategory=shared^ORcategory=viewport');
compGr.orderBy('name');
compGr.query();
var components = {};
var _this = this;
while (compGr.next()) {
var translatableStringMap = {};
var comp = {};
var propValues = compGr.getValue('props') ? compGr.getValue('props').replace(/\n/g, " ") : "[]";
var props = JSON.parse(propValues);
if (props && props.length) {
props.forEach(function(prop, index) {
if (prop.fieldType == "json" && prop.hasOwnProperty('defaultValue') && prop.defaultValue) {
var defaultValue = props[index]['defaultValue'];
var resp = _this.tryParse(defaultValue);
if (resp.status)
props[index]['defaultValue'] = resp.value;
} else if (prop.fieldType == "choice") {
var choices = prop.typeMetadata ? prop.typeMetadata.choices : [];
choices && choices.map(function(choice) {
choice.id = choice.value;
});
}
if (prop.typeMetadata && prop.fieldType == 'json') {
var translatableProp = _this.getTranslatableProp(prop.typeMetadata, 'translatable', '');
if (translatableProp && translatableProp.length > 0) {
translatableStringMap[prop.name] = translatableProp;
}
} else if (prop && prop.typeMetadata && prop.typeMetadata.translatable) {
translatableStringMap[prop.name] = prop.defaultValue;
}
});
}
comp.translatableStringMap = translatableStringMap;
comp.componentName = compGr.getValue('name');
comp.componentTag = compGr.getDisplayValue('root_component');
comp.properties = props;
comp.root_element_metadata = JSON.parse(compGr.getValue('root_component_definition'));
comp.events = [];
comp.icon = icons[compGr.getUniqueValue()];
var dispatchedEvents = compGr.getValue('dispatched_events');
if (dispatchedEvents) {
eventList = dispatchedEvents.split(',');
eventList.forEach(function(eventId) {
comp.events.push(events[eventId]);
});
}
var rootElementMetadata = compGr.getValue('root_component_definition');
if (rootElementMetadata && rootElementMetadata.length) {
rootElementMetadata = JSON.parse(rootElementMetadata);
comp.slots = rootElementMetadata.availableSlots || [];
}
components[comp.componentTag] = comp;
}
return components;
},
getCachedMetadataProperties: function(inputParam, reload) {
if (reload)
this.GLOBAL.flushCachedMacroponentCBProperties();
var aceMetaData = this.GLOBAL.getCachedMacroponentCBProperties();
if (aceMetaData)
return aceMetaData;
var metadata = {};
metadata.contentTypes = this.getContentBlockProperties(inputParam);
metadata.componentProperties = this.getMacroponentProperties();
this.GLOBAL.putCachedMacroponentCBProperties(metadata);
return metadata;
},
/**
* Util method to process visibility and post scripts the content blocks within the given content tree.
*
* @param {Array} contentTree - The content tree to process.
* @param {GlideRecord} contentBlockGr - A gliderecord from sn-ace-content-block table to evaluate the scripts.
* @param {Object} cbScriptVariables
*
* @returns {Array} Returns the content blocks after processing visibility and post scripts.
*/
processCachedContentBlocksUtil: function(contentTree, contentBlockGr, cbScriptVariables) {
var _self = this;
var outputBlocks = [];
contentTree.forEach(function(contentBlock) {
/**
* Set the contentBlockGr scope and package to the contentBlock's scope and package.
* This ensures that there wont be any scope related issues in evaluating scripts.
*/
contentBlockGr.sys_scope = contentBlock.scope;
contentBlockGr.sys_package = contentBlock.package;
/**
* Set the contentBlockGr's visibility to the contentBlock's visibility.
*/
var visibilityScript = contentBlock.visibility;
contentBlockGr.visibility = visibilityScript;
/**
* Only process the curret block if the block's visibility script evaluates to true.
*/
if (_self._visibilityEvaluator(contentBlockGr, cbScriptVariables.pageParams, cbScriptVariables.blockcontext, cbScriptVariables.dataParams)) {
// First process the child blocks of the current block.
if (contentBlock.children && contentBlock.children.length) {
var childBlocks = _self.processCachedContentBlocksUtil(contentBlock.children, contentBlockGr, cbScriptVariables);
contentBlock.children = childBlocks;
}
_self._evaluateCBScriptVariables(contentBlockGr, contentBlock, cbScriptVariables);
if (contentBlock.post_script && contentBlock.post_script.length) {
contentBlockGr.post_script = contentBlock.post_script;
_self._evaluatePostScript(contentBlockGr, contentBlock, cbScriptVariables.pageParams, cbScriptVariables.blockcontext, cbScriptVariables.dataParams);
}
outputBlocks.push(contentBlock);
}
});
return outputBlocks;
},
/**
* As part of ACE performance improvement, we are storing the page bundle in the sn_ace_page record.
* This bundle will contain the structure of the content blocks with all the required metadata to render the ACE page.
*
* @param {Array} contentTree - The content tree to process.
* @param {Object} cbScriptVariables
*
* @returns {Array} Content blocks with evaluated post script and visibility script.
*/
processCachedContentBlocks: function(contentTree, cbScriptVariables) {
/**
* Evaluation of content block visibility and post scripts requires a real GlideRecord object that is
* in the DB. We can reuse just one content block record and change its visibility and post scripts
* to process all the blocks in the content tree.
* Here we check if the content tree is not empty and pick the first block from the content tree.
*/
if (contentTree && contentTree.length) {
var contentBlockGrId = contentTree[0].block_id;
var contentBlockGr = new GlideRecord(this.TABLE.CONTENT_BLOCK);
contentBlockGr.get(contentBlockGrId);
return this.processCachedContentBlocksUtil(contentTree, contentBlockGr, cbScriptVariables);
}
return [];
},
/**
* Utility method to refresh a content block. Refreshing a block executes the given block's scripts again
* to re-fetch data.
*
* @param {Object} block - Data of the block that needs to be refreshed.
* @param {Object} cbScriptVariables - An object containing current pageParams, blockcontext.
* @param {boolean} shouldRefreshChildBlocks - A flag used to decide whether the refresh operation should be executed
* for the given block's children.
* @param {GlideRecord} contentBlockGr
*/
refreshContentBlock: function(block, cbScriptVariables, shouldRefreshChildBlocks, contentBlockGr) {
var _self = this;
var refreshedBlock = JSON.parse(JSON.stringify(block));
if (refreshedBlock) {
if (!contentBlockGr) {
/**
* One time operation to get a persisted content block record.
* This single record can be used to evaluate scripts for all the nested content blocks.
* For subsequent recursive calls of refreshContentBlock method, the contentBlockGr value will be non-null.
*/
contentBlockGr = new GlideRecord(this.TABLE.CONTENT_BLOCK);
contentBlockGr.get(refreshedBlock.block_id);
}
/**
* Only refresh the child content blocks if shouldRefreshChildBlocks is true. Bottom-up approach.
*/
if (shouldRefreshChildBlocks && refreshedBlock.children && refreshedBlock.children.length) {
refreshedBlock.children = refreshedBlock.children.filter(function(childBlock) {
var refreshedChildBlock = refreshContentBlock(childBlock, cbScriptVariables, shouldRefreshChildBlocks, contentBlockGr);
if (refreshedChildBlock) {
return refreshedChildBlock;
}
});
}
/**
* Set the contentBlockGr scope and package to the contentBlock's scope and package.
* This ensures that there wont be any scope related issues in evaluating scripts.
*/
contentBlockGr.sys_scope = refreshedBlock.scope;
contentBlockGr.sys_package = refreshedBlock.package;
/**
* Set the contentBlockGr's visibility to the contentBlock's visibility.
*/
contentBlockGr.visibility = refreshedBlock.visibility;
/**
* Only process the current block if the visibility script evaluates to true.
*/
if (_self._visibilityEvaluator(contentBlockGr, cbScriptVariables.pageParams, cbScriptVariables.blockcontext, cbScriptVariables.dataParams)) {
_self._evaluateCBScriptVariables(contentBlockGr, refreshedBlock, cbScriptVariables);
// Evaluate post script first.
if (refreshedBlock.post_script && refreshedBlock.post_script.length) {
contentBlockGr.post_script = refreshedBlock.post_script;
_self._evaluatePostScript(contentBlockGr, refreshedBlock, cbScriptVariables.pageParams, cbScriptVariables.blockcontext, cbScriptVariables.dataParams);
}
// Evaluate lazy script.
if (refreshedBlock.lazy_script && refreshedBlock.lazy_script.length) {
contentBlockGr.lazy_script = refreshedBlock.lazy_script;
_self._evaluateLazyScript(contentBlockGr, refreshedBlock, cbScriptVariables.pageParams, cbScriptVariables.blockcontext, cbScriptVariables.dataParams);
}
return refreshedBlock;
}
}
return;
},
getCachedProperties: function(reload) {
if (reload) {
this.GLOBAL.flushCachedMacroponentCBProperties();
}
var cachedProperties = this.GLOBAL.getCachedMacroponentCBProperties();
if (cachedProperties) {
return cachedProperties;
} else {
var inputParam = {};
return this.getCachedMetadataProperties(inputParam);
}
},
/**
* Utility method to find a key in nested json.
*
* @param {Object} obj - json object.
* @param {string} key - key that will be search in object .
*/
getTranslatableProp: function(obj, key, prev) {
if (!prev) {
prev = '';
}
var result = [];
for (var prop in obj) {
var path = prev + (prev ? '.' : '') + prop;
if (prop == key) {
result.push(path.replace('.' + key, '').split(".").pop());
} else if (typeof obj[prop] == 'object') {
var resp = this.getTranslatableProp(obj[prop], key, path);
if (resp.length > 0) {
Array.prototype.push.apply(result, resp);
}
}
}
return result
},
evaluateDataBlocks: function(dataBlocks, cbScriptVariables) {
var executedDataBlockOutput = {};
if (dataBlocks && dataBlocks.length) {
var dataBlockIds = dataBlocks.map(function(block) {
return block.block_id;
});
var blockIdToBlockMap = {};
dataBlocks.forEach(function(dataBlock) {
blockIdToBlockMap[dataBlock.block_id] = dataBlock;
});
var dataBlockGr = new GlideRecord(this.TABLE.CONTENT_BLOCK);
dataBlockGr.addEncodedQuery('sys_idIN', dataBlockIds.join(','));
dataBlockGr.query();
while (dataBlockGr.next()) {
var evaluationOutput = this._evaluateLazyScript(dataBlockGr, blockIdToBlockMap[dataBlockGr.getUniqueValue()], cbScriptVariables.pageParams, cbScriptVariables.blockcontext, cbScriptVariables.dataParams);
executedDataBlockOutput[dataBlockGr.getUniqueValue()] = evaluationOutput;
}
}
return executedDataBlockOutput;
},
_evaluateDataBlock: function(dataBlockGr, dataBlockObject, cbScriptVariables) {
var dataBlockEvaluationOutput;
if (dataBlockObject && dataBlockObject.props_details) {
var evaluationSource = dataBlockObject.props_details.evaluation_source;
if (evaluationSource == 'script') {
dataBlockEvaluationOutput = this._evaluateLazyScript(dataBlockGr, dataBlockObject, cbScriptVariables.pageParams, cbScriptVariables.blockcontext, cbScriptVariables.dataParams);
} else if (evaluationSource == 'flow_action') {
// TODO: Implement flow action way of evaluating data blocks.
}
}
return dataBlockEvaluationOutput;
},
eagerlyEvaluateDataBlocks: function(contentBlocks, cbScriptVariables) {
var executedDataBlockOutput = {};
var _self = this;
if (contentBlocks && contentBlocks.length) {
var dataBlocks = [];
var dataBlockIds = [];
contentBlocks.forEach(function(contentBlock) {
if (contentBlock.type && contentBlock.type.id == _self.DATA_BLOCK_CONTENT_TYPE && contentBlock.props_details &&
contentBlock.props_details.evaluation_mode == 'eager') {
dataBlocks.push(contentBlock);
dataBlockIds.push(contentBlock.block_id);
}
});
var blockIdToBlockMap = {};
dataBlocks.forEach(function(dataBlock) {
blockIdToBlockMap[dataBlock.block_id] = dataBlock;
});
var dataBlockGr = new GlideRecord('sn_ace_content_block');
dataBlockGr.addEncodedQuery('sys_idIN' + dataBlockIds.join(','));
dataBlockGr.query();
while (dataBlockGr.next()) {
var evaluationOutput = this._evaluateDataBlock(dataBlockGr, blockIdToBlockMap[dataBlockGr.getUniqueValue()], cbScriptVariables);
executedDataBlockOutput[dataBlockGr.getUniqueValue()] = evaluationOutput;
}
}
contentBlocks.forEach(function(contentBlock) {
if (executedDataBlockOutput[contentBlock.block_id]) {
contentBlock.props_details.data_block_output = executedDataBlockOutput[contentBlock.block_id];
}
});
return contentBlocks;
},
addDataBlockParamsToScriptVariables: function(contentBlocks, scriptVariables) {
var _self = this;
var modifiedScriptVariables = scriptVariables;
var dataParams = {};
contentBlocks.forEach(function(contentBlock) {
if (contentBlock.type.id === _self.DATA_BLOCK_CONTENT_TYPE && contentBlock.elementId) {
dataParams[contentBlock.elementId] = contentBlock.props_details.data_block_output;
};
});
modifiedScriptVariables['dataParams'] = dataParams;
return modifiedScriptVariables;
},
removeTranslationPrefix: function(prop) {
prop = prop.includes(this.TRANSLATED_TEXT_PREFIX) ? prop.replaceAll(this.TRANSLATED_TEXT_PREFIX, "") : prop;
return prop;
},
type: 'ACEAppBuilderUtilV2'
};
Sys ID
290c6147c306211094d388c7c840dd4e