From 0f3d5175667fb921e9b2b08dc95b3eea0fa6abd1 Mon Sep 17 00:00:00 2001 From: Sergey Klyuykov Date: Fri, 22 Dec 2017 20:56:55 +1000 Subject: [PATCH 01/52] Add options in Templates API Every option has name and on execution API wait `option` param for override execution args. For security reasons options does not support project and inventory params. cloud/polemarchplus#106 --- polemarch/api/v1/serializers.py | 38 +++++++++- .../migrations/0035_template_options_data.py | 20 +++++ polemarch/main/models/__init__.py | 4 + polemarch/main/models/tasks.py | 73 +++++++++++++++--- polemarch/main/tests/tasks.py | 74 +++++++++++++++++++ 5 files changed, 193 insertions(+), 16 deletions(-) create mode 100644 polemarch/main/migrations/0035_template_options_data.py diff --git a/polemarch/api/v1/serializers.py b/polemarch/api/v1/serializers.py index 4c53f309..7be7f915 100644 --- a/polemarch/api/v1/serializers.py +++ b/polemarch/api/v1/serializers.py @@ -3,6 +3,7 @@ import json import re +from collections import OrderedDict import six from django import dispatch from django.contrib.auth.models import User @@ -525,6 +526,8 @@ def execute(self): class TemplateSerializer(_WithVariablesSerializer): data = DictField(required=True, write_only=True) + options = DictField(write_only=True) + options_list = DictField(read_only=True) class Meta: model = models.Template @@ -533,6 +536,8 @@ class Meta: 'name', 'kind', 'data', + 'options', + 'options_list', ) def get_vars(self, representation): @@ -541,20 +546,41 @@ def get_vars(self, representation): except KeyError: return None + def set_opts_vars(self, rep, hidden_vars): + if not rep.get('vars', None): + return rep + var = rep['vars'] + for mask_key in hidden_vars: + if mask_key in var.keys(): + var[mask_key] = "[~~ENCRYPTED~~]" + return rep + + def repr_options(self, instance, data, hidden_vars): + hv = hidden_vars + hv = instance.HIDDEN_VARS if hv is None else hv + for name, rep in data.get('options', {}).items(): + data['options'][name] = self.set_opts_vars(rep, hv) + def to_representation(self, instance): + data = OrderedDict() if instance.kind in ["Task", "PeriodicTask", "Module"]: - return super(TemplateSerializer, self).to_representation( - instance, hidden_vars=models.PeriodicTask.HIDDEN_VARS + hidden_vars = models.PeriodicTask.HIDDEN_VARS + data = super(TemplateSerializer, self).to_representation( + instance, hidden_vars=hidden_vars ) + self.repr_options(instance, data, hidden_vars) elif instance.kind in ["Host", "Group"]: - return super(TemplateSerializer, self).to_representation( + data = super(TemplateSerializer, self).to_representation( instance, hidden_vars=models.Inventory.HIDDEN_VARS ) + return data class OneTemplateSerializer(TemplateSerializer): data = DictField(required=True) owner = UserSerializer(read_only=True) + options = DictField(required=False) + options_list = DictField(read_only=True) class Meta: model = models.Template @@ -564,11 +590,15 @@ class Meta: 'kind', 'owner', 'data', + 'options', + 'options_list', ) def execute(self, request): serializer = OneProjectSerializer(self.instance.project) - return self.instance.execute(serializer, request.user) + return self.instance.execute( + serializer, request.user, request.data.get('option', None) + ) ################################### diff --git a/polemarch/main/migrations/0035_template_options_data.py b/polemarch/main/migrations/0035_template_options_data.py new file mode 100644 index 00000000..94bb7c56 --- /dev/null +++ b/polemarch/main/migrations/0035_template_options_data.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.3 on 2017-12-22 08:19 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('main', '0034_periodictask_enabled'), + ] + + operations = [ + migrations.AddField( + model_name='template', + name='options_data', + field=models.TextField(default=''), + ), + ] diff --git a/polemarch/main/models/__init__.py b/polemarch/main/models/__init__.py index 8136c9da..639bdded 100644 --- a/polemarch/main/models/__init__.py +++ b/polemarch/main/models/__init__.py @@ -134,6 +134,10 @@ def validate_template_args(instance, **kwargs): if instance.kind == "PeriodicTask" and instance.data["kind"] == "MODULE": command = "module" AnsibleArgumentsReference().validate_args(command, ansible_args) + for _, data in dict(instance.options).items(): + AnsibleArgumentsReference().validate_args( + command, data.get('vars', {}) + ) @receiver(signals.pre_delete, sender=Project) diff --git a/polemarch/main/models/tasks.py b/polemarch/main/models/tasks.py index a5f9e5bb..aa481a33 100644 --- a/polemarch/main/models/tasks.py +++ b/polemarch/main/models/tasks.py @@ -162,6 +162,7 @@ class Template(ACLModel): name = models.CharField(max_length=512) kind = models.CharField(max_length=32) template_data = models.TextField(default="") + options_data = models.TextField(default="") inventory = models.CharField(max_length=128, default=None, blank=True, null=True) project = ForeignKeyACL(Project, @@ -182,13 +183,20 @@ class Meta: template_fields["Host"] = ["name", "vars"] template_fields["Group"] = template_fields["Host"] + ["children"] + excepted_execution_fields = ['inventory', 'project'] _exec_types = { "Task": "playbook", "Module": "module", } + def get_option_data(self, option): + return self.options.get(option, {}) + + def get_options_data(self): + return json.loads(self.options_data or '{}') + def get_data(self): - data = json.loads(self.template_data) + data = json.loads(self.template_data or '{}') if "project" in self.template_fields[self.kind] and self.project: data['project'] = self.project.id if "inventory" in self.template_fields[self.kind] and self.inventory: @@ -198,14 +206,18 @@ def get_data(self): data['inventory'] = self.inventory return data - def execute(self, serializer, user): + def execute(self, serializer, user, option=None): # pylint: disable=protected-access tp = self._exec_types.get(self.kind, None) if tp is None: raise UnsupportedMediaType(media_type=self.kind) data = self.get_data() data.pop("project", None) + option_data = self.get_option_data(option) + option_vars = option_data.pop("vars", {}) vars = data.pop("vars", {}) + vars.update(option_vars) + data.update(option_data) data.update(vars) return serializer._execution(tp, data, user) @@ -217,15 +229,36 @@ def _convert_to_data(self, value): else: raise ValueError("Unknown data type set.") - def keep_encrypted(self, new_vars): - if not self.template_data: - return new_vars - old_vars = self.data['vars'] + def __encrypt(self, new_vars, data_name='data'): + old_vars = getattr(self, data_name).get('vars', {}) for key in new_vars.keys(): if new_vars[key] == '[~~ENCRYPTED~~]': new_vars[key] = old_vars.get(key, new_vars[key]) return new_vars + def keep_encrypted_data(self, new_vars): + if not self.template_data: + return new_vars + return self.__encrypt(new_vars) + + def _validate_option_data(self, data): + errors = {} + for name in data.keys(): + if name in self.excepted_execution_fields: + errors['options'] = ['Disallowed to override {}.'.format(name)] + if errors: + raise ValidationError(errors) + + def set_options_data(self, value): + options_data = self._convert_to_data(value) + new = dict() + for option, data in options_data.items(): + self._validate_option_data(data) + if data.get('vars', None): + data['vars'] = self.__encrypt(data['vars'], 'options') + new[option] = data + self.options_data = json.dumps(new) + def set_data(self, value): data = self._convert_to_data(value) project_id = data.pop('project', None) @@ -240,7 +273,7 @@ def set_data(self, value): self.inventory = Inventory.objects.get(pk=int(inventory_id)).id except (ValueError, TypeError, Inventory.DoesNotExist): self.inventory = inventory_id - data['vars'] = self.keep_encrypted(data['vars']) + data['vars'] = self.keep_encrypted_data(data['vars']) self.template_data = json.dumps(data) def __setattr__(self, key, value): @@ -255,13 +288,29 @@ def data(self): @data.setter def data(self, value): - return self.set_data(value) + self.set_data(value) @data.deleter - def data(self): - self.template_data = "" # nocv - self.inventory = None # nocv - self.project = None # nocv + def data(self): # nocv + self.template_data = "" + self.inventory = None + self.project = None + + @property + def options(self): + return self.get_options_data() + + @options.setter + def options(self, value): + self.set_options_data(value) + + @options.deleter + def options(self): # nocv + self.options_data = '' + + @property + def options_list(self): + return list(self.options.keys()) class HistoryQuerySet(ACLHistoryQuerySet): diff --git a/polemarch/main/tests/tasks.py b/polemarch/main/tests/tasks.py index 22449e16..baf3048c 100644 --- a/polemarch/main/tests/tasks.py +++ b/polemarch/main/tests/tasks.py @@ -773,6 +773,80 @@ def side_effect(call_args, *args, **kwargs): self.get_result("patch", single_url, data=json.dumps(ptask_data)) self.post_result(single_url + "execute/", code=415) + # Execution with options + ansible_args = [] + tmpl_with_opts = dict( + kind="Module", + name='Test opts', + data=dict( + module="shell", + group="all", + project=self.pr_tmplt.id, + inventory=self.history_inventory.id, + args="ls -la", + vars=dict( + forks=8, + ), + ), + options=dict( + one=dict(group='test_group'), + two=dict(args='pwd', vars=dict(forks=1)) + ) + ) + tmplt = self.post_result(url, data=json.dumps(tmpl_with_opts)) + for option in tmpl_with_opts['options'].keys(): + self.assertIn(option, tmplt['options_list'], tmplt) + single_url = "{}{}/".format(url, tmplt['id']) + # test playbook execution default + self.post_result(single_url + "execute/", 201) + self.assertIn(tmpl_with_opts['data']['module'], ansible_args) + self.assertIn(tmpl_with_opts['data']['group'], ansible_args) + self.assertIn('--forks', ansible_args) + self.assertIn( + str(tmpl_with_opts['data']['vars']['forks']), ansible_args + ) + + # test playbook execution one option + ansible_args = [] + self.post_result(single_url + "execute/", 201, data=dict(option='one')) + self.assertIn(tmpl_with_opts['data']['module'], ansible_args) + self.assertIn(tmpl_with_opts['options']['one']['group'], ansible_args) + self.assertIn('--forks', ansible_args) + self.assertIn( + str(tmpl_with_opts['data']['vars']['forks']), ansible_args + ) + + # test playbook execution two option + ansible_args = [] + self.post_result(single_url + "execute/", 201, data=dict(option='two')) + self.assertIn(tmpl_with_opts['data']['module'], ansible_args) + self.assertIn(tmpl_with_opts['options']['two']['args'], ansible_args) + self.assertIn('--forks', ansible_args) + self.assertIn( + str(tmpl_with_opts['options']['two']['vars']['forks']), + ansible_args + ) + + # test invalid attrs for options + invalid_inventory = dict(**tmpl_with_opts) + invalid_inventory['options']['three'] = dict(inventory='some_str') + res = self.post_result(url, 400, data=json.dumps(invalid_inventory)) + self.assertIn( + 'Disallowed to override inventory.', res['detail']['options'], res + ) + + # test encrypted keys + enc_keys = dict(**tmpl_with_opts) + enc_keys['options']['three'] = dict( + vars={"key-file": "some_very_secret_data"} + ) + res = self.post_result(url, data=json.dumps(enc_keys)) + single_url = "{}{}/".format(url, res['id']) + res = self.get_result("get", single_url) + self.assertEqual( + res['options']['three']['vars']['key-file'], '[~~ENCRYPTED~~]', res + ) + def test_string_template_data(self): tmplt_data = dict( name="test_tmplt", From 4f73a644a4c60b333c263d05d7e24e1eebaca65c Mon Sep 17 00:00:00 2001 From: cepreu Date: Wed, 27 Dec 2017 11:02:19 +1000 Subject: [PATCH 02/52] deployment improvements --- polemarch/main/settings.ini | 8 ++++++-- polemarch/main/settings.py | 3 ++- polemarch/web.ini | 11 +++++++++-- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/polemarch/main/settings.ini b/polemarch/main/settings.ini index d8dfcf4c..40ce48d4 100644 --- a/polemarch/main/settings.ini +++ b/polemarch/main/settings.ini @@ -115,6 +115,10 @@ ############################################################## # pidfile = /var/run/polemarch/web.pid -# Path to log of web-server +# Should web-server run as daemon or as regular application ############################################################## -# daemonize = /var/log/polemarch/web.log \ No newline at end of file +# daemon = true + +# Path to log of web-server (applicable if daemon=true) +############################################################## +# log_file = /var/log/polemarch/web.log \ No newline at end of file diff --git a/polemarch/main/settings.py b/polemarch/main/settings.py index 06bc1583..8ffdc503 100644 --- a/polemarch/main/settings.py +++ b/polemarch/main/settings.py @@ -50,7 +50,8 @@ pass # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = config.getboolean("main", "debug", fallback=False) +DEBUG = os.getenv('DJANGO_DEBUG', + config.getboolean("main", "debug", fallback=False)) # Directory for git projects PROJECTS_DIR = config.get("main", "projects_dir", fallback="{HOME}/projects").format(**__kwargs) diff --git a/polemarch/web.ini b/polemarch/web.ini index ed567b04..57aa0625 100755 --- a/polemarch/web.ini +++ b/polemarch/web.ini @@ -27,8 +27,8 @@ static-map = /static=/opt/%(program_name)/lib/python2.7/site-packages/%(program_ # PID-file for service pidfile = /var/run/%(program_name)/web.pid -# log location -daemonize = /var/log/%(program_name)/web.log +# log settings +log_file = /var/log/%(program_name)/web.log # settings.ini for development if-exists = main/settings.ini @@ -45,3 +45,10 @@ if-env = POLEMARCH_SETTINGS_FILE ini = %(_) endif = +# daemonize or not +if-not-opt = daemon +daemon = true +endif = +if-opt = daemon=true +daemonize = %(log_file) +endif = \ No newline at end of file From 2f8ccf7f6567a12c4d7c1802481acbf8b8e7adde Mon Sep 17 00:00:00 2001 From: Trapenok Victor Date: Tue, 9 Jan 2018 16:26:07 +1000 Subject: [PATCH 03/52] tests for GUI cloud/polemarchplus#22 --- polemarch/static/js/crontabEditor.js | 10 +- polemarch/static/js/jsonEditor.js | 32 +-- polemarch/static/js/libs/spa.js | 141 +------------ polemarch/static/js/pmInventories.js | 4 +- polemarch/static/js/pmItems.js | 32 +-- polemarch/static/js/tests/qUnitTest.js | 273 ++++++++++++++++++++++++- 6 files changed, 307 insertions(+), 185 deletions(-) diff --git a/polemarch/static/js/crontabEditor.js b/polemarch/static/js/crontabEditor.js index fd17720d..bdd50b18 100644 --- a/polemarch/static/js/crontabEditor.js +++ b/polemarch/static/js/crontabEditor.js @@ -102,6 +102,14 @@ crontabEditor.setMinutes = function(value) crontabEditor.updateCronString(); } +/** + * Парсит отдельный элемент в cron строке + * @param {type} resArr + * @param {type} str + * @param {type} minInt + * @param {type} maxInt + * @returns {Array} + */ crontabEditor.parseItem = function(resArr, str, minInt, maxInt) { for(var i=minInt; i< maxInt; i++) @@ -172,7 +180,7 @@ crontabEditor.parseItem = function(resArr, str, minInt, maxInt) } else if(/^([0-9]+)\/([0-9]+)$/.test(Parts[i])) { - var match = /^([0-9]+)-([0-9]+)$/.exec(Parts[i]) + var match = /^([0-9]+)\/([0-9]+)$/.exec(Parts[i]) if(match[1]/1 > maxInt) { match[1] = minInt diff --git a/polemarch/static/js/jsonEditor.js b/polemarch/static/js/jsonEditor.js index 8026867d..455e8386 100644 --- a/polemarch/static/js/jsonEditor.js +++ b/polemarch/static/js/jsonEditor.js @@ -230,29 +230,7 @@ jsonEditor.jsonEditorGetValues = function(prefix) return {} } - return jsonEditor.model.data[prefix]; - - /*var data = {} - var arr = $(".jsonEditor-data"+prefix) - for(var i = 0; i< arr.length; i++) - { - var type = $(arr[i]).attr('data-type'); - var index = $(arr[i]).attr('data-json-name'); - - if(type == "boolean") - { - if($(arr[i]).hasClass('selected')) - { - data[index] = ""; - } - } - else - { - data[index] = $(arr[i]).val() - } - } - - return data*/ + return jsonEditor.model.data[prefix]; } jsonEditor.jsonEditorRmVar = function(name, prefix) @@ -304,7 +282,7 @@ jsonEditor.__devAddVar = function(name, value, optionsblock, prefix) /** * Делает импорт переменных из формата инвентория */ -jsonEditor.jsonEditorImportVars = function(optionsblock, prefix) +jsonEditor.jsonEditorImportVars = function(optionsblock, prefix, varsText) { if(!prefix) { @@ -317,7 +295,11 @@ jsonEditor.jsonEditorImportVars = function(optionsblock, prefix) optionsblock = 'base' } - var varsText = $('#new_json_vars'+prefix).val() + if(varsText == undefined) + { + varsText = $('#new_json_vars'+prefix).val() + } + var vars = varsText.split(/\n/gm) var varsresult = {} diff --git a/polemarch/static/js/libs/spa.js b/polemarch/static/js/libs/spa.js index c0ff0163..6ad54319 100644 --- a/polemarch/static/js/libs/spa.js +++ b/polemarch/static/js/libs/spa.js @@ -447,6 +447,7 @@ if(!window.spajs) { if(!menu.id) { + console.error("Не задан menu.id", menu) return; } @@ -456,37 +457,13 @@ if(!window.spajs) } var targetBlock = $("#left_sidebar") - - if(menu.targetTab == "tab1") - { - targetBlock = $(".spa-tab1") - } - else if(menu.targetTab == "tab2") - { - targetBlock = $(".spa-tab2") - } - else if(menu.targetTab == "tab3") - { - targetBlock = $(".spa-tab3") - } - else if(menu.targetTab == "tablist1") - { - targetBlock = $(".spa-tablist1") - } - else if(menu.targetTab == "tablist2") - { - targetBlock = $(".spa-tablist2") - } - else if(menu.targetTab == "tablist3") - { - targetBlock = $(".spa-tablist3") - } - + for(var i in spajs.opt.menu) { if(spajs.opt.menu[i].id == menu.id) { // Такой пункт уже есть в меню + console.error("Такой пункт уже есть в меню", menu) return; } } @@ -498,16 +475,7 @@ if(!window.spajs) spajs.opt.menu.push(menu) - if(menu.type == "bottom") - { - // @fixme Подумать над множественностью этих пунктов или перевести в тип custom - var bottomMenu = '' - - targetBlock.append(bottomMenu) - } - else if(menu.type == "custom") + if(menu.type == "custom") { targetBlock.append('
'+menu.menuHtml+'
'); } @@ -515,111 +483,14 @@ if(!window.spajs) { // Невидимый пункт меню. } - else if(menu.targetTab == "tablist1" || menu.targetTab == "tablist2" || menu.targetTab == "tablist3") - { - var imgHtml = ""; - if(menu.ico) - { - imgHtml = '
\ - \ -
' - } - - var roomMenu = '
  • \ - \ - \ - '+imgHtml+'\ -
    '+menu.name+'
    \ -
    \ - \ -
    \ -
    \ -
  • '; - - //$("#left_sidebar .left_menu .ul_reset").append(roomMenu) - targetBlock.append(roomMenu) - } - else - { - var imgHtml = ""; - if(menu.ico) - { - imgHtml = '
    \ - \ -
    ' - } - - var roomMenu = ''; - - //$("#left_sidebar .left_menu .ul_reset").append(roomMenu) - targetBlock.append(roomMenu) - } - + spajs.sortMenu(targetBlock) if(menu.onInsert) { menu.onInsert($("#spajs-menu-"+menu.id)) } } - - /** - * Устанавливает значение на счётчик событий - * @param string menu_id - * @param string value если значение не задано то счётчик событий будет обнулён и спрятан - */ - spajs.setEventCounterValue = function(menu_id, value) - { - if(value === undefined || value === "" || value === false) - { - $("#spajs-menu-"+menu_id+" .spa-countNew").hide().html('') - } - else - { - $("#spajs-menu-"+menu_id+" .spa-countNew").show().html(value) - } - } - - /** - * Получает значение на счётчика событий - * @param string menu_id - */ - spajs.getEventCounterValue = function(menu_id) - { - return $("#spajs-menu-"+menu_id+" .spa-countNew").html() - } - - /** - * Добавляет значение на счётчика событий - * @param string menu_id - */ - spajs.addEventCounterValue = function(menu_id) - { - var count = parseInt($("#spajs-menu-"+menu_id+" .spa-countNew").show().html()); - if(count > 0) - { - $("#spajs-menu-"+menu_id+" .spa-countNew").html(count+1) - return count+1 - } - else - { - $("#spajs-menu-"+menu_id+" .spa-countNew").html(1) - return 1; - } - } - - + spajs.currentOpenMenu = undefined /** diff --git a/polemarch/static/js/pmInventories.js b/polemarch/static/js/pmInventories.js index 73c8dda6..be8f5173 100644 --- a/polemarch/static/js/pmInventories.js +++ b/polemarch/static/js/pmInventories.js @@ -1331,7 +1331,7 @@ pmInventories.addSubHosts = function(item_id, hosts_ids) return def.promise(); } -pmInventories.validateGroupName = function(name) +/**pmInventories.validateGroupName = function(name) { if(!name) { @@ -1344,7 +1344,7 @@ pmInventories.validateGroupName = function(name) } return false; -} +}*/ diff --git a/polemarch/static/js/pmItems.js b/polemarch/static/js/pmItems.js index b9756739..b9da59e0 100644 --- a/polemarch/static/js/pmItems.js +++ b/polemarch/static/js/pmItems.js @@ -633,7 +633,7 @@ pmItems.deleteItem = function(item_id, force) return def.promise() } -pmItems.multiOperationsOnEachRow = function(elements, operation) +pmItems.multiOperationsOnEachRow = function(elements, operation, force) { var def = new $.Deferred(); var item_ids = [] @@ -642,7 +642,7 @@ pmItems.multiOperationsOnEachRow = function(elements, operation) item_ids.push($(elements[i]).attr('data-id')) } - $.when(this.multiOperationsOnItems(operation, item_ids)).always(function(){ + $.when(this.multiOperationsOnItems(operation, item_ids, force)).always(function(){ def.resolve() }) @@ -1008,17 +1008,17 @@ pmItems.updateItem = function(item_id, opt) return def.promise(); } - pmItems.getFiledByName = function(fileds, name) - { - for(var i in fileds) - { - for(var j in fileds[i]) - { - if(fileds[i][j].name == name) - { - return fileds[i][j] - } - } - } - debugger; - } \ No newline at end of file +/*pmItems.getFiledByName = function(fileds, name) +{ + for(var i in fileds) + { + for(var j in fileds[i]) + { + if(fileds[i][j].name == name) + { + return fileds[i][j] + } + } + } + debugger; +}*/ \ No newline at end of file diff --git a/polemarch/static/js/tests/qUnitTest.js b/polemarch/static/js/tests/qUnitTest.js index 3e62940a..0811d14c 100644 --- a/polemarch/static/js/tests/qUnitTest.js +++ b/polemarch/static/js/tests/qUnitTest.js @@ -366,6 +366,75 @@ window.qunitTestsArray.push({ }); }}) +/** + * Тестирование crontabEditor + */ +window.qunitTestsArray.push({ + step:1400, + test:function() +{ + syncQUnit.addTest('crontabEditor', function ( assert ) + { + var done = assert.async(); + + var cronString = "1 * * * *" + + crontabEditor.parseCronString(undefined) + assert.ok(cronString != crontabEditor.getCronString(), 'getCronString'); + + crontabEditor.parseCronString("1 5") + assert.ok(cronString != crontabEditor.getCronString(), 'getCronString'); + + crontabEditor.parseCronString(cronString) + assert.ok(cronString == crontabEditor.getCronString(), 'getCronString'); + + cronString = "1 1 1 1 1" + crontabEditor.parseCronString(cronString) + assert.ok("1 1 1 1 1" == crontabEditor.getCronString(), 'getCronString'); + + crontabEditor.setDaysOfWeek("1-2") + assert.ok("1 1 1 1 1,2" == crontabEditor.getCronString(), 'getCronString'); + + crontabEditor.setMonths("1-2") + assert.ok("1 1 1 1,2 1,2" == crontabEditor.getCronString(), 'getCronString'); + + crontabEditor.setDayOfMonth("1-2") + assert.ok("1 1 1,2 1,2 1,2" == crontabEditor.getCronString(), 'getCronString'); + + crontabEditor.setHours("1-2") + assert.ok("1 1,2 1,2 1,2 1,2" == crontabEditor.getCronString(), 'getCronString'); + + crontabEditor.setMinutes("1-2") + assert.ok("1,2 1,2 1,2 1,2 1,2" == crontabEditor.getCronString(), 'getCronString'); + + crontabEditor.setMinutes("1,2,7") + assert.ok("1,2,7 1,2 1,2 1,2 1,2" == crontabEditor.getCronString(), 'getCronString'); + + crontabEditor.setMinutes("1,2,*/7") + assert.ok("*/7,1,2 1,2 1,2 1,2 1,2" == crontabEditor.getCronString(), 'getCronString'); + + crontabEditor.setMinutes("1,2,3,4,*/7") + assert.ok("*/7,1-4 1,2 1,2 1,2 1,2" == crontabEditor.getCronString(), 'getCronString'); + + crontabEditor.setMinutes("1,2,3,4,*/7,45-51") + assert.ok("*/7,1-4,45-48,50,51 1,2 1,2 1,2 1,2" == crontabEditor.getCronString(), 'getCronString'); + + crontabEditor.setMinutes("1,2,3,4,*/7,45-51,17-30/2") + assert.ok("*/7,*/23,*/25,1-4,17,19,27,29,45,47,48,51 1,2 1,2 1,2 1,2" == crontabEditor.getCronString(), 'getCronString'); + + crontabEditor.setMinutes("1,2,3,4,*/7,45-51,17-380/2") + assert.ok("0-4,7,14,17,19,21,23,25,27-29,31,33,35,37,39,41-43,45-51,53,55-57,59 1,2 1,2 1,2 1,2" == crontabEditor.getCronString(), 'getCronString'); + + crontabEditor.setMinutes("1,2,3,4,*/7,45-51,170-38/2") + assert.ok("*/7,*/12,*/16,1-4,6,8,10,18,20,22,26,30,34,38,45-47,50,51 1,2 1,2 1,2 1,2" == crontabEditor.getCronString(), 'getCronString'); + + crontabEditor.setMinutes("1,2,3,4,5/5,45-51,170-38/2") + assert.ok("*/5,*/12,*/16,1-4,6,8,14,18,22,26,28,34,38,46,47,49,51 1,2 1,2 1,2 1,2" == crontabEditor.getCronString(), 'getCronString'); + + render(done) + }); +}}) + /** * Тестирование users */ @@ -389,6 +458,24 @@ window.qunitTestsArray.push({ }) }); + syncQUnit.addTest('Страница списка пользователей toggleSelectEachItem', function ( assert ) + { + var done = assert.async(); + + pmUsers.toggleSelectAll($('.multiple-select tr'), true); + + $.when(pmUsers.toggleSelectEachItem(true)).done(function() + { + assert.ok(true, 'ok:toggleSelectEachItem'); + render(done) + }).fail(function() + { + debugger; + assert.ok(false, 'error:toggleSelectEachItem'); + render(done) + }) + }) + syncQUnit.addTest('Открытие страницы добавления пользователя', function ( assert ) { var done = assert.async(); @@ -1383,6 +1470,38 @@ window.qunitTestsArray.push({ }) }) + syncQUnit.addTest('Проверка showAddSubHostsForm в inventory', function ( assert ) + { + var done = assert.async(); + var itemId = /inventory\/([0-9]+)/.exec(window.location.href)[1] + $.when(pmInventories.showAddSubHostsForm(itemId)).done(function() + { + assert.ok(true, 'Проверка showAddSubHostsForm успешна'); + render(done) + }).fail(function() + { + debugger; + assert.ok(false, 'Ошибка при showAddSubHostsForm'); + render(done) + }) + }) + + syncQUnit.addTest('Проверка showAddSubGroupsForm в inventory', function ( assert ) + { + var done = assert.async(); + var itemId = /inventory\/([0-9]+)/.exec(window.location.href)[1] + $.when(pmInventories.showAddSubGroupsForm(itemId)).done(function() + { + assert.ok(true, 'Проверка showAddSubGroupsForm успешна'); + render(done) + }).fail(function() + { + debugger; + assert.ok(false, 'Ошибка при showAddSubGroupsForm'); + render(done) + }) + }) + syncQUnit.addTest('Проверка добавления невалидных хостов к inventory', function ( assert ) { var done = assert.async(); @@ -2092,6 +2211,39 @@ window.qunitTestsArray.push({ render(done) }) }) + + syncQUnit.addTest('Страница periodic-tasks.toggleSelectEachItem', function ( assert ) + { + var done = assert.async(); + $.when(pmPeriodicTasks.toggleSelectEachItem(true, projectId)).done(function() + { + assert.ok(true, 'ok:toggleSelectEachItem'); + render(done) + }).fail(function() + { + debugger; + assert.ok(false, 'error:toggleSelectEachItem'); + render(done) + }) + }) + + syncQUnit.addTest('Страница periodic-tasks.search', function ( assert ) + { + var done = assert.async(); + $.when(pmPeriodicTasks.search("test", {project_id:projectId})).done(function() + { + assert.ok(true, 'ok:periodic-tasks.search'); + render(done) + }).fail(function() + { + debugger; + assert.ok(false, 'error:periodic-tasks.search'); + render(done) + }) + }) + + // pmPeriodicTasks.showSearchResults + /* syncQUnit.addTest('Страница нового inventory для проекта', function ( assert ) { @@ -2354,7 +2506,9 @@ window.qunitTestsArray.push({ { assert.ok(true, 'Успешно copyAndEdit add Item'); render(done) - }).fail(function(){ + }).fail(function() + { + debugger; assert.ok(false, 'Ошибка при copyAndEdit add Item'); render(done) }) @@ -2373,11 +2527,30 @@ window.qunitTestsArray.push({ { assert.ok(true, 'Успешно delete add Item'); render(done) - }).fail(function(){ + }).fail(function() + { + debugger; assert.ok(false, 'Ошибка при delete add Item'); render(done) }) }); + + syncQUnit.addTest('execute для Periodic Task', function ( assert ) + { + var done = assert.async(); + + // Удаление пользователя. + $.when(pmPeriodicTasks.execute(projectId, taskId)).done(function() + { + assert.ok(true, 'Успешно execute для pmPeriodicTasks'); + render(done) + }).fail(function() + { + debugger; + assert.ok(false, 'Ошибка при execute для pmPeriodicTasks'); + render(done) + }) + }); syncQUnit.addTest('Удаление periodic task', function ( assert ) { @@ -2388,7 +2561,9 @@ window.qunitTestsArray.push({ { assert.ok(true, 'Успешно delete Item'); render(done) - }).fail(function(){ + }).fail(function() + { + debugger; assert.ok(false, 'Ошибка при delete Item'); render(done) }) @@ -2404,6 +2579,7 @@ window.qunitTestsArray.push({ render(done) }).fail(function() { + debugger; assert.ok(false, 'Страница не открылась'); render(done) }) @@ -2419,6 +2595,7 @@ window.qunitTestsArray.push({ render(done) }).fail(function() { + debugger; assert.ok(false, 'Страница не открылась'); render(done) }) @@ -2437,7 +2614,9 @@ window.qunitTestsArray.push({ { assert.ok(true, 'Успешно delete Item'); render(done) - }).fail(function(){ + }).fail(function() + { + debugger; assert.ok(false, 'Ошибка при delete Item'); render(done) }) @@ -2496,7 +2675,10 @@ window.qunitTestsArray.push({ $("#Templates-name").val("!2 d#"); jsonEditor.__devAddVar("syntax-check32", "syntax-check32") - + + jsonEditor.jsonEditorImportVars("ansible_user=abc\nansible_host=htt") + jsonEditor.jsonEditorImportVars("ansible_user2:abc\nansible_host3:htt") + // Отправка формы с данными project $.when(pmTasksTemplates.addItem()).done(function() { @@ -3025,6 +3207,84 @@ window.qunitTestsArray.push({ }) }); + + syncQUnit.addTest('Страница history toggleSelectEachItem', function ( assert ) + { + var done = assert.async(); + + pmHistory.toggleSelectAll($('.multiple-select tr'), true); + + $.when(pmHistory.toggleSelectEachItem(true)).done(function() + { + assert.ok(true, 'ok:toggleSelectEachItem'); + render(done) + }).fail(function() + { + debugger; + assert.ok(false, 'error:toggleSelectEachItem'); + render(done) + }) + }) + + syncQUnit.addTest('Страница history toggleSelectEachItem', function ( assert ) + { + var done = assert.async(); + + pmHistory.toggleSelectAll($('.multiple-select tr'), false); + + $.when(pmHistory.toggleSelectEachItem(false)).done(function() + { + $.when(pmHistory.deleteSelected()).done(function() + { + assert.ok(true, 'ok:deleteSelected'); + render(done) + }).fail(function() + { + debugger; + assert.ok(false, 'error:deleteSelected'); + render(done) + }) + + }).fail(function() + { + debugger; + assert.ok(false, 'error:toggleSelectEachItem'); + render(done) + }) + }) + + syncQUnit.addTest('Страница history deleteRows', function ( assert ) + { + var done = assert.async(); + + $.when(pmHistory.deleteRows([])).done(function() + { + assert.ok(true, 'ok:deleteRows'); + render(done) + }).fail(function() + { + debugger; + assert.ok(false, 'error:deleteRows'); + render(done) + }) + }) + + syncQUnit.addTest('Страница history multiOperationsOnEachRow.loadItem', function ( assert ) + { + var done = assert.async(); + + $.when(pmHistory.multiOperationsOnEachRow([], 'loadItem', true)).done(function() + { + assert.ok(true, 'ok:multiOperationsOnEachRow.loadItem'); + render(done) + }).fail(function() + { + debugger; + assert.ok(false, 'error:multiOperationsOnEachRow.loadItem'); + render(done) + }) + }) + syncQUnit.addTest('Страница history 2', function ( assert ) { var done = assert.async(); @@ -3061,6 +3321,7 @@ window.qunitTestsArray.push({ assert.ok(true, 'Ошибка cancelTask'); render(done) }) - }); + }); + }}) From 564bc02968caa68f093f131008aae1cd33dee820 Mon Sep 17 00:00:00 2001 From: Trapenok Victor Date: Wed, 10 Jan 2018 14:05:25 +1000 Subject: [PATCH 04/52] tests for GUI cloud/polemarchplus#22 --- polemarch/static/js/common.js | 2 +- polemarch/static/js/jsonEditor.js | 20 +++--- polemarch/static/js/polemarch.js | 5 +- polemarch/static/js/tests/qUnitTest.js | 96 ++++++++++++++++++++++++-- 4 files changed, 103 insertions(+), 20 deletions(-) diff --git a/polemarch/static/js/common.js b/polemarch/static/js/common.js index 25dfed92..4e81d3db 100644 --- a/polemarch/static/js/common.js +++ b/polemarch/static/js/common.js @@ -136,5 +136,5 @@ if(pmLocalSettings.get('hideMenu')) function toIdString(str) { - return str.replace(/[^A-z0-9]/img, "_"); + return str.replace(/[^A-z0-9\-]/img, "_").replace(/[\[\]]/gi, "_"); } diff --git a/polemarch/static/js/jsonEditor.js b/polemarch/static/js/jsonEditor.js index 455e8386..d75781d8 100644 --- a/polemarch/static/js/jsonEditor.js +++ b/polemarch/static/js/jsonEditor.js @@ -197,7 +197,7 @@ jsonEditor.editor = function(json, opt) opt.prefix = 'prefix' } - opt.prefix = opt.prefix.replace(/[^A-z0-9]/g, "_").replace(/[\[\]]/gi, "_") + opt.prefix = toIdString(opt.prefix) jsonEditor.model.data[opt.prefix] = json; jsonEditor.model.form[opt.prefix] = { showImportForm:false @@ -213,7 +213,7 @@ jsonEditor.jsonEditorScrollTo = function(param_name, prefix) prefix = "prefix" } - prefix = prefix.replace(/[^A-z0-9]/g, "_").replace(/[\[\]]/gi, "_") + prefix = toIdString(prefix) $("body").scrollTo("#json_"+param_name+"_line"+prefix) } @@ -223,7 +223,7 @@ jsonEditor.jsonEditorGetValues = function(prefix) { prefix = "prefix" } - prefix = prefix.replace(/[^A-z0-9]/g, "_").replace(/[\[\]]/gi, "_") + prefix = toIdString(prefix) if(jsonEditor.model.data[prefix] === undefined) { @@ -240,7 +240,7 @@ jsonEditor.jsonEditorRmVar = function(name, prefix) prefix = "prefix" } - prefix = prefix.replace(/[^A-z0-9]/g, "_").replace(/[\[\]]/gi, "_") + prefix = toIdString(prefix) $('#json_'+name+'_line'+prefix+'').remove() if(!$(".jsonEditor-data"+prefix).length) { @@ -288,7 +288,7 @@ jsonEditor.jsonEditorImportVars = function(optionsblock, prefix, varsText) { prefix = "prefix" } - prefix = prefix.replace(/[^A-z0-9]/g, "_").replace(/[\[\]]/gi, "_") + prefix = toIdString(prefix) if(!optionsblock) { @@ -314,7 +314,7 @@ jsonEditor.jsonEditorImportVars = function(optionsblock, prefix, varsText) if(res !== false) { - if($("#json_"+res.name.replace(/[^A-z0-9]/g, "_").replace(/[\[\]]/gi, "_")+"_value"+prefix).length) + if($("#json_"+res.name.replace(/[^A-z0-9\-]/g, "_").replace(/[\[\]]/gi, "_")+"_value"+prefix).length) { $.notify("Var `"+res.name+"` already exists", "error"); continue; @@ -420,7 +420,7 @@ jsonEditor.jsonEditorAddVar = function(optionsblock, prefix) { prefix = "prefix" } - prefix = prefix.replace(/[^A-z0-9]/g, "_").replace(/[\[\]]/gi, "_") + prefix = toIdString(prefix) if(!optionsblock) { @@ -436,7 +436,7 @@ jsonEditor.jsonEditorAddVar = function(optionsblock, prefix) return; } - if($("#json_"+name.replace(/[^A-z0-9]/g, "_").replace(/[\[\]]/gi, "_")+"_value"+prefix).length) + if($("#json_"+name.replace(/[^A-z0-9\-]/g, "_").replace(/[\[\]]/gi, "_")+"_value"+prefix).length) { $.notify("This var already exists", "error"); return; @@ -495,7 +495,7 @@ jsonEditor.initAutoComplete = function(optionsblock, prefix) { prefix = "prefix" } - prefix = prefix.replace(/[^A-z0-9]/g, "_").replace(/[\[\]]/gi, "_") + prefix = toIdString(prefix) new autoComplete({ selector: '#new_json_name'+prefix, @@ -562,7 +562,7 @@ jsonEditor.initForm = function(optionsblock, prefix) { prefix = "prefix" } - prefix = prefix.replace(/[^A-z0-9]/g, "_").replace(/[\[\]]/gi, "_") + prefix = toIdString(prefix) //console.log(optionsblock, jsonEditor.options[optionsblock]) if(jsonEditor.options[optionsblock]) diff --git a/polemarch/static/js/polemarch.js b/polemarch/static/js/polemarch.js index 6e387f99..259eb840 100644 --- a/polemarch/static/js/polemarch.js +++ b/polemarch/static/js/polemarch.js @@ -199,9 +199,6 @@ spajs.errorPage = function(holder, menuInfo, data, error_data) error.text = error_data.detail.toString() } } - - - debugger; - + $(holder).insertTpl(spajs.just.render("errorPage", {error:error, data:data, menuInfo:menuInfo})) } diff --git a/polemarch/static/js/tests/qUnitTest.js b/polemarch/static/js/tests/qUnitTest.js index 0811d14c..c65e197e 100644 --- a/polemarch/static/js/tests/qUnitTest.js +++ b/polemarch/static/js/tests/qUnitTest.js @@ -2676,8 +2676,8 @@ window.qunitTestsArray.push({ jsonEditor.__devAddVar("syntax-check32", "syntax-check32") - jsonEditor.jsonEditorImportVars("ansible_user=abc\nansible_host=htt") - jsonEditor.jsonEditorImportVars("ansible_user2:abc\nansible_host3:htt") + jsonEditor.jsonEditorImportVars("playbook", "prefix", "syntax-check=\n") + jsonEditor.jsonEditorImportVars("playbook", "prefix", "syntax-check:\n") // Отправка формы с данными project $.when(pmTasksTemplates.addItem()).done(function() @@ -2693,7 +2693,7 @@ window.qunitTestsArray.push({ }); syncQUnit.addTest('Сохранение шаблона задачи', function ( assert ) - { + { // Предполагается что мы от прошлого теста попали на страницу создания project var done = assert.async(); @@ -2805,6 +2805,29 @@ window.qunitTestsArray.push({ }) }); + syncQUnit.addTest('Удаление шаблона', function ( assert ) + { + var done = assert.async(); + + $.when(spajs.open({ menuId:"template/Task/"+itemId})).done(function() + { + $.when(pmTasksTemplates.saveAndExecute(itemId)).done(function() + { + assert.ok(true, 'Успешно pmTasksTemplates.saveAndExecute'); + render(done) + }).fail(function(){ + debugger; + assert.ok(false, 'Ошибка при pmTasksTemplates.saveAndExecute'); + render(done) + }) + }).fail(function() + { + debugger; + assert.ok(false, 'Ошибка при открытиии меню template/Module/'+itemId); + render(done) + }) + }); + syncQUnit.addTest('Удаление шаблона', function ( assert ) { var done = assert.async(); @@ -2997,6 +3020,22 @@ window.qunitTestsArray.push({ }) });*/ + syncQUnit.addTest('pmTemplates.exportToFile', function ( assert ) + { + var done = assert.async(); + + $.when(pmTemplates.exportToFile([itemId])).done(function() + { + assert.ok(true, 'pmTemplates.exportToFile ok'); + render(done) + }).fail(function() + { + debugger; + assert.ok(false, 'pmTemplates.exportToFile error'); + render(done) + }) + }); + syncQUnit.addTest('Удаление копии template Module', function ( assert ) { var done = assert.async(); @@ -3017,6 +3056,29 @@ window.qunitTestsArray.push({ }) }); + syncQUnit.addTest('Удаление шаблона Module', function ( assert ) + { + var done = assert.async(); + + $.when(spajs.open({ menuId:"template/Module/"+itemId})).done(function() + { + $.when(pmModuleTemplates.saveAndExecute(itemId)).done(function() + { + assert.ok(true, 'Успешно pmModuleTemplates.saveAndExecute'); + render(done) + }).fail(function(){ + debugger; + assert.ok(false, 'Ошибка при pmModuleTemplates.saveAndExecute'); + render(done) + }) + }).fail(function() + { + debugger; + assert.ok(false, 'Ошибка при открытиии меню template/Module/'+itemId); + render(done) + }) + }); + syncQUnit.addTest('Удаление шаблона', function ( assert ) { var done = assert.async(); @@ -3049,8 +3111,14 @@ window.qunitTestsArray.push({ $.when(spajs.open({ menuId:"home"})).done(function() { assert.ok(true, 'Успешно открыто меню pmDashboard'); + setTimeout(function(){// Ждём завершения всех асинхронных запросов на странице - render(done) + + tabSignal.emit('pmLocalSettings.hideMenu', {type:'set', name:'hideMenu', value:false}) + setTimeout(function() + { + render(done) + }, 500) }, 5000) }).fail(function() { @@ -3181,6 +3249,24 @@ window.qunitTestsArray.push({ render(done) }) }); + + syncQUnit.addTest('Страница ошибки 400 в history', function ( assert ) + { + var done = assert.async(); + + $.when(spajs.open({ menuId:"project/9999999999/history"})).done(function() + { + debugger; + assert.ok(false, 'Успешно открыто меню project/9999999999/history'); + render(done) + }).fail(function() + { + assert.ok(true, 'Ошибка при открытиии меню project/9999999999/history'); + render(done) + }) + }); + + }}) @@ -3324,4 +3410,4 @@ window.qunitTestsArray.push({ }); }}) - + \ No newline at end of file From 3b55de21a40d23757292183884402e34beef76ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B0=D0=BB=D0=B5=D0=BD=D1=82=D0=B8=D0=BD=D0=B0?= <Валентина@192.168.0.106> Date: Mon, 29 Jan 2018 16:04:39 +1000 Subject: [PATCH 05/52] The username link was added. cloud/polemarchplus#72 --- polemarch/main/templates/menu.html | 35 +++++++++++++++--------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/polemarch/main/templates/menu.html b/polemarch/main/templates/menu.html index 81bc7fa5..5d1b1cd1 100644 --- a/polemarch/main/templates/menu.html +++ b/polemarch/main/templates/menu.html @@ -1,18 +1,19 @@ -{% load i18n %} -{% load staticfiles %} -{% load rest_framework %} -{% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %} - - {% if user.is_authenticated %} -
  • {% trans 'API' %}
  • - {% endif %} - - {% if user.is_active and user.is_staff %} - - {% endif %} - {% if user.is_authenticated %} -
  • Logout
  • - {% else %} -
  • Login
  • - {% endif %} +{% load i18n %} +{% load staticfiles %} +{% load rest_framework %} +{% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %} + + {% if user.is_authenticated %} +
  • {% trans 'API' %}
  • + {% endif %} + + {% if user.is_active and user.is_staff %} + + {% endif %} + {% if user.is_authenticated %} +
  • {{user.username}}
  • +
  • Logout!
  • + {% else %} +
  • Login
  • + {% endif %} \ No newline at end of file From 591adcb00a4b4d87a63ecbe8123aae3916529895 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B0=D0=BB=D0=B5=D0=BD=D1=82=D0=B8=D0=BD=D0=B0?= <Валентина@192.168.0.106> Date: Mon, 29 Jan 2018 17:01:51 +1000 Subject: [PATCH 06/52] The inventory link was added to the history item. cloud/polemarchplus#104 --- polemarch/static/js/pmHistory.js | 1421 +++++++++++---------- polemarch/static/templates/pmHistory.html | 1296 +++++++++---------- 2 files changed, 1374 insertions(+), 1343 deletions(-) diff --git a/polemarch/static/js/pmHistory.js b/polemarch/static/js/pmHistory.js index a0edd3bb..28ca69ed 100644 --- a/polemarch/static/js/pmHistory.js +++ b/polemarch/static/js/pmHistory.js @@ -1,703 +1,720 @@ - -var pmHistory = inheritance(pmItems) - -pmHistory.model.name = "history" -pmHistory.model.bulk_name = "history" -pmHistory.model.linePerPage = 130; -pmHistory.justDeepWatch('model'); -pmHistory.model.className = "pmHistory" - -pmHistory.cancelTask = function(item_id) -{ - return spajs.ajax.Call({ - url: "/api/v1/history/"+item_id+"/cancel/", - type: "POST", - contentType:'application/json', - success: function(data) - { - $.notify("Task canceled!", "warning"); - }, - error:function(e) - { - polemarch.showErrors(e.responseJSON) - } - }) -} - - -pmHistory.showSearchResults = function(holder, menuInfo, data) -{ - var thisObj = this; - - var search = this.searchStringToObject(decodeURIComponent(data.reg[1]), 'mode') - return $.when(this.sendSearchQuery(search)).done(function() - { - $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_list', {query:decodeURIComponent(data.reg[1])})) - }).fail(function() - { - $.notify("", "error"); - }) -} - -pmHistory.search = function(query, options) -{ - if(options.inventory_id) - { - if(this.isEmptySearchQuery(query)) - { - return spajs.open({ menuId:'inventory/' + options.inventory_id +"/" + this.model.name, reopen:true}); - } - - return spajs.open({ menuId:'inventory/' + options.inventory_id +"/" + this.model.name+"/search/"+this.searchObjectToString(trim(query), 'mode'), reopen:true}); - } - else if(options.project_id) - { - if(this.isEmptySearchQuery(query)) - { - return spajs.open({ menuId:'project/' + options.project_id +"/" + this.model.name, reopen:true}); - } - - return spajs.open({ menuId:'project/' + options.project_id +"/" + this.model.name+"/search/"+this.searchObjectToString(trim(query), 'mode'), reopen:true}); - } - else if(this.isEmptySearchQuery(query)) - { - return spajs.open({ menuId:this.model.name, reopen:true}); - } - - return spajs.open({ menuId:this.model.name+"/search/"+this.searchObjectToString(trim(query)), reopen:true}); -} - - -pmHistory.showListInProjects = function(holder, menuInfo, data) -{ - var thisObj = this; - var offset = 0 - var limit = this.pageSize; - if(data.reg && data.reg[2] > 0) - { - offset = this.pageSize*(data.reg[2] - 1); - } - var project_id = data.reg[1]; - - return $.when(this.sendSearchQuery({project:project_id}, limit, offset), pmProjects.loadItem(project_id)).done(function() - { - $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_listInProjects', {query:"", project_id:project_id})) - thisObj.model.selectedCount = $('.multiple-select .selected').length; - }).fail(function() - { - $.notify("", "error"); - }) -} - -pmHistory.showListInInventory = function(holder, menuInfo, data) -{ - var thisObj = this; - var offset = 0 - var limit = this.pageSize; - if(data.reg && data.reg[2] > 0) - { - offset = this.pageSize*(data.reg[2] - 1); - } - var inventory_id = data.reg[1]; - - return $.when(this.sendSearchQuery({inventory:inventory_id}, limit, offset), pmInventories.loadItem(inventory_id)).done(function() - { - $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_listInInventory', {query:"", inventory_id:inventory_id})) - }).fail(function() - { - $.notify("", "error"); - }) -} - -pmHistory.showSearchResultsInProjects = function(holder, menuInfo, data) -{ - var thisObj = this; - var project_id = data.reg[1]; - - var search = this.searchStringToObject(decodeURIComponent(data.reg[2]), 'mode') - search['project'] = project_id - - return $.when(this.sendSearchQuery(search), pmProjects.loadItem(project_id)).done(function() - { - $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_listInProjects', {query:decodeURIComponent(data.reg[2]), project_id:project_id})) - }).fail(function() - { - $.notify("", "error"); - }) -} - - -pmHistory.showItem = function(holder, menuInfo, data) -{ - var thisObj = this; - //console.log(menuInfo, data) - - var item_id = data.reg[1]; - return $.when(this.loadItem(item_id)).done(function() - { - if(pmHistory.model.items[item_id].initiator > 0 ) - { - pmUsers.loadItem(pmHistory.model.items[item_id].initiator); - } - $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_page', {item_id:item_id, project_id:0})) - pmHistory.bindStdoutUpdates(item_id) - }).fail(function() - { - $.notify("", "error"); - }) -} - -pmHistory.showItemInProjects = function(holder, menuInfo, data) -{ - var thisObj = this; - //console.log(menuInfo, data) - var project_id = data.reg[1]; - var item_id = data.reg[2]; - return $.when(this.loadItem(item_id), pmProjects.loadItem(project_id)).done(function() - { - if(pmHistory.model.items[item_id].initiator > 0 ) - { - pmUsers.loadItem(pmHistory.model.items[item_id].initiator); - } - $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_pageInProjects', {item_id:item_id, project_id:project_id})) - pmHistory.bindStdoutUpdates(item_id) - }).fail(function() - { - $.notify("", "error"); - }) -} - -pmHistory.bindStdoutUpdates = function(item_id) -{ - var thisObj = this; - $.when(this.loadNewLines(item_id, 0)).always(function() - { - var content = $('#history-stdout') - content.scroll(function() - { - // End of the document reached? - if (content.scrollTop() < 150) - { - if(thisObj.stdout_minline <= 1) - { - return; - } - - if(thisObj.inLoadTopData) - { - return; - } - - //pmHistory.lastContentScrollHeight = $('#history-stdout').prop('scrollHeight') - content.scrollTop() + 100; - - var stdout_minline = thisObj.model.items[item_id].stdout_minline; - if(stdout_minline <= 1) - { - return; - } - - thisObj.inLoadTopData = true; - $.when(thisObj.loadLines(item_id, {before:stdout_minline, limit:thisObj.model.linePerPage})).always(function() - { - var history_stdout = $("#history-stdout"); - if(!history_stdout || !history_stdout.length) - { - return; - } - - for(var i = stdout_minline-1; i > stdout_minline - thisObj.model.linePerPage; i = i -1) - { - if(thisObj.model.items[item_id].stdout[i] != undefined) - { - history_stdout.prepend(pmHistory.getLine(item_id, i)) - } - } - - thisObj.inLoadTopData = false; - if(content.scrollTop() < 10) - { - content.scrollTop(20) - } - }) - } - }); - }); -} - -/** - * Обновляет поле модел this.model.items[item_id] и ложит туда пользователя - */ -pmHistory.loadItem = function(item_id) -{ - var def = new $.Deferred(); - var thisObj = this; - - spajs.ajax.Call({ - url: "/api/v1/"+this.model.name+"/"+item_id+"/", - type: "GET", - contentType:'application/json', - data: "", - success: function(data) - { - if(!thisObj.model.items[item_id]) - { - thisObj.model.items[item_id] = {} - } - - for(var i in data) - { - thisObj.model.items[item_id][i] = data[i] - } - - var promise = undefined; - - if(data.initiator_type == 'scheduler') - { - promise = pmPeriodicTasks.loadItemsByIds([data.initiator]) - } - - if(data.initiator_type == 'users') - { - promise = pmUsers.loadItemsByIds([data.initiator]) - } - - pmHistory.model.items.justWatch(item_id); - - $.when(pmProjects.loadItem(data.project), promise).always(function(){ - def.resolve(data) - }) - }, - error:function(e) - { - console.warn("pmHistory.loadItem", e) - polemarch.showErrors(e) - def.reject(e) - } - }); - - return def.promise(); -} - -pmHistory.sendSearchQuery = function(query, limit, offset) -{ - if(!limit) - { - limit = 999; - } - - if(!offset) - { - offset = 0; - } - - var q = []; - for(var i in query) - { - q.push(encodeURIComponent(i)+"="+encodeURIComponent(query[i])) - } - - var def = new $.Deferred(); - var thisObj = this; - spajs.ajax.Call({ - url: "/api/v1/"+this.model.name+"/?"+q.join('&'), - type: "GET", - contentType:'application/json', - data: "limit="+encodeURIComponent(limit)+"&offset="+encodeURIComponent(offset), - success: function(data) - { - //console.log("update Items", data) - data.limit = limit - data.offset = offset - thisObj.model.itemslist = data - //thisObj.model.items = {} - - var projects = []; - var usersIds = []; - var periodicTasks = []; - - for(var i in data.results) - { - var val = data.results[i] - - thisObj.model.items[val.id] = val - - if(val.project && !pmProjects.model.items[val.project] && $.inArray(val.project, projects) == -1) - { - projects.push(val.project) - } - - if(val.initiator > 0 && val.initiator_type == 'users' && $.inArray(val.initiator, usersIds) == -1) - { - usersIds.push(val.initiator); - } - else if(val.initiator > 0 && val.initiator_type == 'scheduler' && $.inArray(val.initiator, periodicTasks) == -1) - { - periodicTasks.push(val.initiator); - } - } - - var users_promise = undefined; - var projects_promise = undefined; - var periodicTasks_promise = undefined; - - if(periodicTasks.length) - { - periodicTasks_promise = pmPeriodicTasks.loadItemsByIds(periodicTasks) - } - - if(usersIds.length) - { - users_promise = pmUsers.loadItemsByIds(usersIds) - } - - if(projects.length) - { - projects_promise = pmProjects.sendSearchQuery({id:projects.join(',')}) - } - - $.when(users_promise, projects_promise, periodicTasks_promise).done(function(){ - def.resolve(data) - }) - }, - error:function(e) - { - console.warn(e) - polemarch.showErrors(e) - def.reject(e) - } - }); - - return def.promise(); -} - -/** - * Обновляет поле модел this.model.itemslist и ложит туда список пользователей - * Обновляет поле модел this.model.items и ложит туда список инфу о пользователях по их id - */ -pmHistory.loadItems = function(limit, offset) -{ - if(!limit) - { - limit = 30; - } - - if(!offset) - { - offset = 0; - } - - var def = new $.Deferred(); - var thisObj = this; - spajs.ajax.Call({ - url: "/api/v1/"+this.model.name+"/", - type: "GET", - contentType:'application/json', - data: "limit="+encodeURIComponent(limit)+"&offset="+encodeURIComponent(offset), - success: function(data) - { - - //console.log("update Items", data) - data.limit = limit - data.offset = offset - thisObj.model.itemslist = data - //thisObj.model.items = {} - - var projects = []; - var usersIds = []; - var periodicTasks = []; - - for(var i in data.results) - { - var val = data.results[i] - - thisObj.model.items.justWatch(val.id); - thisObj.model.items[val.id] = mergeDeep(thisObj.model.items[val.id], val) - - if(val.project && !pmProjects.model.items[val.project] && $.inArray(val.project, projects) == -1) - { - projects.push(val.project) - } - - if(val.initiator > 0 && val.initiator_type == 'users' && $.inArray(val.initiator, usersIds) == -1) - { - usersIds.push(val.initiator); - } - else if(val.initiator > 0 && val.initiator_type == 'scheduler' && $.inArray(val.initiator, periodicTasks) == -1) - { - periodicTasks.push(val.initiator); - } - } - - var users_promise = undefined; - var projects_promise = undefined; - var periodicTasks_promise = undefined; - - if(periodicTasks.length) - { - periodicTasks_promise = pmPeriodicTasks.loadItemsByIds(periodicTasks) - } - - if(usersIds.length) - { - users_promise = pmUsers.loadItemsByIds(usersIds) - } - - if(projects.length) - { - projects_promise = pmProjects.sendSearchQuery({id:projects.join(',')}) - } - - $.when(users_promise, projects_promise, periodicTasks_promise).always(function(){ - def.resolve(data) - }) - }, - error:function(e) - { - console.warn(e) - polemarch.showErrors(e) - def.reject(e) - } - }); - - return def.promise(); -} - -pmHistory.stopUpdates = function() -{ - clearTimeout(this.model.updateTimeoutId) - this.model.updateTimeoutId = undefined; - - clearTimeout(this.model.loadNewLines_timeoutId) - this.model.loadNewLines_timeoutId = undefined; -} - -/** - * Подсветка синтаксиса - * @link https://habrahabr.ru/post/43030/ - * - * @param {String} code - * @returns {String} - */ -pmHistory.Syntax = function(code) -{ - var comments = []; // Тут собираем все каменты - var strings = []; // Тут собираем все строки - var res = []; // Тут собираем все RegExp - var all = { 'C': comments, 'S': strings, 'R': res }; - var safe = { '<': '<', '>': '>', '&': '&' }; - - var ansi_up = new AnsiUp; - ansi_up.use_classes = true; - var html = ansi_up.ansi_to_html(code); - return html - // Табуляцию заменяем неразрывными пробелами - .replace(/\t/g, - '    '); -} - -pmHistory.getLine = function(item_id, line_id) -{ - var line = this.model.items[item_id].stdout[line_id] - if(/^fatal:/.test(line.text)) - { - line.fatal = 'fatal'; - } - else - { - line.fatal = ''; - } - - return spajs.just.render(this.model.name+'_stdout_line', {line:line}) -} - -pmHistory.loadNewLines = function(item_id, last_stdout_maxline) -{ - var thisObj = this; - - if(last_stdout_maxline === undefined) - { - last_stdout_maxline = this.model.items[item_id].stdout_maxline; - } - - if(!last_stdout_maxline) - { - last_stdout_maxline = 0; - } - - return $.when(this.loadItem(item_id), this.loadLines(item_id, {after:last_stdout_maxline, limit:pmHistory.model.linePerPage})).always(function() - { - var addData = false; - var history_stdout = $("#history-stdout"); - if(!history_stdout || !history_stdout.length) - { - return; - } - - var needScrollDowun = $('#history-stdout').prop('scrollHeight') - $('#history-stdout').scrollTop() - history_stdout.css('height').replace("px", "")/1 < 100 - - if(last_stdout_maxline == 0) - { - for(var i in thisObj.model.items[item_id].stdout) - { - if(thisObj.model.items[item_id].stdout[i] != undefined) - { - history_stdout.append(pmHistory.getLine(item_id, i)) - addData = true; - } - } - } - else - { - for(var i = last_stdout_maxline+1; i <= thisObj.model.items[item_id].stdout_maxline; i++) - { - if(thisObj.model.items[item_id].stdout[i] != undefined) - { - history_stdout.append(pmHistory.getLine(item_id, i)) - addData = true; - } - } - } - - - if( addData && needScrollDowun) - { - // Прокручиваем в низ только если и так скрол был не сильно приподнят - thisObj.scrollBottom() - } - - if(thisObj.model.items[item_id].status == 'RUN' || thisObj.model.items[item_id].status == 'DELAY') - { - thisObj.loadNewLines_timeoutId = setTimeout(function(){ - thisObj.loadNewLines(item_id) - }, 5001) - } - }).promise() -} - -pmHistory.scrollBottom = function() -{ - jQuery('#history-stdout').scrollTop(9999999); -} -/** - * Обновляет поле модел this.model.itemslist и ложит туда список пользователей - * Обновляет поле модел this.model.items и ложит туда список инфу о пользователях по их id - */ -pmHistory.loadLines = function(item_id, opt) -{ - if(!opt.limit) - { - opt.limit = 30; - } - - if(!opt.offset) - { - opt.offset = 0; - } - - opt.format = 'json'; - - var def = new $.Deferred(); - spajs.ajax.Call({ - url: "/api/v1/history/"+item_id+"/lines/", - type: "GET", - contentType:'application/json', - data: opt, - success: function(data) - { - if(!pmHistory.model.items[item_id].stdout) - { - pmHistory.model.items[item_id].stdout = {} - pmHistory.model.items[item_id].stdout_count = 0 - pmHistory.model.items[item_id].stdout_maxline = 0 - pmHistory.model.items[item_id].stdout_minline = 999999999 - } - - pmHistory.model.items[item_id].stdout_count = data.count; - for(var i in data.results) - { - var line_number = data.results[i].line_number - - if(pmHistory.model.items[item_id].stdout_maxline < line_number) - { - pmHistory.model.items[item_id].stdout_maxline = line_number; - } - - if(pmHistory.model.items[item_id].stdout_minline > line_number) - { - pmHistory.model.items[item_id].stdout_minline = line_number; - } - - pmHistory.model.items[item_id].stdout[line_number] = {id:line_number, text:data.results[i].line} - } - - def.resolve() - - }, - error:function(e) - { - console.warn(e) - polemarch.showErrors(e) - def.reject(e) - } - }); - - return def.promise(); -} - - tabSignal.connect("polemarch.start", function() - { - // history - spajs.addMenu({ - id:"history", - urlregexp:[/^history$/, /^history\/search\/?$/, /^history\/page\/([0-9]+)$/], - onOpen:function(holder, menuInfo, data){return pmHistory.showUpdatedList(holder, menuInfo, data);}, - onClose:function(){return pmHistory.stopUpdates();}, - }) - - spajs.addMenu({ - id:"history-search", - urlregexp:[/^history\/search\/([A-z0-9 %\-.:,=]+)$/], - onOpen:function(holder, menuInfo, data){return pmHistory.showSearchResults(holder, menuInfo, data);} - }) - - spajs.addMenu({ - id:"history-item", - urlregexp:[/^history\/([0-9]+)$/], - onOpen:function(holder, menuInfo, data){return pmHistory.showItem(holder, menuInfo, data);}, - onClose:function(){return pmHistory.stopUpdates();} - }) - - spajs.addMenu({ - id:"history-item-in-project", - urlregexp:[/^project\/([0-9]+)\/history\/([0-9]+)$/], - onOpen:function(holder, menuInfo, data){return pmHistory.showItemInProjects(holder, menuInfo, data);}, - onClose:function(){return pmHistory.stopUpdates();} - }) - - spajs.addMenu({ - id:"project-history", - urlregexp:[/^project\/([0-9]+)\/history$/, /^project\/([0-9]+)\/history\/page\/([0-9]+)$/], - onOpen:function(holder, menuInfo, data){ - return pmHistory.showUpdatedList(holder, menuInfo, data, "showListInProjects", function(menuInfo, data) - { - var offset = 0 - var limit = pmHistory.pageSize; - if(data.reg && data.reg[2] > 0) - { - offset = pmHistory.pageSize*(data.reg[2] - 1); - } - var project_id = data.reg[1]; - - return pmHistory.sendSearchQuery({project:project_id}, limit, offset) - }); - }, - onClose:function(){return pmHistory.stopUpdates();}, - }) - - spajs.addMenu({ - id:"project-history-search", - urlregexp:[/^project\/([0-9]+)\/history\/search\/([A-z0-9 %\-.:,=]+)$/], - onOpen:function(holder, menuInfo, data){return pmHistory.showSearchResultsInProjects(holder, menuInfo, data);} - }) - + +var pmHistory = inheritance(pmItems) + +pmHistory.model.name = "history" +pmHistory.model.bulk_name = "history" +pmHistory.model.linePerPage = 130; +pmHistory.justDeepWatch('model'); +pmHistory.model.className = "pmHistory" + +pmHistory.cancelTask = function(item_id) +{ + return spajs.ajax.Call({ + url: "/api/v1/history/"+item_id+"/cancel/", + type: "POST", + contentType:'application/json', + success: function(data) + { + $.notify("Task canceled!", "warning"); + }, + error:function(e) + { + polemarch.showErrors(e.responseJSON) + } + }) +} + + +pmHistory.showSearchResults = function(holder, menuInfo, data) +{ + var thisObj = this; + + var search = this.searchStringToObject(decodeURIComponent(data.reg[1]), 'mode') + return $.when(this.sendSearchQuery(search)).done(function() + { + $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_list', {query:decodeURIComponent(data.reg[1])})) + }).fail(function() + { + $.notify("", "error"); + }) +} + +pmHistory.search = function(query, options) +{ + if(options.inventory_id) + { + if(this.isEmptySearchQuery(query)) + { + return spajs.open({ menuId:'inventory/' + options.inventory_id +"/" + this.model.name, reopen:true}); + } + + return spajs.open({ menuId:'inventory/' + options.inventory_id +"/" + this.model.name+"/search/"+this.searchObjectToString(trim(query), 'mode'), reopen:true}); + } + else if(options.project_id) + { + if(this.isEmptySearchQuery(query)) + { + return spajs.open({ menuId:'project/' + options.project_id +"/" + this.model.name, reopen:true}); + } + + return spajs.open({ menuId:'project/' + options.project_id +"/" + this.model.name+"/search/"+this.searchObjectToString(trim(query), 'mode'), reopen:true}); + } + else if(this.isEmptySearchQuery(query)) + { + return spajs.open({ menuId:this.model.name, reopen:true}); + } + + return spajs.open({ menuId:this.model.name+"/search/"+this.searchObjectToString(trim(query)), reopen:true}); +} + + +pmHistory.showListInProjects = function(holder, menuInfo, data) +{ + var thisObj = this; + var offset = 0 + var limit = this.pageSize; + if(data.reg && data.reg[2] > 0) + { + offset = this.pageSize*(data.reg[2] - 1); + } + var project_id = data.reg[1]; + + return $.when(this.sendSearchQuery({project:project_id}, limit, offset), pmProjects.loadItem(project_id)).done(function() + { + $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_listInProjects', {query:"", project_id:project_id})) + thisObj.model.selectedCount = $('.multiple-select .selected').length; + }).fail(function() + { + $.notify("", "error"); + }) +} + +pmHistory.showListInInventory = function(holder, menuInfo, data) +{ + var thisObj = this; + var offset = 0 + var limit = this.pageSize; + if(data.reg && data.reg[2] > 0) + { + offset = this.pageSize*(data.reg[2] - 1); + } + var inventory_id = data.reg[1]; + + return $.when(this.sendSearchQuery({inventory:inventory_id}, limit, offset), pmInventories.loadItem(inventory_id)).done(function() + { + $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_listInInventory', {query:"", inventory_id:inventory_id})) + }).fail(function() + { + $.notify("", "error"); + }) +} + +pmHistory.showSearchResultsInProjects = function(holder, menuInfo, data) +{ + var thisObj = this; + var project_id = data.reg[1]; + + var search = this.searchStringToObject(decodeURIComponent(data.reg[2]), 'mode') + search['project'] = project_id + + return $.when(this.sendSearchQuery(search), pmProjects.loadItem(project_id)).done(function() + { + $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_listInProjects', {query:decodeURIComponent(data.reg[2]), project_id:project_id})) + }).fail(function() + { + $.notify("", "error"); + }) +} + + +pmHistory.showItem = function(holder, menuInfo, data) +{ + var thisObj = this; + //console.log(menuInfo, data) + + var item_id = data.reg[1]; + return $.when(this.loadItem(item_id)).done(function() + { + if(pmHistory.model.items[item_id].initiator > 0 ) + { + pmUsers.loadItem(pmHistory.model.items[item_id].initiator); + } + + var promiss = pmInventories.loadItem(pmHistory.model.items[item_id].inventory); + $.when(promiss).done(function(){ + $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_page', {item_id:item_id, project_id:0})) + pmHistory.bindStdoutUpdates(item_id) + }).fail(function(){ + $.notify("", "error"); + }); + + + }).fail(function() + { + $.notify("", "error"); + }) +} + +pmHistory.showItemInProjects = function(holder, menuInfo, data) +{ + var thisObj = this; + //console.log(menuInfo, data) + var project_id = data.reg[1]; + var item_id = data.reg[2]; + return $.when(this.loadItem(item_id), pmProjects.loadItem(project_id)).done(function() + { + if(pmHistory.model.items[item_id].initiator > 0 ) + { + pmUsers.loadItem(pmHistory.model.items[item_id].initiator); + } + + var promiss = pmInventories.loadItem(pmHistory.model.items[item_id].inventory); + $.when(promiss).done(function(){ + $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_page', {item_id:item_id, project_id:0})) + pmHistory.bindStdoutUpdates(item_id) + }).fail(function(){ + $.notify("", "error"); + }); + + $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_pageInProjects', {item_id:item_id, project_id:project_id})) + pmHistory.bindStdoutUpdates(item_id) + }).fail(function() + { + $.notify("", "error"); + }) +} + +pmHistory.bindStdoutUpdates = function(item_id) +{ + var thisObj = this; + $.when(this.loadNewLines(item_id, 0)).always(function() + { + var content = $('#history-stdout') + content.scroll(function() + { + // End of the document reached? + if (content.scrollTop() < 150) + { + if(thisObj.stdout_minline <= 1) + { + return; + } + + if(thisObj.inLoadTopData) + { + return; + } + + //pmHistory.lastContentScrollHeight = $('#history-stdout').prop('scrollHeight') - content.scrollTop() + 100; + + var stdout_minline = thisObj.model.items[item_id].stdout_minline; + if(stdout_minline <= 1) + { + return; + } + + thisObj.inLoadTopData = true; + $.when(thisObj.loadLines(item_id, {before:stdout_minline, limit:thisObj.model.linePerPage})).always(function() + { + var history_stdout = $("#history-stdout"); + if(!history_stdout || !history_stdout.length) + { + return; + } + + for(var i = stdout_minline-1; i > stdout_minline - thisObj.model.linePerPage; i = i -1) + { + if(thisObj.model.items[item_id].stdout[i] != undefined) + { + history_stdout.prepend(pmHistory.getLine(item_id, i)) + } + } + + thisObj.inLoadTopData = false; + if(content.scrollTop() < 10) + { + content.scrollTop(20) + } + }) + } + }); + }); +} + +/** + * Обновляет поле модел this.model.items[item_id] и ложит туда пользователя + */ +pmHistory.loadItem = function(item_id) +{ + var def = new $.Deferred(); + var thisObj = this; + + spajs.ajax.Call({ + url: "/api/v1/"+this.model.name+"/"+item_id+"/", + type: "GET", + contentType:'application/json', + data: "", + success: function(data) + { + if(!thisObj.model.items[item_id]) + { + thisObj.model.items[item_id] = {} + } + + for(var i in data) + { + thisObj.model.items[item_id][i] = data[i] + } + + var promise = undefined; + + if(data.initiator_type == 'scheduler') + { + promise = pmPeriodicTasks.loadItemsByIds([data.initiator]) + } + + if(data.initiator_type == 'users') + { + promise = pmUsers.loadItemsByIds([data.initiator]) + } + + pmHistory.model.items.justWatch(item_id); + + $.when(pmProjects.loadItem(data.project), promise).always(function(){ + def.resolve(data) + }) + }, + error:function(e) + { + console.warn("pmHistory.loadItem", e) + polemarch.showErrors(e) + def.reject(e) + } + }); + + return def.promise(); +} + +pmHistory.sendSearchQuery = function(query, limit, offset) +{ + if(!limit) + { + limit = 999; + } + + if(!offset) + { + offset = 0; + } + + var q = []; + for(var i in query) + { + q.push(encodeURIComponent(i)+"="+encodeURIComponent(query[i])) + } + + var def = new $.Deferred(); + var thisObj = this; + spajs.ajax.Call({ + url: "/api/v1/"+this.model.name+"/?"+q.join('&'), + type: "GET", + contentType:'application/json', + data: "limit="+encodeURIComponent(limit)+"&offset="+encodeURIComponent(offset), + success: function(data) + { + //console.log("update Items", data) + data.limit = limit + data.offset = offset + thisObj.model.itemslist = data + //thisObj.model.items = {} + + var projects = []; + var usersIds = []; + var periodicTasks = []; + + for(var i in data.results) + { + var val = data.results[i] + + thisObj.model.items[val.id] = val + + if(val.project && !pmProjects.model.items[val.project] && $.inArray(val.project, projects) == -1) + { + projects.push(val.project) + } + + if(val.initiator > 0 && val.initiator_type == 'users' && $.inArray(val.initiator, usersIds) == -1) + { + usersIds.push(val.initiator); + } + else if(val.initiator > 0 && val.initiator_type == 'scheduler' && $.inArray(val.initiator, periodicTasks) == -1) + { + periodicTasks.push(val.initiator); + } + } + + var users_promise = undefined; + var projects_promise = undefined; + var periodicTasks_promise = undefined; + + if(periodicTasks.length) + { + periodicTasks_promise = pmPeriodicTasks.loadItemsByIds(periodicTasks) + } + + if(usersIds.length) + { + users_promise = pmUsers.loadItemsByIds(usersIds) + } + + if(projects.length) + { + projects_promise = pmProjects.sendSearchQuery({id:projects.join(',')}) + } + + $.when(users_promise, projects_promise, periodicTasks_promise).done(function(){ + def.resolve(data) + }) + }, + error:function(e) + { + console.warn(e) + polemarch.showErrors(e) + def.reject(e) + } + }); + + return def.promise(); +} + +/** + * Обновляет поле модел this.model.itemslist и ложит туда список пользователей + * Обновляет поле модел this.model.items и ложит туда список инфу о пользователях по их id + */ +pmHistory.loadItems = function(limit, offset) +{ + if(!limit) + { + limit = 30; + } + + if(!offset) + { + offset = 0; + } + + var def = new $.Deferred(); + var thisObj = this; + spajs.ajax.Call({ + url: "/api/v1/"+this.model.name+"/", + type: "GET", + contentType:'application/json', + data: "limit="+encodeURIComponent(limit)+"&offset="+encodeURIComponent(offset), + success: function(data) + { + + //console.log("update Items", data) + data.limit = limit + data.offset = offset + thisObj.model.itemslist = data + //thisObj.model.items = {} + + var projects = []; + var usersIds = []; + var periodicTasks = []; + + for(var i in data.results) + { + var val = data.results[i] + + thisObj.model.items.justWatch(val.id); + thisObj.model.items[val.id] = mergeDeep(thisObj.model.items[val.id], val) + + if(val.project && !pmProjects.model.items[val.project] && $.inArray(val.project, projects) == -1) + { + projects.push(val.project) + } + + if(val.initiator > 0 && val.initiator_type == 'users' && $.inArray(val.initiator, usersIds) == -1) + { + usersIds.push(val.initiator); + } + else if(val.initiator > 0 && val.initiator_type == 'scheduler' && $.inArray(val.initiator, periodicTasks) == -1) + { + periodicTasks.push(val.initiator); + } + } + + var users_promise = undefined; + var projects_promise = undefined; + var periodicTasks_promise = undefined; + + if(periodicTasks.length) + { + periodicTasks_promise = pmPeriodicTasks.loadItemsByIds(periodicTasks) + } + + if(usersIds.length) + { + users_promise = pmUsers.loadItemsByIds(usersIds) + } + + if(projects.length) + { + projects_promise = pmProjects.sendSearchQuery({id:projects.join(',')}) + } + + $.when(users_promise, projects_promise, periodicTasks_promise).always(function(){ + def.resolve(data) + }) + }, + error:function(e) + { + console.warn(e) + polemarch.showErrors(e) + def.reject(e) + } + }); + + return def.promise(); +} + +pmHistory.stopUpdates = function() +{ + clearTimeout(this.model.updateTimeoutId) + this.model.updateTimeoutId = undefined; + + clearTimeout(this.model.loadNewLines_timeoutId) + this.model.loadNewLines_timeoutId = undefined; +} + +/** + * Подсветка синтаксиса + * @link https://habrahabr.ru/post/43030/ + * + * @param {String} code + * @returns {String} + */ +pmHistory.Syntax = function(code) +{ + var comments = []; // Тут собираем все каменты + var strings = []; // Тут собираем все строки + var res = []; // Тут собираем все RegExp + var all = { 'C': comments, 'S': strings, 'R': res }; + var safe = { '<': '<', '>': '>', '&': '&' }; + + var ansi_up = new AnsiUp; + ansi_up.use_classes = true; + var html = ansi_up.ansi_to_html(code); + return html + // Табуляцию заменяем неразрывными пробелами + .replace(/\t/g, + '    '); +} + +pmHistory.getLine = function(item_id, line_id) +{ + var line = this.model.items[item_id].stdout[line_id] + if(/^fatal:/.test(line.text)) + { + line.fatal = 'fatal'; + } + else + { + line.fatal = ''; + } + + return spajs.just.render(this.model.name+'_stdout_line', {line:line}) +} + +pmHistory.loadNewLines = function(item_id, last_stdout_maxline) +{ + var thisObj = this; + + if(last_stdout_maxline === undefined) + { + last_stdout_maxline = this.model.items[item_id].stdout_maxline; + } + + if(!last_stdout_maxline) + { + last_stdout_maxline = 0; + } + + return $.when(this.loadItem(item_id), this.loadLines(item_id, {after:last_stdout_maxline, limit:pmHistory.model.linePerPage})).always(function() + { + var addData = false; + var history_stdout = $("#history-stdout"); + if(!history_stdout || !history_stdout.length) + { + return; + } + + var needScrollDowun = $('#history-stdout').prop('scrollHeight') - $('#history-stdout').scrollTop() - history_stdout.css('height').replace("px", "")/1 < 100 + + if(last_stdout_maxline == 0) + { + for(var i in thisObj.model.items[item_id].stdout) + { + if(thisObj.model.items[item_id].stdout[i] != undefined) + { + history_stdout.append(pmHistory.getLine(item_id, i)) + addData = true; + } + } + } + else + { + for(var i = last_stdout_maxline+1; i <= thisObj.model.items[item_id].stdout_maxline; i++) + { + if(thisObj.model.items[item_id].stdout[i] != undefined) + { + history_stdout.append(pmHistory.getLine(item_id, i)) + addData = true; + } + } + } + + + if( addData && needScrollDowun) + { + // Прокручиваем в низ только если и так скрол был не сильно приподнят + thisObj.scrollBottom() + } + + if(thisObj.model.items[item_id].status == 'RUN' || thisObj.model.items[item_id].status == 'DELAY') + { + thisObj.loadNewLines_timeoutId = setTimeout(function(){ + thisObj.loadNewLines(item_id) + }, 5001) + } + }).promise() +} + +pmHistory.scrollBottom = function() +{ + jQuery('#history-stdout').scrollTop(9999999); +} +/** + * Обновляет поле модел this.model.itemslist и ложит туда список пользователей + * Обновляет поле модел this.model.items и ложит туда список инфу о пользователях по их id + */ +pmHistory.loadLines = function(item_id, opt) +{ + if(!opt.limit) + { + opt.limit = 30; + } + + if(!opt.offset) + { + opt.offset = 0; + } + + opt.format = 'json'; + + var def = new $.Deferred(); + spajs.ajax.Call({ + url: "/api/v1/history/"+item_id+"/lines/", + type: "GET", + contentType:'application/json', + data: opt, + success: function(data) + { + if(!pmHistory.model.items[item_id].stdout) + { + pmHistory.model.items[item_id].stdout = {} + pmHistory.model.items[item_id].stdout_count = 0 + pmHistory.model.items[item_id].stdout_maxline = 0 + pmHistory.model.items[item_id].stdout_minline = 999999999 + } + + pmHistory.model.items[item_id].stdout_count = data.count; + for(var i in data.results) + { + var line_number = data.results[i].line_number + + if(pmHistory.model.items[item_id].stdout_maxline < line_number) + { + pmHistory.model.items[item_id].stdout_maxline = line_number; + } + + if(pmHistory.model.items[item_id].stdout_minline > line_number) + { + pmHistory.model.items[item_id].stdout_minline = line_number; + } + + pmHistory.model.items[item_id].stdout[line_number] = {id:line_number, text:data.results[i].line} + } + + def.resolve() + + }, + error:function(e) + { + console.warn(e) + polemarch.showErrors(e) + def.reject(e) + } + }); + + return def.promise(); +} + + tabSignal.connect("polemarch.start", function() + { + // history + spajs.addMenu({ + id:"history", + urlregexp:[/^history$/, /^history\/search\/?$/, /^history\/page\/([0-9]+)$/], + onOpen:function(holder, menuInfo, data){return pmHistory.showUpdatedList(holder, menuInfo, data);}, + onClose:function(){return pmHistory.stopUpdates();}, + }) + + spajs.addMenu({ + id:"history-search", + urlregexp:[/^history\/search\/([A-z0-9 %\-.:,=]+)$/], + onOpen:function(holder, menuInfo, data){return pmHistory.showSearchResults(holder, menuInfo, data);} + }) + + spajs.addMenu({ + id:"history-item", + urlregexp:[/^history\/([0-9]+)$/], + onOpen:function(holder, menuInfo, data){return pmHistory.showItem(holder, menuInfo, data);}, + onClose:function(){return pmHistory.stopUpdates();} + }) + + spajs.addMenu({ + id:"history-item-in-project", + urlregexp:[/^project\/([0-9]+)\/history\/([0-9]+)$/], + onOpen:function(holder, menuInfo, data){return pmHistory.showItemInProjects(holder, menuInfo, data);}, + onClose:function(){return pmHistory.stopUpdates();} + }) + + spajs.addMenu({ + id:"project-history", + urlregexp:[/^project\/([0-9]+)\/history$/, /^project\/([0-9]+)\/history\/page\/([0-9]+)$/], + onOpen:function(holder, menuInfo, data){ + return pmHistory.showUpdatedList(holder, menuInfo, data, "showListInProjects", function(menuInfo, data) + { + var offset = 0 + var limit = pmHistory.pageSize; + if(data.reg && data.reg[2] > 0) + { + offset = pmHistory.pageSize*(data.reg[2] - 1); + } + var project_id = data.reg[1]; + + return pmHistory.sendSearchQuery({project:project_id}, limit, offset) + }); + }, + onClose:function(){return pmHistory.stopUpdates();}, + }) + + spajs.addMenu({ + id:"project-history-search", + urlregexp:[/^project\/([0-9]+)\/history\/search\/([A-z0-9 %\-.:,=]+)$/], + onOpen:function(holder, menuInfo, data){return pmHistory.showSearchResultsInProjects(holder, menuInfo, data);} + }) + }) \ No newline at end of file diff --git a/polemarch/static/templates/pmHistory.html b/polemarch/static/templates/pmHistory.html index ce8c78e7..83c9d5aa 100644 --- a/polemarch/static/templates/pmHistory.html +++ b/polemarch/static/templates/pmHistory.html @@ -1,641 +1,655 @@ - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + From addea52bc5611b0058b4fce80a32a774253f611f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=92=D0=B0=D0=BB=D0=B5=D0=BD=D1=82=D0=B8=D0=BD=D0=B0?= <Валентина@192.168.0.106> Date: Mon, 29 Jan 2018 17:24:46 +1000 Subject: [PATCH 07/52] Scroll to the SubGroup block was added. cloud/polemarchplus#73 --- polemarch/static/js/pmInventories.js | 2894 +++++++++++++------------- 1 file changed, 1451 insertions(+), 1443 deletions(-) diff --git a/polemarch/static/js/pmInventories.js b/polemarch/static/js/pmInventories.js index 73c8dda6..7f42649f 100644 --- a/polemarch/static/js/pmInventories.js +++ b/polemarch/static/js/pmInventories.js @@ -1,1444 +1,1452 @@ - - -var pmInventories = inheritance(pmItems) -pmInventories.model.name = "inventories" -pmInventories.model.page_name = "inventory" -pmInventories.model.bulk_name = "inventory" -pmInventories.model.className = "pmInventories" - -/** - * Параметры из секции *:vars - * Строка где после первого `=` всё остальное значение. - */ -pmInventories.parseMonoVarsLine = function(index, line) -{ - var vars = {} - var param = /^([^=]+)="(.*)"$/.exec(line) - - if(param) - { - vars[param[1]] = param[2] - } - else - { - param = /^([^=]+)=(.*)$/.exec(line) - if(param) - { - vars[param[1]] = param[2] - } - else - { - throw "Error in line "+index+" invalid varibles string ("+line+")" - } - } - return vars; -} - -/** - * Параметры хоста - * Строка где может быть несколько параметров ключ=значение через пробел - */ -pmInventories.parseVarsLine = function(index, line) -{ - var vars = {} - do{ - if(line.length == 0) - { - break; - } - - var params = /^([^=]+)=["'](.*?)["'] +[^=]+=/.exec(line) - if(params) - { - params[1] = trim(params[1]) - vars[params[1]] = stripslashes(params[2]) - line = trim(line.slice(params[1].length + params[2].length + 3)) - continue; - } - - params = /^([^=]+)=([^ ]*) +[^=]+=/.exec(line) - if(params) - { - params[1] = trim(params[1]) - vars[params[1]] = stripslashes(params[2]) - line = trim(line.slice(params[1].length + params[2].length + 1)) - continue; - } - - params = /^([^=]+)=["'](.*?)["'] *$/.exec(line) - if(params) - { - params[1] = trim(params[1]) - vars[params[1]] = stripslashes(params[2]) - break; - } - - params = /^([^=]+)=([^ ]*) *$/.exec(line) - if(params) - { - params[1] = trim(params[1]) - vars[params[1]] = stripslashes(params[2]) - line = trim(line.slice(params[1].length + params[2].length + 1)) - continue; - } - else - { - throw "Error in line "+index+" invalid varibles string ("+line+")" - } - }while(true) - return vars; -} - -/** - * Хост и параметры - * Строка где идёт сначала имя хоста а потом его параметры в `parseVarsLine` - */ -pmInventories.parseHostLine = function(index, line, section, inventory) -{ - var params = /^([^ ]+)/.exec(line) - if(!params) - { - throw "Error in line "+index+" ("+line+")" - } - - var name = "" - var type = "" - if(pmItems.validateHostName(params[1])) - { - name = params[1] - type = "HOST" - } - else if(pmItems.validateRangeName(params[1])) - { - name = params[1] - type = "RANGE" - } - else - { - throw "Error in line "+index+" invalid host or range name ("+params[1]+")" - } - - line = trim(line.slice(name.length)) - - var host = { - name:name, - type:type, - vars:pmInventories.parseVarsLine(index, line) - } - - if(section !== "_hosts") - { - pmInventories.addGroupIfNotExists(inventory, section) - inventory.groups[section].hosts.push(host) - } - else - { - inventory.hosts.push(host) - } -} - -/** - * Парсит строку файла инвентория - * @param {integer} index - * @param {string} line - * @param {string} section - * @param {Object} inventory - * @returns {Boolean} - */ -pmInventories.parseLine = function(index, line, section, inventory) -{ - line = trim(line); - - if(section == "_hosts") - { - pmInventories.parseHostLine(index, line, section, inventory) - return true; - } - - if(section == "all:vars") - { - var vars = pmInventories.parseMonoVarsLine(index, line) - inventory.vars = Object.assign(inventory.vars, vars) - return true; - } - - if(/:vars$/.test(section)) - { - section = section.substring(0, section.length - ":vars".length) - - pmInventories.addGroupIfNotExists(inventory, section) - inventory.groups[section].vars = Object.assign(inventory.groups[section].vars, pmInventories.parseMonoVarsLine(index, line)) - return true; - } - - if(/:children$/.test(section)) - { - /** - * Параметры из секции *:children - * Строка где просто имя группы - */ - section = section.substring(0, section.length - ":children".length) - - pmInventories.addGroupIfNotExists(inventory, section) - inventory.groups[section].children = true - inventory.groups[section].groups.push(line) - pmInventories.addGroupIfNotExists(inventory, line) - return true; - } - - pmInventories.parseHostLine(index, line, section, inventory) - return false; -} - -/** - * Добавляет группу в инвенторий если её ещё нет - * @param {Object} inventory - * @param {string} group_name имя группы - * @returns {Boolean} true если группа добавлена. - */ -pmInventories.addGroupIfNotExists = function(inventory, group_name) -{ - if(!inventory.groups[group_name]) - { - inventory.groups[group_name] = { - vars:{}, - groups:[], - hosts:[], - } - - return true; - } - - return false; -} - - -/** - * Парсит файла инвентория - * @param {string} text текст файла инвентория - * @returns {Object} - */ -pmInventories.parseFromText = function(text) -{ - var lines = text.split(/\n/g) - - var cSection = "_hosts"; - var inventory = { - hosts:[], - groups:{}, - vars:{}, - name:new Date().toString() - } - - for(var i in lines) - { - var line = lines[i].replace(/^ */g, "") - - if(/^\s*[#;]\s+inventory name: (.*)/ig.test(line)) - { - var name = /^\s*[#;]\s+inventory name: (.*)/ig.exec(line) - inventory.name = name[1] - continue; - } - - if(/^\s*$/ig.test(line)) - { - continue; - } - if(/^\s*[#;]/ig.test(line)) - { - continue; - } - - console.log(i+":\t" + line) - - if(/^\[([A-z0-9\.:\-]+:vars)\]/ig.test(line)) - { - var res = /^\[([A-z0-9\.:\-]+)\]/ig.exec(line) - cSection = res[1] - - var group_name = cSection.substring(0, cSection.length - ":vars".length) - if(group_name != "all") - { - pmInventories.addGroupIfNotExists(inventory, group_name) - } - console.info("setSection:vars ", cSection) - continue; - } - if(/^\[([A-z0-9\.:\-]+:children)\]/ig.test(line)) - { - var res = /^\[([A-z0-9\.:\-]+)\]/ig.exec(line) - cSection = res[1] - pmInventories.addGroupIfNotExists(inventory, cSection.substring(0, cSection.length - ":children".length)) - console.info("setSection:children ", cSection) - continue; - } - if(/^\[([A-z0-9\.:\-]+)\]/ig.test(line)) - { - var res = /^\[([A-z0-9\.:\-]+)\]/ig.exec(line) - cSection = res[1] - pmInventories.addGroupIfNotExists(inventory, cSection) - console.info("setSection ", cSection) - continue; - } - - pmInventories.parseLine(i, line, cSection, inventory) - } - - pmInventories.addHierarchyDataToInventoryGroups(inventory) - console.log("\n\ninventory", inventory) - return inventory; -} - - - -/** - * Формирует вспомагательную информацию в объекте инвентория о вложенности групп друг в друга. - * @param {Object} inventory Инвенторий (Обязательный) - * @param {string} group_name (не обязательный) - * @param {integer} level (не обязательный) - * @param {Array} parents (не обязательный) - */ -pmInventories.addHierarchyDataToInventoryGroups = function(inventory, group_name, level, parents) -{ - if(!level) - { - level = 0 - } - - if(parents === undefined) - { - parents = [] - } - - if(group_name === undefined || group_name == 'all') - { - for(var i in inventory.groups) - { - delete inventory.groups[i]['dataLevel'] - } - - for(var i in inventory.groups) - { - pmInventories.addHierarchyDataToInventoryGroups(inventory, i, 1, ['all']) - } - - return; - } - - - if(inventory.groups[group_name].dataLevel && inventory.groups[group_name].dataLevel.level >= level ) - { - return; - } - - parents.push(group_name) - inventory.groups[group_name].dataLevel = { - level:level, - parents:parents, - } - - for(var i in inventory.groups[group_name].groups) - { - var hasError = false; - for(var j in inventory.groups[group_name].dataLevel.parents) - { - var val = inventory.groups[group_name].dataLevel.parents[j] - if(val == inventory.groups[group_name].groups[i]) - { - inventory.groups[group_name].dataLevel.error = "Group `"+val+"` is recursive include into group `"+inventory.groups[group_name].groups[i]+"`"; - console.warn(inventory.groups[group_name].dataLevel.error) - hasError = true - break; - } - } - - if(hasError) - { - continue; - } - - pmInventories.addHierarchyDataToInventoryGroups(inventory, inventory.groups[group_name].groups[i], level+1, parents.slice()) - } - - return; -} - -// ansible_ssh_private_key_file - запрашивать значение этого параметра. - -pmInventories.importFromFile = function(files_event) -{ - var def = new $.Deferred(); - this.model.files = files_event - this.model.importedInventories = {} - var thisObj = this; - for(var i=0; i - $.notify("Error in field ansible_ssh_private_key_file invalid value", "error"); - pmInventories.showInventoryVarsModal(); - def2.reject() - return def2.promise(); - } - - for(var i in inventory.hosts) - { - var val = inventory.hosts[i] - if(val.vars.ansible_ssh_private_key_file !== undefined && !/-----BEGIN RSA PRIVATE KEY-----/.test(val.vars.ansible_ssh_private_key_file)) - { - // - $.notify("Error in field ansible_ssh_private_key_file invalid value", "error"); - pmInventories.showHostVarsModal({group:'all', name:val.name}); - def2.reject() - return def2.promise(); - } - } - - for(var i in inventory.groups) - { - var val = inventory.groups[i] - if(val.vars.ansible_ssh_private_key_file !== undefined && !/-----BEGIN RSA PRIVATE KEY-----/.test(val.vars.ansible_ssh_private_key_file)) - { - // - $.notify("Error in field ansible_ssh_private_key_file invalid value", "error"); - pmInventories.showGroupVarsModal({name:i}); - def2.reject() - return def2.promise(); - } - - for(var j in val.hosts) - { - var hval = val.hosts[j] - if(hval.vars.ansible_ssh_private_key_file !== undefined && !/-----BEGIN RSA PRIVATE KEY-----/.test(hval.vars.ansible_ssh_private_key_file)) - { - // - $.notify("Error in field ansible_ssh_private_key_file invalid value", "error"); - pmInventories.showHostVarsModal({group:i, name:hval.name}); - def2.reject() - return def2.promise(); - } - } - } - - var def = new $.Deferred(); - - if($("#inventory_name").val() != "") - { - inventory.name = $("#inventory_name").val(); - } - - if(!inventory.name) - { - // inventory.name = "new imported inventory" - - $.notify("Error in field inventory name", "error"); - def2.reject({text:"Error in field inventory name"}) - return def2.promise(); - } - - var inventoryObject = { - name:inventory.name, - vars:inventory.vars - } - - var deleteBulk = [] - $.when(pmInventories.importItem(inventoryObject)).done(function(inventory_id) - { - deleteBulk.push({ - type:"del", - item:'inventory', - pk:inventory_id - }) - var bulkdata = [] - // Сбор групп и вложенных в них хостов - for(var i in inventory.groups) - { - var val = inventory.groups[i] - - bulkdata.push({ - type:"add", - item:'group', - data:{ - name:i, - children:val.children, - vars:val.vars - } - }) - - for(var j in val.hosts) - { - var hval = val.hosts[j] - bulkdata.push({ - type:"add", - item:'host', - data:{ - name:hval.name, - type:hval.type, - vars:hval.vars - } - }) - } - } - - // Сбор хостов вложенных к инвенторию - var bulkHosts = [] - for(var i in inventory.hosts) - { - var val = inventory.hosts[i] - bulkHosts.push({ - type:"add", - item:'host', - data:{ - name:val.name, - type:val.type, - vars:val.vars - } - }) - } - - // Добавление хостов вложенных к инвенторию - spajs.ajax.Call({ - url: "/api/v1/_bulk/", - type: "POST", - contentType:'application/json', - data:JSON.stringify(bulkHosts), - success: function(data) - { - var hasError = false; - var hosts_ids = [] - for(var i in data) - { - var val = data[i] - if(val.status != 201) - { - $.notify("Error "+val.status, "error"); - hasError = true; - continue; - } - hosts_ids.push(val.data.id) - deleteBulk.push({ - type:"del", - item:'host', - pk:val.data.id - }) - } - - if(hasError) - { - // По меньшей мере в одной операции была ошибка вставки. - // Инвенторий импортирован не полностью - def.reject(deleteBulk); - return; - } - - $.when(pmInventories.addSubHosts(inventory_id, hosts_ids)).done(function() - { - // Добавление групп и вложенных в них хостов - spajs.ajax.Call({ - url: "/api/v1/_bulk/", - type: "POST", - contentType:'application/json', - data:JSON.stringify(bulkdata), - success: function(data) - { - var igroups_ids = [] - var bulk_update = [] - var hasError = false; - for(var i in data) - { - deleteBulk.push({ - type:"del", - item:data.item, - pk:data[i].data.id - }) - - var val = data[i] - if(val.status != 201) - { - $.notify("Error "+val.status, "error"); - hasError = true; - } - - if(val.data.children !== undefined ) - { - igroups_ids.push(val.data.id) - - // Это группа - if(val.data.children) - { - if(inventory.groups[val.data.name].groups.length) - { - // Добавление подгрупп - var groups_ids = [] - for(var j in inventory.groups[val.data.name].groups) - { - // найти id группы и прекрепить. - for(var k in data) - { - if(data[k].data.children !== undefined && data[k].data.name == inventory.groups[val.data.name].groups[j]) - { - groups_ids.push(data[k].data.id) - break; - } - } - } - bulk_update.push({ - type: "mod", - item:'group', - method: "PUT", - data_type: 'groups', - pk:val.data.id, - data:groups_ids - }) - } - } - else - { - if(inventory.groups[val.data.name].hosts.length) - { - // Добавление хостов - var hosts_ids = [] - for(var j in inventory.groups[val.data.name].hosts) - { - // найти id группы и прекрепить. - for(var k in data) - { - if(data[k].data.children === undefined && data[k].data.name == inventory.groups[val.data.name].hosts[j].name) - { - hosts_ids.push(data[k].data.id) - break; - } - } - } - bulk_update.push({ - type: "mod", - item:'group', - method: "PUT", - data_type: 'hosts', - pk:val.data.id, - data:hosts_ids - }) - } - } - } - else - { - // Это хост - } - } - - if(hasError) - { - // По меньшей мере в одной операции была ошибка вставки. - // Инвенторий импортирован не полностью - def.reject(deleteBulk); - return; - } - - $.when(pmInventories.addSubGroups(inventory_id, igroups_ids)).done(function() - { - if(bulk_update.length) - { - spajs.ajax.Call({ - url: "/api/v1/_bulk/", - type: "POST", - contentType:'application/json', - data:JSON.stringify(bulk_update), - success: function(data) - { - var hasError = false; - for(var i in data) - { - var val = data[i] - if(val.status != 200) - { - $.notify("Error "+val.status, "error"); - hasError = true; - continue; - } - } - - if(hasError) - { - // По меньшей мере в одной операции была ошибка обновления. - // Инвенторий импортирован не полностью - def.reject(deleteBulk); - return; - } - - def.resolve(inventory_id); - }, - error:function(e) - { - console.warn(e) - polemarch.showErrors(e) - def.reject(deleteBulk); - } - }) - } - else - { - def.resolve(inventory_id); - } - }).fail(function(e){ - console.warn(e) - polemarch.showErrors(e) - def.reject(deleteBulk); - }) - }, - error:function(e) - { - console.warn(e) - polemarch.showErrors(e) - def.reject(deleteBulk); - } - }); - }).fail(function(e){ - console.warn(e) - polemarch.showErrors(e) - def.reject(deleteBulk); - }) - }, - error:function(e) - { - console.warn(e) - polemarch.showErrors(e) - def.reject(deleteBulk); - } - }) - }).fail(function(e) - { - console.warn(e) - polemarch.showErrors(e) - def.reject(deleteBulk); - }) - - $.when(def).done(function(inventory_id) - { - def2.resolve(inventory_id) - }).fail(function(delete_bulk) - { - $.when(spajs.ajax.Call({ - url: "/api/v1/_bulk/", - type: "POST", - contentType:'application/json', - data:JSON.stringify(delete_bulk), - })).always(function(){ - def2.reject() - }) - }) - - return def2.promise(); -} - - -pmInventories.showImportPage = function(holder, menuInfo, data) -{ - var def = new $.Deferred(); - - var text = spajs.just.render(this.model.name+'_import_page', {}) - $(holder).insertTpl(text) - - def.resolve() - return def.promise(); -} - -pmInventories.renderImportedInventory = function(imported) -{ - if(!imported || !imported.inventory) - { - return "" - } - - var text = spajs.just.render(this.model.name+'_imported_inventory', {inventory:imported.inventory, text:imported.text}) - return text; -} - -pmInventories.copyItem = function(item_id) -{ - var def = new $.Deferred(); - var thisObj = this; - - $.when(this.loadItem(item_id)).done(function() - { - var data = thisObj.model.items[item_id]; - delete data.id; - data.name = "copy from " + data.name - - $.when(encryptedCopyModal.replace(data)).done(function(data) - { - spajs.ajax.Call({ - url: "/api/v1/"+thisObj.model.name+"/", - type: "POST", - contentType:'application/json', - data: JSON.stringify(data), - success: function(newItem) - { - thisObj.model.items[newItem.id] = newItem - - var groups = [] - for(var i in data.groups) - { - groups.push(data.groups[i].id) - } - - var hosts = [] - for(var i in data.hosts) - { - hosts.push(data.hosts[i].id) - } - - $.when(thisObj.setSubGroups(newItem.id, groups), thisObj.setSubHosts(newItem.id, hosts)).always(function(){ - def.resolve(newItem.id) - }) - }, - error:function(e) - { - def.reject(e) - } - }); - }).fail(function(e) - { - def.reject(e) - }) - }).fail(function(e) - { - def.reject(e) - }) - - - return def.promise(); -} - - - - -pmInventories.model.page_list = { - title: "Inventories", - short_title: "Inventories", - buttons:[ - { - class:'btn btn-primary', - function:function(){ return "spajs.open({ menuId:'new-"+this.model.page_name+"'}); return false;"}, - title:'Create', - link:function(){ return '/?new-'+this.model.page_name}, - }, - { - tpl:function(){ - return spajs.just.render('inventories_btn_openImportPageAndImportFiles', {}) - }, - }, - ], - fileds:[ - { - title:'Name', - name:'name', - }, - ], - actions:[ - { - class:'btn btn-danger', - function:function(item){ return 'spajs.showLoader('+this.model.className+'.deleteItem('+item.id+')); return false;'}, - title:'Delete', - link:function(){ return '#'} - }, - { - class:'btn btn-default', - function:function(item){ return '';}, - title:'Create sub group', - link:function(item) - { - return '/?inventory/'+item.id+'/new-group' - }, - }, - { - class:'btn btn-default', - function:function(item){ return '';}, - title:'Create sub host', - link:function(item) - { - return '/?inventory/'+item.id+'/new-host' - }, - } - ] -} - -pmInventories.model.page_new = { - title: "New inventory", - short_title: "New inventory", - fileds:[ - [ - { - filed: new filedsLib.filed.text(), - title:'Name', - name:'name', - placeholder:'Enter inventory name', - validator:function(value){ - return filedsLib.validator.notEmpty(value, 'Name') - }, - fast_validator:filedsLib.validator.notEmpty - }, - ] - ], - sections:[ - function(section){ - return jsonEditor.editor({}, {block:this.model.name}); - } - ], - onBeforeSave:function(data) - { - data.vars = jsonEditor.jsonEditorGetValues() - return data; - }, - onCreate:function(data, status, xhr, callOpt) - { - var def = new $.Deferred(); - $.notify("Inventory created", "success"); - - if(callOpt.parent_item) - { - if(callOpt.parent_type == 'project') - { - $.when(pmProjects.addSubInventories(callOpt.parent_item, [data.id])).always(function(){ - $.when(spajs.open({ menuId:"project/"+callOpt.parent_item})).always(function(){ - def.resolve() - }) - }) - } - } - else - { - $.when(spajs.open({ menuId: this.model.page_name + "/"+data.id})).always(function(){ - def.resolve() - }) - } - return def.promise(); - } -} - -pmInventories.model.page_item = { - buttons:[ - { - class:'btn btn-primary', - function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.updateItem('+item_id+')); return false;'}, - title:'Save', - link:function(){ return '#'}, - }, - { - class:'btn btn-primary', - function:function(item_id){ return 'return spajs.openURL(this.href);'}, - title:'History', - link:function(item_id){ return polemarch.opt.host + '/?inventory/' + item_id + '/history'}, - }, - { - class:'btn btn-default copy-btn', - function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.copyAndEdit('+item_id+')); return false;'}, - title:'', - link:function(){ return '#'}, - help:'Copy' - }, - { - class:'btn btn-danger danger-right', - function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.deleteItem('+item_id+')); return false;'}, - title:' ', - link:function(){ return '#'}, - }, - ], - sections:[ - function(section, item_id){ - return jsonEditor.editor(this.model.items[item_id].vars, {block:this.model.name}); - }, - function(section, item_id){ - return spajs.just.render("inventories_sub_items", {item_id:item_id}) - } - ], - title: function(item_id){ - return "Inventory "+this.model.items[item_id].justText('name') - }, - short_title: function(item_id){ - return "Inventory "+this.model.items[item_id].justText('name', function(v){return v.slice(0, 20)}) - }, - fileds:[ - [ - { - filed: new filedsLib.filed.text(), - title:'Name', - name:'name', - placeholder:'Enter inventory name', - validator:function(value){ - return filedsLib.validator.notEmpty(value, 'Name') - }, - fast_validator:function(value){ return value != '' && value} - }, - ] - ], - onUpdate:function(result) - { - return true; - }, - onBeforeSave:function(data, item_id) - { - data.vars = jsonEditor.jsonEditorGetValues() - return data; - }, -} - -/** - * Показывает форму со списком всех групп. - * @return $.Deferred - */ -pmInventories.showAddSubGroupsForm = function(item_id, holder) -{ - if(!item_id) - { - throw "Error in pmInventories.showAddSubGroupsForm with item_id = `" + item_id + "`" - } - - return $.when(pmGroups.loadAllItems()).done(function(){ - $("#add_existing_item_to_inventory").remove() - $(".content").appendTpl(spajs.just.render('add_existing_groups_to_inventory', {item_id:item_id})) - $("#polemarch-model-items-select").select2({ width: '100%' }); - }).fail(function(){ - - }).promise() -} - -/** - * Показывает форму со списком всех хостов. - * @return $.Deferred - */ -pmInventories.showAddSubHostsForm = function(item_id, holder) -{ - if(!item_id) - { - throw "Error in pmInventories.showAddSubHostsForm with item_id = `" + item_id + "`" - } - - return $.when(pmHosts.loadAllItems()).done(function(){ - $("#add_existing_item_to_inventory").remove() - $(".content").appendTpl(spajs.just.render('add_existing_hosts_to_inventory', {item_id:item_id})) - $("#polemarch-model-items-select").select2({ width: '100%' }); - }).fail(function(){ - - }).promise() -} - -/** - * Проверяет принадлежит ли host_id к группе item_id - * @param {Integer} item_id - * @param {Integer} host_id - * @returns {Boolean} - */ -pmInventories.hasHosts = function(item_id, host_id) -{ - if(!item_id) - { - throw "Error in pmInventories.hasHosts with item_id = `" + item_id + "`" - } - - if(pmInventories.model.items[item_id]) - { - for(var i in pmInventories.model.items[item_id].hosts) - { - if(pmInventories.model.items[item_id].hosts[i].id == host_id) - { - return true; - } - } - } - return false; -} - -/** - * Проверяет принадлежит ли host_id к группе item_id - * @param {Integer} item_id - * @param {Integer} host_id - * @returns {Boolean} - */ -pmInventories.hasGroups = function(item_id, group_id) -{ - if(!item_id) - { - throw "Error in pmInventories.hasGroups with item_id = `" + item_id + "`" - } - - if(pmInventories.model.items[item_id]) - { - for(var i in pmInventories.model.items[item_id].groups) - { - if(pmInventories.model.items[item_id].groups[i].id == group_id) - { - return true; - } - } - } - return false; -} - - -/** - * @return $.Deferred - */ -pmInventories.setSubGroups = function(item_id, groups_ids) -{ - if(!item_id) - { - throw "Error in pmInventories.setSubGroups with item_id = `" + item_id + "`" - } - - if(!groups_ids) - { - groups_ids = [] - } - - return spajs.ajax.Call({ - url: "/api/v1/inventories/"+item_id+"/groups/", - type: "PUT", - contentType:'application/json', - data:JSON.stringify(groups_ids), - success: function(data) - { - if(pmInventories.model.items[item_id]) - { - pmInventories.model.items[item_id].groups = [] - for(var i in groups_ids) - { - pmInventories.model.items[item_id].groups.push(pmGroups.model.items[groups_ids[i]]) - } - } - }, - error:function(e) - { - console.warn("group "+item_id+" update error - " + JSON.stringify(e)); - polemarch.showErrors(e.responseJSON) - } - }); -} - -/** - * @return $.Deferred - */ -pmInventories.setSubHosts = function(item_id, hosts_ids) -{ - if(!hosts_ids) - { - hosts_ids = [] - } - - if(!item_id) - { - throw "Error in pmInventories.setSubHosts with item_id = `" + item_id + "`" - } - - - return spajs.ajax.Call({ - url: "/api/v1/inventories/"+item_id+"/hosts/", - type: "PUT", - contentType:'application/json', - data:JSON.stringify(hosts_ids), - success: function(data) - { - if(pmInventories.model.items[item_id]) - { - pmInventories.model.items[item_id].hosts = [] - for(var i in hosts_ids) - { - pmInventories.model.items[item_id].hosts.push(pmHosts.model.items[hosts_ids[i]]) - } - } - }, - error:function(e) - { - console.warn("group "+item_id+" update error - " + JSON.stringify(e)); - polemarch.showErrors(e.responseJSON) - } - }); -} - -/** - * @return $.Deferred - */ -pmInventories.addSubGroups = function(item_id, groups_ids) -{ - if(!item_id) - { - throw "Error in pmInventories.addSubGroups with item_id = `" + item_id + "`" - } - - if(!groups_ids) - { - groups_ids = [] - } - - var def = new $.Deferred(); - spajs.ajax.Call({ - url: "/api/v1/inventories/"+item_id+"/groups/", - type: "POST", - contentType:'application/json', - data:JSON.stringify(groups_ids), - success: function(data) - { - if(data.not_found > 0) - { - $.notify("Item not found", "error"); - def.reject({text:"Item not found", status:404}) - return; - } - - if(pmInventories.model.items[item_id]) - { - if(!pmInventories.model.items[item_id].groups) - { - pmInventories.model.items[item_id].groups = [] - } - - for(var i in groups_ids) - { - pmInventories.model.items[item_id].groups.push(pmGroups.model.items[groups_ids[i]]) - } - } - - $.notify("Save", "success"); - def.resolve() - }, - error:function(e) - { - console.warn("group "+item_id+" update error - " + JSON.stringify(e)); - polemarch.showErrors(e.responseJSON) - def.reject(e) - } - }); - return def.promise(); -} - -/** - * @return $.Deferred - */ -pmInventories.addSubHosts = function(item_id, hosts_ids) -{ - if(!item_id) - { - throw "Error in pmInventories.addSubHosts with item_id = `" + item_id + "`" - } - - var def = new $.Deferred(); - if(!hosts_ids || hosts_ids.length == 0) - { - def.resolve() - return def.promise(); - } - - spajs.ajax.Call({ - url: "/api/v1/inventories/"+item_id+"/hosts/", - type: "POST", - contentType:'application/json', - data:JSON.stringify(hosts_ids), - success: function(data) - { - if(data.not_found > 0) - { - $.notify("Item not found", "error"); - def.reject({text:"Item not found", status:404}) - return; - } - - if(pmInventories.model.items[item_id]) - { - if(!pmInventories.model.items[item_id].hosts) - { - pmInventories.model.items[item_id].hosts = [] - } - - for(var i in hosts_ids) - { - pmInventories.model.items[item_id].hosts.push(pmHosts.model.items[hosts_ids[i]]) - } - } - - $.notify("Save", "success"); - def.resolve() - }, - error:function(e) - { - console.warn("group "+item_id+" update error - " + JSON.stringify(e)); - polemarch.showErrors(e.responseJSON) - def.reject(e) - } - }); - return def.promise(); -} - -pmInventories.validateGroupName = function(name) -{ - if(!name) - { - return false; - } - - if(/^[a-zA-Z0-9\-\._]*$/.test(name.toLowerCase())) - { - return true; - } - - return false; -} - - - - -/** - * Для ввода инвентория - * @type Object - */ -pmInventories.filed.inventoriesAutocomplete = inheritance(filedsLib.filed.simpleText) -pmInventories.filed.inventoriesAutocomplete.type = 'inventoriesAutocomplete' -pmInventories.filed.inventoriesAutocomplete.getValue = function(pmObj, filed) -{ - var inventory = $("#inventories-autocomplete").val() - if($("#inventory-source").val() != 'db') - { - inventory = $("#inventories-file").val() - if(!/^\.\//.test(inventory)) - { - inventory = trim("./"+inventory) - } - } - - - return inventory; -} - -/** - * Функция для рендера поля - * @type Object - */ -pmInventories.filed.inventoriesAutocomplete.render = function(pmObj, filed, item_id, opt) -{ - var html = spajs.just.render('filed_type_'+this.type, {pmObj:pmObj, filed:filed, item_id:item_id, filedObj:this, opt:opt}) - return spajs.just.onInsert(html, function() - { - // @FixMe требует чтоб были загружены все инвентории pmInventories.loadAllItems() - $("#inventories-autocomplete").select2({ width: '100%' }); - - if(filed.onchange && item_id) - { - filed.onchange({value:filed.getFiledValue.apply(pmObj, [item_id])}) - } - else if(filed.onchange) - { - if(pmInventories.model.itemslist.results[0]) - { - filed.onchange({value:pmInventories.model.itemslist.results[0].id}) - } - else - { - filed.onchange({value:""}) - } - } - }); -} - - - tabSignal.connect("polemarch.start", function() - { - // inventories - spajs.addMenu({ - id:"inventories", - urlregexp:[/^inventories$/, /^inventory$/, /^inventories\/search\/?$/, /^inventories\/page\/([0-9]+)$/], - onOpen:function(holder, menuInfo, data){return pmInventories.showList(holder, menuInfo, data);} - }) - - spajs.addMenu({ - id:"inventories-import", - urlregexp:[/^inventories\/import$/], - onOpen:function(holder, menuInfo, data){return pmInventories.showImportPage(holder, menuInfo, data);} - }) - - - spajs.addMenu({ - id:"inventories-search", - urlregexp:[/^inventories\/search\/([A-z0-9 %\-.:,=]+)$/], - onOpen:function(holder, menuInfo, data){return pmInventories.showSearchResults(holder, menuInfo, data);} - }) - - spajs.addMenu({ - id:"inventory", - urlregexp:[/^inventory\/([0-9]+)$/, /^inventories\/([0-9]+)$/], - onOpen:function(holder, menuInfo, data){return pmInventories.showItem(holder, menuInfo, data);} - }) - - spajs.addMenu({ - id:"inventory-history", - urlregexp:[/^inventory\/([0-9]+)\/history$/, /^inventory\/([0-9]+)\/history\/page\/([0-9]+)$/], - onOpen:function(holder, menuInfo, data){return pmHistory.showListInInventory(holder, menuInfo, data);} - }) - - spajs.addMenu({ - id:"newInventory", - urlregexp:[/^new-inventory$/, /^([A-z0-9_]+)\/([0-9]+)\/new-inventory$/], - onOpen:function(holder, menuInfo, data){return pmInventories.showNewItemPage(holder, menuInfo, data);} - }) + + +var pmInventories = inheritance(pmItems) +pmInventories.model.name = "inventories" +pmInventories.model.page_name = "inventory" +pmInventories.model.bulk_name = "inventory" +pmInventories.model.className = "pmInventories" + +/** + * Параметры из секции *:vars + * Строка где после первого `=` всё остальное значение. + */ +pmInventories.parseMonoVarsLine = function(index, line) +{ + var vars = {} + var param = /^([^=]+)="(.*)"$/.exec(line) + + if(param) + { + vars[param[1]] = param[2] + } + else + { + param = /^([^=]+)=(.*)$/.exec(line) + if(param) + { + vars[param[1]] = param[2] + } + else + { + throw "Error in line "+index+" invalid varibles string ("+line+")" + } + } + return vars; +} + +/** + * Параметры хоста + * Строка где может быть несколько параметров ключ=значение через пробел + */ +pmInventories.parseVarsLine = function(index, line) +{ + var vars = {} + do{ + if(line.length == 0) + { + break; + } + + var params = /^([^=]+)=["'](.*?)["'] +[^=]+=/.exec(line) + if(params) + { + params[1] = trim(params[1]) + vars[params[1]] = stripslashes(params[2]) + line = trim(line.slice(params[1].length + params[2].length + 3)) + continue; + } + + params = /^([^=]+)=([^ ]*) +[^=]+=/.exec(line) + if(params) + { + params[1] = trim(params[1]) + vars[params[1]] = stripslashes(params[2]) + line = trim(line.slice(params[1].length + params[2].length + 1)) + continue; + } + + params = /^([^=]+)=["'](.*?)["'] *$/.exec(line) + if(params) + { + params[1] = trim(params[1]) + vars[params[1]] = stripslashes(params[2]) + break; + } + + params = /^([^=]+)=([^ ]*) *$/.exec(line) + if(params) + { + params[1] = trim(params[1]) + vars[params[1]] = stripslashes(params[2]) + line = trim(line.slice(params[1].length + params[2].length + 1)) + continue; + } + else + { + throw "Error in line "+index+" invalid varibles string ("+line+")" + } + }while(true) + return vars; +} + +/** + * Хост и параметры + * Строка где идёт сначала имя хоста а потом его параметры в `parseVarsLine` + */ +pmInventories.parseHostLine = function(index, line, section, inventory) +{ + var params = /^([^ ]+)/.exec(line) + if(!params) + { + throw "Error in line "+index+" ("+line+")" + } + + var name = "" + var type = "" + if(pmItems.validateHostName(params[1])) + { + name = params[1] + type = "HOST" + } + else if(pmItems.validateRangeName(params[1])) + { + name = params[1] + type = "RANGE" + } + else + { + throw "Error in line "+index+" invalid host or range name ("+params[1]+")" + } + + line = trim(line.slice(name.length)) + + var host = { + name:name, + type:type, + vars:pmInventories.parseVarsLine(index, line) + } + + if(section !== "_hosts") + { + pmInventories.addGroupIfNotExists(inventory, section) + inventory.groups[section].hosts.push(host) + } + else + { + inventory.hosts.push(host) + } +} + +/** + * Парсит строку файла инвентория + * @param {integer} index + * @param {string} line + * @param {string} section + * @param {Object} inventory + * @returns {Boolean} + */ +pmInventories.parseLine = function(index, line, section, inventory) +{ + line = trim(line); + + if(section == "_hosts") + { + pmInventories.parseHostLine(index, line, section, inventory) + return true; + } + + if(section == "all:vars") + { + var vars = pmInventories.parseMonoVarsLine(index, line) + inventory.vars = Object.assign(inventory.vars, vars) + return true; + } + + if(/:vars$/.test(section)) + { + section = section.substring(0, section.length - ":vars".length) + + pmInventories.addGroupIfNotExists(inventory, section) + inventory.groups[section].vars = Object.assign(inventory.groups[section].vars, pmInventories.parseMonoVarsLine(index, line)) + return true; + } + + if(/:children$/.test(section)) + { + /** + * Параметры из секции *:children + * Строка где просто имя группы + */ + section = section.substring(0, section.length - ":children".length) + + pmInventories.addGroupIfNotExists(inventory, section) + inventory.groups[section].children = true + inventory.groups[section].groups.push(line) + pmInventories.addGroupIfNotExists(inventory, line) + return true; + } + + pmInventories.parseHostLine(index, line, section, inventory) + return false; +} + +/** + * Добавляет группу в инвенторий если её ещё нет + * @param {Object} inventory + * @param {string} group_name имя группы + * @returns {Boolean} true если группа добавлена. + */ +pmInventories.addGroupIfNotExists = function(inventory, group_name) +{ + if(!inventory.groups[group_name]) + { + inventory.groups[group_name] = { + vars:{}, + groups:[], + hosts:[], + } + + return true; + } + + return false; +} + + +/** + * Парсит файла инвентория + * @param {string} text текст файла инвентория + * @returns {Object} + */ +pmInventories.parseFromText = function(text) +{ + var lines = text.split(/\n/g) + + var cSection = "_hosts"; + var inventory = { + hosts:[], + groups:{}, + vars:{}, + name:new Date().toString() + } + + for(var i in lines) + { + var line = lines[i].replace(/^ */g, "") + + if(/^\s*[#;]\s+inventory name: (.*)/ig.test(line)) + { + var name = /^\s*[#;]\s+inventory name: (.*)/ig.exec(line) + inventory.name = name[1] + continue; + } + + if(/^\s*$/ig.test(line)) + { + continue; + } + if(/^\s*[#;]/ig.test(line)) + { + continue; + } + + console.log(i+":\t" + line) + + if(/^\[([A-z0-9\.:\-]+:vars)\]/ig.test(line)) + { + var res = /^\[([A-z0-9\.:\-]+)\]/ig.exec(line) + cSection = res[1] + + var group_name = cSection.substring(0, cSection.length - ":vars".length) + if(group_name != "all") + { + pmInventories.addGroupIfNotExists(inventory, group_name) + } + console.info("setSection:vars ", cSection) + continue; + } + if(/^\[([A-z0-9\.:\-]+:children)\]/ig.test(line)) + { + var res = /^\[([A-z0-9\.:\-]+)\]/ig.exec(line) + cSection = res[1] + pmInventories.addGroupIfNotExists(inventory, cSection.substring(0, cSection.length - ":children".length)) + console.info("setSection:children ", cSection) + continue; + } + if(/^\[([A-z0-9\.:\-]+)\]/ig.test(line)) + { + var res = /^\[([A-z0-9\.:\-]+)\]/ig.exec(line) + cSection = res[1] + pmInventories.addGroupIfNotExists(inventory, cSection) + console.info("setSection ", cSection) + continue; + } + + pmInventories.parseLine(i, line, cSection, inventory) + } + + pmInventories.addHierarchyDataToInventoryGroups(inventory) + console.log("\n\ninventory", inventory) + return inventory; +} + + + +/** + * Формирует вспомагательную информацию в объекте инвентория о вложенности групп друг в друга. + * @param {Object} inventory Инвенторий (Обязательный) + * @param {string} group_name (не обязательный) + * @param {integer} level (не обязательный) + * @param {Array} parents (не обязательный) + */ +pmInventories.addHierarchyDataToInventoryGroups = function(inventory, group_name, level, parents) +{ + if(!level) + { + level = 0 + } + + if(parents === undefined) + { + parents = [] + } + + if(group_name === undefined || group_name == 'all') + { + for(var i in inventory.groups) + { + delete inventory.groups[i]['dataLevel'] + } + + for(var i in inventory.groups) + { + pmInventories.addHierarchyDataToInventoryGroups(inventory, i, 1, ['all']) + } + + return; + } + + + if(inventory.groups[group_name].dataLevel && inventory.groups[group_name].dataLevel.level >= level ) + { + return; + } + + parents.push(group_name) + inventory.groups[group_name].dataLevel = { + level:level, + parents:parents, + } + + for(var i in inventory.groups[group_name].groups) + { + var hasError = false; + for(var j in inventory.groups[group_name].dataLevel.parents) + { + var val = inventory.groups[group_name].dataLevel.parents[j] + if(val == inventory.groups[group_name].groups[i]) + { + inventory.groups[group_name].dataLevel.error = "Group `"+val+"` is recursive include into group `"+inventory.groups[group_name].groups[i]+"`"; + console.warn(inventory.groups[group_name].dataLevel.error) + hasError = true + break; + } + } + + if(hasError) + { + continue; + } + + pmInventories.addHierarchyDataToInventoryGroups(inventory, inventory.groups[group_name].groups[i], level+1, parents.slice()) + } + + return; +} + +// ansible_ssh_private_key_file - запрашивать значение этого параметра. + +pmInventories.importFromFile = function(files_event) +{ + var def = new $.Deferred(); + this.model.files = files_event + this.model.importedInventories = {} + var thisObj = this; + for(var i=0; i + $.notify("Error in field ansible_ssh_private_key_file invalid value", "error"); + pmInventories.showInventoryVarsModal(); + def2.reject() + return def2.promise(); + } + + for(var i in inventory.hosts) + { + var val = inventory.hosts[i] + if(val.vars.ansible_ssh_private_key_file !== undefined && !/-----BEGIN RSA PRIVATE KEY-----/.test(val.vars.ansible_ssh_private_key_file)) + { + // + $.notify("Error in field ansible_ssh_private_key_file invalid value", "error"); + pmInventories.showHostVarsModal({group:'all', name:val.name}); + def2.reject() + return def2.promise(); + } + } + + for(var i in inventory.groups) + { + var val = inventory.groups[i] + if(val.vars.ansible_ssh_private_key_file !== undefined && !/-----BEGIN RSA PRIVATE KEY-----/.test(val.vars.ansible_ssh_private_key_file)) + { + // + $.notify("Error in field ansible_ssh_private_key_file invalid value", "error"); + pmInventories.showGroupVarsModal({name:i}); + def2.reject() + return def2.promise(); + } + + for(var j in val.hosts) + { + var hval = val.hosts[j] + if(hval.vars.ansible_ssh_private_key_file !== undefined && !/-----BEGIN RSA PRIVATE KEY-----/.test(hval.vars.ansible_ssh_private_key_file)) + { + // + $.notify("Error in field ansible_ssh_private_key_file invalid value", "error"); + pmInventories.showHostVarsModal({group:i, name:hval.name}); + def2.reject() + return def2.promise(); + } + } + } + + var def = new $.Deferred(); + + if($("#inventory_name").val() != "") + { + inventory.name = $("#inventory_name").val(); + } + + if(!inventory.name) + { + // inventory.name = "new imported inventory" + + $.notify("Error in field inventory name", "error"); + def2.reject({text:"Error in field inventory name"}) + return def2.promise(); + } + + var inventoryObject = { + name:inventory.name, + vars:inventory.vars + } + + var deleteBulk = [] + $.when(pmInventories.importItem(inventoryObject)).done(function(inventory_id) + { + deleteBulk.push({ + type:"del", + item:'inventory', + pk:inventory_id + }) + var bulkdata = [] + // Сбор групп и вложенных в них хостов + for(var i in inventory.groups) + { + var val = inventory.groups[i] + + bulkdata.push({ + type:"add", + item:'group', + data:{ + name:i, + children:val.children, + vars:val.vars + } + }) + + for(var j in val.hosts) + { + var hval = val.hosts[j] + bulkdata.push({ + type:"add", + item:'host', + data:{ + name:hval.name, + type:hval.type, + vars:hval.vars + } + }) + } + } + + // Сбор хостов вложенных к инвенторию + var bulkHosts = [] + for(var i in inventory.hosts) + { + var val = inventory.hosts[i] + bulkHosts.push({ + type:"add", + item:'host', + data:{ + name:val.name, + type:val.type, + vars:val.vars + } + }) + } + + // Добавление хостов вложенных к инвенторию + spajs.ajax.Call({ + url: "/api/v1/_bulk/", + type: "POST", + contentType:'application/json', + data:JSON.stringify(bulkHosts), + success: function(data) + { + var hasError = false; + var hosts_ids = [] + for(var i in data) + { + var val = data[i] + if(val.status != 201) + { + $.notify("Error "+val.status, "error"); + hasError = true; + continue; + } + hosts_ids.push(val.data.id) + deleteBulk.push({ + type:"del", + item:'host', + pk:val.data.id + }) + } + + if(hasError) + { + // По меньшей мере в одной операции была ошибка вставки. + // Инвенторий импортирован не полностью + def.reject(deleteBulk); + return; + } + + $.when(pmInventories.addSubHosts(inventory_id, hosts_ids)).done(function() + { + // Добавление групп и вложенных в них хостов + spajs.ajax.Call({ + url: "/api/v1/_bulk/", + type: "POST", + contentType:'application/json', + data:JSON.stringify(bulkdata), + success: function(data) + { + var igroups_ids = [] + var bulk_update = [] + var hasError = false; + for(var i in data) + { + deleteBulk.push({ + type:"del", + item:data.item, + pk:data[i].data.id + }) + + var val = data[i] + if(val.status != 201) + { + $.notify("Error "+val.status, "error"); + hasError = true; + } + + if(val.data.children !== undefined ) + { + igroups_ids.push(val.data.id) + + // Это группа + if(val.data.children) + { + if(inventory.groups[val.data.name].groups.length) + { + // Добавление подгрупп + var groups_ids = [] + for(var j in inventory.groups[val.data.name].groups) + { + // найти id группы и прекрепить. + for(var k in data) + { + if(data[k].data.children !== undefined && data[k].data.name == inventory.groups[val.data.name].groups[j]) + { + groups_ids.push(data[k].data.id) + break; + } + } + } + bulk_update.push({ + type: "mod", + item:'group', + method: "PUT", + data_type: 'groups', + pk:val.data.id, + data:groups_ids + }) + } + } + else + { + if(inventory.groups[val.data.name].hosts.length) + { + // Добавление хостов + var hosts_ids = [] + for(var j in inventory.groups[val.data.name].hosts) + { + // найти id группы и прекрепить. + for(var k in data) + { + if(data[k].data.children === undefined && data[k].data.name == inventory.groups[val.data.name].hosts[j].name) + { + hosts_ids.push(data[k].data.id) + break; + } + } + } + bulk_update.push({ + type: "mod", + item:'group', + method: "PUT", + data_type: 'hosts', + pk:val.data.id, + data:hosts_ids + }) + } + } + } + else + { + // Это хост + } + } + + if(hasError) + { + // По меньшей мере в одной операции была ошибка вставки. + // Инвенторий импортирован не полностью + def.reject(deleteBulk); + return; + } + + $.when(pmInventories.addSubGroups(inventory_id, igroups_ids)).done(function() + { + if(bulk_update.length) + { + spajs.ajax.Call({ + url: "/api/v1/_bulk/", + type: "POST", + contentType:'application/json', + data:JSON.stringify(bulk_update), + success: function(data) + { + var hasError = false; + for(var i in data) + { + var val = data[i] + if(val.status != 200) + { + $.notify("Error "+val.status, "error"); + hasError = true; + continue; + } + } + + if(hasError) + { + // По меньшей мере в одной операции была ошибка обновления. + // Инвенторий импортирован не полностью + def.reject(deleteBulk); + return; + } + + def.resolve(inventory_id); + }, + error:function(e) + { + console.warn(e) + polemarch.showErrors(e) + def.reject(deleteBulk); + } + }) + } + else + { + def.resolve(inventory_id); + } + }).fail(function(e){ + console.warn(e) + polemarch.showErrors(e) + def.reject(deleteBulk); + }) + }, + error:function(e) + { + console.warn(e) + polemarch.showErrors(e) + def.reject(deleteBulk); + } + }); + }).fail(function(e){ + console.warn(e) + polemarch.showErrors(e) + def.reject(deleteBulk); + }) + }, + error:function(e) + { + console.warn(e) + polemarch.showErrors(e) + def.reject(deleteBulk); + } + }) + }).fail(function(e) + { + console.warn(e) + polemarch.showErrors(e) + def.reject(deleteBulk); + }) + + $.when(def).done(function(inventory_id) + { + def2.resolve(inventory_id) + }).fail(function(delete_bulk) + { + $.when(spajs.ajax.Call({ + url: "/api/v1/_bulk/", + type: "POST", + contentType:'application/json', + data:JSON.stringify(delete_bulk), + })).always(function(){ + def2.reject() + }) + }) + + return def2.promise(); +} + + +pmInventories.showImportPage = function(holder, menuInfo, data) +{ + var def = new $.Deferred(); + + var text = spajs.just.render(this.model.name+'_import_page', {}) + $(holder).insertTpl(text) + + def.resolve() + return def.promise(); +} + +pmInventories.renderImportedInventory = function(imported) +{ + if(!imported || !imported.inventory) + { + return "" + } + + var text = spajs.just.render(this.model.name+'_imported_inventory', {inventory:imported.inventory, text:imported.text}) + return text; +} + +pmInventories.copyItem = function(item_id) +{ + var def = new $.Deferred(); + var thisObj = this; + + $.when(this.loadItem(item_id)).done(function() + { + var data = thisObj.model.items[item_id]; + delete data.id; + data.name = "copy from " + data.name + + $.when(encryptedCopyModal.replace(data)).done(function(data) + { + spajs.ajax.Call({ + url: "/api/v1/"+thisObj.model.name+"/", + type: "POST", + contentType:'application/json', + data: JSON.stringify(data), + success: function(newItem) + { + thisObj.model.items[newItem.id] = newItem + + var groups = [] + for(var i in data.groups) + { + groups.push(data.groups[i].id) + } + + var hosts = [] + for(var i in data.hosts) + { + hosts.push(data.hosts[i].id) + } + + $.when(thisObj.setSubGroups(newItem.id, groups), thisObj.setSubHosts(newItem.id, hosts)).always(function(){ + def.resolve(newItem.id) + }) + }, + error:function(e) + { + def.reject(e) + } + }); + }).fail(function(e) + { + def.reject(e) + }) + }).fail(function(e) + { + def.reject(e) + }) + + + return def.promise(); +} + + + + +pmInventories.model.page_list = { + title: "Inventories", + short_title: "Inventories", + buttons:[ + { + class:'btn btn-primary', + function:function(){ return "spajs.open({ menuId:'new-"+this.model.page_name+"'}); return false;"}, + title:'Create', + link:function(){ return '/?new-'+this.model.page_name}, + }, + { + tpl:function(){ + return spajs.just.render('inventories_btn_openImportPageAndImportFiles', {}) + }, + }, + ], + fileds:[ + { + title:'Name', + name:'name', + }, + ], + actions:[ + { + class:'btn btn-danger', + function:function(item){ return 'spajs.showLoader('+this.model.className+'.deleteItem('+item.id+')); return false;'}, + title:'Delete', + link:function(){ return '#'} + }, + { + class:'btn btn-default', + function:function(item){ return '';}, + title:'Create sub group', + link:function(item) + { + return '/?inventory/'+item.id+'/new-group' + }, + }, + { + class:'btn btn-default', + function:function(item){ return '';}, + title:'Create sub host', + link:function(item) + { + return '/?inventory/'+item.id+'/new-host' + }, + } + ] +} + +pmInventories.model.page_new = { + title: "New inventory", + short_title: "New inventory", + fileds:[ + [ + { + filed: new filedsLib.filed.text(), + title:'Name', + name:'name', + placeholder:'Enter inventory name', + validator:function(value){ + return filedsLib.validator.notEmpty(value, 'Name') + }, + fast_validator:filedsLib.validator.notEmpty + }, + ] + ], + sections:[ + function(section){ + return jsonEditor.editor({}, {block:this.model.name}); + } + ], + onBeforeSave:function(data) + { + data.vars = jsonEditor.jsonEditorGetValues() + return data; + }, + onCreate:function(data, status, xhr, callOpt) + { + var def = new $.Deferred(); + $.notify("Inventory created", "success"); + + if(callOpt.parent_item) + { + if(callOpt.parent_type == 'project') + { + $.when(pmProjects.addSubInventories(callOpt.parent_item, [data.id])).always(function(){ + $.when(spajs.open({ menuId:"project/"+callOpt.parent_item})).always(function(){ + def.resolve() + }) + }) + } + } + else + { + $.when(spajs.open({ menuId: this.model.page_name + "/"+data.id})).always(function(){ + def.resolve() + }) + } + return def.promise(); + } +} + +pmInventories.model.page_item = { + buttons:[ + { + class:'btn btn-primary', + function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.updateItem('+item_id+')); return false;'}, + title:'Save', + link:function(){ return '#'}, + }, + { + class:'btn btn-primary', + function:function(item_id){ return 'return spajs.openURL(this.href);'}, + title:'History', + link:function(item_id){ return polemarch.opt.host + '/?inventory/' + item_id + '/history'}, + }, + { + class:'btn btn-default copy-btn', + function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.copyAndEdit('+item_id+')); return false;'}, + title:'', + link:function(){ return '#'}, + help:'Copy' + }, + { + class:'btn btn-danger danger-right', + function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.deleteItem('+item_id+')); return false;'}, + title:' ', + link:function(){ return '#'}, + }, + ], + sections:[ + function(section, item_id){ + return jsonEditor.editor(this.model.items[item_id].vars, {block:this.model.name}); + }, + function(section, item_id){ + return spajs.just.render("inventories_sub_items", {item_id:item_id}) + } + ], + title: function(item_id){ + return "Inventory "+this.model.items[item_id].justText('name') + }, + short_title: function(item_id){ + return "Inventory "+this.model.items[item_id].justText('name', function(v){return v.slice(0, 20)}) + }, + fileds:[ + [ + { + filed: new filedsLib.filed.text(), + title:'Name', + name:'name', + placeholder:'Enter inventory name', + validator:function(value){ + return filedsLib.validator.notEmpty(value, 'Name') + }, + fast_validator:function(value){ return value != '' && value} + }, + ] + ], + onUpdate:function(result) + { + return true; + }, + onBeforeSave:function(data, item_id) + { + data.vars = jsonEditor.jsonEditorGetValues() + return data; + }, +} + +/** + * Показывает форму со списком всех групп. + * @return $.Deferred + */ +pmInventories.showAddSubGroupsForm = function(item_id, holder) +{ + if(!item_id) + { + throw "Error in pmInventories.showAddSubGroupsForm with item_id = `" + item_id + "`" + } + + return $.when(pmGroups.loadAllItems()).done(function(){ + $("#add_existing_item_to_inventory").remove() + $(".content").appendTpl(spajs.just.render('add_existing_groups_to_inventory', {item_id:item_id})) + var scroll_el = "#add_existing_item_to_inventory"; + if ($(scroll_el).length != 0) { + $('html, body').animate({ scrollTop: $(scroll_el).offset().top }, 1000); + } + $("#polemarch-model-items-select").select2({ width: '100%' }); + }).fail(function(){ + + }).promise() +} + +/** + * Показывает форму со списком всех хостов. + * @return $.Deferred + */ +pmInventories.showAddSubHostsForm = function(item_id, holder) +{ + if(!item_id) + { + throw "Error in pmInventories.showAddSubHostsForm with item_id = `" + item_id + "`" + } + + return $.when(pmHosts.loadAllItems()).done(function(){ + $("#add_existing_item_to_inventory").remove() + $(".content").appendTpl(spajs.just.render('add_existing_hosts_to_inventory', {item_id:item_id})) + var scroll_el = "#add_existing_item_to_inventory"; + if ($(scroll_el).length != 0) { + $('html, body').animate({ scrollTop: $(scroll_el).offset().top }, 1000); + } + $("#polemarch-model-items-select").select2({ width: '100%' }); + }).fail(function(){ + + }).promise() +} + +/** + * Проверяет принадлежит ли host_id к группе item_id + * @param {Integer} item_id + * @param {Integer} host_id + * @returns {Boolean} + */ +pmInventories.hasHosts = function(item_id, host_id) +{ + if(!item_id) + { + throw "Error in pmInventories.hasHosts with item_id = `" + item_id + "`" + } + + if(pmInventories.model.items[item_id]) + { + for(var i in pmInventories.model.items[item_id].hosts) + { + if(pmInventories.model.items[item_id].hosts[i].id == host_id) + { + return true; + } + } + } + return false; +} + +/** + * Проверяет принадлежит ли host_id к группе item_id + * @param {Integer} item_id + * @param {Integer} host_id + * @returns {Boolean} + */ +pmInventories.hasGroups = function(item_id, group_id) +{ + if(!item_id) + { + throw "Error in pmInventories.hasGroups with item_id = `" + item_id + "`" + } + + if(pmInventories.model.items[item_id]) + { + for(var i in pmInventories.model.items[item_id].groups) + { + if(pmInventories.model.items[item_id].groups[i].id == group_id) + { + return true; + } + } + } + return false; +} + + +/** + * @return $.Deferred + */ +pmInventories.setSubGroups = function(item_id, groups_ids) +{ + if(!item_id) + { + throw "Error in pmInventories.setSubGroups with item_id = `" + item_id + "`" + } + + if(!groups_ids) + { + groups_ids = [] + } + + return spajs.ajax.Call({ + url: "/api/v1/inventories/"+item_id+"/groups/", + type: "PUT", + contentType:'application/json', + data:JSON.stringify(groups_ids), + success: function(data) + { + if(pmInventories.model.items[item_id]) + { + pmInventories.model.items[item_id].groups = [] + for(var i in groups_ids) + { + pmInventories.model.items[item_id].groups.push(pmGroups.model.items[groups_ids[i]]) + } + } + }, + error:function(e) + { + console.warn("group "+item_id+" update error - " + JSON.stringify(e)); + polemarch.showErrors(e.responseJSON) + } + }); +} + +/** + * @return $.Deferred + */ +pmInventories.setSubHosts = function(item_id, hosts_ids) +{ + if(!hosts_ids) + { + hosts_ids = [] + } + + if(!item_id) + { + throw "Error in pmInventories.setSubHosts with item_id = `" + item_id + "`" + } + + + return spajs.ajax.Call({ + url: "/api/v1/inventories/"+item_id+"/hosts/", + type: "PUT", + contentType:'application/json', + data:JSON.stringify(hosts_ids), + success: function(data) + { + if(pmInventories.model.items[item_id]) + { + pmInventories.model.items[item_id].hosts = [] + for(var i in hosts_ids) + { + pmInventories.model.items[item_id].hosts.push(pmHosts.model.items[hosts_ids[i]]) + } + } + }, + error:function(e) + { + console.warn("group "+item_id+" update error - " + JSON.stringify(e)); + polemarch.showErrors(e.responseJSON) + } + }); +} + +/** + * @return $.Deferred + */ +pmInventories.addSubGroups = function(item_id, groups_ids) +{ + if(!item_id) + { + throw "Error in pmInventories.addSubGroups with item_id = `" + item_id + "`" + } + + if(!groups_ids) + { + groups_ids = [] + } + + var def = new $.Deferred(); + spajs.ajax.Call({ + url: "/api/v1/inventories/"+item_id+"/groups/", + type: "POST", + contentType:'application/json', + data:JSON.stringify(groups_ids), + success: function(data) + { + if(data.not_found > 0) + { + $.notify("Item not found", "error"); + def.reject({text:"Item not found", status:404}) + return; + } + + if(pmInventories.model.items[item_id]) + { + if(!pmInventories.model.items[item_id].groups) + { + pmInventories.model.items[item_id].groups = [] + } + + for(var i in groups_ids) + { + pmInventories.model.items[item_id].groups.push(pmGroups.model.items[groups_ids[i]]) + } + } + + $.notify("Save", "success"); + def.resolve() + }, + error:function(e) + { + console.warn("group "+item_id+" update error - " + JSON.stringify(e)); + polemarch.showErrors(e.responseJSON) + def.reject(e) + } + }); + return def.promise(); +} + +/** + * @return $.Deferred + */ +pmInventories.addSubHosts = function(item_id, hosts_ids) +{ + if(!item_id) + { + throw "Error in pmInventories.addSubHosts with item_id = `" + item_id + "`" + } + + var def = new $.Deferred(); + if(!hosts_ids || hosts_ids.length == 0) + { + def.resolve() + return def.promise(); + } + + spajs.ajax.Call({ + url: "/api/v1/inventories/"+item_id+"/hosts/", + type: "POST", + contentType:'application/json', + data:JSON.stringify(hosts_ids), + success: function(data) + { + if(data.not_found > 0) + { + $.notify("Item not found", "error"); + def.reject({text:"Item not found", status:404}) + return; + } + + if(pmInventories.model.items[item_id]) + { + if(!pmInventories.model.items[item_id].hosts) + { + pmInventories.model.items[item_id].hosts = [] + } + + for(var i in hosts_ids) + { + pmInventories.model.items[item_id].hosts.push(pmHosts.model.items[hosts_ids[i]]) + } + } + + $.notify("Save", "success"); + def.resolve() + }, + error:function(e) + { + console.warn("group "+item_id+" update error - " + JSON.stringify(e)); + polemarch.showErrors(e.responseJSON) + def.reject(e) + } + }); + return def.promise(); +} + +pmInventories.validateGroupName = function(name) +{ + if(!name) + { + return false; + } + + if(/^[a-zA-Z0-9\-\._]*$/.test(name.toLowerCase())) + { + return true; + } + + return false; +} + + + + +/** + * Для ввода инвентория + * @type Object + */ +pmInventories.filed.inventoriesAutocomplete = inheritance(filedsLib.filed.simpleText) +pmInventories.filed.inventoriesAutocomplete.type = 'inventoriesAutocomplete' +pmInventories.filed.inventoriesAutocomplete.getValue = function(pmObj, filed) +{ + var inventory = $("#inventories-autocomplete").val() + if($("#inventory-source").val() != 'db') + { + inventory = $("#inventories-file").val() + if(!/^\.\//.test(inventory)) + { + inventory = trim("./"+inventory) + } + } + + + return inventory; +} + +/** + * Функция для рендера поля + * @type Object + */ +pmInventories.filed.inventoriesAutocomplete.render = function(pmObj, filed, item_id, opt) +{ + var html = spajs.just.render('filed_type_'+this.type, {pmObj:pmObj, filed:filed, item_id:item_id, filedObj:this, opt:opt}) + return spajs.just.onInsert(html, function() + { + // @FixMe требует чтоб были загружены все инвентории pmInventories.loadAllItems() + $("#inventories-autocomplete").select2({ width: '100%' }); + + if(filed.onchange && item_id) + { + filed.onchange({value:filed.getFiledValue.apply(pmObj, [item_id])}) + } + else if(filed.onchange) + { + if(pmInventories.model.itemslist.results[0]) + { + filed.onchange({value:pmInventories.model.itemslist.results[0].id}) + } + else + { + filed.onchange({value:""}) + } + } + }); +} + + + tabSignal.connect("polemarch.start", function() + { + // inventories + spajs.addMenu({ + id:"inventories", + urlregexp:[/^inventories$/, /^inventory$/, /^inventories\/search\/?$/, /^inventories\/page\/([0-9]+)$/], + onOpen:function(holder, menuInfo, data){return pmInventories.showList(holder, menuInfo, data);} + }) + + spajs.addMenu({ + id:"inventories-import", + urlregexp:[/^inventories\/import$/], + onOpen:function(holder, menuInfo, data){return pmInventories.showImportPage(holder, menuInfo, data);} + }) + + + spajs.addMenu({ + id:"inventories-search", + urlregexp:[/^inventories\/search\/([A-z0-9 %\-.:,=]+)$/], + onOpen:function(holder, menuInfo, data){return pmInventories.showSearchResults(holder, menuInfo, data);} + }) + + spajs.addMenu({ + id:"inventory", + urlregexp:[/^inventory\/([0-9]+)$/, /^inventories\/([0-9]+)$/], + onOpen:function(holder, menuInfo, data){return pmInventories.showItem(holder, menuInfo, data);} + }) + + spajs.addMenu({ + id:"inventory-history", + urlregexp:[/^inventory\/([0-9]+)\/history$/, /^inventory\/([0-9]+)\/history\/page\/([0-9]+)$/], + onOpen:function(holder, menuInfo, data){return pmHistory.showListInInventory(holder, menuInfo, data);} + }) + + spajs.addMenu({ + id:"newInventory", + urlregexp:[/^new-inventory$/, /^([A-z0-9_]+)\/([0-9]+)\/new-inventory$/], + onOpen:function(holder, menuInfo, data){return pmInventories.showNewItemPage(holder, menuInfo, data);} + }) }) \ No newline at end of file From b592d2e4d0ca4835a5e8abae56a60db63c5725d2 Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 30 Jan 2018 14:11:28 +1000 Subject: [PATCH 08/52] Scroll to widget in Free Polemarch was added. cloud/polemarchplus#73 --- polemarch/static/js/pmGroups.js | 1598 +++++++++++++------------- polemarch/static/js/pmInventories.js | 2 +- 2 files changed, 804 insertions(+), 796 deletions(-) diff --git a/polemarch/static/js/pmGroups.js b/polemarch/static/js/pmGroups.js index 59bbacf6..6bd4d47e 100644 --- a/polemarch/static/js/pmGroups.js +++ b/polemarch/static/js/pmGroups.js @@ -1,796 +1,804 @@ - -var pmGroups = inheritance(pmItems) -pmGroups.model.name = "groups" -pmGroups.model.page_name = "group" -pmGroups.model.bulk_name = "group" -pmGroups.model.className = "pmGroups" - -pmGroups.copyItem = function(item_id) -{ - var def = new $.Deferred(); - var thisObj = this; - - $.when(this.loadItem(item_id)).done(function() - { - var data = thisObj.model.items[item_id]; - delete data.id; - data.name = "copy-from-" + data.name - $.when(encryptedCopyModal.replace(data)).done(function(data) - { - spajs.ajax.Call({ - url: "/api/v1/"+thisObj.model.name+"/", - type: "POST", - contentType:'application/json', - data: JSON.stringify(data), - success: function(newItem) - { - thisObj.model.items[newItem.id] = newItem - - if(data.children) - { - var groups = [] - for(var i in data.groups) - { - groups.push(data.groups[i].id) - } - $.when(thisObj.setSubGroups(newItem.id, groups)).always(function(){ - def.resolve(newItem.id) - }) - } - else - { - var hosts = [] - for(var i in data.hosts) - { - hosts.push(data.hosts[i].id) - } - - $.when(thisObj.setSubHosts(newItem.id, hosts)).always(function(){ - def.resolve(newItem.id) - }) - } - }, - error:function(e) - { - def.reject(e) - } - }); - }).fail(function(e) - { - def.reject(e) - }) - }).fail(function(e) - { - def.reject(e) - }) - - return def.promise(); -} - - -pmGroups.model.page_list = { - buttons:[ - { - class:'btn btn-primary', - function:function(){ return "spajs.open({ menuId:'new-"+this.model.page_name+"'}); return false;"}, - title:'Create', - link:function(){ return '/?new-'+this.model.page_name}, - }, - ], - title: "Groups", - short_title: "Groups", - fileds:[ - { - title:'Name', - name:'name', - }, - ], - actions:[ - { - class:'btn btn-danger', - function:function(item){ return 'spajs.showLoader('+this.model.className+'.deleteItem('+item.id+')); return false;'}, - title:'Delete', - link:function(){ return '#'} - }, - { - class:'btn btn-default', - function:function(item){ return '';}, - title:function(item) - { - if(item.children) - { - return 'Create sub group' - } - - return 'Create sub host' - }, - link:function(item) - { - if(item.children) - { - return '/?group/'+item.id+'/new-group' - } - - return '/?group/'+item.id+'/new-host' - }, - }, - ] -} - -pmGroups.validator = function(value) -{ - if(value && !/[^A-z0-9_.\-]/.test(value)) - { - return true; - } - $.notify("Invalid value in field name it mast be as [^A-z0-9_.\-]", "error"); - return false; -} - -pmGroups.fast_validator = function(value) -{ - return /[^A-z0-9_.\-]/.test(value) -} - -pmGroups.model.page_new = { - title: "New group", - short_title: "New group", - fileds:[ - [ - { - filed: new filedsLib.filed.text(), - title:'Name', - name:'name', - placeholder:'Enter group name', - validator:pmGroups.validator, - fast_validator:pmGroups.fast_validator - }, - { - filed: new filedsLib.filed.boolean(), - title:'Children', - name:'children', - help:'If turn, then allow adding sub groups to group' - }, - ] - ], - sections:[ - function(section){ - return jsonEditor.editor({}, {block:this.model.name}); - } - ], - onBeforeSave:function(data) - { - data.vars = jsonEditor.jsonEditorGetValues() - return data; - }, - onCreate:function(data, status, xhr, callOpt) - { - var def = new $.Deferred(); - $.notify("Group created", "success"); - - if(callOpt.parent_item) - { - if(callOpt.parent_type == 'group') - { - $.when(pmGroups.addSubGroups(callOpt.parent_item, [data.id])).always(function(){ - $.when(spajs.open({ menuId:"group/"+callOpt.parent_item})).always(function(){ - def.resolve() - }) - }) - } - else if(callOpt.parent_type == 'inventory') - { - $.when(pmInventories.addSubGroups(callOpt.parent_item, [data.id])).always(function(){ - $.when(spajs.open({ menuId:"inventory/"+callOpt.parent_item})).always(function(){ - def.resolve() - }) - }) - } - else if(callOpt.parent_type == 'project') - { - $.when(pmProjects.addSubGroups(callOpt.parent_item, [data.id])).always(function(){ - $.when(spajs.open({ menuId:"project/"+callOpt.parent_item})).always(function(){ - def.resolve() - }) - }) - } - else - { - console.error("Не известный parent_type", callOpt.parent_type) - $.when(spajs.open({ menuId:"group/"+data.id})).always(function(){ - def.resolve() - }) - } - } - else - { - $.when(spajs.open({ menuId:"group/"+data.id})).always(function(){ - def.resolve() - }) - } - return def.promise(); - } -} - -pmGroups.model.page_item = { - buttons:[ - { - class:'btn btn-primary', - function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.updateItem('+item_id+')); return false;'}, - title:'Save', - link:function(){ return '#'}, - }, - { - class:'btn btn-default copy-btn', - function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.copyAndEdit('+item_id+')); return false;'}, - title:'', - link:function(){ return '#'}, - help:'Copy' - }, - { - class:'btn btn-danger danger-right', - function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.deleteItem('+item_id+')); return false;'}, - title:' ', - link:function(){ return '#'}, - }, - ], - sections:[ - function(section, item_id){ - return jsonEditor.editor(this.model.items[item_id].vars, {block:this.model.name}); - }, - function(section, item_id){ - return spajs.just.render("groups_sub_items", {item_id:item_id}) - } - ], - title: function(item_id){ - return "Group "+pmGroups.model.items[item_id].justText('name') - }, - short_title: function(item_id){ - return ""+pmGroups.model.items[item_id].justText('name', function(v){return v.slice(0, 20)}) - }, - fileds:[ - [ - { - filed: new filedsLib.filed.text(), - title:'Name', - name:'name', - placeholder:'Enter group name', - validator:pmGroups.validator, - fast_validator:pmGroups.fast_validator - }, - ] - ], - onUpdate:function(result) - { - return true; - }, - onBeforeSave:function(data, item_id) - { - data.vars = jsonEditor.jsonEditorGetValues() - return data; - }, -} - -/** - * @return $.Deferred - */ -pmGroups.setSubGroups = function(item_id, groups_ids) -{ - if(!item_id) - { - throw "Error in pmGroups.setSubGroups with item_id = `" + item_id + "`" - } - - if(!groups_ids) - { - groups_ids = [] - } - - return spajs.ajax.Call({ - url: "/api/v1/groups/"+item_id+"/groups/", - type: "PUT", - contentType:'application/json', - data:JSON.stringify(groups_ids), - success: function(data) - { - if(pmGroups.model.items[item_id]) - { - pmGroups.model.items[item_id].groups = [] - for(var i in groups_ids) - { - if(!pmGroups.model.items[groups_ids[i]]) - { - continue; - } - pmGroups.model.items[item_id].groups.push(pmGroups.model.items[groups_ids[i]]) - } - } - //console.log("group update", data); - }, - error:function(e) - { - console.warn("group "+item_id+" update error - " + JSON.stringify(e)); - polemarch.showErrors(e.responseJSON) - } - }); -} - -/** - * @return $.Deferred - */ -pmGroups.setSubHosts = function(item_id, hosts_ids) -{ - if(!item_id) - { - throw "Error in pmGroups.setSubHosts with item_id = `" + item_id + "`" - } - - if(!hosts_ids) - { - hosts_ids = [] - } - - return spajs.ajax.Call({ - url: "/api/v1/groups/"+item_id+"/hosts/", - type: "PUT", - contentType:'application/json', - data:JSON.stringify(hosts_ids), - success: function(data) - { - if(pmGroups.model.items[item_id]) - { - pmGroups.model.items[item_id].hosts = [] - for(var i in hosts_ids) - { - if(!pmHosts.model.items[hosts_ids[i]]) - { - continue; - } - pmGroups.model.items[item_id].hosts.push(pmHosts.model.items[hosts_ids[i]]) - } - } - //console.log("group update", data); - }, - error:function(e) - { - console.warn("group "+item_id+" update error - " + JSON.stringify(e)); - polemarch.showErrors(e.responseJSON) - } - }); -} - -/** - * @return $.Deferred - */ -pmGroups.addSubGroups = function(item_id, groups_ids) -{ - if(!item_id) - { - throw "Error in pmGroups.addSubGroups with item_id = `" + item_id + "`" - } - - if(!groups_ids) - { - groups_ids = [] - } - - var def = new $.Deferred(); - spajs.ajax.Call({ - url: "/api/v1/groups/"+item_id+"/groups/", - type: "POST", - contentType:'application/json', - data:JSON.stringify(groups_ids), - success: function(data) - { - if(data.not_found > 0) - { - $.notify("Item not found", "error"); - def.reject({text:"Item not found", status:404}) - return; - } - - if(pmGroups.model.items[item_id]) - { - if(!pmGroups.model.items[item_id].groups) - { - pmGroups.model.items[item_id].groups = [] - } - - for(var i in groups_ids) - { - pmGroups.model.items[item_id].groups.push(pmGroups.model.items[groups_ids[i]]) - } - } - //console.log("group update", data); - $.notify("Save", "success"); - def.resolve() - }, - error:function(e) - { - console.warn("group "+item_id+" update error - " + JSON.stringify(e)); - polemarch.showErrors(e.responseJSON) - def.reject(e) - } - }); - return def.promise(); -} - -/** - * @return $.Deferred - */ -pmGroups.addSubHosts = function(item_id, hosts_ids) -{ - if(!item_id) - { - throw "Error in pmGroups.addSubHosts with item_id = `" + item_id + "`" - } - - if(!hosts_ids) - { - hosts_ids = [] - } - - var def = new $.Deferred(); - spajs.ajax.Call({ - url: "/api/v1/groups/"+item_id+"/hosts/", - type: "POST", - contentType:'application/json', - data:JSON.stringify(hosts_ids), - success: function(data) - { - if(data.not_found > 0) - { - $.notify("Item not found", "error"); - def.reject({text:"Item not found", status:404}) - return; - } - - if(pmGroups.model.items[item_id]) - { - if(!pmGroups.model.items[item_id].hosts) - { - pmGroups.model.items[item_id].hosts = [] - } - - for(var i in hosts_ids) - { - pmGroups.model.items[item_id].hosts.push(pmHosts.model.items[hosts_ids[i]]) - } - } - //console.log("group update", data); - $.notify("Save", "success"); - def.resolve() - }, - error:function(e) - { - console.warn("group "+item_id+" update error - " + JSON.stringify(e)); - polemarch.showErrors(e.responseJSON) - def.reject(e) - } - }); - return def.promise(); -} - -/** - * Показывает форму со списком всех групп. - * @return $.Deferred - */ -pmGroups.showAddSubGroupsForm = function(item_id) -{ - if(!item_id) - { - throw "Error in pmGroups.showAddSubGroupsForm with item_id = `" + item_id + "`" - } - - return $.when(pmGroups.loadAllItems()).done(function(){ - $("#add_existing_item_to_group").remove() - $(".content").appendTpl(spajs.just.render('add_existing_groups_to_group', {item_id:item_id})) - $("#polemarch-model-items-select").select2({ width: '100%' }); - }).fail(function(){ - - }).promise() -} - -/** - * Показывает форму со списком всех хостов. - * @return $.Deferred - */ -pmGroups.showAddSubHostsForm = function(item_id) -{ - if(!item_id) - { - throw "Error in pmGroups.showAddSubHostsForm with item_id = `" + item_id + "`" - } - - return $.when(pmHosts.loadAllItems()).done(function(){ - $("#add_existing_item_to_group").remove() - $(".content").appendTpl(spajs.just.render('add_existing_hosts_to_group', {item_id:item_id})) - $("#polemarch-model-items-select").select2({ width: '100%' }); - }).fail(function(){ - - }).promise() -} - -/** - * Проверяет принадлежит ли host_id к группе item_id - * @param {Integer} item_id - * @param {Integer} host_id - * @returns {Boolean} - */ -pmGroups.hasHosts = function(item_id, host_id) -{ - if(!item_id) - { - throw "Error in pmGroups.hasHosts with item_id = `" + item_id + "`" - } - - if(pmGroups.model.items[item_id]) - { - for(var i in pmGroups.model.items[item_id].hosts) - { - if(pmGroups.model.items[item_id].hosts[i].id == host_id) - { - return true; - } - } - } - return false; -} - -/** - * Проверяет принадлежит ли host_id к группе item_id - * @param {Integer} item_id - * @param {Integer} host_id - * @returns {Boolean} - */ -pmGroups.hasGroups = function(item_id, group_id) -{ - if(!item_id) - { - throw "Error in pmGroups.hasGroups with item_id = `" + item_id + "`" - } - - if(pmGroups.model.items[item_id]) - { - for(var i in pmGroups.model.items[item_id].groups) - { - if(pmGroups.model.items[item_id].groups[i].id == group_id) - { - return true; - } - } - } - return false; -} - - - -/** - * Значение поля автокоплита для строки групп - * @see https://ansible-tips-and-tricks.readthedocs.io/en/latest/ansible/commands/#limit-to-one-or-more-hosts - * @param {string} prefix - * @returns {string} Значение поля автокоплита для строки групп - */ -pmGroups.getGroupsAutocompleteValue = function(prefix) -{ - if(!prefix) - { - prefix = "prefix" - } - return $('#groups_autocomplete_filed'+prefix).val() -} - -/** - * Поле автокоплита для строки групп - * @see https://ansible-tips-and-tricks.readthedocs.io/en/latest/ansible/commands/#limit-to-one-or-more-hosts - * @param {integer} inventory_id - * @param {string} value - * @param {string} prefix - * @returns {string} HTML шаблон - */ -pmGroups.groupsAutocompleteTemplate = function(inventory_id, value, prefix) -{ - if(value === undefined) - { - value = "all" - } - - if(!prefix) - { - prefix = "prefix" - } - - if(inventory_id) - { - $.when(pmInventories.loadItem(inventory_id)).done(function() - { - new autoComplete({ - selector: '#groups_autocomplete_filed'+prefix, - minChars: 0, - cache:false, - showByClick:true, - menuClass:'groups_autocomplete_filed'+prefix, - renderItem: function(item, search) - { - var text = item.name - if(item.isHost) - { - text += ' (Host)' - } - else - { - text += ' (Group)' - } - - return '
    ' + text + '
    '; - }, - onSelect: function(event, term, item) - { - //console.log('onSelect', term, item); - var value = $(item).attr('data-value'); - $("#groups_autocomplete_filed"+prefix).val(value); - $("#groups_autocomplete_filed"+prefix).attr({'data-hide':'hide'}); - - }, - source: function(original_term, response) - { - var isHide = $("#groups_autocomplete_filed"+prefix).attr('data-hide') - if(isHide == "hide") - { - $("#groups_autocomplete_filed"+prefix).attr({'data-hide':'show'}) - return; - } - - pmGroups.groupsAutocompleteMatcher(original_term, response, inventory_id) - } - }); - }) - } - - return spajs.just.render('groups_autocomplete_filed', {selectedInventory:inventory_id, value:value, prefix:prefix}) -} - -pmGroups.groupsAutocompleteMatcher = function(original_term, response, inventory_id) -{ - var addTermToMatches = false - var term = original_term - var baseTerm = "" - if(original_term.indexOf(':') >= 0) - { - addTermToMatches = true - term = original_term.replace(/^(.*):([^:]*)$/gim, "$2") - baseTerm = original_term.replace(/^(.*):([^:]*)$/gim, "$1:") - } - - if(term[0] == '!') - { - term = term.substring(1) - baseTerm = baseTerm + "!" - } - - var arrUsedItems = original_term.toLowerCase().replace(/!/gmi, "").split(/:/g) - - term = term.toLowerCase(); - - var matches = [] - var matchesAll = [] - - for(var i in pmInventories.model.items[inventory_id].groups) - { - var val = pmInventories.model.items[inventory_id].groups[i] - - if($.inArray(val.name.toLowerCase(), arrUsedItems) != -1) - { - continue; - } - - var text = val.name - if(addTermToMatches) - { - text = baseTerm+text - } - - if(val.name.toLowerCase().indexOf(term) == 0 ) - { - matches.push({ - value:text, - isHost:false, - name:val.name, - }) - } - - matchesAll.push({ - value:text, - isHost:false, - name:val.name, - }) - } - - for(var i in pmInventories.model.items[inventory_id].hosts) - { - var val = pmInventories.model.items[inventory_id].hosts[i] - if($.inArray(val.name.toLowerCase(), arrUsedItems) != -1) - { - continue; - } - - if(val.name.indexOf(":") != -1) - { - continue; - } - - var text = val.name - if(addTermToMatches) - { - text = baseTerm+text - } - - if(val.name.toLowerCase().indexOf(term) == 0 ) - { - matches.push({ - value:text, - isHost:true, - name:val.name, - }) - } - - matchesAll.push({ - value:text, - isHost:true, - name:val.name, - }) - } - - if(!addTermToMatches && "All".toLowerCase().indexOf(term) != -1 ) - { - matches.push({ - value:"all", - isHost:false, - name:"all", - }) - - matchesAll.push({ - value:"all", - isHost:false, - name:"all", - }) - } - - if(matches.length > 1 || addTermToMatches) - { - response(matches); - } - else - { - response(matchesAll) - } -} - -tabSignal.connect("polemarch.start", function() -{ - // groups - spajs.addMenu({ - id:"groups", - urlregexp:[/^groups$/, /^group$/, /^groups\/search\/?$/, /^groups\/page\/([0-9]+)$/], - onOpen:function(holder, menuInfo, data){return pmGroups.showList(holder, menuInfo, data);} - }) - - spajs.addMenu({ - id:"groups-search", - urlregexp:[/^groups\/search\/([A-z0-9 %\-.:,=]+)$/], - onOpen:function(holder, menuInfo, data){return pmGroups.showSearchResults(holder, menuInfo, data);} - }) - - spajs.addMenu({ - id:"group", - urlregexp:[/^group\/([0-9]+)$/, /^groups\/([0-9]+)$/], - onOpen:function(holder, menuInfo, data){return pmGroups.showItem(holder, menuInfo, data);} - }) - - spajs.addMenu({ - id:"newGroup", - urlregexp:[/^new-group$/, /^([A-z0-9_]+)\/([0-9]+)\/new-group$/], - onOpen:function(holder, menuInfo, data){return pmGroups.showNewItemPage(holder, menuInfo, data);} - }) - + +var pmGroups = inheritance(pmItems) +pmGroups.model.name = "groups" +pmGroups.model.page_name = "group" +pmGroups.model.bulk_name = "group" +pmGroups.model.className = "pmGroups" + +pmGroups.copyItem = function(item_id) +{ + var def = new $.Deferred(); + var thisObj = this; + + $.when(this.loadItem(item_id)).done(function() + { + var data = thisObj.model.items[item_id]; + delete data.id; + data.name = "copy-from-" + data.name + $.when(encryptedCopyModal.replace(data)).done(function(data) + { + spajs.ajax.Call({ + url: "/api/v1/"+thisObj.model.name+"/", + type: "POST", + contentType:'application/json', + data: JSON.stringify(data), + success: function(newItem) + { + thisObj.model.items[newItem.id] = newItem + + if(data.children) + { + var groups = [] + for(var i in data.groups) + { + groups.push(data.groups[i].id) + } + $.when(thisObj.setSubGroups(newItem.id, groups)).always(function(){ + def.resolve(newItem.id) + }) + } + else + { + var hosts = [] + for(var i in data.hosts) + { + hosts.push(data.hosts[i].id) + } + + $.when(thisObj.setSubHosts(newItem.id, hosts)).always(function(){ + def.resolve(newItem.id) + }) + } + }, + error:function(e) + { + def.reject(e) + } + }); + }).fail(function(e) + { + def.reject(e) + }) + }).fail(function(e) + { + def.reject(e) + }) + + return def.promise(); +} + + +pmGroups.model.page_list = { + buttons:[ + { + class:'btn btn-primary', + function:function(){ return "spajs.open({ menuId:'new-"+this.model.page_name+"'}); return false;"}, + title:'Create', + link:function(){ return '/?new-'+this.model.page_name}, + }, + ], + title: "Groups", + short_title: "Groups", + fileds:[ + { + title:'Name', + name:'name', + }, + ], + actions:[ + { + class:'btn btn-danger', + function:function(item){ return 'spajs.showLoader('+this.model.className+'.deleteItem('+item.id+')); return false;'}, + title:'Delete', + link:function(){ return '#'} + }, + { + class:'btn btn-default', + function:function(item){ return '';}, + title:function(item) + { + if(item.children) + { + return 'Create sub group' + } + + return 'Create sub host' + }, + link:function(item) + { + if(item.children) + { + return '/?group/'+item.id+'/new-group' + } + + return '/?group/'+item.id+'/new-host' + }, + }, + ] +} + +pmGroups.validator = function(value) +{ + if(value && !/[^A-z0-9_.\-]/.test(value)) + { + return true; + } + $.notify("Invalid value in field name it mast be as [^A-z0-9_.\-]", "error"); + return false; +} + +pmGroups.fast_validator = function(value) +{ + return /[^A-z0-9_.\-]/.test(value) +} + +pmGroups.model.page_new = { + title: "New group", + short_title: "New group", + fileds:[ + [ + { + filed: new filedsLib.filed.text(), + title:'Name', + name:'name', + placeholder:'Enter group name', + validator:pmGroups.validator, + fast_validator:pmGroups.fast_validator + }, + { + filed: new filedsLib.filed.boolean(), + title:'Children', + name:'children', + help:'If turn, then allow adding sub groups to group' + }, + ] + ], + sections:[ + function(section){ + return jsonEditor.editor({}, {block:this.model.name}); + } + ], + onBeforeSave:function(data) + { + data.vars = jsonEditor.jsonEditorGetValues() + return data; + }, + onCreate:function(data, status, xhr, callOpt) + { + var def = new $.Deferred(); + $.notify("Group created", "success"); + + if(callOpt.parent_item) + { + if(callOpt.parent_type == 'group') + { + $.when(pmGroups.addSubGroups(callOpt.parent_item, [data.id])).always(function(){ + $.when(spajs.open({ menuId:"group/"+callOpt.parent_item})).always(function(){ + def.resolve() + }) + }) + } + else if(callOpt.parent_type == 'inventory') + { + $.when(pmInventories.addSubGroups(callOpt.parent_item, [data.id])).always(function(){ + $.when(spajs.open({ menuId:"inventory/"+callOpt.parent_item})).always(function(){ + def.resolve() + }) + }) + } + else if(callOpt.parent_type == 'project') + { + $.when(pmProjects.addSubGroups(callOpt.parent_item, [data.id])).always(function(){ + $.when(spajs.open({ menuId:"project/"+callOpt.parent_item})).always(function(){ + def.resolve() + }) + }) + } + else + { + console.error("Не известный parent_type", callOpt.parent_type) + $.when(spajs.open({ menuId:"group/"+data.id})).always(function(){ + def.resolve() + }) + } + } + else + { + $.when(spajs.open({ menuId:"group/"+data.id})).always(function(){ + def.resolve() + }) + } + return def.promise(); + } +} + +pmGroups.model.page_item = { + buttons:[ + { + class:'btn btn-primary', + function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.updateItem('+item_id+')); return false;'}, + title:'Save', + link:function(){ return '#'}, + }, + { + class:'btn btn-default copy-btn', + function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.copyAndEdit('+item_id+')); return false;'}, + title:'', + link:function(){ return '#'}, + help:'Copy' + }, + { + class:'btn btn-danger danger-right', + function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.deleteItem('+item_id+')); return false;'}, + title:' ', + link:function(){ return '#'}, + }, + ], + sections:[ + function(section, item_id){ + return jsonEditor.editor(this.model.items[item_id].vars, {block:this.model.name}); + }, + function(section, item_id){ + return spajs.just.render("groups_sub_items", {item_id:item_id}) + } + ], + title: function(item_id){ + return "Group "+pmGroups.model.items[item_id].justText('name') + }, + short_title: function(item_id){ + return ""+pmGroups.model.items[item_id].justText('name', function(v){return v.slice(0, 20)}) + }, + fileds:[ + [ + { + filed: new filedsLib.filed.text(), + title:'Name', + name:'name', + placeholder:'Enter group name', + validator:pmGroups.validator, + fast_validator:pmGroups.fast_validator + }, + ] + ], + onUpdate:function(result) + { + return true; + }, + onBeforeSave:function(data, item_id) + { + data.vars = jsonEditor.jsonEditorGetValues() + return data; + }, +} + +/** + * @return $.Deferred + */ +pmGroups.setSubGroups = function(item_id, groups_ids) +{ + if(!item_id) + { + throw "Error in pmGroups.setSubGroups with item_id = `" + item_id + "`" + } + + if(!groups_ids) + { + groups_ids = [] + } + + return spajs.ajax.Call({ + url: "/api/v1/groups/"+item_id+"/groups/", + type: "PUT", + contentType:'application/json', + data:JSON.stringify(groups_ids), + success: function(data) + { + if(pmGroups.model.items[item_id]) + { + pmGroups.model.items[item_id].groups = [] + for(var i in groups_ids) + { + if(!pmGroups.model.items[groups_ids[i]]) + { + continue; + } + pmGroups.model.items[item_id].groups.push(pmGroups.model.items[groups_ids[i]]) + } + } + //console.log("group update", data); + }, + error:function(e) + { + console.warn("group "+item_id+" update error - " + JSON.stringify(e)); + polemarch.showErrors(e.responseJSON) + } + }); +} + +/** + * @return $.Deferred + */ +pmGroups.setSubHosts = function(item_id, hosts_ids) +{ + if(!item_id) + { + throw "Error in pmGroups.setSubHosts with item_id = `" + item_id + "`" + } + + if(!hosts_ids) + { + hosts_ids = [] + } + + return spajs.ajax.Call({ + url: "/api/v1/groups/"+item_id+"/hosts/", + type: "PUT", + contentType:'application/json', + data:JSON.stringify(hosts_ids), + success: function(data) + { + if(pmGroups.model.items[item_id]) + { + pmGroups.model.items[item_id].hosts = [] + for(var i in hosts_ids) + { + if(!pmHosts.model.items[hosts_ids[i]]) + { + continue; + } + pmGroups.model.items[item_id].hosts.push(pmHosts.model.items[hosts_ids[i]]) + } + } + //console.log("group update", data); + }, + error:function(e) + { + console.warn("group "+item_id+" update error - " + JSON.stringify(e)); + polemarch.showErrors(e.responseJSON) + } + }); +} + +/** + * @return $.Deferred + */ +pmGroups.addSubGroups = function(item_id, groups_ids) +{ + if(!item_id) + { + throw "Error in pmGroups.addSubGroups with item_id = `" + item_id + "`" + } + + if(!groups_ids) + { + groups_ids = [] + } + + var def = new $.Deferred(); + spajs.ajax.Call({ + url: "/api/v1/groups/"+item_id+"/groups/", + type: "POST", + contentType:'application/json', + data:JSON.stringify(groups_ids), + success: function(data) + { + if(data.not_found > 0) + { + $.notify("Item not found", "error"); + def.reject({text:"Item not found", status:404}) + return; + } + + if(pmGroups.model.items[item_id]) + { + if(!pmGroups.model.items[item_id].groups) + { + pmGroups.model.items[item_id].groups = [] + } + + for(var i in groups_ids) + { + pmGroups.model.items[item_id].groups.push(pmGroups.model.items[groups_ids[i]]) + } + } + //console.log("group update", data); + $.notify("Save", "success"); + def.resolve() + }, + error:function(e) + { + console.warn("group "+item_id+" update error - " + JSON.stringify(e)); + polemarch.showErrors(e.responseJSON) + def.reject(e) + } + }); + return def.promise(); +} + +/** + * @return $.Deferred + */ +pmGroups.addSubHosts = function(item_id, hosts_ids) +{ + if(!item_id) + { + throw "Error in pmGroups.addSubHosts with item_id = `" + item_id + "`" + } + + if(!hosts_ids) + { + hosts_ids = [] + } + + var def = new $.Deferred(); + spajs.ajax.Call({ + url: "/api/v1/groups/"+item_id+"/hosts/", + type: "POST", + contentType:'application/json', + data:JSON.stringify(hosts_ids), + success: function(data) + { + if(data.not_found > 0) + { + $.notify("Item not found", "error"); + def.reject({text:"Item not found", status:404}) + return; + } + + if(pmGroups.model.items[item_id]) + { + if(!pmGroups.model.items[item_id].hosts) + { + pmGroups.model.items[item_id].hosts = [] + } + + for(var i in hosts_ids) + { + pmGroups.model.items[item_id].hosts.push(pmHosts.model.items[hosts_ids[i]]) + } + } + //console.log("group update", data); + $.notify("Save", "success"); + def.resolve() + }, + error:function(e) + { + console.warn("group "+item_id+" update error - " + JSON.stringify(e)); + polemarch.showErrors(e.responseJSON) + def.reject(e) + } + }); + return def.promise(); +} + +/** + * Показывает форму со списком всех групп. + * @return $.Deferred + */ +pmGroups.showAddSubGroupsForm = function(item_id) +{ + if(!item_id) + { + throw "Error in pmGroups.showAddSubGroupsForm with item_id = `" + item_id + "`" + } + + return $.when(pmGroups.loadAllItems()).done(function(){ + $("#add_existing_item_to_group").remove() + $(".content").appendTpl(spajs.just.render('add_existing_groups_to_group', {item_id:item_id})) + var scroll_el = "#add_existing_item_to_group"; + if ($(scroll_el).length != 0) { + $('html, body').animate({ scrollTop: $(scroll_el).offset().top }, 1000); + } + $("#polemarch-model-items-select").select2({ width: '100%' }); + }).fail(function(){ + + }).promise() +} + +/** + * Показывает форму со списком всех хостов. + * @return $.Deferred + */ +pmGroups.showAddSubHostsForm = function(item_id) +{ + if(!item_id) + { + throw "Error in pmGroups.showAddSubHostsForm with item_id = `" + item_id + "`" + } + + return $.when(pmHosts.loadAllItems()).done(function(){ + $("#add_existing_item_to_group").remove() + $(".content").appendTpl(spajs.just.render('add_existing_hosts_to_group', {item_id:item_id})) + var scroll_el = "#add_existing_item_to_group"; + if ($(scroll_el).length != 0) { + $('html, body').animate({ scrollTop: $(scroll_el).offset().top }, 1000); + } + $("#polemarch-model-items-select").select2({ width: '100%' }); + }).fail(function(){ + + }).promise() +} + +/** + * Проверяет принадлежит ли host_id к группе item_id + * @param {Integer} item_id + * @param {Integer} host_id + * @returns {Boolean} + */ +pmGroups.hasHosts = function(item_id, host_id) +{ + if(!item_id) + { + throw "Error in pmGroups.hasHosts with item_id = `" + item_id + "`" + } + + if(pmGroups.model.items[item_id]) + { + for(var i in pmGroups.model.items[item_id].hosts) + { + if(pmGroups.model.items[item_id].hosts[i].id == host_id) + { + return true; + } + } + } + return false; +} + +/** + * Проверяет принадлежит ли host_id к группе item_id + * @param {Integer} item_id + * @param {Integer} host_id + * @returns {Boolean} + */ +pmGroups.hasGroups = function(item_id, group_id) +{ + if(!item_id) + { + throw "Error in pmGroups.hasGroups with item_id = `" + item_id + "`" + } + + if(pmGroups.model.items[item_id]) + { + for(var i in pmGroups.model.items[item_id].groups) + { + if(pmGroups.model.items[item_id].groups[i].id == group_id) + { + return true; + } + } + } + return false; +} + + + +/** + * Значение поля автокоплита для строки групп + * @see https://ansible-tips-and-tricks.readthedocs.io/en/latest/ansible/commands/#limit-to-one-or-more-hosts + * @param {string} prefix + * @returns {string} Значение поля автокоплита для строки групп + */ +pmGroups.getGroupsAutocompleteValue = function(prefix) +{ + if(!prefix) + { + prefix = "prefix" + } + return $('#groups_autocomplete_filed'+prefix).val() +} + +/** + * Поле автокоплита для строки групп + * @see https://ansible-tips-and-tricks.readthedocs.io/en/latest/ansible/commands/#limit-to-one-or-more-hosts + * @param {integer} inventory_id + * @param {string} value + * @param {string} prefix + * @returns {string} HTML шаблон + */ +pmGroups.groupsAutocompleteTemplate = function(inventory_id, value, prefix) +{ + if(value === undefined) + { + value = "all" + } + + if(!prefix) + { + prefix = "prefix" + } + + if(inventory_id) + { + $.when(pmInventories.loadItem(inventory_id)).done(function() + { + new autoComplete({ + selector: '#groups_autocomplete_filed'+prefix, + minChars: 0, + cache:false, + showByClick:true, + menuClass:'groups_autocomplete_filed'+prefix, + renderItem: function(item, search) + { + var text = item.name + if(item.isHost) + { + text += ' (Host)' + } + else + { + text += ' (Group)' + } + + return '
    ' + text + '
    '; + }, + onSelect: function(event, term, item) + { + //console.log('onSelect', term, item); + var value = $(item).attr('data-value'); + $("#groups_autocomplete_filed"+prefix).val(value); + $("#groups_autocomplete_filed"+prefix).attr({'data-hide':'hide'}); + + }, + source: function(original_term, response) + { + var isHide = $("#groups_autocomplete_filed"+prefix).attr('data-hide') + if(isHide == "hide") + { + $("#groups_autocomplete_filed"+prefix).attr({'data-hide':'show'}) + return; + } + + pmGroups.groupsAutocompleteMatcher(original_term, response, inventory_id) + } + }); + }) + } + + return spajs.just.render('groups_autocomplete_filed', {selectedInventory:inventory_id, value:value, prefix:prefix}) +} + +pmGroups.groupsAutocompleteMatcher = function(original_term, response, inventory_id) +{ + var addTermToMatches = false + var term = original_term + var baseTerm = "" + if(original_term.indexOf(':') >= 0) + { + addTermToMatches = true + term = original_term.replace(/^(.*):([^:]*)$/gim, "$2") + baseTerm = original_term.replace(/^(.*):([^:]*)$/gim, "$1:") + } + + if(term[0] == '!') + { + term = term.substring(1) + baseTerm = baseTerm + "!" + } + + var arrUsedItems = original_term.toLowerCase().replace(/!/gmi, "").split(/:/g) + + term = term.toLowerCase(); + + var matches = [] + var matchesAll = [] + + for(var i in pmInventories.model.items[inventory_id].groups) + { + var val = pmInventories.model.items[inventory_id].groups[i] + + if($.inArray(val.name.toLowerCase(), arrUsedItems) != -1) + { + continue; + } + + var text = val.name + if(addTermToMatches) + { + text = baseTerm+text + } + + if(val.name.toLowerCase().indexOf(term) == 0 ) + { + matches.push({ + value:text, + isHost:false, + name:val.name, + }) + } + + matchesAll.push({ + value:text, + isHost:false, + name:val.name, + }) + } + + for(var i in pmInventories.model.items[inventory_id].hosts) + { + var val = pmInventories.model.items[inventory_id].hosts[i] + if($.inArray(val.name.toLowerCase(), arrUsedItems) != -1) + { + continue; + } + + if(val.name.indexOf(":") != -1) + { + continue; + } + + var text = val.name + if(addTermToMatches) + { + text = baseTerm+text + } + + if(val.name.toLowerCase().indexOf(term) == 0 ) + { + matches.push({ + value:text, + isHost:true, + name:val.name, + }) + } + + matchesAll.push({ + value:text, + isHost:true, + name:val.name, + }) + } + + if(!addTermToMatches && "All".toLowerCase().indexOf(term) != -1 ) + { + matches.push({ + value:"all", + isHost:false, + name:"all", + }) + + matchesAll.push({ + value:"all", + isHost:false, + name:"all", + }) + } + + if(matches.length > 1 || addTermToMatches) + { + response(matches); + } + else + { + response(matchesAll) + } +} + +tabSignal.connect("polemarch.start", function() +{ + // groups + spajs.addMenu({ + id:"groups", + urlregexp:[/^groups$/, /^group$/, /^groups\/search\/?$/, /^groups\/page\/([0-9]+)$/], + onOpen:function(holder, menuInfo, data){return pmGroups.showList(holder, menuInfo, data);} + }) + + spajs.addMenu({ + id:"groups-search", + urlregexp:[/^groups\/search\/([A-z0-9 %\-.:,=]+)$/], + onOpen:function(holder, menuInfo, data){return pmGroups.showSearchResults(holder, menuInfo, data);} + }) + + spajs.addMenu({ + id:"group", + urlregexp:[/^group\/([0-9]+)$/, /^groups\/([0-9]+)$/], + onOpen:function(holder, menuInfo, data){return pmGroups.showItem(holder, menuInfo, data);} + }) + + spajs.addMenu({ + id:"newGroup", + urlregexp:[/^new-group$/, /^([A-z0-9_]+)\/([0-9]+)\/new-group$/], + onOpen:function(holder, menuInfo, data){return pmGroups.showNewItemPage(holder, menuInfo, data);} + }) + }) \ No newline at end of file diff --git a/polemarch/static/js/pmInventories.js b/polemarch/static/js/pmInventories.js index 96301dc5..5af85cbe 100644 --- a/polemarch/static/js/pmInventories.js +++ b/polemarch/static/js/pmInventories.js @@ -1061,7 +1061,7 @@ pmInventories.showAddSubGroupsForm = function(item_id, holder) $("#add_existing_item_to_inventory").remove() $(".content").appendTpl(spajs.just.render('add_existing_groups_to_inventory', {item_id:item_id})) var scroll_el = "#add_existing_item_to_inventory"; - if ($(scroll_el).length != 0) { + if ($(scroll_el).length != 0) { $('html, body').animate({ scrollTop: $(scroll_el).offset().top }, 1000); } $("#polemarch-model-items-select").select2({ width: '100%' }); From de35d1584e421b10d033d65922658fd6add3d959 Mon Sep 17 00:00:00 2001 From: Sergey Klyuykov Date: Tue, 30 Jan 2018 15:49:02 +1000 Subject: [PATCH 09/52] Remove year stats from test because calendar changed every day. --- polemarch/main/tests/api.py | 1 - 1 file changed, 1 deletion(-) diff --git a/polemarch/main/tests/api.py b/polemarch/main/tests/api.py index b96efbab..1f80689c 100644 --- a/polemarch/main/tests/api.py +++ b/polemarch/main/tests/api.py @@ -384,4 +384,3 @@ def test_statistic(self): self.assertEqual(result['users'], self.get_count(User)) # Check history counts self._check_stats_history(data['day'], result['jobs']['day']) - self._check_stats_history(data['year'], result['jobs']['year']) From 5583ef6b72075cf8cbd3208fec1290f8f9a488bc Mon Sep 17 00:00:00 2001 From: Sergey Klyuykov Date: Tue, 30 Jan 2018 16:06:46 +1000 Subject: [PATCH 10/52] Fix pylint --- tox.ini | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index c08dd5bf..899b970b 100644 --- a/tox.ini +++ b/tox.ini @@ -38,7 +38,8 @@ commands = [testenv:pylint] deps = pylint==1.6.5 - pylint-django + pylint-django==0.7.2 + pylint-plugin-utils==0.2.6 django>=1.11,<2.0 -rrequirements.txt -rrequirements-git.txt From 0fdd4bd33627fa5deb97a5e5805936f0b005c04f Mon Sep 17 00:00:00 2001 From: Roman Date: Tue, 30 Jan 2018 17:43:17 +1000 Subject: [PATCH 11/52] Scroll to widget in PolemarchPlus was added[1]. cloud/polemarchplus#73 --- polemarch/static/js/pmGroups.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/polemarch/static/js/pmGroups.js b/polemarch/static/js/pmGroups.js index 6bd4d47e..539968bd 100644 --- a/polemarch/static/js/pmGroups.js +++ b/polemarch/static/js/pmGroups.js @@ -485,7 +485,7 @@ pmGroups.showAddSubGroupsForm = function(item_id) return $.when(pmGroups.loadAllItems()).done(function(){ $("#add_existing_item_to_group").remove() $(".content").appendTpl(spajs.just.render('add_existing_groups_to_group', {item_id:item_id})) - var scroll_el = "#add_existing_item_to_group"; + var scroll_el = "#add_existing_item_to_group"; if ($(scroll_el).length != 0) { $('html, body').animate({ scrollTop: $(scroll_el).offset().top }, 1000); } From 9fcb2126a8d539bfe13ce02b6f6e51b88c192a42 Mon Sep 17 00:00:00 2001 From: Roman Date: Wed, 31 Jan 2018 11:16:57 +1000 Subject: [PATCH 12/52] Bug with inventory link in history was fixed. cloud/polemarchplus#104 Validation of inventory input in form "Run playbook" was added. cloud/polemarchplus#121 --- polemarch/static/js/pmHistory.js | 40 ++- polemarch/static/js/pmTasks.js | 317 +++++++++++----------- polemarch/static/templates/pmHistory.html | 28 +- 3 files changed, 203 insertions(+), 182 deletions(-) diff --git a/polemarch/static/js/pmHistory.js b/polemarch/static/js/pmHistory.js index 28ca69ed..41ebda01 100644 --- a/polemarch/static/js/pmHistory.js +++ b/polemarch/static/js/pmHistory.js @@ -140,13 +140,20 @@ pmHistory.showItem = function(holder, menuInfo, data) pmUsers.loadItem(pmHistory.model.items[item_id].initiator); } - var promiss = pmInventories.loadItem(pmHistory.model.items[item_id].inventory); - $.when(promiss).done(function(){ - $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_page', {item_id:item_id, project_id:0})) - pmHistory.bindStdoutUpdates(item_id) - }).fail(function(){ - $.notify("", "error"); - }); + + if (pmHistory.model.items[item_id].inventory != null) { + var promiss = pmInventories.loadItem(pmHistory.model.items[item_id].inventory); + $.when(promiss).done(function () { + $(holder).insertTpl(spajs.just.render(thisObj.model.name + '_page', {item_id: item_id, project_id: 0})) + pmHistory.bindStdoutUpdates(item_id) + }).fail(function () { + $.notify("", "error"); + }); + } else { + $(holder).insertTpl(spajs.just.render(thisObj.model.name + '_page', {item_id: item_id, project_id: 0})) + pmHistory.bindStdoutUpdates(item_id) + } + }).fail(function() @@ -168,13 +175,18 @@ pmHistory.showItemInProjects = function(holder, menuInfo, data) pmUsers.loadItem(pmHistory.model.items[item_id].initiator); } - var promiss = pmInventories.loadItem(pmHistory.model.items[item_id].inventory); - $.when(promiss).done(function(){ - $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_page', {item_id:item_id, project_id:0})) - pmHistory.bindStdoutUpdates(item_id) - }).fail(function(){ - $.notify("", "error"); - }); + if (pmHistory.model.items[item_id].inventory != null) { + var promiss = pmInventories.loadItem(pmHistory.model.items[item_id].inventory); + $.when(promiss).done(function () { + $(holder).insertTpl(spajs.just.render(thisObj.model.name + '_page', {item_id: item_id, project_id: 0})) + pmHistory.bindStdoutUpdates(item_id) + }).fail(function () { + $.notify("", "error"); + }); + } else { + $(holder).insertTpl(spajs.just.render(thisObj.model.name + '_page', {item_id: item_id, project_id: 0})) + pmHistory.bindStdoutUpdates(item_id) + } $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_pageInProjects', {item_id:item_id, project_id:project_id})) pmHistory.bindStdoutUpdates(item_id) diff --git a/polemarch/static/js/pmTasks.js b/polemarch/static/js/pmTasks.js index d047fc52..2a635197 100644 --- a/polemarch/static/js/pmTasks.js +++ b/polemarch/static/js/pmTasks.js @@ -1,155 +1,162 @@ - -/** - * Список playbook из всех проектов. - * Сейчас используется для автокомплитов при выборе playbook. - * И для запуска конкретного playbook - */ -var pmTasks = inheritance(pmItems) - -pmTasks.model.name = "tasks" -pmTasks.model.className = "pmTasks" - -pmTasks.execute = function(project_id, inventory, playbook, data_vars) -{ - var def = new $.Deferred(); - if(!playbook) - { - $.notify("Playbook name is empty", "error"); - def.reject({text:"Playbook name is empty"}); - return def.promise(); - } - - if(!(project_id/1)) - { - $.notify("Invalid field `project` ", "error"); - def.reject({text:"Invalid field `project` "}); - return def.promise(); - } - - if(data_vars == undefined) - { - data_vars = jsonEditor.jsonEditorGetValues(); - } - - data_vars.playbook = playbook - data_vars.inventory = inventory - spajs.ajax.Call({ - url: "/api/v1/projects/"+project_id+"/execute-playbook/", - type: "POST", - data:JSON.stringify(data_vars), - contentType:'application/json', - success: function(data) - { - $.notify("Started", "success"); - if(data && data.history_id) - { - $.when(spajs.open({ menuId:"project/"+project_id+"/history/"+data.history_id}) ).done(function(){ - def.resolve() - }).fail(function(e){ - def.reject(e) - }) - } - else - { - def.reject({text:"No history_id", status:500}) - } - }, - error:function(e) - { - def.reject(e) - polemarch.showErrors(e.responseJSON) - } - }) - - return def.promise(); -} - -/** - * Обновляет поле модел this.model.itemslist и ложит туда список пользователей - * Обновляет поле модел this.model.items и ложит туда список инфу о пользователях по их id - */ -pmTasks.loadItems = function(limit, offset) -{ - if(!limit) - { - limit = 30; - } - - if(!offset) - { - offset = 0; - } - - var thisObj = this; - return spajs.ajax.Call({ - url: "/api/v1/"+this.model.name+"/", - type: "GET", - contentType:'application/json', - data: "limit="+encodeURIComponent(limit)+"&offset="+encodeURIComponent(offset), - success: function(data) - { - //console.log("update Items", data) - data.limit = limit - data.offset = offset - thisObj.model.itemslist = data - - for(var i in data.results) - { - data.results[i].id = data.results[i].playbook - var val = data.results[i] - thisObj.model.items.justWatch(val.id); - thisObj.model.items[val.id] = mergeDeep(thisObj.model.items[val.id], val) - } - }, - error:function(e) - { - console.warn(e) - polemarch.showErrors(e) - } - }); -} - -pmTasks.sendSearchQuery = function(query, limit, offset) -{ - if(!limit) - { - limit = 999; - } - - if(!offset) - { - offset = 0; - } - - var q = []; - for(var i in query) - { - q.push(encodeURIComponent(i)+"="+encodeURIComponent(query[i])) - } - - var thisObj = this; - return spajs.ajax.Call({ - url: "/api/v1/"+this.model.name+"/?"+q.join('&'), - type: "GET", - contentType:'application/json', - data: "limit="+encodeURIComponent(limit)+"&offset="+encodeURIComponent(offset), - success: function(data) - { - //console.log("update Items", data) - thisObj.model.itemslist = data - - for(var i in data.results) - { - data.results[i].id = data.results[i].playbook - - var val = data.results[i] - thisObj.model.items[val.id] = val - } - }, - error:function(e) - { - console.warn(e) - polemarch.showErrors(e) - } - }); -} + +/** + * Список playbook из всех проектов. + * Сейчас используется для автокомплитов при выборе playbook. + * И для запуска конкретного playbook + */ +var pmTasks = inheritance(pmItems) + +pmTasks.model.name = "tasks" +pmTasks.model.className = "pmTasks" + +pmTasks.execute = function(project_id, inventory, playbook, data_vars) +{ + var def = new $.Deferred(); + if(!playbook) + { + $.notify("Playbook name is empty", "error"); + def.reject({text:"Playbook name is empty"}); + return def.promise(); + } + + if(!(project_id/1)) + { + $.notify("Invalid field `project` ", "error"); + def.reject({text:"Invalid field `project` "}); + return def.promise(); + } + + if(inventory=="./") + { + $.notify("Inventory name is empty", "error"); + def.reject({text:"Inventory name is empty"}); + return def.promise(); + } + + if(data_vars == undefined) + { + data_vars = jsonEditor.jsonEditorGetValues(); + } + + data_vars.playbook = playbook + data_vars.inventory = inventory + spajs.ajax.Call({ + url: "/api/v1/projects/"+project_id+"/execute-playbook/", + type: "POST", + data:JSON.stringify(data_vars), + contentType:'application/json', + success: function(data) + { + $.notify("Started", "success"); + if(data && data.history_id) + { + $.when(spajs.open({ menuId:"project/"+project_id+"/history/"+data.history_id}) ).done(function(){ + def.resolve() + }).fail(function(e){ + def.reject(e) + }) + } + else + { + def.reject({text:"No history_id", status:500}) + } + }, + error:function(e) + { + def.reject(e) + polemarch.showErrors(e.responseJSON) + } + }) + + return def.promise(); +} + +/** + * Обновляет поле модел this.model.itemslist и ложит туда список пользователей + * Обновляет поле модел this.model.items и ложит туда список инфу о пользователях по их id + */ +pmTasks.loadItems = function(limit, offset) +{ + if(!limit) + { + limit = 30; + } + + if(!offset) + { + offset = 0; + } + + var thisObj = this; + return spajs.ajax.Call({ + url: "/api/v1/"+this.model.name+"/", + type: "GET", + contentType:'application/json', + data: "limit="+encodeURIComponent(limit)+"&offset="+encodeURIComponent(offset), + success: function(data) + { + //console.log("update Items", data) + data.limit = limit + data.offset = offset + thisObj.model.itemslist = data + + for(var i in data.results) + { + data.results[i].id = data.results[i].playbook + var val = data.results[i] + thisObj.model.items.justWatch(val.id); + thisObj.model.items[val.id] = mergeDeep(thisObj.model.items[val.id], val) + } + }, + error:function(e) + { + console.warn(e) + polemarch.showErrors(e) + } + }); +} + +pmTasks.sendSearchQuery = function(query, limit, offset) +{ + if(!limit) + { + limit = 999; + } + + if(!offset) + { + offset = 0; + } + + var q = []; + for(var i in query) + { + q.push(encodeURIComponent(i)+"="+encodeURIComponent(query[i])) + } + + var thisObj = this; + return spajs.ajax.Call({ + url: "/api/v1/"+this.model.name+"/?"+q.join('&'), + type: "GET", + contentType:'application/json', + data: "limit="+encodeURIComponent(limit)+"&offset="+encodeURIComponent(offset), + success: function(data) + { + //console.log("update Items", data) + thisObj.model.itemslist = data + + for(var i in data.results) + { + data.results[i].id = data.results[i].playbook + + var val = data.results[i] + thisObj.model.items[val.id] = val + } + }, + error:function(e) + { + console.warn(e) + polemarch.showErrors(e) + } + }); +} diff --git a/polemarch/static/templates/pmHistory.html b/polemarch/static/templates/pmHistory.html index 83c9d5aa..33fd31bd 100644 --- a/polemarch/static/templates/pmHistory.html +++ b/polemarch/static/templates/pmHistory.html @@ -526,19 +526,21 @@

    - + <% if(pmHistory.model.items[item_id].inventory!=null) { %> + + <% } %> <% if( pmHistory.model.items[item_id].execute_args From 25325df7bc33a6b3f1b3abbfea97b9e7125e2c43 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 1 Feb 2018 17:30:40 +1000 Subject: [PATCH 13/52] Search with pagination was added. cloud/polemarchplus#114 --- polemarch/static/js/pmGroups.js | 2 +- polemarch/static/js/pmHistory.js | 21 +- polemarch/static/js/pmHosts.js | 576 ++++----- polemarch/static/js/pmInventories.js | 2 +- polemarch/static/js/pmItems.js | 496 ++++---- polemarch/static/js/pmPeriodicTasks.js | 1432 +++++++++++------------ polemarch/static/js/pmProjects.js | 1212 +++++++++---------- polemarch/static/js/pmTasksTemplates.js | 864 +++++++------- polemarch/static/js/pmUsers.js | 546 ++++----- 9 files changed, 2584 insertions(+), 2567 deletions(-) diff --git a/polemarch/static/js/pmGroups.js b/polemarch/static/js/pmGroups.js index 539968bd..96e02c85 100644 --- a/polemarch/static/js/pmGroups.js +++ b/polemarch/static/js/pmGroups.js @@ -785,7 +785,7 @@ tabSignal.connect("polemarch.start", function() spajs.addMenu({ id:"groups-search", - urlregexp:[/^groups\/search\/([A-z0-9 %\-.:,=]+)$/], + urlregexp:[/^groups\/search\/([A-z0-9 %\-.:,=]+)$/, /^groups\/search\/([A-z0-9 %\-.:,=]+)\/page\/([0-9]+)$/], onOpen:function(holder, menuInfo, data){return pmGroups.showSearchResults(holder, menuInfo, data);} }) diff --git a/polemarch/static/js/pmHistory.js b/polemarch/static/js/pmHistory.js index 41ebda01..eedde752 100644 --- a/polemarch/static/js/pmHistory.js +++ b/polemarch/static/js/pmHistory.js @@ -28,11 +28,20 @@ pmHistory.cancelTask = function(item_id) pmHistory.showSearchResults = function(holder, menuInfo, data) { var thisObj = this; - - var search = this.searchStringToObject(decodeURIComponent(data.reg[1]), 'mode') - return $.when(this.sendSearchQuery(search)).done(function() + + var limit = this.pageSize; + + if(data.reg && data.reg[2] > 0) { - $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_list', {query:decodeURIComponent(data.reg[1])})) + offset = this.pageSize*(data.reg[2] - 1); + } else { + offset=0; + } + + var search = this.searchStringToObject(decodeURIComponent(data.reg[1]), 'mode') + return $.when(this.sendSearchQuery(search,limit,offset)).done(function() + { + $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_list', {query:decodeURIComponent(data.reg[1])})) }).fail(function() { $.notify("", "error"); @@ -685,7 +694,7 @@ pmHistory.loadLines = function(item_id, opt) spajs.addMenu({ id:"history-search", - urlregexp:[/^history\/search\/([A-z0-9 %\-.:,=]+)$/], + urlregexp:[/^history\/search\/([A-z0-9 %\-.:,=]+)$/, /^history\/search\/([A-z0-9 %\-.:,=]+)\/page\/([0-9]+)$/], onOpen:function(holder, menuInfo, data){return pmHistory.showSearchResults(holder, menuInfo, data);} }) @@ -725,7 +734,7 @@ pmHistory.loadLines = function(item_id, opt) spajs.addMenu({ id:"project-history-search", - urlregexp:[/^project\/([0-9]+)\/history\/search\/([A-z0-9 %\-.:,=]+)$/], + urlregexp:[/^project\/([0-9]+)\/history\/search\/([A-z0-9 %\-.:,=]+)$/,/^project\/([0-9]+)\/history\/search\/([A-z0-9 %\-.:,=]+)\/page\/([0-9]+)$/], onOpen:function(holder, menuInfo, data){return pmHistory.showSearchResultsInProjects(holder, menuInfo, data);} }) diff --git a/polemarch/static/js/pmHosts.js b/polemarch/static/js/pmHosts.js index 6b7d139e..086ec525 100644 --- a/polemarch/static/js/pmHosts.js +++ b/polemarch/static/js/pmHosts.js @@ -1,289 +1,289 @@ - -var pmHosts = inheritance(pmItems) - -pmHosts.model.name = "hosts" -pmHosts.model.page_name = "host" -pmHosts.model.bulk_name = "host" -pmHosts.model.className = "pmHosts" - -pmHosts.model.page_list = { - buttons:[ - { - class:'btn btn-primary', - function:function(){ return "spajs.open({ menuId:'new-"+this.model.page_name+"'}); return false;"}, - title:'Create', - link:function(){ return '/?new-'+this.model.page_name}, - }, - ], - title: "Hosts", - short_title: "Hosts", - fileds:[ - { - title:'Name', - name:'name', - }, - { - title:'Type', - name:'type', - style:function(item){ return 'style="width: 70px"'}, - class:function(item){ return 'class="hidden-xs"'}, - } - ], - actions:[ - { - class:'btn btn-danger', - function:function(item){ return 'spajs.showLoader('+this.model.className+'.deleteItem('+item.id+')); return false;'}, - title:'Delete', - link:function(){ return '#'} - } - ] -} - - -pmHosts.fileds = [ - [ - { - filed: new filedsLib.filed.text(), - title:'Name', - name:'name', - placeholder:'Enter host or range name', - help:'Host or range name', - validator:function(value) - { - if(this.validateRangeName(value) || this.validateHostName(value)) - { - return true; - } - - $.notify("Invalid value in field `name` it mast be valid host or range name", "error"); - return false; - }, - fast_validator:function(value){ return this.validateRangeName(value) || this.validateHostName(value)} - }, - ] -] - -pmHosts.model.page_new = { - title: "New host", - short_title: "New host", - fileds:pmHosts.fileds, - sections:[ - function(section){ - return jsonEditor.editor({}, {block:this.model.name}); - } - ], - onBeforeSave:function(data) - { - if(this.validateHostName(data.name)) - { - data.type = 'HOST' - } - else if(this.validateRangeName(data.name)) - { - data.type = 'RANGE' - } - else - { - $.notify("Error in host or range name", "error"); - return undefined; - } - - data.vars = jsonEditor.jsonEditorGetValues() - return data; - }, - onCreate:function(result, status, xhr, callOpt) - { - var def = new $.Deferred(); - $.notify("Host created", "success"); - - if(callOpt.parent_item) - { - if(callOpt.parent_type == 'group') - { - $.when(pmGroups.addSubHosts(callOpt.parent_item, [result.id])).always(function(){ - $.when(spajs.open({ menuId:"group/"+callOpt.parent_item})).always(function(){ - def.resolve() - }) - }) - } - else if(callOpt.parent_type == 'inventory') - { - $.when(pmInventories.addSubHosts(callOpt.parent_item, [result.id])).always(function(){ - $.when(spajs.open({ menuId:"inventory/"+callOpt.parent_item})).always(function(){ - def.resolve() - }) - }) - } - else if(callOpt.parent_type == 'project') - { - $.when(pmProjects.addSubHosts(callOpt.parent_item, [result.id])).always(function(){ - $.when(spajs.open({ menuId:"project/"+callOpt.parent_item})).always(function(){ - def.resolve() - }) - }) - } - else - { - console.error("Не известный parent_type", callOpt.parent_type) - $.when(spajs.open({ menuId:"host/"+result.id})).always(function(){ - def.resolve() - }) - } - } - else - { - $.when(spajs.open({ menuId:"host/"+result.id})).always(function(){ - def.resolve() - }) - } - - return def.promise(); - } -} - -pmHosts.model.page_item = { - buttons:[ - { - class:'btn btn-primary', - function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.updateItem('+item_id+')); return false;'}, - title:'Save', - link:function(){ return '#'}, - }, - { - class:'btn btn-default copy-btn', - function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.copyAndEdit('+item_id+')); return false;'}, - title:'', - link:function(){ return '#'}, - help:'Copy' - }, - { - class:'btn btn-danger danger-right', - function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.deleteItem('+item_id+')); return false;'}, - title:' ', - link:function(){ return '#'}, - }, - ], - sections:[ - function(section, item_id){ - return jsonEditor.editor(this.model.items[item_id].vars, {block:this.model.name}); - } - ], - title: function(item_id){ - return "Host "+pmHosts.model.items[item_id].justText('name') - }, - short_title: function(item_id){ - return "Host "+pmHosts.model.items[item_id].justText('name', function(v){return v.slice(0, 20)}) - }, - fileds:pmHosts.fileds, - onUpdate:function(result) - { - return true; - }, - onBeforeSave:function(data, item_id) - { - data.vars = jsonEditor.jsonEditorGetValues() - if(this.validateHostName(data.name)) - { - data.type = 'HOST' - } - else if(this.validateRangeName(data.name)) - { - data.type = 'RANGE' - } - else - { - $.notify("Error in host or range name", "error"); - return undefined; - } - return data; - }, -} - -pmHosts.copyItem = function(item_id) -{ - var def = new $.Deferred(); - var thisObj = this; - - $.when(this.loadItem(item_id)).done(function() - { - var data = thisObj.model.items[item_id]; - $.when(encryptedCopyModal.replace(data)).done(function(data) - { - delete data.id; - spajs.ajax.Call({ - url: "/api/v1/"+thisObj.model.name+"/", - type: "POST", - contentType:'application/json', - data: JSON.stringify(data), - success: function(data) - { - thisObj.model.items[data.id] = data - def.resolve(data.id) - }, - error:function(e) - { - def.reject(e) - } - }); - }).fail(function(e) - { - def.reject(e) - }) - - }).fail(function(e) - { - def.reject(e) - }) - - - return def.promise(); -} - - -/* - * -detail:"database is locked" -error_type:"OperationalError" - * -for(var i =0; i< 10000; i++) -{ -setTimeout(function(){ - name = Math.random()+"-"+Math.random() - name = name.replace(/\./g, "") - spajs.ajax.Call({ - url: "/api/v1/hosts/", - type: "POST", - contentType:'application/json', - data: JSON.stringify({name:name, type:"HOST"}), - }) -}, i*400); -} - */ - - tabSignal.connect("polemarch.start", function() - { - // hosts - spajs.addMenu({ - id:"hosts", - urlregexp:[/^hosts$/, /^host$/, /^hosts\/search\/?$/, /^hosts\/page\/([0-9]+)$/], - onOpen:function(holder, menuInfo, data){return pmHosts.showList(holder, menuInfo, data);} - }) - - spajs.addMenu({ - id:"hosts-search", - urlregexp:[/^hosts\/search\/([A-z0-9 %\-.:,=]+)$/], - onOpen:function(holder, menuInfo, data){return pmHosts.showSearchResults(holder, menuInfo, data);} - }) - - spajs.addMenu({ - id:"host", - urlregexp:[/^host\/([0-9]+)$/, /^hosts\/([0-9]+)$/], - onOpen:function(holder, menuInfo, data){return pmHosts.showItem(holder, menuInfo, data);} - }) - - spajs.addMenu({ - id:"newHost", - urlregexp:[/^new-host$/, /^([A-z0-9_]+)\/([0-9]+)\/new-host$/], - onOpen:function(holder, menuInfo, data){return pmHosts.showNewItemPage(holder, menuInfo, data);} - }) + +var pmHosts = inheritance(pmItems) + +pmHosts.model.name = "hosts" +pmHosts.model.page_name = "host" +pmHosts.model.bulk_name = "host" +pmHosts.model.className = "pmHosts" + +pmHosts.model.page_list = { + buttons:[ + { + class:'btn btn-primary', + function:function(){ return "spajs.open({ menuId:'new-"+this.model.page_name+"'}); return false;"}, + title:'Create', + link:function(){ return '/?new-'+this.model.page_name}, + }, + ], + title: "Hosts", + short_title: "Hosts", + fileds:[ + { + title:'Name', + name:'name', + }, + { + title:'Type', + name:'type', + style:function(item){ return 'style="width: 70px"'}, + class:function(item){ return 'class="hidden-xs"'}, + } + ], + actions:[ + { + class:'btn btn-danger', + function:function(item){ return 'spajs.showLoader('+this.model.className+'.deleteItem('+item.id+')); return false;'}, + title:'Delete', + link:function(){ return '#'} + } + ] +} + + +pmHosts.fileds = [ + [ + { + filed: new filedsLib.filed.text(), + title:'Name', + name:'name', + placeholder:'Enter host or range name', + help:'Host or range name', + validator:function(value) + { + if(this.validateRangeName(value) || this.validateHostName(value)) + { + return true; + } + + $.notify("Invalid value in field `name` it mast be valid host or range name", "error"); + return false; + }, + fast_validator:function(value){ return this.validateRangeName(value) || this.validateHostName(value)} + }, + ] +] + +pmHosts.model.page_new = { + title: "New host", + short_title: "New host", + fileds:pmHosts.fileds, + sections:[ + function(section){ + return jsonEditor.editor({}, {block:this.model.name}); + } + ], + onBeforeSave:function(data) + { + if(this.validateHostName(data.name)) + { + data.type = 'HOST' + } + else if(this.validateRangeName(data.name)) + { + data.type = 'RANGE' + } + else + { + $.notify("Error in host or range name", "error"); + return undefined; + } + + data.vars = jsonEditor.jsonEditorGetValues() + return data; + }, + onCreate:function(result, status, xhr, callOpt) + { + var def = new $.Deferred(); + $.notify("Host created", "success"); + + if(callOpt.parent_item) + { + if(callOpt.parent_type == 'group') + { + $.when(pmGroups.addSubHosts(callOpt.parent_item, [result.id])).always(function(){ + $.when(spajs.open({ menuId:"group/"+callOpt.parent_item})).always(function(){ + def.resolve() + }) + }) + } + else if(callOpt.parent_type == 'inventory') + { + $.when(pmInventories.addSubHosts(callOpt.parent_item, [result.id])).always(function(){ + $.when(spajs.open({ menuId:"inventory/"+callOpt.parent_item})).always(function(){ + def.resolve() + }) + }) + } + else if(callOpt.parent_type == 'project') + { + $.when(pmProjects.addSubHosts(callOpt.parent_item, [result.id])).always(function(){ + $.when(spajs.open({ menuId:"project/"+callOpt.parent_item})).always(function(){ + def.resolve() + }) + }) + } + else + { + console.error("Не известный parent_type", callOpt.parent_type) + $.when(spajs.open({ menuId:"host/"+result.id})).always(function(){ + def.resolve() + }) + } + } + else + { + $.when(spajs.open({ menuId:"host/"+result.id})).always(function(){ + def.resolve() + }) + } + + return def.promise(); + } +} + +pmHosts.model.page_item = { + buttons:[ + { + class:'btn btn-primary', + function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.updateItem('+item_id+')); return false;'}, + title:'Save', + link:function(){ return '#'}, + }, + { + class:'btn btn-default copy-btn', + function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.copyAndEdit('+item_id+')); return false;'}, + title:'', + link:function(){ return '#'}, + help:'Copy' + }, + { + class:'btn btn-danger danger-right', + function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.deleteItem('+item_id+')); return false;'}, + title:' ', + link:function(){ return '#'}, + }, + ], + sections:[ + function(section, item_id){ + return jsonEditor.editor(this.model.items[item_id].vars, {block:this.model.name}); + } + ], + title: function(item_id){ + return "Host "+pmHosts.model.items[item_id].justText('name') + }, + short_title: function(item_id){ + return "Host "+pmHosts.model.items[item_id].justText('name', function(v){return v.slice(0, 20)}) + }, + fileds:pmHosts.fileds, + onUpdate:function(result) + { + return true; + }, + onBeforeSave:function(data, item_id) + { + data.vars = jsonEditor.jsonEditorGetValues() + if(this.validateHostName(data.name)) + { + data.type = 'HOST' + } + else if(this.validateRangeName(data.name)) + { + data.type = 'RANGE' + } + else + { + $.notify("Error in host or range name", "error"); + return undefined; + } + return data; + }, +} + +pmHosts.copyItem = function(item_id) +{ + var def = new $.Deferred(); + var thisObj = this; + + $.when(this.loadItem(item_id)).done(function() + { + var data = thisObj.model.items[item_id]; + $.when(encryptedCopyModal.replace(data)).done(function(data) + { + delete data.id; + spajs.ajax.Call({ + url: "/api/v1/"+thisObj.model.name+"/", + type: "POST", + contentType:'application/json', + data: JSON.stringify(data), + success: function(data) + { + thisObj.model.items[data.id] = data + def.resolve(data.id) + }, + error:function(e) + { + def.reject(e) + } + }); + }).fail(function(e) + { + def.reject(e) + }) + + }).fail(function(e) + { + def.reject(e) + }) + + + return def.promise(); +} + + +/* + * +detail:"database is locked" +error_type:"OperationalError" + * +for(var i =0; i< 10000; i++) +{ +setTimeout(function(){ + name = Math.random()+"-"+Math.random() + name = name.replace(/\./g, "") + spajs.ajax.Call({ + url: "/api/v1/hosts/", + type: "POST", + contentType:'application/json', + data: JSON.stringify({name:name, type:"HOST"}), + }) +}, i*400); +} + */ + + tabSignal.connect("polemarch.start", function() + { + // hosts + spajs.addMenu({ + id:"hosts", + urlregexp:[/^hosts$/, /^host$/, /^hosts\/search\/?$/, /^hosts\/page\/([0-9]+)$/], + onOpen:function(holder, menuInfo, data){return pmHosts.showList(holder, menuInfo, data);} + }) + + spajs.addMenu({ + id:"hosts-search", + urlregexp:[/^hosts\/search\/([A-z0-9 %\-.:,=]+)$/, /^hosts\/search\/([A-z0-9 %\-.:,=]+)\/page\/([0-9]+)$/], + onOpen:function(holder, menuInfo, data){return pmHosts.showSearchResults(holder, menuInfo, data);} + }) + + spajs.addMenu({ + id:"host", + urlregexp:[/^host\/([0-9]+)$/, /^hosts\/([0-9]+)$/], + onOpen:function(holder, menuInfo, data){return pmHosts.showItem(holder, menuInfo, data);} + }) + + spajs.addMenu({ + id:"newHost", + urlregexp:[/^new-host$/, /^([A-z0-9_]+)\/([0-9]+)\/new-host$/], + onOpen:function(holder, menuInfo, data){return pmHosts.showNewItemPage(holder, menuInfo, data);} + }) }) \ No newline at end of file diff --git a/polemarch/static/js/pmInventories.js b/polemarch/static/js/pmInventories.js index 5af85cbe..5a2d6b28 100644 --- a/polemarch/static/js/pmInventories.js +++ b/polemarch/static/js/pmInventories.js @@ -1428,7 +1428,7 @@ pmInventories.filed.inventoriesAutocomplete.render = function(pmObj, filed, item spajs.addMenu({ id:"inventories-search", - urlregexp:[/^inventories\/search\/([A-z0-9 %\-.:,=]+)$/], + urlregexp:[/^inventories\/search\/([A-z0-9 %\-.:,=]+)$/, /^inventories\/search\/([A-z0-9 %\-.:,=]+)\/page\/([0-9]+)$/], onOpen:function(holder, menuInfo, data){return pmInventories.showSearchResults(holder, menuInfo, data);} }) diff --git a/polemarch/static/js/pmItems.js b/polemarch/static/js/pmItems.js index b9da59e0..6b3aef01 100644 --- a/polemarch/static/js/pmItems.js +++ b/polemarch/static/js/pmItems.js @@ -19,35 +19,32 @@ pmItems.model.className = "pmItems" pmItems.filed = {} -pmItems.toggleSelect = function(item_id, mode) +pmItems.toggleSelect = function (item_id, mode) { - if(!item_id) + if (!item_id) { return; } console.log(item_id, mode) - if(mode === undefined) + if (mode === undefined) { this.model.selectedItems[item_id] = !this.model.selectedItems[item_id] - if(this.model.selectedItems[item_id]) + if (this.model.selectedItems[item_id]) { this.model.selectedCount++ - } - else + } else { this.model.selectedCount-- } - } - else + } else { - if(this.model.selectedItems[item_id] != mode) + if (this.model.selectedItems[item_id] != mode) { - if(mode) + if (mode) { this.model.selectedCount++ - } - else + } else { this.model.selectedCount-- } @@ -55,7 +52,7 @@ pmItems.toggleSelect = function(item_id, mode) this.model.selectedItems[item_id] = mode } - if(this.model.selectedCount < 0) + if (this.model.selectedCount < 0) { this.model.selectedCount = 0; } @@ -68,23 +65,22 @@ pmItems.toggleSelect = function(item_id, mode) * @param {boolean} mode * @returns {promise} */ -pmItems.toggleSelectEachItem = function(mode) +pmItems.toggleSelectEachItem = function (mode) { var thisObj = this; - return $.when(this.loadAllItems()).done(function() + return $.when(this.loadAllItems()).done(function () { var delta = 0; - for(var i in thisObj.model.itemslist.results) + for (var i in thisObj.model.itemslist.results) { var item_id = thisObj.model.itemslist.results[i].id - if(thisObj.model.selectedItems[item_id] != mode) + if (thisObj.model.selectedItems[item_id] != mode) { - if(mode) + if (mode) { delta++ - } - else + } else { delta-- } @@ -93,7 +89,7 @@ pmItems.toggleSelectEachItem = function(mode) } thisObj.model.selectedCount += delta - if(thisObj.model.selectedCount < 0) + if (thisObj.model.selectedCount < 0) { thisObj.model.selectedCount = 0; } @@ -101,38 +97,38 @@ pmItems.toggleSelectEachItem = function(mode) }).promise() } -pmItems.toggleSelectAll = function(elements, mode) +pmItems.toggleSelectAll = function (elements, mode) { - for(var i=0; i< elements.length; i++) + for (var i = 0; i < elements.length; i++) { this.toggleSelect($(elements[i]).attr('data-id'), mode) } } -pmItems.validateHostName = function(name) +pmItems.validateHostName = function (name) { - if(!name) + if (!name) { return false; } var regexp = { - ipTest : /^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$/, - ip6Test : /((^|:)([0-9a-fA-F]{0,4})){1,8}$/, - domenTest : /^((\.{0,1}[a-z0-9][a-z0-9-]{0,62}[a-z0-9]\.{0,1})*)$/ + ipTest: /^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$/, + ip6Test: /((^|:)([0-9a-fA-F]{0,4})){1,8}$/, + domenTest: /^((\.{0,1}[a-z0-9][a-z0-9-]{0,62}[a-z0-9]\.{0,1})*)$/ } - if(regexp.ipTest.test(name.toLowerCase())) + if (regexp.ipTest.test(name.toLowerCase())) { return true; } - if(regexp.ip6Test.test(name.toLowerCase())) + if (regexp.ip6Test.test(name.toLowerCase())) { return true; } - if(regexp.domenTest.test(name.toLowerCase())) + if (regexp.domenTest.test(name.toLowerCase())) { return true; } @@ -140,9 +136,9 @@ pmItems.validateHostName = function(name) return false; } -pmItems.validateRangeName = function(name) +pmItems.validateRangeName = function (name) { - if(!name) + if (!name) { return false; } @@ -158,26 +154,26 @@ pmItems.validateRangeName = function(name) * @param {type} data * @returns {$.Deferred} */ -pmItems.showList = function(holder, menuInfo, data) +pmItems.showList = function (holder, menuInfo, data) { var thisObj = this; var offset = 0 var limit = this.pageSize; - if(data.reg && data.reg[1] > 0) + if (data.reg && data.reg[1] > 0) { - offset = this.pageSize*(data.reg[1] - 1); + offset = this.pageSize * (data.reg[1] - 1); } - return $.when(this.loadItems(limit, offset)).done(function() + return $.when(this.loadItems(limit, offset)).done(function () { - var tpl = thisObj.model.name+'_list' - if(!spajs.just.isTplExists(tpl)) + var tpl = thisObj.model.name + '_list' + if (!spajs.just.isTplExists(tpl)) { tpl = 'items_list' } - $(holder).insertTpl(spajs.just.render(tpl, {query:"", pmObj:thisObj, opt:{}})) - }).fail(function() + $(holder).insertTpl(spajs.just.render(tpl, {query: "", pmObj: thisObj, opt: {}})) + }).fail(function () { $.notify("", "error"); }) @@ -187,11 +183,11 @@ pmItems.showList = function(holder, menuInfo, data) * @param {string} query * @returns {HTML} Шаблон формы поиска */ -pmItems.searchFiled = function(options) +pmItems.searchFiled = function (options) { options.className = this.model.className; this.model.searchAdditionalData = options - return spajs.just.render('searchFiled', {opt:options}); + return spajs.just.render('searchFiled', {opt: options}); } /** @@ -199,14 +195,17 @@ pmItems.searchFiled = function(options) * @param {string} query * @returns {$.Deferred} */ -pmItems.search = function(query, options) +pmItems.search = function (query, options) { - if(this.isEmptySearchQuery(query)) - { - return spajs.open({ menuId:this.model.name, reopen:true}); + if (this.isEmptySearchQuery(query)) + { + return spajs.open({menuId: this.model.name, reopen: true}); + } + - return spajs.open({ menuId:this.model.name+"/search/"+this.searchObjectToString(trim(query)), reopen:true}); + return spajs.open({menuId: this.model.name + "/search/" + this.searchObjectToString(trim(query)), reopen: true}); + //this.paginationHtml(this.model.itemslist); } /** @@ -214,9 +213,9 @@ pmItems.search = function(query, options) * @param {type} query * @returns {Boolean} */ -pmItems.isEmptySearchQuery = function(query) +pmItems.isEmptySearchQuery = function (query) { - if(!query || !trim(query)) + if (!query || !trim(query)) { return true; } @@ -231,79 +230,88 @@ pmItems.isEmptySearchQuery = function(query) * @param {type} data * @returns {$.Deferred} */ -pmItems.showSearchResults = function(holder, menuInfo, data) +pmItems.showSearchResults = function (holder, menuInfo, data) { var thisObj = this; - return $.when(this.sendSearchQuery(this.searchStringToObject(decodeURIComponent(data.reg[1])))).done(function() + + var limit = this.pageSize; + + if (data.reg && data.reg[2] > 0) { - var tpl = thisObj.model.name+'_list' - if(!spajs.just.isTplExists(tpl)) + offset = this.pageSize * (data.reg[2] - 1); + } else { + offset = 0; + } + return $.when(this.sendSearchQuery(this.searchStringToObject(decodeURIComponent(data.reg[1])), limit, offset)).done(function () + { + var tpl = thisObj.model.name + '_list' + if (!spajs.just.isTplExists(tpl)) { tpl = 'items_list' } - - $(holder).insertTpl(spajs.just.render(tpl, {query:decodeURIComponent(data.reg[1]), pmObj:thisObj, opt:{}})) - }).fail(function() + + $(holder).insertTpl(spajs.just.render(tpl, {query: decodeURIComponent(data.reg[1]), pmObj: thisObj, opt: {}})) + }).fail(function () { $.notify("", "error"); }) } -pmItems.copyItem = function(item_id) +pmItems.copyItem = function (item_id) { var def = new $.Deferred(); var thisObj = this; - $.when(this.loadItem(item_id)).done(function() + $.when(this.loadItem(item_id)).done(function () { var data = thisObj.model.items[item_id]; delete data.id; data.name = "copy from " + data.name - $.when(encryptedCopyModal.replace(data)).done(function(data) + $.when(encryptedCopyModal.replace(data)).done(function (data) { spajs.ajax.Call({ - url: "/api/v1/"+thisObj.model.name+"/", + url: "/api/v1/" + thisObj.model.name + "/", type: "POST", - contentType:'application/json', + contentType: 'application/json', data: JSON.stringify(data), - success: function(data) + success: function (data) { thisObj.model.items[data.id] = data def.resolve(data.id) }, - error:function(e) + error: function (e) { def.reject(e) } }); - }).fail(function(e) + }).fail(function (e) { def.reject(e) }) - }).fail(function(){ + }).fail(function () { def.reject(e) }) return def.promise(); } -pmItems.importItem = function(data) +pmItems.importItem = function (data) { var def = new $.Deferred(); var thisObj = this; spajs.ajax.Call({ - url: "/api/v1/"+thisObj.model.name+"/", + url: "/api/v1/" + thisObj.model.name + "/", type: "POST", - contentType:'application/json', + contentType: 'application/json', data: JSON.stringify(data), - success: function(data) + success: function (data) { thisObj.model.items[data.id] = data def.resolve(data.id) }, - error:function(e) + error: function (e) { $.notify("Error in import item", "error"); polemarch.showErrors(e) @@ -313,65 +321,65 @@ pmItems.importItem = function(data) return def.promise(); } -pmItems.copyAndEdit = function(item_id) +pmItems.copyAndEdit = function (item_id) { var def = new $.Deferred(); var thisObj = this; - return $.when(this.copyItem(item_id)).done(function(newItemId) + return $.when(this.copyItem(item_id)).done(function (newItemId) { - $.when(spajs.open({ menuId:thisObj.model.page_name + "/"+newItemId})).done(function(){ + $.when(spajs.open({menuId: thisObj.model.page_name + "/" + newItemId})).done(function () { $.notify("Item was duplicate", "success"); def.resolve() - }).fail(function(e){ + }).fail(function (e) { $.notify("Error in duplicate item", "error"); polemarch.showErrors(e) def.reject(e) }) - }).fail(function(e){ + }).fail(function (e) { def.reject(e) }) return def.promise(); } -pmItems.showItem = function(holder, menuInfo, data) +pmItems.showItem = function (holder, menuInfo, data) { var thisObj = this; //console.log(menuInfo, data) - return $.when(this.loadItem(data.reg[1])).done(function() + return $.when(this.loadItem(data.reg[1])).done(function () { - var tpl = thisObj.model.name+'_page' - if(!spajs.just.isTplExists(tpl)) + var tpl = thisObj.model.name + '_page' + if (!spajs.just.isTplExists(tpl)) { tpl = 'items_page' } - $(holder).insertTpl(spajs.just.render(tpl, {item_id:data.reg[1], pmObj:thisObj, opt:{}})) - }).fail(function() + $(holder).insertTpl(spajs.just.render(tpl, {item_id: data.reg[1], pmObj: thisObj, opt: {}})) + }).fail(function () { $.notify("", "error"); }).promise() } -pmItems.showNewItemPage = function(holder, menuInfo, data) +pmItems.showNewItemPage = function (holder, menuInfo, data) { var def = new $.Deferred(); - var tpl = this.model.name+'_new_page' - if(!spajs.just.isTplExists(tpl)) + var tpl = this.model.name + '_new_page' + if (!spajs.just.isTplExists(tpl)) { tpl = 'items_new_page' } - var text = spajs.just.render(tpl, {parent_item:data.reg[2], parent_type:data.reg[1], pmObj:this, opt:{}}) + var text = spajs.just.render(tpl, {parent_item: data.reg[2], parent_type: data.reg[1], pmObj: this, opt: {}}) $(holder).insertTpl(text) def.resolve() return def.promise(); } -pmItems.loadAllItems = function() +pmItems.loadAllItems = function () { return this.loadItems(999999); } @@ -382,7 +390,7 @@ pmItems.loadAllItems = function() * @param {object} item загруженный с сервера элемента * @returns {object} обработаный элемент */ -pmItems.afterItemLoad = function(item) +pmItems.afterItemLoad = function (item) { return item; } @@ -391,25 +399,25 @@ pmItems.afterItemLoad = function(item) * Обновляет поле модел this.model.itemslist и ложит туда список пользователей * Обновляет поле модел this.model.items и ложит туда список инфу о пользователях по их id */ -pmItems.loadItems = function(limit, offset) +pmItems.loadItems = function (limit, offset) { - if(!limit) + if (!limit) { limit = 30; } - if(!offset) + if (!offset) { offset = 0; } var thisObj = this; return spajs.ajax.Call({ - url: "/api/v1/"+this.model.name+"/", + url: "/api/v1/" + this.model.name + "/", type: "GET", - contentType:'application/json', - data: "limit="+encodeURIComponent(limit)+"&offset="+encodeURIComponent(offset), - success: function(data) + contentType: 'application/json', + data: "limit=" + encodeURIComponent(limit) + "&offset=" + encodeURIComponent(offset), + success: function (data) { //console.log("update Items", data) data.limit = limit @@ -417,14 +425,14 @@ pmItems.loadItems = function(limit, offset) thisObj.model.itemslist = data //thisObj.model.items = {} - for(var i in data.results) + for (var i in data.results) { var val = thisObj.afterItemLoad(data.results[i]) thisObj.model.items.justWatch(val.id); thisObj.model.items[val.id] = mergeDeep(thisObj.model.items[val.id], val) } }, - error:function(e) + error: function (e) { console.warn(e) polemarch.showErrors(e) @@ -438,15 +446,15 @@ pmItems.loadItems = function(limit, offset) * @param {string} defaultName имя параметра по умолчанию * @returns {pmItems.searchStringToObject.search} объект для поиска */ -pmItems.searchStringToObject = function(query, defaultName) +pmItems.searchStringToObject = function (query, defaultName) { var search = {} - if(query == "") + if (query == "") { return search; } - if(!defaultName) + if (!defaultName) { defaultName = 'name' } @@ -462,7 +470,7 @@ pmItems.searchStringToObject = function(query, defaultName) * @param {string} defaultName имя параметра по умолчанию * @returns {string} строка для параметра страницы поиска */ -pmItems.searchObjectToString = function(query, defaultName) +pmItems.searchObjectToString = function (query, defaultName) { return encodeURIComponent(query); } @@ -474,44 +482,44 @@ pmItems.searchObjectToString = function(query, defaultName) * @param {integer} offset * @returns {jQuery.ajax|spajs.ajax.Call.defpromise|type|spajs.ajax.Call.opt|spajs.ajax.Call.spaAnonym$10|Boolean|undefined|spajs.ajax.Call.spaAnonym$9} */ -pmItems.sendSearchQuery = function(query, limit, offset) +pmItems.sendSearchQuery = function (query, limit, offset) { - if(!limit) + if (!limit) { limit = 999; } - if(!offset) + if (!offset) { offset = 0; } var q = []; - q.push("limit="+encodeURIComponent(limit)) - q.push("offset="+encodeURIComponent(offset)) + q.push("limit=" + encodeURIComponent(limit)) + q.push("offset=" + encodeURIComponent(offset)) - for(var i in query) + for (var i in query) { - if(Array.isArray(query[i])) + if (Array.isArray(query[i])) { - for(var j in query[i]) + for (var j in query[i]) { query[i][j] = encodeURIComponent(query[i][j]) } - q.push(encodeURIComponent(i)+"="+query[i].join(",")) + q.push(encodeURIComponent(i) + "=" + query[i].join(",")) continue; } - q.push(encodeURIComponent(i)+"="+encodeURIComponent(query[i])) + q.push(encodeURIComponent(i) + "=" + encodeURIComponent(query[i])) } var thisObj = this; return spajs.ajax.Call({ - url: "/api/v1/"+this.model.name+"/?"+q.join("&"), + url: "/api/v1/" + this.model.name + "/?" + q.join("&"), type: "GET", - contentType:'application/json', - success: function(data) + contentType: 'application/json', + success: function (data) { //console.log("update Items", data) data.limit = limit @@ -519,13 +527,13 @@ pmItems.sendSearchQuery = function(query, limit, offset) thisObj.model.itemslist = data //thisObj.model.items = {} - for(var i in data.results) + for (var i in data.results) { var val = data.results[i] thisObj.model.items[val.id] = val } }, - error:function(e) + error: function (e) { console.warn(e) polemarch.showErrors(e) @@ -533,9 +541,9 @@ pmItems.sendSearchQuery = function(query, limit, offset) }); } -pmItems.searchItems = function(query, attrName, limit, offset) +pmItems.searchItems = function (query, attrName, limit, offset) { - if(!attrName) + if (!attrName) { attrName = "name"; } @@ -545,12 +553,12 @@ pmItems.searchItems = function(query, attrName, limit, offset) return this.sendSearchQuery(q, limit, offset); } -pmItems.loadItemsByIds = function(ids) +pmItems.loadItemsByIds = function (ids) { - var q = {id:ids} - for(var i in ids) + var q = {id: ids} + for (var i in ids) { - if(this.model.items[ids[i]] === undefined) + if (this.model.items[ids[i]] === undefined) { this.model.items[ids[i]] = {} } @@ -560,9 +568,9 @@ pmItems.loadItemsByIds = function(ids) /** * Обновляет поле модел this.model.items[item_id] и ложит туда пользователя */ -pmItems.loadItem = function(item_id) +pmItems.loadItem = function (item_id) { - if(!item_id) + if (!item_id) { throw "Error in pmItems.loadItem with item_id = `" + item_id + "`" } @@ -570,24 +578,24 @@ pmItems.loadItem = function(item_id) var def = new $.Deferred(); var thisObj = this; - if(thisObj.model.items[item_id] === undefined) + if (thisObj.model.items[item_id] === undefined) { thisObj.model.items[item_id] = {} } spajs.ajax.Call({ - url: "/api/v1/"+this.model.name+"/"+item_id+"/", + url: "/api/v1/" + this.model.name + "/" + item_id + "/", type: "GET", - contentType:'application/json', + contentType: 'application/json', data: "", - success: function(data) + success: function (data) { //console.log("loadUser", data) thisObj.model.items.justWatch(item_id) thisObj.model.items[item_id] = thisObj.afterItemLoad(data) def.resolve(data) }, - error:function(e) + error: function (e) { console.warn(e) //polemarch.showErrors(e) @@ -601,31 +609,31 @@ pmItems.loadItem = function(item_id) /** * @return $.Deferred */ -pmItems.deleteItem = function(item_id, force) +pmItems.deleteItem = function (item_id, force) { - if(!item_id) + if (!item_id) { throw "Error in pmItems.deleteItem with item_id = `" + item_id + "`" } var def = new $.Deferred(); - if(!force && !confirm("Are you sure?")) + if (!force && !confirm("Are you sure?")) { def.reject(); return def.promise() } var thisObj = this; - $.when(this.deleteItemQuery(item_id)).done(function(data) + $.when(this.deleteItemQuery(item_id)).done(function (data) { - $.when(spajs.open({ menuId:thisObj.model.name})).done(function() + $.when(spajs.open({menuId: thisObj.model.name})).done(function () { def.resolve() - }).fail(function(e){ + }).fail(function (e) { def.reject(e); polemarch.showErrors(e.responseJSON) }) - }).fail(function(e){ + }).fail(function (e) { def.reject(e); polemarch.showErrors(e.responseJSON) }) @@ -633,54 +641,54 @@ pmItems.deleteItem = function(item_id, force) return def.promise() } -pmItems.multiOperationsOnEachRow = function(elements, operation, force) +pmItems.multiOperationsOnEachRow = function (elements, operation, force) { var def = new $.Deferred(); var item_ids = [] - for(var i=0; i< elements.length; i++) + for (var i = 0; i < elements.length; i++) { item_ids.push($(elements[i]).attr('data-id')) } - $.when(this.multiOperationsOnItems(operation, item_ids, force)).always(function(){ + $.when(this.multiOperationsOnItems(operation, item_ids, force)).always(function () { def.resolve() }) return def.promise(); } -pmItems.deleteRows = function(elements) +pmItems.deleteRows = function (elements) { - if($.inArray(this.model.bulk_name, ['history', 'host', 'group', 'inventory', 'project', 'periodictask', 'template']) != -1) + if ($.inArray(this.model.bulk_name, ['history', 'host', 'group', 'inventory', 'project', 'periodictask', 'template']) != -1) { var deleteBulk = [] - for(var i=0; i< elements.length; i++) + for (var i = 0; i < elements.length; i++) { deleteBulk.push({ - type:"del", - item:this.model.bulk_name, - pk:$(elements[i]).attr('data-id') + type: "del", + item: this.model.bulk_name, + pk: $(elements[i]).attr('data-id') }) } var thisObj = this; return $.when(spajs.ajax.Call({ - url: "/api/v1/_bulk/", - type: "POST", - contentType:'application/json', - data:JSON.stringify(deleteBulk) - })).always(function() + url: "/api/v1/_bulk/", + type: "POST", + contentType: 'application/json', + data: JSON.stringify(deleteBulk) + })).always(function () { - for(var i in deleteBulk) + for (var i in deleteBulk) { - $(".item-"+deleteBulk[i].pk).hide(); + $(".item-" + deleteBulk[i].pk).hide(); thisObj.toggleSelect(deleteBulk[i].pk, false); } spajs.openURL(window.location.href); }).promise(); } - $.when(this.multiOperationsOnEachRow(elements, 'deleteItemQuery')).always(function(){ + $.when(this.multiOperationsOnEachRow(elements, 'deleteItemQuery')).always(function () { spajs.openURL(window.location.href); }) } @@ -689,34 +697,34 @@ pmItems.deleteRows = function(elements) * Удалит все выделенные элементы * @returns {promise} */ -pmItems.deleteSelected = function() +pmItems.deleteSelected = function () { - if($.inArray(this.model.bulk_name, ['history', 'host', 'group', 'inventory', 'project', 'periodictask', 'template']) != -1) + if ($.inArray(this.model.bulk_name, ['history', 'host', 'group', 'inventory', 'project', 'periodictask', 'template']) != -1) { var thisObj = this; var deleteBulk = [] - for(var i in this.model.selectedItems) + for (var i in this.model.selectedItems) { - if(this.model.selectedItems[i]) + if (this.model.selectedItems[i]) { deleteBulk.push({ - type:"del", - item:this.model.bulk_name, - pk:i + type: "del", + item: this.model.bulk_name, + pk: i }) } } return $.when(spajs.ajax.Call({ - url: "/api/v1/_bulk/", - type: "POST", - contentType:'application/json', - data:JSON.stringify(deleteBulk) - })).always(function() + url: "/api/v1/_bulk/", + type: "POST", + contentType: 'application/json', + data: JSON.stringify(deleteBulk) + })).always(function () { - for(var i in deleteBulk) + for (var i in deleteBulk) { - $(".item-"+deleteBulk[i].pk).hide(); + $(".item-" + deleteBulk[i].pk).hide(); thisObj.toggleSelect(deleteBulk[i].pk, false); } spajs.openURL(window.location.href); @@ -724,39 +732,39 @@ pmItems.deleteSelected = function() } var item_ids = [] - for(var i in this.model.selectedItems) + for (var i in this.model.selectedItems) { - if(this.model.selectedItems[i]) + if (this.model.selectedItems[i]) { item_ids.push(i) } } - return $.when(this.multiOperationsOnItems('deleteItemQuery', item_ids)).always(function(){ + return $.when(this.multiOperationsOnItems('deleteItemQuery', item_ids)).always(function () { spajs.openURL(window.location.href); }).promise(); } -pmItems.multiOperationsOnItems = function(operation, item_ids, force, def) +pmItems.multiOperationsOnItems = function (operation, item_ids, force, def) { - if(!force && !confirm("Are you sure?")) + if (!force && !confirm("Are you sure?")) { return; } - if(def === undefined) + if (def === undefined) { def = new $.Deferred(); } - if(!item_ids || !item_ids.length) + if (!item_ids || !item_ids.length) { def.resolve() return def.promise(); } var thisObj = this; - $.when(this[operation](item_ids[0])).always(function(){ + $.when(this[operation](item_ids[0])).always(function () { item_ids.splice(0, 1) thisObj.multiOperationsOnItems(operation, item_ids, true, def); }) @@ -767,43 +775,43 @@ pmItems.multiOperationsOnItems = function(operation, item_ids, force, def) /** * @return $.Deferred */ -pmItems.deleteItemQuery = function(item_id) +pmItems.deleteItemQuery = function (item_id) { - $(".item-"+item_id).hide(); + $(".item-" + item_id).hide(); this.toggleSelect(item_id, false); return spajs.ajax.Call({ - url: "/api/v1/"+this.model.name+"/"+item_id+"/", + url: "/api/v1/" + this.model.name + "/" + item_id + "/", type: "DELETE", - contentType:'application/json', - success: function(data) + contentType: 'application/json', + success: function (data) { - $(".item-"+item_id).remove(); + $(".item-" + item_id).remove(); }, - error:function(e) + error: function (e) { - $(".item-"+item_id).show(); + $(".item-" + item_id).show(); } }); } -pmItems.updateList = function(updated_ids) +pmItems.updateList = function (updated_ids) { - + var thisObj = this; - $.when(this.loadItemsByIds(updated_ids)).always(function() + $.when(this.loadItemsByIds(updated_ids)).always(function () { - if(thisObj.model.updateTimeoutId) + if (thisObj.model.updateTimeoutId) { clearTimeout(thisObj.model.updateTimeoutId) } - thisObj.model.updateTimeoutId = setTimeout(function(){ + thisObj.model.updateTimeoutId = setTimeout(function () { thisObj.updateList(updated_ids) }, 5001) }) } -pmItems.stopUpdates = function() +pmItems.stopUpdates = function () { clearTimeout(this.model.updateTimeoutId) this.model.updateTimeoutId = undefined; @@ -818,26 +826,26 @@ pmItems.stopUpdates = function() * @param {function} searchFunction функция поиска новых данных * @returns {$.Deferred} */ -pmItems.showUpdatedList = function(holder, menuInfo, data, functionName) +pmItems.showUpdatedList = function (holder, menuInfo, data, functionName) { var thisObj = this; - if(functionName == undefined) + if (functionName == undefined) { functionName = "showList" } - return $.when(this[functionName](holder, menuInfo, data)).always(function(updated_data) + return $.when(this[functionName](holder, menuInfo, data)).always(function (updated_data) { var updated_ids = [] - if(updated_data.results) + if (updated_data.results) { - for(var i in updated_data.results) + for (var i in updated_data.results) { updated_ids.push(updated_data.results[i].id) } } - - thisObj.model.updateTimeoutId = setTimeout(function(){ + + thisObj.model.updateTimeoutId = setTimeout(function () { thisObj.updateList(updated_ids); }, 5001) }).promise(); @@ -847,39 +855,39 @@ pmItems.showUpdatedList = function(holder, menuInfo, data, functionName) // pagination //////////////////////////////////////////////// -pmItems.paginationHtml = function(list) +pmItems.paginationHtml = function (list) { var totalPage = list.count / list.limit - if(totalPage > Math.floor(totalPage)) + if (totalPage > Math.floor(totalPage)) { totalPage = Math.floor(totalPage) + 1 } var currentPage = 0; - if(list.offset) + if (list.offset) { currentPage = Math.floor(list.offset / list.limit) } var url = window.location.href return spajs.just.render('pagination', { - totalPage:totalPage, - currentPage:currentPage, - url:url}) + totalPage: totalPage, + currentPage: currentPage, + url: url}) } -pmItems.getTotalPages = function(list) +pmItems.getTotalPages = function (list) { var totalPage = list.count / list.limit return totalPage } -pmItems.exportSelecedToFile = function(){ +pmItems.exportSelecedToFile = function () { var item_ids = [] - for(var i in this.model.selectedItems) + for (var i in this.model.selectedItems) { - if(this.model.selectedItems[i]) + if (this.model.selectedItems[i]) { item_ids.push(i) } @@ -892,19 +900,19 @@ pmItems.exportSelecedToFile = function(){ * Добавление сущности * @return $.Deferred */ -pmItems.addItem = function(parent_type, parent_item, opt) +pmItems.addItem = function (parent_type, parent_item, opt) { var def = new $.Deferred(); var data = {} - for(var i in this.model.page_new.fileds) + for (var i in this.model.page_new.fileds) { - for(var j in this.model.page_new.fileds[i]) + for (var j in this.model.page_new.fileds[i]) { var val = this.model.page_new.fileds[i][j]; data[val.name] = val.filed.getValue(this, val) - if(val.validator !== undefined && !val.validator.apply(this, [data[val.name]])) + if (val.validator !== undefined && !val.validator.apply(this, [data[val.name]])) { def.reject() return def.promise(); @@ -912,10 +920,10 @@ pmItems.addItem = function(parent_type, parent_item, opt) } } - if(this.model.page_new.onBeforeSave) + if (this.model.page_new.onBeforeSave) { data = this.model.page_new.onBeforeSave.apply(this, [data, opt]); - if(data == undefined || data == false) + if (data == undefined || data == false) { def.reject() return def.promise(); @@ -924,28 +932,28 @@ pmItems.addItem = function(parent_type, parent_item, opt) var thisObj = this; spajs.ajax.Call({ - url: "/api/v1/"+this.model.name+"/", + url: "/api/v1/" + this.model.name + "/", type: "POST", - contentType:'application/json', + contentType: 'application/json', data: JSON.stringify(data), - success: function() + success: function () { var agrs = [] - for(var i =0; i 0) - { - offset = this.pageSize*(data.reg[2] - 1); - } - var project_id = data.reg[1]; - - return $.when(this.searchItems(project_id, 'project'), pmProjects.loadItem(project_id)).done(function() - { - $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_list', {query:"", project_id:project_id})) - }).fail(function() - { - $.notify("", "error"); - }).promise(); -} - -pmPeriodicTasks.search = function(query, options) -{ - if(this.isEmptySearchQuery(query)) - { - return spajs.open({ menuId:'project/' + options.project_id +"/" + this.model.name, reopen:true}); - } - - return spajs.open({ menuId:'project/' + options.project_id +"/" + this.model.name+"/search/"+this.searchObjectToString(trim(query)), reopen:true}); -} - -pmPeriodicTasks.showSearchResults = function(holder, menuInfo, data) -{ - var thisObj = this; - var project_id = data.reg[1]; - - - var search = this.searchStringToObject(decodeURIComponent(data.reg[2])) - search['project'] = project_id - - return $.when(this.sendSearchQuery(search), pmProjects.loadItem(project_id)).done(function() - { - $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_list', {query:decodeURIComponent(data.reg[2]), project_id:project_id})) - }).fail(function() - { - $.notify("", "error"); - }).promise(); -} - -pmPeriodicTasks.showNewItemPage = function(holder, menuInfo, data) -{ - var project_id = data.reg[1]; - var thisObj = this; - return $.when(pmTasks.searchItems(project_id, "project"), pmProjects.loadItem(project_id), pmInventories.loadAllItems()).done(function() - { - thisObj.model.newitem = {type:'INTERVAL', kind:'PLAYBOOK'} - $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_new_page', {project_id:project_id})) - - $('#new_periodic-tasks_inventory').select2({ width: '100%' }); - - new autoComplete({ - selector: '#new_periodic-tasks_playbook', - minChars: 0, - cache:false, - showByClick:false, - renderItem: function(item, search) - { - return '
    ' + item.playbook + '
    '; - }, - onSelect: function(event, term, item) - { - $("#new_periodic-tasks_playbook").val($(item).text()); - //console.log('onSelect', term, item); - //var value = $(item).attr('data-value'); - }, - source: function(term, response) - { - term = term.toLowerCase(); - - var matches = [] - for(var i in pmTasks.model.items) - { - var val = pmTasks.model.items[i] - if(val.name.toLowerCase().indexOf(term) != -1 && val.project == project_id) - { - matches.push(val) - } - } - response(matches); - } - }); - }).fail(function() - { - $.notify("", "error"); - }).promise(); -} - -/** - * Для ввода пароля - * @type Object - */ -pmPeriodicTasks.filed.selectInventoryKindPlaybookGroupModuleAndTime = inheritance(filedsLib.filed.simpleText) -pmPeriodicTasks.filed.selectInventoryKindPlaybookGroupModuleAndTime.type = 'selectInventoryKindPlaybookGroupModuleAndTime' -pmPeriodicTasks.filed.selectInventoryKindPlaybookGroupModuleAndTime.getValue = function(pmObj, filed){ - return ''; -} - -pmPeriodicTasks.model.page_list = { - short_title: 'Periodic tasks', -} - -pmPeriodicTasks.model.page_item = { - buttons:[ - { - class:'btn btn-primary', - function:function(item_id, opt){ return 'spajs.showLoader('+this.model.className+'.updateItem('+item_id+', {project_id:'+opt.project_id+'})); return false;'}, - title:'Save', - link:function(){ return '#'}, - }, - { - class:'btn btn-warning', - function:function(item_id, opt){ - return "spajs.showLoader(pmPeriodicTasks.execute("+opt.project_id+", "+item_id+")); return false;" - }, - title:'Execute', - link:function(){ return '#'}, - help:'Execute' - }, - { - class:'btn btn-default copy-btn', - function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.copyAndEdit('+item_id+')); return false;'}, - title:'', - link:function(){ return '#'}, - help:'Copy' - }, - { - class:'btn btn-danger danger-right', - function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.deleteItem('+item_id+')); return false;'}, - title:' ', - link:function(){ return '#'}, - }, - ], - sections:[ - function(section, item_id) - { - return spajs.just.render('periodic-tasks_page_vars_section', { - item_id:item_id, - pmObj:this, - PLAYBOOK_VARS: jQuery.extend(true, {}, pmPeriodicTasks.model.items[item_id].vars), - MODULE_VARS: jQuery.extend(true, {}, pmPeriodicTasks.model.items[item_id].vars) - }) - } - ], - title: function(item_id){ - return "Periodic task "+this.model.items[item_id].justText('name') - }, - back_link: function(item_id, opt){ - return polemarch.opt.host + "/?project/" + opt.project_id + "/" + this.model.name; - }, - short_title: function(item_id){ - return this.model.items[item_id].justText('name', function(v){return v.slice(0, 20)}) - }, - fileds:[ - [ - { - filed: new filedsLib.filed.text(), - title:'Name', - name:'name', - placeholder:'Enter template name', - validator:function(value){ - return filedsLib.validator.notEmpty(value, 'Name') - }, - fast_validator:function(value){ return value != '' && value} - }, - { - filed: new filedsLib.filed.boolean(), - title:'Save in history', - name:'save_result', - help:'Save result of task in history', - }, - { - filed: new filedsLib.filed.boolean(), - title:'Enabled', - name:'enabled', - help:'', - }, - ],[ - { - filed: new pmPeriodicTasks.filed.selectInventoryKindPlaybookGroupModuleAndTime(), - name:'project', - }, - ] - ], - onUpdate:function(result) - { - return true; - }, - onBeforeSave:function(data, item_id, opt) - { - if(!opt || !opt.project_id) - { - throw "Error in pmPeriodicTasks.onBeforeSave with opt.project_id is null" - } - - data.project = opt.project_id - - data.type = $("#periodic-tasks_"+item_id+"_type").val() - data.inventory = pmPeriodicTasks.inventoriesAutocompletefiled.getValue() - - data.kind = $("#periodic-tasks_"+item_id+"_kind").val() - - if(!data.inventory) - { - $.notify("Invalid field `inventory` ", "error"); - return false; - } - - - if(data.kind == "MODULE") - { - data.mode = moduleArgsEditor.getSelectedModuleName() - if(!data.mode) - { - $.notify("Module name is empty", "error"); - return false; - } - } - else - { - data.mode = $("#periodic-tasks_"+item_id+"_playbook").val() - if(!data.mode) - { - $.notify("Playbook name is empty", "error"); - return false; - } - } - - if(data.type == "CRONTAB") - { - data.schedule = crontabEditor.getCronString() - } - else - { - data.schedule = $("#periodic-tasks_"+item_id+"_schedule_INTERVAL").val() - if(!data.schedule) - { - $.notify("Invalid field `Interval schedule` ", "error"); - return; - } - } - - data.vars = jsonEditor.jsonEditorGetValues(data.kind) - - if(data.kind == "MODULE") - { - data.vars.group = pmGroups.getGroupsAutocompleteValue() - data.vars.args = moduleArgsEditor.getModuleArgs(); - } - return data; - }, -} - -pmPeriodicTasks.showItem = function(holder, menuInfo, data) -{ - var def = new $.Deferred(); - var thisObj = this; - var item_id = data.reg[2]; - var project_id = data.reg[1]; - - $.when(pmPeriodicTasks.loadItem(item_id), pmTasks.loadAllItems(), pmInventories.loadAllItems(), pmProjects.loadItem(project_id)).done(function() - { - var tpl = thisObj.model.name+'_page' - if(!spajs.just.isTplExists(tpl)) - { - tpl = 'items_page' - } - - $(holder).insertTpl(spajs.just.render(tpl, {item_id:item_id, pmObj:thisObj, opt:{project_id:project_id}})) - pmPeriodicTasks.selectInventory(pmPeriodicTasks.model.items[item_id].inventory) - - $('#periodic-tasks_'+item_id+'_inventory').select2({ width: '100%' }); - - new autoComplete({ - selector: '#periodic-tasks_'+item_id+'_playbook', - minChars: 0, - cache:false, - showByClick:false, - renderItem: function(item, search) - { - return '
    ' + item.name + '.yaml
    '; - }, - onSelect: function(event, term, item) - { - $("#periodic-tasks_"+item_id+"_playbook").val($(item).text()); - //console.log('onSelect', term, item); - //var value = $(item).attr('data-value'); - }, - source: function(term, response) - { - term = term.toLowerCase(); - - var matches = [] - for(var i in pmTasks.model.items) - { - var val = pmTasks.model.items[i] - if(val.name.toLowerCase().indexOf(term) != -1 && val.project == project_id) - { - matches.push(val) - } - } - response(matches); - } - }); - - def.resolve(); - - }).fail(function(e) - { - $.notify("", "error"); - def.reject(e); - }) - - return def.promise() -} - - -/** - * @return $.Deferred - */ -pmPeriodicTasks.addItem = function(project_id) -{ - if(!project_id) - { - throw "Error in pmPeriodicTasks.addItem with project_id = `" + project_id + "`" - } - - var def = new $.Deferred(); - - var data = {} - - data.project = project_id - - data.name = $("#new_periodic-tasks_name").val() - data.type = $("#new_periodic-tasks_type").val() - data.inventory = pmPeriodicTasks.inventoriesAutocompletefiled.getValue() - - if(!data.name) - { - $.notify("Invalid field `name` ", "error"); - def.reject(); - return def.promise(); - } - - if(!data.inventory) - { - $.notify("Invalid field `inventory` ", "error"); - def.reject(); - return def.promise(); - } - - - data.kind = $("#new_periodic-tasks_kind").val() - - if(data.kind == "MODULE") - { - data.mode = moduleArgsEditor.getSelectedModuleName() - if(!data.mode) - { - $.notify("Module name is empty", "error"); - def.reject(); - return def.promise(); - } - } - else - { - data.mode = $("#new_periodic-tasks_playbook").val() - if(!data.mode) - { - $.notify("Playbook name is empty", "error"); - def.reject(); - return def.promise(); - } - } - - if(data.type == "CRONTAB") - { - data.schedule = crontabEditor.getCronString() - } - else - { - data.schedule = $("#new_periodic-tasks_schedule_INTERVAL").val() - if(!data.schedule) - { - $.notify("Invalid field `Interval schedule` ", "error"); - def.reject(); - return def.promise(); - } - } - - data.save_result = $("#new_periodic-tasks_save_result").hasClass('selected') - - data.vars = jsonEditor.jsonEditorGetValues(data.kind) - - if(data.kind == "MODULE") - { - data.vars.group = pmGroups.getGroupsAutocompleteValue() - data.vars.args = moduleArgsEditor.getModuleArgs(); - } - - spajs.ajax.Call({ - url: "/api/v1/"+this.model.name+"/", - type: "POST", - contentType:'application/json', - data: JSON.stringify(data), - success: function(data) - { - $.notify("periodic task created", "success"); - - $.when(spajs.open({ menuId:"project/"+project_id+"/periodic-task/"+data.id})).always(function(){ - def.resolve() - }) - }, - error:function(e) - { - polemarch.showErrors(e.responseJSON) - def.reject(e) - } - }); - return def.promise(); -} - -pmPeriodicTasks.loadItem = function(item_id) -{ - var thisObj = this; - return spajs.ajax.Call({ - url: "/api/v1/"+this.model.name+"/"+item_id+"/", - type: "GET", - contentType:'application/json', - data: "", - success: function(data) - { - if(data.kind == "MODULE") - { - if(data && data.vars && data.vars.group !== undefined) - { - data.group = data.vars.group - delete data.vars.group - } - - if(data && data.vars && data.vars.args !== undefined) - { - data.args = data.vars.args - delete data.vars.args - } - } - thisObj.model.items.justWatch(item_id) - thisObj.model.items[item_id] = data - }, - error:function(e) - { - console.warn(e) - polemarch.showErrors(e) - } - }); -} - -tabSignal.connect("polemarch.start", function() -{ - // tasks - spajs.addMenu({ - id:"PeriodicTasks", - urlregexp:[/^project\/([0-9]+)\/periodic-tasks$/, /^project\/([0-9]+)\/periodic-task$/, /^project\/([0-9]+)\/periodic-tasks\/page\/([0-9]+)$/], - onOpen:function(holder, menuInfo, data){return pmPeriodicTasks.showList(holder, menuInfo, data);} - }) - - spajs.addMenu({ - id:"PeriodicTasks-search", - urlregexp:[/^project\/([0-9]+)\/periodic-tasks\/search\/([A-z0-9 %\-.:,=]+)$/], - onOpen:function(holder, menuInfo, data){return pmPeriodicTasks.showSearchResults(holder, menuInfo, data);} - }) - - spajs.addMenu({ - id:"PeriodicTask", - urlregexp:[/^project\/([0-9]+)\/periodic-task\/([0-9]+)$/], - onOpen:function(holder, menuInfo, data){return pmPeriodicTasks.showItem(holder, menuInfo, data);} - }) - - spajs.addMenu({ - id:"newPeriodicTask", - urlregexp:[/^project\/([0-9]+)\/new-periodic-tasks$/], - onOpen:function(holder, menuInfo, data){return pmPeriodicTasks.showNewItemPage(holder, menuInfo, data);} - }) - + +var pmPeriodicTasks = inheritance(pmItems) + +pmPeriodicTasks.model.page_name = "periodic-task" +pmPeriodicTasks.model.bulk_name = "periodictask" +pmPeriodicTasks.model.name = "periodic-tasks" +pmPeriodicTasks.model.selectedInventory = 0; +pmPeriodicTasks.model.className = "pmPeriodicTasks" + +pmPeriodicTasks.inventoriesAutocompletefiled = new pmInventories.filed.inventoriesAutocomplete() + + +pmPeriodicTasks.copyAndEdit = function(item_id) +{ + if(!item_id) + { + throw "Error in pmPeriodicTasks.copyAndEdit with item_id = `" + item_id + "`" + } + + var def = new $.Deferred(); + var thisObj = this; + return $.when(this.copyItem(item_id)).done(function(newItemId) + { + $.when(spajs.open({ menuId:"project/"+thisObj.model.items[item_id].project+"/"+thisObj.model.page_name + "/"+newItemId})).done(function(){ + $.notify("Item was duplicate", "success"); + def.resolve() + }).fail(function(e){ + $.notify("Error in duplicate item", "error"); + polemarch.showErrors(e) + def.reject(e) + }) + }).fail(function(e){ + def.reject(e) + }) + + return def.promise(); +} + +pmPeriodicTasks.copyItem = function(item_id) +{ + if(!item_id) + { + throw "Error in pmPeriodicTasks.copyItem with item_id = `" + item_id + "`" + } + + var def = new $.Deferred(); + var thisObj = this; + + $.when(this.loadItem(item_id)).done(function() + { + var data = thisObj.model.items[item_id]; + delete data.id; + data.name = "copy from " + data.name + data.vars.group = data.group + data.vars.args = data.args + + delete data.group; + delete data.args; + + $.when(encryptedCopyModal.replace(data)).done(function(data) + { + spajs.ajax.Call({ + url: "/api/v1/"+thisObj.model.name+"/", + type: "POST", + contentType:'application/json', + data: JSON.stringify(data), + success: function(data) + { + thisObj.model.items[data.id] = data + def.resolve(data.id) + }, + error:function(e) + { + def.reject(e) + } + }); + }).fail(function(e) + { + def.reject(e) + }) + + }).fail(function(e) + { + def.reject(e) + }) + + + return def.promise(); +} + + +pmPeriodicTasks.selectInventory = function(inventory_id) +{ + var def = new $.Deferred(); + var thisObj = this; + inventory_id = inventory_id/1 + if(inventory_id) + { + $.when(pmInventories.loadItem(inventory_id)).done(function(){ + thisObj.model.selectedInventory = inventory_id; + def.resolve(); + }).fail(function(e){ + def.reject(e); + }); + } + else + { + thisObj.model.selectedInventory = 0; + def.resolve(); + } + return def.promise() +} + +pmPeriodicTasks.deleteItem = function(item_id, force) +{ + if(!item_id) + { + throw "Error in pmPeriodicTasks.deleteItem with item_id = `" + item_id + "`" + } + + if(!force && !confirm("Are you sure?")) + { + return; + } + + var def = new $.Deferred(); + var thisObj = this; + $.when(this.loadItem(item_id)).done(function() + { + var project_id = pmPeriodicTasks.model.items[item_id].project; + $.when(thisObj.deleteItemQuery(item_id)).done(function(data) + { + $.when(spajs.open({ menuId: "project/"+project_id+"/periodic-tasks"})).done(function() + { + def.resolve() + }).fail(function(e){ + def.reject(e); + polemarch.showErrors(e.responseJSON) + }) + }).fail(function(e){ + def.reject(e); + polemarch.showErrors(e.responseJSON) + }) + }).fail(function(e){ + def.reject(e); + polemarch.showErrors(e.responseJSON) + }) + + return def.promise(); +} + +pmPeriodicTasks.execute = function(project_id, item_id) +{ + var def = new $.Deferred(); + spajs.ajax.Call({ + url: "/api/v1/"+this.model.name+"/" + item_id+"/execute/", + type: "POST", + data:JSON.stringify({}), + contentType:'application/json', + success: function(data) + { + $.notify("Started", "success"); + if(data && data.history_id) + { + $.when(spajs.open({ menuId:"project/"+project_id+"/history/"+data.history_id}) ).done(function(){ + def.resolve() + }).fail(function(e){ + def.reject(e) + }) + } + else + { + def.reject({text:"No history_id", status:500}) + } + }, + error:function(e) + { + def.reject(e) + polemarch.showErrors(e.responseJSON) + } + }) + + return def.promise(); +} + +/** + * Выделеть всё или снять выделение + * @param {boolean} mode + * @param {integer} project_id проект для которого с тасками работаем. + * @returns {promise} + */ +pmPeriodicTasks.toggleSelectEachItem = function(mode, project_id) +{ + var thisObj = this; + return $.when(this.searchItems(project_id, 'project')).done(function() + { + var delta = 0; + for(var i in thisObj.model.itemslist.results) + { + var item_id = thisObj.model.itemslist.results[i].id + + if(thisObj.model.selectedItems[item_id] != mode) + { + if(mode) + { + delta++ + } + else + { + delta-- + } + } + thisObj.model.selectedItems[item_id] = mode + } + thisObj.model.selectedCount += delta + + if(thisObj.model.selectedCount < 0) + { + thisObj.model.selectedCount = 0; + } + + }).promise() +} + +pmPeriodicTasks.showList = function(holder, menuInfo, data) +{ + var thisObj = this; + var offset = 0 + var limit = this.pageSize; + if(data.reg && data.reg[2] > 0) + { + offset = this.pageSize*(data.reg[2] - 1); + } + var project_id = data.reg[1]; + + return $.when(this.searchItems(project_id, 'project'), pmProjects.loadItem(project_id)).done(function() + { + $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_list', {query:"", project_id:project_id})) + }).fail(function() + { + $.notify("", "error"); + }).promise(); +} + +pmPeriodicTasks.search = function(query, options) +{ + if(this.isEmptySearchQuery(query)) + { + return spajs.open({ menuId:'project/' + options.project_id +"/" + this.model.name, reopen:true}); + } + + return spajs.open({ menuId:'project/' + options.project_id +"/" + this.model.name+"/search/"+this.searchObjectToString(trim(query)), reopen:true}); +} + +pmPeriodicTasks.showSearchResults = function(holder, menuInfo, data) +{ + var thisObj = this; + var project_id = data.reg[1]; + + + var search = this.searchStringToObject(decodeURIComponent(data.reg[2])) + search['project'] = project_id + + return $.when(this.sendSearchQuery(search), pmProjects.loadItem(project_id)).done(function() + { + $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_list', {query:decodeURIComponent(data.reg[2]), project_id:project_id})) + }).fail(function() + { + $.notify("", "error"); + }).promise(); +} + +pmPeriodicTasks.showNewItemPage = function(holder, menuInfo, data) +{ + var project_id = data.reg[1]; + var thisObj = this; + return $.when(pmTasks.searchItems(project_id, "project"), pmProjects.loadItem(project_id), pmInventories.loadAllItems()).done(function() + { + thisObj.model.newitem = {type:'INTERVAL', kind:'PLAYBOOK'} + $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_new_page', {project_id:project_id})) + + $('#new_periodic-tasks_inventory').select2({ width: '100%' }); + + new autoComplete({ + selector: '#new_periodic-tasks_playbook', + minChars: 0, + cache:false, + showByClick:false, + renderItem: function(item, search) + { + return '
    ' + item.playbook + '
    '; + }, + onSelect: function(event, term, item) + { + $("#new_periodic-tasks_playbook").val($(item).text()); + //console.log('onSelect', term, item); + //var value = $(item).attr('data-value'); + }, + source: function(term, response) + { + term = term.toLowerCase(); + + var matches = [] + for(var i in pmTasks.model.items) + { + var val = pmTasks.model.items[i] + if(val.name.toLowerCase().indexOf(term) != -1 && val.project == project_id) + { + matches.push(val) + } + } + response(matches); + } + }); + }).fail(function() + { + $.notify("", "error"); + }).promise(); +} + +/** + * Для ввода пароля + * @type Object + */ +pmPeriodicTasks.filed.selectInventoryKindPlaybookGroupModuleAndTime = inheritance(filedsLib.filed.simpleText) +pmPeriodicTasks.filed.selectInventoryKindPlaybookGroupModuleAndTime.type = 'selectInventoryKindPlaybookGroupModuleAndTime' +pmPeriodicTasks.filed.selectInventoryKindPlaybookGroupModuleAndTime.getValue = function(pmObj, filed){ + return ''; +} + +pmPeriodicTasks.model.page_list = { + short_title: 'Periodic tasks', +} + +pmPeriodicTasks.model.page_item = { + buttons:[ + { + class:'btn btn-primary', + function:function(item_id, opt){ return 'spajs.showLoader('+this.model.className+'.updateItem('+item_id+', {project_id:'+opt.project_id+'})); return false;'}, + title:'Save', + link:function(){ return '#'}, + }, + { + class:'btn btn-warning', + function:function(item_id, opt){ + return "spajs.showLoader(pmPeriodicTasks.execute("+opt.project_id+", "+item_id+")); return false;" + }, + title:'Execute', + link:function(){ return '#'}, + help:'Execute' + }, + { + class:'btn btn-default copy-btn', + function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.copyAndEdit('+item_id+')); return false;'}, + title:'', + link:function(){ return '#'}, + help:'Copy' + }, + { + class:'btn btn-danger danger-right', + function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.deleteItem('+item_id+')); return false;'}, + title:' ', + link:function(){ return '#'}, + }, + ], + sections:[ + function(section, item_id) + { + return spajs.just.render('periodic-tasks_page_vars_section', { + item_id:item_id, + pmObj:this, + PLAYBOOK_VARS: jQuery.extend(true, {}, pmPeriodicTasks.model.items[item_id].vars), + MODULE_VARS: jQuery.extend(true, {}, pmPeriodicTasks.model.items[item_id].vars) + }) + } + ], + title: function(item_id){ + return "Periodic task "+this.model.items[item_id].justText('name') + }, + back_link: function(item_id, opt){ + return polemarch.opt.host + "/?project/" + opt.project_id + "/" + this.model.name; + }, + short_title: function(item_id){ + return this.model.items[item_id].justText('name', function(v){return v.slice(0, 20)}) + }, + fileds:[ + [ + { + filed: new filedsLib.filed.text(), + title:'Name', + name:'name', + placeholder:'Enter template name', + validator:function(value){ + return filedsLib.validator.notEmpty(value, 'Name') + }, + fast_validator:function(value){ return value != '' && value} + }, + { + filed: new filedsLib.filed.boolean(), + title:'Save in history', + name:'save_result', + help:'Save result of task in history', + }, + { + filed: new filedsLib.filed.boolean(), + title:'Enabled', + name:'enabled', + help:'', + }, + ],[ + { + filed: new pmPeriodicTasks.filed.selectInventoryKindPlaybookGroupModuleAndTime(), + name:'project', + }, + ] + ], + onUpdate:function(result) + { + return true; + }, + onBeforeSave:function(data, item_id, opt) + { + if(!opt || !opt.project_id) + { + throw "Error in pmPeriodicTasks.onBeforeSave with opt.project_id is null" + } + + data.project = opt.project_id + + data.type = $("#periodic-tasks_"+item_id+"_type").val() + data.inventory = pmPeriodicTasks.inventoriesAutocompletefiled.getValue() + + data.kind = $("#periodic-tasks_"+item_id+"_kind").val() + + if(!data.inventory) + { + $.notify("Invalid field `inventory` ", "error"); + return false; + } + + + if(data.kind == "MODULE") + { + data.mode = moduleArgsEditor.getSelectedModuleName() + if(!data.mode) + { + $.notify("Module name is empty", "error"); + return false; + } + } + else + { + data.mode = $("#periodic-tasks_"+item_id+"_playbook").val() + if(!data.mode) + { + $.notify("Playbook name is empty", "error"); + return false; + } + } + + if(data.type == "CRONTAB") + { + data.schedule = crontabEditor.getCronString() + } + else + { + data.schedule = $("#periodic-tasks_"+item_id+"_schedule_INTERVAL").val() + if(!data.schedule) + { + $.notify("Invalid field `Interval schedule` ", "error"); + return; + } + } + + data.vars = jsonEditor.jsonEditorGetValues(data.kind) + + if(data.kind == "MODULE") + { + data.vars.group = pmGroups.getGroupsAutocompleteValue() + data.vars.args = moduleArgsEditor.getModuleArgs(); + } + return data; + }, +} + +pmPeriodicTasks.showItem = function(holder, menuInfo, data) +{ + var def = new $.Deferred(); + var thisObj = this; + var item_id = data.reg[2]; + var project_id = data.reg[1]; + + $.when(pmPeriodicTasks.loadItem(item_id), pmTasks.loadAllItems(), pmInventories.loadAllItems(), pmProjects.loadItem(project_id)).done(function() + { + var tpl = thisObj.model.name+'_page' + if(!spajs.just.isTplExists(tpl)) + { + tpl = 'items_page' + } + + $(holder).insertTpl(spajs.just.render(tpl, {item_id:item_id, pmObj:thisObj, opt:{project_id:project_id}})) + pmPeriodicTasks.selectInventory(pmPeriodicTasks.model.items[item_id].inventory) + + $('#periodic-tasks_'+item_id+'_inventory').select2({ width: '100%' }); + + new autoComplete({ + selector: '#periodic-tasks_'+item_id+'_playbook', + minChars: 0, + cache:false, + showByClick:false, + renderItem: function(item, search) + { + return '
    ' + item.name + '.yaml
    '; + }, + onSelect: function(event, term, item) + { + $("#periodic-tasks_"+item_id+"_playbook").val($(item).text()); + //console.log('onSelect', term, item); + //var value = $(item).attr('data-value'); + }, + source: function(term, response) + { + term = term.toLowerCase(); + + var matches = [] + for(var i in pmTasks.model.items) + { + var val = pmTasks.model.items[i] + if(val.name.toLowerCase().indexOf(term) != -1 && val.project == project_id) + { + matches.push(val) + } + } + response(matches); + } + }); + + def.resolve(); + + }).fail(function(e) + { + $.notify("", "error"); + def.reject(e); + }) + + return def.promise() +} + + +/** + * @return $.Deferred + */ +pmPeriodicTasks.addItem = function(project_id) +{ + if(!project_id) + { + throw "Error in pmPeriodicTasks.addItem with project_id = `" + project_id + "`" + } + + var def = new $.Deferred(); + + var data = {} + + data.project = project_id + + data.name = $("#new_periodic-tasks_name").val() + data.type = $("#new_periodic-tasks_type").val() + data.inventory = pmPeriodicTasks.inventoriesAutocompletefiled.getValue() + + if(!data.name) + { + $.notify("Invalid field `name` ", "error"); + def.reject(); + return def.promise(); + } + + if(!data.inventory) + { + $.notify("Invalid field `inventory` ", "error"); + def.reject(); + return def.promise(); + } + + + data.kind = $("#new_periodic-tasks_kind").val() + + if(data.kind == "MODULE") + { + data.mode = moduleArgsEditor.getSelectedModuleName() + if(!data.mode) + { + $.notify("Module name is empty", "error"); + def.reject(); + return def.promise(); + } + } + else + { + data.mode = $("#new_periodic-tasks_playbook").val() + if(!data.mode) + { + $.notify("Playbook name is empty", "error"); + def.reject(); + return def.promise(); + } + } + + if(data.type == "CRONTAB") + { + data.schedule = crontabEditor.getCronString() + } + else + { + data.schedule = $("#new_periodic-tasks_schedule_INTERVAL").val() + if(!data.schedule) + { + $.notify("Invalid field `Interval schedule` ", "error"); + def.reject(); + return def.promise(); + } + } + + data.save_result = $("#new_periodic-tasks_save_result").hasClass('selected') + + data.vars = jsonEditor.jsonEditorGetValues(data.kind) + + if(data.kind == "MODULE") + { + data.vars.group = pmGroups.getGroupsAutocompleteValue() + data.vars.args = moduleArgsEditor.getModuleArgs(); + } + + spajs.ajax.Call({ + url: "/api/v1/"+this.model.name+"/", + type: "POST", + contentType:'application/json', + data: JSON.stringify(data), + success: function(data) + { + $.notify("periodic task created", "success"); + + $.when(spajs.open({ menuId:"project/"+project_id+"/periodic-task/"+data.id})).always(function(){ + def.resolve() + }) + }, + error:function(e) + { + polemarch.showErrors(e.responseJSON) + def.reject(e) + } + }); + return def.promise(); +} + +pmPeriodicTasks.loadItem = function(item_id) +{ + var thisObj = this; + return spajs.ajax.Call({ + url: "/api/v1/"+this.model.name+"/"+item_id+"/", + type: "GET", + contentType:'application/json', + data: "", + success: function(data) + { + if(data.kind == "MODULE") + { + if(data && data.vars && data.vars.group !== undefined) + { + data.group = data.vars.group + delete data.vars.group + } + + if(data && data.vars && data.vars.args !== undefined) + { + data.args = data.vars.args + delete data.vars.args + } + } + thisObj.model.items.justWatch(item_id) + thisObj.model.items[item_id] = data + }, + error:function(e) + { + console.warn(e) + polemarch.showErrors(e) + } + }); +} + +tabSignal.connect("polemarch.start", function() +{ + // tasks + spajs.addMenu({ + id:"PeriodicTasks", + urlregexp:[/^project\/([0-9]+)\/periodic-tasks$/, /^project\/([0-9]+)\/periodic-task$/, /^project\/([0-9]+)\/periodic-tasks\/page\/([0-9]+)$/], + onOpen:function(holder, menuInfo, data){return pmPeriodicTasks.showList(holder, menuInfo, data);} + }) + + spajs.addMenu({ + id:"PeriodicTasks-search", + urlregexp:[/^project\/([0-9]+)\/periodic-tasks\/search\/([A-z0-9 %\-.:,=]+)$/, /^project\/([0-9]+)\/periodic-tasks\/search\/([A-z0-9 %\-.:,=]+)\/page\/([0-9]+)$/], + onOpen:function(holder, menuInfo, data){return pmPeriodicTasks.showSearchResults(holder, menuInfo, data);} + }) + + spajs.addMenu({ + id:"PeriodicTask", + urlregexp:[/^project\/([0-9]+)\/periodic-task\/([0-9]+)$/], + onOpen:function(holder, menuInfo, data){return pmPeriodicTasks.showItem(holder, menuInfo, data);} + }) + + spajs.addMenu({ + id:"newPeriodicTask", + urlregexp:[/^project\/([0-9]+)\/new-periodic-tasks$/], + onOpen:function(holder, menuInfo, data){return pmPeriodicTasks.showNewItemPage(holder, menuInfo, data);} + }) + }) \ No newline at end of file diff --git a/polemarch/static/js/pmProjects.js b/polemarch/static/js/pmProjects.js index c3ca1f3b..b3c92826 100644 --- a/polemarch/static/js/pmProjects.js +++ b/polemarch/static/js/pmProjects.js @@ -1,607 +1,607 @@ - -var pmProjects = inheritance(pmItems) - -pmProjects.model.name = "projects" -pmProjects.model.page_name = "project" -pmProjects.model.className = "pmProjects" -pmProjects.model.bulk_name = "project" - -jsonEditor.options[pmProjects.model.name] = {}; -pmProjects.model.selectedInventory = 0 - -jsonEditor.options[pmProjects.model.name]['repo_password'] = { - type:'password', - help:'Password from repository', - helpcontent:'Password from repository required for GIT' -} - -/** - * Для ввода пароля - * @type Object - */ -pmProjects.filed.selectRepositoryType = inheritance(filedsLib.filed.simpleText) -pmProjects.filed.selectRepositoryType.type = 'selectRepositoryType' -pmProjects.filed.selectRepositoryType.getValue = function(pmObj, filed){ - return ''; -} - - -/** - * Вызывается после загрузки информации об элементе но до его вставки в любые массивы. - * Должна вернуть отредактированый или не изменный элемент - * @param {object} item загруженный с сервера элемента - * @returns {object} обработаный элемент - */ -pmProjects.afterItemLoad = function(item) -{ - if(item.status == "WAIT_SYNC" && item.revision == "ERROR") - { - item.revision = "WAIT SYNC" - } - return item; -} - -pmProjects.inventoriesAutocompletefiled = new pmInventories.filed.inventoriesAutocomplete() - -/** - * Описывает как формировать страницу списка элементов - * @type object - */ -pmProjects.model.page_list = { - // Массив для описания кнопок в верху страницы - buttons:[ - { - class:'btn btn-primary', // Класс - //function:function(){ return "spajs.open({ menuId:'new-"+this.model.page_name+"'}); return false;"}, // То что подставится в шаблон на onclick - onclick:function(){ spajs.open({ menuId:"new-"+this.model.page_name}); return false;}, // Функция вызываемая на onclick - title:'Create', // Текст на кнопке - link:function(){ return '/?new-'+this.model.page_name}, // То что попадёт в href - help:'' // Текст для поля подсказки title - }, - ], - title: "Projects", // Текст заголовка страницы - short_title: "Projects", // Короткий текст заголовка страницы - // Описание полей в списке элементов - fileds:[ - { - title:'Name', // Текст в заголовке - name:'name', // Имя поля в объекте из которого надо взять значение - }, - { - title:'Status', - name:'status', - /** - * Стиль элемента td в таблице - * @param {object} item объект для которого строится строка - * @param {object} opt объект доп параметров переданных в шаблон - * @returns {String} Стиль элемента td в таблице - */ - style:function(item, opt){ return 'style="width: 110px"'}, - /** - * Класс элемента td в таблице - * @param {object} item объект для которого строится строка - * @param {object} opt объект доп параметров переданных в шаблон - * @returns {String} Класс элемента td в таблице - */ - class:function(item, opt) - { - if(!item || !item.id) - { - return 'class="hidden-xs hidden-sm"'; - } - - return 'class="hidden-xs hidden-sm project-status ' - + this.model.items[item.id].justClassName('status', function(v){ return "project-status-"+v})+'"' - }, - /** - * Значение для ячейки в таблице - * @param {object} item объект для которого строится строка - * @param {String} filed_name имя поля - * @param {object} opt объект доп параметров переданных в шаблон - * @returns {String} Значение для ячейки в таблице - */ - value:function(item, filed_name, opt){ - return this.model.items[item.id].justText(filed_name) - }, - } - ], - // Список действий которые можно совершить из страницы просмотра списка - actions:[ - { - /** - * Функция для onclick - * @param {object} item объект для которого строится строка - * @param {object} opt объект доп параметров переданных в шаблон - * @returns {String} Функция для onclick - */ - function:function(item){ return 'spajs.showLoader('+this.model.className+'.syncRepo('+item.id+')); return false;'}, - /** - * Текст заголовка кнопки - */ - title:'Sync', - /** - * Функция для href - * @param {object} item объект для которого строится строка - * @param {object} opt объект доп параметров переданных в шаблон - * @returns {String} Функция для href - */ - link:function(){ return '#'} - }, - { - // separator - }, - { - function:function(item){ return "spajs.open({ menuId:'project/"+item.id+"/playbook/run'}); return false;"}, - title:'Run playbook', - link:function(){ return '#'} - }, - { - function:function(item){ return "spajs.open({ menuId:'project/"+item.id+"/ansible-module/run'}); return false;"}, - title:'Run ansible module', - link:function(){ return '#'} - }, - { - function:function(item){ return "spajs.open({ menuId:'project/"+item.id+"/periodic-tasks'}); return false;"}, - title:'Periodic tasks', - link:function(){ return '#'} - }, - { - function:function(item){ return "spajs.open({ menuId:'project/"+item.id+"/history'}); return false;"}, - title:'History', - link:function(){ return '#'} - }, - { - // separator - }, - { - class:'btn btn-danger', - function:function(item){ return 'spajs.showLoader('+this.model.className+'.deleteItem('+item.id+')); return false;'}, - title:'Delete', - link:function(){ return '#'} - }, - ] -} - -/** - * Описывает как формировать страницу создания элемента - * @type object - */ -pmProjects.model.page_new = { - title: "New project", // Текст заголовка страницы - short_title: "New project", // Короткий текст заголовка страницы - /** - * Содержит массив с массивами описаний полей в списке элементов - * Масиивы представляют собой блоки строк в которые вставляются поля ввода - * @type Array - */ - fileds:[ - [ - /** - * Поле ввода - * @todo В целом в следующем приступе неудержимого рефакторинга будет правильнее - * все параметры кроме filed перенести в конструктор поля filed - * и тогда массив fileds будет содержать только экземпляры filedsLib.filed.* или его наследников - */ - { - filed: new filedsLib.filed.text(), // Объект поля ввода - title:'Name', // Заголовок - name:'name', // Имя поля из которого брать значение - placeholder:'Project name', // Подсказка - help:'', // Подсказка - validator:function(value){ // Функция валидации должна вернуть true или false+вывод сообщения об ошибке - return filedsLib.validator.notEmpty(value, 'Name') - }, - fast_validator:function(value){ return value != '' && value} // Функция быстрой валидации должна вернуть true или false - }, - { - filed: new pmProjects.filed.selectRepositoryType(), - name:'repository', - }, - ] - ], - /** - * Список дополнительных блоков которые надо вставить в страницу - * @type Array - */ - sections:[ - /** - * @returns {String} Текст шаблона для вставки дополнительных блоков в страницу - */ - // function(){ return ''} - ], - /** - * Функция вызываемая до сохранения объекта, должна вернуть объект - * отправляемый на сохранение можно изменённый или вернуть false для того чтоб отменить сохранение. - * @param {object} data объект отправляемый на сохранение - * @returns {object|boolean} - */ - onBeforeSave:function(data) - { - data.repository = $("#new_project_repository").val() - data.vars = { - repo_type:$("#new_project_type").val(), - repo_password:$("#new_project_password").val(), - } - - if(!data.repository) - { - if(data.vars.repo_type == "MANUAL") - { - data.repository = "MANUAL" - } - else - { - $.notify("Invalid value in field `Repository URL`", "error"); - return false; - } - } - - return data; - }, - /** - * Функция вызываемая после сохранения объекта - * @param {object} result - * @returns {function|boolean} - */ - onCreate:function(result) - { - var def = new $.Deferred(); - $.notify("Project created", "success"); - $.when(spajs.open({ menuId:this.model.page_name+"/"+result.id})).always(function(){ - def.resolve() - }) - - return def.promise(); - } -} - - -/** - * Описывает как формировать страницу редактирования элемента - * @type object - */ -pmProjects.model.page_item = { - // Массив для описания кнопок в верху страницы - buttons:[ - { - class:'btn btn-primary', - /** - * @param {Integer} item_id Идентификатор редактируемого элемента - * @returns {String} То что подставится в шаблон на onclick - */ - function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.updateItem('+item_id+')); return false;'}, - title:'Save', - link:function(){ return '#'}, - }, - { - class:'btn btn-warning', - function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.syncRepo('+item_id+')); return false;'}, - title:' Sync', - link:function(){ return '#'}, - help:'Sync' - }, - { - class:'btn btn-info', - function:function(item_id){ return 'return spajs.openURL(this.href);'}, - title:' Run playbook', - link:function(item_id){ return polemarch.opt.host +'/?project/'+ item_id + '/playbook/run'}, - help:'Run playbook' - }, - { - class:'btn btn-info', - function:function(item_id){ return 'return spajs.openURL(this.href);'}, - title:' Run module', - link:function(item_id){ return polemarch.opt.host +'/?project/'+ item_id + '/ansible-module/run'}, - help:'Run module' - }, - { - class:'btn btn-info', - function:function(item_id){ return 'return spajs.openURL(this.href);'}, - title:' Periodic tasks', - link:function(item_id){ return polemarch.opt.host +'/?project/'+ item_id + '/periodic-tasks'}, - help:'Periodic tasks' - }, - { - class:'btn btn-info', - function:function(item_id){ return 'return spajs.openURL(this.href);'}, - title:' History', - link:function(item_id){ return polemarch.opt.host +'/?project/'+ item_id + '/history'}, - help:'history' - }, - { - tpl:function(item_id){ - return spajs.just.render('pmTasksTemplates_btn_importFromFile', {item_id:item_id}) - }, - }, - { - class:'btn btn-danger danger-right', - function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.deleteItem('+item_id+')); return false;'}, - title:' ', - link:function(){ return '#'}, - }, - ], - sections:[], - /** - * @param {Integer} item_id Идентификатор редактируемого элемента - * @returns {String} То что подставится в шаблон на title - */ - title: function(item_id){ - return "Project "+this.model.items[item_id].justText('name') - }, - /** - * @param {Integer} item_id Идентификатор редактируемого элемента - * @returns {String} То что подставится в шаблон на short_title - */ - short_title: function(item_id){ - return "Project "+this.model.items[item_id].justText('name', function(v){return v.slice(0, 20)}) - }, - fileds:[ - [ - { - filed: new filedsLib.filed.text(), - title:'Name', - name:'name', - placeholder:'Enter project name', - validator:function(value){ - return filedsLib.validator.notEmpty(value, 'Name') - }, - fast_validator:function(value){ return value != '' && value} - }, - { - filed: new pmProjects.filed.selectRepositoryType(), - name:'repository', - }, - { - filed: new filedsLib.filed.disabled(), - name:'revision', - title:'Revision', - }, - { - filed: new filedsLib.filed.disabled(), - name:'status', - title:'Status', - }, - ] - ], - /** - * Функция вызываемая после сохранения объекта - * @param {object} result - * @returns {function|boolean} - */ - onUpdate:function(result) - { - return true; - }, - onBeforeSave:function(data, item_id) - { - data.repository = $("#project_"+item_id+"_repository").val() - - data.vars = { - repo_type:$("#project_"+item_id+"_type").val(), - repo_password:$("#project_"+item_id+"_password").val(), - } - - if(!data.repository) - { - if(data.vars.repo_type == "MANUAL") - { - data.repository = "MANUAL" - } - else - { - $.notify("Invalid value in field `Repository URL`", "error"); - return false; - } - } - - delete data.revision - delete data.repository - return data; - }, -} - -pmProjects.startUpdateProjectItem = function(item_id) -{ - var thisObj = this; - if(thisObj.model.items[item_id].status == "WAIT_SYNC" || thisObj.model.items[item_id].status == "SYNC") - { - thisObj.model.updateTimeoutId = setTimeout(function() - { - $.when(thisObj.loadItem(item_id)).always(function() - { - thisObj.startUpdateProjectItem(item_id) - }) - }, 5000) - } -} - -pmProjects.openItem = function(holder, menuInfo, data) -{ - var item_id = data.reg[1] - var def = new $.Deferred(); - $.when(pmProjects.supportedRepos()).always(function() - { - $.when(pmProjects.showItem(holder, menuInfo, data)) .always(function() - { - pmProjects.startUpdateProjectItem(item_id) - def.resolve(); - }) - }).promise(); - - return def.promise(); -} - -pmProjects.openNewItemPage = function(holder, menuInfo, data) -{ - var def = new $.Deferred(); - $.when(pmProjects.supportedRepos()).always(function() - { - $.when(pmProjects.showNewItemPage(holder, menuInfo, data)) .always(function() - { - def.resolve(); - }) - }) - - return def.promise(); -} - -/** - * Берёт данные со страницы "run playbook options" ( /?project/1/playbook/run ) для проекта и запускает выполнение Playbook - * @returns {$.Deferred} - */ -pmProjects.executePlaybook = function(project_id) -{ - var data_vars = jsonEditor.jsonEditorGetValues(); - data_vars.limit = pmGroups.getGroupsAutocompleteValue(); - return pmTasks.execute(project_id, pmProjects.inventoriesAutocompletefiled.getValue(), $('#playbook-autocomplete').val(), data_vars); -} - -/** - * Строит страницу "run playbook options" ( /?project/1/playbook/run ) для проекта - * @returns {$.Deferred} - */ -pmProjects.openRunPlaybookPage = function(holder, menuInfo, data) -{ - var def = new $.Deferred(); - var thisObj = this; - var project_id = data.reg[1] - $.when(pmTasks.searchItems(project_id, "project"), pmProjects.loadItem(project_id), pmInventories.loadAllItems()).done(function(results) - { - $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_run_playbook', {item_id:project_id, query:project_id})) - - //$("#inventories-autocomplete").select2({ width: '100%' }); - - new autoComplete({ - selector: '#playbook-autocomplete', - minChars: 0, - cache:false, - showByClick:false, - menuClass:'playbook-autocomplete', - renderItem: function(item, search) - { - return '
    ' + item.playbook + '
    '; - }, - onSelect: function(event, term, item) - { - $("#playbook-autocomplete").val($(item).text()); - //console.log('onSelect', term, item); - //var value = $(item).attr('data-value'); - }, - source: function(term, response) - { - term = term.toLowerCase(); - - var matches = [] - for(var i in results[0].results) - { - var val = pmTasks.model.itemslist.results[i] - if(val.name.toLowerCase().indexOf(term) != -1 && val.project == project_id && val.name.toLowerCase() != term) - { - matches.push(val) - } - } - - if(matches.length) - { - response(matches); - } - } - }); - - def.resolve(); - }).fail(function(e) - { - def.reject(e); - }) - - return def.promise(); -} - -/** - * @return $.Deferred - */ -pmProjects.syncRepo = function(item_id) -{ - return spajs.ajax.Call({ - url: "/api/v1/projects/"+item_id+"/sync/", - type: "POST", - contentType:'application/json', - success: function(data) - { - $.notify("Send sync query", "success"); - }, - error:function(e) - { - console.warn("project "+item_id+" sync error - " + JSON.stringify(e)); - polemarch.showErrors(e.responseJSON) - } - }); -} - -/** - * @return $.Deferred - */ -pmProjects.supportedRepos = function() -{ - return spajs.ajax.Call({ - url: "/api/v1/projects/supported-repos/", - type: "GET", - contentType:'application/json', - success: function(data) - { - pmProjects.model.supportedRepos = data; - pmProjects.model.repository_type = data[0] - jsonEditor.options['projects'].repo_type = { - type:'select', - options:pmProjects.model.supportedRepos, - required:true, - } - }, - error:function(e) - { - console.warn("supportedRepos error - " + JSON.stringify(e)); - } - }); -} - -tabSignal.connect("polemarch.start", function() -{ - // projects - spajs.addMenu({ - id:"projects", - urlregexp:[/^projects$/, /^projects\/search\/?$/, /^project$/, /^projects\/page\/([0-9]+)$/], - onOpen:function(holder, menuInfo, data){return pmProjects.showUpdatedList(holder, menuInfo, data);}, - onClose:function(){return pmProjects.stopUpdates();}, - }) - - spajs.addMenu({ - id:"projects-search", - urlregexp:[/^projects\/search\/([A-z0-9 %\-.:,=]+)$/], - onOpen:function(holder, menuInfo, data){return pmProjects.showSearchResults(holder, menuInfo, data);} - }) - - spajs.addMenu({ - id:"project", - urlregexp:[/^project\/([0-9]+)$/, /^projects\/([0-9]+)$/], - onOpen:function(holder, menuInfo, data){return pmProjects.openItem(holder, menuInfo, data);}, - onClose:function(){return pmHistory.stopUpdates();}, - }) - - spajs.addMenu({ - id:"newProject", - urlregexp:[/^new-project$/], - onOpen:function(holder, menuInfo, data){return pmProjects.openNewItemPage(holder, menuInfo, data);} - }) - - spajs.addMenu({ - id:"project-run-playbook", - urlregexp:[/^project\/([0-9]+)\/playbook\/run$/], - onOpen:function(holder, menuInfo, data){return pmProjects.openRunPlaybookPage(holder, menuInfo, data);} - }) - - spajs.addMenu({ - id:"project-ansible-module-run", - urlregexp:[/^project\/([0-9]+)\/ansible-module\/run$/], - onOpen:function(holder, menuInfo, data){return pmAnsibleModule.showInProject(holder, menuInfo, data);} - }) + +var pmProjects = inheritance(pmItems) + +pmProjects.model.name = "projects" +pmProjects.model.page_name = "project" +pmProjects.model.className = "pmProjects" +pmProjects.model.bulk_name = "project" + +jsonEditor.options[pmProjects.model.name] = {}; +pmProjects.model.selectedInventory = 0 + +jsonEditor.options[pmProjects.model.name]['repo_password'] = { + type:'password', + help:'Password from repository', + helpcontent:'Password from repository required for GIT' +} + +/** + * Для ввода пароля + * @type Object + */ +pmProjects.filed.selectRepositoryType = inheritance(filedsLib.filed.simpleText) +pmProjects.filed.selectRepositoryType.type = 'selectRepositoryType' +pmProjects.filed.selectRepositoryType.getValue = function(pmObj, filed){ + return ''; +} + + +/** + * Вызывается после загрузки информации об элементе но до его вставки в любые массивы. + * Должна вернуть отредактированый или не изменный элемент + * @param {object} item загруженный с сервера элемента + * @returns {object} обработаный элемент + */ +pmProjects.afterItemLoad = function(item) +{ + if(item.status == "WAIT_SYNC" && item.revision == "ERROR") + { + item.revision = "WAIT SYNC" + } + return item; +} + +pmProjects.inventoriesAutocompletefiled = new pmInventories.filed.inventoriesAutocomplete() + +/** + * Описывает как формировать страницу списка элементов + * @type object + */ +pmProjects.model.page_list = { + // Массив для описания кнопок в верху страницы + buttons:[ + { + class:'btn btn-primary', // Класс + //function:function(){ return "spajs.open({ menuId:'new-"+this.model.page_name+"'}); return false;"}, // То что подставится в шаблон на onclick + onclick:function(){ spajs.open({ menuId:"new-"+this.model.page_name}); return false;}, // Функция вызываемая на onclick + title:'Create', // Текст на кнопке + link:function(){ return '/?new-'+this.model.page_name}, // То что попадёт в href + help:'' // Текст для поля подсказки title + }, + ], + title: "Projects", // Текст заголовка страницы + short_title: "Projects", // Короткий текст заголовка страницы + // Описание полей в списке элементов + fileds:[ + { + title:'Name', // Текст в заголовке + name:'name', // Имя поля в объекте из которого надо взять значение + }, + { + title:'Status', + name:'status', + /** + * Стиль элемента td в таблице + * @param {object} item объект для которого строится строка + * @param {object} opt объект доп параметров переданных в шаблон + * @returns {String} Стиль элемента td в таблице + */ + style:function(item, opt){ return 'style="width: 110px"'}, + /** + * Класс элемента td в таблице + * @param {object} item объект для которого строится строка + * @param {object} opt объект доп параметров переданных в шаблон + * @returns {String} Класс элемента td в таблице + */ + class:function(item, opt) + { + if(!item || !item.id) + { + return 'class="hidden-xs hidden-sm"'; + } + + return 'class="hidden-xs hidden-sm project-status ' + + this.model.items[item.id].justClassName('status', function(v){ return "project-status-"+v})+'"' + }, + /** + * Значение для ячейки в таблице + * @param {object} item объект для которого строится строка + * @param {String} filed_name имя поля + * @param {object} opt объект доп параметров переданных в шаблон + * @returns {String} Значение для ячейки в таблице + */ + value:function(item, filed_name, opt){ + return this.model.items[item.id].justText(filed_name) + }, + } + ], + // Список действий которые можно совершить из страницы просмотра списка + actions:[ + { + /** + * Функция для onclick + * @param {object} item объект для которого строится строка + * @param {object} opt объект доп параметров переданных в шаблон + * @returns {String} Функция для onclick + */ + function:function(item){ return 'spajs.showLoader('+this.model.className+'.syncRepo('+item.id+')); return false;'}, + /** + * Текст заголовка кнопки + */ + title:'Sync', + /** + * Функция для href + * @param {object} item объект для которого строится строка + * @param {object} opt объект доп параметров переданных в шаблон + * @returns {String} Функция для href + */ + link:function(){ return '#'} + }, + { + // separator + }, + { + function:function(item){ return "spajs.open({ menuId:'project/"+item.id+"/playbook/run'}); return false;"}, + title:'Run playbook', + link:function(){ return '#'} + }, + { + function:function(item){ return "spajs.open({ menuId:'project/"+item.id+"/ansible-module/run'}); return false;"}, + title:'Run ansible module', + link:function(){ return '#'} + }, + { + function:function(item){ return "spajs.open({ menuId:'project/"+item.id+"/periodic-tasks'}); return false;"}, + title:'Periodic tasks', + link:function(){ return '#'} + }, + { + function:function(item){ return "spajs.open({ menuId:'project/"+item.id+"/history'}); return false;"}, + title:'History', + link:function(){ return '#'} + }, + { + // separator + }, + { + class:'btn btn-danger', + function:function(item){ return 'spajs.showLoader('+this.model.className+'.deleteItem('+item.id+')); return false;'}, + title:'Delete', + link:function(){ return '#'} + }, + ] +} + +/** + * Описывает как формировать страницу создания элемента + * @type object + */ +pmProjects.model.page_new = { + title: "New project", // Текст заголовка страницы + short_title: "New project", // Короткий текст заголовка страницы + /** + * Содержит массив с массивами описаний полей в списке элементов + * Масиивы представляют собой блоки строк в которые вставляются поля ввода + * @type Array + */ + fileds:[ + [ + /** + * Поле ввода + * @todo В целом в следующем приступе неудержимого рефакторинга будет правильнее + * все параметры кроме filed перенести в конструктор поля filed + * и тогда массив fileds будет содержать только экземпляры filedsLib.filed.* или его наследников + */ + { + filed: new filedsLib.filed.text(), // Объект поля ввода + title:'Name', // Заголовок + name:'name', // Имя поля из которого брать значение + placeholder:'Project name', // Подсказка + help:'', // Подсказка + validator:function(value){ // Функция валидации должна вернуть true или false+вывод сообщения об ошибке + return filedsLib.validator.notEmpty(value, 'Name') + }, + fast_validator:function(value){ return value != '' && value} // Функция быстрой валидации должна вернуть true или false + }, + { + filed: new pmProjects.filed.selectRepositoryType(), + name:'repository', + }, + ] + ], + /** + * Список дополнительных блоков которые надо вставить в страницу + * @type Array + */ + sections:[ + /** + * @returns {String} Текст шаблона для вставки дополнительных блоков в страницу + */ + // function(){ return ''} + ], + /** + * Функция вызываемая до сохранения объекта, должна вернуть объект + * отправляемый на сохранение можно изменённый или вернуть false для того чтоб отменить сохранение. + * @param {object} data объект отправляемый на сохранение + * @returns {object|boolean} + */ + onBeforeSave:function(data) + { + data.repository = $("#new_project_repository").val() + data.vars = { + repo_type:$("#new_project_type").val(), + repo_password:$("#new_project_password").val(), + } + + if(!data.repository) + { + if(data.vars.repo_type == "MANUAL") + { + data.repository = "MANUAL" + } + else + { + $.notify("Invalid value in field `Repository URL`", "error"); + return false; + } + } + + return data; + }, + /** + * Функция вызываемая после сохранения объекта + * @param {object} result + * @returns {function|boolean} + */ + onCreate:function(result) + { + var def = new $.Deferred(); + $.notify("Project created", "success"); + $.when(spajs.open({ menuId:this.model.page_name+"/"+result.id})).always(function(){ + def.resolve() + }) + + return def.promise(); + } +} + + +/** + * Описывает как формировать страницу редактирования элемента + * @type object + */ +pmProjects.model.page_item = { + // Массив для описания кнопок в верху страницы + buttons:[ + { + class:'btn btn-primary', + /** + * @param {Integer} item_id Идентификатор редактируемого элемента + * @returns {String} То что подставится в шаблон на onclick + */ + function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.updateItem('+item_id+')); return false;'}, + title:'Save', + link:function(){ return '#'}, + }, + { + class:'btn btn-warning', + function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.syncRepo('+item_id+')); return false;'}, + title:' Sync', + link:function(){ return '#'}, + help:'Sync' + }, + { + class:'btn btn-info', + function:function(item_id){ return 'return spajs.openURL(this.href);'}, + title:' Run playbook', + link:function(item_id){ return polemarch.opt.host +'/?project/'+ item_id + '/playbook/run'}, + help:'Run playbook' + }, + { + class:'btn btn-info', + function:function(item_id){ return 'return spajs.openURL(this.href);'}, + title:' Run module', + link:function(item_id){ return polemarch.opt.host +'/?project/'+ item_id + '/ansible-module/run'}, + help:'Run module' + }, + { + class:'btn btn-info', + function:function(item_id){ return 'return spajs.openURL(this.href);'}, + title:' Periodic tasks', + link:function(item_id){ return polemarch.opt.host +'/?project/'+ item_id + '/periodic-tasks'}, + help:'Periodic tasks' + }, + { + class:'btn btn-info', + function:function(item_id){ return 'return spajs.openURL(this.href);'}, + title:' History', + link:function(item_id){ return polemarch.opt.host +'/?project/'+ item_id + '/history'}, + help:'history' + }, + { + tpl:function(item_id){ + return spajs.just.render('pmTasksTemplates_btn_importFromFile', {item_id:item_id}) + }, + }, + { + class:'btn btn-danger danger-right', + function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.deleteItem('+item_id+')); return false;'}, + title:' ', + link:function(){ return '#'}, + }, + ], + sections:[], + /** + * @param {Integer} item_id Идентификатор редактируемого элемента + * @returns {String} То что подставится в шаблон на title + */ + title: function(item_id){ + return "Project "+this.model.items[item_id].justText('name') + }, + /** + * @param {Integer} item_id Идентификатор редактируемого элемента + * @returns {String} То что подставится в шаблон на short_title + */ + short_title: function(item_id){ + return "Project "+this.model.items[item_id].justText('name', function(v){return v.slice(0, 20)}) + }, + fileds:[ + [ + { + filed: new filedsLib.filed.text(), + title:'Name', + name:'name', + placeholder:'Enter project name', + validator:function(value){ + return filedsLib.validator.notEmpty(value, 'Name') + }, + fast_validator:function(value){ return value != '' && value} + }, + { + filed: new pmProjects.filed.selectRepositoryType(), + name:'repository', + }, + { + filed: new filedsLib.filed.disabled(), + name:'revision', + title:'Revision', + }, + { + filed: new filedsLib.filed.disabled(), + name:'status', + title:'Status', + }, + ] + ], + /** + * Функция вызываемая после сохранения объекта + * @param {object} result + * @returns {function|boolean} + */ + onUpdate:function(result) + { + return true; + }, + onBeforeSave:function(data, item_id) + { + data.repository = $("#project_"+item_id+"_repository").val() + + data.vars = { + repo_type:$("#project_"+item_id+"_type").val(), + repo_password:$("#project_"+item_id+"_password").val(), + } + + if(!data.repository) + { + if(data.vars.repo_type == "MANUAL") + { + data.repository = "MANUAL" + } + else + { + $.notify("Invalid value in field `Repository URL`", "error"); + return false; + } + } + + delete data.revision + delete data.repository + return data; + }, +} + +pmProjects.startUpdateProjectItem = function(item_id) +{ + var thisObj = this; + if(thisObj.model.items[item_id].status == "WAIT_SYNC" || thisObj.model.items[item_id].status == "SYNC") + { + thisObj.model.updateTimeoutId = setTimeout(function() + { + $.when(thisObj.loadItem(item_id)).always(function() + { + thisObj.startUpdateProjectItem(item_id) + }) + }, 5000) + } +} + +pmProjects.openItem = function(holder, menuInfo, data) +{ + var item_id = data.reg[1] + var def = new $.Deferred(); + $.when(pmProjects.supportedRepos()).always(function() + { + $.when(pmProjects.showItem(holder, menuInfo, data)) .always(function() + { + pmProjects.startUpdateProjectItem(item_id) + def.resolve(); + }) + }).promise(); + + return def.promise(); +} + +pmProjects.openNewItemPage = function(holder, menuInfo, data) +{ + var def = new $.Deferred(); + $.when(pmProjects.supportedRepos()).always(function() + { + $.when(pmProjects.showNewItemPage(holder, menuInfo, data)) .always(function() + { + def.resolve(); + }) + }) + + return def.promise(); +} + +/** + * Берёт данные со страницы "run playbook options" ( /?project/1/playbook/run ) для проекта и запускает выполнение Playbook + * @returns {$.Deferred} + */ +pmProjects.executePlaybook = function(project_id) +{ + var data_vars = jsonEditor.jsonEditorGetValues(); + data_vars.limit = pmGroups.getGroupsAutocompleteValue(); + return pmTasks.execute(project_id, pmProjects.inventoriesAutocompletefiled.getValue(), $('#playbook-autocomplete').val(), data_vars); +} + +/** + * Строит страницу "run playbook options" ( /?project/1/playbook/run ) для проекта + * @returns {$.Deferred} + */ +pmProjects.openRunPlaybookPage = function(holder, menuInfo, data) +{ + var def = new $.Deferred(); + var thisObj = this; + var project_id = data.reg[1] + $.when(pmTasks.searchItems(project_id, "project"), pmProjects.loadItem(project_id), pmInventories.loadAllItems()).done(function(results) + { + $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_run_playbook', {item_id:project_id, query:project_id})) + + //$("#inventories-autocomplete").select2({ width: '100%' }); + + new autoComplete({ + selector: '#playbook-autocomplete', + minChars: 0, + cache:false, + showByClick:false, + menuClass:'playbook-autocomplete', + renderItem: function(item, search) + { + return '
    ' + item.playbook + '
    '; + }, + onSelect: function(event, term, item) + { + $("#playbook-autocomplete").val($(item).text()); + //console.log('onSelect', term, item); + //var value = $(item).attr('data-value'); + }, + source: function(term, response) + { + term = term.toLowerCase(); + + var matches = [] + for(var i in results[0].results) + { + var val = pmTasks.model.itemslist.results[i] + if(val.name.toLowerCase().indexOf(term) != -1 && val.project == project_id && val.name.toLowerCase() != term) + { + matches.push(val) + } + } + + if(matches.length) + { + response(matches); + } + } + }); + + def.resolve(); + }).fail(function(e) + { + def.reject(e); + }) + + return def.promise(); +} + +/** + * @return $.Deferred + */ +pmProjects.syncRepo = function(item_id) +{ + return spajs.ajax.Call({ + url: "/api/v1/projects/"+item_id+"/sync/", + type: "POST", + contentType:'application/json', + success: function(data) + { + $.notify("Send sync query", "success"); + }, + error:function(e) + { + console.warn("project "+item_id+" sync error - " + JSON.stringify(e)); + polemarch.showErrors(e.responseJSON) + } + }); +} + +/** + * @return $.Deferred + */ +pmProjects.supportedRepos = function() +{ + return spajs.ajax.Call({ + url: "/api/v1/projects/supported-repos/", + type: "GET", + contentType:'application/json', + success: function(data) + { + pmProjects.model.supportedRepos = data; + pmProjects.model.repository_type = data[0] + jsonEditor.options['projects'].repo_type = { + type:'select', + options:pmProjects.model.supportedRepos, + required:true, + } + }, + error:function(e) + { + console.warn("supportedRepos error - " + JSON.stringify(e)); + } + }); +} + +tabSignal.connect("polemarch.start", function() +{ + // projects + spajs.addMenu({ + id:"projects", + urlregexp:[/^projects$/, /^projects\/search\/?$/, /^project$/, /^projects\/page\/([0-9]+)$/], + onOpen:function(holder, menuInfo, data){return pmProjects.showUpdatedList(holder, menuInfo, data);}, + onClose:function(){return pmProjects.stopUpdates();}, + }) + + spajs.addMenu({ + id:"projects-search", + urlregexp:[/^projects\/search\/([A-z0-9 %\-.:,=]+)$/, /^projects\/search\/([A-z0-9 %\-.:,=]+)\/page\/([0-9]+)$/], + onOpen:function(holder, menuInfo, data){return pmProjects.showSearchResults(holder, menuInfo, data);} + }) + + spajs.addMenu({ + id:"project", + urlregexp:[/^project\/([0-9]+)$/, /^projects\/([0-9]+)$/], + onOpen:function(holder, menuInfo, data){return pmProjects.openItem(holder, menuInfo, data);}, + onClose:function(){return pmHistory.stopUpdates();}, + }) + + spajs.addMenu({ + id:"newProject", + urlregexp:[/^new-project$/], + onOpen:function(holder, menuInfo, data){return pmProjects.openNewItemPage(holder, menuInfo, data);} + }) + + spajs.addMenu({ + id:"project-run-playbook", + urlregexp:[/^project\/([0-9]+)\/playbook\/run$/], + onOpen:function(holder, menuInfo, data){return pmProjects.openRunPlaybookPage(holder, menuInfo, data);} + }) + + spajs.addMenu({ + id:"project-ansible-module-run", + urlregexp:[/^project\/([0-9]+)\/ansible-module\/run$/], + onOpen:function(holder, menuInfo, data){return pmAnsibleModule.showInProject(holder, menuInfo, data);} + }) }) \ No newline at end of file diff --git a/polemarch/static/js/pmTasksTemplates.js b/polemarch/static/js/pmTasksTemplates.js index 469c3a29..253ec155 100644 --- a/polemarch/static/js/pmTasksTemplates.js +++ b/polemarch/static/js/pmTasksTemplates.js @@ -1,433 +1,433 @@ - - -var pmTasksTemplates = inheritance(pmTemplates) - - -pmTasksTemplates.model.name = "templates" -pmTasksTemplates.model.page_name = "template" -pmTasksTemplates.model.bulk_name = "template" -pmTasksTemplates.model.className = "pmTasksTemplates" - -// Поддерживаемые kind /api/v1/templates/supported-kinds/ -pmTasksTemplates.model.kind = "Task" -pmTemplates.model.kindObjects[pmTasksTemplates.model.kind] = pmTasksTemplates - -/** - * Для ввода пароля - * @type Object - */ -pmTasksTemplates.filed.selectProjectInventoryAndPlaybook = inheritance(filedsLib.filed.simpleText) -pmTasksTemplates.filed.selectProjectInventoryAndPlaybook.type = 'selectProjectInventoryAndPlaybook' -pmTasksTemplates.filed.selectProjectInventoryAndPlaybook.getValue = function(pmObj, filed){ - return ''; -} - - -/** - * Функция для рендера текстового поля - * @type Object - */ -pmTasksTemplates.filed.selectProjectInventoryAndPlaybook.render = function(pmObj, filed, item_id){ - var html = spajs.just.render('filed_type_'+this.type, {pmObj:pmObj, filed:filed, item_id:item_id}) - return spajs.just.onInsert(html, function() - { - $("#inventories-autocomplete").select2({ width: '100%' }); - $("#projects-autocomplete").select2({ width: '100%' }); - - new autoComplete({ - selector: '#playbook-autocomplete', - minChars: 0, - cache:false, - showByClick:false, - menuClass:'playbook-autocomplete', - renderItem: function(item, search) - { - return '
    ' + item.playbook + '
    '; - }, - onSelect: function(event, term, item) - { - $("#playbook-autocomplete").val($(item).text()); - //console.log('onSelect', term, item); - //var value = $(item).attr('data-value'); - }, - source: function(term, response) - { - term = term.toLowerCase(); - - var matches = [] - for(var i in pmTasks.model.items) - { - var val = pmTasks.model.items[i] - if(val.name.toLowerCase().indexOf(term) != -1 && thisObj.model.selectedProject == val.project) - { - matches.push(val) - } - } - if(matches.length) - { - response(matches); - } - } - }); - - }) -} - - // Export all selected templates - -pmTasksTemplates.model.page_list = { - buttons:[ - { - class:'btn btn-primary', - function:function(){ return "spajs.open({ menuId:'template/new-task'}); return false;"}, - title:'Create task template', - link:function(){ return '/?template/new-task'}, - }, - { - class:'btn btn-primary', - function:function(){ return "spajs.open({ menuId:'template/new-module'}); return false;"}, - title:'Create module template', - link:function(){ return '/?template/new-module'}, - }, - ], - actionsOnSelected:[ - {}, - { - class:'btn btn-primary', - function:function(){ return "pmTasksTemplates.exportSelecedToFile(); return false;"}, - title:'Export all selected templates', - link:function(){ return '#'}, - }, - ], - title: "Templates", - short_title: "Templates", - fileds:[ - { - title:'Name', - name:'name', - value:function(item) - { - return ''+item.name+''; - } - }, - { - title:'Kind', - name:'kind', - style:function(item){ return 'style="width: 110px"'}, - class:function(item) - { - return 'class="hidden-xs hidden-sm"'; - }, - value:function(item) - { - return item.kind; - } - } - ], - actions:[ - { - class:'btn btn-warning', - function:function(item){ return "spajs.showLoader(pmTemplates.model.kindObjects['"+item.kind+"'].execute("+item.id+")); return false;"}, - title:'Execute', - link:function(){ return '#'} - }, - ] -} - -pmTasksTemplates.model.page_item = { - buttons:[ - { - class:'btn btn-primary', - function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.updateItem('+item_id+')); return false;'}, - title:'Save', - link:function(){ return '#'}, - }, - { - class:'btn btn-warning', - function:function(item_id){ - return "spajs.showLoader("+this.model.className+".saveAndExecute("+item_id+")); return false;" - }, - title:'Save and execute', - link:function(){ return '#'}, - help:'Save and execute' - }, - { - class:'btn btn-default copy-btn', - function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.copyAndEdit('+item_id+')); return false;'}, - title:'', - link:function(){ return '#'}, - help:'Copy' - }, - { - class:'btn btn-danger danger-right', - function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.deleteItem('+item_id+')); return false;'}, - title:' ', - link:function(){ return '#'}, - }, - ], - sections:[ - function(section, item_id){ - return jsonEditor.editor(pmTasksTemplates.model.items[item_id].data.vars, {block:'playbook', title1:'Arguments', title2:'Adding new argument', select2:true}); - } - ], - title: function(item_id){ - return "Task template "+this.model.items[item_id].justText('name') - }, - short_title: function(item_id){ - return this.model.items[item_id].justText('name', function(v){return v.slice(0, 20)}) - }, - fileds:[ - [ - { - filed: new filedsLib.filed.text(), - title:'Name', - name:'name', - placeholder:'Enter template name', - validator:function(value){ - return filedsLib.validator.notEmpty(value, 'Name') - }, - fast_validator:function(value){ return value != '' && value} - }, - // @todo дорефакторить поля ввода - ],[ - { - filed: new pmTasksTemplates.filed.selectProjectInventoryAndPlaybook(), - name:'project', - }, - ] - ], - onUpdate:function(result) - { - return true; - }, - onBeforeSave:function(data, item_id) - { - data.kind = this.model.kind - - data.data = { - inventory:pmTasksTemplates.inventoriesAutocompletefiled.getValue(), - vars:jsonEditor.jsonEditorGetValues() - } - - data.data.playbook = $("#playbook-autocomplete").val() - data.data.project = $("#projects-autocomplete").val()/1 - - return data; - }, -} - -pmTasksTemplates.saveAndExecute = function(item_id) -{ - var def = new $.Deferred(); - $.when(this.updateItem(item_id)).done(function() - { - $.when(pmTasksTemplates.execute(item_id)).always(function(){ - def.resolve(); - }) - }).fail(function(e){ - def.reject(e); - }) - return def.promise() -} - - -pmTasksTemplates.inventoriesAutocompletefiled = new pmInventories.filed.inventoriesAutocomplete() -pmTasksTemplates.showWidget = function(holder, kind) -{ - var thisObj = this; - var offset = 0 - var limit = this.pageSize; - return $.when(this.sendSearchQuery({kind:kind}, limit, offset)).done(function() - { - $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_widget', {query:"", kind:kind})) - }).fail(function() - { - $.notify("", "error"); - }).promise() -} - -pmTasksTemplates.showTaskWidget = function(holder) -{ - return pmTasksTemplates.showWidget(holder, "Task") -} - -pmTasksTemplates.showModuleWidget = function(holder) -{ - return pmTasksTemplates.showWidget(holder, "Module") -} - -pmTasksTemplates.showItem = function(holder, menuInfo, data) -{ - var def = new $.Deferred(); - var thisObj = this; - var item_id = data.reg[1] - $.when(pmProjects.loadAllItems(), pmTasksTemplates.loadItem(item_id), pmInventories.loadAllItems(), pmTasks.loadAllItems()).done(function() - { - thisObj.model.selectedProject == pmTasksTemplates.model.items[item_id].project - - var tpl = thisObj.model.name+'_page' - if(!spajs.just.isTplExists(tpl)) - { - tpl = 'items_page' - } - - $(holder).insertTpl(spajs.just.render(tpl, {item_id:item_id, pmObj:thisObj, opt:{}})) - - def.resolve(); - }).fail(function(e) - { - def.reject(e); - }) - - return def.promise() -} - -pmTasksTemplates.selectProject = function(project_id){ - console.log("select project", project_id) - $(".autocomplete-suggestion").hide() - $(".playbook-project-"+project_id).show() - pmTasksTemplates.model.selectedProject = project_id -} - -pmTasksTemplates.showNewItemPage = function(holder, menuInfo, data) -{ - var def = new $.Deferred(); - var thisObj = this; - $.when(pmProjects.loadAllItems(), pmInventories.loadAllItems(), pmTasks.loadAllItems()).done(function() - { - $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_new_page', {})) - - $("#inventories-autocomplete").select2({ width: '100%' }); - //$("#projects-autocomplete").select2({ width: '100%' }); - - new autoComplete({ - selector: '#playbook-autocomplete', - minChars: 0, - cache:false, - showByClick:false, - menuClass:'playbook-autocomplete', - renderItem: function(item, search) - { - var style = ""; - if(thisObj.model.selectedProject != item.project) - { - style = "style='disolay:none'" - } - return '
    ' + item.playbook + '
    '; - }, - onSelect: function(event, term, item) - { - $("#playbook-autocomplete").val($(item).text()); - //console.log('onSelect', term, item); - //var value = $(item).attr('data-value'); - }, - source: function(term, response) - { - term = term.toLowerCase(); - - var matches = [] - for(var i in pmTasks.model.items) - { - var val = pmTasks.model.items[i] - if(val.name.toLowerCase().indexOf(term) != -1 && thisObj.model.selectedProject == val.project) - { - matches.push(val) - } - } - if(matches.length) - { - response(matches); - } - } - }); - - def.resolve(); - }).fail(function(e) - { - def.reject(e); - }) - - return def.promise() -} - -/** - * @return $.Deferred - * @todo дорефакторить форму создания Task template - */ -pmTasksTemplates.addItem = function() -{ - var def = new $.Deferred(); - var data = {} - - var inventory = pmTasksTemplates.inventoriesAutocompletefiled.getValue() - - data.name = $("#Templates-name").val() - data.kind = this.model.kind - data.data = { - playbook:$("#playbook-autocomplete").val(), - inventory:inventory, - project:$("#projects-autocomplete").val(), - vars:jsonEditor.jsonEditorGetValues() - } - - - if(!data.name) - { - console.warn("Invalid value in field name") - $.notify("Invalid value in field name", "error"); - def.reject("Invalid value in field name") - return def.promise(); - } - - var thisObj = this; - spajs.ajax.Call({ - url: "/api/v1/templates/", - type: "POST", - contentType:'application/json', - data:JSON.stringify(data), - success: function(data) - { - $.notify("template created", "success"); - $.when(spajs.open({ menuId:"template/"+thisObj.model.kind+"/"+data.id})).always(function(){ - def.resolve() - }) - }, - error:function(e) - { - polemarch.showErrors(e.responseJSON) - def.reject(e) - } - }); - - return def.promise(); -} - - -tabSignal.connect("polemarch.start", function() -{ - // Tasks Templates - spajs.addMenu({ - id:"tasks", - urlregexp:[/^templates$/, /^templates\/page\/([0-9]+)$/], - onOpen:function(holder, menuInfo, data){return pmTasksTemplates.showList(holder, menuInfo, data);} - }) - - spajs.addMenu({ - id:"tasks-search", - urlregexp:[/^templates\/search\/([A-z0-9 %\-.:,=]+)$/], - onOpen:function(holder, menuInfo, data){return pmTasksTemplates.showSearchResults(holder, menuInfo, data);} - }) - - spajs.addMenu({ - id:"Task-item", - urlregexp:[/^template\/Task\/([0-9]+)$/, /^templates\/Task\/([0-9]+)$/], - onOpen:function(holder, menuInfo, data){return pmTasksTemplates.showItem(holder, menuInfo, data);}, - }) - - spajs.addMenu({ - id:"task-new", - urlregexp:[/^template\/new-task$/], - onOpen:function(holder, menuInfo, data){return pmTasksTemplates.showNewItemPage(holder, menuInfo, data);} - }) - + + +var pmTasksTemplates = inheritance(pmTemplates) + + +pmTasksTemplates.model.name = "templates" +pmTasksTemplates.model.page_name = "template" +pmTasksTemplates.model.bulk_name = "template" +pmTasksTemplates.model.className = "pmTasksTemplates" + +// Поддерживаемые kind /api/v1/templates/supported-kinds/ +pmTasksTemplates.model.kind = "Task" +pmTemplates.model.kindObjects[pmTasksTemplates.model.kind] = pmTasksTemplates + +/** + * Для ввода пароля + * @type Object + */ +pmTasksTemplates.filed.selectProjectInventoryAndPlaybook = inheritance(filedsLib.filed.simpleText) +pmTasksTemplates.filed.selectProjectInventoryAndPlaybook.type = 'selectProjectInventoryAndPlaybook' +pmTasksTemplates.filed.selectProjectInventoryAndPlaybook.getValue = function(pmObj, filed){ + return ''; +} + + +/** + * Функция для рендера текстового поля + * @type Object + */ +pmTasksTemplates.filed.selectProjectInventoryAndPlaybook.render = function(pmObj, filed, item_id){ + var html = spajs.just.render('filed_type_'+this.type, {pmObj:pmObj, filed:filed, item_id:item_id}) + return spajs.just.onInsert(html, function() + { + $("#inventories-autocomplete").select2({ width: '100%' }); + $("#projects-autocomplete").select2({ width: '100%' }); + + new autoComplete({ + selector: '#playbook-autocomplete', + minChars: 0, + cache:false, + showByClick:false, + menuClass:'playbook-autocomplete', + renderItem: function(item, search) + { + return '
    ' + item.playbook + '
    '; + }, + onSelect: function(event, term, item) + { + $("#playbook-autocomplete").val($(item).text()); + //console.log('onSelect', term, item); + //var value = $(item).attr('data-value'); + }, + source: function(term, response) + { + term = term.toLowerCase(); + + var matches = [] + for(var i in pmTasks.model.items) + { + var val = pmTasks.model.items[i] + if(val.name.toLowerCase().indexOf(term) != -1 && thisObj.model.selectedProject == val.project) + { + matches.push(val) + } + } + if(matches.length) + { + response(matches); + } + } + }); + + }) +} + + // Export all selected templates + +pmTasksTemplates.model.page_list = { + buttons:[ + { + class:'btn btn-primary', + function:function(){ return "spajs.open({ menuId:'template/new-task'}); return false;"}, + title:'Create task template', + link:function(){ return '/?template/new-task'}, + }, + { + class:'btn btn-primary', + function:function(){ return "spajs.open({ menuId:'template/new-module'}); return false;"}, + title:'Create module template', + link:function(){ return '/?template/new-module'}, + }, + ], + actionsOnSelected:[ + {}, + { + class:'btn btn-primary', + function:function(){ return "pmTasksTemplates.exportSelecedToFile(); return false;"}, + title:'Export all selected templates', + link:function(){ return '#'}, + }, + ], + title: "Templates", + short_title: "Templates", + fileds:[ + { + title:'Name', + name:'name', + value:function(item) + { + return ''+item.name+''; + } + }, + { + title:'Kind', + name:'kind', + style:function(item){ return 'style="width: 110px"'}, + class:function(item) + { + return 'class="hidden-xs hidden-sm"'; + }, + value:function(item) + { + return item.kind; + } + } + ], + actions:[ + { + class:'btn btn-warning', + function:function(item){ return "spajs.showLoader(pmTemplates.model.kindObjects['"+item.kind+"'].execute("+item.id+")); return false;"}, + title:'Execute', + link:function(){ return '#'} + }, + ] +} + +pmTasksTemplates.model.page_item = { + buttons:[ + { + class:'btn btn-primary', + function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.updateItem('+item_id+')); return false;'}, + title:'Save', + link:function(){ return '#'}, + }, + { + class:'btn btn-warning', + function:function(item_id){ + return "spajs.showLoader("+this.model.className+".saveAndExecute("+item_id+")); return false;" + }, + title:'Save and execute', + link:function(){ return '#'}, + help:'Save and execute' + }, + { + class:'btn btn-default copy-btn', + function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.copyAndEdit('+item_id+')); return false;'}, + title:'', + link:function(){ return '#'}, + help:'Copy' + }, + { + class:'btn btn-danger danger-right', + function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.deleteItem('+item_id+')); return false;'}, + title:' ', + link:function(){ return '#'}, + }, + ], + sections:[ + function(section, item_id){ + return jsonEditor.editor(pmTasksTemplates.model.items[item_id].data.vars, {block:'playbook', title1:'Arguments', title2:'Adding new argument', select2:true}); + } + ], + title: function(item_id){ + return "Task template "+this.model.items[item_id].justText('name') + }, + short_title: function(item_id){ + return this.model.items[item_id].justText('name', function(v){return v.slice(0, 20)}) + }, + fileds:[ + [ + { + filed: new filedsLib.filed.text(), + title:'Name', + name:'name', + placeholder:'Enter template name', + validator:function(value){ + return filedsLib.validator.notEmpty(value, 'Name') + }, + fast_validator:function(value){ return value != '' && value} + }, + // @todo дорефакторить поля ввода + ],[ + { + filed: new pmTasksTemplates.filed.selectProjectInventoryAndPlaybook(), + name:'project', + }, + ] + ], + onUpdate:function(result) + { + return true; + }, + onBeforeSave:function(data, item_id) + { + data.kind = this.model.kind + + data.data = { + inventory:pmTasksTemplates.inventoriesAutocompletefiled.getValue(), + vars:jsonEditor.jsonEditorGetValues() + } + + data.data.playbook = $("#playbook-autocomplete").val() + data.data.project = $("#projects-autocomplete").val()/1 + + return data; + }, +} + +pmTasksTemplates.saveAndExecute = function(item_id) +{ + var def = new $.Deferred(); + $.when(this.updateItem(item_id)).done(function() + { + $.when(pmTasksTemplates.execute(item_id)).always(function(){ + def.resolve(); + }) + }).fail(function(e){ + def.reject(e); + }) + return def.promise() +} + + +pmTasksTemplates.inventoriesAutocompletefiled = new pmInventories.filed.inventoriesAutocomplete() +pmTasksTemplates.showWidget = function(holder, kind) +{ + var thisObj = this; + var offset = 0 + var limit = this.pageSize; + return $.when(this.sendSearchQuery({kind:kind}, limit, offset)).done(function() + { + $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_widget', {query:"", kind:kind})) + }).fail(function() + { + $.notify("", "error"); + }).promise() +} + +pmTasksTemplates.showTaskWidget = function(holder) +{ + return pmTasksTemplates.showWidget(holder, "Task") +} + +pmTasksTemplates.showModuleWidget = function(holder) +{ + return pmTasksTemplates.showWidget(holder, "Module") +} + +pmTasksTemplates.showItem = function(holder, menuInfo, data) +{ + var def = new $.Deferred(); + var thisObj = this; + var item_id = data.reg[1] + $.when(pmProjects.loadAllItems(), pmTasksTemplates.loadItem(item_id), pmInventories.loadAllItems(), pmTasks.loadAllItems()).done(function() + { + thisObj.model.selectedProject == pmTasksTemplates.model.items[item_id].project + + var tpl = thisObj.model.name+'_page' + if(!spajs.just.isTplExists(tpl)) + { + tpl = 'items_page' + } + + $(holder).insertTpl(spajs.just.render(tpl, {item_id:item_id, pmObj:thisObj, opt:{}})) + + def.resolve(); + }).fail(function(e) + { + def.reject(e); + }) + + return def.promise() +} + +pmTasksTemplates.selectProject = function(project_id){ + console.log("select project", project_id) + $(".autocomplete-suggestion").hide() + $(".playbook-project-"+project_id).show() + pmTasksTemplates.model.selectedProject = project_id +} + +pmTasksTemplates.showNewItemPage = function(holder, menuInfo, data) +{ + var def = new $.Deferred(); + var thisObj = this; + $.when(pmProjects.loadAllItems(), pmInventories.loadAllItems(), pmTasks.loadAllItems()).done(function() + { + $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_new_page', {})) + + $("#inventories-autocomplete").select2({ width: '100%' }); + //$("#projects-autocomplete").select2({ width: '100%' }); + + new autoComplete({ + selector: '#playbook-autocomplete', + minChars: 0, + cache:false, + showByClick:false, + menuClass:'playbook-autocomplete', + renderItem: function(item, search) + { + var style = ""; + if(thisObj.model.selectedProject != item.project) + { + style = "style='disolay:none'" + } + return '
    ' + item.playbook + '
    '; + }, + onSelect: function(event, term, item) + { + $("#playbook-autocomplete").val($(item).text()); + //console.log('onSelect', term, item); + //var value = $(item).attr('data-value'); + }, + source: function(term, response) + { + term = term.toLowerCase(); + + var matches = [] + for(var i in pmTasks.model.items) + { + var val = pmTasks.model.items[i] + if(val.name.toLowerCase().indexOf(term) != -1 && thisObj.model.selectedProject == val.project) + { + matches.push(val) + } + } + if(matches.length) + { + response(matches); + } + } + }); + + def.resolve(); + }).fail(function(e) + { + def.reject(e); + }) + + return def.promise() +} + +/** + * @return $.Deferred + * @todo дорефакторить форму создания Task template + */ +pmTasksTemplates.addItem = function() +{ + var def = new $.Deferred(); + var data = {} + + var inventory = pmTasksTemplates.inventoriesAutocompletefiled.getValue() + + data.name = $("#Templates-name").val() + data.kind = this.model.kind + data.data = { + playbook:$("#playbook-autocomplete").val(), + inventory:inventory, + project:$("#projects-autocomplete").val(), + vars:jsonEditor.jsonEditorGetValues() + } + + + if(!data.name) + { + console.warn("Invalid value in field name") + $.notify("Invalid value in field name", "error"); + def.reject("Invalid value in field name") + return def.promise(); + } + + var thisObj = this; + spajs.ajax.Call({ + url: "/api/v1/templates/", + type: "POST", + contentType:'application/json', + data:JSON.stringify(data), + success: function(data) + { + $.notify("template created", "success"); + $.when(spajs.open({ menuId:"template/"+thisObj.model.kind+"/"+data.id})).always(function(){ + def.resolve() + }) + }, + error:function(e) + { + polemarch.showErrors(e.responseJSON) + def.reject(e) + } + }); + + return def.promise(); +} + + +tabSignal.connect("polemarch.start", function() +{ + // Tasks Templates + spajs.addMenu({ + id:"tasks", + urlregexp:[/^templates$/, /^templates\/page\/([0-9]+)$/], + onOpen:function(holder, menuInfo, data){return pmTasksTemplates.showList(holder, menuInfo, data);} + }) + + spajs.addMenu({ + id:"tasks-search", + urlregexp:[/^templates\/search\/([A-z0-9 %\-.:,=]+)$/, /^templates\/search\/([A-z0-9 %\-.:,=]+)\/page\/([0-9]+)$/], + onOpen:function(holder, menuInfo, data){return pmTasksTemplates.showSearchResults(holder, menuInfo, data);} + }) + + spajs.addMenu({ + id:"Task-item", + urlregexp:[/^template\/Task\/([0-9]+)$/, /^templates\/Task\/([0-9]+)$/], + onOpen:function(holder, menuInfo, data){return pmTasksTemplates.showItem(holder, menuInfo, data);}, + }) + + spajs.addMenu({ + id:"task-new", + urlregexp:[/^template\/new-task$/], + onOpen:function(holder, menuInfo, data){return pmTasksTemplates.showNewItemPage(holder, menuInfo, data);} + }) + }) \ No newline at end of file diff --git a/polemarch/static/js/pmUsers.js b/polemarch/static/js/pmUsers.js index b7a714f4..0fa63c58 100644 --- a/polemarch/static/js/pmUsers.js +++ b/polemarch/static/js/pmUsers.js @@ -1,273 +1,273 @@ - -var pmUsers = inheritance(pmItems) - -pmUsers.model.name = "users" -pmUsers.model.page_name = "user" -pmUsers.model.className = "pmUsers" - -pmUsers.model.page_list = { - buttons:[ - { - class:'btn btn-primary', - function:function(){ return "spajs.open({ menuId:'new-"+this.model.page_name+"'}); return false;"}, - title:'Create', - link:function(){ return '/?new-'+this.model.page_name}, - }, - ], - title: "Users", - short_title: "Users", - fileds:[ - { - title:'Name', - name:'username', - } - ], - actions:[ - { - class:'btn btn-danger', - function:function(item){ return 'spajs.showLoader('+this.model.className+'.deleteItem('+item.id+')); return false;'}, - title:'Delete', - link:function(){ return '#'} - } - ] -} - -pmUsers.model.page_new = { - title: "New user", - short_title: "New user", - fileds:[ - [ - { - filed: new filedsLib.filed.text(), - title:'User name', - name:'username', - placeholder:'Enter user name', - help:'', - validator:function(value){ - return filedsLib.validator.notEmpty(value, 'Name') - }, - fast_validator:function(value){ return value != '' && value} - }, - { - filed: new filedsLib.filed.password(), - title:'Password', - name:'password', - placeholder:'Enter user password', - help:'', - validator:function(value){ - return filedsLib.validator.notEmpty(value, 'Password') - }, - fast_validator:function(value){ return value != '' && value} - }, - ],[ - { - filed: new filedsLib.filed.text(), - title:'Email', - name:'email', - placeholder:'Enter user email', - help:'', - }, - { - filed: new filedsLib.filed.text(), - title:'First name', - name:'first_name', - placeholder:'Enter user first name', - help:'', - }, - ],[ - { - filed: new filedsLib.filed.text(), - title:'Last name', - name:'last_name', - placeholder:'Enter user last name', - help:'', - }, - { - filed: new filedsLib.filed.boolean(), - title:'Is active', - name:'is_active', - default:true, - } - ] - ], - onBeforeSave:function(data, item_id) - { - data.is_staff = true - return data; - }, - onCreate:function(result) - { - var def = new $.Deferred(); - $.notify("User created", "success"); - $.when(spajs.open({ menuId:pmUsers.model.page_name+"/"+result.id})).always(function(){ - def.resolve() - }) - - return def.promise(); - } -} - -pmUsers.model.page_item = { - buttons:[ - { - class:'btn btn-primary', - function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.updateItem('+item_id+')); return false;'}, - title:'Save', - link:function(){ return '#'}, - }, - { - class:'btn btn-default copy-btn', - function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.copyAndEdit('+item_id+')); return false;'}, - title:'', - link:function(){ return '#'}, - help:'Copy' - }, - { - class:'btn btn-danger danger-right', - function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.deleteItem('+item_id+')); return false;'}, - title:' ', - link:function(){ return '#'}, - }, - ], - title: function(item_id){ - return "User "+pmUsers.model.items[item_id].justText('username') - }, - short_title: function(item_id){ - return "User "+pmUsers.model.items[item_id].justText('username', function(v){return v.slice(0, 20)}) - }, - fileds:[ - [ - { - filed: new filedsLib.filed.text(), - title:'User name', - name:'username', - placeholder:'Enter user name', - help:'', - validator:function(value){ - return filedsLib.validator.notEmpty(value, 'Name') - }, - fast_validator:function(value){ return value != '' && value} - }, - { - filed: new filedsLib.filed.password(), - title:'Password', - name:'password', - placeholder:'Enter user password', - help:'', - }, - ],[ - { - filed: new filedsLib.filed.text(), - title:'Email', - name:'email', - placeholder:'Enter user email', - help:'', - }, - { - filed: new filedsLib.filed.text(), - title:'First name', - name:'first_name', - placeholder:'Enter user first name', - help:'', - }, - ],[ - { - filed: new filedsLib.filed.text(), - title:'Last name', - name:'last_name', - placeholder:'Enter user last name', - help:'', - }, - { - filed: new filedsLib.filed.boolean(), - title:'Is active', - name:'is_active', - } - ] - ], - onUpdate:function(result) - { - return true; - }, - onBeforeSave:function(data, item_id) - { - if(!data.password) - { - delete data.password - } - data.is_staff = true - - return data; - }, -} - - -pmUsers.copyItem = function(item_id) -{ - var def = new $.Deferred(); - var thisObj = this; - - $.when(this.loadItem(item_id)).done(function() - { - var data = thisObj.model.items[item_id]; - delete data.id; - data.username = "copy-from-" + data.username - - $.when(encryptedCopyModal.replace(data)).done(function(data) - { - spajs.ajax.Call({ - url: "/api/v1/"+thisObj.model.name+"/", - type: "POST", - contentType:'application/json', - data: JSON.stringify(data), - success: function(data) - { - thisObj.model.items[data.id] = data - def.resolve(data.id) - }, - error:function(e) - { - def.reject(e) - } - }); - }).fail(function(e) - { - def.reject(e) - }) - }).fail(function(e) - { - def.reject(e) - }) - - - return def.promise(); -} - - tabSignal.connect("polemarch.start", function() - { - // users - spajs.addMenu({ - id:"users", - urlregexp:[/^users$/, /^user$/, /^users\/search\/?$/, /^users\/page\/([0-9]+)$/], - onOpen:function(holder, menuInfo, data){return pmUsers.showList(holder, menuInfo, data);} - }) - - spajs.addMenu({ - id:"users-search", - urlregexp:[/^users\/search\/([A-z0-9 %\-.:,=]+)$/], - onOpen:function(holder, menuInfo, data){return pmUsers.showSearchResults(holder, menuInfo, data);} - }) - - spajs.addMenu({ - id:"user", - urlregexp:[/^user\/([0-9]+)$/, /^users\/([0-9]+)$/], - onOpen:function(holder, menuInfo, data){return pmUsers.showItem(holder, menuInfo, data);} - }) - - spajs.addMenu({ - id:"newuser", - urlregexp:[/^new-user$/], - onOpen:function(holder, menuInfo, data){return pmUsers.showNewItemPage(holder, menuInfo, data);} - }) - - }) + +var pmUsers = inheritance(pmItems) + +pmUsers.model.name = "users" +pmUsers.model.page_name = "user" +pmUsers.model.className = "pmUsers" + +pmUsers.model.page_list = { + buttons:[ + { + class:'btn btn-primary', + function:function(){ return "spajs.open({ menuId:'new-"+this.model.page_name+"'}); return false;"}, + title:'Create', + link:function(){ return '/?new-'+this.model.page_name}, + }, + ], + title: "Users", + short_title: "Users", + fileds:[ + { + title:'Name', + name:'username', + } + ], + actions:[ + { + class:'btn btn-danger', + function:function(item){ return 'spajs.showLoader('+this.model.className+'.deleteItem('+item.id+')); return false;'}, + title:'Delete', + link:function(){ return '#'} + } + ] +} + +pmUsers.model.page_new = { + title: "New user", + short_title: "New user", + fileds:[ + [ + { + filed: new filedsLib.filed.text(), + title:'User name', + name:'username', + placeholder:'Enter user name', + help:'', + validator:function(value){ + return filedsLib.validator.notEmpty(value, 'Name') + }, + fast_validator:function(value){ return value != '' && value} + }, + { + filed: new filedsLib.filed.password(), + title:'Password', + name:'password', + placeholder:'Enter user password', + help:'', + validator:function(value){ + return filedsLib.validator.notEmpty(value, 'Password') + }, + fast_validator:function(value){ return value != '' && value} + }, + ],[ + { + filed: new filedsLib.filed.text(), + title:'Email', + name:'email', + placeholder:'Enter user email', + help:'', + }, + { + filed: new filedsLib.filed.text(), + title:'First name', + name:'first_name', + placeholder:'Enter user first name', + help:'', + }, + ],[ + { + filed: new filedsLib.filed.text(), + title:'Last name', + name:'last_name', + placeholder:'Enter user last name', + help:'', + }, + { + filed: new filedsLib.filed.boolean(), + title:'Is active', + name:'is_active', + default:true, + } + ] + ], + onBeforeSave:function(data, item_id) + { + data.is_staff = true + return data; + }, + onCreate:function(result) + { + var def = new $.Deferred(); + $.notify("User created", "success"); + $.when(spajs.open({ menuId:pmUsers.model.page_name+"/"+result.id})).always(function(){ + def.resolve() + }) + + return def.promise(); + } +} + +pmUsers.model.page_item = { + buttons:[ + { + class:'btn btn-primary', + function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.updateItem('+item_id+')); return false;'}, + title:'Save', + link:function(){ return '#'}, + }, + { + class:'btn btn-default copy-btn', + function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.copyAndEdit('+item_id+')); return false;'}, + title:'', + link:function(){ return '#'}, + help:'Copy' + }, + { + class:'btn btn-danger danger-right', + function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.deleteItem('+item_id+')); return false;'}, + title:' ', + link:function(){ return '#'}, + }, + ], + title: function(item_id){ + return "User "+pmUsers.model.items[item_id].justText('username') + }, + short_title: function(item_id){ + return "User "+pmUsers.model.items[item_id].justText('username', function(v){return v.slice(0, 20)}) + }, + fileds:[ + [ + { + filed: new filedsLib.filed.text(), + title:'User name', + name:'username', + placeholder:'Enter user name', + help:'', + validator:function(value){ + return filedsLib.validator.notEmpty(value, 'Name') + }, + fast_validator:function(value){ return value != '' && value} + }, + { + filed: new filedsLib.filed.password(), + title:'Password', + name:'password', + placeholder:'Enter user password', + help:'', + }, + ],[ + { + filed: new filedsLib.filed.text(), + title:'Email', + name:'email', + placeholder:'Enter user email', + help:'', + }, + { + filed: new filedsLib.filed.text(), + title:'First name', + name:'first_name', + placeholder:'Enter user first name', + help:'', + }, + ],[ + { + filed: new filedsLib.filed.text(), + title:'Last name', + name:'last_name', + placeholder:'Enter user last name', + help:'', + }, + { + filed: new filedsLib.filed.boolean(), + title:'Is active', + name:'is_active', + } + ] + ], + onUpdate:function(result) + { + return true; + }, + onBeforeSave:function(data, item_id) + { + if(!data.password) + { + delete data.password + } + data.is_staff = true + + return data; + }, +} + + +pmUsers.copyItem = function(item_id) +{ + var def = new $.Deferred(); + var thisObj = this; + + $.when(this.loadItem(item_id)).done(function() + { + var data = thisObj.model.items[item_id]; + delete data.id; + data.username = "copy-from-" + data.username + + $.when(encryptedCopyModal.replace(data)).done(function(data) + { + spajs.ajax.Call({ + url: "/api/v1/"+thisObj.model.name+"/", + type: "POST", + contentType:'application/json', + data: JSON.stringify(data), + success: function(data) + { + thisObj.model.items[data.id] = data + def.resolve(data.id) + }, + error:function(e) + { + def.reject(e) + } + }); + }).fail(function(e) + { + def.reject(e) + }) + }).fail(function(e) + { + def.reject(e) + }) + + + return def.promise(); +} + + tabSignal.connect("polemarch.start", function() + { + // users + spajs.addMenu({ + id:"users", + urlregexp:[/^users$/, /^user$/, /^users\/search\/?$/, /^users\/page\/([0-9]+)$/], + onOpen:function(holder, menuInfo, data){return pmUsers.showList(holder, menuInfo, data);} + }) + + spajs.addMenu({ + id:"users-search", + urlregexp:[/^users\/search\/([A-z0-9 %\-.:,=]+)$/, /^users\/search\/([A-z0-9 %\-.:,=]+)\/page\/([0-9]+)$/], + onOpen:function(holder, menuInfo, data){return pmUsers.showSearchResults(holder, menuInfo, data);} + }) + + spajs.addMenu({ + id:"user", + urlregexp:[/^user\/([0-9]+)$/, /^users\/([0-9]+)$/], + onOpen:function(holder, menuInfo, data){return pmUsers.showItem(holder, menuInfo, data);} + }) + + spajs.addMenu({ + id:"newuser", + urlregexp:[/^new-user$/], + onOpen:function(holder, menuInfo, data){return pmUsers.showNewItemPage(holder, menuInfo, data);} + }) + + }) From 1386e5748d71513c286dbc63096200291de5e72c Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 5 Feb 2018 13:28:08 +1000 Subject: [PATCH 14/52] Different input types for `vars` were added. cloud/polemarchplus#45 --- polemarch/static/js/jsonEditor.js | 502 ++++++++++++------- polemarch/static/js/pmGroups.js | 1 + polemarch/static/js/pmHosts.js | 4 +- polemarch/static/templates/jsonEditor.html | 537 +++++++++++---------- 4 files changed, 602 insertions(+), 442 deletions(-) diff --git a/polemarch/static/js/jsonEditor.js b/polemarch/static/js/jsonEditor.js index d75781d8..a52ffd9e 100644 --- a/polemarch/static/js/jsonEditor.js +++ b/polemarch/static/js/jsonEditor.js @@ -1,5 +1,5 @@ -function jsonEditor(){ +function jsonEditor() { } @@ -17,108 +17,108 @@ jsonEditor.options['item'] = {} //////////////////////////////////////////////// jsonEditor.options['item']['ansible_connection'] = { - type:'text', - help:'Inventory Parameter - ansible_connection', - helpcontent:'Connection type to the host. This can be the name of any of\ + type: 'text', + help: 'Inventory Parameter - ansible_connection', + helpcontent: 'Connection type to the host. This can be the name of any of\ ansible’s connection plugins. SSH protocol types are smart, ssh or\ paramiko. The default is smart. Non-SSH based types are described\ in the next section.' } jsonEditor.options['item']['ansible_host'] = { - type:'text', - help:'Inventory Parameter - ansible_host', - helpcontent:'The name of the host to connect to, if different from the alias you wish to give to it.' + type: 'text', + help: 'Inventory Parameter - ansible_host', + helpcontent: 'The name of the host to connect to, if different from the alias you wish to give to it.' } jsonEditor.options['item']['ansible_port'] = { - type:'text', - help:'Inventory Parameter - ansible_port', - helpcontent:'The ssh port number, if not 22' + type: 'text', + help: 'Inventory Parameter - ansible_port', + helpcontent: 'The ssh port number, if not 22' } jsonEditor.options['item']['ansible_user'] = { - type:'text', - help:'Inventory Parameter - ansible_user', - helpcontent:'The default ssh user name to use.' + type: 'text', + help: 'Inventory Parameter - ansible_user', + helpcontent: 'The default ssh user name to use.' } jsonEditor.options['item']['ansible_ssh_pass'] = { - type:'password', - help:'Inventory Parameter - ansible_ssh_pass', - helpcontent:'The ssh password to use (never store this variable in plain text; always use a vault.)' + type: 'password', + help: 'Inventory Parameter - ansible_ssh_pass', + helpcontent: 'The ssh password to use (never store this variable in plain text; always use a vault.)' } jsonEditor.options['item']['ansible_ssh_private_key_file'] = { - type:'keyfile', - help:'Inventory Parameter - ansible_ssh_private_key_file', - helpcontent:'Private key file used by ssh. Useful if using multiple keys and you don’t want to use SSH agent.' + type: 'keyfile', + help: 'Inventory Parameter - ansible_ssh_private_key_file', + helpcontent: 'Private key file used by ssh. Useful if using multiple keys and you don’t want to use SSH agent.' } jsonEditor.options['item']['ansible_ssh_common_args'] = { - type:'text', - help:'Inventory Parameter - ansible_ssh_common_args', - helpcontent:'This setting is always appended to the default command line for sftp, scp, and ssh. Useful to configure a ProxyCommand for a certain host (or group).' + type: 'text', + help: 'Inventory Parameter - ansible_ssh_common_args', + helpcontent: 'This setting is always appended to the default command line for sftp, scp, and ssh. Useful to configure a ProxyCommand for a certain host (or group).' } jsonEditor.options['item']['ansible_sftp_extra_args'] = { - type:'text', - help:'Inventory Parameter - ansible_sftp_extra_args', - helpcontent:'This setting is always appended to the default sftp command line.' + type: 'text', + help: 'Inventory Parameter - ansible_sftp_extra_args', + helpcontent: 'This setting is always appended to the default sftp command line.' } jsonEditor.options['item']['ansible_scp_extra_args'] = { - type:'text', - help:'Inventory Parameter - ansible_scp_extra_args', - helpcontent:'This setting is always appended to the default scp command line.' + type: 'text', + help: 'Inventory Parameter - ansible_scp_extra_args', + helpcontent: 'This setting is always appended to the default scp command line.' } jsonEditor.options['item']['ansible_ssh_extra_args'] = { - type:'text', - help:'Inventory Parameter - ansible_ssh_extra_args', - helpcontent:'This setting is always appended to the default ssh command line.' + type: 'text', + help: 'Inventory Parameter - ansible_ssh_extra_args', + helpcontent: 'This setting is always appended to the default ssh command line.' } jsonEditor.options['item']['ansible_ssh_pipelining'] = { - type:'text', - help:'Inventory Parameter - ansible_ssh_pipelining', - helpcontent:'Determines whether or not to use SSH pipelining. This can override the pipelining setting in ansible.cfg.' + type: 'text', + help: 'Inventory Parameter - ansible_ssh_pipelining', + helpcontent: 'Determines whether or not to use SSH pipelining. This can override the pipelining setting in ansible.cfg.' } jsonEditor.options['item']['ansible_ssh_executable'] = { - type:'text', - help:'Inventory Parameter - ansible_ssh_executable', - helpcontent:'This setting overrides the default behavior to use the system ssh. This can override the ssh_executable setting in ansible.cfg.' + type: 'text', + help: 'Inventory Parameter - ansible_ssh_executable', + helpcontent: 'This setting overrides the default behavior to use the system ssh. This can override the ssh_executable setting in ansible.cfg.' } jsonEditor.options['item']['ansible_become'] = { - type:'text', - help:'Inventory Parameter - ansible_become', - helpcontent:'Equivalent to ansible_sudo or ansible_su, allows to force privilege escalation' + type: 'text', + help: 'Inventory Parameter - ansible_become', + helpcontent: 'Equivalent to ansible_sudo or ansible_su, allows to force privilege escalation' } jsonEditor.options['item']['ansible_become_method'] = { - type:'text', - help:'Inventory Parameter - ansible_become_method', - helpcontent:'Allows to set privilege escalation method' + type: 'text', + help: 'Inventory Parameter - ansible_become_method', + helpcontent: 'Allows to set privilege escalation method' } jsonEditor.options['item']['ansible_become_user'] = { - type:'text', - help:'Inventory Parameter - ansible_become_user', - helpcontent:'Equivalent to ansible_sudo_user or ansible_su_user, allows to set the user you become through privilege escalation' + type: 'text', + help: 'Inventory Parameter - ansible_become_user', + helpcontent: 'Equivalent to ansible_sudo_user or ansible_su_user, allows to set the user you become through privilege escalation' } jsonEditor.options['item']['ansible_become_pass'] = { - type:'password', - help:'Inventory Parameter - ansible_become_pass', - helpcontent:'Equivalent to ansible_sudo_pass or ansible_su_pass, allows you to set the privilege escalation password (never store this variable in plain text; always use a vault.)' + type: 'password', + help: 'Inventory Parameter - ansible_become_pass', + helpcontent: 'Equivalent to ansible_sudo_pass or ansible_su_pass, allows you to set the privilege escalation password (never store this variable in plain text; always use a vault.)' } jsonEditor.options['item']['ansible_shell_type'] = { - type:'text', - help:'Inventory Parameter - ansible_shell_type', - helpcontent:'The shell type of the target system. You should not use this \n\ + type: 'text', + help: 'Inventory Parameter - ansible_shell_type', + helpcontent: 'The shell type of the target system. You should not use this \n\ setting unless you have set the ansible_shell_executable to a \n\ non-Bourne (sh) compatible shell. By default commands are \n\ formatted using sh-style syntax. Setting this to csh or fish \n\ @@ -127,9 +127,9 @@ jsonEditor.options['item']['ansible_shell_type'] = { } jsonEditor.options['item']['ansible_python_interpreter'] = { - type:'text', - help:'Inventory Parameter - ansible_python_interpreter', - helpcontent:'The target host python path. This is useful for systems with \n\ + type: 'text', + help: 'Inventory Parameter - ansible_python_interpreter', + helpcontent: 'The target host python path. This is useful for systems with \n\ more than one Python or not located at /usr/bin/python such as\n\ *BSD, or where /usr/bin/python is not a 2.X series Python.\n\ We do not use the /usr/bin/env mechanism as that requires the\n\ @@ -139,9 +139,9 @@ jsonEditor.options['item']['ansible_python_interpreter'] = { } jsonEditor.options['item']['ansible_shell_executable'] = { - type:'text', - help:'Inventory Parameter - ansible_shell_executable', - helpcontent:'This sets the shell the ansible controller will use on the \n\ + type: 'text', + help: 'Inventory Parameter - ansible_shell_executable', + helpcontent: 'This sets the shell the ansible controller will use on the \n\ target machine, overrides executable in ansible.cfg which \n\ defaults to /bin/sh. You should really only change it if is not\n\ possible to use /bin/sh (i.e. /bin/sh is not installed on the\n\ @@ -175,24 +175,24 @@ jsonEditor.model.data = {} * @param {Object} opt * @returns {string} текст шаблона формы */ -jsonEditor.editor = function(json, opt) +jsonEditor.editor = function (json, opt) { - if(!opt) + if (!opt) { opt = {} } - if(!opt.title1) + if (!opt.title1) { opt.title1 = 'Variables' } - if(!opt.title2) + if (!opt.title2) { opt.title2 = 'Adding new variable' } - if(!opt.prefix) + if (!opt.prefix) { opt.prefix = 'prefix' } @@ -200,56 +200,56 @@ jsonEditor.editor = function(json, opt) opt.prefix = toIdString(opt.prefix) jsonEditor.model.data[opt.prefix] = json; jsonEditor.model.form[opt.prefix] = { - showImportForm:false + showImportForm: false }; - return spajs.just.render('jsonEditor', {data:json, optionsblock:opt.block, opt:opt}) + return spajs.just.render('jsonEditor', {data: json, optionsblock: opt.block, opt: opt}) } -jsonEditor.jsonEditorScrollTo = function(param_name, prefix) +jsonEditor.jsonEditorScrollTo = function (param_name, prefix) { - if(!prefix) + if (!prefix) { prefix = "prefix" } prefix = toIdString(prefix) - $("body").scrollTo("#json_"+param_name+"_line"+prefix) + $("body").scrollTo("#json_" + param_name + "_line" + prefix) } -jsonEditor.jsonEditorGetValues = function(prefix) +jsonEditor.jsonEditorGetValues = function (prefix) { - if(!prefix) + if (!prefix) { prefix = "prefix" } prefix = toIdString(prefix) - if(jsonEditor.model.data[prefix] === undefined) + if (jsonEditor.model.data[prefix] === undefined) { return {} } - return jsonEditor.model.data[prefix]; + return jsonEditor.model.data[prefix]; } -jsonEditor.jsonEditorRmVar = function(name, prefix) +jsonEditor.jsonEditorRmVar = function (name, prefix) { - if(!prefix) + if (!prefix) { prefix = "prefix" } prefix = toIdString(prefix) - $('#json_'+name+'_line'+prefix+'').remove() - if(!$(".jsonEditor-data"+prefix).length) + $('#json_' + name + '_line' + prefix + '').remove() + if (!$(".jsonEditor-data" + prefix).length) { - $("#jsonEditorVarListHolder"+prefix).hide() + $("#jsonEditorVarListHolder" + prefix).hide() } delete jsonEditor.model.data[prefix][name] - tabSignal.emit(prefix+".jsonEditorUpdate",{name:name, value:undefined, prefix:prefix}) - tabSignal.emit("jsonEditorUpdate",{name:name, value:undefined, prefix:prefix}) + tabSignal.emit(prefix + ".jsonEditorUpdate", {name: name, value: undefined, prefix: prefix}) + tabSignal.emit("jsonEditorUpdate", {name: name, value: undefined, prefix: prefix}) } /** @@ -259,72 +259,72 @@ jsonEditor.jsonEditorRmVar = function(name, prefix) * @param {string} optionsblock * @param {string} prefix */ -jsonEditor.__devAddVar = function(name, value, optionsblock, prefix) +jsonEditor.__devAddVar = function (name, value, optionsblock, prefix) { - if(!prefix) + if (!prefix) { prefix = "prefix" } - if(!optionsblock) + if (!optionsblock) { optionsblock = 'base' } jsonEditor.model.data[prefix][name] = value - $("#jsonEditorVarList"+prefix).appendTpl(spajs.just.render('jsonEditorLine', {name:name, value:value, optionsblock:optionsblock, opt:{prefix:prefix}})) - $("#jsonEditorVarListHolder"+prefix).show() + $("#jsonEditorVarList" + prefix).appendTpl(spajs.just.render('jsonEditorLine', {name: name, value: value, optionsblock: optionsblock, opt: {prefix: prefix}})) + $("#jsonEditorVarListHolder" + prefix).show() - tabSignal.emit(prefix+".jsonEditorUpdate",{name:name, value:value, prefix:prefix}) - tabSignal.emit("jsonEditorUpdate",{name:name, value:value, prefix:prefix}) + tabSignal.emit(prefix + ".jsonEditorUpdate", {name: name, value: value, prefix: prefix}) + tabSignal.emit("jsonEditorUpdate", {name: name, value: value, prefix: prefix}) } /** * Делает импорт переменных из формата инвентория */ -jsonEditor.jsonEditorImportVars = function(optionsblock, prefix, varsText) +jsonEditor.jsonEditorImportVars = function (optionsblock, prefix, varsText) { - if(!prefix) + if (!prefix) { prefix = "prefix" } prefix = toIdString(prefix) - if(!optionsblock) + if (!optionsblock) { optionsblock = 'base' } - if(varsText == undefined) + if (varsText == undefined) { - varsText = $('#new_json_vars'+prefix).val() + varsText = $('#new_json_vars' + prefix).val() } - + var vars = varsText.split(/\n/gm) var varsresult = {} - for(var i in vars) + for (var i in vars) { - if(/^[\s \t]*$/.test(vars[i])) + if (/^[\s \t]*$/.test(vars[i])) { continue; } var res = jsonEditor.parseMonoVarsLine(i, vars[i]) - if(res !== false) + if (res !== false) { - if($("#json_"+res.name.replace(/[^A-z0-9\-]/g, "_").replace(/[\[\]]/gi, "_")+"_value"+prefix).length) + if ($("#json_" + res.name.replace(/[^A-z0-9\-]/g, "_").replace(/[\[\]]/gi, "_") + "_value" + prefix).length) { - $.notify("Var `"+res.name+"` already exists", "error"); + $.notify("Var `" + res.name + "` already exists", "error"); continue; } - if(/^-[A-z0-9]$/.test(res.name)) + if (/^-[A-z0-9]$/.test(res.name)) { - for(var i in jsonEditor.options[optionsblock]) + for (var i in jsonEditor.options[optionsblock]) { - if("-"+jsonEditor.options[optionsblock][i].alias == res.name) + if ("-" + jsonEditor.options[optionsblock][i].alias == res.name) { val.name = i break; @@ -332,10 +332,10 @@ jsonEditor.jsonEditorImportVars = function(optionsblock, prefix, varsText) } } - if(optionsblock && jsonEditor.options[optionsblock] && jsonEditor.options[optionsblock][res.name]) + if (optionsblock && jsonEditor.options[optionsblock] && jsonEditor.options[optionsblock][res.name]) { var optInfo = jsonEditor.options[optionsblock][res.name] - if(optInfo.type == 'error') + if (optInfo.type == 'error') { $.notify("Adding this variable will be the mistake", "error"); continue; @@ -346,24 +346,24 @@ jsonEditor.jsonEditorImportVars = function(optionsblock, prefix, varsText) varsresult[res.name] = res.value } } - $('#new_json_vars'+prefix).val(varsText) + $('#new_json_vars' + prefix).val(varsText) console.log(varsresult) var opt = { - prefix:prefix + prefix: prefix } - for(var i in varsresult) + for (var i in varsresult) { var val = varsresult[i] jsonEditor.model.data[prefix][i] = val - $("#jsonEditorVarList"+prefix).appendTpl(spajs.just.render('jsonEditorLine', {name:i, value:val, optionsblock:optionsblock, opt:opt})) - $("#jsonEditorVarListHolder"+prefix).show() + $("#jsonEditorVarList" + prefix).appendTpl(spajs.just.render('jsonEditorLine', {name: i, value: val, optionsblock: optionsblock, opt: opt})) + $("#jsonEditorVarListHolder" + prefix).show() - tabSignal.emit(prefix+".jsonEditorUpdate",{name:i, value:val, prefix:prefix}) - tabSignal.emit("jsonEditorUpdate",{name:i, value:val, prefix:prefix}) + tabSignal.emit(prefix + ".jsonEditorUpdate", {name: i, value: val, prefix: prefix}) + tabSignal.emit("jsonEditorUpdate", {name: i, value: val, prefix: prefix}) } } @@ -374,33 +374,30 @@ jsonEditor.jsonEditorImportVars = function(optionsblock, prefix, varsText) * Параметры из секции *:vars * Строка где после первого `=` всё остальное значение. */ -jsonEditor.parseMonoVarsLine = function(index, line) +jsonEditor.parseMonoVarsLine = function (index, line) { var vars = {} var param = /^([^=]+)="(.*)"$/.exec(line) - if(param) + if (param) { vars.name = param[1] vars.value = param[2] - } - else + } else { param = /^([^=]+)=(.*)$/.exec(line) - if(param) + if (param) { vars.name = param[1] vars.value = param[2] - } - else + } else { param = /^([^:]+):(.*)$/.exec(line) - if(param) + if (param) { vars.name = param[1] vars.value = param[2] - } - else + } else { return false; //throw "Error in line "+index+" invalid varibles string ("+line+")" @@ -414,39 +411,52 @@ jsonEditor.parseMonoVarsLine = function(index, line) } -jsonEditor.jsonEditorAddVar = function(optionsblock, prefix) +jsonEditor.jsonEditorAddVar = function (optionsblock, prefix) { - if(!prefix) + if (!prefix) { prefix = "prefix" } prefix = toIdString(prefix) - if(!optionsblock) + if (!optionsblock) { optionsblock = 'base' } - var name = $('#new_json_name'+prefix).val() - var value = $('#new_json_value'+prefix).val() + //для autocomplete.js + var name = $('#new_json_name' + prefix).val() + var value = $('#new_json_value' + prefix).val() + // + if (typeof name == 'undefined' || jsonEditor.options[optionsblock].name == undefined) + { + type1 = "text"; + } else + { + var type1 = jsonEditor.options[optionsblock][name].type; + } + // + + //console.log(jsonEditor.options[optionsblock]); + - if(!name) + if (!name) { $.notify("Empty varible name", "error"); return; } - if($("#json_"+name.replace(/[^A-z0-9\-]/g, "_").replace(/[\[\]]/gi, "_")+"_value"+prefix).length) + if ($("#json_" + name.replace(/[^A-z0-9\-]/g, "_").replace(/[\[\]]/gi, "_") + "_value" + prefix).length) { $.notify("This var already exists", "error"); return; } - if(/^-[A-z0-9]$/.test(name)) + if (/^-[A-z0-9]$/.test(name)) { - for(var i in jsonEditor.options[optionsblock]) + for (var i in jsonEditor.options[optionsblock]) { - if("-"+jsonEditor.options[optionsblock][i].alias == name) + if ("-" + jsonEditor.options[optionsblock][i].alias == name) { name = i break; @@ -454,100 +464,137 @@ jsonEditor.jsonEditorAddVar = function(optionsblock, prefix) } } - if(optionsblock && jsonEditor.options[optionsblock] && jsonEditor.options[optionsblock][name]) + if (optionsblock && jsonEditor.options[optionsblock] && jsonEditor.options[optionsblock][name]) { var optInfo = jsonEditor.options[optionsblock][name] - if(optInfo.type == 'error') + if (optInfo.type == 'error') { $.notify("Adding this variable will be the mistake", "error"); return; } } - $('#new_json_name'+prefix).val('') - $('#new_json_value'+prefix).val('') + $('#new_json_name' + prefix).val('') + $('#new_json_value' + prefix).val('') var opt = { - prefix:prefix + prefix: prefix } - - if(optionsblock - && jsonEditor.options[optionsblock] - && jsonEditor.options[optionsblock][name] - && jsonEditor.options[optionsblock][name].type == 'boolean') + + if (optionsblock + && jsonEditor.options[optionsblock] + && jsonEditor.options[optionsblock][name] + && jsonEditor.options[optionsblock][name].type == 'boolean') { value = ""; } - + + removeLoadFileButton(); + jsonEditor.model.data[prefix][name] = value - $("#jsonEditorVarList"+prefix).appendTpl(spajs.just.render('jsonEditorLine', {name:name, value:value, optionsblock:optionsblock, opt:opt})) - $("#jsonEditorVarListHolder"+prefix).show() - - $("#new_json_name"+prefix).trigger('change'); + $("#jsonEditorVarList" + prefix).appendTpl(spajs.just.render('jsonEditorLine', {name: name, value: value, optionsblock: optionsblock, opt: opt})) + $("#jsonEditorVarListHolder" + prefix).show() + + + $("#new_json_name" + prefix).trigger('change'); + + tabSignal.emit(prefix + ".jsonEditorUpdate", {name: name, value: value, prefix: prefix}) + tabSignal.emit("jsonEditorUpdate", {name: name, value: value, prefix: prefix}) - tabSignal.emit(prefix+".jsonEditorUpdate",{name:name, value:value, prefix:prefix}) - tabSignal.emit("jsonEditorUpdate",{name:name, value:value, prefix:prefix}) } -jsonEditor.initAutoComplete = function(optionsblock, prefix) +jsonEditor.initAutoComplete = function (optionsblock, prefix) { - if(!prefix) + if (!prefix) { prefix = "prefix" } prefix = toIdString(prefix) new autoComplete({ - selector: '#new_json_name'+prefix, + selector: '#new_json_name' + prefix, minChars: 0, - cache:false, - showByClick:false, - menuClass:'new_json_name'+prefix, - renderItem: function(item, search) + cache: false, + showByClick: false, + menuClass: 'new_json_name' + prefix, + renderItem: function (item, search) { return '
    ' + item.value + ' - ' + item.help + '
    '; }, - onSelect: function(event, term, item) + onSelect: function (event, term, item) { //console.log('onSelect', term, item); var value = $(item).attr('data-value'); - $("#new_json_name"+prefix).val(value); + $("#new_json_name" + prefix).val(value); + var name = value; + var varType = jsonEditor.options[optionsblock][name].type; + removeLoadFileButton(); + if (varType == "keyfile" || varType == "textfile") + { + var elementVar = '"#new_json_value' + prefix + '"'; + var nameVar = '"ansible_ssh_private_key_file"'; + var prefixVar = '"prefix"'; + var inputVar = "" + + ""; + $("#new_json_value" + prefix).before(inputVar); + } + }, - source: function(term, response) + source: function (term, response) { term = term.toLowerCase(); var matches = [] - for(var i in jsonEditor.options[optionsblock]) + for (var i in jsonEditor.options[optionsblock]) { var val = jsonEditor.options[optionsblock][i] - if(i.toLowerCase().indexOf(term) != -1 - || (val['shortopts'] && val['shortopts'][0] && val['shortopts'][0].toLowerCase().indexOf(term) != -1) ) + if (i.toLowerCase().indexOf(term) != -1 + || (val['shortopts'] && val['shortopts'][0] && val['shortopts'][0].toLowerCase().indexOf(term) != -1)) { val.value = i matches.push(val) } } - if(matches.length) + if (matches.length) { response(matches.sort(jsonEditor.sortFunction)); + } + } + }); + $("#new_json_name" + prefix).change(function () + { + var name = $(this).val(); + if (jsonEditor.options[optionsblock][name] == undefined) { + removeLoadFileButton(); + } else + { + var varType = jsonEditor.options[optionsblock][name].type; + removeLoadFileButton(); + if (varType == "keyfile" || varType == "textfile") + { + var elementVar = '"#new_json_value' + prefix + '"'; + var nameVar = '"ansible_ssh_private_key_file"'; + var prefixVar = '"prefix"'; + var inputVar = "" + + ""; + $("#new_json_value" + prefix).before(inputVar); } } }); } -jsonEditor.sortFunction = function(a, b) +jsonEditor.sortFunction = function (a, b) { a = a.value b = b.value - for(var i in a) + for (var i in a) { - if(b.length <= i) + if (b.length <= i) { return 1; } - if(a.charCodeAt(i) != b.charCodeAt(i)) + if (a.charCodeAt(i) != b.charCodeAt(i)) { return a.charCodeAt(i) - b.charCodeAt(i) } @@ -556,28 +603,27 @@ jsonEditor.sortFunction = function(a, b) return 0; } -jsonEditor.initForm = function(optionsblock, prefix) +jsonEditor.initForm = function (optionsblock, prefix) { - if(!prefix) + if (!prefix) { prefix = "prefix" } prefix = toIdString(prefix) //console.log(optionsblock, jsonEditor.options[optionsblock]) - if(jsonEditor.options[optionsblock]) + if (jsonEditor.options[optionsblock]) { jsonEditor.initAutoComplete(optionsblock, prefix) return; - } - else + } else { return spajs.ajax.Call({ url: "/api/v1/ansible/cli_reference/", type: "GET", - contentType:'application/json', + contentType: 'application/json', data: "", - success: function(data) + success: function (data) { Object.assign(jsonEditor.options, data) jsonEditor.initAutoComplete(optionsblock, prefix) @@ -587,12 +633,12 @@ jsonEditor.initForm = function(optionsblock, prefix) } } -jsonEditor.loadFile = function(event, element, name, prefix) +jsonEditor.loadFile = function (event, element, name, prefix) { console.log("jsonEditor.loadFile", event.target.files) - for(var i=0; i 1024*1024*1) + if (event.target.files[i].size > 1024 * 1024 * 1) { $.notify("File too large", "error"); console.log("File too large " + event.target.files[i].size) @@ -600,15 +646,117 @@ jsonEditor.loadFile = function(event, element, name, prefix) } var reader = new FileReader(); - reader.onload = function(e) + reader.onload = function (e) { $(element)[0].setAttribute("value", e.target.result) $(element).val(e.target.result) - tabSignal.emit(prefix+".jsonEditorUpdate",{name:name, value:e.target.result, prefix:prefix}) - tabSignal.emit("jsonEditorUpdate",{name:name, value:e.target.result, prefix:prefix}) + tabSignal.emit(prefix + ".jsonEditorUpdate", {name: name, value: e.target.result, prefix: prefix}) + tabSignal.emit("jsonEditorUpdate", {name: name, value: e.target.result, prefix: prefix}) } reader.readAsText(event.target.files[i]); return; } +} + +function removeLoadFileButton() +{ + if ($("#loadFileId")) { + $("#loadFileId").remove(); + } +} + +function removeNewJsonValueTeaxarea() +{ + if ($("#new_json_value_block")) { + $("#new_json_value_block").hide(); + } +} + +function addNewJsonValueTeaxarea() +{ + $("#new_json_value_block").show(); +} + +function changeTextareaSettings(element, options, prefix) +{ + var thisElement = $(element); + var thisOptions = options; + var name = thisElement.val(); + var prefix = prefix; + removeLoadFileButton(); + addNewJsonValueTeaxarea(); + if (name != undefined) + { + var inputType = jsonEditor.options[thisOptions][name].type; + console.log(inputType); + var textareaEl = $("#new_json_value" + prefix); + if (inputType == "textfile" || inputType == "keyfile") + { + var elementVar = '"#new_json_value' + prefix + '"'; + var nameVar = '"' + name + '"'; + var prefixVar = '"' + prefix + '"'; + var inputVar = "" + + ""; + $("#new_json_value" + prefix).before(inputVar); + makeNotOnlyNumberInput('new_json_value', prefix); + } else if (inputType == "boolean") + { + removeNewJsonValueTeaxarea(prefix); + makeNotOnlyNumberInput('new_json_value', prefix); + } else if (inputType == "integer") + { + makeNumberInputOnly('new_json_value', prefix); + + } else { + makeNotOnlyNumberInput('new_json_value', prefix); + } + } +} + + +function makeNumberInputOnly(element, prefix) +{ + var element = element; + var prefix = prefix; + $('#' + element + prefix).val(""); + document.getElementById(element + prefix).onkeypress = function (e) + { + e = e || event; + if (e.ctrlKey || e.altKey || e.metaKey) + return; + var chr = getChar(e); + if (chr == null) + return; + if (chr < '0' || chr > '9') { + return false; + } + } + + function getChar(event) + { + if (event.which == null) { + if (event.keyCode < 32) + return null; + return String.fromCharCode(event.keyCode) + } + if (event.which != 0 && event.charCode != 0) { + if (event.which < 32) + return null; + return String.fromCharCode(event.which) + } + return null; + } + +} + + +function makeNotOnlyNumberInput(element, prefix) +{ + var element = element; + var prefix = prefix; + document.getElementById('new_json_value' + prefix).onkeypress = function () + { + //console.log("не integer"); + } } \ No newline at end of file diff --git a/polemarch/static/js/pmGroups.js b/polemarch/static/js/pmGroups.js index 96e02c85..bd9a7534 100644 --- a/polemarch/static/js/pmGroups.js +++ b/polemarch/static/js/pmGroups.js @@ -586,6 +586,7 @@ pmGroups.getGroupsAutocompleteValue = function(prefix) prefix = "prefix" } return $('#groups_autocomplete_filed'+prefix).val() + debugger; } /** diff --git a/polemarch/static/js/pmHosts.js b/polemarch/static/js/pmHosts.js index 086ec525..61be26db 100644 --- a/polemarch/static/js/pmHosts.js +++ b/polemarch/static/js/pmHosts.js @@ -286,4 +286,6 @@ setTimeout(function(){ urlregexp:[/^new-host$/, /^([A-z0-9_]+)\/([0-9]+)\/new-host$/], onOpen:function(holder, menuInfo, data){return pmHosts.showNewItemPage(holder, menuInfo, data);} }) - }) \ No newline at end of file + }) + + //изменение типа input'a на file при выборе \ No newline at end of file diff --git a/polemarch/static/templates/jsonEditor.html b/polemarch/static/templates/jsonEditor.html index b3af2182..857a2054 100644 --- a/polemarch/static/templates/jsonEditor.html +++ b/polemarch/static/templates/jsonEditor.html @@ -1,265 +1,274 @@ - - - - - - + + + \ No newline at end of file From 647b89076b93e453c35e3b41294c27d84eaf9899 Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 5 Feb 2018 16:58:17 +1000 Subject: [PATCH 15/52] Varification of "import template" file was added. cloud/polemarchplus#89 Different input types for `vars` were added.[1]-some bugs with tests were fixed cloud/polemarchplus#45 --- polemarch/static/js/jsonEditor.js | 42 +-- polemarch/static/js/pmTemplates.js | 429 +++++++++++++++-------------- 2 files changed, 250 insertions(+), 221 deletions(-) diff --git a/polemarch/static/js/jsonEditor.js b/polemarch/static/js/jsonEditor.js index a52ffd9e..45c3c3cf 100644 --- a/polemarch/static/js/jsonEditor.js +++ b/polemarch/static/js/jsonEditor.js @@ -427,19 +427,7 @@ jsonEditor.jsonEditorAddVar = function (optionsblock, prefix) //для autocomplete.js var name = $('#new_json_name' + prefix).val() var value = $('#new_json_value' + prefix).val() - // - if (typeof name == 'undefined' || jsonEditor.options[optionsblock].name == undefined) - { - type1 = "text"; - } else - { - var type1 = jsonEditor.options[optionsblock][name].type; - } - // - - //console.log(jsonEditor.options[optionsblock]); - - + if (!name) { $.notify("Empty varible name", "error"); @@ -534,7 +522,7 @@ jsonEditor.initAutoComplete = function (optionsblock, prefix) var elementVar = '"#new_json_value' + prefix + '"'; var nameVar = '"ansible_ssh_private_key_file"'; var prefixVar = '"prefix"'; - var inputVar = "" + + var inputVar = "" + ""; $("#new_json_value" + prefix).before(inputVar); } @@ -575,7 +563,7 @@ jsonEditor.initAutoComplete = function (optionsblock, prefix) var elementVar = '"#new_json_value' + prefix + '"'; var nameVar = '"ansible_ssh_private_key_file"'; var prefixVar = '"prefix"'; - var inputVar = "" + + var inputVar = "" + ""; $("#new_json_value" + prefix).before(inputVar); } @@ -659,6 +647,10 @@ jsonEditor.loadFile = function (event, element, name, prefix) } } +/** + * Функция, убирающая кнопку-инпут для загрузки текстового файла/файла ключа + * при добавлении vars + */ function removeLoadFileButton() { if ($("#loadFileId")) { @@ -666,6 +658,9 @@ function removeLoadFileButton() } } +/** + * Функция, скрывающая textarea, при добавлении vars + */ function removeNewJsonValueTeaxarea() { if ($("#new_json_value_block")) { @@ -673,11 +668,18 @@ function removeNewJsonValueTeaxarea() } } +/** + * Функция, показывающая textarea, при добавлении vars + */ function addNewJsonValueTeaxarea() { $("#new_json_value_block").show(); } +/** + * Функция, изменяющая параметры textarea, в зависимости от типа инпута, + * при добавлении vars + */ function changeTextareaSettings(element, options, prefix) { var thisElement = $(element); @@ -696,7 +698,7 @@ function changeTextareaSettings(element, options, prefix) var elementVar = '"#new_json_value' + prefix + '"'; var nameVar = '"' + name + '"'; var prefixVar = '"' + prefix + '"'; - var inputVar = "" + + var inputVar = "" + ""; $("#new_json_value" + prefix).before(inputVar); makeNotOnlyNumberInput('new_json_value', prefix); @@ -714,7 +716,9 @@ function changeTextareaSettings(element, options, prefix) } } - +/** + * Функция, позволяющая вводить в инпут только цифры + */ function makeNumberInputOnly(element, prefix) { var element = element; @@ -750,7 +754,9 @@ function makeNumberInputOnly(element, prefix) } - +/** + * Функция, снимающая ограничение на ввод только цифр + */ function makeNotOnlyNumberInput(element, prefix) { var element = element; diff --git a/polemarch/static/js/pmTemplates.js b/polemarch/static/js/pmTemplates.js index 6a08ad86..61a4d787 100644 --- a/polemarch/static/js/pmTemplates.js +++ b/polemarch/static/js/pmTemplates.js @@ -1,204 +1,227 @@ - -var pmTemplates = inheritance(pmItems) - -pmTemplates.model.name = "templates" - - -// Поддерживаемые kind /api/v1/templates/supported-kinds/ -pmTemplates.model.kind = "Task,Module" -pmTemplates.model.page_name = "templates" -pmTemplates.model.bulk_name = "template" -pmTemplates.model.className = "pmTemplates" - -pmTemplates.copyAndEdit = function(item_id) -{ - if(!item_id) - { - throw "Error in pmTemplates.copyAndEdit with item_id = `" + item_id + "`" - } - - var def = new $.Deferred(); - var thisObj = this; - return $.when(this.copyItem(item_id)).done(function(newItemId) - { - $.when(spajs.open({ menuId:thisObj.model.page_name + "/"+thisObj.model.items[item_id].kind+"/"+newItemId})).done(function(){ - $.notify("Item was duplicate", "success"); - def.resolve() - }).fail(function(e){ - $.notify("Error in duplicate item", "error"); - polemarch.showErrors(e) - def.reject(e) - }) - }).fail(function(e){ - def.reject(e) - }) - - return def.promise(); -} - -pmTemplates.execute = function(item_id) -{ - var def = new $.Deferred(); - spajs.ajax.Call({ - url: "/api/v1/"+this.model.name+"/" + item_id+"/execute/", - type: "POST", - data:JSON.stringify({}), - contentType:'application/json', - success: function(data) - { - $.notify("Started", "success"); - if(data && data.history_id) - { - $.when(spajs.open({ menuId:"history/"+data.history_id}) ).done(function(){ - def.resolve() - }).fail(function(e){ - def.reject(e) - }) - } - else - { - def.reject({text:"No history_id", status:500}) - } - }, - error:function(e) - { - def.reject(e) - polemarch.showErrors(e.responseJSON) - } - }) - - return def.promise(); -} - - -// Содержит соответсвия разных kind к объектами с ними работающими. -pmTemplates.model.kindObjects = {} - - -pmTemplates.exportToFile = function(item_ids) -{ - var def = new $.Deferred(); - if(!item_ids) - { - $.notify("No data for export", "error"); - def.reject("No data for export"); - return def.promise(); - } - - var data = { - "filter": { - "id__in": item_ids, - }, - } - - var thisObj = this; - spajs.ajax.Call({ - url: "/api/v1/"+this.model.name+"/filter/?detail=1", - type: "POST", - contentType:'application/json', - data:JSON.stringify(data), - success: function(data) - { - var filedata = [] - for(var i in data.results) - { - var val = data.results[i] - delete val['id']; - delete val['url']; - - filedata.push({ - item: thisObj.model.page_name, - data: val - }) - } - - var fileInfo = { - data:filedata, - count:filedata.length, - version:"1" - } - - var textFileAsBlob = new Blob([JSON.stringify(fileInfo)], { - type: 'text/plain' - }); - - var newLink = document.createElement('a') - newLink.href = window.URL.createObjectURL(textFileAsBlob) - newLink.download = thisObj.model.name+"-"+Date()+".json" - newLink.target = "_blanl" - var event = new MouseEvent("click"); - newLink.dispatchEvent(event); - - - def.resolve(); - }, - error:function(e) - { - console.warn(e) - polemarch.showErrors(e) - def.reject(e); - } - }); - - return def.promise(); -} - -pmTemplates.importFromFile = function(files_event, project_id) -{ - var def = new $.Deferred(); - this.model.files = files_event - - for(var i=0; i 1) - { - polemarch.showErrors("Error file version is "+filedata.version) - def.reject({text:"Error file version is "+filedata.version}); - return; - } - - for(var i in filedata.data) - { - var val = filedata.data[i] - val.data.data.project = project_id - val.type = "add" - bulkdata.push(val) - } - console.log(bulkdata) - - spajs.ajax.Call({ - url: "/api/v1/_bulk/", - type: "POST", - contentType:'application/json', - data:JSON.stringify(bulkdata), - success: function(data) - { - def.resolve(); - spajs.openURL(window.location.href); - }, - error:function(e) - { - console.warn(e) - polemarch.showErrors(e) - def.reject(e); - } - }); - }; - })(i); - reader.readAsText(files_event.target.files[i]); - - // Нет поддержки загрузки более одного файла за раз. - break; - } - - return def.promise(); + +var pmTemplates = inheritance(pmItems) + +pmTemplates.model.name = "templates" + + +// Поддерживаемые kind /api/v1/templates/supported-kinds/ +pmTemplates.model.kind = "Task,Module" +pmTemplates.model.page_name = "templates" +pmTemplates.model.bulk_name = "template" +pmTemplates.model.className = "pmTemplates" + +pmTemplates.copyAndEdit = function (item_id) +{ + if (!item_id) + { + throw "Error in pmTemplates.copyAndEdit with item_id = `" + item_id + "`" + } + + var def = new $.Deferred(); + var thisObj = this; + return $.when(this.copyItem(item_id)).done(function (newItemId) + { + $.when(spajs.open({menuId: thisObj.model.page_name + "/" + thisObj.model.items[item_id].kind + "/" + newItemId})).done(function () { + $.notify("Item was duplicate", "success"); + def.resolve() + }).fail(function (e) { + $.notify("Error in duplicate item", "error"); + polemarch.showErrors(e) + def.reject(e) + }) + }).fail(function (e) { + def.reject(e) + }) + + return def.promise(); +} + +pmTemplates.execute = function (item_id) +{ + var def = new $.Deferred(); + spajs.ajax.Call({ + url: "/api/v1/" + this.model.name + "/" + item_id + "/execute/", + type: "POST", + data: JSON.stringify({}), + contentType: 'application/json', + success: function (data) + { + $.notify("Started", "success"); + if (data && data.history_id) + { + $.when(spajs.open({menuId: "history/" + data.history_id})).done(function () { + def.resolve() + }).fail(function (e) { + def.reject(e) + }) + } else + { + def.reject({text: "No history_id", status: 500}) + } + }, + error: function (e) + { + def.reject(e) + polemarch.showErrors(e.responseJSON) + } + }) + + return def.promise(); +} + + +// Содержит соответсвия разных kind к объектами с ними работающими. +pmTemplates.model.kindObjects = {} + + +pmTemplates.exportToFile = function (item_ids) +{ + var def = new $.Deferred(); + if (!item_ids) + { + $.notify("No data for export", "error"); + def.reject("No data for export"); + return def.promise(); + } + + var data = { + "filter": { + "id__in": item_ids, + }, + } + + var thisObj = this; + spajs.ajax.Call({ + url: "/api/v1/" + this.model.name + "/filter/?detail=1", + type: "POST", + contentType: 'application/json', + data: JSON.stringify(data), + success: function (data) + { + var filedata = [] + for (var i in data.results) + { + var val = data.results[i] + delete val['id']; + delete val['url']; + + filedata.push({ + item: thisObj.model.page_name, + data: val + }) + } + + var fileInfo = { + data: filedata, + count: filedata.length, + version: "1" + } + + var textFileAsBlob = new Blob([JSON.stringify(fileInfo)], { + type: 'text/plain' + }); + + var newLink = document.createElement('a') + newLink.href = window.URL.createObjectURL(textFileAsBlob) + newLink.download = thisObj.model.name + "-" + Date() + ".json" + newLink.target = "_blanl" + var event = new MouseEvent("click"); + newLink.dispatchEvent(event); + + + def.resolve(); + }, + error: function (e) + { + console.warn(e) + polemarch.showErrors(e) + def.reject(e); + } + }); + + return def.promise(); +} + +pmTemplates.importFromFile = function (files_event, project_id) +{ + var def = new $.Deferred(); + this.model.files = files_event + + for (var i = 0; i < files_event.target.files.length; i++) + { + var t1 = $(".input-file")[0].files[0]; + var fileParts = t1.name.split("."); + var fileExt = fileParts[fileParts.length - 1].toLowerCase(); + var fileSize = t1.size; + if (fileExt == "txt" || fileExt == "json") + { + if (fileSize <= 2000000) { + var reader = new FileReader(); + reader.onload = (function (index_in_files_array) + { + return function (e) + { + console.log(e) + var bulkdata = [] + try + { + var filedata = JSON.parse(e.target.result); + if (filedata.version / 1 > 1) + { + polemarch.showErrors("Error file version is " + filedata.version) + def.reject({text: "Error file version is " + filedata.version}); + return; + } + + for (var i in filedata.data) + { + var val = filedata.data[i] + val.data.data.project = project_id + val.type = "add" + bulkdata.push(val) + } + console.log(bulkdata) + + spajs.ajax.Call({ + url: "/api/v1/_bulk/", + type: "POST", + contentType: 'application/json', + data: JSON.stringify(bulkdata), + success: function (data) + { + def.resolve(); + $.notify("JSON-file was imported", "success"); + spajs.openURL(window.location.href); + }, + error: function (e) + { + console.warn(e) + polemarch.showErrors(e) + def.reject(e); + } + }); + } catch (e) + { + $.notify("Invalid/incorrect JSON-file", "error"); + def.reject(); + } + }; + })(i); + reader.readAsText(files_event.target.files[i]); + } else + { + $.notify("File's size has to be less, than 2MB", "error"); + def.reject(); + } + + } else + { + $.notify("File has to be .txt or .json format", "error"); + def.reject(); + } + // Нет поддержки загрузки более одного файла за раз. + break; + } + + return def.promise(); } \ No newline at end of file From cf3c5caa6a17043ea60c03514a62a9c75e87ad82 Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 8 Feb 2018 16:08:53 +1000 Subject: [PATCH 16/52] Reload of page#1 after adding /removing tasks on page#2 was added cloud/polemarchplus#95 --- polemarch/static/js/pmHistory.js | 69 +++++++++++++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/polemarch/static/js/pmHistory.js b/polemarch/static/js/pmHistory.js index eedde752..57576c83 100644 --- a/polemarch/static/js/pmHistory.js +++ b/polemarch/static/js/pmHistory.js @@ -407,6 +407,70 @@ pmHistory.sendSearchQuery = function(query, limit, offset) return def.promise(); } +////////////////////////////// +/** +*Функция проверяет, произошло ли изменение в количестве записей в истории. +*Если изменения произошли, то она обновляет соответствующее свойство в объекте this.model +*/ + +pmHistory.ifIncreaseTotalCount = function() +{ + var def = new $.Deferred(); + var thisObj = this; + spajs.ajax.Call({ + url: "/api/v1/history", + type: "GET", + contentType:'application/json', + data: "limit=1&rand="+Math.random(), + success: function(data) + { + var totalCount=data.count; + //console.log("new totalCount="+totalCount, "old totalCount="+thisObj.model.totalCount); + if(thisObj.model.totalCount!=totalCount) + { + thisObj.model.totalCount=totalCount; + def.resolve(); + } + else + { + def.reject(); + } + + }, + error: function (){ + def.reject(); + } + }); + return def.promise(); +} + +/** +*Функция обновляет список записей в истории каждые 5 секунд. +*Если произошли изменения в количестве записей в списке, +*то содержимое страницы обновляется. +*/ + +pmHistory.updateList = function (updated_ids) +{ + var thisObj = this; + return $.when(this.loadItemsByIds(updated_ids)).always(function () + { + if (thisObj.model.updateTimeoutId) + { + clearTimeout(thisObj.model.updateTimeoutId) + } + + thisObj.model.updateTimeoutId = setTimeout(function () { + thisObj.updateList(updated_ids) + }, 5001) + + $.when(pmHistory.ifIncreaseTotalCount()).done(function() { + spajs.openMenuFromUrl(); + }) + }).promise() +} + +////////////////////////////// /** * Обновляет поле модел this.model.itemslist и ложит туда список пользователей @@ -439,7 +503,10 @@ pmHistory.loadItems = function(limit, offset) data.offset = offset thisObj.model.itemslist = data //thisObj.model.items = {} - + ////// + thisObj.model.totalCount=data.count; + //console.log(thisObj.model); + //////// var projects = []; var usersIds = []; var periodicTasks = []; From 26491347c65fb0442abb694b40caf7a59584ff45 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 9 Feb 2018 12:40:31 +1000 Subject: [PATCH 17/52] Using 1 stats query instead of 6-7 queries on Dashboard page was added. cloud/polemarchplus#127 --- polemarch/api/v1/views.py | 1 + polemarch/static/js/pmDashboard.js | 234 +++++++++++++++++------------ 2 files changed, 141 insertions(+), 94 deletions(-) diff --git a/polemarch/api/v1/views.py b/polemarch/api/v1/views.py index d91dbdf9..92cedb53 100644 --- a/polemarch/api/v1/views.py +++ b/polemarch/api/v1/views.py @@ -358,6 +358,7 @@ def list(self, request, *args, **kwargs): # pylint: disable=unused-argument stats = OrderedDict( projects=self._get_count_by_user(serializers.models.Project), + templates=self._get_count_by_user(serializers.models.Template), inventories=self._get_count_by_user(serializers.models.Inventory), groups=self._get_count_by_user(serializers.models.Group), hosts=self._get_count_by_user(serializers.models.Host), diff --git a/polemarch/static/js/pmDashboard.js b/polemarch/static/js/pmDashboard.js index bfadccef..6f73691b 100644 --- a/polemarch/static/js/pmDashboard.js +++ b/polemarch/static/js/pmDashboard.js @@ -17,44 +17,53 @@ pmDashboard.model.count = { history:'-', } +pmDashboard.statsData={ + projects:'-', + inventories:'-', + hosts:'-', + groups:'-', + users:'-', + templates:'-' +} + /** * Двумерный массив с описанием списка отображаемых виджетов в каждой строке - * - * @example + * + * @example * [ * [{ name:'pmwTemplatesCounter', // Имя класса виджета opt:{}, // Опции для виджета }] - ] + ] * * @type Array */ pmDashboard.model.widgets = [ [ { - name:'pmwTemplatesCounter', - opt:{}, + name:'pmwTemplatesCounter', + opt:{}, }, { - name:'pmwProjectsCounter', - opt:{}, + name:'pmwProjectsCounter', + opt:{}, }, { - name:'pmwInventoriesCounter', - opt:{}, + name:'pmwInventoriesCounter', + opt:{}, }, { - name:'pmwHostsCounter', - opt:{}, + name:'pmwHostsCounter', + opt:{}, }, { - name:'pmwGroupsCounter', - opt:{}, + name:'pmwGroupsCounter', + opt:{}, }, { - name:'pmwUsersCounter', - opt:{}, + name:'pmwUsersCounter', + opt:{}, }, /**/ ], ] @@ -66,11 +75,11 @@ pmDashboard.stopUpdates = function() } tabSignal.connect('pmLocalSettings.hideMenu', function(){ - - setTimeout(function(){ - + + setTimeout(function(){ + if(spajs.currentOpenMenu && spajs.currentOpenMenu.id == 'home') - { + { pmDashboard.updateData() } }, 200) @@ -78,79 +87,81 @@ tabSignal.connect('pmLocalSettings.hideMenu', function(){ pmDashboard.updateData = function() { - var thisObj = this + var thisObj = this if(this.model.updateTimeoutId) { clearTimeout(this.model.updateTimeoutId) this.model.updateTimeoutId = undefined } - - var startTime = moment().subtract(14, 'days').format("YYYY-MM-DD")+"T00:00:00.000000Z" - $.when(pmHistory.sendSearchQuery({start_time__gt:startTime})).done(function() - { - tasks_data = {} + + $.when(pmDashboard.loadStats()).done(function() + { + //обновляем счетчики для виджетов + pmwHostsCounter.updateCount(); + pmwTemplatesCounter.updateCount(); + pmwGroupsCounter.updateCount(); + pmwProjectsCounter.updateCount(); + pmwInventoriesCounter.updateCount(); + pmwUsersCounter.updateCount(); + + //строим график + var startTime = moment().subtract(14, 'days').format("YYYY-MM-DD")+"T00:00:00.000000Z" + + tasks_data = {} tasks_data_t = [] - + var time = new Date(startTime) - time = Math.floor(time.getTime()/(1000*3600*24))*3600*1000*24; + time = Math.floor(time.getTime()/(1000*3600*24))*3600*1000*24; for(var i = 0; i< 14; i++) - { + { tasks_data[time] = 0; tasks_data_t.push(time) - time+=(3600*24*1000) + time+=(3600*24*1000) } - - for(var i in pmHistory.model.itemslist.results) + + for(var i in pmDashboard.statsData.jobs.day) { - var val = pmHistory.model.itemslist.results[i] - var time = new Date(val.start_time) + var val = pmDashboard.statsData.jobs.day[i]; + var time = new Date(val.day) time = Math.floor(time.getTime()/(1000*3600*24))*3600*1000*24; - + if(!tasks_data[time]) { - tasks_data[time] = 1 + tasks_data[time] = val.all; tasks_data_t.push(time) } - else - { - tasks_data[time] += 1 - } - } - - //tasks_data_t.sort(function(a, b) { - // return a - b; - //}); + } chart_tasks_start_x = ['time']; chart_tasks_data = ['tasks']; for(var j in tasks_data_t) { - var time = tasks_data_t[j] + var time = tasks_data_t[j] chart_tasks_start_x.push(time/1); - chart_tasks_data.push(tasks_data[time]/1); + chart_tasks_data.push(tasks_data[time]/1); } - + pmDashboard.model.historyChart.load({ columns: [ chart_tasks_start_x,chart_tasks_data ] }); - }) - + }); + this.model.updateTimeoutId = setTimeout(function(){ pmDashboard.updateData() - }, 5001*30) + }, 1000*30) } pmDashboard.open = function(holder, menuInfo, data) { - - var thisObj = this - + + var thisObj = this + // Инициализация всех виджетов на странице for(var i in pmDashboard.model.widgets) - { + { for(var j in pmDashboard.model.widgets[i]) { if(pmDashboard.model.widgets[i][j].widget === undefined) @@ -159,14 +170,14 @@ pmDashboard.open = function(holder, menuInfo, data) } } } - + this.updateData() - $(holder).insertTpl(spajs.just.render('dashboard_page', {})) - + $(holder).insertTpl(spajs.just.render('dashboard_page', {})) + pmTasksTemplates.showTaskWidget($("#pmTasksTemplates-showTaskWidget")); pmTasksTemplates.showModuleWidget($("#pmTasksTemplates-showModuleWidget")); pmAnsibleModule.fastCommandWidget($("#pmAnsibleModule-fastCommandWidget")) - + pmDashboard.model.historyChart = c3.generate({ bindto: '#c3-history-chart', data: { @@ -189,16 +200,43 @@ pmDashboard.open = function(holder, menuInfo, data) } tabSignal.connect("polemarch.start", function() -{ +{ spajs.addMenu({ - id:"home", + id:"home", urlregexp:[/^(home|)$/], onOpen:function(holder, menuInfo, data){return pmDashboard.open(holder, menuInfo, data);}, onClose:function(){return pmDashboard.stopUpdates();}, }) - + }) + +/** + * Функция, отправляющая запрос /api/v1/stats/, + * который дает нам информацию для виджетов класса pmwItemsCounter, + * а также для графика на странице Dashboard. + */ +pmDashboard.loadStats=function() +{ + var limit=1; + var thisObj = this; + return spajs.ajax.Call({ + url: "/api/v1/stats/", + type: "GET", + contentType: 'application/json', + data: "limit=" + encodeURIComponent(limit)+"&rand="+Math.random(), + success: function (data) + { + pmDashboard.statsData=data; + }, + error: function (e) + { + console.warn(e) + polemarch.showErrors(e) + } + }); +} + /** * Базовый класс виджета * @type Object @@ -209,7 +247,7 @@ pmDashboardWidget = { test:1 }, render:function(){ - + }, init:function(opt){ mergeDeep(this.model, opt) @@ -220,62 +258,70 @@ pmDashboardWidget = { * Базовый класс виджета показывающего количество элементов * @type Object */ -var pmwItemsCounter = inheritance(pmDashboardWidget) +var pmwItemsCounter = inheritance(pmDashboardWidget); + +pmwItemsCounter.model.count = '-'; +pmwItemsCounter.model.countObject = pmItems; +pmwItemsCounter.model.nameInStats = ""; -pmwItemsCounter.model.count = '-' -pmwItemsCounter.model.countObject = pmItems pmwItemsCounter.render = function() -{ +{ + var thisObj = this; - var html = spajs.just.render('pmwItemsCounter', {model:this.model}) - return window.JUST.onInsert(html, function() - { - $.when(thisObj.model.countObject.loadItems(1)).done(function() - { - thisObj.model.count = thisObj.model.countObject.model.itemslist.count; - }) - }) -} - + var html = spajs.just.render('pmwItemsCounter', {model:this.model}); + return window.JUST.onInsert(html, function(){}); +} +pmwItemsCounter.updateCount = function() +{ + var thisObj = this; + var statsData=pmDashboard.statsData; + thisObj.model.count=statsData[thisObj.model.nameInStats]; +} + /** * Класс виджета показывающий количество хостов * @type Object */ -var pmwHostsCounter = inheritance(pmwItemsCounter) -pmwHostsCounter.model.countObject = pmHosts - +var pmwHostsCounter = inheritance(pmwItemsCounter); +pmwHostsCounter.model.countObject = pmHosts; +pmwHostsCounter.model.nameInStats = "hosts"; + /** * Класс виджета показывающий количество шаблонов * @type Object */ -var pmwTemplatesCounter = inheritance(pmwItemsCounter) -pmwTemplatesCounter.model.countObject = pmTemplates - +var pmwTemplatesCounter = inheritance(pmwItemsCounter); +pmwTemplatesCounter.model.countObject = pmTemplates; +pmwTemplatesCounter.model.nameInStats = "templates"; + /** * Класс виджета показывающий количество групп * @type Object */ -var pmwGroupsCounter = inheritance(pmwItemsCounter) -pmwGroupsCounter.model.countObject = pmGroups - +var pmwGroupsCounter = inheritance(pmwItemsCounter); +pmwGroupsCounter.model.countObject = pmGroups; +pmwGroupsCounter.model.nameInStats = "groups"; + /** * Класс виджета показывающий количество проектов * @type Object */ -var pmwProjectsCounter = inheritance(pmwItemsCounter) -pmwProjectsCounter.model.countObject = pmProjects - +var pmwProjectsCounter = inheritance(pmwItemsCounter); +pmwProjectsCounter.model.countObject = pmProjects; +pmwProjectsCounter.model.nameInStats = "projects"; + /** * Класс виджета показывающий количество инвенториев * @type Object */ -var pmwInventoriesCounter = inheritance(pmwItemsCounter) -pmwInventoriesCounter.model.countObject = pmInventories - +var pmwInventoriesCounter = inheritance(pmwItemsCounter); +pmwInventoriesCounter.model.countObject = pmInventories; +pmwInventoriesCounter.model.nameInStats = "inventories"; + /** * Класс виджета показывающий количество пользователей * @type Object */ -var pmwUsersCounter = inheritance(pmwItemsCounter) -pmwUsersCounter.model.countObject = pmUsers - \ No newline at end of file +var pmwUsersCounter = inheritance(pmwItemsCounter); +pmwUsersCounter.model.countObject = pmUsers; +pmwUsersCounter.model.nameInStats = "users"; From 9e06de19a0bec1faea46eb13dee35397da9175ce Mon Sep 17 00:00:00 2001 From: Roman Date: Mon, 12 Feb 2018 16:05:15 +1000 Subject: [PATCH 18/52] Error message for adding new subitem was added. cloud/polemarchplus#69 --- polemarch/static/js/pmGroups.js | 28 +-- polemarch/static/js/pmInventories.js | 355 +++++++++++++-------------- polemarch/static/js/pmItems.js | 35 +++ 3 files changed, 211 insertions(+), 207 deletions(-) diff --git a/polemarch/static/js/pmGroups.js b/polemarch/static/js/pmGroups.js index bd9a7534..4fea190c 100644 --- a/polemarch/static/js/pmGroups.js +++ b/polemarch/static/js/pmGroups.js @@ -276,6 +276,7 @@ pmGroups.model.page_item = { */ pmGroups.setSubGroups = function(item_id, groups_ids) { + var thisObj=this; if(!item_id) { throw "Error in pmGroups.setSubGroups with item_id = `" + item_id + "`" @@ -293,18 +294,7 @@ pmGroups.setSubGroups = function(item_id, groups_ids) data:JSON.stringify(groups_ids), success: function(data) { - if(pmGroups.model.items[item_id]) - { - pmGroups.model.items[item_id].groups = [] - for(var i in groups_ids) - { - if(!pmGroups.model.items[groups_ids[i]]) - { - continue; - } - pmGroups.model.items[item_id].groups.push(pmGroups.model.items[groups_ids[i]]) - } - } + pmItems.checkSubItemsAndAdd(thisObj, pmGroups, data, item_id, "groups", groups_ids); //console.log("group update", data); }, error:function(e) @@ -320,6 +310,7 @@ pmGroups.setSubGroups = function(item_id, groups_ids) */ pmGroups.setSubHosts = function(item_id, hosts_ids) { + var thisObj=this; if(!item_id) { throw "Error in pmGroups.setSubHosts with item_id = `" + item_id + "`" @@ -337,18 +328,7 @@ pmGroups.setSubHosts = function(item_id, hosts_ids) data:JSON.stringify(hosts_ids), success: function(data) { - if(pmGroups.model.items[item_id]) - { - pmGroups.model.items[item_id].hosts = [] - for(var i in hosts_ids) - { - if(!pmHosts.model.items[hosts_ids[i]]) - { - continue; - } - pmGroups.model.items[item_id].hosts.push(pmHosts.model.items[hosts_ids[i]]) - } - } + pmItems.checkSubItemsAndAdd(thisObj, pmHosts, data, item_id, "hosts", hosts_ids); //console.log("group update", data); }, error:function(e) diff --git a/polemarch/static/js/pmInventories.js b/polemarch/static/js/pmInventories.js index 5a2d6b28..d590274d 100644 --- a/polemarch/static/js/pmInventories.js +++ b/polemarch/static/js/pmInventories.js @@ -33,13 +33,13 @@ pmInventories.parseMonoVarsLine = function(index, line) } return vars; } - + /** * Параметры хоста * Строка где может быть несколько параметров ключ=значение через пробел */ pmInventories.parseVarsLine = function(index, line) -{ +{ var vars = {} do{ if(line.length == 0) @@ -50,7 +50,7 @@ pmInventories.parseVarsLine = function(index, line) var params = /^([^=]+)=["'](.*?)["'] +[^=]+=/.exec(line) if(params) { - params[1] = trim(params[1]) + params[1] = trim(params[1]) vars[params[1]] = stripslashes(params[2]) line = trim(line.slice(params[1].length + params[2].length + 3)) continue; @@ -59,7 +59,7 @@ pmInventories.parseVarsLine = function(index, line) params = /^([^=]+)=([^ ]*) +[^=]+=/.exec(line) if(params) { - params[1] = trim(params[1]) + params[1] = trim(params[1]) vars[params[1]] = stripslashes(params[2]) line = trim(line.slice(params[1].length + params[2].length + 1)) continue; @@ -68,7 +68,7 @@ pmInventories.parseVarsLine = function(index, line) params = /^([^=]+)=["'](.*?)["'] *$/.exec(line) if(params) { - params[1] = trim(params[1]) + params[1] = trim(params[1]) vars[params[1]] = stripslashes(params[2]) break; } @@ -76,7 +76,7 @@ pmInventories.parseVarsLine = function(index, line) params = /^([^=]+)=([^ ]*) *$/.exec(line) if(params) { - params[1] = trim(params[1]) + params[1] = trim(params[1]) vars[params[1]] = stripslashes(params[2]) line = trim(line.slice(params[1].length + params[2].length + 1)) continue; @@ -127,7 +127,7 @@ pmInventories.parseHostLine = function(index, line, section, inventory) } if(section !== "_hosts") - { + { pmInventories.addGroupIfNotExists(inventory, section) inventory.groups[section].hosts.push(host) } @@ -181,8 +181,8 @@ pmInventories.parseLine = function(index, line, section, inventory) pmInventories.addGroupIfNotExists(inventory, section) inventory.groups[section].children = true - inventory.groups[section].groups.push(line) - pmInventories.addGroupIfNotExists(inventory, line) + inventory.groups[section].groups.push(line) + pmInventories.addGroupIfNotExists(inventory, line) return true; } @@ -197,7 +197,7 @@ pmInventories.parseLine = function(index, line, section, inventory) * @returns {Boolean} true если группа добавлена. */ pmInventories.addGroupIfNotExists = function(inventory, group_name) -{ +{ if(!inventory.groups[group_name]) { inventory.groups[group_name] = { @@ -205,10 +205,10 @@ pmInventories.addGroupIfNotExists = function(inventory, group_name) groups:[], hosts:[], } - + return true; } - + return false; } @@ -228,19 +228,19 @@ pmInventories.parseFromText = function(text) groups:{}, vars:{}, name:new Date().toString() - } + } for(var i in lines) { - var line = lines[i].replace(/^ */g, "") - + var line = lines[i].replace(/^ */g, "") + if(/^\s*[#;]\s+inventory name: (.*)/ig.test(line)) { var name = /^\s*[#;]\s+inventory name: (.*)/ig.exec(line) inventory.name = name[1] continue; } - + if(/^\s*$/ig.test(line)) { continue; @@ -256,7 +256,7 @@ pmInventories.parseFromText = function(text) { var res = /^\[([A-z0-9\.:\-]+)\]/ig.exec(line) cSection = res[1] - + var group_name = cSection.substring(0, cSection.length - ":vars".length) if(group_name != "all") { @@ -305,44 +305,44 @@ pmInventories.addHierarchyDataToInventoryGroups = function(inventory, group_name { level = 0 } - + if(parents === undefined) { parents = [] } - + if(group_name === undefined || group_name == 'all') { for(var i in inventory.groups) - { + { delete inventory.groups[i]['dataLevel'] } - + for(var i in inventory.groups) - { + { pmInventories.addHierarchyDataToInventoryGroups(inventory, i, 1, ['all']) } - + return; } - - + + if(inventory.groups[group_name].dataLevel && inventory.groups[group_name].dataLevel.level >= level ) { return; - } - + } + parents.push(group_name) inventory.groups[group_name].dataLevel = { level:level, parents:parents, } - + for(var i in inventory.groups[group_name].groups) - { + { var hasError = false; for(var j in inventory.groups[group_name].dataLevel.parents) - { + { var val = inventory.groups[group_name].dataLevel.parents[j] if(val == inventory.groups[group_name].groups[i]) { @@ -352,15 +352,15 @@ pmInventories.addHierarchyDataToInventoryGroups = function(inventory, group_name break; } } - + if(hasError) { continue; } - + pmInventories.addHierarchyDataToInventoryGroups(inventory, inventory.groups[group_name].groups[i], level+1, parents.slice()) } - + return; } @@ -398,7 +398,7 @@ pmInventories.openImportPageAndImportFiles = function(files_event) { $.when(spajs.open({ menuId:"inventories/import"})).done(function() { - pmInventories.importFromFile(files_event) + pmInventories.importFromFile(files_event) }) } @@ -413,7 +413,7 @@ pmInventories.importInventoriesAndOpen = function(inventory) spajs.open({ menuId:"inventory/"+inventory_id}) }).fail(function(e){ console.warn(e) - polemarch.showErrors(e) + polemarch.showErrors(e) }).promise() } @@ -423,22 +423,22 @@ pmInventories.showGroupVarsModal = function(opt) } pmInventories.showHostVarsModal = function(opt) -{ +{ return jsonEditor.jsonEditorScrollTo("ansible_ssh_private_key_file", "host"+opt.name) } pmInventories.showInventoryVarsModal = function(opt) -{ +{ return jsonEditor.jsonEditorScrollTo("ansible_ssh_private_key_file", "inventory") } pmInventories.importInventory = function(inventory) -{ - var def2 = new $.Deferred(); +{ + var def2 = new $.Deferred(); if(inventory.vars.ansible_ssh_private_key_file !== undefined && !/-----BEGIN RSA PRIVATE KEY-----/.test(inventory.vars.ansible_ssh_private_key_file)) { // - $.notify("Error in field ansible_ssh_private_key_file invalid value", "error"); + $.notify("Error in field ansible_ssh_private_key_file invalid value", "error"); pmInventories.showInventoryVarsModal(); def2.reject() return def2.promise(); @@ -446,7 +446,7 @@ pmInventories.importInventory = function(inventory) for(var i in inventory.hosts) { - var val = inventory.hosts[i] + var val = inventory.hosts[i] if(val.vars.ansible_ssh_private_key_file !== undefined && !/-----BEGIN RSA PRIVATE KEY-----/.test(val.vars.ansible_ssh_private_key_file)) { // @@ -459,55 +459,55 @@ pmInventories.importInventory = function(inventory) for(var i in inventory.groups) { - var val = inventory.groups[i] + var val = inventory.groups[i] if(val.vars.ansible_ssh_private_key_file !== undefined && !/-----BEGIN RSA PRIVATE KEY-----/.test(val.vars.ansible_ssh_private_key_file)) { // $.notify("Error in field ansible_ssh_private_key_file invalid value", "error"); - pmInventories.showGroupVarsModal({name:i}); + pmInventories.showGroupVarsModal({name:i}); def2.reject() return def2.promise(); } for(var j in val.hosts) { - var hval = val.hosts[j] + var hval = val.hosts[j] if(hval.vars.ansible_ssh_private_key_file !== undefined && !/-----BEGIN RSA PRIVATE KEY-----/.test(hval.vars.ansible_ssh_private_key_file)) { // - $.notify("Error in field ansible_ssh_private_key_file invalid value", "error"); + $.notify("Error in field ansible_ssh_private_key_file invalid value", "error"); pmInventories.showHostVarsModal({group:i, name:hval.name}); def2.reject() return def2.promise(); } } } - + var def = new $.Deferred(); - + if($("#inventory_name").val() != "") { inventory.name = $("#inventory_name").val(); } - + if(!inventory.name) { // inventory.name = "new imported inventory" - + $.notify("Error in field inventory name", "error"); def2.reject({text:"Error in field inventory name"}) - return def2.promise(); + return def2.promise(); } var inventoryObject = { name:inventory.name, vars:inventory.vars } - + var deleteBulk = [] $.when(pmInventories.importItem(inventoryObject)).done(function(inventory_id) { - deleteBulk.push({ + deleteBulk.push({ type:"del", item:'inventory', pk:inventory_id @@ -565,7 +565,7 @@ pmInventories.importInventory = function(inventory) type: "POST", contentType:'application/json', data:JSON.stringify(bulkHosts), - success: function(data) + success: function(data) { var hasError = false; var hosts_ids = [] @@ -579,7 +579,7 @@ pmInventories.importInventory = function(inventory) continue; } hosts_ids.push(val.data.id) - deleteBulk.push({ + deleteBulk.push({ type:"del", item:'host', pk:val.data.id @@ -602,14 +602,14 @@ pmInventories.importInventory = function(inventory) type: "POST", contentType:'application/json', data:JSON.stringify(bulkdata), - success: function(data) + success: function(data) { var igroups_ids = [] var bulk_update = [] var hasError = false; for(var i in data) - { - deleteBulk.push({ + { + deleteBulk.push({ type:"del", item:data.item, pk:data[i].data.id @@ -625,7 +625,7 @@ pmInventories.importInventory = function(inventory) if(val.data.children !== undefined ) { igroups_ids.push(val.data.id) - + // Это группа if(val.data.children) { @@ -646,13 +646,13 @@ pmInventories.importInventory = function(inventory) } } bulk_update.push({ - type: "mod", + type: "mod", item:'group', method: "PUT", data_type: 'groups', pk:val.data.id, data:groups_ids - }) + }) } } else @@ -674,13 +674,13 @@ pmInventories.importInventory = function(inventory) } } bulk_update.push({ - type: "mod", + type: "mod", item:'group', method: "PUT", data_type: 'hosts', pk:val.data.id, data:hosts_ids - }) + }) } } } @@ -689,7 +689,7 @@ pmInventories.importInventory = function(inventory) // Это хост } } - + if(hasError) { // По меньшей мере в одной операции была ошибка вставки. @@ -697,7 +697,7 @@ pmInventories.importInventory = function(inventory) def.reject(deleteBulk); return; } - + $.when(pmInventories.addSubGroups(inventory_id, igroups_ids)).done(function() { if(bulk_update.length) @@ -707,7 +707,7 @@ pmInventories.importInventory = function(inventory) type: "POST", contentType:'application/json', data:JSON.stringify(bulk_update), - success: function(data) + success: function(data) { var hasError = false; for(var i in data) @@ -734,10 +734,10 @@ pmInventories.importInventory = function(inventory) error:function(e) { console.warn(e) - polemarch.showErrors(e) + polemarch.showErrors(e) def.reject(deleteBulk); } - }) + }) } else { @@ -745,42 +745,42 @@ pmInventories.importInventory = function(inventory) } }).fail(function(e){ console.warn(e) - polemarch.showErrors(e) + polemarch.showErrors(e) def.reject(deleteBulk); - }) + }) }, error:function(e) { console.warn(e) - polemarch.showErrors(e) + polemarch.showErrors(e) def.reject(deleteBulk); } }); }).fail(function(e){ console.warn(e) - polemarch.showErrors(e) + polemarch.showErrors(e) def.reject(deleteBulk); }) }, error:function(e) { console.warn(e) - polemarch.showErrors(e) + polemarch.showErrors(e) def.reject(deleteBulk); } }) }).fail(function(e) { console.warn(e) - polemarch.showErrors(e) + polemarch.showErrors(e) def.reject(deleteBulk); }) - + $.when(def).done(function(inventory_id) { def2.resolve(inventory_id) }).fail(function(delete_bulk) - { + { $.when(spajs.ajax.Call({ url: "/api/v1/_bulk/", type: "POST", @@ -790,7 +790,7 @@ pmInventories.importInventory = function(inventory) def2.reject() }) }) - + return def2.promise(); } @@ -799,7 +799,7 @@ pmInventories.showImportPage = function(holder, menuInfo, data) { var def = new $.Deferred(); - var text = spajs.just.render(this.model.name+'_import_page', {}) + var text = spajs.just.render(this.model.name+'_import_page', {}) $(holder).insertTpl(text) def.resolve() @@ -812,7 +812,7 @@ pmInventories.renderImportedInventory = function(imported) { return "" } - + var text = spajs.just.render(this.model.name+'_imported_inventory', {inventory:imported.inventory, text:imported.text}) return text; } @@ -827,7 +827,7 @@ pmInventories.copyItem = function(item_id) var data = thisObj.model.items[item_id]; delete data.id; data.name = "copy from " + data.name - + $.when(encryptedCopyModal.replace(data)).done(function(data) { spajs.ajax.Call({ @@ -835,25 +835,25 @@ pmInventories.copyItem = function(item_id) type: "POST", contentType:'application/json', data: JSON.stringify(data), - success: function(newItem) + success: function(newItem) { thisObj.model.items[newItem.id] = newItem - var groups = [] - for(var i in data.groups) - { - groups.push(data.groups[i].id) - } + var groups = [] + for(var i in data.groups) + { + groups.push(data.groups[i].id) + } - var hosts = [] - for(var i in data.hosts) - { - hosts.push(data.hosts[i].id) - } + var hosts = [] + for(var i in data.hosts) + { + hosts.push(data.hosts[i].id) + } - $.when(thisObj.setSubGroups(newItem.id, groups), thisObj.setSubHosts(newItem.id, hosts)).always(function(){ - def.resolve(newItem.id) - }) + $.when(thisObj.setSubGroups(newItem.id, groups), thisObj.setSubHosts(newItem.id, hosts)).always(function(){ + def.resolve(newItem.id) + }) }, error:function(e) { @@ -863,7 +863,7 @@ pmInventories.copyItem = function(item_id) }).fail(function(e) { def.reject(e) - }) + }) }).fail(function(e) { def.reject(e) @@ -883,8 +883,8 @@ pmInventories.model.page_list = { { class:'btn btn-primary', function:function(){ return "spajs.open({ menuId:'new-"+this.model.page_name+"'}); return false;"}, - title:'Create', - link:function(){ return '/?new-'+this.model.page_name}, + title:'Create', + link:function(){ return '/?new-'+this.model.page_name}, }, { tpl:function(){ @@ -910,8 +910,8 @@ pmInventories.model.page_list = { function:function(item){ return '';}, title:'Create sub group', link:function(item) - { - return '/?inventory/'+item.id+'/new-group' + { + return '/?inventory/'+item.id+'/new-group' }, }, { @@ -919,8 +919,8 @@ pmInventories.model.page_list = { function:function(item){ return '';}, title:'Create sub host', link:function(item) - { - return '/?inventory/'+item.id+'/new-host' + { + return '/?inventory/'+item.id+'/new-host' }, } ] @@ -936,11 +936,11 @@ pmInventories.model.page_new = { title:'Name', name:'name', placeholder:'Enter inventory name', - validator:function(value){ + validator:function(value){ return filedsLib.validator.notEmpty(value, 'Name') }, fast_validator:filedsLib.validator.notEmpty - }, + }, ] ], sections:[ @@ -954,10 +954,10 @@ pmInventories.model.page_new = { return data; }, onCreate:function(data, status, xhr, callOpt) - { + { var def = new $.Deferred(); $.notify("Inventory created", "success"); - + if(callOpt.parent_item) { if(callOpt.parent_type == 'project') @@ -978,14 +978,14 @@ pmInventories.model.page_new = { return def.promise(); } } - + pmInventories.model.page_item = { buttons:[ { class:'btn btn-primary', function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.updateItem('+item_id+')); return false;'}, - title:'Save', - link:function(){ return '#'}, + title:'Save', + link:function(){ return '#'}, }, { class:'btn btn-primary', @@ -1004,7 +1004,7 @@ pmInventories.model.page_item = { class:'btn btn-danger danger-right', function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.deleteItem('+item_id+')); return false;'}, title:' ', - link:function(){ return '#'}, + link:function(){ return '#'}, }, ], sections:[ @@ -1012,7 +1012,7 @@ pmInventories.model.page_item = { return jsonEditor.editor(this.model.items[item_id].vars, {block:this.model.name}); }, function(section, item_id){ - return spajs.just.render("inventories_sub_items", {item_id:item_id}) + return spajs.just.render("inventories_sub_items", {item_id:item_id}) } ], title: function(item_id){ @@ -1032,7 +1032,7 @@ pmInventories.model.page_item = { return filedsLib.validator.notEmpty(value, 'Name') }, fast_validator:function(value){ return value != '' && value} - }, + }, ] ], onUpdate:function(result) @@ -1041,11 +1041,11 @@ pmInventories.model.page_item = { }, onBeforeSave:function(data, item_id) { - data.vars = jsonEditor.jsonEditorGetValues() + data.vars = jsonEditor.jsonEditorGetValues() return data; }, } - + /** * Показывает форму со списком всех групп. * @return $.Deferred @@ -1056,14 +1056,14 @@ pmInventories.showAddSubGroupsForm = function(item_id, holder) { throw "Error in pmInventories.showAddSubGroupsForm with item_id = `" + item_id + "`" } - + return $.when(pmGroups.loadAllItems()).done(function(){ $("#add_existing_item_to_inventory").remove() $(".content").appendTpl(spajs.just.render('add_existing_groups_to_inventory', {item_id:item_id})) - var scroll_el = "#add_existing_item_to_inventory"; - if ($(scroll_el).length != 0) { - $('html, body').animate({ scrollTop: $(scroll_el).offset().top }, 1000); - } + var scroll_el = "#add_existing_item_to_inventory"; + if ($(scroll_el).length != 0) { + $('html, body').animate({ scrollTop: $(scroll_el).offset().top }, 1000); + } $("#polemarch-model-items-select").select2({ width: '100%' }); }).fail(function(){ @@ -1080,14 +1080,14 @@ pmInventories.showAddSubHostsForm = function(item_id, holder) { throw "Error in pmInventories.showAddSubHostsForm with item_id = `" + item_id + "`" } - + return $.when(pmHosts.loadAllItems()).done(function(){ $("#add_existing_item_to_inventory").remove() $(".content").appendTpl(spajs.just.render('add_existing_hosts_to_inventory', {item_id:item_id})) - var scroll_el = "#add_existing_item_to_inventory"; - if ($(scroll_el).length != 0) { - $('html, body').animate({ scrollTop: $(scroll_el).offset().top }, 1000); - } + var scroll_el = "#add_existing_item_to_inventory"; + if ($(scroll_el).length != 0) { + $('html, body').animate({ scrollTop: $(scroll_el).offset().top }, 1000); + } $("#polemarch-model-items-select").select2({ width: '100%' }); }).fail(function(){ @@ -1106,7 +1106,7 @@ pmInventories.hasHosts = function(item_id, host_id) { throw "Error in pmInventories.hasHosts with item_id = `" + item_id + "`" } - + if(pmInventories.model.items[item_id]) { for(var i in pmInventories.model.items[item_id].hosts) @@ -1132,7 +1132,7 @@ pmInventories.hasGroups = function(item_id, group_id) { throw "Error in pmInventories.hasGroups with item_id = `" + item_id + "`" } - + if(pmInventories.model.items[item_id]) { for(var i in pmInventories.model.items[item_id].groups) @@ -1152,11 +1152,12 @@ pmInventories.hasGroups = function(item_id, group_id) */ pmInventories.setSubGroups = function(item_id, groups_ids) { + var thisObj=this; if(!item_id) { throw "Error in pmInventories.setSubGroups with item_id = `" + item_id + "`" } - + if(!groups_ids) { groups_ids = [] @@ -1167,16 +1168,9 @@ pmInventories.setSubGroups = function(item_id, groups_ids) type: "PUT", contentType:'application/json', data:JSON.stringify(groups_ids), - success: function(data) + success: function(data) { - if(pmInventories.model.items[item_id]) - { - pmInventories.model.items[item_id].groups = [] - for(var i in groups_ids) - { - pmInventories.model.items[item_id].groups.push(pmGroups.model.items[groups_ids[i]]) - } - } + pmItems.checkSubItemsAndAdd(thisObj, pmGroups, data, item_id, "groups", groups_ids); }, error:function(e) { @@ -1191,32 +1185,25 @@ pmInventories.setSubGroups = function(item_id, groups_ids) */ pmInventories.setSubHosts = function(item_id, hosts_ids) { + var thisObj=this; if(!hosts_ids) { hosts_ids = [] } - + if(!item_id) { throw "Error in pmInventories.setSubHosts with item_id = `" + item_id + "`" } - return spajs.ajax.Call({ url: "/api/v1/inventories/"+item_id+"/hosts/", type: "PUT", contentType:'application/json', data:JSON.stringify(hosts_ids), - success: function(data) + success: function(data) { - if(pmInventories.model.items[item_id]) - { - pmInventories.model.items[item_id].hosts = [] - for(var i in hosts_ids) - { - pmInventories.model.items[item_id].hosts.push(pmHosts.model.items[hosts_ids[i]]) - } - } + pmItems.checkSubItemsAndAdd(thisObj, pmHosts, data, item_id, "hosts", hosts_ids); }, error:function(e) { @@ -1226,6 +1213,8 @@ pmInventories.setSubHosts = function(item_id, hosts_ids) }); } + + /** * @return $.Deferred */ @@ -1235,7 +1224,7 @@ pmInventories.addSubGroups = function(item_id, groups_ids) { throw "Error in pmInventories.addSubGroups with item_id = `" + item_id + "`" } - + if(!groups_ids) { groups_ids = [] @@ -1247,11 +1236,11 @@ pmInventories.addSubGroups = function(item_id, groups_ids) type: "POST", contentType:'application/json', data:JSON.stringify(groups_ids), - success: function(data) + success: function(data) { if(data.not_found > 0) { - $.notify("Item not found", "error"); + $.notify("Item not found", "error"); def.reject({text:"Item not found", status:404}) return; } @@ -1291,10 +1280,10 @@ pmInventories.addSubHosts = function(item_id, hosts_ids) { throw "Error in pmInventories.addSubHosts with item_id = `" + item_id + "`" } - + var def = new $.Deferred(); if(!hosts_ids || hosts_ids.length == 0) - { + { def.resolve() return def.promise(); } @@ -1304,7 +1293,7 @@ pmInventories.addSubHosts = function(item_id, hosts_ids) type: "POST", contentType:'application/json', data:JSON.stringify(hosts_ids), - success: function(data) + success: function(data) { if(data.not_found > 0) { @@ -1340,19 +1329,19 @@ pmInventories.addSubHosts = function(item_id, hosts_ids) } /**pmInventories.validateGroupName = function(name) -{ - if(!name) - { - return false; - } - - if(/^[a-zA-Z0-9\-\._]*$/.test(name.toLowerCase())) - { - return true; - } - - return false; -}*/ + { + if(!name) + { + return false; + } + + if(/^[a-zA-Z0-9\-\._]*$/.test(name.toLowerCase())) + { + return true; + } + + return false; + }*/ @@ -1362,7 +1351,7 @@ pmInventories.addSubHosts = function(item_id, hosts_ids) * @type Object */ pmInventories.filed.inventoriesAutocomplete = inheritance(filedsLib.filed.simpleText) -pmInventories.filed.inventoriesAutocomplete.type = 'inventoriesAutocomplete' +pmInventories.filed.inventoriesAutocomplete.type = 'inventoriesAutocomplete' pmInventories.filed.inventoriesAutocomplete.getValue = function(pmObj, filed) { var inventory = $("#inventories-autocomplete").val() @@ -1375,7 +1364,7 @@ pmInventories.filed.inventoriesAutocomplete.getValue = function(pmObj, filed) } } - + return inventory; } @@ -1384,13 +1373,13 @@ pmInventories.filed.inventoriesAutocomplete.getValue = function(pmObj, filed) * @type Object */ pmInventories.filed.inventoriesAutocomplete.render = function(pmObj, filed, item_id, opt) -{ - var html = spajs.just.render('filed_type_'+this.type, {pmObj:pmObj, filed:filed, item_id:item_id, filedObj:this, opt:opt}) +{ + var html = spajs.just.render('filed_type_'+this.type, {pmObj:pmObj, filed:filed, item_id:item_id, filedObj:this, opt:opt}) return spajs.just.onInsert(html, function() { // @FixMe требует чтоб были загружены все инвентории pmInventories.loadAllItems() $("#inventories-autocomplete").select2({ width: '100%' }); - + if(filed.onchange && item_id) { filed.onchange({value:filed.getFiledValue.apply(pmObj, [item_id])}) @@ -1408,45 +1397,45 @@ pmInventories.filed.inventoriesAutocomplete.render = function(pmObj, filed, item } }); } - - tabSignal.connect("polemarch.start", function() - { + +tabSignal.connect("polemarch.start", function() +{ // inventories spajs.addMenu({ - id:"inventories", + id:"inventories", urlregexp:[/^inventories$/, /^inventory$/, /^inventories\/search\/?$/, /^inventories\/page\/([0-9]+)$/], onOpen:function(holder, menuInfo, data){return pmInventories.showList(holder, menuInfo, data);} }) - + spajs.addMenu({ - id:"inventories-import", + id:"inventories-import", urlregexp:[/^inventories\/import$/], onOpen:function(holder, menuInfo, data){return pmInventories.showImportPage(holder, menuInfo, data);} }) - + spajs.addMenu({ - id:"inventories-search", + id:"inventories-search", urlregexp:[/^inventories\/search\/([A-z0-9 %\-.:,=]+)$/, /^inventories\/search\/([A-z0-9 %\-.:,=]+)\/page\/([0-9]+)$/], onOpen:function(holder, menuInfo, data){return pmInventories.showSearchResults(holder, menuInfo, data);} }) spajs.addMenu({ - id:"inventory", + id:"inventory", urlregexp:[/^inventory\/([0-9]+)$/, /^inventories\/([0-9]+)$/], onOpen:function(holder, menuInfo, data){return pmInventories.showItem(holder, menuInfo, data);} }) spajs.addMenu({ - id:"inventory-history", + id:"inventory-history", urlregexp:[/^inventory\/([0-9]+)\/history$/, /^inventory\/([0-9]+)\/history\/page\/([0-9]+)$/], onOpen:function(holder, menuInfo, data){return pmHistory.showListInInventory(holder, menuInfo, data);} }) - + spajs.addMenu({ - id:"newInventory", + id:"newInventory", urlregexp:[/^new-inventory$/, /^([A-z0-9_]+)\/([0-9]+)\/new-inventory$/], onOpen:function(holder, menuInfo, data){return pmInventories.showNewItemPage(holder, menuInfo, data);} - }) - }) + }) +}) diff --git a/polemarch/static/js/pmItems.js b/polemarch/static/js/pmItems.js index 6b3aef01..17f0cffc 100644 --- a/polemarch/static/js/pmItems.js +++ b/polemarch/static/js/pmItems.js @@ -1016,6 +1016,41 @@ pmItems.updateItem = function (item_id, opt) return def.promise(); } +/** + *Функция сравнивает полученные данные из запроса(failed_list) с теми данными, что ввел пользователь. + *Если нашлись соответствия, то данный subItem не будет добавлен на страницу. + */ +pmItems.checkSubItemsAndAdd=function(thisObj, ObjToAdd, data, itemId, itemType, itemType_ids) +{ + var failled_list=data.failed_list; + console.log(failled_list); + if(thisObj.model.items[itemId]) + { + //pmInventories.model.items[item_id].hosts= [] + thisObj.model.items[itemId][itemType] = [] + for(var i in itemType_ids) + { + var failIdBool=false; + for(var j=0; j Date: Wed, 14 Feb 2018 16:37:31 +1000 Subject: [PATCH 19/52] The graph for each status was added to the chart on Dashboard. cloud/polemarchplus#129 --- polemarch/static/js/pmDashboard.js | 67 ++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 4 deletions(-) diff --git a/polemarch/static/js/pmDashboard.js b/polemarch/static/js/pmDashboard.js index 6f73691b..7a9321ec 100644 --- a/polemarch/static/js/pmDashboard.js +++ b/polemarch/static/js/pmDashboard.js @@ -119,6 +119,7 @@ pmDashboard.updateData = function() time+=(3600*24*1000) } + for(var i in pmDashboard.statsData.jobs.day) { var val = pmDashboard.statsData.jobs.day[i]; @@ -133,7 +134,7 @@ pmDashboard.updateData = function() } chart_tasks_start_x = ['time']; - chart_tasks_data = ['tasks']; + chart_tasks_data = ['All tasks']; for(var j in tasks_data_t) { @@ -142,9 +143,19 @@ pmDashboard.updateData = function() chart_tasks_data.push(tasks_data[time]/1); } + chart_tasks_data_OK=pmDashboard.getDataForStatusChart(tasks_data, tasks_data_t, "OK"); + chart_tasks_data_ERROR=pmDashboard.getDataForStatusChart(tasks_data, tasks_data_t, "ERROR"); + chart_tasks_data_INTERRUPTED=pmDashboard.getDataForStatusChart(tasks_data, tasks_data_t, "INTERRUPTED"); + chart_tasks_data_DELAY=pmDashboard.getDataForStatusChart(tasks_data, tasks_data_t, "DELAY"); + chart_tasks_data_OFFLINE=pmDashboard.getDataForStatusChart(tasks_data, tasks_data_t, "OFFLINE"); + pmDashboard.model.historyChart.load({ columns: [ - chart_tasks_start_x,chart_tasks_data + chart_tasks_start_x,chart_tasks_data, + + chart_tasks_data_OK, chart_tasks_data_ERROR, + chart_tasks_data_INTERRUPTED, chart_tasks_data_DELAY, + chart_tasks_data_OFFLINE ] }); }); @@ -154,6 +165,39 @@ pmDashboard.updateData = function() }, 1000*30) } +/** + * Функция, которая формирует массив данных для кривых графика по отдельному статусу + */ +pmDashboard.getDataForStatusChart = function(tasks_data, tasks_data_t, status) +{ + for(var i in tasks_data) { + tasks_data[i]=0; + } + + for(var i in pmDashboard.statsData.jobs.day) + { + var val = pmDashboard.statsData.jobs.day[i]; + var time = new Date(val.day) + time = Math.floor(time.getTime()/(1000*3600*24))*3600*1000*24; + + if(val.status==status){ + tasks_data[time] = val.sum; + tasks_data_t.push(time) + } + } + + var chart_tasks_data1 = [status]; + + for(var j in tasks_data_t) + { + var time = tasks_data_t[j] + chart_tasks_data1.push(tasks_data[time]/1); + } + return chart_tasks_data1; + +} + + pmDashboard.open = function(holder, menuInfo, data) { @@ -183,10 +227,22 @@ pmDashboard.open = function(holder, menuInfo, data) data: { x: 'time', columns: [ - ['time'] + ['time'], + ['All tasks'], + ['OK'], + ['ERROR'], + ['INTERRUPTED'], + ['DELAY'], + ['OFFLINE'] ], - //type: 'area-spline', type: 'area', + types: { + OK: 'line', + ERROR: 'line', + INTERRUPTED: 'line', + DELAY: 'line', + OFFLINE: 'line' + }, }, axis: { x: { @@ -195,6 +251,9 @@ pmDashboard.open = function(holder, menuInfo, data) format: '%Y-%m-%d' } } + }, + color: { + pattern: ['#1f77b4', '#276900', '#333333', '#9b97e4', '#808419', '#9e9e9e', '#d62728', '#9467bd', '#c5b0d5', '#8c564b', '#c49c94', '#e377c2', '#f7b6d2', '#7f7f7f', '#c7c7c7', '#bcbd22', '#dbdb8d', '#17becf', '#9edae5'] } }); } From 9572ce0f9e6c18c78df845eadf0562ac5b9d6ffc Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 15 Feb 2018 11:31:04 +1000 Subject: [PATCH 20/52] The reverse ordering for task templates on Dashboard was added. cloud/polemarchplus#130 --- polemarch/main/settings.py | 5 ++++- polemarch/static/js/pmItems.js | 9 ++++++++- polemarch/static/js/pmTasksTemplates.js | 3 ++- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/polemarch/main/settings.py b/polemarch/main/settings.py index 06bc1583..ec67f7b8 100644 --- a/polemarch/main/settings.py +++ b/polemarch/main/settings.py @@ -213,7 +213,10 @@ "polemarch.api.permissions.ModelPermission", ), 'EXCEPTION_HANDLER': 'polemarch.api.handlers.polemarch_exception_handler', - 'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.DjangoFilterBackend',), + 'DEFAULT_FILTER_BACKENDS': ( + 'rest_framework.filters.DjangoFilterBackend', + 'rest_framework.filters.OrderingFilter', + ), 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination', 'PAGE_SIZE': config.getint("web", "rest_page_limit", fallback=PAGE_LIMIT), } diff --git a/polemarch/static/js/pmItems.js b/polemarch/static/js/pmItems.js index 17f0cffc..440a7de3 100644 --- a/polemarch/static/js/pmItems.js +++ b/polemarch/static/js/pmItems.js @@ -480,9 +480,10 @@ pmItems.searchObjectToString = function (query, defaultName) * @param {string|object} query запрос * @param {integer} limit * @param {integer} offset + * @param {string} ordering - сортировка по какому-то свойству объекта(id, name и т.д). Для обратной сортировки передавать "-id" * @returns {jQuery.ajax|spajs.ajax.Call.defpromise|type|spajs.ajax.Call.opt|spajs.ajax.Call.spaAnonym$10|Boolean|undefined|spajs.ajax.Call.spaAnonym$9} */ -pmItems.sendSearchQuery = function (query, limit, offset) +pmItems.sendSearchQuery = function (query, limit, offset, ordering) { if (!limit) { @@ -494,10 +495,16 @@ pmItems.sendSearchQuery = function (query, limit, offset) offset = 0; } + if (!ordering) + { + ordering = ""; + } + var q = []; q.push("limit=" + encodeURIComponent(limit)) q.push("offset=" + encodeURIComponent(offset)) + q.push("ordering=" + encodeURIComponent(ordering)) for (var i in query) { diff --git a/polemarch/static/js/pmTasksTemplates.js b/polemarch/static/js/pmTasksTemplates.js index 253ec155..4b0217de 100644 --- a/polemarch/static/js/pmTasksTemplates.js +++ b/polemarch/static/js/pmTasksTemplates.js @@ -237,7 +237,8 @@ pmTasksTemplates.showWidget = function(holder, kind) var thisObj = this; var offset = 0 var limit = this.pageSize; - return $.when(this.sendSearchQuery({kind:kind}, limit, offset)).done(function() + var ordering="-id"; + return $.when(this.sendSearchQuery({kind:kind}, limit, offset, ordering)).done(function() { $(holder).insertTpl(spajs.just.render(thisObj.model.name+'_widget', {query:"", kind:kind})) }).fail(function() From 20c7427fee05bf3f17ededf68accf804c42ff6ae Mon Sep 17 00:00:00 2001 From: Roman Date: Thu, 15 Feb 2018 17:27:08 +1000 Subject: [PATCH 21/52] 2 list points were done: select of chart period and using of "/api/v1/stats/?last=14" cloud/polemarchplus#12 --- polemarch/static/js/pmDashboard.js | 137 +++++++++++++++++--- polemarch/static/templates/pmDashboard.html | 23 ++++ 2 files changed, 140 insertions(+), 20 deletions(-) diff --git a/polemarch/static/js/pmDashboard.js b/polemarch/static/js/pmDashboard.js index 7a9321ec..8ceb9583 100644 --- a/polemarch/static/js/pmDashboard.js +++ b/polemarch/static/js/pmDashboard.js @@ -26,6 +26,17 @@ pmDashboard.statsData={ templates:'-' } +pmDashboard.statsDataLast=14; +pmDashboard.statsDataLastQuery=14; +pmDashboard.statsDataMomentType='day'; + +if(window.localStorage['selected-chart-period'] && window.localStorage['selected-chart-period-query'] && window.localStorage['selected-chart-period-type']) +{ + pmDashboard.statsDataLast=window.localStorage['selected-chart-period']; + pmDashboard.statsDataLastQuery=window.localStorage['selected-chart-period-query']; + pmDashboard.statsDataMomentType=window.localStorage['selected-chart-period-type']; +} + /** * Двумерный массив с описанием списка отображаемых виджетов в каждой строке * @@ -105,25 +116,66 @@ pmDashboard.updateData = function() pmwUsersCounter.updateCount(); //строим график - var startTime = moment().subtract(14, 'days').format("YYYY-MM-DD")+"T00:00:00.000000Z" - tasks_data = {} - tasks_data_t = [] + //ветка выполняется, если единица измерения для оси X - день + if(pmDashboard.statsDataMomentType=="day"){ + var startTime = moment().subtract(pmDashboard.statsDataLast, pmDashboard.statsDataMomentType).format("YYYY-MM-DD")+"T00:00:00.000000Z"; + tasks_data = {} + tasks_data_t = [] - var time = new Date(startTime) - time = Math.floor(time.getTime()/(1000*3600*24))*3600*1000*24; - for(var i = 0; i< 14; i++) - { - tasks_data[time] = 0; - tasks_data_t.push(time) - time+=(3600*24*1000) - } + var time = new Date(startTime) + time = Math.floor(time.getTime()/(1000*3600*24))*3600*1000*24; + + for(var i = -1; i<= pmDashboard.statsDataLast; i++) + { + tasks_data[time] = 0; + tasks_data_t.push(time) + time+=(3600*24*1000) + } + //ветка выполняется, если единица измерения для оси X - месяц или год + } else { + //определяем текущий месяц и год + var monthNum=moment().format("MM"); + var yearNum=moment().format("YYYY"); + if(pmDashboard.statsDataMomentType=="year") + { + var startTimeOrg=yearNum+"-01-01"; + } + else + { + var startTimeOrg=yearNum+"-"+monthNum+"-01"; + } + + //задаем стартовую дату для графика. + //pmDashboard.statsDataLast - количество периодов назад + //pmDashboard.statsDataMomentType - тип периода - месяц/год + var startTime = moment(startTimeOrg).subtract(pmDashboard.statsDataLast-1, pmDashboard.statsDataMomentType).format("YYYY-MM-DD")+"T00:00:00.000000Z"; + tasks_data = {} + tasks_data_t = [] - for(var i in pmDashboard.statsData.jobs.day) + + var time = new Date(startTime) + time = Math.floor(time.getTime()/(1000*3600*24))*3600*1000*24; + + //формируем в цикле временные отрезки для графика относительно стартовой даты + for(var i = 1; i<= pmDashboard.statsDataLast+1; i++) + { + tasks_data[time] = 0; + tasks_data_t.push(time); + + //идем на период вперед + var newTime=moment(startTime).add(i, pmDashboard.statsDataMomentType).format("YYYY-MM-DD")+"T00:00:00.000000Z"; + time = new Date(newTime); + time=Math.floor(time.getTime()/(1000*3600*24))*3600*1000*24; + } + } + + //формируем массив значений для кривой all tasks + for(var i in pmDashboard.statsData.jobs[pmDashboard.statsDataMomentType]) { - var val = pmDashboard.statsData.jobs.day[i]; - var time = new Date(val.day) + var val = pmDashboard.statsData.jobs[pmDashboard.statsDataMomentType][i]; + var time = new Date(val[pmDashboard.statsDataMomentType]) time = Math.floor(time.getTime()/(1000*3600*24))*3600*1000*24; if(!tasks_data[time]) @@ -132,10 +184,8 @@ pmDashboard.updateData = function() tasks_data_t.push(time) } } - chart_tasks_start_x = ['time']; chart_tasks_data = ['All tasks']; - for(var j in tasks_data_t) { var time = tasks_data_t[j] @@ -143,12 +193,14 @@ pmDashboard.updateData = function() chart_tasks_data.push(tasks_data[time]/1); } + //формируем массив значений для кривой каждого статуса chart_tasks_data_OK=pmDashboard.getDataForStatusChart(tasks_data, tasks_data_t, "OK"); chart_tasks_data_ERROR=pmDashboard.getDataForStatusChart(tasks_data, tasks_data_t, "ERROR"); chart_tasks_data_INTERRUPTED=pmDashboard.getDataForStatusChart(tasks_data, tasks_data_t, "INTERRUPTED"); chart_tasks_data_DELAY=pmDashboard.getDataForStatusChart(tasks_data, tasks_data_t, "DELAY"); chart_tasks_data_OFFLINE=pmDashboard.getDataForStatusChart(tasks_data, tasks_data_t, "OFFLINE"); + //загружаем график, перечисляем массивы данных для графика pmDashboard.model.historyChart.load({ columns: [ chart_tasks_start_x,chart_tasks_data, @@ -174,10 +226,11 @@ pmDashboard.getDataForStatusChart = function(tasks_data, tasks_data_t, status) tasks_data[i]=0; } - for(var i in pmDashboard.statsData.jobs.day) + + for(var i in pmDashboard.statsData.jobs[pmDashboard.statsDataMomentType]) { - var val = pmDashboard.statsData.jobs.day[i]; - var time = new Date(val.day) + var val = pmDashboard.statsData.jobs[pmDashboard.statsDataMomentType][i]; + var time = new Date(val[pmDashboard.statsDataMomentType]) time = Math.floor(time.getTime()/(1000*3600*24))*3600*1000*24; if(val.status==status){ @@ -256,6 +309,10 @@ pmDashboard.open = function(holder, menuInfo, data) pattern: ['#1f77b4', '#276900', '#333333', '#9b97e4', '#808419', '#9e9e9e', '#d62728', '#9467bd', '#c5b0d5', '#8c564b', '#c49c94', '#e377c2', '#f7b6d2', '#7f7f7f', '#c7c7c7', '#bcbd22', '#dbdb8d', '#17becf', '#9edae5'] } }); + if($('select').is('#chart-period')) + { + $('#chart-period').val(pmDashboard.statsDataLastQuery).change(); + } } tabSignal.connect("polemarch.start", function() @@ -280,7 +337,7 @@ pmDashboard.loadStats=function() var limit=1; var thisObj = this; return spajs.ajax.Call({ - url: "/api/v1/stats/", + url: "/api/v1/stats/?last="+pmDashboard.statsDataLastQuery, type: "GET", contentType: 'application/json', data: "limit=" + encodeURIComponent(limit)+"&rand="+Math.random(), @@ -296,6 +353,46 @@ pmDashboard.loadStats=function() }); } +/** + *Функция вызывается, когда происходит изменение периода на графике(пользователь выбрал другой option в select). + *Функция обновляет значения переменных, которые в дальнейшем используются для запроса к api/v1/stats и отрисовки графика. + */ + +pmDashboard.updateStatsDataLast=function(thisEl) +{ + var newLast=thisEl.value; + switch(newLast) { + case '1095': + pmDashboard.statsDataLast=3; + pmDashboard.statsDataMomentType="year"; + window.localStorage['selected-chart-period']=3; + window.localStorage['selected-chart-period-type']="year"; + break; + case '365': + pmDashboard.statsDataLast=12; + pmDashboard.statsDataMomentType="month"; + window.localStorage['selected-chart-period']=12; + window.localStorage['selected-chart-period-type']="month"; + break; + case '99': + pmDashboard.statsDataLast=3; + pmDashboard.statsDataMomentType="month"; + window.localStorage['selected-chart-period']=3; + window.localStorage['selected-chart-period-type']="month"; + break; + default: + pmDashboard.statsDataLast=newLast; + pmDashboard.statsDataMomentType="day"; + window.localStorage['selected-chart-period']=newLast; + window.localStorage['selected-chart-period-type']="day"; + break; + } + pmDashboard.statsDataLastQuery=newLast; + window.localStorage['selected-chart-period-query']=newLast; + pmDashboard.updateData(); +} + + /** * Базовый класс виджета * @type Object diff --git a/polemarch/static/templates/pmDashboard.html b/polemarch/static/templates/pmDashboard.html index 6773d10e..cf1ea275 100644 --- a/polemarch/static/templates/pmDashboard.html +++ b/polemarch/static/templates/pmDashboard.html @@ -49,6 +49,29 @@

    Tasks history

    + + + +
    + + +
    + + + + + + + + + + \ No newline at end of file diff --git a/polemarch/static/templates/pmTasksTemplates.html b/polemarch/static/templates/pmTasksTemplates.html index 1a89f250..54f8ee33 100644 --- a/polemarch/static/templates/pmTasksTemplates.html +++ b/polemarch/static/templates/pmTasksTemplates.html @@ -2,9 +2,48 @@ - + - + - \ No newline at end of file + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/polemarch/static/templates/pmTasksTemplates.html b/polemarch/static/templates/pmTasksTemplates.html index 54f8ee33..00a6cc86 100644 --- a/polemarch/static/templates/pmTasksTemplates.html +++ b/polemarch/static/templates/pmTasksTemplates.html @@ -114,6 +114,69 @@ <% } %> + + + + + + + + + @@ -101,7 +127,7 @@ Import templates - + - \ No newline at end of file + + + \ No newline at end of file diff --git a/polemarch/static/templates/pmTasksTemplates.html b/polemarch/static/templates/pmTasksTemplates.html index 00a6cc86..cbec322e 100644 --- a/polemarch/static/templates/pmTasksTemplates.html +++ b/polemarch/static/templates/pmTasksTemplates.html @@ -61,7 +61,28 @@ onclick="return spajs.openURL(this.href);" ><%- val.name %> - Execute +
    + + +
    <% } %> From 6ef91438bf7155d8229de445122ebddae6d7ad8a Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 Mar 2018 12:15:06 +1000 Subject: [PATCH 49/52] Some code update was added. cloud/polemarchplus#125 --- polemarch/static/js/pmProjects.js | 37 +++++++++---------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/polemarch/static/js/pmProjects.js b/polemarch/static/js/pmProjects.js index 4f26cdb8..15ecf5ea 100644 --- a/polemarch/static/js/pmProjects.js +++ b/polemarch/static/js/pmProjects.js @@ -437,38 +437,23 @@ pmProjects.startUpdateProjectItem = function(item_id) { var thisObj = this; - if(thisObj.model.items[item_id].vars.repo_type=="GIT") + if(thisObj.model.items[item_id].status == "WAIT_SYNC" || thisObj.model.items[item_id].status == "SYNC") { - if(thisObj.model.items[item_id].status == "WAIT_SYNC" || thisObj.model.items[item_id].status == "SYNC") + thisObj.model.updateTimeoutId = setTimeout(function() { - thisObj.model.updateTimeoutId = setTimeout(function() + $.when(thisObj.loadItem(item_id)).always(function() { - $.when(thisObj.loadItem(item_id)).always(function() - { - thisObj.startUpdateProjectItem(item_id) - }) - }, 5000) - } - if(thisObj.model.items[item_id].status != "WAIT_SYNC" || thisObj.model.items[item_id].status != "SYNC") - { - $("#branch_block").empty(); - $("#branch_block").html(pmProjects.renderBranchInput(item_id)); - } + thisObj.startUpdateProjectItem(item_id) + }) + }, 5000) } - else + + if(thisObj.model.items[item_id].status != "WAIT_SYNC" || thisObj.model.items[item_id].status != "SYNC") { - if(thisObj.model.items[item_id].status == "WAIT_SYNC" || thisObj.model.items[item_id].status == "SYNC") - { - console.log(thisObj.model.items[item_id].vars.repo_type); - thisObj.model.updateTimeoutId = setTimeout(function() - { - $.when(thisObj.loadItem(item_id)).always(function() - { - thisObj.startUpdateProjectItem(item_id) - }) - }, 5000) - } + $("#branch_block").empty(); + $("#branch_block").html(pmProjects.renderBranchInput(item_id)); } + } pmProjects.openItem = function(holder, menuInfo, data) From ce380fe749d70aa7e7df8e1bb8dedfbcc880d5f0 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 Mar 2018 12:42:14 +1000 Subject: [PATCH 50/52] Some code update was added[2]. cloud/polemarchplus#125 --- polemarch/static/js/pmProjects.js | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/polemarch/static/js/pmProjects.js b/polemarch/static/js/pmProjects.js index 15ecf5ea..03b48932 100644 --- a/polemarch/static/js/pmProjects.js +++ b/polemarch/static/js/pmProjects.js @@ -289,7 +289,7 @@ pmProjects.model.page_item = { }, { class:'btn btn-warning', - function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.syncRepo('+item_id+')); return false;'}, + function:function(item_id){ return 'spajs.showLoader('+this.model.className+'.syncRepoFromProjectPage('+item_id+')); return false;'}, title:' Sync', link:function(){ return '#'}, help:'Sync' @@ -562,6 +562,25 @@ pmProjects.openRunPlaybookPage = function(holder, menuInfo, data) * @return $.Deferred */ pmProjects.syncRepo = function(item_id) +{ + return spajs.ajax.Call({ + url: "/api/v1/projects/"+item_id+"/sync/", + type: "POST", + contentType:'application/json', + success: function(data) + { + $.notify("Send sync query", "success"); + }, + error:function(e) + { + console.warn("project "+item_id+" sync error - " + JSON.stringify(e)); + polemarch.showErrors(e.responseJSON) + } + }); + +} + +pmProjects.syncRepoFromProjectPage = function(item_id) { var thisObj = this; thisObj.model.items[item_id].status = "WAIT_SYNC"; @@ -632,8 +651,7 @@ pmProjects.checkBranchInput = function(item_id) pmProjects.renderBranchInput = function(item_id) { - var thisObj = this; - var html=spajs.just.render('branch_input', {item_id:item_id, pmObj:thisObj}); + var html=spajs.just.render('branch_input', {item_id:item_id, pmObj:pmProjects}); return html; } From eea394ec9368615a8d0230410bd419bb2c6524d9 Mon Sep 17 00:00:00 2001 From: Roman Date: Fri, 2 Mar 2018 13:48:38 +1000 Subject: [PATCH 51/52] Template of action button for task/module template without options was fixed. Template option tests were updated (there were some problems with arguments in test). --- polemarch/static/js/tests/qUnitTest.js | 24 ------------------------ polemarch/static/templates/pmItems.html | 18 ++++++++++-------- 2 files changed, 10 insertions(+), 32 deletions(-) diff --git a/polemarch/static/js/tests/qUnitTest.js b/polemarch/static/js/tests/qUnitTest.js index 509746d4..cb9f497c 100644 --- a/polemarch/static/js/tests/qUnitTest.js +++ b/polemarch/static/js/tests/qUnitTest.js @@ -3420,30 +3420,6 @@ window.qunitTestsArray.push({ }) - }); - - syncQUnit.addTest('Сохранение новой невалидной опции шаблона модуля2', function ( assert ) - { - // Предполагается что мы от прошлого теста попали на страницу создания project - var done = assert.async(); - var itemId = /template\/Module\/([0-9]+)\/new-option/.exec(window.location.href)[1]; - - jsonEditor.jsonEditorRmVar("new-vault-password-file") - $("#new_json_nameprefix").val("new-vault-password-file"); - $("#new_json_valueprefix").val("syntax-check"); - jsonEditor.jsonEditorAddVar(); - - $.when(pmModuleTemplates.saveOption(itemId)).done(function() - { - debugger; - assert.ok(false, 'Успешно сохранено, а не должно было'); - render(done) - }).fail(function(){ - assert.ok(true, 'Ошибка при сохранении, как и задумано'); - render(done) - }) - - }); syncQUnit.addTest('Сохранение новой валидной опции шаблона модуля', function ( assert ) diff --git a/polemarch/static/templates/pmItems.html b/polemarch/static/templates/pmItems.html index fa61e3c6..1763f8e7 100644 --- a/polemarch/static/templates/pmItems.html +++ b/polemarch/static/templates/pmItems.html @@ -173,14 +173,16 @@

    <% } %> <% if(pmObj.model.itemslist.results[i].options_list) { %> - - <% for(var k in pmObj.model.itemslist.results[i].options_list) { %> -
  • - - Execute with <%= pmObj.model.itemslist.results[i].options_list[k] %> - -
  • + <% if(pmObj.model.itemslist.results[i].options_list.length!=0) { %> + + <% for(var k in pmObj.model.itemslist.results[i].options_list) { %> +
  • + + Execute with <%= pmObj.model.itemslist.results[i].options_list[k] %> + +
  • + <% } %> <% } %> <% } %> From a319e66c9a49df25e1ab2b5c5fb830dd8963ec2e Mon Sep 17 00:00:00 2001 From: Sergey Klyuykov Date: Fri, 2 Mar 2018 13:54:52 +1000 Subject: [PATCH 52/52] Hide some fields in history raw_inventory Close vstconsulting/polemarch#32 --- polemarch/main/models/utils.py | 16 ++++++++++- polemarch/main/tests/tasks.py | 50 +++++++++++++++++++++------------- 2 files changed, 46 insertions(+), 20 deletions(-) diff --git a/polemarch/main/models/utils.py b/polemarch/main/models/utils.py index 01e1a6ff..b15d0e3c 100644 --- a/polemarch/main/models/utils.py +++ b/polemarch/main/models/utils.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals +import re import sys import logging from os.path import dirname @@ -8,6 +9,7 @@ import six from django.utils import timezone +from .hosts import Inventory from ...main.utils import (tmp_file, CmdExecutor, KVExchanger, CalledProcessError) @@ -92,6 +94,7 @@ class Inventory(object): def __init__(self, inventory, cwd="/tmp"): self.cwd = cwd self.__file = None + self.hidden_vars = Inventory.HIDDEN_VARS if isinstance(inventory, (six.string_types, six.text_type)): self.raw, self.keys = self.get_from_file(inventory) else: @@ -162,12 +165,23 @@ def workdir(self): def path_to_ansible(self): return dirname(sys.executable) + "/" + self.command_type + def hide_passwords(self, raw): + regex = r"" + for hide in self.inventory_object.hidden_vars: + regex += r"|" if regex else r"" + regex += r"(?<=" + hide + r"=).{1,}?(?=[\n\t\s])" + subst = "[~~ENCRYPTED~~]" + raw = re.sub(regex, subst, raw, 0, re.MULTILINE) + return raw + def prepare(self, target, inventory, history, project): project.check_path(inventory) self.target, self.project = target, project self.history = history if history else DummyHistory() self.inventory_object = self.Inventory(inventory, cwd=self.workdir) - self.history.raw_inventory = self.inventory_object.raw + self.history.raw_inventory = self.hide_passwords( + self.inventory_object.raw + ) self.history.status = "RUN" self.history.revision = project.revision self.history.save() diff --git a/polemarch/main/tests/tasks.py b/polemarch/main/tests/tasks.py index c0c9f704..6de5e801 100644 --- a/polemarch/main/tests/tasks.py +++ b/polemarch/main/tests/tasks.py @@ -43,13 +43,15 @@ def test_get_tasks(self): correct_simple_inventory = ( "127.0.1.1 ansible_user=centos " - "ansible_ssh_private_key_file=" + "ansible_ssh_private_key_file=[~~ENCRYPTED~~] " + "ansible_become_pass=[~~ENCRYPTED~~]" ) def create_inventory(self): inventory_data = dict(name="Inv1", vars={}) host_data = dict(name="127.0.1.1", type="HOST", vars={"ansible_user": "centos", + "ansible_become_pass": "secret", "ansible_ssh_private_key_file": "somekey"}) # make host, inventory inventory = self.post_result("/api/v1/inventories/", @@ -68,17 +70,18 @@ def create_inventory(self): def test_execute(self, subprocess_function): inv1, h1 = self.create_inventory() # mock side effect to get ansible-playbook args for assertions in test - result = ["", ""] + result = dict() def side_effect(call_args, *args, **kwargs): inventory_path = call_args[3] with open(inventory_path, 'r') as inventory_file: inventory = inventory_file.read().split('\n') - l = lambda x: x.startswith('127.') - result[0] = list(filter(l, inventory))[0] - key_path = result[0].split("=")[-1] - with open(key_path, 'r') as key_file: - result[1] = key_file.read() + sHst = inventory[1].split(" ") + result['host'] = sHst[0] + result['ansible_user'] = sHst[1].split("=")[1] + result['ansible_become_pass'] = sHst[1].split("=")[1] + with open(sHst[2].split("=")[1], 'r') as key_file: + result['ansible_ssh_private_key_file'] = key_file.read() subprocess_function.side_effect = side_effect # test that can't execute without inventory self.post_result( @@ -92,8 +95,9 @@ def side_effect(call_args, *args, **kwargs): call_args = subprocess_function.call_args[0][0] self.assertTrue(call_args[0].endswith("ansible-playbook")) self.assertTrue(call_args[1].endswith("first.yml")) - self.assertTrue(result[0].startswith(self.correct_simple_inventory)) - self.assertEquals(result[1], "somekey") + self.assertEquals(result['host'], "127.0.1.1") + self.assertEquals(result['ansible_user'], "centos") + self.assertEquals(result['ansible_ssh_private_key_file'], "somekey") # test simple execution sync subprocess_function.reset_mock() self.post_result( @@ -104,14 +108,15 @@ def side_effect(call_args, *args, **kwargs): call_args = subprocess_function.call_args[0][0] self.assertTrue(call_args[0].endswith("ansible-playbook")) self.assertTrue(call_args[1].endswith("first.yml")) - self.assertTrue(result[0].startswith(self.correct_simple_inventory)) - self.assertEquals(result[1], "somekey") + self.assertEquals(result['host'], "127.0.1.1") + self.assertEquals(result['ansible_user'], "centos") + self.assertEquals(result['ansible_ssh_private_key_file'], "somekey") @patch('polemarch.main.utils.CmdExecutor.execute') def test_execute_module(self, subprocess_function): inv1, h1 = self.create_inventory() # mock side effect to get ansible-playbook args for assertions in test - result = ["", ""] + result = dict() def side_effect(call_args, *args, **kwargs): # check additional args @@ -126,11 +131,12 @@ def side_effect(call_args, *args, **kwargs): inventory_path = call_args[3] with open(inventory_path, 'r') as inventory_file: inventory = inventory_file.read().split('\n') - l = lambda x: x.startswith('127.') - result[0] = list(filter(l, inventory))[0] - key_path = result[0].split("=")[-1] - with open(key_path, 'r') as key_file: - result[1] = key_file.read() + sHst = inventory[1].split(" ") + result['host'] = sHst[0] + result['ansible_user'] = sHst[1].split("=")[1] + result['ansible_become_pass'] = sHst[3].split("=")[1] + with open(sHst[2].split("=")[1], 'r') as key_file: + result['ansible_ssh_private_key_file'] = key_file.read() subprocess_function.side_effect = side_effect # test that can't execute without inventory self.post_result( @@ -147,11 +153,17 @@ def side_effect(call_args, *args, **kwargs): call_args = subprocess_function.call_args[0][0] self.assertTrue(call_args[0].endswith("ansible")) self.assertTrue(call_args[1].endswith("all")) - self.assertTrue(result[0].startswith(self.correct_simple_inventory)) - self.assertEquals(result[1], "somekey") + self.assertEquals(result['host'], "127.0.1.1") + self.assertEquals(result['ansible_user'], "centos") + self.assertEquals(result['ansible_ssh_private_key_file'], "somekey") + self.assertEquals(result['ansible_become_pass'], "secret") history = History.objects.get(id=answer["history_id"]) self.assertEquals(history.kind, "MODULE") self.assertEquals(history.mode, "shell") + self.assertIn( + "ansible_become_pass=[~~ENCRYPTED~~]", history.raw_inventory, + "\n"+history.raw_inventory + ) # test simple execution without args kw_list = [dict(args=""), dict(args=None), dict()] for kwargs in kw_list: