diff --git a/apps/assets/forms.py b/apps/assets/forms.py index 4b2040e5f..1eb7d7c18 100644 --- a/apps/assets/forms.py +++ b/apps/assets/forms.py @@ -5,20 +5,42 @@ from .models import IDC, Asset, AssetGroup, AdminUser, SystemUser from django.utils.translation import gettext_lazy as _ -class AssetForm(forms.ModelForm): +# class AssetForm(forms.ModelForm): +# class Meta: +# model = Asset +# +# fields = [ +# 'ip', 'other_ip', 'remote_card_ip', 'hostname', 'port', 'groups', 'username', 'password', +# 'idc', 'mac_address', 'brand', 'cpu', 'memory', 'disk', 'os', 'cabinet_no', 'cabinet_pos', +# 'number', 'status', 'type', 'env', 'sn', 'is_active', 'comment', 'admin_user', 'system_users' +# ] +# +# widgets = { +# 'groups': forms.SelectMultiple(attrs={'class': 'select2-groups', 'data-placeholder': _('Select asset groups')}), +# 'system_user': forms.SelectMultiple(attrs={'class': 'select2-system-user', 'data-placeholder': _('Select asset system user')}), +# 'admin_user': forms.SelectMultiple(attrs={'class': 'select2-admin-user', 'data-placeholder': _('Select asset admin user')}), + # } +# + +class AssetCreateForm(forms.ModelForm): class Meta: model = Asset fields = [ - "ip", "other_ip", "remote_card_ip", "hostname", "port", "groups", "username", "password", - "idc", "mac_address", "brand", "cpu", "memory", "disk", "os", "cabinet_no", "cabinet_pos", - "number", "status", "type", "env", "sn", "is_active", "comment", "admin_user", "system_users" + 'hostname', 'ip', 'port', 'type', 'zone', 'comment', 'admin_user', 'system_users', 'idc', 'groups' ] widgets = { - 'groups': forms.SelectMultiple(attrs={'class': 'select2-groups', 'data-placeholder': _('Select asset groups')}), - 'system_user': forms.SelectMultiple(attrs={'class': 'select2-system-user', 'data-placeholder': _('Select asset system user')}), - # 'admin_user': forms.SelectMultiple(attrs={'class': 'select2-admin-user', 'data-placeholder': _('Select asset admin user')}), + 'groups': forms.SelectMultiple(attrs={'class': 'select2', + 'data-placeholder': _('Select asset groups')}), + 'system_users': forms.SelectMultiple(attrs={'class': 'select2', + 'data-placeholder': _('Select asset system users')}), + 'admin_user': forms.Select(attrs={'class': 'select2', 'data-placeholder': _('Select asset admin user')}), + } + help_texts = { + 'hostname': '* required', + 'ip': '* required', + 'type': '* required', } diff --git a/apps/assets/models.py b/apps/assets/models.py index 2d66992a3..56dc90a08 100644 --- a/apps/assets/models.py +++ b/apps/assets/models.py @@ -271,38 +271,38 @@ class AssetGroup(models.Model): class Asset(models.Model): - ip = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('IP')) + ip = models.CharField(max_length=32, verbose_name=_('IP')) other_ip = models.CharField(max_length=255, null=True, blank=True, verbose_name=_('Other IP')) remote_card_ip = models.CharField(max_length=16, null=True, blank=True, verbose_name=_('Remote card IP')) - hostname = models.CharField(max_length=128, unique=True, null=True, blank=True, verbose_name=_('Hostname')) - port = models.IntegerField(default=22, null=True, blank=True, verbose_name=_('Port')) + hostname = models.CharField(max_length=128, blank=True, verbose_name=_('Hostname')) + port = models.IntegerField(default=22, verbose_name=_('Port')) groups = models.ManyToManyField(AssetGroup, blank=True, related_name='assets', verbose_name=_('Asset groups')) - username = models.CharField(max_length=16, null=True, blank=True, verbose_name=_('Admin user')) - password = models.CharField(max_length=256, null=True, blank=True, verbose_name=_("Admin password")) admin_user = models.ForeignKey(AdminUser, null=True, blank=True, related_name='assets', on_delete=models.SET_NULL, verbose_name=_("Admin user")) system_users = models.ManyToManyField(SystemUser, blank=True, related_name='assets', verbose_name=_("System User")) idc = models.ForeignKey(IDC, null=True, related_name='assets', on_delete=models.SET_NULL, verbose_name=_('IDC')) mac_address = models.CharField(max_length=20, null=True, blank=True, verbose_name=_("Mac address")) brand = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('Brand')) - cpu = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('CPU')) + cpu = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('CPU')) memory = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('Memory')) disk = models.CharField(max_length=1024, null=True, blank=True, verbose_name=_('Disk')) os = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('OS')) cabinet_no = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Cabinet number')) cabinet_pos = models.IntegerField(null=True, blank=True, verbose_name=_('Cabinet position')) number = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Asset number')) - status = models.ForeignKey(AssetExtend, null=True, blank=True, related_name="asset_status_extend", + status = models.ForeignKey(AssetExtend, null=True, blank=True, related_name="status_asset", verbose_name=_('Asset status')) - type = models.ForeignKey(AssetExtend, null=True, blank=True, related_name="asset_type_extend", + type = models.ForeignKey(AssetExtend, null=True, blank=True, related_name="type_asset", verbose_name=_('Asset type')) - env = models.ForeignKey(AssetExtend, null=True, blank=True, related_name="asset_env_extend", + env = models.ForeignKey(AssetExtend, null=True, blank=True, related_name="env_asset", verbose_name=_('Asset environment')) + zone = models.ForeignKey(AssetExtend, null=True, blank=True, related_name="zone_asset", + verbose_name=_('Asset zone')) sn = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('Serial number')) created_by = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Created by')) is_active = models.BooleanField(default=True, verbose_name=_('Is active')) date_created = models.DateTimeField(auto_now=True, null=True, blank=True, verbose_name=_('Date added')) - comment = models.CharField(max_length=128, null=True, blank=True, verbose_name=_('Comment')) + comment = models.TextField(max_length=128, null=True, blank=True, verbose_name=_('Comment')) def __unicode__(self): return '%(ip)s:%(port)s' % {'ip': self.ip, 'port': self.port} @@ -322,8 +322,8 @@ class Asset(models.Model): seed() for i in range(count): - asset = cls(ip='%s.%s.%s.%s' % tuple([forgery_py.forgery.basic.text(length=3, digits=True) - for i in range(0, 4)]), + asset = cls(ip='%s.%s.%s.%s' % (i, i, i, i), + hostname=forgery_py.internet.user_name(True), admin_user=choice(AdminUser.objects.all()), idc=choice(IDC.objects.all()), port=22, @@ -338,13 +338,12 @@ class Asset(models.Model): continue -class Label(models.Model): - key = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('KEY')) - value = models.CharField(max_length=64, null=True, blank=True, verbose_name=_('VALUE')) +class Tag(models.Model): + key = models.CharField(max_length=64, blank=True, verbose_name=_('KEY')) + value = models.CharField(max_length=64, verbose_name=_('VALUE')) asset = models.ForeignKey(Asset, null=True, blank=True, on_delete=models.SET_NULL, verbose_name=_('Asset')) created_by = models.CharField(max_length=32, blank=True, verbose_name=_("Created by")) date_created = models.DateTimeField(auto_now=True, null=True) - comment = models.CharField(max_length=128, blank=True, verbose_name=_('Comment')) def __unicode__(self): return self.key diff --git a/apps/assets/templates/assets/admin_user_list.html b/apps/assets/templates/assets/admin_user_list.html index 2031dfa49..ed41a5cfe 100644 --- a/apps/assets/templates/assets/admin_user_list.html +++ b/apps/assets/templates/assets/admin_user_list.html @@ -1,4 +1,4 @@ -{% extends '_list_base.html' %} +{% extends '_base_list.html' %} {% load i18n %} {% load common_tags %} {% block content_left_head %} diff --git a/apps/assets/templates/assets/asset_create.html b/apps/assets/templates/assets/asset_create.html deleted file mode 100644 index f47cfb880..000000000 --- a/apps/assets/templates/assets/asset_create.html +++ /dev/null @@ -1,109 +0,0 @@ -{% extends 'base.html' %} -{% load static %} -{% load bootstrap %} -{% block custom_head_css_js %} - - -{% endblock %} -{% block content %} -
-
-
-
-
-
添加资产
- -
- -
-
-
-
- {% csrf_token %} -

