importPackage(Packages.de.elo.ix.client);
//@include lib_Class.js
//@include lib_sol.common.Cache.js
//@include lib_sol.common.UserUtils.js
//@include lib_sol.common.IxUtils.js
//@include lib_sol.common.jc.ActionHandler.js
/**
* This class loads ELO Business Solutions action definitions and creates the ribbon, button groups and buttons.
*
* This class is automatically called during client startup.
*
* @author ELO Digital Office GmbH
* @version 1.09.000
*
* @requires sol.common.IxUtils
*/
sol.define("sol.common.jc.ActionDefinitionUtils", {
singleton: true,
TAB_CONST: {
HOME: { name: "home" },
DOCUMENT: { name: "document" },
ARCHIVE: { name: "archive" },
VIEW: { name: "view" },
WORKFLOW: { name: "workflow" },
INTRAY: { name: "intray" },
SEARCH: { name: "search" },
NEW: { name: "new" },
OUTPUT: { name: "output" },
ORGANIZE: { name: "organize" }
},
BUTTON_MODES: {
BIG: { name: "big", jcPrio: 1 },
SMALL: { name: "small", jcPrio: 3 }
},
initialize: function (config) {
var me = this;
me.$super("sol.Base", "initialize", [config]);
},
/**
* Initializes ribbon buttons, groups and tabs for ELO Business Solutions.
*
* If called with a context, the method will use the old JavaClient way to initialize all the ribbon buttons.
* If called without a context, the method will use the new dynamic button registration.
*
* @param {Object} ctx (optional) The current script scope.
*/
initializeRibbon: function (ctx) {
var me = this,
result, errorMessage;
me.logJavaInfo();
try {
result = sol.common.IxUtils.execute("RF_sol_common_services_ActionDefinitionCollector", {});
if (result && result.definitions) {
me.jcVersion20OrLater = sol.common.IxUtils.checkVersion(workspace.getClientVersion(), "20.00.000");
me.buildRibbonFromDefinition(result.definitions, ctx);
}
} catch (ex) {
errorMessage = String(ex);
me.logger.error("loading ribbon definition failed.", errorMessage);
}
},
/**
* @private
* Logs the Java version and the path of the JRE
*/
logJavaInfo: function () {
var me = this,
urlLoaderClassName = "com.sun.webkit.network.URLLoader",
javaRuntimeVersion, bitness, javaVersion, urlLoaderClass, urlLoaderClassLocation, urlLoaderJarDate;
javaRuntimeVersion = String(java.lang.System.getProperty("java.runtime.version"));
bitness = String(java.lang.System.getProperty("sun.arch.data.model"));
javaVersion = javaRuntimeVersion + " - " + bitness + " bit";
me.logger.info("java.version=" + javaVersion);
me.logger.info("java.home=" + java.lang.System.getProperty("java.home"));
me.logger.info("java.ext.dirs=" + java.lang.System.getProperty("java.ext.dirs"));
try {
urlLoaderClass = java.lang.Class.forName(urlLoaderClassName);
urlLoaderClassLocation = urlLoaderClass.protectionDomain.codeSource.location;
urlLoaderJarDate = new Date(new java.io.File(urlLoaderClassLocation.toURI()).lastModified());
me.logger.info(["Location of the class '{0}': {1} (last modified: {2})", urlLoaderClassName, urlLoaderClassLocation, urlLoaderJarDate]);
} catch (ignore) {
}
},
/**
* @private
* Builds the java client ribbon based on ribbon button / group / tab definitions given by RF_sol_common_services_ActionDefinitionCollector.
* @param {Object[]} defs action/ ribbon definition
* @param {Object} ctx
*/
buildRibbonFromDefinition: function (defs, ctx) {
var me = this,
dynRibbon = {
_tabs: {},
tabs: [],
_bands: {},
bands: [],
_buttons: {},
buttons: []
},
def, i, j, rib,
tabIdentifierValid, bandIdentifierValid, buttonIdentifierValid;
for (i = 0; i < defs.length; i++) {
def = defs[i];
// legacy action definition - just one ribbon definition instead of an array
if (def.ribbon) {
def.ribbons = [def.ribbon];
}
if (def.ribbons) {
for (j = 0; j < def.ribbons.length; j++) {
rib = def.ribbons[j];
tabIdentifierValid = me.isValidId("rib.ribbonTab.name", rib.ribbonTab.name);
if (rib && rib.button && rib.ribbonTab && rib.buttongroup && tabIdentifierValid) {
if (!dynRibbon._tabs[rib.ribbonTab.name]) {
dynRibbon._tabs[rib.ribbonTab.name] = true;
dynRibbon.tabs.push({
name: rib.ribbonTab.name,
text: rib.ribbonTab.text,
position: rib.ribbonTab.position,
access: rib.ribbonTab.access
});
}
bandIdentifierValid = me.isValidId("rib.buttongroup.name", rib.buttongroup.name);
if (!dynRibbon._bands[rib.buttongroup.name] && bandIdentifierValid) {
dynRibbon._bands[rib.buttongroup.name] = true;
dynRibbon.bands.push({
ribbonTab: rib.ribbonTab,
name: rib.buttongroup.name,
text: rib.buttongroup.text,
position: rib.buttongroup.position
});
}
buttonIdentifierValid = me.isValidId("rib.button.name", rib.button.name);
if (buttonIdentifierValid) {
if (!dynRibbon._buttons[rib.button.name]) {
dynRibbon._buttons[rib.button.name] = {
type: def.type,
action: def.action,
button: rib.button,
additionalButtonPositions: [],
ribbonTab: rib.ribbonTab,
buttongroup: rib.buttongroup
};
dynRibbon.buttons.push(dynRibbon._buttons[rib.button.name]);
} else {
dynRibbon._buttons[rib.button.name].additionalButtonPositions.push({ ribbonTab: rib.ribbonTab, buttongroupName: rib.buttongroup.name });
}
}
}
}
}
}
me.registerTabs(dynRibbon.tabs, ctx);
me.registerBands(dynRibbon.bands, ctx);
me.registerButtons(dynRibbon.buttons, ctx);
},
/**
* @private
* Registers the tabs.
* @param {Object[]} tabDefinitions This contains one action definitions per tab which was found
* @param {Object} ctx (optional) Whether this is set or not, this method will use the old or the new way to register tabs in the client (see {@link #initializeRibbon})
*/
registerTabs: function (tabDefinitions, ctx) {
var me = this,
tabsArray = [];
if (ctx) {
tabDefinitions.forEach(function (def) {
if (!me.TAB_CONST[def.name]) {
tabsArray.push(def.position + "," + def.text);
}
});
ctx.getExtraTabs = function () {
return tabsArray.join(";");
};
} else {
tabDefinitions.forEach(function (def) {
var tab;
if (!me.TAB_CONST[def.name]) {
tab = ribbon.addTab(def.position, null, def.name);
tab.setTitle(def.text);
if (def.access && (me.jcVersion20OrLater === true)) {
tab.setVisibleCallback(function () {
return me.buttonEnabledHandler(def.access, def.name);
}, me);
}
}
});
}
},
/**
* @private
* Registers the buton groups.
* @param {Object[]} bandDefinitions This contains one action definitions per button group which was found
* @param {Object} ctx (optional) Whether this is set or not, this method will use the old or the new way to register tabs in the client (see {@link #initializeRibbon})
*/
registerBands: function (bandDefinitions, ctx) {
var me = this,
bandsArray = [];
if (ctx) {
bandDefinitions.forEach(function (def) {
var tabDef = me.TAB_CONST[def.ribbonTab.name] || def.ribbonTab;
bandsArray.push(tabDef.name + "," + def.position + "," + def.text);
});
ctx.getExtraBands = function () {
return bandsArray.join(";");
};
} else {
bandDefinitions.forEach(function (def) {
var tabDef = me.TAB_CONST[def.ribbonTab.name] || def.ribbonTab;
ribbon.addBand(tabDef.name, def.position, def.name).setTitle(def.text);
});
}
},
/**
* @private
* Registers the butons.
* @param {Object[]} buttonDefinitions This contains one action definitions per button which was found
* @param {Object} ctx (optional) Whether this is set or not, this method will use the old or the new way to register tabs in the client (see {@link #initializeRibbon})
*/
registerButtons: function (buttonDefinitions, ctx) {
var me = this;
buttonDefinitions.sort(function (def1, def2) {
return def1.button.position - def2.button.position;
});
if (ctx) {
me.registerButtonsOld(buttonDefinitions, ctx);
} else {
me.registerButtonsNew(buttonDefinitions);
}
},
/**
* @private
* Registers the butons in the old fashion.
* @param {Object[]} buttonDefinitions This contains one action definitions per button group which was found
* @param {Object} ctx
*/
registerButtonsOld: function (buttonDefinitions, ctx) {
var me = this,
buttonPositions = [];
buttonDefinitions.forEach(function (def) {
var tabDef = me.TAB_CONST[def.ribbonTab.name] || def.ribbonTab;
buttonPositions.push(def.button.jc.buttonId + "," + tabDef.name + "," + def.buttongroup.text + "," + me.getPriority(def));
me.buttonEnabledStatic(def.button.jc.buttonId, def.button.access);
ctx["eloScriptButton" + def.button.jc.buttonId + "Start"] = function () {
me.buttonHandler(def);
};
ctx["getScriptButton" + def.button.jc.buttonId + "Name"] = function () {
return def.button.text;
};
ctx["getScriptButton" + def.button.jc.buttonId + "Tooltip"] = function () {
return def.button.tooltipText;
};
});
ctx.getScriptButtonPositions = function () {
return buttonPositions.join(";");
};
},
/**
* @private
* Registers the butons in the new dynamic way.
* @param {Object[]} buttonDefinitions This contains one action definitions per button group which was found
*/
registerButtonsNew: function (buttonDefinitions) {
var me = this,
abp;
buttonDefinitions.forEach(function (def) {
var tabDef = me.TAB_CONST[def.ribbonTab.name] || def.ribbonTab,
btn = ribbon.addButton(tabDef.name, def.buttongroup.name, def.button.name),
imageName = me.getIconName(def.button), i;
if (def.additionalButtonPositions && def.additionalButtonPositions.length > 0) {
if (typeof btn.addPosition == "function") {
for (i = 0; i < def.additionalButtonPositions.length; i++) {
abp = def.additionalButtonPositions[i];
tabDef = me.TAB_CONST[abp.ribbonTab.name] || abp.ribbonTab;
btn.addPosition(tabDef.name, abp.buttongroupName);
}
} else {
me.logger.error("ActionDef Button: " + def.button.name + " - additionalButtonPositions found. This feature is not supported by this client. Please update to a newer version.");
}
}
btn.setCallback(function () {
me.buttonHandler(def);
}, me);
btn.setTitle(def.button.text);
btn.setTooltip(def.button.tooltipText);
btn.setIconName(imageName);
btn.asTile(def.button.asTile === true);
btn.setTileIconName(imageName);
btn.setPriority(me.getPriority(def));
if (def.button.access && def.button.access.solTypes) {
btn.setEnabledCallback(function () {
return me.buttonEnabledHandler(def.button.access, def.button.name);
}, me);
} else {
me.buttonEnabledStatic(btn.fctNr - 9000, def.button.access);
}
if (btn.setDefaultPinState) {
btn.setDefaultPinState(!!def.button.pinned);
}
});
},
/**
* @private
* @param {String} idType Identifier type
* @param {String} id Identifier
* @return {Boolean} is valid
*/
isValidId: function (idType, id) {
var me = this,
valid = true,
idRegEx;
idType = (idType || "ID") + "";
id += "";
idRegEx = "^[0-9a-zA-Z_-]*$";
if (!id) {
me.logger.warn("ID is missing: idType=" + idType);
valid = false;
} else if (!id.match(idRegEx)) {
me.logger.warn("ID contains illegal characters: idType=" + idType + ", id=" + id + ", idRegEx=" + idRegEx);
valid = false;
}
return valid;
},
/**
* @private
* @param {Object} buttonDef
* @return {String}
*/
getIconName: function (buttonDef) {
var me = this;
if (buttonDef && buttonDef.iconName && (me.jcVersion20OrLater === true)) {
return buttonDef.iconName;
}
return "ScriptButton" + buttonDef.jc.buttonId;
},
/**
* @private
* Retrieves the priority from a button definition.
* @param {Object} btnDef
* @return {Number}
*/
getPriority: function (btnDef) {
var me = this;
return (btnDef.buttongroup.mode === me.BUTTON_MODES.SMALL.name) ? me.BUTTON_MODES.SMALL.jcPrio : me.BUTTON_MODES.BIG.jcPrio;
},
/**
* @private
* Checks, when the button should be enabled, and calls the `setScriptButtonEnabled` method accordingly.
* This is used by the old way of registering buttons and only while the button is initialized.
* @param {String} btnId A button id
* @param {Object} access A button access definition
*/
buttonEnabledStatic: function (btnId, access) {
if (access && (access.document || access.folder || access.multi)) {
workspace.setScriptButtonEnabled(
btnId,
access.document === true,
access.folder === true,
false,
false,
false,
access.multi === true
);
}
},
/**
* @private
* Checks, if the button should be enabled. This is used by the the new way of registering buttons and will be called dynamically.
* @param {Object} access The access element of a button definition
* @return {Boolean}
*/
buttonEnabledHandler: function (access, targetName) {
var me = this,
activeView, activeNavBarSelection, checkElement, selectionCount, firstSelected, allSelected, current, i;
// if there is no access definition (or it has no content) the element should always be enabled
if (!access || (!access.document && !access.folder && !access.multi && !access.solTypes)) { // nothing set, button will always be enabled
return true;
}
try {
activeView = workspace.activeView;
} catch (ex) {
me.logger.warn("Active view not available: " + ex);
return false;
}
activeNavBarSelection = workspace.workspace.navigationBar && workspace.workspace.navigationBar.active && workspace.workspace.navigationBar.active.selectedIds;
// function to check a single indexed element
checkElement = function (e) {
var solType, fieldValue, conditions, visible = false;
if (!e) {
return false;
}
if ((access.document !== true || access.folder !== true) && ((access.document === true && !e.isDocument()) || (access.folder === true && !e.isStructure()))) {
return false;
}
if (!access.solTypes || (access.solTypes.length <= 0)) {
return true;
}
try {
solType = e.getObjKeyValue("SOL_TYPE") + "";
if (solType != "" && access.solTypes.indexOf(solType) !== -1) {
conditions = access.conditions;
if (conditions && Array.isArray(conditions)) {
// we're checking if one of our filter criterias is not
// true. This is when our some-function returns true
// (and we're inverting this for the 'visible' variable)
visible = !conditions.some(function (condition) {
if (condition.key && condition.value) {
try {
fieldValue = e.getObjKeyValue(condition.key) + "";
return fieldValue != condition.value;
} catch (ex) {
me.logger.warn(["Button/Tab access check failed. Condition Field {0} does not exist in mask of clicked object {1}", condition.key, e.name], targetName);
return true;
}
} else {
me.logger.error("Button/Tab access check failed. Bad condition configuration.", targetName);
return true;
}
});
} else {
visible = true;
}
}
} catch (ex) {
// can not get value from the SOL_TYPE field
visible = false;
}
return visible;
};
selectionCount = (activeView) ? activeView.selectionCount : ((activeNavBarSelection) ? activeNavBarSelection.length : 0);
if (selectionCount <= 0) {
return false; // nothing selected
}
// check single access
if (access.multi !== true) {
if (selectionCount !== 1) {
return false;
}
firstSelected = (activeView) ? activeView.firstSelected : ((activeNavBarSelection) ? archive.getElement(workspace.workspace.navigationBar.active.selectedIds[0]) : null);
return checkElement(firstSelected);
}
// check multi select
if (activeView && activeView.allSelected) { // for normal views
allSelected = activeView.allSelected;
while (allSelected.hasMoreElements()) {
current = allSelected.nextElement();
if (!checkElement(current)) {
return false;
}
}
} else if (activeNavBarSelection) { // for AppViews, because those do not have an active view in 9.03
allSelected = workspace.workspace.navigationBar.active.selectedIds; // do not try this at home
for (i = 0; i < allSelected.length; i++) {
current = allSelected[i];
if (!checkElement(archive.getElement(current))) {
return false;
}
}
} else {
return false;
}
return true;
},
/**
* This handles the button execution. This is used by the old and the new way of registering buttons alike.
* @param {Object} def A button definition
*/
buttonHandler: function (def) {
var me = this,
objId = me.getSelection(def.button.access);
switch (def.type) {
case "ADVANCED_ACTION":
sol.common.jc.ActionHelper.executeAdvancedAction(objId, def.action);
break;
case "SIMPLE_ACTION":
sol.common.jc.ActionHelper.executeSimpleAction(objId, def.action);
break;
default:
me.logger.error("No handler found for button definition", def.button.name);
}
},
/**
* @private
* returns the selected element objIds.
* @param {Object} access This object determines if this returns just one or many objIds (in case of multiselect).
* @return {String|String[]}
*/
getSelection: function (access) {
var allSelected, selected, objIds;
if (workspace.activeView && workspace.activeView.allSelectedArchiveElements) {
allSelected = workspace.activeView.allSelectedArchiveElements;
objIds = [];
while (allSelected.hasMoreElements()) {
selected = allSelected.nextElement();
if (selected.id) {
objIds.push(selected.id);
}
}
} else if (workspace.workspace.navigationBar && workspace.workspace.navigationBar.active && workspace.workspace.navigationBar.active.selectedIds) {
// FIXME as soon as the JC supports the retrival of selected elements from "AppViews" this should use the official API
allSelected = workspace.workspace.navigationBar.active.selectedIds; // do not try this at home
objIds = allSelected.map(function (objId) {
return objId;
}); // convert to JavaScript array
}
if (access && (access.multi === true)) {
return (objIds && objIds.length > 0) ? objIds : null;
} else {
return (objIds && objIds.length > 0) ? objIds[0] : null;
}
}
});
// This will be used by ELO10 JavaClients //
function eloExpandRibbon() {
if (typeof ribbon !== "undefined") {
sol.common.jc.ActionDefinitionUtils.initializeRibbon();
}
}
// This is the fallback for older JavaClients //
(function (ctx) {
if (typeof ribbon === "undefined") {
sol.common.jc.ActionDefinitionUtils.initializeRibbon(ctx);
}
}(this));