UI
The UI contains web resource files of different content types that the REST layer must set when returning the resource. API Platform supports a default mapping for HTML, JavaScript and JSON files.
The ui
element in metadata.json file contains the file names that render the policy and a Javascript file feeds the UI. When creating a metadata.json file for your custom policy follow these guidelines covering all aspect of the policy.
-
edit: rendered for edit mode in the implementation tab.
-
view: rendered for view-only mode, for example as seen in a deployment.
Metadata for each UI component contains three attributes html, js and l10nbundle which are relative paths for the root HTML, JavaScript and l10 bundles.
"ui": {
"edit": {
"html": "custompolicy-edit.html",
"js": "custompolicy-edit.js",
"helpInfo": "#helpInfo",
"helpUrl": "http://www.oracle.com",
"helpTopicId": "policies.custompolicy"
},
"view": {
"html": "custompolicy-view.html",
"js": "custompolicy-view.js",
"helpInfo": "#helpInfo",
"helpUrl": "http://www.oracle.com",
"helpTopicId": "policies.custompolicy"
},
"l10nbundle": "L10n/custompolicy.json"
},
The attribute helpInfo refers to a string to be displayed as is. Presence of the HASH (#) sign in the beginning of the value of “helpInfo” as in #apiReqHelpInfo
indicates that the string is to be referenced from the l10nbundle file request.json
.
Sample contents for {policy name}-edit.js
Use the sample files as a model while creating HTML and JS files for your policy.
function PolicyConfigurationModel(ko, $, oj, config, additionalParams) {
self = this;
self.config = config;
self.conditions = ko.observableArray();
self.l10n = ko.observable(additionalParams.l10nbundle);
self.disableApplyPolicyButton = additionalParams.disableApplyPolicyButton;
self.opList = ko.observableArray([
{"value" : "=", "label" : "="},
{"value" : "!=", "label" : "!="},
{"value" : ">", "label" : ">"},
{"value" : "<", "label" : "<"},
{"value" : ">=", "label" : ">="},
{"value" : "<=", "label" : "<="},
{"value" : "null", "label" : self.l10n()['label.isnull']},
{"value" : "not null", "label" : self.l10n()['label.isnotnull']}
]);
self.actions = ko.observableArray([
{"value" : "PASS", "label" : self.l10n()['label.pass']},
{"value" : "REJECT", "label" : self.l10n()['label.reject']}
]);
self.conjunctions = ko.observableArray([
{"value" : "ANY", "label" : self.l10n()['label.any']},
{"value" : "ALL", "label" : self.l10n()['label.all']}
]);
self.selectedAction = ko.observableArray(["REJECT"]);
self.selectedConjunction = ko.observableArray(["ANY"]);
self.initialize = function() {
if (self.config) {
for (var i = 0; i < self.config.conditions.length; ++i) {
var condition = {};
if (self.isUnary(self.config.conditions[i].operator)) {
condition = {
"headerName": self.config.conditions[i].headerName,
"operator": ko.observableArray([self.config.conditions[i].operator]),
"showValueCol": ko.observable(false)
};
} else {
condition = {
"headerName": self.config.conditions[i].headerName,
"operator": ko.observableArray([self.config.conditions[i].operator]),
"headerValue": self.config.conditions[i].headerValue,
"showValueCol": ko.observable(true)
};
}
self.conditions.push(condition);
}
}
if (self.config && self.config.action) {
self.selectedAction([self.config.action]);
}
if (self.config && self.config.conjunction) {
self.selectedConjunction([self.config.conjunction]);
}
// we need at least one header to start with
if (self.conditions().length === 0) {
self.addCondition();
} else {
self.disableApplyButton(false);
}
self.populatePassRejectContainer();
};
self.addCondition = function() {
self.conditions.push({"headerName" : "", "operator" : ko.observableArray(["="]), "headerValue" : "", "showValueCol": ko.observable(true)});
self.disableApplyButton(false);
};
self.removeCondition = function(condition) {
self.conditions.remove(condition);
self.disableApplyButton(self.conditions().length === 0);
};
self.handleOperatorChange = function(index, event, data) {
if (data.option !== 'value'){
return;
}
if (self.isUnary(self.conditions()[index].operator()[0])) {
self.conditions()[index].showValueCol(false);
} else {
self.conditions()[index].showValueCol(true);
}
};
self.isUnary = function(operator) {
return operator === "null" || operator === "not null";
};
self.populatePassRejectContainer = function() {
var actionSelect = $('<select id=\'action\' data-bind="ojComponent: { component: \'ojSelect\', options: actions, value: selectedAction, rootAttributes: { style:\'max-width:50px;\'}}"></select>');
var optionSelect = $('<select id=\'option\' data-bind="ojComponent: { component: \'ojSelect\', options: conjunctions, value: selectedConjunction, rootAttributes: { style:\'max-width:50px;\'}}"></select>');
var passRejectHtml = apiplatform.utils.substituteParams(self.l10n()['label.passorreject'], [actionSelect[0].outerHTML, optionSelect[0].outerHTML]);
$("#pass-reject-container").append(passRejectHtml);
};
// Begin - Framework methods
self.getPolicyConfiguration = function(){
//self.config.operator = self.operator()[0];
var config = {"action": "", "conjunction" : "", "conditions" : []};
for (var i = 0; i < self.conditions().length; i++) {
var condition = {};
if (self.isUnary(self.conditions()[i].operator()[0])) {
condition = {
"headerName": self.conditions()[i].headerName,
"operator" : self.conditions()[i].operator()[0]
};
} else {
condition = {
"headerName" : self.conditions()[i].headerName,
"operator" : self.conditions()[i].operator()[0],
"headerValue": self.conditions()[i].headerValue
};
}
config.conditions.push(condition);
};
config.action = self.selectedAction()[0];
config.conjunction = self.selectedConjunction()[0];
return config;
};
self.disableApplyButton = function(flag) {
self.disableApplyPolicyButton(flag);
};
// End - Framework methods
self.initialize();
}
Sample Contents of {policy name}-edit.html
Top level <div> must have the id policy-ui-content
because the Policy UI code uses this div to bind the contents dynamically.
<div id="policy-ui-content" class="oj-form" >
<div id="pass-reject-container">
</div>
<div class="conditions-container-div">
<table
data-bind="visible: conditions().length > 0 ">
<col style="width:35%">
<col style="width:15%">
<col style="width:35%">
<col style="width:15%">
<tr>
<th style="text-align: left;"><span data-bind="text: l10n()['label.headername']"
class="condition-field-label condition-first-or-last-column"></span></th>
<th style="text-align: left;"><span data-bind="text: l10n()['label.headeroperator']"
class="condition-field-label condition-middle-column"></span></th>
<th style="text-align: left;"><span data-bind="text: l10n()['label.headervalue']"
class="condition-field-label condition-first-or-last-column"></span></th>
<th></th>
</tr>
<tbody data-bind="foreach: {data: conditions, as: 'condition'}">
<tr class="condition-container-row">
<td class="condition-first-or-last-column">
<input type="text" required
data-bind="attr: {id: 'headerName_' + $index()}, ojComponent: {
component: 'ojInputText', value: condition.headerName,
placeholder: $parent.l10n()['placeholder.headername']}"/>
</td>
<td class="condition-middle-column">
<select data-bind="attr: {id: 'operator_' + $index()}, ojComponent: {
component: 'ojSelect',
options: $parent.opList,
optionChange: function(data, event) {
$parent.handleOperatorChange($index(), data, event);
},
value: condition.operator}">
</select>
</td>
<td class="condition-middle-column">
<div data-bind="visible: condition.showValueCol">
<input type="text" required
data-bind="attr: {id: 'headerValue_' + $index()}, ojComponent: {
component: 'ojInputText', value: condition.headerValue,
placeholder: $parent.l10n()['placeholder.headervalue']}"/>
</div>
<div data-bind="visible: !condition.showValueCol()">
<span data-bind="text: $parent.l10n()['label.notApplicable']"></span>
</div>
</td>
<td class="condition-last-column">
<div>
<div style="float:left; vertical-align: top; display: none;"
class="policy-remove-item-icon-black-withpadding"
data-bind="attr: {id: 'btn_remove_' + $index()}, click: $parent.removeCondition,
visible: $parent.conditions().length > 1,
attr: {title: $parent.l10n()['tooltip.delcondition']}">
</div>
<div style="float:left; vertical-align: top; display: none;"
class="policy-add-item-icon-black"
data-bind="attr: {id: 'btn_add_' + $index()}, click: $parent.addCondition,
visible: $index() === $parent.conditions().length - 1,
attr: {title: $parent.l10n()['tooltip.addcondition']}">
</div>
</div>
</td>
</tr>
<tr>
<td colspan="3" class="policy-condition-separator"></td>
</tr>
</tbody>
</table>
</div>
</div>
Sample contents for {policy name}-view.js
function PolicyConfigurationModel(ko, $, oj, config, additionalParams){
self = this;
self.config = config;
self.l10n = ko.observable(additionalParams.l10nbundle);
self.conditions = ko.observableArray();
self.selectedAction = ko.observable();
self.selectedConjunction = ko.observable();
self.operatorsMap = [];
self.initialize = function() {
self.operatorsMap["="] = "=";
self.operatorsMap["!="] = "!=";
self.operatorsMap[">"] = ">";
self.operatorsMap["<"] = "<";
self.operatorsMap[">="] = ">=";
self.operatorsMap["<="] = "<=";
self.operatorsMap["null"] = self.l10n()['label.isnull'];
self.operatorsMap["not null"] = self.l10n()['label.isnotnull'];
if (self.config) {
for (var i = 0; i < self.config.conditions.length; ++i) {
var condition = {};
if (self.isUnary(self.config.conditions[i].operator)) {
condition = {
"headerName": self.config.conditions[i].headerName,
"operator": self.operatorsMap[self.config.conditions[i].operator],
"showValueCol": ko.observable(false)
};
} else {
condition = {
"headerName": self.config.conditions[i].headerName,
"operator": self.operatorsMap[self.config.conditions[i].operator],
"headerValue": self.config.conditions[i].headerValue,
"showValueCol": ko.observable(true)
};
}
self.conditions.push(condition);
}
}
if (self.config && self.config.action) {
self.selectedAction(self.config.action);
}
if (self.config && self.config.conjunction) {
self.selectedConjunction(self.config.conjunction);
}
self.populatePassRejectContainer();
};
self.isUnary = function(operator) {
return operator === "null" || operator === "not null";
};
self.populatePassRejectContainer = function() {
var actionHtml = $("<span id='action' data-bind='text: selectedAction'
style='padding:0px 35px 0px 0px; color: #959595'></span>");
var conjunctionHtml = $("<span id='action' data-bind='text: selectedConjunction'
style='padding:0px 35px 0px 35px; color: #959595'></span>");
var passRejectHtml = apiplatform.utils.substituteParams(self.l10n()['label.passorreject'],
[actionHtml[0].outerHTML, conjunctionHtml[0].outerHTML]);
$("#pass-reject-container").append(passRejectHtml);
};
self.initialize();
}
Sample contents for {policy name}-view.html
<div id="policy-ui-content" class="oj-form" >
<div id="pass-reject-container" class="condition-field-label-2">
</div>
<div class="conditions-container-div">
<table style="width:100%; padding:0px;"
data-bind="visible: conditions().length > 0 ">
<col style="width:40%">
<col style="width:20%">
<col style="width:40%">
<tbody data-bind="foreach: {data: conditions, as: 'condition'}">
<tr data-bind="visible: $index() === 0">
<td class="condition-first-or-last-column">
<label data-bind="text: $parent.l10n()['label.headername']" class="condition-field-label"></label>
</td>
<td class=" condition-middle-column">
<label data-bind="text: $parent.l10n()['label.headeroperator']" class="condition-field-label"></label>
</td>
<td class="condition-first-or-last-column">
<label data-bind="text: $parent.l10n()['label.headervalue']" class="condition-field-label"></label>
</td>
</tr>
<tr class="condition-container-row">
<td class="condition-first-or-last-column">
<label data-bind="text:condition.headerName" class="policy-readonly-text"></label>
</td>
<td class="condition-middle-column">
<label data-bind="text:condition.operator" class="policy-readonly-text"></label>
</td>
<td class="condition-first-or-last-column">
<div data-bind="visible: condition.showValueCol">
<label data-bind="text: condition.headerValue" class="policy-readonly-text"></label>
</div>
<div data-bind="visible: !condition.showValueCol()">
<span data-bind="text: $parent.l10n()['label.notApplicable']" class="policy-readonly-text"></span>
</div>
</td>
</tr>
<tr>
<td colspan="3" class="policy-condition-separator"></td>
</tr>
</tbody>
</table>
</div>
</div>