基本信息

- - {{ form.hostname|bootstrap_horizontal }} - - {{ form.ip|bootstrap_horizontal }} - - {{ form.port|bootstrap_horizontal }} - - {{ form.type|bootstrap_horizontal }} - - {{ form.comment|bootstrap_horizontal }} - -
-

关联资产用户

-
- -
-
- - -
-
-
- - {{ form.admin_user|bootstrap_horizontal }} -

Tips: 管理用户是服务器存在的root或拥有sudo的用户,用来推送系统用户

- -
- {{ form.system_user|bootstrap_horizontal }} - -
-

所属

- {{ form.idc|bootstrap_horizontal }} - - {{ form.groups|bootstrap_horizontal }} - -
-

标签

-
- -
- -
-
- -
-
-
- 添加 -
- -
-
-
- - -
-
-
-
-
-
-
-
-
-
-{% endblock %} - -{% block custom_foot_js %} - -{% endblock %} \ No newline at end of file diff --git a/apps/assets/templates/assets/asset_create_update.html b/apps/assets/templates/assets/asset_create_update.html new file mode 100644 index 000000000..23351e8d9 --- /dev/null +++ b/apps/assets/templates/assets/asset_create_update.html @@ -0,0 +1,59 @@ +{% extends '_base_create_update.html' %} +{% load static %} +{% load bootstrap %} +{% load i18n %} + +{% block custom_head_css_js_create %} + + +{% endblock %} + +{% block form %} +
+ {% csrf_token %} +

