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-contentbecause 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>