diff --git a/awx/ui/client/features/output/_index.less b/awx/ui/client/features/output/_index.less index 71228d20e7..4238573fb3 100644 --- a/awx/ui/client/features/output/_index.less +++ b/awx/ui/client/features/output/_index.less @@ -352,12 +352,8 @@ flex-wrap: wrap; } -.JobResults-resultRow--variables { - flex-direction: column; - - #cm-variables-container { - width: 100%; - } +.JobResults-codeMirrorResultRowLabel{ + font-size: 12px; } .JobResults-resultRowLabel { @@ -531,4 +527,4 @@ job-results-standard-out { cursor: pointer; border-radius: 5px; font-size: 11px; -} \ No newline at end of file +} diff --git a/awx/ui/client/features/output/details.directive.js b/awx/ui/client/features/output/details.directive.js index f372f75624..eb2b7277c7 100644 --- a/awx/ui/client/features/output/details.directive.js +++ b/awx/ui/client/features/output/details.directive.js @@ -398,8 +398,9 @@ function getExtraVarsDetails () { const label = 'Extra Variables'; const tooltip = 'Read-only view of extra variables added to the job template.'; const value = parse(extraVars); + const disabled = true; - return { label, tooltip, value }; + return { label, tooltip, value, disabled }; } function getLabelDetails () { @@ -558,21 +559,6 @@ function AtJobDetailsController ( vm.job = _.get(resource.model, 'model.GET', {}); vm.canDelete = resource.model.get('summary_fields.user_capabilities.delete'); - // XX - Codemirror - if (vm.extraVars) { - const cm = { - parseType: 'yaml', - $apply: $scope.$apply, - variables: vm.extraVars.value, - }; - - ParseTypeChange({ - field_id: 'cm-extra-vars', - readOnly: true, - scope: cm, - }); - } - vm.cancelJob = cancelJob; vm.deleteJob = deleteJob; vm.toggleLabels = toggleLabels; diff --git a/awx/ui/client/features/output/details.partial.html b/awx/ui/client/features/output/details.partial.html index c8940ec561..25a8f7cbb5 100644 --- a/awx/ui/client/features/output/details.partial.html +++ b/awx/ui/client/features/output/details.partial.html @@ -252,23 +252,14 @@ -
- - -
+ +
diff --git a/awx/ui/client/lib/components/_index.less b/awx/ui/client/lib/components/_index.less index ae992079c9..72c14fe4b5 100644 --- a/awx/ui/client/lib/components/_index.less +++ b/awx/ui/client/lib/components/_index.less @@ -10,3 +10,4 @@ @import 'tabs/_index'; @import 'truncate/_index'; @import 'utility/_index'; +@import 'code-mirror/_index'; diff --git a/awx/ui/client/lib/components/code-mirror/_index.less b/awx/ui/client/lib/components/code-mirror/_index.less new file mode 100644 index 0000000000..770546b151 --- /dev/null +++ b/awx/ui/client/lib/components/code-mirror/_index.less @@ -0,0 +1,76 @@ +.noselect { + -webkit-touch-callout: none; /* iOS Safari */ + -webkit-user-select: none; /* Chrome/Safari/Opera */ + -khtml-user-select: none; /* Konqueror */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* Internet Explorer/Edge */ + user-select: none; /* Non-prefixed version, currently + not supported by any browser */ +} + +.atCodeMirror-label{ + display: flex; + width: 100%; + margin-bottom: 5px; +} + +.atCodeMirror-labelLeftSide{ + flex: 1 0 auto; +} + +.atCodeMirror-labelText{ + text-transform: uppercase; + color: #707070; + font-weight: normal; + font-size: small; + padding-right: 5px; + width: 100%; +} + +.atCodeMirror-toggleContainer{ + margin: 0 0 0 10px; + display: initial; + padding-bottom: 5px; +} + +.atCodeMirror-expandTextContainer{ + flex: 1 0 auto; + text-align: right; + font-weight: normal; + color: @default-link; + cursor: pointer; + font-size: 12px; +} + +.CodeMirror-modal .modal-dialog{ + width: calc(~"100% - 200px"); + height: calc(~"100vh - 80px"); +} + + +@media screen and (min-width: 768px){ + .NetworkingExtraVars .modal-dialog{ + width: 700px; + } +} + +.CodeMirror-modal .modal-dialog{ + width: calc(~"100% - 200px"); + height: calc(~"100vh - 80px"); +} + +.CodeMirror-modal .modal-content{ + height: 100%; +} + +.NetworkingExtraVars .CodeMirror{ + overflow-x: hidden; +} + +.CodeMirror-modalControls{ + float: right; + margin-top: 15px; + button { + margin-left: 10px; + } +} diff --git a/awx/ui/client/lib/components/code-mirror/code-mirror.directive.js b/awx/ui/client/lib/components/code-mirror/code-mirror.directive.js new file mode 100644 index 0000000000..6d74f2a6aa --- /dev/null +++ b/awx/ui/client/lib/components/code-mirror/code-mirror.directive.js @@ -0,0 +1,78 @@ +const templateUrl = require('~components/code-mirror/code-mirror.partial.html'); + +const CodeMirrorID = 'codemirror-extra-vars'; +const CodeMirrorModalID = '#CodeMirror-modal'; +const ParseVariable = 'parseType'; +const CodeMirrorVar = 'variables'; +const ParseType = 'yaml'; + +function atCodeMirrorController ( + $scope, + strings, + ParseTypeChange, + ParseVariableString +) { + const vm = this; + + function init (vars) { + $scope.variables = ParseVariableString(_.cloneDeep(vars)); + $scope.parseType = ParseType; + const options = { + scope: $scope, + variable: CodeMirrorVar, + parse_variable: ParseVariable, + field_id: CodeMirrorID, + readOnly: $scope.disabled + }; + ParseTypeChange(options); + } + + function expand () { + vm.expanded = true; + } + + function close () { + $(CodeMirrorModalID).off('hidden.bs.modal'); + $(CodeMirrorModalID).modal('hide'); + $('.popover').popover('hide'); + vm.expanded = false; + } + + vm.strings = strings; + vm.expanded = false; + vm.close = close; + vm.expand = expand; + if ($scope.init) { + $scope.init = init; + } + init($scope.variables); +} + +atCodeMirrorController.$inject = [ + '$scope', + 'CodeMirrorStrings', + 'ParseTypeChange', + 'ParseVariableString' +]; + +function atCodeMirrorTextarea () { + return { + restrict: 'E', + replace: true, + transclude: true, + templateUrl, + controller: atCodeMirrorController, + controllerAs: 'vm', + scope: { + disabled: '@', + label: '@', + labelClass: '@', + tooltip: '@', + tooltipPlacement: '@', + variables: '@', + init: '=' + } + }; +} + +export default atCodeMirrorTextarea; diff --git a/awx/ui/client/lib/components/code-mirror/code-mirror.partial.html b/awx/ui/client/lib/components/code-mirror/code-mirror.partial.html new file mode 100644 index 0000000000..98349fd3a3 --- /dev/null +++ b/awx/ui/client/lib/components/code-mirror/code-mirror.partial.html @@ -0,0 +1,60 @@ +
+
+
+ + {{ label || vm.strings.get('code_mirror.label.VARIABLES') }} + + + + +
+
+ + +
+
+
+
{{ vm.strings.get('label.EXPAND') }}
+
+ + + +
diff --git a/awx/ui/client/lib/components/code-mirror/code-mirror.strings.js b/awx/ui/client/lib/components/code-mirror/code-mirror.strings.js new file mode 100644 index 0000000000..0dae4bc6a5 --- /dev/null +++ b/awx/ui/client/lib/components/code-mirror/code-mirror.strings.js @@ -0,0 +1,55 @@ +function CodeMirrorStrings (BaseString) { + BaseString.call(this, 'code_mirror'); + + const { t } = this; + const ns = this.code_mirror; + + ns.label = { + EXTRA_VARIABLES: t.s('EXTRA VARIABLES'), + VARIABLES: t.s('VARIABLES'), + EXPAND: t.s('EXPAND'), + YAML: t.s('YAML'), + JSON: t.s('JSON') + + }; + + ns.tooltip = { + TOOLTIP: t.s(` +

+ Enter inventory variables using either JSON or YAML + syntax. Use the radio button to toggle between the two. +

+ JSON: +
+
+ { +
"somevar": "somevalue", +
"password": "magic" +
+ } +
+ YAML: +
+
+ --- +
somevar: somevalue +
password: magic +
+
+

+ View JSON examples at + www.json.org +

+

+ View YAML examples at + + docs.ansible.com +

`), + TOOLTIP_TITLE: t.s('EXTRA VARIABLES'), + JOB_RESULTS: t.s('Read-only view of extra variables added to the job template.') + }; +} + +CodeMirrorStrings.$inject = ['BaseStringService']; + +export default CodeMirrorStrings; diff --git a/awx/ui/client/lib/components/code-mirror/index.js b/awx/ui/client/lib/components/code-mirror/index.js new file mode 100644 index 0000000000..21d4aedc09 --- /dev/null +++ b/awx/ui/client/lib/components/code-mirror/index.js @@ -0,0 +1,12 @@ +import codemirror from './code-mirror.directive'; +import modal from './modal/code-mirror-modal.directive'; +import strings from './code-mirror.strings'; + +const MODULE_NAME = 'at.code.mirror'; + +angular.module(MODULE_NAME, []) + .directive('atCodeMirror', codemirror) + .directive('atCodeMirrorModal', modal) + .service('CodeMirrorStrings', strings); + +export default MODULE_NAME; diff --git a/awx/ui/client/lib/components/code-mirror/modal/code-mirror-modal.directive.js b/awx/ui/client/lib/components/code-mirror/modal/code-mirror-modal.directive.js new file mode 100644 index 0000000000..6a1837272c --- /dev/null +++ b/awx/ui/client/lib/components/code-mirror/modal/code-mirror-modal.directive.js @@ -0,0 +1,84 @@ +const templateUrl = require('~components/code-mirror/modal/code-mirror-modal.partial.html'); + +const CodeMirrorModalID = '#CodeMirror-modal'; +const CodeMirrorID = 'codemirror-extra-vars-modal'; +const ParseVariable = 'parseType'; +const CodeMirrorVar = 'extra_variables'; +const ParseType = 'yaml'; +const ModalHeight = '#CodeMirror-modal .modal-dialog'; +const ModalHeader = '.atCodeMirror-label'; +const ModalFooter = '.CodeMirror-modalControls'; + +function atCodeMirrorModalController ( + $scope, + strings, + ParseTypeChange, + ParseVariableString +) { + const vm = this; + + function resize () { + const editor = $(`${CodeMirrorModalID} .CodeMirror`)[0].CodeMirror; + const height = $(ModalHeight).height() - $(ModalHeader).height() - + $(ModalFooter).height() - 100; + editor.setSize('100%', height); + } + + function toggle () { + $scope.parseTypeChange('parseType', 'extra_variables'); + setTimeout(resize, 0); + } + + function init () { + $(CodeMirrorModalID).modal('show'); + $scope.extra_variables = ParseVariableString(_.cloneDeep($scope.variables)); + $scope.parseType = ParseType; + const options = { + scope: $scope, + variable: CodeMirrorVar, + parse_variable: ParseVariable, + field_id: CodeMirrorID, + readOnly: $scope.disabled + }; + ParseTypeChange(options); + resize(); + $(CodeMirrorModalID).on('hidden.bs.modal', $scope.closeFn); + $(`${CodeMirrorModalID} .modal-dialog`).resizable({ + minHeight: 523, + minWidth: 600 + }); + $(`${CodeMirrorModalID} .modal-dialog`).on('resize', resize); + } + + vm.strings = strings; + vm.toggle = toggle; + init(); +} + +atCodeMirrorModalController.$inject = [ + '$scope', + 'CodeMirrorStrings', + 'ParseTypeChange', + 'ParseVariableString', +]; + +function atCodeMirrorModal () { + return { + restrict: 'E', + replace: true, + transclude: true, + templateUrl, + controller: atCodeMirrorModalController, + controllerAs: 'vm', + scope: { + disabled: '@', + label: '@', + labelClass: '@', + tooltip: '@', + variables: '@', + closeFn: '&' + } + }; +} + +export default atCodeMirrorModal; diff --git a/awx/ui/client/lib/components/code-mirror/modal/code-mirror-modal.partial.html b/awx/ui/client/lib/components/code-mirror/modal/code-mirror-modal.partial.html new file mode 100644 index 0000000000..056672a2b5 --- /dev/null +++ b/awx/ui/client/lib/components/code-mirror/modal/code-mirror-modal.partial.html @@ -0,0 +1,68 @@ + diff --git a/awx/ui/client/lib/components/index.js b/awx/ui/client/lib/components/index.js index 9ac933628c..35b0e0193d 100644 --- a/awx/ui/client/lib/components/index.js +++ b/awx/ui/client/lib/components/index.js @@ -34,6 +34,7 @@ import tab from '~components/tabs/tab.directive'; import tabGroup from '~components/tabs/group.directive'; import topNavItem from '~components/layout/top-nav-item.directive'; import truncate from '~components/truncate/truncate.directive'; +import atCodeMirror from '~components/code-mirror'; import BaseInputController from '~components/input/base.controller'; import ComponentsStrings from '~components/components.strings'; @@ -42,7 +43,8 @@ const MODULE_NAME = 'at.lib.components'; angular .module(MODULE_NAME, [ - atLibServices + atLibServices, + atCodeMirror ]) .directive('atActionGroup', actionGroup) .directive('atDivider', divider) diff --git a/awx/ui/client/src/network-ui/network-details/details.controller.js b/awx/ui/client/src/network-ui/network-details/details.controller.js index cf629ffa62..ff30b97e9f 100644 --- a/awx/ui/client/src/network-ui/network-details/details.controller.js +++ b/awx/ui/client/src/network-ui/network-details/details.controller.js @@ -5,10 +5,15 @@ *************************************************/ export default - ['$scope', '$state', '$stateParams', 'GenerateForm', 'ParseTypeChange', 'HostsService', - function($scope, $state, $stateParams, GenerateForm, ParseTypeChange, HostsService){ + ['$scope', 'HostsService', + function($scope, HostsService){ - $scope.parseType = 'yaml'; + function codemirror () { + return { + init:{} + }; + } + $scope.codeMirror = new codemirror(); $scope.formCancel = function(){ $scope.$parent.$broadcast('awxNet-closeDetailsPanel'); }; @@ -30,7 +35,6 @@ $scope.saveConfirmed = false; }, 3000); }); - }; $scope.$parent.$on('awxNet-showDetails', (e, data, canAdd) => { @@ -40,5 +44,8 @@ } else { $scope.item = data; } + if ($scope.codeMirror.init) { + $scope.codeMirror.init($scope.item.variables); + } }); }]; diff --git a/awx/ui/client/src/network-ui/network-details/details.partial.html b/awx/ui/client/src/network-ui/network-details/details.partial.html index 5b52ae1e6a..f7771e8916 100644 --- a/awx/ui/client/src/network-ui/network-details/details.partial.html +++ b/awx/ui/client/src/network-ui/network-details/details.partial.html @@ -31,7 +31,12 @@
- + +
diff --git a/awx/ui/client/src/network-ui/network-details/main.js b/awx/ui/client/src/network-ui/network-details/main.js index 1827ce7297..ffa06f41e4 100644 --- a/awx/ui/client/src/network-ui/network-details/main.js +++ b/awx/ui/client/src/network-ui/network-details/main.js @@ -5,9 +5,7 @@ *************************************************/ import awxNetDetailsPanel from './details.directive'; -import awxNetExtraVars from './network-extra-vars/network-extra-vars.directive'; export default angular.module('networkDetailsDirective', []) - .directive('awxNetDetailsPanel', awxNetDetailsPanel) - .directive('awxNetExtraVars', awxNetExtraVars); + .directive('awxNetDetailsPanel', awxNetDetailsPanel); diff --git a/awx/ui/client/src/network-ui/network-details/network-extra-vars/network-extra-vars.block.less b/awx/ui/client/src/network-ui/network-details/network-extra-vars/network-extra-vars.block.less deleted file mode 100644 index 9e340cccec..0000000000 --- a/awx/ui/client/src/network-ui/network-details/network-extra-vars/network-extra-vars.block.less +++ /dev/null @@ -1,168 +0,0 @@ -.NetworkingExtraVarsLabel{ - display: flex; - width: 100%; -} - -.NetworkingExtraVars-extraVarsLabelContainer{ - flex: 1 0 auto; -} - -.NetworkingExtraVars-expandTextContainer{ - flex: 1 0 auto; - text-align: right; - font-weight: normal; - color: @default-link; - cursor: pointer; - font-size: 12px; -} - -.noselect { - -webkit-touch-callout: none; /* iOS Safari */ - -webkit-user-select: none; /* Chrome/Safari/Opera */ - -khtml-user-select: none; /* Konqueror */ - -moz-user-select: none; /* Firefox */ - -ms-user-select: none; /* Internet Explorer/Edge */ - user-select: none; /* Non-prefixed version, currently - not supported by any browser */ -} - -@media screen and (min-width: 768px){ - .NetworkingExtraVars .modal-dialog{ - width: 700px; - } -} - -.NetworkingExtraVarsModal .modal-dialog{ - width: calc(~"100% - 200px"); - height: calc(~"100vh - 80px"); -} - -.NetworkingExtraVarsModal .modal-content{ - height: 100%; -} - -.NetworkingExtraVars .CodeMirror{ - overflow-x: hidden; -} - -.NetworkingExtraVars-close:hover{ - color: @btn-txt; - background-color: @btn-bg-hov; -} - -.NetworkingExtraVars-body{ - margin-bottom: 20px; -} - -.NetworkingExtraVars-tab:hover { - color: @btn-txt; - background-color: @btn-bg-hov; - cursor: pointer; -} -.NetworkingExtraVars-tab--selected{ - color: @btn-txt-sel!important; - background-color: @default-icon!important; - border-color: @default-icon!important; -} -.NetworkingExtraVars-view--container{ - width: 100%; - display: flex; - flex-direction: row; - flex-wrap: nowrap; - justify-content: space-between; -} -.NetworkingExtraVars .modal-footer{ - border: 0; - margin-top: 0px; - padding-top: 5px; -} -.NetworkingExtraVars-controls{ - float: right; - margin-top: 15px; - button { - margin-left: 10px; - } -} - -.NetworkingExtraVars-header{ - padding-bottom: 15px; -} -.NetworkingExtraVars-title{ - color: @default-interface-txt; - font-weight: 600; - margin-bottom: 8px; -} -.NetworkingExtraVarsModal .modal-body{ - padding: 0px!important; - overflow-y: auto; -} -.NetworkingExtraVars-nav{ - padding-top: 12px; - padding-bottom: 20px; -} -.NetworkingExtraVars-field{ - margin-bottom: 8px; - flex: 0 1 12em; -} -.NetworkingExtraVars-field--label{ - text-transform: uppercase; - flex: 0 1 80px; - max-width: 80px; - min-width: 80px; - font-size: 12px; - word-wrap: break-word; -} -.NetworkingExtraVars-field{ - .OnePlusTwo-left--detailsRow; -} -.NetworkingExtraVars-field--content{ - word-wrap: break-word; -} -.NetworkingExtraVars-field--monospaceContent{ - font-family: monospace; -} -.NetworkingExtraVars-button:disabled { - pointer-events: all!important; -} - -.NetworkingExtraVars-numberColumnPreload { - background-color: @default-list-header-bg; - height: 198px; - border-right: 1px solid #ccc; - width: 30px; - position: fixed; -} - -.NetworkingExtraVars-numberColumn { - background-color: @default-list-header-bg; - border-right: 1px solid #ccc; - border-bottom-left-radius: 5px; - color: #999; - font-family: Monaco, Menlo, Consolas, "Courier New", monospace; - position: fixed; - padding: 4px 3px 0 5px; - text-align: right; - white-space: nowrap; - width: 30px; -} - -.NetworkingExtraVars-numberColumn--second{ - padding-top:0px; -} - -.NetworkingExtraVars-noJson{ - align-items: center; - background-color: @default-no-items-bord; - border: 1px solid @default-icon-hov; - border-radius: 5px; - color: @b7grey; - display: flex; - height: 200px; - justify-content: center; - text-transform: uppercase; - width: 100%; -} - -.NetworkingExtraVarsModal .CodeMirror{ - max-height: none; -} diff --git a/awx/ui/client/src/network-ui/network-details/network-extra-vars/network-extra-vars.directive.js b/awx/ui/client/src/network-ui/network-details/network-extra-vars/network-extra-vars.directive.js deleted file mode 100644 index 35cf89539d..0000000000 --- a/awx/ui/client/src/network-ui/network-details/network-extra-vars/network-extra-vars.directive.js +++ /dev/null @@ -1,70 +0,0 @@ -/************************************************* - * Copyright (c) 2018 Ansible, Inc. - * - * All Rights Reserved - *************************************************/ - -const templateUrl = require('~network-ui/network-details/network-extra-vars/network-extra-vars.partial.html'); - -export default [ 'ParseTypeChange', 'ParseVariableString', - function(ParseTypeChange, ParseVariableString) { - return { - scope:{ - item: "=" - }, - templateUrl, - restrict: 'E', - link(scope){ - scope.networkingExtraVarsModalOpen = true; - function init(){ - if(scope.item && scope.item.host_id){ - scope.variables = ParseVariableString(scope.item.variables); - scope.parseType = 'yaml'; - ParseTypeChange({ - scope: scope, - field_id: 'network_host_variables', - variable: 'variables', - readOnly: true - }); - } - } - - scope.$watch('item', function(){ - init(); - }); - - scope.closeExtraVarModal = function() { - // Unbind the listener so it doesn't fire when we close the modal via navigation - $('.CodeMirror')[1].remove(); - $('#NetworkingExtraVarsModal').off('hidden.bs.modal'); - $('#NetworkingExtraVarsModal').modal('hide'); - scope.networkingExtraVarsModalOpen = false; - }; - - scope.openExtraVarsModal = function(){ - scope.networkingExtraVarsModalOpen = true; - $('#NetworkingExtraVarsModal').modal('show'); - - $('.modal-dialog').on('resize', function(){ - resize(); - }); - scope.extra_variables = ParseVariableString(_.cloneDeep(scope.item.variables)); - scope.parseType = 'yaml'; - ParseTypeChange({ - scope: scope, - field_id: 'NetworkingExtraVars-codemirror', - variable: 'extra_variables', - readOnly: true - }); - resize(); - }; - - function resize(){ - let editor = $('.CodeMirror')[1].CodeMirror; - let height = $('#NetworkingExtraVarsModalDialog').height() - $('.NetworkingExtraVars-header').height() - $('.NetworkingExtraVars-controls').height() - 110; - editor.setSize("100%", height); - } - - } - }; -}]; diff --git a/awx/ui/client/src/network-ui/network-details/network-extra-vars/network-extra-vars.partial.html b/awx/ui/client/src/network-ui/network-details/network-extra-vars/network-extra-vars.partial.html deleted file mode 100644 index 1ac026747e..0000000000 --- a/awx/ui/client/src/network-ui/network-details/network-extra-vars/network-extra-vars.partial.html +++ /dev/null @@ -1,72 +0,0 @@ - - - - - diff --git a/awx/ui/client/src/network-ui/style.less b/awx/ui/client/src/network-ui/style.less index 0eddbaa9e7..b49b66c8f8 100644 --- a/awx/ui/client/src/network-ui/style.less +++ b/awx/ui/client/src/network-ui/style.less @@ -1,7 +1,6 @@ /* Copyright (c) 2017 Red Hat, Inc. */ @import 'network-nav/network.nav.block.less'; @import 'network-details/details.block.less'; -@import 'network-details/network-extra-vars/network-extra-vars.block.less'; @import 'zoom-widget/zoom.block.less'; @font-face {