mirror of
https://gitlab2.federez.net/re2o/re2o
synced 2024-11-26 06:32:26 +00:00
Merge branch 'massive_use_bft_tag' into 'master'
Massive use bft tag See merge request rezo/re2o!18
This commit is contained in:
commit
00e71a7041
14 changed files with 1898 additions and 424 deletions
|
@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
{% load staticfiles%}
|
{% load staticfiles%}
|
||||||
{% load bootstrap_form_typeahead %}
|
{% load massive_bootstrap_form %}
|
||||||
|
|
||||||
{% block title %}Création et modification de factures{% endblock %}
|
{% block title %}Création et modification de factures{% endblock %}
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<form class="form" method="post">
|
<form class="form" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<h3>Editer la facture</h3>
|
<h3>Editer la facture</h3>
|
||||||
{% bootstrap_form_typeahead factureform 'user' %}
|
{% massive_bootstrap_form factureform 'user' %}
|
||||||
{{ venteform.management_form }}
|
{{ venteform.management_form }}
|
||||||
<h3>Articles de la facture</h3>
|
<h3>Articles de la facture</h3>
|
||||||
<table class="table table-striped">
|
<table class="table table-striped">
|
||||||
|
|
|
@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
{% load bootstrap_form_typeahead %}
|
{% load massive_bootstrap_form %}
|
||||||
|
|
||||||
{% block title %}Création et modification de machines{% endblock %}
|
{% block title %}Création et modification de machines{% endblock %}
|
||||||
|
|
||||||
|
@ -78,10 +78,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if interfaceform %}
|
{% if interfaceform %}
|
||||||
<h3>Interface</h3>
|
<h3>Interface</h3>
|
||||||
{% if i_bft_param %}
|
{% if i_mbf_param %}
|
||||||
{% bootstrap_form_typeahead interfaceform 'ipv4,machine' bft_param=i_bft_param %}
|
{% massive_bootstrap_form interfaceform 'ipv4,machine' mbf_param=i_mbf_param %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% bootstrap_form_typeahead interfaceform 'ipv4,machine' %}
|
{% massive_bootstrap_form interfaceform 'ipv4,machine' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if domainform %}
|
{% if domainform %}
|
||||||
|
@ -98,15 +98,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if extensionform %}
|
{% if extensionform %}
|
||||||
<h3>Extension</h3>
|
<h3>Extension</h3>
|
||||||
{% bootstrap_form_typeahead extensionform 'origin' %}
|
{% massive_bootstrap_form extensionform 'origin' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if mxform %}
|
{% if mxform %}
|
||||||
<h3>Enregistrement MX</h3>
|
<h3>Enregistrement MX</h3>
|
||||||
{% bootstrap_form_typeahead mxform 'name' %}
|
{% massive_bootstrap_form mxform 'name' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if nsform %}
|
{% if nsform %}
|
||||||
<h3>Enregistrement NS</h3>
|
<h3>Enregistrement NS</h3>
|
||||||
{% bootstrap_form_typeahead nsform 'ns' %}
|
{% massive_bootstrap_form nsform 'ns' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if txtform %}
|
{% if txtform %}
|
||||||
<h3>Enregistrement TXT</h3>
|
<h3>Enregistrement TXT</h3>
|
||||||
|
@ -118,7 +118,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if serviceform %}
|
{% if serviceform %}
|
||||||
<h3>Service</h3>
|
<h3>Service</h3>
|
||||||
{% bootstrap_form serviceform %}
|
{% massive_bootstrap_form serviceform 'servers' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if vlanform %}
|
{% if vlanform %}
|
||||||
<h3>Vlan</h3>
|
<h3>Vlan</h3>
|
||||||
|
|
|
@ -54,7 +54,7 @@ from .forms import EditOuverturePortListForm, EditOuverturePortConfigForm
|
||||||
from .models import IpType, Machine, Interface, IpList, MachineType, Extension, Mx, Ns, Domain, Service, Service_link, Vlan, Nas, Text, OuverturePortList, OuverturePort
|
from .models import IpType, Machine, Interface, IpList, MachineType, Extension, Mx, Ns, Domain, Service, Service_link, Vlan, Nas, Text, OuverturePortList, OuverturePort
|
||||||
from users.models import User
|
from users.models import User
|
||||||
from preferences.models import GeneralOption, OptionalMachine
|
from preferences.models import GeneralOption, OptionalMachine
|
||||||
from re2o.templatetags.bootstrap_form_typeahead import hidden_id, input_id
|
from re2o.templatetags.massive_bootstrap_form import hidden_id, input_id
|
||||||
from re2o.utils import all_active_assigned_interfaces, all_has_access
|
from re2o.utils import all_active_assigned_interfaces, all_has_access
|
||||||
from re2o.views import form
|
from re2o.views import form
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ def f_type_id( is_type_tt ):
|
||||||
return 'id_Interface-type_hidden' if is_type_tt else 'id_Interface-type'
|
return 'id_Interface-type_hidden' if is_type_tt else 'id_Interface-type'
|
||||||
|
|
||||||
def generate_ipv4_choices( form ) :
|
def generate_ipv4_choices( form ) :
|
||||||
""" Generate the parameter choices for the bootstrap_form_typeahead tag
|
""" Generate the parameter choices for the massive_bootstrap_form tag
|
||||||
"""
|
"""
|
||||||
f_ipv4 = form.fields['ipv4']
|
f_ipv4 = form.fields['ipv4']
|
||||||
used_mtype_id = []
|
used_mtype_id = []
|
||||||
|
@ -92,7 +92,7 @@ def generate_ipv4_choices( form ) :
|
||||||
return choices
|
return choices
|
||||||
|
|
||||||
def generate_ipv4_engine( is_type_tt ) :
|
def generate_ipv4_engine( is_type_tt ) :
|
||||||
""" Generate the parameter engine for the bootstrap_form_typeahead tag
|
""" Generate the parameter engine for the massive_bootstrap_form tag
|
||||||
"""
|
"""
|
||||||
return (
|
return (
|
||||||
'new Bloodhound( {{'
|
'new Bloodhound( {{'
|
||||||
|
@ -106,7 +106,7 @@ def generate_ipv4_engine( is_type_tt ) :
|
||||||
)
|
)
|
||||||
|
|
||||||
def generate_ipv4_match_func( is_type_tt ) :
|
def generate_ipv4_match_func( is_type_tt ) :
|
||||||
""" Generate the parameter match_func for the bootstrap_form_typeahead tag
|
""" Generate the parameter match_func for the massive_bootstrap_form tag
|
||||||
"""
|
"""
|
||||||
return (
|
return (
|
||||||
'function(q, sync) {{'
|
'function(q, sync) {{'
|
||||||
|
@ -122,20 +122,20 @@ def generate_ipv4_match_func( is_type_tt ) :
|
||||||
type_id = f_type_id( is_type_tt )
|
type_id = f_type_id( is_type_tt )
|
||||||
)
|
)
|
||||||
|
|
||||||
def generate_ipv4_bft_param( form, is_type_tt ):
|
def generate_ipv4_mbf_param( form, is_type_tt ):
|
||||||
""" Generate all the parameters to use with the bootstrap_form_typeahead
|
""" Generate all the parameters to use with the massive_bootstrap_form
|
||||||
tag """
|
tag """
|
||||||
i_choices = { 'ipv4': generate_ipv4_choices( form ) }
|
i_choices = { 'ipv4': generate_ipv4_choices( form ) }
|
||||||
i_engine = { 'ipv4': generate_ipv4_engine( is_type_tt ) }
|
i_engine = { 'ipv4': generate_ipv4_engine( is_type_tt ) }
|
||||||
i_match_func = { 'ipv4': generate_ipv4_match_func( is_type_tt ) }
|
i_match_func = { 'ipv4': generate_ipv4_match_func( is_type_tt ) }
|
||||||
i_update_on = { 'ipv4': [f_type_id( is_type_tt )] }
|
i_update_on = { 'ipv4': [f_type_id( is_type_tt )] }
|
||||||
i_bft_param = {
|
i_mbf_param = {
|
||||||
'choices': i_choices,
|
'choices': i_choices,
|
||||||
'engine': i_engine,
|
'engine': i_engine,
|
||||||
'match_func': i_match_func,
|
'match_func': i_match_func,
|
||||||
'update_on': i_update_on
|
'update_on': i_update_on
|
||||||
}
|
}
|
||||||
return i_bft_param
|
return i_mbf_param
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def new_machine(request, userid):
|
def new_machine(request, userid):
|
||||||
|
@ -183,8 +183,8 @@ def new_machine(request, userid):
|
||||||
reversion.set_comment("Création")
|
reversion.set_comment("Création")
|
||||||
messages.success(request, "La machine a été créée")
|
messages.success(request, "La machine a été créée")
|
||||||
return redirect("/users/profil/" + str(user.id))
|
return redirect("/users/profil/" + str(user.id))
|
||||||
i_bft_param = generate_ipv4_bft_param( interface, False )
|
i_mbf_param = generate_ipv4_mbf_param( interface, False )
|
||||||
return form({'machineform': machine, 'interfaceform': interface, 'domainform': domain, 'i_bft_param': i_bft_param}, 'machines/machine.html', request)
|
return form({'machineform': machine, 'interfaceform': interface, 'domainform': domain, 'i_mbf_param': i_mbf_param}, 'machines/machine.html', request)
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def edit_interface(request, interfaceid):
|
def edit_interface(request, interfaceid):
|
||||||
|
@ -223,8 +223,8 @@ def edit_interface(request, interfaceid):
|
||||||
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in domain_form.changed_data))
|
reversion.set_comment("Champs modifié(s) : %s" % ', '.join(field for field in domain_form.changed_data))
|
||||||
messages.success(request, "La machine a été modifiée")
|
messages.success(request, "La machine a été modifiée")
|
||||||
return redirect("/users/profil/" + str(interface.machine.user.id))
|
return redirect("/users/profil/" + str(interface.machine.user.id))
|
||||||
i_bft_param = generate_ipv4_bft_param( interface_form, False )
|
i_mbf_param = generate_ipv4_mbf_param( interface_form, False )
|
||||||
return form({'machineform': machine_form, 'interfaceform': interface_form, 'domainform': domain_form, 'i_bft_param': i_bft_param}, 'machines/machine.html', request)
|
return form({'machineform': machine_form, 'interfaceform': interface_form, 'domainform': domain_form, 'i_mbf_param': i_mbf_param}, 'machines/machine.html', request)
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def del_machine(request, machineid):
|
def del_machine(request, machineid):
|
||||||
|
@ -282,8 +282,8 @@ def new_interface(request, machineid):
|
||||||
reversion.set_comment("Création")
|
reversion.set_comment("Création")
|
||||||
messages.success(request, "L'interface a été ajoutée")
|
messages.success(request, "L'interface a été ajoutée")
|
||||||
return redirect("/users/profil/" + str(machine.user.id))
|
return redirect("/users/profil/" + str(machine.user.id))
|
||||||
i_bft_param = generate_ipv4_bft_param( interface_form, False )
|
i_mbf_param = generate_ipv4_mbf_param( interface_form, False )
|
||||||
return form({'interfaceform': interface_form, 'domainform': domain_form, 'i_bft_param': i_bft_param}, 'machines/machine.html', request)
|
return form({'interfaceform': interface_form, 'domainform': domain_form, 'i_mbf_param': i_mbf_param}, 'machines/machine.html', request)
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def del_interface(request, interfaceid):
|
def del_interface(request, interfaceid):
|
||||||
|
|
|
@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
{% load bootstrap_form_typeahead %}
|
{% load massive_bootstrap_form %}
|
||||||
|
|
||||||
{% block title %}Création et modification des préférences{% endblock %}
|
{% block title %}Création et modification des préférences{% endblock %}
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
<form class="form" method="post">
|
<form class="form" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% bootstrap_form_typeahead options 'utilisateur_asso' %}
|
{% massive_bootstrap_form options 'utilisateur_asso' %}
|
||||||
{% bootstrap_button "Créer ou modifier" button_type="submit" icon="star" %}
|
{% bootstrap_button "Créer ou modifier" button_type="submit" icon="star" %}
|
||||||
</form>
|
</form>
|
||||||
<br />
|
<br />
|
||||||
|
|
|
@ -1,386 +0,0 @@
|
||||||
# -*- mode: python; coding: utf-8 -*-
|
|
||||||
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
|
||||||
# se veut agnostique au réseau considéré, de manière à être installable en
|
|
||||||
# quelques clics.
|
|
||||||
#
|
|
||||||
# Copyright © 2017 Maël Kervella
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify
|
|
||||||
# it under the terms of the GNU General Public License as published by
|
|
||||||
# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
# (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful,
|
|
||||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
# GNU General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License along
|
|
||||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
|
||||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
||||||
|
|
||||||
from django import template
|
|
||||||
from django.utils.safestring import mark_safe
|
|
||||||
from django.forms import TextInput
|
|
||||||
from bootstrap3.templatetags.bootstrap3 import bootstrap_form
|
|
||||||
from bootstrap3.utils import render_tag
|
|
||||||
from bootstrap3.forms import render_field
|
|
||||||
|
|
||||||
register = template.Library()
|
|
||||||
|
|
||||||
@register.simple_tag
|
|
||||||
def bootstrap_form_typeahead(django_form, typeahead_fields, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
Render a form where some specific fields are rendered using Typeahead.
|
|
||||||
Using Typeahead really improves the performance, the speed and UX when
|
|
||||||
dealing with very large datasets (select with 50k+ elts for instance).
|
|
||||||
For convenience, it accepts the same parameters as a standard bootstrap
|
|
||||||
can accept.
|
|
||||||
|
|
||||||
**Tag name**::
|
|
||||||
|
|
||||||
bootstrap_form_typeahead
|
|
||||||
|
|
||||||
**Parameters**:
|
|
||||||
|
|
||||||
form
|
|
||||||
The form that is to be rendered
|
|
||||||
|
|
||||||
typeahead_fields
|
|
||||||
A list of field names (comma separated) that should be rendered
|
|
||||||
with typeahead instead of the default bootstrap renderer.
|
|
||||||
|
|
||||||
bft_param
|
|
||||||
A dict of parameters for the bootstrap_form_typeahead tag. The
|
|
||||||
possible parameters are the following.
|
|
||||||
|
|
||||||
choices
|
|
||||||
A dict of strings representing the choices in JS. The keys of
|
|
||||||
the dict are the names of the concerned fields. The choices
|
|
||||||
must be an array of objects. Each of those objects must at
|
|
||||||
least have the fields 'key' (value to send) and 'value' (value
|
|
||||||
to display). Other fields can be added as desired.
|
|
||||||
For a more complex structure you should also consider
|
|
||||||
reimplementing the engine and the match_func.
|
|
||||||
If not specified, the key is the id of the object and the value
|
|
||||||
is its string representation as in a normal bootstrap form.
|
|
||||||
Example :
|
|
||||||
'choices' : {
|
|
||||||
'field_A':'[{key:0,value:"choice0",extra:"data0"},{...},...]',
|
|
||||||
'field_B':...,
|
|
||||||
...
|
|
||||||
}
|
|
||||||
|
|
||||||
engine
|
|
||||||
A dict of strings representating the engine used for matching
|
|
||||||
queries and possible values with typeahead. The keys of the
|
|
||||||
dict are the names of the concerned fields. The string is valid
|
|
||||||
JS code.
|
|
||||||
If not specified, BloodHound with relevant basic properties is
|
|
||||||
used.
|
|
||||||
Example :
|
|
||||||
'engine' : {'field_A': 'new Bloodhound()', 'field_B': ..., ...}
|
|
||||||
|
|
||||||
match_func
|
|
||||||
A dict of strings representing a valid JS function used in the
|
|
||||||
dataset to overload the matching engine. The keys of the dict
|
|
||||||
are the names of the concerned fields. This function is used
|
|
||||||
the source of the dataset. This function receives 2 parameters,
|
|
||||||
the query and the synchronize function as specified in
|
|
||||||
typeahead.js documentation. If needed, the local variables
|
|
||||||
'choices_<fieldname>' and 'engine_<fieldname>' contains
|
|
||||||
respectively the array of all possible values and the engine
|
|
||||||
to match queries with possible values.
|
|
||||||
If not specified, the function used display up to the 10 first
|
|
||||||
elements if the query is empty and else the matching results.
|
|
||||||
Example :
|
|
||||||
'match_func' : {
|
|
||||||
'field_A': 'function(q, sync) { engine.search(q, sync); }',
|
|
||||||
'field_B': ...,
|
|
||||||
...
|
|
||||||
}
|
|
||||||
|
|
||||||
update_on
|
|
||||||
A dict of list of ids that the values depends on. The engine
|
|
||||||
and the typeahead properties are recalculated and reapplied.
|
|
||||||
Example :
|
|
||||||
'addition' : {
|
|
||||||
'field_A' : [ 'id0', 'id1', ... ] ,
|
|
||||||
'field_B' : ... ,
|
|
||||||
...
|
|
||||||
}
|
|
||||||
|
|
||||||
See boostrap_form_ for other arguments
|
|
||||||
|
|
||||||
**Usage**::
|
|
||||||
|
|
||||||
{% bootstrap_form_typeahead
|
|
||||||
form
|
|
||||||
[ '<field1>[,<field2>[,...]]' ]
|
|
||||||
[ {
|
|
||||||
[ 'choices': {
|
|
||||||
[ '<field1>': '<choices1>'
|
|
||||||
[, '<field2>': '<choices2>'
|
|
||||||
[, ... ] ] ]
|
|
||||||
} ]
|
|
||||||
[, 'engine': {
|
|
||||||
[ '<field1>': '<engine1>'
|
|
||||||
[, '<field2>': '<engine2>'
|
|
||||||
[, ... ] ] ]
|
|
||||||
} ]
|
|
||||||
[, 'match_func': {
|
|
||||||
[ '<field1>': '<match_func1>'
|
|
||||||
[, '<field2>': '<match_func2>'
|
|
||||||
[, ... ] ] ]
|
|
||||||
} ]
|
|
||||||
[, 'update_on': {
|
|
||||||
[ '<field1>': '<update_on1>'
|
|
||||||
[, '<field2>': '<update_on2>'
|
|
||||||
[, ... ] ] ]
|
|
||||||
} ]
|
|
||||||
} ]
|
|
||||||
[ <standard boostrap_form parameters> ]
|
|
||||||
%}
|
|
||||||
|
|
||||||
**Example**:
|
|
||||||
|
|
||||||
{% bootstrap_form_typeahead form 'ipv4' choices='[...]' %}
|
|
||||||
"""
|
|
||||||
|
|
||||||
t_fields = typeahead_fields.split(',')
|
|
||||||
params = kwargs.get('bft_param', {})
|
|
||||||
exclude = params.get('exclude', None)
|
|
||||||
exclude = exclude.split(',') if exclude else []
|
|
||||||
t_choices = params.get('choices', {})
|
|
||||||
t_engine = params.get('engine', {})
|
|
||||||
t_match_func = params.get('match_func', {})
|
|
||||||
t_update_on = params.get('update_on', {})
|
|
||||||
hidden = [h.name for h in django_form.hidden_fields()]
|
|
||||||
|
|
||||||
form = ''
|
|
||||||
for f_name, f_value in django_form.fields.items() :
|
|
||||||
if not f_name in exclude :
|
|
||||||
if f_name in t_fields and not f_name in hidden :
|
|
||||||
f_bound = f_value.get_bound_field( django_form, f_name )
|
|
||||||
f_value.widget = TextInput(
|
|
||||||
attrs={
|
|
||||||
'name': 'typeahead_'+f_name,
|
|
||||||
'placeholder': f_value.empty_label
|
|
||||||
}
|
|
||||||
)
|
|
||||||
form += render_field(
|
|
||||||
f_value.get_bound_field( django_form, f_name ),
|
|
||||||
*args,
|
|
||||||
**kwargs
|
|
||||||
)
|
|
||||||
form += render_tag(
|
|
||||||
'div',
|
|
||||||
content = hidden_tag( f_bound, f_name ) +
|
|
||||||
typeahead_js(
|
|
||||||
f_name,
|
|
||||||
f_value,
|
|
||||||
f_bound,
|
|
||||||
t_choices,
|
|
||||||
t_engine,
|
|
||||||
t_match_func,
|
|
||||||
t_update_on
|
|
||||||
)
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
form += render_field(
|
|
||||||
f_value.get_bound_field(django_form, f_name),
|
|
||||||
*args,
|
|
||||||
**kwargs
|
|
||||||
)
|
|
||||||
|
|
||||||
return mark_safe( form )
|
|
||||||
|
|
||||||
def input_id( f_bound ) :
|
|
||||||
""" The id of the HTML input element """
|
|
||||||
return f_bound.auto_id
|
|
||||||
|
|
||||||
def hidden_id( f_bound ):
|
|
||||||
""" The id of the HTML hidden input element """
|
|
||||||
return input_id( f_bound ) +'_hidden'
|
|
||||||
|
|
||||||
def hidden_tag( f_bound, f_name ):
|
|
||||||
""" The HTML hidden input element """
|
|
||||||
return render_tag(
|
|
||||||
'input',
|
|
||||||
attrs={
|
|
||||||
'id': hidden_id( f_bound ),
|
|
||||||
'name': f_bound.html_name,
|
|
||||||
'type': 'hidden',
|
|
||||||
'value': f_bound.value() or ""
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
def typeahead_js( f_name, f_value, f_bound,
|
|
||||||
t_choices, t_engine, t_match_func, t_update_on ) :
|
|
||||||
""" The whole script to use """
|
|
||||||
|
|
||||||
choices = mark_safe( t_choices[f_name] ) if f_name in t_choices.keys() \
|
|
||||||
else default_choices( f_value )
|
|
||||||
|
|
||||||
engine = mark_safe( t_engine[f_name] ) if f_name in t_engine.keys() \
|
|
||||||
else default_engine ( f_name )
|
|
||||||
|
|
||||||
match_func = mark_safe(t_match_func[f_name]) \
|
|
||||||
if f_name in t_match_func.keys() else default_match_func( f_name )
|
|
||||||
|
|
||||||
update_on = t_update_on[f_name] if f_name in t_update_on.keys() else []
|
|
||||||
|
|
||||||
js_content = (
|
|
||||||
'var choices_{f_name} = {choices};'
|
|
||||||
'var engine_{f_name};'
|
|
||||||
'var setup_{f_name} = function() {{'
|
|
||||||
'engine_{f_name} = {engine};'
|
|
||||||
'$( "#{input_id}" ).typeahead( "destroy" );'
|
|
||||||
'$( "#{input_id}" ).typeahead( {datasets} );'
|
|
||||||
'}};'
|
|
||||||
'$( "#{input_id}" ).bind( "typeahead:select", {updater} );'
|
|
||||||
'$( "#{input_id}" ).bind( "typeahead:change", {change} );'
|
|
||||||
'{updates}'
|
|
||||||
'$( "#{input_id}" ).ready( function() {{'
|
|
||||||
'setup_{f_name}();'
|
|
||||||
'{init_input}'
|
|
||||||
'}} );'
|
|
||||||
).format(
|
|
||||||
f_name = f_name,
|
|
||||||
choices = choices,
|
|
||||||
engine = engine,
|
|
||||||
input_id = input_id( f_bound ),
|
|
||||||
datasets = default_datasets( f_name, match_func ),
|
|
||||||
updater = typeahead_updater( f_bound ),
|
|
||||||
change = typeahead_change( f_bound ),
|
|
||||||
updates = ''.join( [ (
|
|
||||||
'$( "#{u_id}" ).change( function() {{'
|
|
||||||
'setup_{f_name}();'
|
|
||||||
'{reset_input}'
|
|
||||||
'}} );'
|
|
||||||
).format(
|
|
||||||
u_id = u_id,
|
|
||||||
reset_input = reset_input( f_bound ),
|
|
||||||
f_name = f_name
|
|
||||||
) for u_id in update_on ]
|
|
||||||
),
|
|
||||||
init_input = init_input( f_name, f_bound ),
|
|
||||||
)
|
|
||||||
|
|
||||||
return render_tag( 'script', content=mark_safe( js_content ) )
|
|
||||||
|
|
||||||
def init_input( f_name, f_bound ) :
|
|
||||||
""" The JS script to init the fields values """
|
|
||||||
init_key = f_bound.value() or '""'
|
|
||||||
return (
|
|
||||||
'$( "#{input_id}" ).typeahead("val", {init_val});'
|
|
||||||
'$( "#{hidden_id}" ).val( {init_key} );'
|
|
||||||
).format(
|
|
||||||
input_id = input_id( f_bound ),
|
|
||||||
init_val = '""' if init_key == '""' else
|
|
||||||
'engine_{f_name}.get( {init_key} )[0].value'.format(
|
|
||||||
f_name = f_name,
|
|
||||||
init_key = init_key
|
|
||||||
),
|
|
||||||
init_key = init_key,
|
|
||||||
hidden_id = hidden_id( f_bound )
|
|
||||||
)
|
|
||||||
|
|
||||||
def reset_input( f_bound ) :
|
|
||||||
""" The JS script to reset the fields values """
|
|
||||||
return (
|
|
||||||
'$( "#{input_id}" ).typeahead("val", "");'
|
|
||||||
'$( "#{hidden_id}" ).val( "" );'
|
|
||||||
).format(
|
|
||||||
input_id = input_id( f_bound ),
|
|
||||||
hidden_id = hidden_id( f_bound )
|
|
||||||
)
|
|
||||||
|
|
||||||
def default_choices( f_value ) :
|
|
||||||
""" The JS script creating the variable choices_<fieldname> """
|
|
||||||
return '[{objects}]'.format(
|
|
||||||
objects = ','.join(
|
|
||||||
[ '{{key:{k},value:"{v}"}}'.format(
|
|
||||||
k = choice[0] if choice[0] != '' else '""',
|
|
||||||
v = choice[1]
|
|
||||||
) for choice in f_value.choices ]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def default_engine ( f_name ) :
|
|
||||||
""" The JS script creating the variable engine_<field_name> """
|
|
||||||
return (
|
|
||||||
'new Bloodhound({{'
|
|
||||||
'datumTokenizer: Bloodhound.tokenizers.obj.whitespace("value"),'
|
|
||||||
'queryTokenizer: Bloodhound.tokenizers.whitespace,'
|
|
||||||
'local: choices_{f_name},'
|
|
||||||
'identify: function(obj) {{ return obj.key; }}'
|
|
||||||
'}})'
|
|
||||||
).format(
|
|
||||||
f_name = f_name
|
|
||||||
)
|
|
||||||
|
|
||||||
def default_datasets( f_name, match_func ) :
|
|
||||||
""" The JS script creating the datasets to use with typeahead """
|
|
||||||
return (
|
|
||||||
'{{'
|
|
||||||
'hint: true,'
|
|
||||||
'highlight: true,'
|
|
||||||
'minLength: 0'
|
|
||||||
'}},'
|
|
||||||
'{{'
|
|
||||||
'display: "value",'
|
|
||||||
'name: "{f_name}",'
|
|
||||||
'source: {match_func}'
|
|
||||||
'}}'
|
|
||||||
).format(
|
|
||||||
f_name = f_name,
|
|
||||||
match_func = match_func
|
|
||||||
)
|
|
||||||
|
|
||||||
def default_match_func ( f_name ) :
|
|
||||||
""" The JS script creating the matching function to use with typeahed """
|
|
||||||
return (
|
|
||||||
'function ( q, sync ) {{'
|
|
||||||
'if ( q === "" ) {{'
|
|
||||||
'var first = choices_{f_name}.slice( 0, 5 ).map('
|
|
||||||
'function ( obj ) {{ return obj.key; }}'
|
|
||||||
');'
|
|
||||||
'sync( engine_{f_name}.get( first ) );'
|
|
||||||
'}} else {{'
|
|
||||||
'engine_{f_name}.search( q, sync );'
|
|
||||||
'}}'
|
|
||||||
'}}'
|
|
||||||
).format(
|
|
||||||
f_name = f_name
|
|
||||||
)
|
|
||||||
|
|
||||||
def typeahead_updater( f_bound ):
|
|
||||||
""" The JS script creating the function triggered when an item is
|
|
||||||
selected through typeahead """
|
|
||||||
return (
|
|
||||||
'function(evt, item) {{'
|
|
||||||
'$( "#{hidden_id}" ).val( item.key );'
|
|
||||||
'$( "#{hidden_id}" ).change();'
|
|
||||||
'return item;'
|
|
||||||
'}}'
|
|
||||||
).format(
|
|
||||||
hidden_id = hidden_id( f_bound )
|
|
||||||
)
|
|
||||||
|
|
||||||
def typeahead_change( f_bound ):
|
|
||||||
""" The JS script creating the function triggered when an item is changed
|
|
||||||
(i.e. looses focus and value has changed since the moment it gained focus
|
|
||||||
"""
|
|
||||||
return (
|
|
||||||
'function(evt) {{'
|
|
||||||
'if ( $( "#{input_id}" ).typeahead( "val" ) === "" ) {{'
|
|
||||||
'$( "#{hidden_id}" ).val( "" );'
|
|
||||||
'$( "#{hidden_id}" ).change();'
|
|
||||||
'}}'
|
|
||||||
'}}'
|
|
||||||
).format(
|
|
||||||
input_id = input_id( f_bound ),
|
|
||||||
hidden_id = hidden_id( f_bound )
|
|
||||||
)
|
|
||||||
|
|
572
re2o/templatetags/massive_bootstrap_form.py
Normal file
572
re2o/templatetags/massive_bootstrap_form.py
Normal file
|
@ -0,0 +1,572 @@
|
||||||
|
# -*- mode: python; coding: utf-8 -*-
|
||||||
|
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il
|
||||||
|
# se veut agnostique au réseau considéré, de manière à être installable en
|
||||||
|
# quelques clics.
|
||||||
|
#
|
||||||
|
# Copyright © 2017 Maël Kervella
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation; either version 2 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along
|
||||||
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
|
||||||
|
from django import template
|
||||||
|
from django.utils.safestring import mark_safe
|
||||||
|
from django.forms import TextInput
|
||||||
|
from django.forms.widgets import Select
|
||||||
|
from bootstrap3.templatetags.bootstrap3 import bootstrap_form
|
||||||
|
from bootstrap3.utils import render_tag
|
||||||
|
from bootstrap3.forms import render_field
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
@register.simple_tag
|
||||||
|
def massive_bootstrap_form(form, mbf_fields, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Render a form where some specific fields are rendered using Twitter
|
||||||
|
Typeahead and/or splitree's Bootstrap Tokenfield to improve the performance, the
|
||||||
|
speed and UX when dealing with very large datasets (select with 50k+ elts
|
||||||
|
for instance).
|
||||||
|
When the fields specified should normally be rendered as a select with
|
||||||
|
single selectable option, Twitter Typeahead is used for a better display
|
||||||
|
and the matching query engine. When dealing with multiple selectable
|
||||||
|
options, sliptree's Bootstrap Tokenfield in addition with Typeahead.
|
||||||
|
For convenience, it accepts the same parameters as a standard bootstrap
|
||||||
|
can accept.
|
||||||
|
|
||||||
|
**Tag name**::
|
||||||
|
|
||||||
|
massive_bootstrap_form
|
||||||
|
|
||||||
|
**Parameters**:
|
||||||
|
|
||||||
|
form (required)
|
||||||
|
The form that is to be rendered
|
||||||
|
|
||||||
|
mbf_fields (optional)
|
||||||
|
A list of field names (comma separated) that should be rendered
|
||||||
|
with Typeahead/Tokenfield instead of the default bootstrap
|
||||||
|
renderer.
|
||||||
|
If not specified, all fields will be rendered as a normal bootstrap
|
||||||
|
field.
|
||||||
|
|
||||||
|
mbf_param (optional)
|
||||||
|
A dict of parameters for the massive_bootstrap_form tag. The
|
||||||
|
possible parameters are the following.
|
||||||
|
|
||||||
|
choices (optional)
|
||||||
|
A dict of strings representing the choices in JS. The keys of
|
||||||
|
the dict are the names of the concerned fields. The choices
|
||||||
|
must be an array of objects. Each of those objects must at
|
||||||
|
least have the fields 'key' (value to send) and 'value' (value
|
||||||
|
to display). Other fields can be added as desired.
|
||||||
|
For a more complex structure you should also consider
|
||||||
|
reimplementing the engine and the match_func.
|
||||||
|
If not specified, the key is the id of the object and the value
|
||||||
|
is its string representation as in a normal bootstrap form.
|
||||||
|
Example :
|
||||||
|
'choices' : {
|
||||||
|
'field_A':'[{key:0,value:"choice0",extra:"data0"},{...},...]',
|
||||||
|
'field_B':...,
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
engine (optional)
|
||||||
|
A dict of strings representating the engine used for matching
|
||||||
|
queries and possible values with typeahead. The keys of the
|
||||||
|
dict are the names of the concerned fields. The string is valid
|
||||||
|
JS code.
|
||||||
|
If not specified, BloodHound with relevant basic properties is
|
||||||
|
used.
|
||||||
|
Example :
|
||||||
|
'engine' : {'field_A': 'new Bloodhound()', 'field_B': ..., ...}
|
||||||
|
|
||||||
|
match_func (optional)
|
||||||
|
A dict of strings representing a valid JS function used in the
|
||||||
|
dataset to overload the matching engine. The keys of the dict
|
||||||
|
are the names of the concerned fields. This function is used
|
||||||
|
the source of the dataset. This function receives 2 parameters,
|
||||||
|
the query and the synchronize function as specified in
|
||||||
|
typeahead.js documentation. If needed, the local variables
|
||||||
|
'choices_<fieldname>' and 'engine_<fieldname>' contains
|
||||||
|
respectively the array of all possible values and the engine
|
||||||
|
to match queries with possible values.
|
||||||
|
If not specified, the function used display up to the 10 first
|
||||||
|
elements if the query is empty and else the matching results.
|
||||||
|
Example :
|
||||||
|
'match_func' : {
|
||||||
|
'field_A': 'function(q, sync) { engine.search(q, sync); }',
|
||||||
|
'field_B': ...,
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
update_on (optional)
|
||||||
|
A dict of list of ids that the values depends on. The engine
|
||||||
|
and the typeahead properties are recalculated and reapplied.
|
||||||
|
Example :
|
||||||
|
'addition' : {
|
||||||
|
'field_A' : [ 'id0', 'id1', ... ] ,
|
||||||
|
'field_B' : ... ,
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
See boostrap_form_ for other arguments
|
||||||
|
|
||||||
|
**Usage**::
|
||||||
|
|
||||||
|
{% massive_bootstrap_form
|
||||||
|
form
|
||||||
|
[ '<field1>[,<field2>[,...]]' ]
|
||||||
|
[ mbf_param = {
|
||||||
|
[ 'choices': {
|
||||||
|
[ '<field1>': '<choices1>'
|
||||||
|
[, '<field2>': '<choices2>'
|
||||||
|
[, ... ] ] ]
|
||||||
|
} ]
|
||||||
|
[, 'engine': {
|
||||||
|
[ '<field1>': '<engine1>'
|
||||||
|
[, '<field2>': '<engine2>'
|
||||||
|
[, ... ] ] ]
|
||||||
|
} ]
|
||||||
|
[, 'match_func': {
|
||||||
|
[ '<field1>': '<match_func1>'
|
||||||
|
[, '<field2>': '<match_func2>'
|
||||||
|
[, ... ] ] ]
|
||||||
|
} ]
|
||||||
|
[, 'update_on': {
|
||||||
|
[ '<field1>': '<update_on1>'
|
||||||
|
[, '<field2>': '<update_on2>'
|
||||||
|
[, ... ] ] ]
|
||||||
|
} ]
|
||||||
|
} ]
|
||||||
|
[ <standard boostrap_form parameters> ]
|
||||||
|
%}
|
||||||
|
|
||||||
|
**Example**:
|
||||||
|
|
||||||
|
{% massive_bootstrap_form form 'ipv4' choices='[...]' %}
|
||||||
|
"""
|
||||||
|
|
||||||
|
fields = mbf_fields.split(',')
|
||||||
|
param = kwargs.pop('mbf_param', {})
|
||||||
|
exclude = param.get('exclude', '').split(',')
|
||||||
|
choices = param.get('choices', {})
|
||||||
|
engine = param.get('engine', {})
|
||||||
|
match_func = param.get('match_func', {})
|
||||||
|
update_on = param.get('update_on', {})
|
||||||
|
hidden_fields = [h.name for h in form.hidden_fields()]
|
||||||
|
|
||||||
|
html = ''
|
||||||
|
|
||||||
|
for f_name, f_value in form.fields.items() :
|
||||||
|
if not f_name in exclude :
|
||||||
|
if f_name in fields and not f_name in hidden_fields :
|
||||||
|
|
||||||
|
if not isinstance(f_value.widget, Select) :
|
||||||
|
raise ValueError(
|
||||||
|
('Field named {f_name} from {form} is not a Select and'
|
||||||
|
'can\'t be rendered with massive_bootstrap_form.'
|
||||||
|
).format(
|
||||||
|
f_name=f_name,
|
||||||
|
form=form
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
multiple = f_value.widget.allow_multiple_selected
|
||||||
|
f_bound = f_value.get_bound_field( form, f_name )
|
||||||
|
|
||||||
|
f_value.widget = TextInput(
|
||||||
|
attrs = {
|
||||||
|
'name': 'mbf_'+f_name,
|
||||||
|
'placeholder': f_value.empty_label
|
||||||
|
}
|
||||||
|
)
|
||||||
|
html += render_field(
|
||||||
|
f_value.get_bound_field( form, f_name ),
|
||||||
|
*args,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
if multiple :
|
||||||
|
content = mbf_js(
|
||||||
|
f_name,
|
||||||
|
f_value,
|
||||||
|
f_bound,
|
||||||
|
multiple,
|
||||||
|
choices,
|
||||||
|
engine,
|
||||||
|
match_func,
|
||||||
|
update_on
|
||||||
|
)
|
||||||
|
else :
|
||||||
|
content = hidden_tag( f_bound, f_name ) + mbf_js(
|
||||||
|
f_name,
|
||||||
|
f_value,
|
||||||
|
f_bound,
|
||||||
|
multiple,
|
||||||
|
choices,
|
||||||
|
engine,
|
||||||
|
match_func,
|
||||||
|
update_on
|
||||||
|
)
|
||||||
|
html += render_tag(
|
||||||
|
'div',
|
||||||
|
content = content,
|
||||||
|
attrs = { 'id': custom_div_id( f_bound ) }
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
html += render_field(
|
||||||
|
f_value.get_bound_field( form, f_name ),
|
||||||
|
*args,
|
||||||
|
**kwargs
|
||||||
|
)
|
||||||
|
|
||||||
|
return mark_safe( html )
|
||||||
|
|
||||||
|
def input_id( f_bound ) :
|
||||||
|
""" The id of the HTML input element """
|
||||||
|
return f_bound.auto_id
|
||||||
|
|
||||||
|
def hidden_id( f_bound ):
|
||||||
|
""" The id of the HTML hidden input element """
|
||||||
|
return input_id( f_bound ) + '_hidden'
|
||||||
|
|
||||||
|
def custom_div_id( f_bound ):
|
||||||
|
""" The id of the HTML div element containing values and script """
|
||||||
|
return input_id( f_bound ) + '_div'
|
||||||
|
|
||||||
|
def hidden_tag( f_bound, f_name ):
|
||||||
|
""" The HTML hidden input element """
|
||||||
|
return render_tag(
|
||||||
|
'input',
|
||||||
|
attrs={
|
||||||
|
'id': hidden_id( f_bound ),
|
||||||
|
'name': f_bound.html_name,
|
||||||
|
'type': 'hidden',
|
||||||
|
'value': f_bound.value() or ""
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
def mbf_js( f_name, f_value, f_bound, multiple,
|
||||||
|
choices_, engine_, match_func_, update_on_ ) :
|
||||||
|
""" The whole script to use """
|
||||||
|
|
||||||
|
choices = ( mark_safe( choices_[f_name] ) if f_name in choices_.keys()
|
||||||
|
else default_choices( f_value ) )
|
||||||
|
|
||||||
|
engine = ( mark_safe( engine_[f_name] ) if f_name in engine_.keys()
|
||||||
|
else default_engine ( f_name ) )
|
||||||
|
|
||||||
|
match_func = ( mark_safe( match_func_[f_name] )
|
||||||
|
if f_name in match_func_.keys() else default_match_func( f_name ) )
|
||||||
|
|
||||||
|
update_on = update_on_[f_name] if f_name in update_on_.keys() else []
|
||||||
|
|
||||||
|
if multiple :
|
||||||
|
js_content = (
|
||||||
|
'var choices_{f_name} = {choices};'
|
||||||
|
'var engine_{f_name};'
|
||||||
|
'var setup_{f_name} = function() {{'
|
||||||
|
'engine_{f_name} = {engine};'
|
||||||
|
'$( "#{input_id}" ).tokenfield( "destroy" );'
|
||||||
|
'$( "#{input_id}" ).tokenfield({{typeahead: [ {datasets} ] }});'
|
||||||
|
'}};'
|
||||||
|
'$( "#{input_id}" ).bind( "tokenfield:createtoken", {create} );'
|
||||||
|
'$( "#{input_id}" ).bind( "tokenfield:edittoken", {edit} );'
|
||||||
|
'$( "#{input_id}" ).bind( "tokenfield:removetoken", {remove} );'
|
||||||
|
'{updates}'
|
||||||
|
'$( "#{input_id}" ).ready( function() {{'
|
||||||
|
'setup_{f_name}();'
|
||||||
|
'{init_input}'
|
||||||
|
'}} );'
|
||||||
|
).format(
|
||||||
|
f_name = f_name,
|
||||||
|
choices = choices,
|
||||||
|
engine = engine,
|
||||||
|
input_id = input_id( f_bound ),
|
||||||
|
datasets = default_datasets( f_name, match_func ),
|
||||||
|
create = tokenfield_create( f_name, f_bound ),
|
||||||
|
edit = tokenfield_edit( f_name, f_bound ),
|
||||||
|
remove = tokenfield_remove( f_name, f_bound ),
|
||||||
|
updates = ''.join( [ (
|
||||||
|
'$( "#{u_id}" ).change( function() {{'
|
||||||
|
'setup_{f_name}();'
|
||||||
|
'{reset_input}'
|
||||||
|
'}} );'
|
||||||
|
).format(
|
||||||
|
u_id = u_id,
|
||||||
|
reset_input = tokenfield_reset_input( f_bound ),
|
||||||
|
f_name = f_name
|
||||||
|
) for u_id in update_on ]
|
||||||
|
),
|
||||||
|
init_input = tokenfield_init_input( f_name, f_bound ),
|
||||||
|
)
|
||||||
|
else :
|
||||||
|
js_content = (
|
||||||
|
'var choices_{f_name} = {choices};'
|
||||||
|
'var engine_{f_name};'
|
||||||
|
'var setup_{f_name} = function() {{'
|
||||||
|
'engine_{f_name} = {engine};'
|
||||||
|
'$( "#{input_id}" ).typeahead( "destroy" );'
|
||||||
|
'$( "#{input_id}" ).typeahead( {datasets} );'
|
||||||
|
'}};'
|
||||||
|
'$( "#{input_id}" ).bind( "typeahead:select", {select} );'
|
||||||
|
'$( "#{input_id}" ).bind( "typeahead:change", {change} );'
|
||||||
|
'{updates}'
|
||||||
|
'$( "#{input_id}" ).ready( function() {{'
|
||||||
|
'setup_{f_name}();'
|
||||||
|
'{init_input}'
|
||||||
|
'}} );'
|
||||||
|
).format(
|
||||||
|
f_name = f_name,
|
||||||
|
choices = choices,
|
||||||
|
engine = engine,
|
||||||
|
input_id = input_id( f_bound ),
|
||||||
|
datasets = default_datasets( f_name, match_func ),
|
||||||
|
select = typeahead_select( f_bound ),
|
||||||
|
change = typeahead_change( f_bound ),
|
||||||
|
updates = ''.join( [ (
|
||||||
|
'$( "#{u_id}" ).change( function() {{'
|
||||||
|
'setup_{f_name}();'
|
||||||
|
'{reset_input}'
|
||||||
|
'}} );'
|
||||||
|
).format(
|
||||||
|
u_id = u_id,
|
||||||
|
reset_input = typeahead_reset_input( f_bound ),
|
||||||
|
f_name = f_name
|
||||||
|
) for u_id in update_on ]
|
||||||
|
),
|
||||||
|
init_input = typeahead_init_input( f_name, f_bound ),
|
||||||
|
)
|
||||||
|
|
||||||
|
return render_tag( 'script', content=mark_safe( js_content ) )
|
||||||
|
|
||||||
|
def typeahead_init_input( f_name, f_bound ) :
|
||||||
|
""" The JS script to init the fields values """
|
||||||
|
init_key = f_bound.value() or '""'
|
||||||
|
return (
|
||||||
|
'$( "#{input_id}" ).typeahead("val", {init_val});'
|
||||||
|
'$( "#{hidden_id}" ).val( {init_key} );'
|
||||||
|
).format(
|
||||||
|
input_id = input_id( f_bound ),
|
||||||
|
init_val = '""' if init_key == '""' else
|
||||||
|
'engine_{f_name}.get( {init_key} )[0].value'.format(
|
||||||
|
f_name = f_name,
|
||||||
|
init_key = init_key
|
||||||
|
),
|
||||||
|
init_key = init_key,
|
||||||
|
hidden_id = hidden_id( f_bound )
|
||||||
|
)
|
||||||
|
|
||||||
|
def typeahead_reset_input( f_bound ) :
|
||||||
|
""" The JS script to reset the fields values """
|
||||||
|
return (
|
||||||
|
'$( "#{input_id}" ).typeahead("val", "");'
|
||||||
|
'$( "#{hidden_id}" ).val( "" );'
|
||||||
|
).format(
|
||||||
|
input_id = input_id( f_bound ),
|
||||||
|
hidden_id = hidden_id( f_bound )
|
||||||
|
)
|
||||||
|
|
||||||
|
def tokenfield_init_input( f_name, f_bound ) :
|
||||||
|
""" The JS script to init the fields values """
|
||||||
|
init_key = f_bound.value() or '""'
|
||||||
|
return (
|
||||||
|
'$( "#{input_id}" ).tokenfield("setTokens", {init_val});'
|
||||||
|
).format(
|
||||||
|
input_id = input_id( f_bound ),
|
||||||
|
init_val = '""' if init_key == '""' else (
|
||||||
|
'engine_{f_name}.get( {init_key} ).map('
|
||||||
|
'function(o) {{ return o.value; }}'
|
||||||
|
')').format(
|
||||||
|
f_name = f_name,
|
||||||
|
init_key = init_key
|
||||||
|
),
|
||||||
|
init_key = init_key,
|
||||||
|
)
|
||||||
|
|
||||||
|
def tokenfield_reset_input( f_bound ) :
|
||||||
|
""" The JS script to reset the fields values """
|
||||||
|
return (
|
||||||
|
'$( "#{input_id}" ).tokenfield("setTokens", "");'
|
||||||
|
).format(
|
||||||
|
input_id = input_id( f_bound ),
|
||||||
|
)
|
||||||
|
|
||||||
|
def default_choices( f_value ) :
|
||||||
|
""" The JS script creating the variable choices_<fieldname> """
|
||||||
|
return '[{objects}]'.format(
|
||||||
|
objects = ','.join(
|
||||||
|
[ '{{key:{k},value:"{v}"}}'.format(
|
||||||
|
k = choice[0] if choice[0] != '' else '""',
|
||||||
|
v = choice[1]
|
||||||
|
) for choice in f_value.choices ]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def default_engine ( f_name ) :
|
||||||
|
""" The JS script creating the variable engine_<field_name> """
|
||||||
|
return (
|
||||||
|
'new Bloodhound({{'
|
||||||
|
'datumTokenizer: Bloodhound.tokenizers.obj.whitespace("value"),'
|
||||||
|
'queryTokenizer: Bloodhound.tokenizers.whitespace,'
|
||||||
|
'local: choices_{f_name},'
|
||||||
|
'identify: function(obj) {{ return obj.key; }}'
|
||||||
|
'}})'
|
||||||
|
).format(
|
||||||
|
f_name = f_name
|
||||||
|
)
|
||||||
|
|
||||||
|
def default_datasets( f_name, match_func ) :
|
||||||
|
""" The JS script creating the datasets to use with typeahead """
|
||||||
|
return (
|
||||||
|
'{{'
|
||||||
|
'hint: true,'
|
||||||
|
'highlight: true,'
|
||||||
|
'minLength: 0'
|
||||||
|
'}},'
|
||||||
|
'{{'
|
||||||
|
'display: "value",'
|
||||||
|
'name: "{f_name}",'
|
||||||
|
'source: {match_func}'
|
||||||
|
'}}'
|
||||||
|
).format(
|
||||||
|
f_name = f_name,
|
||||||
|
match_func = match_func
|
||||||
|
)
|
||||||
|
|
||||||
|
def default_match_func ( f_name ) :
|
||||||
|
""" The JS script creating the matching function to use with typeahed """
|
||||||
|
return (
|
||||||
|
'function ( q, sync ) {{'
|
||||||
|
'if ( q === "" ) {{'
|
||||||
|
'var first = choices_{f_name}.slice( 0, 5 ).map('
|
||||||
|
'function ( obj ) {{ return obj.key; }}'
|
||||||
|
');'
|
||||||
|
'sync( engine_{f_name}.get( first ) );'
|
||||||
|
'}} else {{'
|
||||||
|
'engine_{f_name}.search( q, sync );'
|
||||||
|
'}}'
|
||||||
|
'}}'
|
||||||
|
).format(
|
||||||
|
f_name = f_name
|
||||||
|
)
|
||||||
|
|
||||||
|
def typeahead_select( f_bound ):
|
||||||
|
""" The JS script creating the function triggered when an item is
|
||||||
|
selected through typeahead """
|
||||||
|
return (
|
||||||
|
'function(evt, item) {{'
|
||||||
|
'$( "#{hidden_id}" ).val( item.key );'
|
||||||
|
'$( "#{hidden_id}" ).change();'
|
||||||
|
'return item;'
|
||||||
|
'}}'
|
||||||
|
).format(
|
||||||
|
hidden_id = hidden_id( f_bound )
|
||||||
|
)
|
||||||
|
|
||||||
|
def typeahead_change( f_bound ):
|
||||||
|
""" The JS script creating the function triggered when an item is changed
|
||||||
|
(i.e. looses focus and value has changed since the moment it gained focus
|
||||||
|
"""
|
||||||
|
return (
|
||||||
|
'function(evt) {{'
|
||||||
|
'if ( $( "#{input_id}" ).typeahead( "val" ) === "" ) {{'
|
||||||
|
'$( "#{hidden_id}" ).val( "" );'
|
||||||
|
'$( "#{hidden_id}" ).change();'
|
||||||
|
'}}'
|
||||||
|
'}}'
|
||||||
|
).format(
|
||||||
|
input_id = input_id( f_bound ),
|
||||||
|
hidden_id = hidden_id( f_bound )
|
||||||
|
)
|
||||||
|
|
||||||
|
def tokenfield_create( f_name, f_bound ):
|
||||||
|
""" The JS script triggered when a new token is created in tokenfield. """
|
||||||
|
return (
|
||||||
|
'function(evt) {{'
|
||||||
|
'var k = evt.attrs.key;'
|
||||||
|
'if (!k) {{'
|
||||||
|
'var data = evt.attrs.value;'
|
||||||
|
'var i = 0;'
|
||||||
|
'while ( i<choices_{f_name}.length &&'
|
||||||
|
'choices_{f_name}[i].value !== data ) {{'
|
||||||
|
'i++;'
|
||||||
|
'}}'
|
||||||
|
'if ( i === choices_{f_name}.length ) {{ return false; }}'
|
||||||
|
'k = choices_{f_name}[i].key;'
|
||||||
|
'}}'
|
||||||
|
'var new_input = document.createElement("input");'
|
||||||
|
'new_input.type = "hidden";'
|
||||||
|
'new_input.id = "{hidden_id}_"+k.toString();'
|
||||||
|
'new_input.value = k.toString();'
|
||||||
|
'new_input.name = "{name}";'
|
||||||
|
'$( "#{div_id}" ).append(new_input);'
|
||||||
|
'}}'
|
||||||
|
).format(
|
||||||
|
f_name = f_name,
|
||||||
|
hidden_id = hidden_id( f_bound ),
|
||||||
|
name = f_bound.html_name,
|
||||||
|
div_id = custom_div_id( f_bound )
|
||||||
|
)
|
||||||
|
|
||||||
|
def tokenfield_edit( f_name, f_bound ):
|
||||||
|
""" The JS script triggered when a token is edited in tokenfield. """
|
||||||
|
return (
|
||||||
|
'function(evt) {{'
|
||||||
|
'var k = evt.attrs.key;'
|
||||||
|
'if (!k) {{'
|
||||||
|
'var data = evt.attrs.value;'
|
||||||
|
'var i = 0;'
|
||||||
|
'while ( i<choices_{f_name}.length &&'
|
||||||
|
'choices_{f_name}[i].value !== data ) {{'
|
||||||
|
'i++;'
|
||||||
|
'}}'
|
||||||
|
'if ( i === choices_{f_name}.length ) {{ return true; }}'
|
||||||
|
'k = choices_{f_name}[i].key;'
|
||||||
|
'}}'
|
||||||
|
'var old_input = document.getElementById('
|
||||||
|
'"{hidden_id}_"+k.toString()'
|
||||||
|
');'
|
||||||
|
'old_input.parentNode.removeChild(old_input);'
|
||||||
|
'}}'
|
||||||
|
).format(
|
||||||
|
f_name = f_name,
|
||||||
|
hidden_id = hidden_id( f_bound )
|
||||||
|
)
|
||||||
|
|
||||||
|
def tokenfield_remove( f_name, f_bound ):
|
||||||
|
""" The JS script trigggered when a token is removed from tokenfield. """
|
||||||
|
return (
|
||||||
|
'function(evt) {{'
|
||||||
|
'var k = evt.attrs.key;'
|
||||||
|
'if (!k) {{'
|
||||||
|
'var data = evt.attrs.value;'
|
||||||
|
'var i = 0;'
|
||||||
|
'while ( i<choices_{f_name}.length &&'
|
||||||
|
'choices_{f_name}[i].value !== data ) {{'
|
||||||
|
'i++;'
|
||||||
|
'}}'
|
||||||
|
'if ( i === choices_{f_name}.length ) {{ return true; }}'
|
||||||
|
'k = choices_{f_name}[i].key;'
|
||||||
|
'}}'
|
||||||
|
'var old_input = document.getElementById('
|
||||||
|
'"{hidden_id}_"+k.toString()'
|
||||||
|
');'
|
||||||
|
'old_input.parentNode.removeChild(old_input);'
|
||||||
|
'}}'
|
||||||
|
).format(
|
||||||
|
f_name = f_name,
|
||||||
|
hidden_id = hidden_id( f_bound )
|
||||||
|
)
|
||||||
|
|
210
static/css/bootstrap-tokenfield.css
vendored
Normal file
210
static/css/bootstrap-tokenfield.css
vendored
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
/*!
|
||||||
|
* bootstrap-tokenfield
|
||||||
|
* https://github.com/sliptree/bootstrap-tokenfield
|
||||||
|
* Copyright 2013-2014 Sliptree and other contributors; Licensed MIT
|
||||||
|
*/
|
||||||
|
@-webkit-keyframes blink {
|
||||||
|
0% {
|
||||||
|
border-color: #ededed;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
border-color: #b94a48;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@-moz-keyframes blink {
|
||||||
|
0% {
|
||||||
|
border-color: #ededed;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
border-color: #b94a48;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes blink {
|
||||||
|
0% {
|
||||||
|
border-color: #ededed;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
border-color: #b94a48;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.tokenfield {
|
||||||
|
height: auto;
|
||||||
|
min-height: 34px;
|
||||||
|
padding-bottom: 0px;
|
||||||
|
}
|
||||||
|
.tokenfield.focus {
|
||||||
|
border-color: #66afe9;
|
||||||
|
outline: 0;
|
||||||
|
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, 0.6);
|
||||||
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, 0.6);
|
||||||
|
}
|
||||||
|
.tokenfield .token {
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
-webkit-border-radius: 3px;
|
||||||
|
-moz-border-radius: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
display: inline-block;
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
background-color: #ededed;
|
||||||
|
white-space: nowrap;
|
||||||
|
margin: -1px 5px 5px 0;
|
||||||
|
height: 22px;
|
||||||
|
vertical-align: top;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
.tokenfield .token:hover {
|
||||||
|
border-color: #b9b9b9;
|
||||||
|
}
|
||||||
|
.tokenfield .token.active {
|
||||||
|
border-color: #52a8ec;
|
||||||
|
border-color: rgba(82, 168, 236, 0.8);
|
||||||
|
}
|
||||||
|
.tokenfield .token.duplicate {
|
||||||
|
border-color: #ebccd1;
|
||||||
|
-webkit-animation-name: blink;
|
||||||
|
animation-name: blink;
|
||||||
|
-webkit-animation-duration: 0.1s;
|
||||||
|
animation-duration: 0.1s;
|
||||||
|
-webkit-animation-direction: normal;
|
||||||
|
animation-direction: normal;
|
||||||
|
-webkit-animation-timing-function: ease;
|
||||||
|
animation-timing-function: ease;
|
||||||
|
-webkit-animation-iteration-count: infinite;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
}
|
||||||
|
.tokenfield .token.invalid {
|
||||||
|
background: none;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
-webkit-border-radius: 0;
|
||||||
|
-moz-border-radius: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
border-bottom: 1px dotted #d9534f;
|
||||||
|
}
|
||||||
|
.tokenfield .token.invalid.active {
|
||||||
|
background: #ededed;
|
||||||
|
border: 1px solid #ededed;
|
||||||
|
-webkit-border-radius: 3px;
|
||||||
|
-moz-border-radius: 3px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
.tokenfield .token .token-label {
|
||||||
|
display: inline-block;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
padding-left: 4px;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
.tokenfield .token .close {
|
||||||
|
font-family: Arial;
|
||||||
|
display: inline-block;
|
||||||
|
line-height: 100%;
|
||||||
|
font-size: 1.1em;
|
||||||
|
line-height: 1.49em;
|
||||||
|
margin-left: 5px;
|
||||||
|
float: none;
|
||||||
|
height: 100%;
|
||||||
|
vertical-align: top;
|
||||||
|
padding-right: 4px;
|
||||||
|
}
|
||||||
|
.tokenfield .token-input {
|
||||||
|
background: none;
|
||||||
|
width: 60px;
|
||||||
|
min-width: 60px;
|
||||||
|
border: 0;
|
||||||
|
height: 20px;
|
||||||
|
padding: 0;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
-webkit-box-shadow: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
.tokenfield .token-input:focus {
|
||||||
|
border-color: transparent;
|
||||||
|
outline: 0;
|
||||||
|
/* IE6-9 */
|
||||||
|
-webkit-box-shadow: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
.tokenfield.disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
background-color: #eeeeee;
|
||||||
|
}
|
||||||
|
.tokenfield.disabled .token-input {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
.tokenfield.disabled .token:hover {
|
||||||
|
cursor: not-allowed;
|
||||||
|
border-color: #d9d9d9;
|
||||||
|
}
|
||||||
|
.tokenfield.disabled .token:hover .close {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.2;
|
||||||
|
filter: alpha(opacity=20);
|
||||||
|
}
|
||||||
|
.has-warning .tokenfield.focus {
|
||||||
|
border-color: #66512c;
|
||||||
|
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;
|
||||||
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #c0a16b;
|
||||||
|
}
|
||||||
|
.has-error .tokenfield.focus {
|
||||||
|
border-color: #843534;
|
||||||
|
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;
|
||||||
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;
|
||||||
|
}
|
||||||
|
.has-success .tokenfield.focus {
|
||||||
|
border-color: #2b542c;
|
||||||
|
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;
|
||||||
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #67b168;
|
||||||
|
}
|
||||||
|
.tokenfield.input-sm,
|
||||||
|
.input-group-sm .tokenfield {
|
||||||
|
min-height: 30px;
|
||||||
|
padding-bottom: 0px;
|
||||||
|
}
|
||||||
|
.input-group-sm .token,
|
||||||
|
.tokenfield.input-sm .token {
|
||||||
|
height: 20px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
.input-group-sm .token-input,
|
||||||
|
.tokenfield.input-sm .token-input {
|
||||||
|
height: 18px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
.tokenfield.input-lg,
|
||||||
|
.input-group-lg .tokenfield {
|
||||||
|
height: auto;
|
||||||
|
min-height: 45px;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
}
|
||||||
|
.input-group-lg .token,
|
||||||
|
.tokenfield.input-lg .token {
|
||||||
|
height: 25px;
|
||||||
|
}
|
||||||
|
.input-group-lg .token-label,
|
||||||
|
.tokenfield.input-lg .token-label {
|
||||||
|
line-height: 23px;
|
||||||
|
}
|
||||||
|
.input-group-lg .token .close,
|
||||||
|
.tokenfield.input-lg .token .close {
|
||||||
|
line-height: 1.3em;
|
||||||
|
}
|
||||||
|
.input-group-lg .token-input,
|
||||||
|
.tokenfield.input-lg .token-input {
|
||||||
|
height: 23px;
|
||||||
|
line-height: 23px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
.tokenfield.rtl {
|
||||||
|
direction: rtl;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.tokenfield.rtl .token {
|
||||||
|
margin: -1px 0 5px 5px;
|
||||||
|
}
|
||||||
|
.tokenfield.rtl .token .token-label {
|
||||||
|
padding-left: 0px;
|
||||||
|
padding-right: 4px;
|
||||||
|
}
|
23
static/js/bootstrap-tokenfield/LICENSE.md
Normal file
23
static/js/bootstrap-tokenfield/LICENSE.md
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
#### Sliptree
|
||||||
|
- by Illimar Tambek for [Sliptree](http://sliptree.com)
|
||||||
|
- Copyright (c) 2013 by Sliptree
|
||||||
|
|
||||||
|
Available for use under the [MIT License](http://en.wikipedia.org/wiki/MIT_License)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
1042
static/js/bootstrap-tokenfield/bootstrap-tokenfield.js
vendored
Normal file
1042
static/js/bootstrap-tokenfield/bootstrap-tokenfield.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
@ -33,12 +33,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{# Load CSS and JavaScript #}
|
{# Load CSS and JavaScript #}
|
||||||
{% bootstrap_css %}
|
{% bootstrap_css %}
|
||||||
<link href="/static/css/typeaheadjs.css" rel="stylesheet">
|
<link href="/static/css/typeaheadjs.css" rel="stylesheet">
|
||||||
|
<link href="/static/css/bootstrap-tokenfield.css" rel="stylesheet">
|
||||||
|
{% comment %}<link href="/static/css/jquery-ui.css" rel="stylesheet">{% endcomment %}
|
||||||
|
|
||||||
{% bootstrap_javascript %}
|
{% bootstrap_javascript %}
|
||||||
<script src="/static/js/typeahead/typeahead.js"></script>
|
<script src="/static/js/typeahead/typeahead.js"></script>
|
||||||
<script src="/static/js/handlebars/handlebars.js"></script>
|
<script src="/static/js/handlebars/handlebars.js"></script>
|
||||||
<script src="/static/js/konami/konami.js"></script>
|
<script src="/static/js/konami/konami.js"></script>
|
||||||
<script src="/static/js/sapphire.js"> var s=Sapphire(); Konami(s.activate); </script>
|
<script src="/static/js/sapphire.js"> var s=Sapphire(); Konami(s.activate); </script>
|
||||||
|
<script src="/static/js/bootstrap-tokenfield/bootstrap-tokenfield.js"></script>
|
||||||
|
{% comment %}<script src="/static/js/jquery-ui.js"></script>{% endcomment %}
|
||||||
<link rel="stylesheet" href="{% static "/css/base.css" %}">
|
<link rel="stylesheet" href="{% static "/css/base.css" %}">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<title>{{ site_name }} : {% block title %}Accueil{% endblock %}</title>
|
<title>{{ site_name }} : {% block title %}Accueil{% endblock %}</title>
|
||||||
|
|
|
@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
{% load bootstrap_form_typeahead %}
|
{% load massive_bootstrap_form %}
|
||||||
|
|
||||||
{% block title %}Création et modification d'un switch{% endblock %}
|
{% block title %}Création et modification d'un switch{% endblock %}
|
||||||
|
|
||||||
|
@ -47,16 +47,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
<form class="form" method="post">
|
<form class="form" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% if topoform %}
|
{% if topoform %}
|
||||||
{% bootstrap_form_typeahead topoform 'switch_interface' %}
|
{% massive_bootstrap_form topoform 'switch_interface' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if machineform %}
|
{% if machineform %}
|
||||||
{% bootstrap_form_typeahead machineform 'user' %}
|
{% massive_bootstrap_form machineform 'user' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if interfaceform %}
|
{% if interfaceform %}
|
||||||
{% if i_bft_param %}
|
{% if i_bft_param %}
|
||||||
{% bootstrap_form_typeahead interfaceform 'ipv4,machine' bft_param=i_bft_param %}
|
{% massive_bootstrap_form interfaceform 'ipv4,machine' mbf_param=i_bft_param %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% bootstrap_form_typeahead interfaceform 'ipv4,machine' %}
|
{% massive_bootstrap_form interfaceform 'ipv4,machine' %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if domainform %}
|
{% if domainform %}
|
||||||
|
|
|
@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
{% load bootstrap_form_typeahead %}
|
{% load massive_bootstrap_form %}
|
||||||
|
|
||||||
{% block title %}Création et modificationd 'utilisateur{% endblock %}
|
{% block title %}Création et modificationd 'utilisateur{% endblock %}
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
<form class="form" method="post">
|
<form class="form" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% bootstrap_form_typeahead topoform 'room,related,machine_interface' %}
|
{% massive_bootstrap_form topoform 'room,related,machine_interface' %}
|
||||||
{%bootstrap_button "Créer ou modifier" button_type="submit" icon="ok" %}
|
{%bootstrap_button "Créer ou modifier" button_type="submit" icon="ok" %}
|
||||||
</form>
|
</form>
|
||||||
<br />
|
<br />
|
||||||
|
|
|
@ -50,9 +50,8 @@ from topologie.forms import EditPortForm, NewSwitchForm, EditSwitchForm
|
||||||
from topologie.forms import AddPortForm, EditRoomForm, StackForm
|
from topologie.forms import AddPortForm, EditRoomForm, StackForm
|
||||||
from users.views import form
|
from users.views import form
|
||||||
|
|
||||||
from machines.forms import AliasForm, NewMachineForm, EditMachineForm
|
from machines.forms import AliasForm, NewMachineForm, EditMachineForm, EditInterfaceForm, AddInterfaceForm
|
||||||
from machines.forms import EditInterfaceForm, AddInterfaceForm
|
from machines.views import generate_ipv4_mbf_param
|
||||||
from machines.views import generate_ipv4_bft_param
|
|
||||||
from preferences.models import AssoOption, GeneralOption
|
from preferences.models import AssoOption, GeneralOption
|
||||||
|
|
||||||
|
|
||||||
|
@ -382,6 +381,10 @@ def new_switch(request):
|
||||||
reversion.set_comment("Création")
|
reversion.set_comment("Création")
|
||||||
messages.success(request, "Le switch a été créé")
|
messages.success(request, "Le switch a été créé")
|
||||||
return redirect("/topologie/")
|
return redirect("/topologie/")
|
||||||
|
<<<<<<< HEAD
|
||||||
|
i_bft_param = generate_ipv4_mbf_param( interface, False )
|
||||||
|
return form({'topoform':switch, 'machineform': machine, 'interfaceform': interface, 'domainform': domain, 'i_bft_param': i_bft_param}, 'topologie/switch.html', request)
|
||||||
|
=======
|
||||||
i_bft_param = generate_ipv4_bft_param(interface, False)
|
i_bft_param = generate_ipv4_bft_param(interface, False)
|
||||||
return form({
|
return form({
|
||||||
'topoform': switch,
|
'topoform': switch,
|
||||||
|
@ -391,6 +394,7 @@ def new_switch(request):
|
||||||
'i_bft_param': i_bft_param
|
'i_bft_param': i_bft_param
|
||||||
}, 'topologie/switch.html', request)
|
}, 'topologie/switch.html', request)
|
||||||
|
|
||||||
|
>>>>>>> master
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('infra')
|
@permission_required('infra')
|
||||||
|
@ -450,6 +454,10 @@ def edit_switch(request, switch_id):
|
||||||
)
|
)
|
||||||
messages.success(request, "Le switch a bien été modifié")
|
messages.success(request, "Le switch a bien été modifié")
|
||||||
return redirect("/topologie/")
|
return redirect("/topologie/")
|
||||||
|
<<<<<<< HEAD
|
||||||
|
i_bft_param = generate_ipv4_mbf_param( interface_form, False )
|
||||||
|
return form({'topoform':switch_form, 'machineform': machine_form, 'interfaceform': interface_form, 'domainform': domain_form, 'i_bft_param': i_bft_param}, 'topologie/switch.html', request)
|
||||||
|
=======
|
||||||
i_bft_param = generate_ipv4_bft_param(interface_form, False)
|
i_bft_param = generate_ipv4_bft_param(interface_form, False)
|
||||||
return form({
|
return form({
|
||||||
'topoform': switch_form,
|
'topoform': switch_form,
|
||||||
|
@ -459,6 +467,7 @@ def edit_switch(request, switch_id):
|
||||||
'i_bft_param': i_bft_param
|
'i_bft_param': i_bft_param
|
||||||
}, 'topologie/switch.html', request)
|
}, 'topologie/switch.html', request)
|
||||||
|
|
||||||
|
>>>>>>> master
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@permission_required('infra')
|
@permission_required('infra')
|
||||||
|
|
|
@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
{% load bootstrap_form_typeahead %}
|
{% load massive_bootstrap_form %}
|
||||||
|
|
||||||
{% block title %}Création et modification d'utilisateur{% endblock %}
|
{% block title %}Création et modification d'utilisateur{% endblock %}
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
|
||||||
<form class="form" method="post">
|
<form class="form" method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{% bootstrap_form_typeahead userform 'room' %}
|
{% massive_bootstrap_form userform 'room' %}
|
||||||
{% bootstrap_button "Créer ou modifier" button_type="submit" icon="star" %}
|
{% bootstrap_button "Créer ou modifier" button_type="submit" icon="star" %}
|
||||||
</form>
|
</form>
|
||||||
<br />
|
<br />
|
||||||
|
|
Loading…
Reference in a new issue