-
Step 1: Select a platform
+
Step 1: Select a platform
-
+
+
+
- {{else}}
-
- Permission Denied
-
-
- You not authorized to enter Policy Management Section.
-
{{/if}}
+{{/zone}}
+{{#zone "bottomJs"}}
+ {{js "/js/policy-create.js"}}
{{/zone}}
\ No newline at end of file
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.policy.create/public/js/policy-create.js b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.policy.create/public/js/policy-create.js
new file mode 100644
index 00000000000..f2fc06b55dc
--- /dev/null
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.policy.create/public/js/policy-create.js
@@ -0,0 +1,532 @@
+/*
+ *
+ * WSO2 Inc. licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+var stepForwardFrom = {};
+var stepBackFrom = {};
+var policy = {};
+var configuredOperations = [];
+var validateInline = {};
+var clearInline = {};
+var validateStep = {};
+
+var enableInlineError = function (inputField, errorMsg, errorSign) {
+ var fieldIdentifier = "#" + inputField;
+ var errorMsgIdentifier = "#" + inputField + " ." + errorMsg;
+ var errorSignIdentifier = "#" + inputField + " ." + errorSign;
+
+ if (inputField) {
+ $(fieldIdentifier).addClass(" has-error has-feedback");
+ }
+
+ if (errorMsg) {
+ $(errorMsgIdentifier).removeClass(" hidden");
+ }
+
+ if (errorSign) {
+ $(errorSignIdentifier).removeClass(" hidden");
+ }
+};
+
+var disableInlineError = function (inputField, errorMsg, errorSign) {
+ var fieldIdentifier = "#" + inputField;
+ var errorMsgIdentifier = "#" + inputField + " ." + errorMsg;
+ var errorSignIdentifier = "#" + inputField + " ." + errorSign;
+
+ if (inputField) {
+ $(fieldIdentifier).removeClass(" has-error has-feedback");
+ }
+
+ if (errorMsg) {
+ $(errorMsgIdentifier).addClass(" hidden");
+ }
+
+ if (errorSign) {
+ $(errorSignIdentifier).addClass(" hidden");
+ }
+};
+
+/**
+ *clear inline validation messages.
+ */
+clearInline["policy-name"] = function () {
+ disableInlineError("policyNameField", "nameEmpty", "nameError");
+};
+
+
+/**
+ * Validate if provided policy name is valid against RegEx configures.
+ */
+validateInline["policy-name"] = function () {
+ var policyName = $("input#policy-name-input").val();
+ if (policyName && inputIsValidAgainstLength(policyName, 1, 30)) {
+ disableInlineError("policyNameField", "nameEmpty", "nameError");
+ } else {
+ enableInlineError("policyNameField", "nameEmpty", "nameError");
+ }
+};
+
+$("#policy-name-input").focus(function(){
+ clearInline["policy-name"]();
+}).blur(function(){
+ validateInline["policy-name"]();
+});
+
+stepForwardFrom["policy-platform"] = function (actionButton) {
+ $("#device-type-policy-operations").html("").addClass("hidden");
+ $("#generic-policy-operations").addClass("hidden");
+ policy["platform"] = $(actionButton).data("platform");
+ policy["platformId"] = $(actionButton).data("platform-type");
+ // updating next-page wizard title with selected platform
+ $("#policy-profile-page-wizard-title").text("ADD " + policy["platform"] + " POLICY");
+
+ var deviceType = policy["platform"];
+ var policyOperationsTemplateSrc = context + '/public/cdmf.unit.device.type.' + deviceType +
+ '.policy-wizard/templates/' + deviceType + '-policy-operations.hbs';
+ var policyOperationsScriptSrc = context + '/public/cdmf.unit.device.type.' + deviceType +
+ '.policy-wizard/js/' + deviceType + '-policy-operations.js';
+ var policyOperationsStylesSrc = context + '/public/cdmf.unit.device.type.' + deviceType +
+ '.policy-wizard/css/' + deviceType + '-policy-operations.css';
+ var policyOperationsTemplateCacheKey = deviceType + '-policy-operations';
+
+ $.isResourceExists(policyOperationsTemplateSrc, function (status) {
+ if (status) {
+ $.template(policyOperationsTemplateCacheKey, policyOperationsTemplateSrc, function (template) {
+ var content = template();
+ $("#device-type-policy-operations").html(content).removeClass("hidden");
+ // $("#device-type-policy-operations").removeClass("hidden");
+ $(".policy-platform").addClass("hidden");
+ });
+
+ $.isResourceExists(policyOperationsScriptSrc, function (status) {
+ if (status) {
+ var script = document.createElement('script');
+ script.type = 'text/javascript';
+ script.src = policyOperationsScriptSrc;
+ $(".wr-advance-operations").prepend(script);
+ }
+ });
+
+ $.isResourceExists(policyOperationsStylesSrc, function (status) {
+ if (status) {
+ var style = document.createElement('link');
+ style.type = 'text/css';
+ style.rel = 'stylesheet';
+ style.href = policyOperationsStylesSrc;
+ $(".wr-advance-operations").prepend(style);
+ }
+ });
+ } else {
+ $("#generic-policy-operations").removeClass("hidden");
+ }
+ $(".wr-advance-operations-init").addClass("hidden");
+ });
+};
+
+stepForwardFrom["policy-profile"] = function () {
+ policy["profile"] = operationModule.generateProfile(policy["platform"], configuredOperations);
+ // updating next-page wizard title with selected platform
+ $("#policy-criteria-page-wizard-title").text("ADD " + policy["platform"] + " POLICY");
+};
+
+stepBackFrom["policy-profile"] = function () {
+ // reinitialize configuredOperations
+ configuredOperations = [];
+};
+
+stepForwardFrom["policy-criteria"] = function () {
+ $("input[type='radio'].select-users-radio").each(function () {
+ if ($(this).is(':radio')) {
+ if ($(this).is(":checked")) {
+ if ($(this).attr("id") == "users-radio-btn") {
+ policy["selectedUsers"] = $("#users-input").val();
+ policy["selectedUserRoles"] = null;
+ } else if ($(this).attr("id") == "user-roles-radio-btn") {
+ policy["selectedUsers"] = null;
+ policy["selectedUserRoles"] = $("#user-roles-input").val();
+ }
+ }
+ }
+ });
+ policy["selectedNonCompliantAction"] = $("#action-input").find(":selected").data("action");
+ policy["selectedOwnership"] = $("#ownership-input").val();
+ //updating next-page wizard title with selected platform
+ $("#policy-naming-page-wizard-title").text("ADD " + policy["platform"] + " POLICY");
+};
+
+/**
+ * Checks if provided input is valid against provided length range.
+ *
+ * @param input Alphanumeric or non-alphanumeric input
+ * @param minLength Minimum Required Length
+ * @param maxLength Maximum Required Length
+ * @returns {boolean} Returns true if input matches the provided minimum length and maximum length
+ */
+var inputIsValidAgainstLength = function (input, minLength, maxLength) {
+ var length = input.length;
+ return (length == minLength || (length > minLength && length < maxLength) || length == maxLength);
+};
+
+validateStep["policy-criteria"] = function () {
+ var validationStatus = {};
+ var selectedAssignees;
+ var selectedField = "Role(s)";
+
+ $("input[type='radio'].select-users-radio").each(function () {
+ if ($(this).is(":checked")) {
+ if ($(this).attr("id") == "users-radio-btn") {
+ selectedAssignees = $("#users-input").val();
+ selectedField = "User(s)";
+ } else if ($(this).attr("id") == "user-roles-radio-btn") {
+ selectedAssignees = $("#user-roles-input").val();
+ }
+ return false;
+ }
+ });
+
+ if (selectedAssignees) {
+ validationStatus["error"] = false;
+ } else {
+ validationStatus["error"] = true;
+ validationStatus["mainErrorMsg"] = selectedField + " is a required field. It cannot be empty";
+ }
+
+ var wizardIsToBeContinued;
+ if (validationStatus["error"]) {
+ wizardIsToBeContinued = false;
+ var mainErrorMsgWrapper = "#policy-criteria-main-error-msg";
+ var mainErrorMsg = mainErrorMsgWrapper + " span";
+ $(mainErrorMsg).text(validationStatus["mainErrorMsg"]);
+ $(mainErrorMsgWrapper).removeClass("hidden");
+ } else {
+ wizardIsToBeContinued = true;
+ }
+
+ return wizardIsToBeContinued;
+};
+
+validateStep["policy-naming"] = function () {
+ var validationStatus = {};
+
+ // taking values of inputs to be validated
+ var policyName = $("input#policy-name-input").val();
+ // starting validation process and updating validationStatus
+ if (!policyName) {
+ validationStatus["error"] = true;
+ validationStatus["mainErrorMsg"] = "Policy name is empty. You cannot proceed.";
+ } else if (!inputIsValidAgainstLength(policyName, 1, 30)) {
+ validationStatus["error"] = true;
+ validationStatus["mainErrorMsg"] =
+ "Policy name exceeds maximum allowed length.";
+ } else {
+ validationStatus["error"] = false;
+ }
+ // ending validation process
+
+ // start taking specific actions upon validation
+ var wizardIsToBeContinued;
+ if (validationStatus["error"]) {
+ wizardIsToBeContinued = false;
+ var mainErrorMsgWrapper = "#policy-naming-main-error-msg";
+ var mainErrorMsg = mainErrorMsgWrapper + " span";
+ $(mainErrorMsg).text(validationStatus["mainErrorMsg"]);
+ $(mainErrorMsgWrapper).removeClass("hidden");
+ } else {
+ wizardIsToBeContinued = true;
+ }
+
+ return wizardIsToBeContinued;
+};
+
+validateStep["policy-platform"] = function () {
+ return false;
+};
+
+validateStep["policy-naming-publish"] = function () {
+ var validationStatus = {};
+
+ // taking values of inputs to be validated
+ var policyName = $("input#policy-name-input").val();
+ // starting validation process and updating validationStatus
+ if (!policyName) {
+ validationStatus["error"] = true;
+ validationStatus["mainErrorMsg"] = "Policy name is empty. You cannot proceed.";
+ } else if (!inputIsValidAgainstLength(policyName, 1, 30)) {
+ validationStatus["error"] = true;
+ validationStatus["mainErrorMsg"] =
+ "Policy name exceeds maximum allowed length.";
+ } else {
+ validationStatus["error"] = false;
+ }
+ // ending validation process
+
+ // start taking specific actions upon validation
+ var wizardIsToBeContinued;
+ if (validationStatus["error"]) {
+ wizardIsToBeContinued = false;
+ var mainErrorMsgWrapper = "#policy-naming-main-error-msg";
+ var mainErrorMsg = mainErrorMsgWrapper + " span";
+ $(mainErrorMsg).text(validationStatus["mainErrorMsg"]);
+ $(mainErrorMsgWrapper).removeClass("hidden");
+ } else {
+ wizardIsToBeContinued = true;
+ }
+
+ return wizardIsToBeContinued;
+};
+
+stepForwardFrom["policy-naming-publish"] = function () {
+ policy["policyName"] = $("#policy-name-input").val();
+ policy["description"] = $("#policy-description-input").val();
+ //All data is collected. Policy can now be updated.
+ savePolicy(policy, true, "/api/device-mgt/v1.0/policies/");
+};
+
+stepForwardFrom["policy-naming"] = function () {
+ policy["policyName"] = $("#policy-name-input").val();
+ policy["description"] = $("#policy-description-input").val();
+ //All data is collected. Policy can now be updated.
+ savePolicy(policy, false, "/api/device-mgt/v1.0/policies/");
+};
+
+var savePolicy = function (policy, isActive, serviceURL) {
+ var profilePayloads = [];
+ // traverses key by key in policy["profile"]
+ var key;
+ for (key in policy["profile"]) {
+ if (policy["profile"].hasOwnProperty(key)) {
+ profilePayloads.push({
+ "featureCode": key,
+ "deviceType": policy["platform"],
+ "content": policy["profile"][key]
+ });
+ }
+ }
+
+ $.each(profilePayloads, function (i, item) {
+ $.each(item.content, function (key, value) {
+ //cannot add a true check since it will catch value = false as well
+ if (value === null || value === undefined || value === "") {
+ item.content[key] = null;
+ }
+ });
+ });
+
+ var payload = {
+ "policyName": policy["policyName"],
+ "description": policy["description"],
+ "compliance": policy["selectedNonCompliantAction"],
+ "ownershipType": policy["selectedOwnership"],
+ "active": isActive,
+ "profile": {
+ "profileName": policy["policyName"],
+ "deviceType": policy["platform"],
+ "profileFeaturesList": profilePayloads
+ }
+ };
+
+ if (policy["selectedUsers"]) {
+ payload["users"] = policy["selectedUsers"];
+ } else if (policy["selectedUserRoles"]) {
+ payload["roles"] = policy["selectedUserRoles"];
+ } else {
+ payload["users"] = [];
+ payload["roles"] = [];
+ }
+
+ invokerUtil.post(
+ serviceURL,
+ payload,
+ function () {
+ $(".add-policy").addClass("hidden");
+ $(".policy-naming").addClass("hidden");
+ $(".policy-message").removeClass("hidden");
+ },
+ function (data) {
+ }
+ );
+};
+
+function formatRepo(user) {
+ if (user.loading) {
+ return user.text;
+ }
+ if (!user.username) {
+ return;
+ }
+ var markup = '
' +
+ '
' +
+ '
' +
+ '
' + user.username + '
';
+ if (user.firstname) {
+ markup += '
' + user.firstname + '
';
+ }
+ if (user.emailAddress) {
+ markup += '
' + user.emailAddress + '
';
+ }
+ markup += '
';
+ return markup;
+}
+
+function formatRepoSelection(user) {
+ return user.username || user.text;
+}
+
+// End of functions related to grid-input-view
+
+
+$(document).ready(function () {
+ $("#users-input").select2({
+ multiple: true,
+ tags: false,
+ ajax: {
+ url: context + "/api/invoker/execute/",
+ method: "POST",
+ dataType: 'json',
+ delay: 250,
+ id: function (user) {
+ return user.username;
+ },
+ data: function (params) {
+ var postData = {};
+ postData.requestMethod = "GET";
+ postData.requestURL = "/api/device-mgt/v1.0/users/search/usernames?filter=" + params.term;
+ postData.requestPayload = null;
+ return JSON.stringify(postData);
+ },
+ processResults: function (data) {
+ var newData = [];
+ $.each(data, function (index, value) {
+ value.id = value.username;
+ newData.push(value);
+ });
+ return {
+ results: newData
+ };
+ },
+ cache: true
+ },
+ escapeMarkup: function (markup) {
+ return markup;
+ }, // let our custom formatter work
+ minimumInputLength: 1,
+ templateResult: formatRepo, // omitted for brevity, see the source of this page
+ templateSelection: formatRepoSelection // omitted for brevity, see the source of this page
+ });
+
+ $("#loading-content").remove();
+ $(".policy-platform").removeClass("hidden");
+ // Adding initial state of wizard-steps.
+ $("#policy-platform-wizard-steps").html($(".wr-steps").html());
+
+ $("select.select2[multiple=multiple]").select2({
+ "tags": false
+ });
+
+ $("#users-select-field").hide();
+ $("#user-roles-select-field").show();
+
+ $("input[type='radio'].select-users-radio").change(function () {
+ if ($("#users-radio-btn").is(":checked")) {
+ $("#user-roles-select-field").hide();
+ $("#users-select-field").show();
+ }
+ if ($("#user-roles-radio-btn").is(":checked")) {
+ $("#users-select-field").hide();
+ $("#user-roles-select-field").show();
+ }
+ });
+
+ // Support for special input type "ANY" on user(s) & user-role(s) selection
+ $("#user-roles-input").select2({
+ "tags": false
+ }).on("select2:select", function (e) {
+ if (e.params.data.id == "ANY") {
+ $(this).val("ANY").trigger("change");
+ } else {
+ $("option[value=ANY]", this).prop("selected", false).parent().trigger("change");
+ }
+ });
+
+ //Policy wizard stepper
+ $(".wizard-stepper").click(function () {
+ // button clicked here can be either a continue button or a back button.
+ var currentStep = $(this).data("current");
+ var validationIsRequired = $(this).data("validate");
+ var wizardIsToBeContinued;
+
+ if (validationIsRequired) {
+ if (currentStep == "policy-profile") {
+ wizardIsToBeContinued = validatePolicyProfile();
+ } else {
+ wizardIsToBeContinued = validateStep[currentStep]();
+ }
+ } else {
+ wizardIsToBeContinued = true;
+ }
+
+ if (wizardIsToBeContinued) {
+ // When moving back and forth, following code segment will
+ // remove if there are any visible error-messages.
+ var errorMsgWrappers = ".alert.alert-danger";
+ $(errorMsgWrappers).each(
+ function () {
+ if (!$(this).hasClass("hidden")) {
+ $(this).addClass("hidden");
+ }
+ }
+ );
+
+ var nextStep = $(this).data("next");
+ var isBackBtn = $(this).data("is-back-btn");
+
+ // if current button is a continuation...
+ if (!isBackBtn) {
+ // initiate stepForwardFrom[*] functions to gather form data.
+ if (stepForwardFrom[currentStep]) {
+ stepForwardFrom[currentStep](this);
+ }
+ } else {
+ // initiate stepBackFrom[*] functions to rollback.
+ if (stepBackFrom[currentStep]) {
+ stepBackFrom[currentStep]();
+ }
+ }
+
+ // following step occurs only at the last stage of the wizard.
+ if (!nextStep) {
+ window.location.href = $(this).data("direct");
+ }
+
+ // updating next wizard step as current.
+ $(".itm-wiz").each(function () {
+ var step = $(this).data("step");
+ if (step == nextStep) {
+ $(this).addClass("itm-wiz-current");
+ } else {
+ $(this).removeClass("itm-wiz-current");
+ }
+ });
+
+ // adding next update of wizard-steps.
+ $("#" + nextStep + "-wizard-steps").html($(".wr-steps").html());
+
+ // hiding current section of the wizard and showing next section.
+ $("." + currentStep).addClass("hidden");
+ $("." + nextStep).removeClass("hidden");
+ }
+ });
+});
\ No newline at end of file
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.policy.wizard/public/js/policy-create.js b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.policy.wizard/public/js/policy-create.js
index ee2c8051968..fd2bf3ff4f5 100644
--- a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.policy.wizard/public/js/policy-create.js
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.policy.wizard/public/js/policy-create.js
@@ -1,17 +1,16 @@
/*
- * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 Inc. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
@@ -21,60 +20,120 @@ var stepForwardFrom = {};
var stepBackFrom = {};
var policy = {};
var configuredOperations = [];
-var deviceTypeLabel;
+var validateInline = {};
+var clearInline = {};
-/**
- * Method to update the visibility of grouped input.
- * @param domElement HTML grouped-input element with class name "grouped-input"
- */
-var updateGroupedInputVisibility = function (domElement) {
- if ($(".parent-input:first", domElement).is(":checked")) {
- if ($(".grouped-child-input:first", domElement).hasClass("disabled")) {
- $(".grouped-child-input:first", domElement).removeClass("disabled");
- }
- $(".child-input", domElement).each(function () {
- $(this).prop('disabled', false);
- });
- } else {
- if (!$(".grouped-child-input:first", domElement).hasClass("disabled")) {
- $(".grouped-child-input:first", domElement).addClass("disabled");
- }
- $(".child-input", domElement).each(function () {
- $(this).prop('disabled', true);
- });
+var enableInlineError = function (inputField, errorMsg, errorSign) {
+ var fieldIdentifier = "#" + inputField;
+ var errorMsgIdentifier = "#" + inputField + " ." + errorMsg;
+ var errorSignIdentifier = "#" + inputField + " ." + errorSign;
+
+ if (inputField) {
+ $(fieldIdentifier).addClass(" has-error has-feedback");
+ }
+
+ if (errorMsg) {
+ $(errorMsgIdentifier).removeClass(" hidden");
+ }
+
+ if (errorSign) {
+ $(errorSignIdentifier).removeClass(" hidden");
+ }
+};
+
+var disableInlineError = function (inputField, errorMsg, errorSign) {
+ var fieldIdentifier = "#" + inputField;
+ var errorMsgIdentifier = "#" + inputField + " ." + errorMsg;
+ var errorSignIdentifier = "#" + inputField + " ." + errorSign;
+
+ if (inputField) {
+ $(fieldIdentifier).removeClass(" has-error has-feedback");
+ }
+
+ if (errorMsg) {
+ $(errorMsgIdentifier).addClass(" hidden");
+ }
+
+ if (errorSign) {
+ $(errorSignIdentifier).addClass(" hidden");
}
};
/**
- * Checks if provided number is valid against a range.
- *
- * @param numberInput Number Input
- * @param min Minimum Limit
- * @param max Maximum Limit
- * @returns {boolean} Returns true if input is within the specified range
+ *clear inline validation messages.
*/
-var inputIsValidAgainstRange = function (numberInput, min, max) {
- return (numberInput == min || (numberInput > min && numberInput < max) || numberInput == max);
+clearInline["policy-name"] = function () {
+ disableInlineError("policyNameField", "nameEmpty", "nameError");
};
+
/**
- * Checks if provided input is valid against RegEx input.
- *
- * @param regExp Regular expression
- * @param input Input string to check
- * @returns {boolean} Returns true if input matches RegEx
+ * Validate if provided policy name is valid against RegEx configures.
*/
-var inputIsValidAgainstRegExp = function (regExp, input) {
- return regExp.test(input);
+validateInline["policy-name"] = function () {
+ var policyName = $("input#policy-name-input").val();
+ if (policyName && inputIsValidAgainstLength(policyName, 1, 30)) {
+ disableInlineError("policyNameField", "nameEmpty", "nameError");
+ } else {
+ enableInlineError("policyNameField", "nameEmpty", "nameError");
+ }
};
-validateStep["policy-profile"] = function () {
- return true;
+$("#policy-name-input").focus(function(){
+ clearInline["policy-name"]();
+}).blur(function(){
+ validateInline["policy-name"]();
+});
+
+stepForwardFrom["policy-platform"] = function (actionButton) {
+ policy["platform"] = $(actionButton).data("platform");
+ policy["platformId"] = $(actionButton).data("platform-type");
+ // updating next-page wizard title with selected platform
+ $("#policy-profile-page-wizard-title").text("ADD " + policy["platform"] + " POLICY");
+
+ var deviceType = policy["platform"];
+ var policyOperationsTemplateSrc = context + '/public/cdmf.unit.device.type.' + deviceType +
+ '.policy-wizard/templates/' + deviceType + '-policy-operations.hbs';
+ var policyOperationsScriptSrc = context + '/public/cdmf.unit.device.type.' + deviceType +
+ '.policy-wizard/js/' + deviceType + '-policy-operations.js';
+ var policyOperationsStylesSrc = context + '/public/cdmf.unit.device.type.' + deviceType +
+ '.policy-wizard/css/' + deviceType + '-policy-operations.css';
+ var policyOperationsTemplateCacheKey = deviceType + '-policy-operations';
+
+ $.isResourceExists(policyOperationsTemplateSrc, function (status) {
+ if (status) {
+ $.template(policyOperationsTemplateCacheKey, policyOperationsTemplateSrc, function (template) {
+ var content = template();
+ $(".wr-advance-operations").html(content);
+ $(".policy-platform").addClass("hidden");
+ });
+ }
+ });
+
+ $.isResourceExists(policyOperationsScriptSrc, function (status) {
+ if (status) {
+ var script = document.createElement('script');
+ script.type = 'text/javascript';
+ script.src = policyOperationsScriptSrc;
+ $(".wr-advance-operations").prepend(script);
+ }
+ });
+
+ $.isResourceExists(policyOperationsStylesSrc, function (status) {
+ if (status) {
+ var style = document.createElement('link');
+ style.type = 'text/css';
+ style.rel = 'stylesheet';
+ style.href = policyOperationsStylesSrc;
+ $(".wr-advance-operations").prepend(style);
+ }
+ });
};
stepForwardFrom["policy-profile"] = function () {
+ policy["profile"] = operationModule.generateProfile(policy["platform"], configuredOperations);
// updating next-page wizard title with selected platform
- $("#policy-criteria-page-wizard-title").text("ADD " + deviceTypeLabel + " POLICY");
+ $("#policy-criteria-page-wizard-title").text("ADD " + policy["platform"] + " POLICY");
};
stepBackFrom["policy-profile"] = function () {
@@ -84,15 +143,13 @@ stepBackFrom["policy-profile"] = function () {
// so that, the wrong content would not be shown at the first glance, in case
// the user selects a different platform
$(".wr-advance-operations").html(
- "
" +
- "
" +
- " " +
- "" +
- " " +
- "Loading Platform Features . . ." +
- "
" +
- "
" +
- "
"
+ "
" +
+ "
" +
+ "" +
+ "Loading Platform Features . . ." +
+ "
" +
+ "
" +
+ "
"
);
};
@@ -102,18 +159,18 @@ stepForwardFrom["policy-criteria"] = function () {
if ($(this).is(":checked")) {
if ($(this).attr("id") == "users-radio-btn") {
policy["selectedUsers"] = $("#users-input").val();
+ policy["selectedUserRoles"] = null;
} else if ($(this).attr("id") == "user-roles-radio-btn") {
+ policy["selectedUsers"] = null;
policy["selectedUserRoles"] = $("#user-roles-input").val();
- } else if ($(this).attr("id") == "groups-radio-btn") {
- policy["selectedUserGroups"] = $("#groups-input").val();
}
}
}
});
policy["selectedNonCompliantAction"] = $("#action-input").find(":selected").data("action");
policy["selectedOwnership"] = $("#ownership-input").val();
- // updating next-page wizard title with selected platform
- $("#policy-naming-page-wizard-title").text("ADD " + deviceTypeLabel + " POLICY");
+ //updating next-page wizard title with selected platform
+ $("#policy-naming-page-wizard-title").text("ADD " + policy["platform"] + " POLICY");
};
/**
@@ -129,6 +186,44 @@ var inputIsValidAgainstLength = function (input, minLength, maxLength) {
return (length == minLength || (length > minLength && length < maxLength) || length == maxLength);
};
+validateStep["policy-criteria"] = function () {
+ var validationStatus = {};
+ var selectedAssignees;
+ var selectedField = "Role(s)";
+
+ $("input[type='radio'].select-users-radio").each(function () {
+ if ($(this).is(":checked")) {
+ if ($(this).attr("id") == "users-radio-btn") {
+ selectedAssignees = $("#users-input").val();
+ selectedField = "User(s)";
+ } else if ($(this).attr("id") == "user-roles-radio-btn") {
+ selectedAssignees = $("#user-roles-input").val();
+ }
+ return false;
+ }
+ });
+
+ if (selectedAssignees) {
+ validationStatus["error"] = false;
+ } else {
+ validationStatus["error"] = true;
+ validationStatus["mainErrorMsg"] = selectedField + " is a required field. It cannot be empty";
+ }
+
+ var wizardIsToBeContinued;
+ if (validationStatus["error"]) {
+ wizardIsToBeContinued = false;
+ var mainErrorMsgWrapper = "#policy-criteria-main-error-msg";
+ var mainErrorMsg = mainErrorMsgWrapper + " span";
+ $(mainErrorMsg).text(validationStatus["mainErrorMsg"]);
+ $(mainErrorMsgWrapper).removeClass("hidden");
+ } else {
+ wizardIsToBeContinued = true;
+ }
+
+ return wizardIsToBeContinued;
+};
+
validateStep["policy-naming"] = function () {
var validationStatus = {};
@@ -141,7 +236,7 @@ validateStep["policy-naming"] = function () {
} else if (!inputIsValidAgainstLength(policyName, 1, 30)) {
validationStatus["error"] = true;
validationStatus["mainErrorMsg"] =
- "Policy name exceeds maximum allowed length. Please check.";
+ "Policy name exceeds maximum allowed length.";
} else {
validationStatus["error"] = false;
}
@@ -162,6 +257,10 @@ validateStep["policy-naming"] = function () {
return wizardIsToBeContinued;
};
+validateStep["policy-platform"] = function () {
+ return false;
+};
+
validateStep["policy-naming-publish"] = function () {
var validationStatus = {};
@@ -174,7 +273,7 @@ validateStep["policy-naming-publish"] = function () {
} else if (!inputIsValidAgainstLength(policyName, 1, 30)) {
validationStatus["error"] = true;
validationStatus["mainErrorMsg"] =
- "Policy name exceeds maximum allowed length. Please check.";
+ "Policy name exceeds maximum allowed length.";
} else {
validationStatus["error"] = false;
}
@@ -199,34 +298,48 @@ stepForwardFrom["policy-naming-publish"] = function () {
policy["policyName"] = $("#policy-name-input").val();
policy["description"] = $("#policy-description-input").val();
//All data is collected. Policy can now be updated.
- savePolicy(policy, "publish");
+ savePolicy(policy, true, "/api/device-mgt/v1.0/policies/");
};
+
stepForwardFrom["policy-naming"] = function () {
policy["policyName"] = $("#policy-name-input").val();
policy["description"] = $("#policy-description-input").val();
//All data is collected. Policy can now be updated.
- savePolicy(policy, "save");
+ savePolicy(policy, false, "/api/device-mgt/v1.0/policies/");
};
-var savePolicy = function (policy, state) {
- var profilePayloads = [{
- "featureCode": "CONFIG",
- "deviceTypeId": policy["platformId"],
- "content": {"policyDefinition": window.queryEditor.getValue()}
- }];
+var savePolicy = function (policy, isActive, serviceURL) {
+ var profilePayloads = [];
+ // traverses key by key in policy["profile"]
+ var key;
+ for (key in policy["profile"]) {
+ if (policy["profile"].hasOwnProperty(key)) {
+ profilePayloads.push({
+ "featureCode": key,
+ "deviceType": policy["platform"],
+ "content": policy["profile"][key]
+ });
+ }
+ }
+
+ $.each(profilePayloads, function (i, item) {
+ $.each(item.content, function (key, value) {
+ //cannot add a true check since it will catch value = false as well
+ if (value === null || value === undefined || value === "") {
+ item.content[key] = null;
+ }
+ });
+ });
var payload = {
"policyName": policy["policyName"],
"description": policy["description"],
"compliance": policy["selectedNonCompliantAction"],
- "ownershipType": "ANY",
- "active": (state != "save"),
+ "ownershipType": policy["selectedOwnership"],
+ "active": isActive,
"profile": {
"profileName": policy["policyName"],
- "deviceType": {
- "id": policy["platformId"],
- "name": policy["platform"]
- },
+ "deviceType": policy["platform"],
"profileFeaturesList": profilePayloads
}
};
@@ -241,101 +354,29 @@ var savePolicy = function (policy, state) {
}
invokerUtil.post(
- "/api/device-mgt/v1.0/policies",
- payload,
- function (response) {
- response = JSON.parse(response);
- if (response["statusCode"] == 201) {
- $(".add-policy").addClass("hidden");
- $(".policy-naming").addClass("hidden");
- $(".policy-message").removeClass("hidden");
- if (state == "publish") {
- publishToDevice();
- }
- }
- },
- function (err) {
- console.log(err);
- }
- );
-};
-
-function publishToDevice() {
- var payload = {
- "policyName": policy["policyName"],
- "description": policy["description"],
- "compliance": policy["selectedNonCompliantAction"],
- "ownershipType": "ANY",
- "deviceId": getParameterByName('deviceId'),
- "profile": {
- "profileName": policy["policyName"],
- "deviceType": {
- "id": policy["platformId"],
- "name": policy["platform"]
- },
- "policyDefinition": window.queryEditor.getValue(),
- "policyDescription": policy["description"]
- }
- };
-
- var successCallback = function (data, status) {
- console.log("Data: " + data + "\nStatus: " + status);
- };
-
- var data = {
- url: "/devicemgt/api/policies/add",
- type: "POST",
- contentType: "application/json",
- accept: "application/json",
- success: successCallback,
- data: JSON.stringify(payload)
- };
-
- $.ajax(data).fail(function (jqXHR) {
- console.log("Error: " + jqXHR);
- });
-
-}
-
-// Start of functions related to grid-input-view
-
-/**
- * Method to set count id to cloned elements.
- * @param {object} addFormContainer
- */
-var setId = function (addFormContainer) {
- $(addFormContainer).find("[data-add-form-clone]").each(function (i) {
- $(this).attr("id", $(this).attr("data-add-form-clone").slice(1) + "-" + (i + 1));
- if ($(this).find(".index").length > 0) {
- $(this).find(".index").html(i + 1);
+ serviceURL,
+ payload,
+ function () {
+ $(".add-policy").addClass("hidden");
+ $(".policy-naming").addClass("hidden");
+ $(".policy-message").removeClass("hidden");
+ },
+ function (data) {
}
- });
-};
-
-/**
- * Method to set count id to cloned elements.
- * @param {object} addFormContainer
- */
-var showHideHelpText = function (addFormContainer) {
- var helpText = "[data-help-text=add-form]";
- if ($(addFormContainer).find("[data-add-form-clone]").length > 0) {
- $(addFormContainer).find(helpText).hide();
- } else {
- $(addFormContainer).find(helpText).show();
- }
+ );
};
function formatRepo(user) {
if (user.loading) {
- return user.text
+ return user.text;
}
if (!user.username) {
return;
}
var markup = '
' +
- '
' +
- '
' +
- '
' + user.username + '
';
+ '
' +
+ '
' +
+ '
' + user.username + '
';
if (user.firstname) {
markup += '
' + user.firstname + '
';
}
@@ -350,170 +391,74 @@ function formatRepoSelection(user) {
return user.username || user.text;
}
-function formatGroupRepo(group) {
- if (group.loading) {
- return group.text
- }
- if (!group.name) {
- return;
- }
- var markup = '
' +
- '
' +
- '
' +
- '
' + group.name + '
';
- if (group.name) {
- markup += '
' + group.name + '
';
- }
- if (group.owner) {
- markup += '
' + group.owner + '
';
- }
- markup += '
';
- return markup;
-}
-
-function formatGroupRepoSelection(group) {
- return group.name || group.text;
-}
-
// End of functions related to grid-input-view
$(document).ready(function () {
- window.queryEditor = CodeMirror.fromTextArea(document.getElementById('policy-definition-input'), {
- mode: MIME_TYPE_SIDDHI_QL,
- indentWithTabs: true,
- smartIndent: true,
- lineNumbers: true,
- matchBrackets: true,
- autofocus: true,
- extraKeys: {
- "Shift-2": function (cm) {
- insertStr(cm, cm.getCursor(), '@');
- CodeMirror.showHint(cm, getAnnotationHints);
+ $("#users-input").select2({
+ multiple: true,
+ tags: false,
+ ajax: {
+ url: context + "/api/invoker/execute/",
+ method: "POST",
+ dataType: 'json',
+ delay: 250,
+ id: function (user) {
+ return user.username;
},
- "Ctrl-Space": "autocomplete"
- }
+ data: function (params) {
+ var postData = {};
+ postData.requestMethod = "GET";
+ postData.requestURL = "/api/device-mgt/v1.0/users/search/usernames?filter=" + params.term;
+ postData.requestPayload = null;
+ return JSON.stringify(postData);
+ },
+ processResults: function (data) {
+ var newData = [];
+ $.each(data, function (index, value) {
+ value.id = value.username;
+ newData.push(value);
+ });
+ return {
+ results: newData
+ };
+ },
+ cache: true
+ },
+ escapeMarkup: function (markup) {
+ return markup;
+ }, // let our custom formatter work
+ minimumInputLength: 1,
+ templateResult: formatRepo, // omitted for brevity, see the source of this page
+ templateSelection: formatRepoSelection // omitted for brevity, see the source of this page
});
- $("#users-input").select2({
- multiple: true,
- tags: true,
- ajax: {
- url: window.location.origin + "/devicemgt/api/invoker/execute/",
- method: "POST",
- dataType: 'json',
- delay: 250,
- id: function (user) {
- return user.username;
- },
- data: function (params) {
- var postData = {};
- postData.actionMethod = "GET";
- postData.actionUrl = "/devicemgt_admin/users";
- postData.actionPayload = JSON.stringify({
- q: params.term, // search term
- page: params.page
- });
-
- return JSON.stringify(postData);
- },
- processResults: function (data, page) {
- var newData = [];
- $.each(data.responseContent, function (index, value) {
- value.id = value.username;
- newData.push(value);
- });
- return {
- results: newData
- };
- },
- cache: true
- },
- escapeMarkup: function (markup) {
- return markup;
- }, // let our custom formatter work
- minimumInputLength: 1,
- templateResult: formatRepo, // omitted for brevity, see the source of this page
- templateSelection: formatRepoSelection // omitted for brevity, see the source of this page
- });
-
- $("#groups-input").select2({
- multiple: true,
- tags: true,
- ajax: {
- url: window.location.origin + "/devicemgt/api/invoker/execute/",
- method: "POST",
- dataType: 'json',
- delay: 250,
- id: function (group) {
- return group.name;
- },
- data: function (params) {
- var postData = {};
- postData.actionMethod = "GET";
- var username = $("#platform").data("username");
- postData.actionUrl = "/devicemgt_admin/groups/user/" + username +
- "/search?groupName=" + params.term;
- return JSON.stringify(postData);
- },
- processResults: function (data, page) {
- var newData = [];
- $.each(data, function (index, value) {
- value.id = value.name;
- newData.push(value);
- });
- return {
- results: newData
- };
- },
- cache: true
- },
- escapeMarkup: function (markup) {
- return markup;
- }, // let our custom formatter work
- minimumInputLength: 1,
- templateResult: formatGroupRepo, // omitted for brevity, see the source of this page
- templateSelection: formatGroupRepoSelection // omitted for brevity, see the source of this page
- });
-
+ $("#loading-content").remove();
+ $(".policy-platform").removeClass("hidden");
// Adding initial state of wizard-steps.
- $("#policy-profile-wizard-steps").html($(".wr-steps").html());
-
- policy["platform"] = $("#platform").data("platform");
- policy["platformId"] = $("#platform").data("platform-id");
- deviceTypeLabel = $("#platform").data("platform-label");
- // updating next-page wizard title with selected platform
- $("#policy-profile-page-wizard-title").text("ADD " + deviceTypeLabel + " POLICY");
+ $("#policy-platform-wizard-steps").html($(".wr-steps").html());
$("select.select2[multiple=multiple]").select2({
- "tags": true
+ "tags": false
});
$("#users-select-field").hide();
- $("#groups-select-field").hide();
$("#user-roles-select-field").show();
$("input[type='radio'].select-users-radio").change(function () {
- if ($("#user-roles-radio-btn").is(":checked")) {
- $("#user-roles-select-field").show();
- $("#users-select-field").hide();
- $("#groups-select-field").hide();
- }
if ($("#users-radio-btn").is(":checked")) {
$("#user-roles-select-field").hide();
$("#users-select-field").show();
- $("#groups-select-field").hide();
}
- if ($("#groups-radio-btn").is(":checked")) {
- $("#user-roles-select-field").hide();
+ if ($("#user-roles-radio-btn").is(":checked")) {
$("#users-select-field").hide();
- $("#groups-select-field").show();
+ $("#user-roles-select-field").show();
}
});
// Support for special input type "ANY" on user(s) & user-role(s) selection
$("#user-roles-input").select2({
- "tags": true
+ "tags": false
}).on("select2:select", function (e) {
if (e.params.data.id == "ANY") {
$(this).val("ANY").trigger("change");
@@ -522,152 +467,7 @@ $(document).ready(function () {
}
});
- // Maintains an array of configured features of the profile
- var advanceOperations = ".wr-advance-operations";
- $(advanceOperations).on("click", ".wr-input-control.switch", function (event) {
- var operationCode = $(this).parents(".operation-data").data("operation-code");
- var operation = $(this).parents(".operation-data").data("operation");
- var operationDataWrapper = $(this).data("target");
- // prevents event bubbling by figuring out what element it's being called from.
- if (event.target.tagName == "INPUT") {
- var featureConfiguredIcon;
- if ($("input[type='checkbox']", this).is(":checked")) {
- configuredOperations.push(operationCode);
- // when a feature is enabled, if "zero-configured-features" msg is available, hide that.
- var zeroConfiguredOperationsErrorMsg = "#policy-profile-main-error-msg";
- if (!$(zeroConfiguredOperationsErrorMsg).hasClass("hidden")) {
- $(zeroConfiguredOperationsErrorMsg).addClass("hidden");
- }
- // add configured-state-icon to the feature
- featureConfiguredIcon = "#" + operation + "-configured";
- if ($(featureConfiguredIcon).hasClass("hidden")) {
- $(featureConfiguredIcon).removeClass("hidden");
- }
- } else {
- //splicing the array if operation is present.
- var index = $.inArray(operationCode, configuredOperations);
- if (index != -1) {
- configuredOperations.splice(index, 1);
- }
- // when a feature is disabled, clearing all its current configured, error or success states
- var subErrorMsgWrapper = "#" + operation + "-feature-error-msg";
- var subErrorIcon = "#" + operation + "-error";
- var subOkIcon = "#" + operation + "-ok";
- featureConfiguredIcon = "#" + operation + "-configured";
-
- if (!$(subErrorMsgWrapper).hasClass("hidden")) {
- $(subErrorMsgWrapper).addClass("hidden");
- }
- if (!$(subErrorIcon).hasClass("hidden")) {
- $(subErrorIcon).addClass("hidden");
- }
- if (!$(subOkIcon).hasClass("hidden")) {
- $(subOkIcon).addClass("hidden");
- }
- if (!$(featureConfiguredIcon).hasClass("hidden")) {
- $(featureConfiguredIcon).addClass("hidden");
- }
- // reinitializing input fields into the defaults
- $(operationDataWrapper + " input").each(
- function () {
- if ($(this).is("input:text")) {
- $(this).val($(this).data("default"));
- } else if ($(this).is("input:password")) {
- $(this).val("");
- } else if ($(this).is("input:checkbox")) {
- $(this).prop("checked", $(this).data("default"));
- // if this checkbox is the parent input of a grouped-input
- if ($(this).hasClass("parent-input")) {
- var groupedInput = $(this).parent().parent().parent();
- updateGroupedInputVisibility(groupedInput);
- }
- }
- }
- );
- // reinitializing select fields into the defaults
- $(operationDataWrapper + " select").each(
- function () {
- var defaultOption = $(this).data("default");
- $("option:eq(" + defaultOption + ")", this).prop("selected", "selected");
- }
- );
- // collapsing expanded-panes (upon the selection of html-select-options) if any
- $(operationDataWrapper + " .expanded").each(
- function () {
- if ($(this).hasClass("expanded")) {
- $(this).removeClass("expanded");
- }
- $(this).slideUp();
- }
- );
- // removing all entries of grid-input elements if exist
- $(operationDataWrapper + " .grouped-array-input").each(
- function () {
- var gridInputs = $(this).find("[data-add-form-clone]");
- if (gridInputs.length > 0) {
- gridInputs.remove();
- }
- var helpTexts = $(this).find("[data-help-text=add-form]");
- if (helpTexts.length > 0) {
- helpTexts.show();
- }
- }
- );
- }
- }
- });
-
- // adding support for cloning multiple profiles per feature with cloneable class definitions
- $(advanceOperations).on("click", ".multi-view.add.enabled", function () {
- // get a copy of .cloneable and create new .cloned div element
- var cloned = "
" + $(".cloneable", $(this).parent().parent()).html() + "";
- // append newly created .cloned div element to panel-body
- $(this).parent().parent().append(cloned);
- // enable remove action of newly cloned div element
- $(".cloned", $(this).parent().parent()).each(
- function () {
- if ($(".multi-view.remove", this).hasClass("disabled")) {
- $(".multi-view.remove", this).removeClass("disabled");
- }
- if (!$(".multi-view.remove", this).hasClass("enabled")) {
- $(".multi-view.remove", this).addClass("enabled");
- }
- }
- );
- });
-
- $(advanceOperations).on("click", ".multi-view.remove.enabled", function () {
- $(this).parent().remove();
- });
-
- // enabling or disabling grouped-input based on the status of a parent check-box
- $(advanceOperations).on("click", ".grouped-input", function () {
- updateGroupedInputVisibility(this);
- });
-
- // add form entry click function for grid inputs
- $(advanceOperations).on("click", "[data-click-event=add-form]", function () {
- var addFormContainer = $("[data-add-form-container=" + $(this).attr("href") + "]");
- var clonedForm = $("[data-add-form=" + $(this).attr("href") + "]").clone().
- find("[data-add-form-element=clone]").attr("data-add-form-clone", $(this).attr("href"));
-
- // adding class .child-input to capture text-input-array-values
- $("input, select", clonedForm).addClass("child-input");
-
- $(addFormContainer).append(clonedForm);
- setId(addFormContainer);
- showHideHelpText(addFormContainer);
- });
-
- // remove form entry click function for grid inputs
- $(advanceOperations).on("click", "[data-click-event=remove-form]", function () {
- var addFormContainer = $("[data-add-form-container=" + $(this).attr("href") + "]");
-
- $(this).closest("[data-add-form-element=clone]").remove();
- setId(addFormContainer);
- showHideHelpText(addFormContainer);
- });
-
+ //Policy wizard stepper
$(".wizard-stepper").click(function () {
// button clicked here can be either a continue button or a back button.
var currentStep = $(this).data("current");
@@ -685,11 +485,11 @@ $(document).ready(function () {
// remove if there are any visible error-messages.
var errorMsgWrappers = ".alert.alert-danger";
$(errorMsgWrappers).each(
- function () {
- if (!$(this).hasClass("hidden")) {
- $(this).addClass("hidden");
- }
+ function () {
+ if (!$(this).hasClass("hidden")) {
+ $(this).addClass("hidden");
}
+ }
);
var nextStep = $(this).data("next");
@@ -728,16 +528,7 @@ $(document).ready(function () {
// hiding current section of the wizard and showing next section.
$("." + currentStep).addClass("hidden");
- if (nextStep !== "policy-message") {
- $("." + nextStep).removeClass("hidden");
- }
+ $("." + nextStep).removeClass("hidden");
}
});
-});
-
-function getParameterByName(name) {
- name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
- var regex = new RegExp("[\\?&]" + name + "=([^]*)"),
- results = regex.exec(location.search);
- return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
-}
\ No newline at end of file
+});
\ No newline at end of file
diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.policy.wizard/wizard.hbs b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.policy.wizard/wizard.hbs
index 1c661f00023..02f7bc76cf6 100644
--- a/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.policy.wizard/wizard.hbs
+++ b/components/device-mgt/org.wso2.carbon.device.mgt.ui/src/main/resources/jaggeryapps/devicemgt/app/units/cdmf.unit.policy.wizard/wizard.hbs
@@ -1,47 +1,13 @@
-{{#zone "topCss"}}
- {{css "css/codemirror.css"}}
-{{/zone}}
-
+
-
-
-
-
1
-
Select a platform
-
-
-
-
-
-
2
-
Configure profile
-
-
-
-
-
-
-
4
-
Publish to devices
-
-
-
-
-