{% trans 'Basic' %}

+ {{ form.hostname|bootstrap_horizontal }} + {{ form.ip|bootstrap_horizontal }} + {{ form.port|bootstrap_horizontal }} + {{ form.type|bootstrap_horizontal }} + +
+

{% trans 'Group' %}

+ {{ form.idc|bootstrap_horizontal }} + {{ form.groups|bootstrap_horizontal }} + +
+

{% trans 'Asset user' %}

+ {{ form.admin_user|bootstrap_horizontal }} + {{ form.system_users|bootstrap_horizontal }} + +
+

{% trans 'Other' %}

+
+ +
+ +

{% trans 'Tips: Use `,` split' %}

+
+
+ {{ form.comment|bootstrap_horizontal }} + +
+
+
+ + +
+
+ +
+{% endblock %} + +{% block custom_foot_js %} + +{% endblock %} \ No newline at end of file diff --git a/apps/assets/templates/assets/asset_group_list.html b/apps/assets/templates/assets/asset_group_list.html index c945127cd..ecaf13cd8 100644 --- a/apps/assets/templates/assets/asset_group_list.html +++ b/apps/assets/templates/assets/asset_group_list.html @@ -1,4 +1,4 @@ -{% extends '_list_base.html' %} +{% extends '_base_list.html' %} {% load i18n %} {% load common_tags %} {% block content_left_head %} diff --git a/apps/assets/templates/assets/asset_list.html b/apps/assets/templates/assets/asset_list.html index ca3feca9b..71d826010 100644 --- a/apps/assets/templates/assets/asset_list.html +++ b/apps/assets/templates/assets/asset_list.html @@ -1,431 +1,71 @@ -{% extends 'base.html' %} +{% extends '_base_list.html' %} {% load i18n %} -{% block content %} +{% load common_tags %} +{% block content_left_head %} + {% trans "Create asset" %} +{% endblock %} -
-
-
-
-
-
资产列表
- -
+{% block table_head %} + + + + {% trans 'Hostname' %} + {% trans 'IP' %} + {% trans 'Port' %} + {% trans 'Type' %} + {% trans 'Hardware' %} + {% trans 'Valid' %} + +{% endblock %} -
-
- +{% block table_body %} + {% for asset in asset_list %} + + + + + + + {{ asset.hostname }} + + + {{ asset.ip }} + {{ asset.port }} + {{ asset.type }} + {{ asset.cpu }} {{ asset.memory }} {{ asset.disk }} + + {% if asset.is_expired %} + + {% else %} + + {% endif %} + + + {% trans 'Update' %} + {% trans 'Delete' %}- +{# {% trans 'Delete' %}#} + + + {% endfor %} +{% endblock %} -
- +{% block content_bottom_left %} + +
+ - - - - - -
- -
-
- - -
- - -
-
-
-
- - - - - - - - - - - - - - - {% for asset in assets %} - - - - - - - - - - - {% endfor %} - -
- - 主机名 IP 类型 配置 资产组 状态 操作
- - {{ asset.hostname }} {{ asset.ip }} {{ asset.system_type }} {{ asset.cpu }} | {{ asset.memory }} | {{ asset.disk }} {% for group in asset.group.all %} {{ group.name }} {% endfor %} - {% if asset.is_active %} - - {% else %} - - {% endif %} - -{# 编辑#} - 连接 -{# 删除#} -
-
-
- -{# 修改#} - -{# #} - -
- {% include '_pagination.html' %} -
- -
+
+
+
-
-
+ {% endblock %} -{% block self_footer_js %} - - -{% endblock %} diff --git a/apps/assets/templates/assets/idc_list.html b/apps/assets/templates/assets/idc_list.html index 3ce6f601b..94d131732 100644 --- a/apps/assets/templates/assets/idc_list.html +++ b/apps/assets/templates/assets/idc_list.html @@ -1,4 +1,4 @@ -{% extends '_list_base.html' %} +{% extends '_base_list.html' %} {% load i18n %} {% load common_tags %} {% block content_left_head %} diff --git a/apps/assets/templates/assets/system_user_list.html b/apps/assets/templates/assets/system_user_list.html index 2a9b04178..fb1e41323 100644 --- a/apps/assets/templates/assets/system_user_list.html +++ b/apps/assets/templates/assets/system_user_list.html @@ -1,4 +1,4 @@ -{% extends '_list_base.html' %} +{% extends '_base_list.html' %} {% load i18n %} {% load common_tags %} {% block content_left_head %} diff --git a/apps/assets/views.py b/apps/assets/views.py index 48503757d..33b19e12a 100644 --- a/apps/assets/views.py +++ b/apps/assets/views.py @@ -10,15 +10,30 @@ from django.urls import reverse_lazy from django.contrib.messages.views import SuccessMessageMixin from django.views.generic.detail import DetailView, SingleObjectMixin -from .models import Asset, AssetGroup, IDC, AssetExtend, AdminUser, SystemUser, Label -from .forms import AssetForm, AssetGroupForm, IDCForm, AdminUserForm, SystemUserForm +from .models import Asset, AssetGroup, IDC, AssetExtend, AdminUser, SystemUser, Tag +from .forms import AssetCreateForm, AssetGroupForm, IDCForm, AdminUserForm, SystemUserForm from .hands import AdminUserRequiredMixin +class AssetListView(ListView): + paginate_by = settings.CONFIG.DISPLAY_PER_PAGE + model = Asset + context_object_name = 'asset_list' + template_name = 'assets/asset_list.html' + + def get_context_data(self, **kwargs): + context = { + 'app': 'Assets', + 'action': 'Asset list', + } + kwargs.update(context) + return super(AssetListView, self).get_context_data(**kwargs) + + class AssetCreateView(AdminUserRequiredMixin, CreateView): model = Asset - form_class = AssetForm - template_name = 'assets/asset_create.html' + form_class = AssetCreateForm + template_name = 'assets/asset_create_update.html' success_url = reverse_lazy('assets:asset-list') def form_valid(self, form): @@ -26,14 +41,15 @@ class AssetCreateView(AdminUserRequiredMixin, CreateView): key = self.request.POST.get('key', '') value = self.request.POST.get('value', '') asset.save() - Label.objects.create(key=key, value=value, asset=asset) return super(AssetCreateView, self).form_valid(form) def get_context_data(self, **kwargs): - context = super(AssetCreateView, self).get_context_data(**kwargs) - context.update({'admin_users': AdminUser.objects.all()}) - assert isinstance(context, object) - return context + context = { + 'app': 'Assets', + 'action': 'Create asset', + } + kwargs.update(context) + return super(AssetCreateView, self).get_context_data(**kwargs) class AssetUpdateView(UpdateView): @@ -45,12 +61,6 @@ class AssetDeleteView(DeleteView): success_url = reverse_lazy('assets:asset-list') -class AssetListView(ListView): - model = Asset - context_object_name = 'assets' - template_name = 'assets/asset_list.html' - - class AssetDetailView(DetailView): model = Asset context_object_name = 'asset' diff --git a/apps/perms/templates/perms/asset_permission_list.html b/apps/perms/templates/perms/asset_permission_list.html index d6e30a35b..6332e2e6d 100644 --- a/apps/perms/templates/perms/asset_permission_list.html +++ b/apps/perms/templates/perms/asset_permission_list.html @@ -1,4 +1,4 @@ -{% extends '_list_base.html' %} +{% extends '_base_list.html' %} {% load i18n %} {% load common_tags %} {% block content_left_head %} diff --git a/apps/static/css/plugins/inputTags.css b/apps/static/css/plugins/inputTags.css new file mode 100644 index 000000000..89f953f01 --- /dev/null +++ b/apps/static/css/plugins/inputTags.css @@ -0,0 +1,214 @@ +@import url("https://fonts.useso.com/css?family=Open+Sans:300,400,600,700"); +@import url("https://fonts.useso.com/css?family=Roboto:400,300,500,700"); +/** {*/ + /*box-sizing: border-box;*/ +/*}*/ +/*.color {*/ + /*color: #19BC9C !important;*/ +/*}*/ +/*html,*/ +/*body {*/ + /*width: 100%;*/ + /*height: 100%;*/ + /*margin: 0;*/ + /*font-family: 'Ubuntu Condensed', sans-serif;*/ + /*background-color: #fff;*/ +/*}*/ +/*h1 {*/ + /*text-align: center;*/ + /*margin-bottom: 32px;*/ +/*}*/ +div.container { + display: block; + width: 50%; + margin: 32px auto; + padding: 16px 32px; + background-color: #fff; + box-shadow: 2px 2px 8px rgba(0, 0, 0, 0.1); +} +div.inputTags-list { + display: inline-block; + width: 100%; + padding: 6px; + border: 1px solid rgba(25, 188, 156, 0.35); + /*background-color: #f9f9f9;*/ + box-shadow: 1px 2px 2px rgba(221, 221, 221, 0.2); + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -moz-background-clip: padding; + -webkit-background-clip: padding-box; + background-clip: padding-box; +} +div.inputTags-list span.inputTags-item { + position: relative; + display: inline-block; + margin: 2px; + padding: 3px 22px 4px 8px; + background-color: #19BC9C; + text-align: center; + color: #fff; + opacity: 1; + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + -moz-background-clip: padding; + -webkit-background-clip: padding-box; + background-clip: padding-box; +} +div.inputTags-list span.inputTags-item.is-edit { + display: none; +} +div.inputTags-list span.inputTags-item.is-hidden { + display: none !important; +} +div.inputTags-list span.inputTags-item.is-exists { + background-color: rgba(231, 76, 60, 0.7); +} +div.inputTags-list span.inputTags-item span.value { + cursor: pointer; +} +div.inputTags-list span.inputTags-item i { + position: absolute; + top: 50%; + right: 6px; + font-size: 20px; + cursor: pointer; + z-index: 10; + font-weight: 400; + font-family: sans-serif; + line-height: 1; + font-style: normal; + -webkit-transition: color 0.2s; + -khtml-transition: color 0.2s; + -moz-transition: color 0.2s; + -ms-transition: color 0.2s; + -o-transition: color 0.2s; + -webkit-transform: translateY(-50%); + -khtml-transform: translateY(-50%); + -moz-transform: translateY(-50%); + -ms-transform: translateY(-50%); + -o-transform: translateY(-50%); +} +div.inputTags-list span.inputTags-item i:hover { + color: #e74c3c; +} +div.inputTags-list input.inputTags-field { + border: none; + margin-left: 4px; + background-color: transparent; +} +div.inputTags-list input.inputTags-field:focus, +div.inputTags-list input.inputTags-field:active { + outline: none; +} +div.inputTags-list input.inputTags-field.is-edit { + margin: 0 2px; + padding: 4px 8px 3px 8px; + border: 1px dashed #c4c4c4; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -moz-background-clip: padding; + -webkit-background-clip: padding-box; + background-clip: padding-box; +} +div.inputTags-list ul.inputTags-autocomplete-list { + position: absolute; + max-height: 192px; + margin: 0; + padding: 0; + list-style-type: none; + background-color: #fff; + border: 1px solid #ddd; + overflow-y: auto; + z-index: 100; + opacity: 0; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -moz-background-clip: padding; + -webkit-background-clip: padding-box; + background-clip: padding-box; + -webkit-transform: scaleY(0); + -khtml-transform: scaleY(0); + -moz-transform: scaleY(0); + -ms-transform: scaleY(0); + -o-transform: scaleY(0); + -webkit-transform-origin: 50% 0; + -khtml-transform-origin: 50% 0; + -moz-transform-origin: 50% 0; + -ms-transform-origin: 50% 0; + -o-transform-origin: 50% 0; + -webkit-transition-duration: 0.2s; + -khtml-transition-duration: 0.2s; + -moz-transition-duration: 0.2s; + -ms-transition-duration: 0.2s; + -o-transition-duration: 0.2s; +} +div.inputTags-list ul.inputTags-autocomplete-list.is-active { + opacity: 1; + -webkit-transform: scaleY(1); + -khtml-transform: scaleY(1); + -moz-transform: scaleY(1); + -ms-transform: scaleY(1); + -o-transform: scaleY(1); +} +div.inputTags-list ul.inputTags-autocomplete-list li { + height: 32px; + line-height: 32px; + padding: 0 16px; + cursor: pointer; + border-bottom: 1px solid #ddd; + -webkit-transition-duration: 0.3s; + -khtml-transition-duration: 0.3s; + -moz-transition-duration: 0.3s; + -ms-transition-duration: 0.3s; + -o-transition-duration: 0.3s; + -webkit-transition-duration: 0.2s; + -khtml-transition-duration: 0.2s; + -moz-transition-duration: 0.2s; + -ms-transition-duration: 0.2s; + -o-transition-duration: 0.2s; +} +div.inputTags-list ul.inputTags-autocomplete-list li:last-child { + border: none; +} +div.inputTags-list ul.inputTags-autocomplete-list li:hover { + background-color: #19BC9C; + color: #fff; +} +div.inputTags-list ul.inputTags-autocomplete-list li.is-disabled { + cursor: default; + background-color: #f7f7f7; + color: initial; +} +p.inputTags-error { + position: relative; + margin: 0; + padding: 0.5em 1em; + color: #fff; + background-color: rgba(231, 76, 60, 0.7); + cursor: pointer; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + -moz-background-clip: padding; + -webkit-background-clip: padding-box; + background-clip: padding-box; +} +p.inputTags-error:first-of-type { + margin-top: 8px; +} +p.inputTags-error:after { + position: absolute; + content: "\000D7"; + top: 50%; + right: 0.5em; + -webkit-transform: translateY(-50%); + -khtml-transform: translateY(-50%); + -moz-transform: translateY(-50%); + -ms-transform: translateY(-50%); + -o-transform: translateY(-50%); + font-size: 28px; +} diff --git a/apps/static/js/plugins/inputTags.jquery.min.js b/apps/static/js/plugins/inputTags.jquery.min.js new file mode 100644 index 000000000..db89547c2 --- /dev/null +++ b/apps/static/js/plugins/inputTags.jquery.min.js @@ -0,0 +1 @@ +!function(a){a.fn.inputTags=function(b){if("inputTags"in window||(window.inputTags={instances:[]}),window.inputTags.methods={tags:function(a,b){if(a){switch(typeof a){case"string":switch(a){case"_toString":var c=d.tags.toString();return b?b(c):c;case"_toObject":var e=d._toObject(d.tags);return b?b(e):e;case"_toJSON":var e=d._toObject(d.tags),f=JSON.stringify(e);return b?b(f):f;case"_toArray":return b?b(d.tags):d.tags}var g=a.split(",");if(g.length>1){var h=d.tags;d.tags=h.concat(g)}else d.tags.push(g[0]);break;case"object":var h=d.tags;"[object Object]"===Object.prototype.toString.call(a)&&(a=Object.keys(a).map(function(b){return a[b]})),d.tags=h.concat(a);break;case"function":return a(d.tags)}if(d._clean(),d._fill(),d._updateValue(),d.destroy(),d._setInstance(d),b)return b(d.tags)}return d.tags},event:function(a,b){d.options[a]=b,d._setInstance(d)},options:function(a,b){return a||b?b?(d.options[a]=b,void d._setInstance(d)):d.options[a]:d.options},destroy:function(){var b=a(this).attr("data-uniqid");delete window.inputTags.instances[b]}},"object"==typeof b||!b){var b=a.extend(!0,{},a.fn.inputTags.defaults,b);this.each(function(){var c=a(this);c.UNIQID=Math.round(Date.now()/(494*Math.random()-54)),c.DEFAULT_CLASS="inputTags",c.ELEMENT_CLASS=c.DEFAULT_CLASS+"-"+c.UNIQID,c.LIST_CLASS=c.DEFAULT_CLASS+"-list",c.ITEM_CLASS=c.DEFAULT_CLASS+"-item",c.ITEM_CONTENT='%s×',c.FIELD_CLASS=c.DEFAULT_CLASS+"-field",c.ERROR_CLASS=c.DEFAULT_CLASS+"-error",c.ERROR_CONTENT='

%s

',c.AUTOCOMPLETE_LIST_CLASS=c.DEFAULT_CLASS+"-autocomplete-list",c.AUTOCOMPLETE_ITEM_CLASS=c.DEFAULT_CLASS+"-autocomplete-item",c.AUTOCOMPLETE_ITEM_CONTENT='
  • %s
  • ',c.options=b,c.keys=[13,188,27],c.tags=[],c.options.keys.length>0&&(c.keys=c.keys.concat(c.options.keys)),c.init=function(){c.addClass(c.ELEMENT_CLASS).attr("data-uniqid",c.UNIQID),c.$element=a("."+c.ELEMENT_CLASS),c.$element.hide(),c.build(),c.fill(),c.save(),c.edit(),c.destroy(),c._autocomplete()._init(),c._focus()},c.build=function(){c.$html=a("
    ").addClass(c.LIST_CLASS),c.$input=a("").attr({type:"text",class:c.FIELD_CLASS}),c.$html.insertAfter(c.$element).prepend(c.$input),c.$list=c.$element.next("."+c.LIST_CLASS),c.$list.on("click",function(b){return!a(b.target).hasClass("inputTags-field")&&void c.$input.focus()})},c.fill=function(){return c._getDefaultValues(),0!==c.options.tags&&(c._concatenate(),c._updateValue(),void c._fill())},c._fill=function(){c.tags.forEach(function(a,b){var d=c._validate(a,!1);(!0===d||"max"===d&&b+1<=c.options.max)&&c._buildItem(a)})},c._clean=function(){a("."+c.ITEM_CLASS,c.$list).remove()},c.save=function(){c.$input.on("keyup",function(b){b.preventDefault();var d=b.keyCode||b.which,e=c.$input.val().trim();if(a.inArray(d,c.keys)<0)return!1;if(27===d)return c._cancel(),!1;if(e=188===d?e.slice(0,-1):e,!c._validate(e,!0))return!1;if(c.options.only&&c._exists(e))return c._errors("exists"),!1;if(c.$input.hasClass("is-edit")){var f=c.$input.attr("data-old-value");if(f===e)return c._cancel(),!0;c._update(f,e),c._clean(),c._fill()}else{if(c._autocomplete()._isSet()&&c._autocomplete()._get("only")&&a.inArray(e,c._autocomplete()._get("values"))<0)return c._autocomplete()._hide(),c._errors("autocomplete_only"),!1;if(c._exists(e)){c.$input.removeClass("is-autocomplete"),c._errors("exists");var g=a('[data-tag="'+e+'"]',c.$list);return g.addClass("is-exists"),setTimeout(function(){g.removeClass("is-exists")},300),!1}c._buildItem(e),c._insert(e)}return c._cancel(),c._updateValue(),c.destroy(),c._autocomplete()._build(),c._setInstance(c),c.$input.focus(),!1})},c.edit=function(){c.$list.on("click","."+c.ITEM_CLASS,function(b){if(a(b.target).hasClass("close-item")||!1===c.options.editable||c._autocomplete()._isSet()&&c._autocomplete()._get("only"))return c._cancel(),!0;var d=a(this).addClass("is-edit"),e=a(".value",d).text();c.$input.width(d.outerWidth()).insertAfter(d).addClass("is-edit").attr("data-old-value",e).val(e).focus(),c._bindEvent("selected"),c.$input.on("blur",function(){c._cancel(),c._bindEvent("unselected")})})},c.destroy=function(){a("."+c.ITEM_CLASS,c.$list).off("click").on("click",".close-item",function(){var b=a(this).parent("."+c.ITEM_CLASS),d=a(".value",b).text();b.addClass("is-closed"),setTimeout(function(){c._pop(d),c._updateValue(),b.remove(),c._autocomplete()._build(),c.$input.focus(),c._setInstance(c)},200)})},c._buildItem=function(b){var d=a(c.ITEM_CONTENT.replace("%s",b)),e=a("").addClass(c.ITEM_CLASS+" is-closed").attr("data-tag",b).html(d);e.insertBefore(c.$input).delay(100).queue(function(){a(this).removeClass("is-closed")})},c._getIndex=function(a){return c.tags.indexOf(a)},c._concatenate=function(){(!1===typeof c.options.max||c.options.max>0)&&c.options.tags.length>c.options.max&&c.options.tags.splice(-Math.abs(c.options.tags.length-c.options.max)),c.tags=c.tags.concat(c.options.tags)},c._getDefaultValues=function(){c.$element.val().length>0?c.tags=c.tags.concat(c.$element.val().split(",")):c.$element.attr("value","")},c._insert=function(a){c.tags.push(a),c._bindEvent(["change","create"])},c._update=function(a,b){var d=c._getIndex(a);c.tags[d]=b,c._bindEvent(["change","update"])},c._pop=function(a){var b=c._getIndex(a);return!(b<0)&&(c.tags.splice(b,1),void c._bindEvent(["change","destroy"]))},c._cancel=function(){a("."+c.ITEM_CLASS).removeClass("is-edit"),c.$input.removeClass("is-edit is-autocomplete").removeAttr("data-old-value style").val("").appendTo(c.$list)},c._autocomplete=function(){var b=c.options.autocomplete.values;return{_isSet:function(){return b.length>0},_init:function(){return!!c._autocomplete()._isSet()&&void c._autocomplete()._build()},_build:function(){c._autocomplete()._exists()&&c.$autocomplete.remove(),c.$autocomplete=a("
      ").addClass(c.AUTOCOMPLETE_LIST_CLASS),c._autocomplete()._get("values").forEach(function(b,d){var e=c.AUTOCOMPLETE_ITEM_CONTENT.replace("%s",b),f=a.inArray(b,c.tags)>=0?a(e).addClass("is-disabled"):a(e);f.appendTo(c.$autocomplete)}),c._autocomplete()._bindClick(),a(document).not(c.$autocomplete).on("click",function(){c._autocomplete()._hide()})},_bindClick:function(){a(c.$autocomplete).off("click").on("click","."+c.AUTOCOMPLETE_ITEM_CLASS,function(b){if(a(b.target).hasClass("is-disabled"))return!1;c.$input.addClass("is-autocomplete").val(a(this).text()),c._autocomplete()._hide(),c._bindEvent("autocompleteTagSelect");var b=a.Event("keyup");b.which=13,c.$input.trigger(b)})},_show:function(){return!!c._autocomplete()._isSet()&&(c.$autocomplete.css({left:c.$input[0].offsetLeft,minWidth:c.$input.width()}).insertAfter(c.$input),void setTimeout(function(){c._autocomplete()._bindClick(),c.$autocomplete.addClass("is-active")},100))},_hide:function(){c.$autocomplete.removeClass("is-active")},_get:function(a){return c.options.autocomplete[a]},_exists:function(){return void 0!==c.$autocomplete}}},c._updateValue=function(){c.$element.attr("value",c.tags.join(","))},c._focus=function(){c.$input.on("focus",function(){c._bindEvent("focus"),!c._autocomplete()._isSet()||c.$input.hasClass("is-autocomplete")||c.$input.hasClass("is-edit")||c._autocomplete()._show()})},c._toObject=function(a){return a.reduce(function(a,b,c){return a[c]=b,a},{})},c._validate=function(a,b){var d,e="";switch(!0){case!a:case void 0===a:case 0===a.length:c._cancel(),e="empty";break;case a.length>0&&a.lengthc.options.maxLength:e="maxLength";break;case c.options.max>0&&c.tags.length>=c.options.max:c.$input.hasClass("is-edit")||(e="max");break;case c.options.email:d=/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,d.test(a)||(e="email")}return!(e.length>0)||(b?c._errors(e):e)},c._exists=function(b){return a.inArray(b,c.tags)>=0},c._errors=function(a){return 0!==a.length&&(c._autocomplete()._exists()&&c.$autocomplete.remove(),c._displayErrors(c.options.errors[a].replace("%s",c.options[a]),a),!1)},c._displayErrors=function(b,d){var e=a(c.ERROR_CONTENT.replace("%s",b)).attr("data-error",d),f=c.options.errors.timeout;return!a("."+c.ERROR_CLASS+'[data-error="'+d+'"]').length&&(e.hide().insertAfter(c.$list).slideDown(),!(!f||f<=0)&&(a("."+c.ERROR_CLASS).on("click",function(){c._collapseErrors(a(this))}),void setTimeout(function(){c._collapseErrors()},f)))},c._collapseErrors=function(b){var d=b?b:a("."+c.ERROR_CLASS);d.slideUp(300,function(){d.remove()})},c._getInstance=function(){return window.inputTags.instances[c.UNIQID]},c._setInstance=function(a){window.inputTags.instances[c.UNIQID]=c},c._isSet=function(a){return!(void 0===c.options[a]||!1===c.options[a]||c.options[a].length<=0)},c._callMethod=function(a,b){return void 0!==b.options[a]&&"function"==typeof b.options[a]&&void b.options[a].apply(this,Array.prototype.slice.call(arguments,1))},c._initEvent=function(a,b){if(!a)return!1;switch(typeof a){case"string":b(a,c);break;case"object":a.forEach(function(a,d){b(a,c)})}return!0},c._bindEvent=function(a){return c._initEvent(a,function(a,b){c._callMethod(a,b)})},c._unbindEvent=function(a){return c._initEvent(a,function(a,b){c.options[a]=!1})},c.init(),c._bindEvent("init"),c._setInstance(c)});return{on:function(a,b){window.inputTags.methods.event(a,b)}}}if(window.inputTags.methods[b]){var c=a(this).attr("data-uniqid"),d=window.inputTags.instances[c];return void 0===d?a.error("[undefined instance] No inputTags instance found."):window.inputTags.methods[b].apply(this,Array.prototype.slice.call(arguments,1))}a.error("[undefined method] The method ["+b+"] does not exists.")},a.fn.inputTags.defaults={tags:[],keys:[],minLength:2,maxLength:30,max:6,email:!1,only:!0,init:!1,create:!1,update:!1,destroy:!1,focus:!1,selected:!1,unselected:!1,change:!1,autocompleteTagSelect:!1,editable:!0,autocomplete:{values:[],only:!1},errors:{empty:"Attention, vous ne pouvez pas ajouter un tag vide.",minLength:"Attention, votre tag doit avoir au minimum %s caractères.",maxLength:"Attention, votre tag ne doit pas dépasser %s caractères.",max:"Attention, le nombre de tags ne doit pas dépasser %s.",email:"Attention, l'adresse email que vous avez entré n'est pas valide",exists:"Attention, ce tag existe déjà !",autocomplete_only:"Attention, vous devez sélectionner une valeur dans la liste.",timeout:8e3}}}(jQuery); \ No newline at end of file diff --git a/apps/templates/_base_create_update.html b/apps/templates/_base_create_update.html new file mode 100644 index 000000000..2e1a981f8 --- /dev/null +++ b/apps/templates/_base_create_update.html @@ -0,0 +1,37 @@ +{% extends 'base.html' %} +{% load i18n %} +{% load static %} +{% load bootstrap %} +{% block custom_head_css_js %} + + + {% block custom_head_css_js_create %} {% endblock %} +{% endblock %} + +{% block content %} +
      +
      +
      +
      +
      +
      {{ action }}
      + +
      +
      + {% block form %} {% endblock %} +
      +
      +
      +
      +
      +{% endblock %} diff --git a/apps/templates/_list_base.html b/apps/templates/_base_list.html similarity index 100% rename from apps/templates/_list_base.html rename to apps/templates/_base_list.html diff --git a/apps/users/templates/users/user_create.html b/apps/users/templates/users/user_create.html index 8337b3756..07235d018 100644 --- a/apps/users/templates/users/user_create.html +++ b/apps/users/templates/users/user_create.html @@ -1,6 +1,7 @@ {% extends 'users/_user.html' %} {% load i18n %} {% load bootstrap %} +{% block user_template_title %}{% trans "Create user" %}{% endblock %} {% block username %} {{ form.username|bootstrap_horizontal }} {% endblock %} diff --git a/apps/users/templates/users/user_group_list.html b/apps/users/templates/users/user_group_list.html index 851291880..25649007d 100644 --- a/apps/users/templates/users/user_group_list.html +++ b/apps/users/templates/users/user_group_list.html @@ -1,4 +1,4 @@ -{% extends '_list_base.html' %} +{% extends '_base_list.html' %} {% load i18n %} {% load common_tags %} {% block content_left_head %} diff --git a/apps/users/templates/users/user_list.html b/apps/users/templates/users/user_list.html index d220a4a84..2017510ea 100644 --- a/apps/users/templates/users/user_list.html +++ b/apps/users/templates/users/user_list.html @@ -1,4 +1,4 @@ -{% extends '_list_base.html' %} +{% extends '_base_list.html' %} {% load i18n %} {% load common_tags %} {% block content_left_head %}