Navbar (#31)
* started workin on 'navbar' module * changed bootstrap theme to bootswatch/Simplex * big work on navbar logic * starting creating menus using navbar * converted app views to new Wepage decorator, updated navbar * reimplemented DernieresMaraudes as a dropdown instead of ContextMixin * reorganised static files, minor code cleanups * turned Link.href into lazy-evaluated property * collapsed 'navbar' module into 'website', dynamic building of ApplicationMenu subclasses * minor cleanup * blah blah blah * added way to add admin/non-admin links * minor style change : red border for active page instead of all dropdowns * deleted file * prepare adding removing menu templates files, being replaced by code * essayé de généraliser le code pour les modaux bootstrap, non testé git status * more preparation and thinking on navbar app_menus logic... * added LinkManager and DropdownManager, getting closer... * small fix in DropdownManager.__get__ * boosted up work: keep it simple so it can be merged fast, major layout changes * added month filter on maraudes:liste * added 'as_icon' filter to display boolean/null values as bootstrap icons * remove inactive user from planning selection * removed all unused 'menu' templates * set up django_select2 to use static files * small fix after review
This commit is contained in:
1 parent
288ca2cc20
commit
0be59a61a7
61 files changed
+665
-525
No files matched your search
@@ -8,3 +8,19 @@ class Config(AppConfig):
|
|||||||
menu_icon = "road"
|
menu_icon = "road"
|
||||||
def get_index_url(self):
|
def get_index_url(self):
|
||||||
return "/maraudes/"
|
return "/maraudes/"
|
||||||
|
|
||||||
|
|
||||||
|
from utilisateurs.models import Maraudeur
|
||||||
|
from website.decorators import Webpage
|
||||||
|
|
||||||
|
maraudes = Webpage('maraudes',
|
||||||
|
icon="road",
|
||||||
|
defaults={
|
||||||
|
'users': [Maraudeur],
|
||||||
|
'ajax': False,
|
||||||
|
'title': ('Maraudes','app'),
|
||||||
|
})
|
||||||
|
# Setting up some links
|
||||||
|
maraudes.app_menu.add_link(('Liste des maraudes', 'maraudes:liste', "list"))
|
||||||
|
maraudes.app_menu.add_link(('Planning', 'maraudes:planning', "calendar"), admin=True)
|
||||||
|
|
||||||
@@ -87,6 +87,7 @@ class Maraude(models.Model):
|
|||||||
limit_choices_to={
|
limit_choices_to={
|
||||||
'is_superuser': False,
|
'is_superuser': False,
|
||||||
'is_staff': True,
|
'is_staff': True,
|
||||||
|
'is_active': True,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
{% load notes %}
|
{% load notes %}
|
||||||
<h3> Compte-rendu </h3>
|
<div class="col-lg-6 col-md-12">
|
||||||
|
<table class="table table-bordered">
|
||||||
<table class="table table-bordered">
|
|
||||||
{% for note in notes %}
|
{% for note in notes %}
|
||||||
{% inline_table note header="sujet" %}
|
{% inline_table note header="sujet" %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
<div class="well bg-info">
|
<div class="col-lg-6 col-md-12">
|
||||||
<p><strong>Informations</strong></p>
|
<div class="well bg-info">
|
||||||
<p>Rencontres : {{ maraude.observation_count}}</p>
|
<p><strong>Informations</strong></p>
|
||||||
|
<p>Rencontres : {{ maraude.rencontre_count}}</p>
|
||||||
|
<p>Personnes rencontrées : {{ maraude.observation_count}}</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{% load bootstrap3 %}{% load staticfiles %}
|
{% load bootstrap3 %}{% load staticfiles %}
|
||||||
|
|
||||||
<script type="text/javascript" src="{% static "jquery.formset.js" %}"></script>
|
<script type="text/javascript" src="{% static "scripts/jquery.formset.js" %}"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
/* Dynamic Formsets */
|
/* Dynamic Formsets */
|
||||||
$(function() {
|
$(function() {
|
||||||
@@ -38,6 +38,14 @@
|
|||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-6 col-md-12">
|
<div class="col-lg-6 col-md-12">
|
||||||
|
<!-- Modal buttons -->
|
||||||
|
<div class="well well-sm text-right"><strong>Créer un nouvel objet :</strong>
|
||||||
|
<div class="btn-group" role="group" aria-label="...">
|
||||||
|
<button id="new-sujet" class="btn btn-sm btn-primary">{% bootstrap_icon "user" %} Sujet</button>
|
||||||
|
<button id="new-lieu" class="btn btn-sm btn-primary">{% bootstrap_icon "globe" %} Lieu</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<form method="post" action="{% url 'maraudes:create' maraude.pk %}?finalize=False">
|
<form method="post" action="{% url 'maraudes:create' maraude.pk %}?finalize=False">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div class="panel panel-primary panel-collapse">
|
<div class="panel panel-primary panel-collapse">
|
||||||
@@ -71,7 +79,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Modal and button linking -->
|
||||||
<div class="modal fade" id="form-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
<div class="modal fade" id="form-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||||
<div class="modal-dialog">
|
<div class="modal-dialog">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
@@ -85,3 +93,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<script type="text/javascript" src="{% static 'scripts/bootstrap-modal.js' %}"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
$.fn.openModalEvent('new-sujet',
|
||||||
|
'{% url "sujets:create" %}?next={% url "maraudes:create" pk=maraude.pk %}',
|
||||||
|
'Nouveau sujet');
|
||||||
|
$.fn.openModalEvent('new-lieu',
|
||||||
|
'{% url "maraudes:lieu-create" %}?next={% url "maraudes:create" pk=maraude.pk %}',
|
||||||
|
'Nouveau lieu');
|
||||||
|
</script>
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
{% load bootstrap3 %}
|
|
||||||
<li class="dropdown app-menu"><a class="dropdown-toggle" data-toggle="dropdown" href="#">Création<b class="caret"></b></a>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
<li><a href="#" id="new-sujet">{% bootstrap_icon "user" %} Nouveau sujet</a></li>
|
|
||||||
<li><a href="#" id="new-lieu">{% bootstrap_icon "globe" %} Nouveau lieu</a></li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
/* Lier les boutons de création
|
|
||||||
* Thanks to Derek Morgan, https://dmorgan.info/posts/django-views-bootstrap-modals/
|
|
||||||
*/
|
|
||||||
$(function() {
|
|
||||||
|
|
||||||
var formAjaxSubmit = function(form, modal) {
|
|
||||||
$(form).submit(function (e) {
|
|
||||||
e.preventDefault();
|
|
||||||
$.ajax({
|
|
||||||
type: $(this).attr('method'),
|
|
||||||
url: $(this).attr('action'),
|
|
||||||
data: $(this).serialize(),
|
|
||||||
success: function (xhr, ajaxOptions, thrownError) {
|
|
||||||
if ( $(xhr).find('.has-error').length > 0 || $(xhr).find('.alert-danger').length > 0) {
|
|
||||||
$(modal).find('.modal-body').html(xhr);
|
|
||||||
formAjaxSubmit(form, modal);
|
|
||||||
} else {
|
|
||||||
$(modal).modal('toggle');
|
|
||||||
// Reload page ?
|
|
||||||
location.reload(true)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: function (xhr, ajaxOptions, thrownError) {
|
|
||||||
// handle response errors here
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
/* TODO: Use formAjaxSubmit above, but reload page on form success */
|
|
||||||
$('#new-sujet').click(function() {
|
|
||||||
$('#form-modal-body').load('{% url "sujets:create" %}?next={% url "maraudes:create" pk=maraude.id %}', function () {
|
|
||||||
$('.modal-title').text("Nouveau sujet");
|
|
||||||
$('#form-modal').modal('toggle');
|
|
||||||
formAjaxSubmit("#form-modal-body form", "#form-modal");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
$('#new-lieu').click(function() {
|
|
||||||
|
|
||||||
$('#form-modal-body').load('{% url "maraudes:lieu-create" %}?next={% url "maraudes:create" pk=maraude.id %}', function () {
|
|
||||||
$('.modal-title').text("Nouveau lieu");
|
|
||||||
$('#form-modal').modal('toggle');
|
|
||||||
formAjaxSubmit("#form-modal-body form", "#form-modal");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
Whitespace-only changes.
@@ -1,9 +1,7 @@
|
|||||||
<div class="col-md-12 col-lg-6">
|
|
||||||
{% if maraude.est_terminee %}
|
{% if maraude.est_terminee %}
|
||||||
{% include "compte_rendu/compterendu.html" %}
|
{% include "compte_rendu/compterendu.html" %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% if perms.maraudes.can_add_compterendu %}<a class="btn btn-primary" href="{% url 'maraudes:create' maraude.pk %}">Écrire le compte-rendu</a>
|
{% if perms.maraudes.can_add_compterendu %}<a class="btn btn-primary" href="{% url 'maraudes:create' maraude.pk %}">Écrire le compte-rendu</a>
|
||||||
{% else %} <p class="alert alert-info">Le compte-rendu n'a pas encore été écrit</p>{% endif %}
|
{% else %} <p class="alert alert-info">Le compte-rendu n'a pas encore été écrit</p>{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
|
||||||
|
|
||||||
@@ -11,7 +11,13 @@
|
|||||||
avec {% if user.is_superuser %}{{prochaine_maraude.binome}}{%else%}{{prochaine_maraude.referent}}{%endif%}.
|
avec {% if user.is_superuser %}{{prochaine_maraude.binome}}{%else%}{{prochaine_maraude.referent}}{%endif%}.
|
||||||
</strong></p>
|
</strong></p>
|
||||||
<hr />
|
<hr />
|
||||||
<p><mark>Informations, notes, rendez-vous ?</mark></p>
|
{% if prochaine_maraude.est_terminee %}
|
||||||
|
<a class="btn btn-sm btn-primary" href="{% url 'maraudes:details' pk=prochaine_maraude.pk %}">
|
||||||
|
Voir le compte-rendu
|
||||||
|
</a>{%else%}
|
||||||
|
<a class="btn btn-sm btn-primary" href="{% url 'maraudes:create' pk=prochaine_maraude.pk %}">
|
||||||
|
Rédiger le compte-rendu
|
||||||
|
</a>{% endif %}
|
||||||
{% else %}<p>Aucune maraude prévue.</p>{% endif %}
|
{% else %}<p>Aucune maraude prévue.</p>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
{% if object.est_terminee %}
|
{% if object.est_terminee %}<a href="{% url 'maraudes:details' object.id %}" class="btn btn-link">
|
||||||
<a href="{% url 'maraudes:details' object.id %}" class="btn btn-link">
|
{% else %}<a href="#" class="btn btn-link disabled">{% endif %}
|
||||||
{% else %}
|
{{object.date}}</a>
|
||||||
<a href="#" class="btn btn-link disabled">
|
<div class="pull-right">
|
||||||
{% endif %}
|
|
||||||
{{object.date}}
|
|
||||||
</a>
|
|
||||||
<span class="label label-info">{{ object.binome }} & {{ object.referent }}</span>
|
<span class="label label-info">{{ object.binome }} & {{ object.referent }}</span>
|
||||||
{% if object.est_terminee %}
|
{% if object.est_terminee %}
|
||||||
<span class="label label-success">{{object.rencontres.count}} rencontres</span>
|
<span class="label label-success">{{object.rencontres.count}} rencontres</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
</div>
|
||||||
@@ -1,14 +1,13 @@
|
|||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
{% load tables %}
|
{% load tables %}
|
||||||
<div class="col-md-12 col-lg-6">
|
<div class="col-lg-10">
|
||||||
<div class="panel panel-primary">
|
<div class="panel panel-primary">
|
||||||
<!-- Default panel contents -->
|
<!-- Default panel contents -->
|
||||||
<div class="panel-heading text-center">
|
<div class="panel-heading text-center">
|
||||||
<h3 class="panel-title">Maraudes passées</h3>
|
<h3 class="panel-title">Maraudes passées</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Table -->
|
<!-- Table -->
|
||||||
{% table object_list cols=2 cell_template="maraudes/list_table_cell.html" %}
|
{% table object_list cols=3 cell_template="maraudes/list_table_cell.html" %}
|
||||||
{% if is_paginated %}
|
{% if is_paginated %}
|
||||||
<div class="panel-footer text-center">
|
<div class="panel-footer text-center">
|
||||||
<ul class="pagination">
|
<ul class="pagination">
|
||||||
@@ -20,15 +19,14 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-lg-3 col-md-3">
|
<div class="pull-left">
|
||||||
<div class="panel panel-default">
|
<div class="btn-group">
|
||||||
<!-- Default panel contents -->
|
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
<div class="panel-heading text-center">
|
Filtrer <span class="caret"></span>
|
||||||
<h3 class="panel-title">Filtres</h3>
|
</button>
|
||||||
</div>
|
<ul class="dropdown-menu">
|
||||||
<div class="list-group">
|
<li><a href="{% url 'maraudes:liste' %}">Pas de filtre</a></li>
|
||||||
<a href="#" class="list-group-item disabled">Vos maraudes seulement</a></li>
|
<li><a href="?filter=month-only">Ce mois-ci</a></li>
|
||||||
<a href="#" class="list-group-item disabled">Ce mois-ci</a></li>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{% if user.is_superuser %}{% load bootstrap3 %}
|
|
||||||
<li class="dropdown app-menu"><a class="dropdown-toggle" data-toggle="dropdown" href="#">Administration<b class="caret"></b></a>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
<li><a href="{% url 'maraudes:planning' %}">{% bootstrap_icon "calendar" %} Planning</a></li>
|
|
||||||
<li><a href="{% url 'admin:app_list' app_label=active_app.label %}">{% bootstrap_icon "wrench" %} Gérer les maraudes</a></li>
|
|
||||||
</ul>
|
|
||||||
{% endif %}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
{% load bootstrap3 %}
|
|
||||||
<li class="app-menu">
|
|
||||||
<a href="{% url 'maraudes:liste' %}">Liste des maraudes
|
|
||||||
<span class="pull-right">{% bootstrap_icon "list" %}</span></a>
|
|
||||||
</li>
|
|
||||||
{% if dernieres_maraudes %}
|
|
||||||
<li class="dropdown app-menu"><a class="dropdown-toggle" data-toggle="dropdown" href="#">Dernières maraudes <b class="caret"></b></a>
|
|
||||||
<ul class="dropdown-menu">{% for m in dernieres_maraudes %}
|
|
||||||
<li><a href="{% url 'maraudes:details' m.pk %}" {% if maraude == m %}style="color:#fff;"{% endif %}>
|
|
||||||
<strong>{{ m }}</strong> <small>{{m.binome}} & {{m.referent}}</small></a>
|
|
||||||
</li>{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
+23
-93
@@ -1,7 +1,6 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import calendar
|
import calendar
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.functional import cached_property
|
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.shortcuts import render, redirect
|
from django.shortcuts import render, redirect
|
||||||
# Views
|
# Views
|
||||||
@@ -22,56 +21,14 @@ from .forms import ( RencontreForm, RencontreInlineFormSet,
|
|||||||
ObservationInlineFormSet, ObservationInlineFormSetNoExtra,
|
ObservationInlineFormSet, ObservationInlineFormSetNoExtra,
|
||||||
MaraudeAutoDateForm, MonthSelectForm, )
|
MaraudeAutoDateForm, MonthSelectForm, )
|
||||||
|
|
||||||
from utilisateurs.models import Maraudeur
|
|
||||||
|
|
||||||
from website import decorators as website
|
|
||||||
maraudes = website.app_config(
|
|
||||||
name="maraudes",
|
|
||||||
groups=[Maraudeur],
|
|
||||||
menu=["maraudes/menu/dernieres_maraudes.html"],
|
|
||||||
admin_menu=["maraudes/menu/admin_menu.html"],
|
|
||||||
ajax=False,
|
|
||||||
)
|
|
||||||
compte_rendu = website.app_config(
|
|
||||||
name="maraudes",
|
|
||||||
groups=[Maraudeur],
|
|
||||||
menu=["compte_rendu/menu/creation.html"],
|
|
||||||
ajax=False,
|
|
||||||
)
|
|
||||||
maraudes_ajax = website.app_config(
|
|
||||||
name="maraudes",
|
|
||||||
groups=[Maraudeur],
|
|
||||||
ajax=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
from django.core.mail import send_mail
|
from django.core.mail import send_mail
|
||||||
|
|
||||||
class DerniereMaraudeMixin(object):
|
from .apps import maraudes
|
||||||
count = 5
|
|
||||||
@cached_property
|
|
||||||
def dernieres_maraudes(self):
|
|
||||||
""" Renvoie la liste des 'Maraude' passées et terminées """
|
|
||||||
return Maraude.objects.get_past().filter(
|
|
||||||
heure_fin__isnull=False
|
|
||||||
).order_by(
|
|
||||||
'-date'
|
|
||||||
)[:self.count]
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
context['dernieres_maraudes'] = self.dernieres_maraudes
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
|
@maraudes.using(title=('La Maraude', 'Tableau de bord'))
|
||||||
|
class IndexView(generic.TemplateView):
|
||||||
|
|
||||||
@maraudes
|
|
||||||
class IndexView(DerniereMaraudeMixin, generic.TemplateView):
|
|
||||||
|
|
||||||
class PageInfo:
|
|
||||||
title = "Maraude - Tableau de bord"
|
|
||||||
header = "La Maraude"
|
|
||||||
header_small = "Tableau de bord"
|
|
||||||
# TemplateView
|
|
||||||
template_name = "maraudes/index.html"
|
template_name = "maraudes/index.html"
|
||||||
|
|
||||||
def get_context_data(self, *args, **kwargs):
|
def get_context_data(self, *args, **kwargs):
|
||||||
@@ -101,48 +58,44 @@ class IndexView(DerniereMaraudeMixin, generic.TemplateView):
|
|||||||
return Maraude.objects.next
|
return Maraude.objects.next
|
||||||
|
|
||||||
## MARAUDES
|
## MARAUDES
|
||||||
@maraudes
|
@maraudes.using(title=('{{maraude.date}}', 'compte-rendu'))
|
||||||
class MaraudeDetailsView(DerniereMaraudeMixin, generic.DetailView):
|
class MaraudeDetailsView(generic.DetailView):
|
||||||
""" Vue détaillé d'un compte-rendu de maraude """
|
""" Vue détaillé d'un compte-rendu de maraude """
|
||||||
|
|
||||||
model = CompteRendu
|
model = CompteRendu
|
||||||
context_object_name = "maraude"
|
context_object_name = "maraude"
|
||||||
template_name = "maraudes/details.html"
|
template_name = "maraudes/details.html"
|
||||||
|
|
||||||
# Template
|
|
||||||
class PageInfo:
|
|
||||||
title = "Maraude - {{maraude.date}}"
|
|
||||||
header = "{{maraude.date}}"
|
|
||||||
header_small = "{{maraude.referent}} & {{maraude.binome}}"
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['notes'] = self.object.get_observations()
|
context['notes'] = self.object.get_observations()
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
@maraudes.using(title=('Liste des maraudes',))
|
||||||
@maraudes
|
class MaraudeListView(generic.ListView):
|
||||||
class MaraudeListView(DerniereMaraudeMixin, generic.ListView):
|
|
||||||
""" Vue de la liste des compte-rendus de maraude """
|
""" Vue de la liste des compte-rendus de maraude """
|
||||||
|
|
||||||
model = CompteRendu
|
model = CompteRendu
|
||||||
template_name = "maraudes/liste.html"
|
template_name = "maraudes/liste.html"
|
||||||
paginate_by = 30
|
paginate_by = 30
|
||||||
|
|
||||||
class PageInfo:
|
|
||||||
title = "Maraude - Liste des maraudes"
|
|
||||||
header = "Liste des maraudes"
|
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
today = datetime.date.today()
|
current_date = timezone.localtime(timezone.now()).date()
|
||||||
return super().get_queryset().filter(
|
qs = super().get_queryset().filter(
|
||||||
date__lte=timezone.localtime(timezone.now()).date()
|
date__lte=current_date
|
||||||
).order_by('-date')
|
).order_by('-date')
|
||||||
|
|
||||||
|
filtre = self.request.GET.get('filter', None)
|
||||||
|
if filtre == "month-only":
|
||||||
|
return qs.filter(date__month=current_date.month)
|
||||||
|
#Other cases...
|
||||||
|
else:
|
||||||
|
return qs
|
||||||
|
|
||||||
|
|
||||||
## COMPTE-RENDU DE MARAUDE
|
## COMPTE-RENDU DE MARAUDE
|
||||||
@compte_rendu
|
@maraudes.using(title=('{{maraude.date}}', 'rédaction'))
|
||||||
class CompteRenduCreateView(generic.DetailView):
|
class CompteRenduCreateView(generic.DetailView):
|
||||||
""" Vue pour la création d'un compte-rendu de maraude """
|
""" Vue pour la création d'un compte-rendu de maraude """
|
||||||
|
|
||||||
@@ -153,11 +106,6 @@ class CompteRenduCreateView(generic.DetailView):
|
|||||||
form = None
|
form = None
|
||||||
inline_formset = None
|
inline_formset = None
|
||||||
|
|
||||||
class PageInfo:
|
|
||||||
title = "{{maraude}} - Compte-rendu"
|
|
||||||
header = "{{maraude.date}}"
|
|
||||||
header_small = "écriture du compte-rendu"
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
#WARNING: Overrides app_menu and replace it
|
#WARNING: Overrides app_menu and replace it
|
||||||
@@ -180,11 +128,8 @@ class CompteRenduCreateView(generic.DetailView):
|
|||||||
# Add text to some mails ? Transmission, message à un référent, etc...
|
# Add text to some mails ? Transmission, message à un référent, etc...
|
||||||
# Send mail to Maraudeurs
|
# Send mail to Maraudeurs
|
||||||
_from = maraude.referent.email
|
_from = maraude.referent.email
|
||||||
exclude = (maraude.referent, maraude.binome)
|
# Shall select only Maraudeur where 'is_active' is True !
|
||||||
recipients = []
|
recipients = [m for m in Maraudeur.objects.all() if m not in (maraude.referent, maraude.binome)]
|
||||||
for m in Maraudeur.objects.all():
|
|
||||||
if not m in exclude:
|
|
||||||
recipients.append(m.email)
|
|
||||||
objet = "Compte-rendu de maraude : %s" % maraude.date
|
objet = "Compte-rendu de maraude : %s" % maraude.date
|
||||||
message = "Sujets rencontrés : ..." #TODO: Mail content
|
message = "Sujets rencontrés : ..." #TODO: Mail content
|
||||||
send_mail(objet, message, _from, recipients)
|
send_mail(objet, message, _from, recipients)
|
||||||
@@ -245,7 +190,7 @@ class CompteRenduCreateView(generic.DetailView):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
@compte_rendu
|
@maraudes.using(title=('{{maraude.date}}', 'mise à jour'))
|
||||||
class CompteRenduUpdateView(generic.DetailView):
|
class CompteRenduUpdateView(generic.DetailView):
|
||||||
""" Vue pour mettre à jour le compte-rendu de la maraude """
|
""" Vue pour mettre à jour le compte-rendu de la maraude """
|
||||||
|
|
||||||
@@ -253,11 +198,6 @@ class CompteRenduUpdateView(generic.DetailView):
|
|||||||
context_object_name = "maraude"
|
context_object_name = "maraude"
|
||||||
template_name = "compte_rendu/compterendu_update.html"
|
template_name = "compte_rendu/compterendu_update.html"
|
||||||
|
|
||||||
class PageInfo:
|
|
||||||
title = "{{maraude}} - Compte-rendu"
|
|
||||||
header = "{{maraude.date}}"
|
|
||||||
header_small = "compte-rendu"
|
|
||||||
|
|
||||||
base_formset = None
|
base_formset = None
|
||||||
inline_formsets = []
|
inline_formsets = []
|
||||||
rencontres_queryset = None
|
rencontres_queryset = None
|
||||||
@@ -311,17 +251,12 @@ class CompteRenduUpdateView(generic.DetailView):
|
|||||||
|
|
||||||
|
|
||||||
## PLANNING
|
## PLANNING
|
||||||
@maraudes
|
@maraudes.using(title=('Planning',))
|
||||||
class PlanningView(generic.TemplateView):
|
class PlanningView(generic.TemplateView):
|
||||||
""" Display and edit the planning of next Maraudes """
|
""" Display and edit the planning of next Maraudes """
|
||||||
|
|
||||||
template_name = "planning/planning.html"
|
template_name = "planning/planning.html"
|
||||||
|
|
||||||
class PageInfo:
|
|
||||||
title = "Planning"
|
|
||||||
header = "Planning"
|
|
||||||
header_small = "{{month}} {{year}}" #TODO: does not parse extra context
|
|
||||||
|
|
||||||
def _parse_request(self):
|
def _parse_request(self):
|
||||||
self.current_date = datetime.date.today()
|
self.current_date = datetime.date.today()
|
||||||
try: self.month = int(self.request.GET['month'])
|
try: self.month = int(self.request.GET['month'])
|
||||||
@@ -381,18 +316,13 @@ class PlanningView(generic.TemplateView):
|
|||||||
|
|
||||||
## LIEU
|
## LIEU
|
||||||
|
|
||||||
@maraudes_ajax
|
@maraudes.using(ajax=True)
|
||||||
class LieuCreateView(generic.edit.CreateView):
|
class LieuCreateView(generic.edit.CreateView):
|
||||||
model = Lieu
|
model = Lieu
|
||||||
template_name = "maraudes/lieu_create.html"
|
template_name = "maraudes/lieu_create.html"
|
||||||
fields = "__all__"
|
fields = "__all__"
|
||||||
success_url = "/maraudes/"
|
success_url = "/maraudes/"
|
||||||
|
|
||||||
class PageInfo:
|
|
||||||
pass
|
|
||||||
|
|
||||||
permissions = ['maraudes.add_lieu']
|
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
if 'next' in self.request.POST:
|
if 'next' in self.request.POST:
|
||||||
self.success_url = self.request.POST["next"]
|
self.success_url = self.request.POST["next"]
|
||||||
|
|||||||
+6
-2
@@ -23,8 +23,8 @@ LOGIN_URL = 'index'
|
|||||||
|
|
||||||
BOOTSTRAP3 = {
|
BOOTSTRAP3 = {
|
||||||
# The URL to the jQuery JavaScript file
|
# The URL to the jQuery JavaScript file
|
||||||
'base_url': os.path.join(STATIC_URL, 'bootstrap/'),
|
'base_url': os.path.join(STATIC_URL, 'css', 'bootstrap/'),
|
||||||
'jquery_url': '//code.jquery.com/jquery.min.js',
|
'jquery_url': os.path.join(STATIC_URL, 'scripts', 'jquery.min.js'),
|
||||||
'include_jquery': True,
|
'include_jquery': True,
|
||||||
# Label class to use in horizontal forms
|
# Label class to use in horizontal forms
|
||||||
'horizontal_label_class': 'col-md-2',
|
'horizontal_label_class': 'col-md-2',
|
||||||
@@ -32,6 +32,10 @@ BOOTSTRAP3 = {
|
|||||||
'horizontal_field_class': 'col-md-10',
|
'horizontal_field_class': 'col-md-10',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Django-select2 Configuration
|
||||||
|
SELECT2_JS = 'scripts/select2.min.js'
|
||||||
|
SELECT2_CSS = 'css/select2.min.css'
|
||||||
|
|
||||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||||
AUTHENTICATION_BACKENDS = [
|
AUTHENTICATION_BACKENDS = [
|
||||||
'website.backends.MyBackend'
|
'website.backends.MyBackend'
|
||||||
|
|||||||
@@ -3,3 +3,14 @@ from django.apps import AppConfig
|
|||||||
|
|
||||||
class SuiviConfig(AppConfig):
|
class SuiviConfig(AppConfig):
|
||||||
name = 'suivi'
|
name = 'suivi'
|
||||||
|
|
||||||
|
from utilisateurs.models import Maraudeur
|
||||||
|
from website.decorators import Webpage
|
||||||
|
suivi = Webpage("suivi", icon="eye-open", defaults={
|
||||||
|
'restricted': [Maraudeur],
|
||||||
|
'ajax': False,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
suivi.app_menu.add_link(('Liste des sujets', 'suivi:liste', 'list'))
|
||||||
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
{{ form.media.js }}{{ form.media.css }}
|
|
||||||
<form action="" method="POST">{% csrf_token %}
|
<form action="" method="POST">{% csrf_token %}
|
||||||
{% with "inline" as layout %}
|
{% with "inline" as layout %}
|
||||||
<div class="form-{{layout}} well col-md-10 col-md-offset-2">
|
<div class="form-{{layout}} well col-md-10 col-md-offset-2">
|
||||||
@@ -14,3 +13,4 @@
|
|||||||
</div> {% endwith %}
|
</div> {% endwith %}
|
||||||
<div class="pull-right">{% bootstrap_button "Enregistrer l'appel" button_type="submit" %}</div>
|
<div class="pull-right">{% bootstrap_button "Enregistrer l'appel" button_type="submit" %}</div>
|
||||||
</form>
|
</form>
|
||||||
|
{{ form.media.js }}{{ form.media.css }}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{% load bootstrap3 %}
|
|
||||||
<li class="dropdown app-menu">
|
|
||||||
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
|
||||||
Administration <b class="caret"></b>
|
|
||||||
</a>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
<li><a href="{% url 'sujets:create' %}">{% bootstrap_icon "plus" %} Nouveau sujet</a></li>
|
|
||||||
<li><a href="{% url 'admin:app_list' app_label='sujets' %}">
|
|
||||||
{% bootstrap_icon "wrench" %} Gérer les sujets</a></li>
|
|
||||||
</ul>
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{% load bootstrap3 %}
|
|
||||||
<li class="app-menu">
|
|
||||||
<a href="{% url 'suivi:liste' %}">Liste des sujets
|
|
||||||
<span class="pull-right">{% bootstrap_icon "list" %}</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
+10
-26
@@ -7,15 +7,7 @@ from .forms import *
|
|||||||
from notes.mixins import NoteFormMixin
|
from notes.mixins import NoteFormMixin
|
||||||
from notes.forms import AutoNoteForm
|
from notes.forms import AutoNoteForm
|
||||||
# Create your views here.
|
# Create your views here.
|
||||||
from utilisateurs.models import Maraudeur
|
|
||||||
from website import decorators as website
|
|
||||||
suivi = website.app_config(
|
|
||||||
name="suivi",
|
|
||||||
groups=[Maraudeur],
|
|
||||||
menu=["suivi/menu/sujets.html"],
|
|
||||||
admin_menu=["suivi/menu/admin_sujets.html"],
|
|
||||||
ajax=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
from maraudes.compte_rendu import CompteRendu
|
from maraudes.compte_rendu import CompteRendu
|
||||||
|
|
||||||
@@ -30,12 +22,10 @@ def derniers_sujets_rencontres():
|
|||||||
sujets.add(obs.sujet)
|
sujets.add(obs.sujet)
|
||||||
return sujets
|
return sujets
|
||||||
|
|
||||||
@suivi
|
from .apps import suivi
|
||||||
|
|
||||||
|
@suivi.using(title=("Suivi", "Tableau de bord"))
|
||||||
class IndexView(NoteFormMixin, generic.TemplateView):
|
class IndexView(NoteFormMixin, generic.TemplateView):
|
||||||
class PageInfo:
|
|
||||||
title = "Suivi des bénéficiaires"
|
|
||||||
header = "Suivi"
|
|
||||||
header_small = "Tableau de bord"
|
|
||||||
#NoteFormMixin
|
#NoteFormMixin
|
||||||
forms = {
|
forms = {
|
||||||
'appel': AppelForm,
|
'appel': AppelForm,
|
||||||
@@ -54,11 +44,10 @@ class IndexView(NoteFormMixin, generic.TemplateView):
|
|||||||
context['derniers_sujets'] = ", ".join(map(str, derniers_sujets_rencontres()))
|
context['derniers_sujets'] = ", ".join(map(str, derniers_sujets_rencontres()))
|
||||||
return context
|
return context
|
||||||
|
|
||||||
@suivi
|
|
||||||
|
|
||||||
|
@suivi.using(title=('Liste des sujets',))
|
||||||
class SujetListView(generic.ListView):
|
class SujetListView(generic.ListView):
|
||||||
class PageInfo:
|
|
||||||
title = "Sujet - Liste des sujets"
|
|
||||||
header = "Liste des sujets"
|
|
||||||
#ListView
|
#ListView
|
||||||
model = Sujet
|
model = Sujet
|
||||||
template_name = "sujets/sujet_liste.html"
|
template_name = "sujets/sujet_liste.html"
|
||||||
@@ -77,15 +66,10 @@ class SujetListView(generic.ListView):
|
|||||||
context['query_text'] = self.request.POST.get('q', None)
|
context['query_text'] = self.request.POST.get('q', None)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
# Import app_config from 'sujets' application, using
|
|
||||||
# its admin_menu option
|
|
||||||
from sujets.views import sujets
|
@suivi.using(title=('{{sujet}}', 'suivi'))
|
||||||
@sujets
|
|
||||||
class SuiviSujetView(NoteFormMixin, generic.DetailView):
|
class SuiviSujetView(NoteFormMixin, generic.DetailView):
|
||||||
class PageInfo:
|
|
||||||
title = "Sujet - {{sujet}}"
|
|
||||||
header = "{{sujet}}"
|
|
||||||
header_small = "suivi"
|
|
||||||
#NoteFormMixin
|
#NoteFormMixin
|
||||||
forms = {
|
forms = {
|
||||||
'note': AutoNoteForm,
|
'note': AutoNoteForm,
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
{% if user.is_superuser %}
|
|
||||||
{% load bootstrap3 %}
|
|
||||||
<li class="dropdown app-menu"><a class="dropdown-toggle" data-toggle="dropdown" href="#">Sujet<b class="caret"></b></a>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
<li><a href="{% url 'admin:notes_note_changelist' %}?sujet__personne_ptr__exact={{sujet.pk}}">{% bootstrap_icon "wrench" %} Éditer les notes</a></li>
|
|
||||||
</ul>
|
|
||||||
{% endif %}
|
|
||||||
Whitespace-only changes.
@@ -1,16 +1,63 @@
|
|||||||
|
{% load boolean_icons %}
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<tr><th colspan="4" class="active">Informations générales</th></tr>
|
<tr>
|
||||||
<tr><th>Première rencontre :</th><td> {{ sujet.premiere_rencontre }}</td></tr>
|
<th colspan="4" class="active">Informations générales</th>
|
||||||
<tr><th colspan="4" class="active">État-civil</th></tr>
|
</tr>
|
||||||
<tr><th>Nom</th><td>{{ sujet.nom|default:'?' }}</td><th>Prénom</th><td>{{ sujet.prenom|default:'?' }}</td></tr>
|
<tr>
|
||||||
<tr><th>Sexe</th><td>{{ sujet.genre }}</td><th>Âge</th><td>{{ sujet.age|default:'?' }}</td></tr>
|
<th>Première rencontre :</th>
|
||||||
<tr><th colspan="4" class="active">Problématiques</th></tr>
|
<td> {{ sujet.premiere_rencontre }}</td>
|
||||||
<tr><th>Psychiatrique</th><td>{{ sujet.prob_psychiatrie }}</td><th>Addiction</th><td>{{ sujet.prob_addiction }}</td></tr>
|
</tr>
|
||||||
<tr><th>Administratif</th><td>{{ sujet.prob_administratif }}</td><th>Somatique</th><td>{{ sujet.prob_somatique }}</td></tr>
|
<tr>
|
||||||
<tr><th colspan="4" class="active">Habitation</th></tr></td></tr>
|
<th colspan="4" class="active">État-civil</th>
|
||||||
<tr><th>Type</th><td>{{ sujet.habitation }}</td><th>Connu du SIAO</th><td>{{ sujet.connu_siao }}</td></tr>
|
</tr>
|
||||||
<tr><th colspan="4" class="active">Ressources</th></tr></td></tr>
|
<tr>
|
||||||
<tr><td colspan="4">{{ sujet.ressources }}</td></tr>
|
<th>Nom</th>
|
||||||
<tr><th colspan="4" class="active">Parcours de vie</th></tr></td></tr>
|
<td>{{ sujet.nom|default:'?' }}</td>
|
||||||
<tr><td colspan="2">{{ sujet.parcours_de_vie }}</td><th>Lien familial</th><td>{{ sujet.lien_familial }}</td></tr>
|
<th>Prénom</th>
|
||||||
|
<td>{{ sujet.prenom|default:'?' }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Sexe</th>
|
||||||
|
<td>{{ sujet.genre }}</td>
|
||||||
|
<th>Âge</th>
|
||||||
|
<td>{{ sujet.age|default:'?' }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th colspan="4" class="active">Problématiques</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Psychiatrique</th>
|
||||||
|
<td>{{ sujet.prob_psychiatrie|as_icon }}</td>
|
||||||
|
<th>Addiction</th>
|
||||||
|
<td>{{ sujet.prob_addiction|as_icon }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Administratif</th>
|
||||||
|
<td>{{ sujet.prob_administratif|as_icon }}</td>
|
||||||
|
<th>Somatique</th>
|
||||||
|
<td>{{ sujet.prob_somatique|as_icon }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th colspan="4" class="active">Habitation</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Type</th>
|
||||||
|
<td>{{ sujet.habitation }}</td>
|
||||||
|
<th>Connu du SIAO</th>
|
||||||
|
<td>{{ sujet.connu_siao|as_icon }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th colspan="4" class="active">Ressources</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="4">{{ sujet.ressources }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th colspan="4" class="active">Parcours de vie</th>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colspan="2">{{ sujet.parcours_de_vie }}</td>
|
||||||
|
<th>Lien familial</th>
|
||||||
|
<td>{{ sujet.lien_familial|as_icon }}</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
@@ -1,10 +1,12 @@
|
|||||||
{% load tables %}
|
{% load tables %}
|
||||||
{% load bootstrap3 %}
|
{% load bootstrap3 %}
|
||||||
<div class="col-md-12 col-lg-6">
|
<div class="col-lg-10">
|
||||||
<div class="panel panel-primary">
|
<div class="panel panel-primary">
|
||||||
<!-- Default panel contents -->
|
<!-- Default panel contents -->
|
||||||
<div class="panel-heading text-center">
|
<div class="panel-heading text-center">
|
||||||
<h3 class="panel-title">Sujets rencontrés</h3>
|
<h3 class="panel-title">Sujets rencontrés
|
||||||
|
|
||||||
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="panel-body">
|
<div class="panel-body">
|
||||||
<form action="" method="POST" class="form form-group">{% csrf_token %}
|
<form action="" method="POST" class="form form-group">{% csrf_token %}
|
||||||
@@ -41,19 +43,17 @@
|
|||||||
{% endif %}{% endif %}
|
{% endif %}{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="pull-left">
|
||||||
<div class="col-lg-3 col-md-3">
|
<div class="btn-group">
|
||||||
<div class="panel panel-default">
|
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||||
<!-- Default panel contents -->
|
Filtrer <span class="caret"></span>
|
||||||
<div class="panel-heading text-center">
|
</button>
|
||||||
<h3 class="panel-title">Filtres</h3>
|
<ul class="dropdown-menu">
|
||||||
</div>
|
<li><a href="{% url 'suivi:liste' %}">Pas de filtre</a></li>
|
||||||
<div class="list-group">
|
<li role="separator" class="divider"></li>
|
||||||
<a href="#" class="list-group-item disabled">Rencontrés ce mois-ci</a></li>
|
<li><a href="?filter=month-only">Rencontrés ce mois-ci</a></li>
|
||||||
<a href="#" class="list-group-item disabled"><strong>Mes accompagnements</strong></a></li>
|
<li><a href="?filter=">Hébérgé en urgence</a></li>
|
||||||
<a href="#" class="list-group-item disabled">Hébérgé en urgence</a></li>
|
<li><a href="?filter=">Sans abri</a></li>
|
||||||
<a href="#" class="list-group-item disabled">Sans logement</a></li>
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
+7
-25
@@ -6,49 +6,31 @@ from .forms import SujetCreateForm
|
|||||||
|
|
||||||
### Webpage config
|
### Webpage config
|
||||||
from utilisateurs.models import Maraudeur
|
from utilisateurs.models import Maraudeur
|
||||||
from website import decorators as website
|
from website.decorators import Webpage
|
||||||
sujets = website.app_config(
|
sujets = Webpage( "suivi", menu=False, defaults={
|
||||||
name="suivi",
|
'restricted': [Maraudeur],
|
||||||
groups=[Maraudeur],
|
'ajax': True,
|
||||||
menu=["suivi/menu/sujets.html"],
|
}
|
||||||
admin_menu=["sujets/menu/admin_sujet.html"],
|
)
|
||||||
ajax=True,
|
|
||||||
)
|
|
||||||
### Views
|
### Views
|
||||||
|
|
||||||
@sujets
|
@sujets.using(title=('{{object}}', 'details'))
|
||||||
class SujetDetailsView(generic.DetailView):
|
class SujetDetailsView(generic.DetailView):
|
||||||
class PageInfo:
|
|
||||||
title = "Sujet - {{ sujet }}"
|
|
||||||
header = "{{ sujet }}"
|
|
||||||
header_small = "informations"
|
|
||||||
#DetailView
|
#DetailView
|
||||||
template_name = "sujets/sujet_details.html"
|
template_name = "sujets/sujet_details.html"
|
||||||
model = Sujet
|
model = Sujet
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@sujets
|
@sujets
|
||||||
class SujetUpdateView(generic.edit.UpdateView):
|
class SujetUpdateView(generic.edit.UpdateView):
|
||||||
class PageInfo:
|
|
||||||
title = "Mise à jour - {{sujet}}"
|
|
||||||
header = "{{sujet}}"
|
|
||||||
header_small = "mise à jour"
|
|
||||||
#UpdateView
|
#UpdateView
|
||||||
template_name = "sujets/sujet_update.html"
|
template_name = "sujets/sujet_update.html"
|
||||||
model = Sujet
|
model = Sujet
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@sujets
|
@sujets
|
||||||
class SujetCreateView(generic.edit.CreateView):
|
class SujetCreateView(generic.edit.CreateView):
|
||||||
class PageInfo:
|
|
||||||
title = "Nouveau sujet"
|
|
||||||
header = "Nouveau sujet"
|
|
||||||
#CreateView
|
#CreateView
|
||||||
template_name = "sujets/sujet_create.html"
|
template_name = "sujets/sujet_create.html"
|
||||||
form_class = SujetCreateForm
|
form_class = SujetCreateForm
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ class MaraudeurAdmin(admin.ModelAdmin):
|
|||||||
('Informations', {'fields': [('first_name', 'last_name')]}),
|
('Informations', {'fields': [('first_name', 'last_name')]}),
|
||||||
]
|
]
|
||||||
|
|
||||||
list_display = ('first_name', 'last_name', 'is_active')
|
list_display = ('username', 'is_active')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,16 @@
|
|||||||
from django.apps import AppConfig
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
from website.decorators import Webpage
|
||||||
|
from .models import Professionnel
|
||||||
|
|
||||||
class UtilisateursConfig(AppConfig):
|
class UtilisateursConfig(AppConfig):
|
||||||
name = 'utilisateurs'
|
name = 'utilisateurs'
|
||||||
|
|
||||||
|
|
||||||
|
utilisateurs = Webpage('utilisateurs',
|
||||||
|
icon="user",
|
||||||
|
defaults={
|
||||||
|
'users': [Professionnel],
|
||||||
|
'ajax': False,
|
||||||
|
'title': ('Utilisateurs','app'),
|
||||||
|
})
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
|
||||||
|
{{ user.first_name }}, {{ user.last_name }}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
from django.conf.urls import url
|
||||||
|
|
||||||
|
from . import views
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
url(r'^$', views.UtilisateurView.as_view(), name="index"),
|
||||||
|
]
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
from django.views import generic
|
||||||
|
|
||||||
|
from .apps import utilisateurs
|
||||||
|
from .models import Professionnel
|
||||||
|
|
||||||
|
@utilisateurs
|
||||||
|
class UtilisateurView(generic.DetailView):
|
||||||
|
|
||||||
|
template_name = "utilisateurs/details.html"
|
||||||
|
model = Professionnel
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
qs = self.get_queryset()
|
||||||
|
return qs.filter(pk=self.request.user.pk)
|
||||||
+82
-28
@@ -1,39 +1,93 @@
|
|||||||
from .mixins import *
|
from .mixins import (WebsiteTemplateMixin, WebsiteAjaxTemplateMixin,
|
||||||
|
SpecialUserRequiredMixin)
|
||||||
|
from .navbar import ApplicationMenu
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _insert_bases(cls, bases):
|
def _insert_bases(cls, bases):
|
||||||
|
""" Insert new bases in given view class """
|
||||||
old_bases = cls.__bases__
|
old_bases = cls.__bases__
|
||||||
new_bases = tuple(bases) + old_bases
|
new_bases = tuple(bases) + old_bases
|
||||||
cls.__bases__ = new_bases
|
cls.__bases__ = new_bases
|
||||||
|
|
||||||
def app_config(**options):
|
|
||||||
""" Insert per-application configuration options :
|
|
||||||
-- name : name of the app to register under in navbar
|
class Webpage:
|
||||||
-- groups : user groups needed to access this application
|
""" Webpage configurator. It is used as a decorator.
|
||||||
-- menu : user menu templates to be used
|
|
||||||
-- admin_menu : admin menu templates, only appear for superuser
|
The constructor takes one positionnal argument:
|
||||||
-- ajax : view will return content_template for Ajax requests
|
- app_name : name of the application where this view shall be categorized.
|
||||||
|
and keyword arguments:
|
||||||
|
- defaults : mapping of default options.
|
||||||
|
- menu : does it register a menu ? default is True
|
||||||
|
- icon : bootstrap name of menu header icon, ignored if 'menu' is False.
|
||||||
|
|
||||||
|
Options are :
|
||||||
|
- title: tuple of (header, header_small), header_small is optionnal.
|
||||||
|
- restricted: set of group to which access is restricted.
|
||||||
|
- ajax: can this view be called as ajax ?
|
||||||
|
|
||||||
"""
|
"""
|
||||||
name = options.pop('name', None)
|
|
||||||
groups = options.pop('groups', []) #Transition from app_users
|
|
||||||
menu = options.pop('menu', [])
|
|
||||||
admin_menu = options.pop('admin_menu', [])
|
|
||||||
ajax = options.pop('ajax', False)
|
|
||||||
|
|
||||||
new_bases = []
|
options = [
|
||||||
if ajax:
|
('title', ('Unset', 'small header')),
|
||||||
new_bases.append(WebsiteAjaxTemplateMixin)
|
('restricted', []),
|
||||||
else:
|
('ajax', False)
|
||||||
new_bases.append(WebsiteTemplateMixin)
|
]
|
||||||
|
|
||||||
if groups: #TODO: use group instaed of user class
|
def __init__(self, app_name, icon=None, defaults={}, menu=True):
|
||||||
new_bases.append(SpecialUserRequiredMixin)
|
self.app_name = app_name
|
||||||
|
|
||||||
def class_decorator(cls):
|
if menu: # Build ApplicationMenu subclass
|
||||||
_insert_bases(cls, new_bases)
|
app_menu = type(
|
||||||
cls._user_menu = menu
|
app_name.title() + "Menu",
|
||||||
cls._admin_menu = admin_menu
|
(ApplicationMenu,),
|
||||||
cls.app_name = name
|
{'name': app_name,
|
||||||
cls.app_users = groups.copy()
|
'header': (app_name.title(), '%s:index' % app_name, icon),
|
||||||
return cls
|
'_links': [],
|
||||||
|
'_dropdowns': [],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.app_menu = app_menu
|
||||||
|
else:
|
||||||
|
self.app_menu = None
|
||||||
|
|
||||||
return class_decorator
|
self._defaults = {}
|
||||||
|
self._updated = {} # Store updated options
|
||||||
|
# Set all default options
|
||||||
|
for opt_name, opt_default in self.options:
|
||||||
|
self._set_option(opt_name, defaults.get(opt_name, opt_default))
|
||||||
|
|
||||||
|
def __getattr__(self, attr):
|
||||||
|
""" Return the overriden value if any, default overwise """
|
||||||
|
return self._updated.get(attr, self._defaults[attr])
|
||||||
|
|
||||||
|
def _set_option(self, attr, value):
|
||||||
|
""" Set the default value if there is none already, updated overwise """
|
||||||
|
if not attr in self._defaults:
|
||||||
|
self._defaults[attr] = value
|
||||||
|
else:
|
||||||
|
if attr in self._updated:
|
||||||
|
raise RuntimeError(attr, 'has already been updated !')
|
||||||
|
self._updated[attr] = value
|
||||||
|
|
||||||
|
def __call__(self, view_cls):
|
||||||
|
""" Setup the view and return it """
|
||||||
|
bases_to_add = []
|
||||||
|
if self.ajax: bases_to_add.append(WebsiteAjaxTemplateMixin)
|
||||||
|
else: bases_to_add.append(WebsiteTemplateMixin)
|
||||||
|
if self.restricted: bases_to_add.append(SpecialUserRequiredMixin)
|
||||||
|
_insert_bases(view_cls, bases_to_add)
|
||||||
|
# Setup configuration. ISSUE: defaults values will be overriden !
|
||||||
|
view_cls.app_name = self.app_name
|
||||||
|
view_cls.header = self.title
|
||||||
|
view_cls.app_users = self.restricted
|
||||||
|
self._updated = {} # Reset updated attributes to avoid misbehavior
|
||||||
|
return view_cls
|
||||||
|
|
||||||
|
def using(self, **kwargs):
|
||||||
|
""" Overrides defaults options with the values given """
|
||||||
|
for opt_name, _ in self.options:
|
||||||
|
if opt_name in kwargs:
|
||||||
|
self._set_option(opt_name, kwargs[opt_name])
|
||||||
|
return self
|
||||||
+28
-125
@@ -1,151 +1,64 @@
|
|||||||
import datetime
|
|
||||||
from django.utils import timezone
|
|
||||||
from django.core.exceptions import ImproperlyConfigured
|
from django.core.exceptions import ImproperlyConfigured
|
||||||
from django.apps import apps
|
|
||||||
from django.contrib.auth.decorators import user_passes_test
|
from django.contrib.auth.decorators import user_passes_test
|
||||||
from django.template import Template, Context
|
from django.template import Template, Context
|
||||||
from django.views.generic.base import ContextMixin, TemplateResponseMixin
|
from django.views.generic.base import TemplateResponseMixin
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Mixins ##
|
## Mixins ##
|
||||||
|
|
||||||
def special_user_required(authorized_users):
|
|
||||||
|
|
||||||
valid_cls = tuple(authorized_users)
|
|
||||||
|
|
||||||
def check_special_user(user):
|
|
||||||
print('check user is instance of', valid_cls)
|
|
||||||
if isinstance(user, valid_cls):
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
return user_passes_test(check_special_user)
|
|
||||||
|
|
||||||
|
|
||||||
class SpecialUserRequiredMixin(object):
|
class SpecialUserRequiredMixin(object):
|
||||||
|
""" Requires that the User is an instance of some class """
|
||||||
app_users = []
|
app_users = []
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def as_view(cls, **initkwargs):
|
def as_view(cls, **initkwargs):
|
||||||
view = super().as_view(**initkwargs)
|
view = super().as_view(**initkwargs)
|
||||||
return special_user_required(cls.app_users)(view)
|
return cls.special_user_required(cls.app_users)(view)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def special_user_required(authorized_users):
|
||||||
|
valid_cls = tuple(authorized_users)
|
||||||
|
if not valid_cls: # No restriction usually means misconfiguration !
|
||||||
|
raise ImproperlyConfigured(
|
||||||
|
'A view was configured as "restricted" with no restricting parameters !')
|
||||||
|
|
||||||
|
def check_special_user(user):
|
||||||
class TemplateFieldsMetaclass(type):
|
if isinstance(user, valid_cls):
|
||||||
""" Loads Template objects with given string for
|
return True
|
||||||
header, header_small, title, ...
|
else:
|
||||||
|
return False
|
||||||
Theses strings shall be found in cls.Template
|
return user_passes_test(check_special_user)
|
||||||
"""
|
|
||||||
def __init__(cls, bases, Dict):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def user_processor(request, context):
|
def user_processor(request, context):
|
||||||
context['user_group'] = request.user.__class__.__qualname__
|
context['user_group'] = request.user.__class__.__qualname__
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
def header_processor(header, context):
|
||||||
class NavbarMixin(object):
|
context['page_header'] = Template(header[0]).render(context)
|
||||||
|
context['page_header_small'] = Template(header[1]).render(context) if len(header) == 2 else ''
|
||||||
registered_apps = ['maraudes', 'suivi']
|
context['page_title'] = " - ".join((context['page_header'], context['page_header_small']))
|
||||||
app_name = None
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_apps_config(self):
|
class WebsiteTemplateMixin(TemplateResponseMixin):
|
||||||
""" Load additionnal config data on each app registered in navbar
|
|
||||||
Add :
|
|
||||||
- menu_icon : glyphicon in sidebar
|
|
||||||
- disabled : show/hide in sidebar
|
|
||||||
"""
|
|
||||||
## Utils ##
|
|
||||||
APP_ICONS = {
|
|
||||||
'maraudes': 'road',
|
|
||||||
'suivi': 'eye-open',
|
|
||||||
}
|
|
||||||
app_names = self.registered_apps
|
|
||||||
self._apps = []
|
|
||||||
for name in app_names:
|
|
||||||
app_config = apps.get_app_config(name)
|
|
||||||
app_config.menu_icon = APP_ICONS[name]
|
|
||||||
#TODO: Seems unsafe (only need module perm)
|
|
||||||
app_config.disabled = not self.request.user.has_module_perms(name)
|
|
||||||
self._apps.append(app_config)
|
|
||||||
return self._apps
|
|
||||||
|
|
||||||
@property
|
|
||||||
def apps(self):
|
|
||||||
if not hasattr(self, '_apps'):
|
|
||||||
self._apps = self.get_apps_config()
|
|
||||||
return self._apps
|
|
||||||
|
|
||||||
def get_active_app(self):
|
|
||||||
if not self.app_name:
|
|
||||||
self.app_name = self.__class__.__module__.split(".")[0]
|
|
||||||
# If app is website, there is no "active" application
|
|
||||||
if self.app_name == "website":
|
|
||||||
return None
|
|
||||||
|
|
||||||
active_app = apps.get_app_config(self.app_name)
|
|
||||||
if not active_app in self.apps: #TODO: how do we deal with this ?
|
|
||||||
raise ValueError("%s must be registered in Configuration.navbar_apps" % active_app)
|
|
||||||
return active_app
|
|
||||||
|
|
||||||
@property
|
|
||||||
def active_app(self):
|
|
||||||
if not hasattr(self, '_active_app'):
|
|
||||||
self._active_app = self.get_active_app()
|
|
||||||
return self._active_app
|
|
||||||
|
|
||||||
@property
|
|
||||||
def menu(self):
|
|
||||||
""" Renvoie la liste des templates utilisés comme menu pour l'application
|
|
||||||
active
|
|
||||||
"""
|
|
||||||
if not self.request.user.is_superuser:
|
|
||||||
return self._user_menu
|
|
||||||
return self._user_menu + self._admin_menu
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class WebsiteTemplateMixin(NavbarMixin, TemplateResponseMixin):
|
|
||||||
""" Mixin for easy integration of 'website' templates
|
""" Mixin for easy integration of 'website' templates
|
||||||
|
|
||||||
Each child can specify:
|
|
||||||
- title : title of the page
|
|
||||||
- header : header of the page
|
|
||||||
- header_small : sub-header of the page
|
|
||||||
|
|
||||||
If 'content_template' is not defined, value will fallback to template_name
|
If 'content_template' is not defined, value will fallback to template_name
|
||||||
in child view.
|
in child view.
|
||||||
"""
|
"""
|
||||||
base_template = "base_site.html"
|
base_template = "base_site.html"
|
||||||
content_template = None
|
content_template = None
|
||||||
|
app_name = None
|
||||||
_user_menu = []
|
|
||||||
_admin_menu = []
|
|
||||||
_groups = []
|
|
||||||
|
|
||||||
|
|
||||||
class Configuration:
|
class Configuration:
|
||||||
stylesheets = ['base.css']
|
stylesheets = ['css/base.css']
|
||||||
page_blocks = ['header', 'header_small', 'title']
|
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
self.user = None
|
self.user = None
|
||||||
self._page_blocks = []
|
|
||||||
if not hasattr(self, "PageInfo"):
|
|
||||||
raise ImproperlyConfigured("You must define a PageInfo on ", self)
|
|
||||||
for attr, val in self.PageInfo.__dict__.items():
|
|
||||||
if attr[0] is not "_" and type(val) is str:
|
|
||||||
setattr(self, attr, Template(val))
|
|
||||||
self._page_blocks.append(attr)
|
|
||||||
|
|
||||||
def get_template_names(self):
|
def get_template_names(self):
|
||||||
""" Ensure same template for all children views. """
|
""" Ensure same template for all children views. """
|
||||||
@@ -159,30 +72,20 @@ class WebsiteTemplateMixin(NavbarMixin, TemplateResponseMixin):
|
|||||||
raise ImproperlyConfigured(self, "has no template defined !")
|
raise ImproperlyConfigured(self, "has no template defined !")
|
||||||
return self.content_template
|
return self.content_template
|
||||||
|
|
||||||
|
|
||||||
def _update_context_with_rendered_blocks(self, context):
|
|
||||||
""" Render text for existing PageInfo attributes.
|
|
||||||
See Configuration.page_blocks for valid attribute names """
|
|
||||||
render_context = Context(context)
|
|
||||||
for attr in self._page_blocks:
|
|
||||||
name = "page_%s" % attr
|
|
||||||
context[name] = getattr(self, attr).render(render_context)
|
|
||||||
return context
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = Context(super().get_context_data(**kwargs))
|
||||||
self._update_context_with_rendered_blocks(context)
|
|
||||||
#Website processor
|
#Website processor
|
||||||
context['stylesheets'] = self.Configuration.stylesheets
|
context['stylesheets'] = self.Configuration.stylesheets
|
||||||
context['apps'] = self.apps
|
context['active_app'] = self.app_name # Set by Webpage decorator
|
||||||
context['active_app'] = self.active_app
|
|
||||||
# User processor
|
# User processor
|
||||||
|
context = header_processor(self.header, context)
|
||||||
context = user_processor(self.request, context)
|
context = user_processor(self.request, context)
|
||||||
#Webpage
|
#Webpage
|
||||||
context['content_template'] = self.get_content_template()
|
context['content_template'] = self.get_content_template()
|
||||||
context['app_menu'] = self.menu
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class WebsiteAjaxTemplateMixin(WebsiteTemplateMixin):
|
class WebsiteAjaxTemplateMixin(WebsiteTemplateMixin):
|
||||||
""" Mixin that returns content_template instead of base_template when
|
""" Mixin that returns content_template instead of base_template when
|
||||||
request is Ajax.
|
request is Ajax.
|
||||||
|
|||||||
@@ -0,0 +1,102 @@
|
|||||||
|
""" Draft for navbar application menu
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
registered = []
|
||||||
|
|
||||||
|
|
||||||
|
class Link:
|
||||||
|
""" Navbar link
|
||||||
|
|
||||||
|
Constructor takes one required argument :
|
||||||
|
- text : text to display for the link
|
||||||
|
|
||||||
|
and two optional arguments :
|
||||||
|
- target : str or tuple (str, dict)
|
||||||
|
- icon : bootstrap icon name, no validation
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, text, target="#", icon=None):
|
||||||
|
self.text = text
|
||||||
|
self._target = target
|
||||||
|
self.icon = icon
|
||||||
|
|
||||||
|
@property
|
||||||
|
def href(self):
|
||||||
|
""" Lazy creation of html 'href' value, using 'target' instance attribute """
|
||||||
|
if not hasattr(self, '_href'):
|
||||||
|
if self._target == "#": #Create void link
|
||||||
|
self._href = "#"
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
target, kwargs = self._target
|
||||||
|
except ValueError:
|
||||||
|
target = self._target
|
||||||
|
kwargs = {}
|
||||||
|
assert type(target) == str
|
||||||
|
assert type(kwargs) == dict
|
||||||
|
self._href = reverse(target, **kwargs)
|
||||||
|
return self._href
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class LinkManager:
|
||||||
|
""" Per-class manager of links """
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.items = []
|
||||||
|
|
||||||
|
def __get__(self, instance, owner):
|
||||||
|
if instance: #Filtering done at each call, not optimized at all !
|
||||||
|
if not instance.user.is_superuser:
|
||||||
|
return [link for link in self.items if link.admin_link == False]
|
||||||
|
else:
|
||||||
|
return self.items.copy()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def add(self, link):
|
||||||
|
self.items.append(link)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<LinkManager: [' + ', '.join((l.text for l in self.items)) + ']>'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class MenuRegistry(type):
|
||||||
|
""" Metaclass that registers subclass into module level variable 'registered' """
|
||||||
|
def __new__(metacls, name, bases, attrs):
|
||||||
|
cls = type.__new__(metacls, name, bases, attrs)
|
||||||
|
if name != "ApplicationMenu":
|
||||||
|
print('registering menu', cls)
|
||||||
|
registered.append(cls)
|
||||||
|
# Create Link instance for header attributes
|
||||||
|
try:
|
||||||
|
header, target, icon = cls.header
|
||||||
|
except ValueError:
|
||||||
|
header = cls.header
|
||||||
|
target = "#"
|
||||||
|
icon = None
|
||||||
|
cls.header = Link(header, target, icon)
|
||||||
|
cls.links = LinkManager()
|
||||||
|
return cls
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ApplicationMenu(metaclass=MenuRegistry):
|
||||||
|
name = None
|
||||||
|
header = None
|
||||||
|
|
||||||
|
def __init__(self, view, user):
|
||||||
|
self.view = view
|
||||||
|
self.user = user
|
||||||
|
self.is_active = self.name == self.view.app_name
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_link(cls, link, admin=False):
|
||||||
|
if not isinstance(link, Link):
|
||||||
|
link = Link(*link)
|
||||||
|
link.admin_link = admin
|
||||||
|
cls.links.add(link)
|
||||||
|
|
||||||
-14
@@ -1,14 +0,0 @@
|
|||||||
/*!
|
|
||||||
* Bootstrap v3.3.7 (http://getbootstrap.com)
|
|
||||||
* Copyright 2011-2016 Twitter, Inc.
|
|
||||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*!
|
|
||||||
* Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=7f853f3d936c9ba68499a06009229bc9)
|
|
||||||
* Config saved to config.json and https://gist.github.com/7f853f3d936c9ba68499a06009229bc9
|
|
||||||
*//*!
|
|
||||||
* Bootstrap v3.3.7 (http://getbootstrap.com)
|
|
||||||
* Copyright 2011-2016 Twitter, Inc.
|
|
||||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
|
||||||
*//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{background:transparent !important;color:#000 !important;-webkit-box-shadow:none !important;box-shadow:none !important;text-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:'Glyphicons Halflings';src:url('../fonts/glyphicons-halflings-regular.eot');src:url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'),url('../fonts/glyphicons-halflings-regular.woff') format('woff'),url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'),url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-downloadLine truncated
|
|
||||||
@@ -1,23 +1,19 @@
|
|||||||
|
|
||||||
#menu {
|
|
||||||
border: none;
|
|
||||||
border-right: 4px solid #980300;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-toggle{
|
|
||||||
border-right: 4px solid #980300 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dropdown-menu {
|
|
||||||
border-bottom: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-fixed-side .navbar-nav>li>a {
|
.navbar-fixed-side .navbar-nav>li>a {
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
font-variant: small-caps;
|
font-variant: small-caps;
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#menu {
|
||||||
|
border: none;
|
||||||
|
border-right: 4px solid #980300;
|
||||||
|
background-color: #121212;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width:768px){
|
||||||
|
#menu { border: none; }
|
||||||
|
}
|
||||||
|
|
||||||
.app-menu {
|
.app-menu {
|
||||||
background-color: #121212;
|
background-color: #121212;
|
||||||
@@ -25,10 +21,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@media (max-width:768px){
|
.active{
|
||||||
#menu { border: none; }
|
border-right: 2px solid #980300 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.jumbotron {
|
.dropdown-menu {
|
||||||
background-color: #fefefe;
|
border-bottom: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
Vendored
File renamed without changes.
@@ -0,0 +1,11 @@
|
|||||||
|
@import url("https://fonts.googleapis.com/css?family=Open+Sans:400,700");/*!
|
||||||
|
* bootswatch v3.3.7
|
||||||
|
* Homepage: http://bootswatch.com
|
||||||
|
* Copyright 2012-2016 Thomas Park
|
||||||
|
* Licensed under MIT
|
||||||
|
* Based on Bootstrap
|
||||||
|
*//*!
|
||||||
|
* Bootstrap v3.3.7 (http://getbootstrap.com)
|
||||||
|
* Copyright 2011-2016 Twitter, Inc.
|
||||||
|
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||||
|
*//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{background:transparent !important;color:#000 !important;-webkit-box-shadow:none !important;box-shadow:none !important;text-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}@font-face{font-family:'Glyphicons Halflings';src:url('../fonts/glyphicons-halflings-regular.eot');src:url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'),url('../fonts/glyphicons-halflings-regular.woff') format('woff'),url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'),url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-euro:before,.glyphicon-eur:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-downloadLine truncated
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Vendored
File renamed without changes.
File renamed without changes.
Vendored
+1
@@ -0,0 +1 @@
|
|||||||
|
.select2-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle;}.select2-container .select2-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:28px;user-select:none;-webkit-user-select:none;}.select2-container .select2-selection--single .select2-selection__rendered{display:block;padding-left:8px;padding-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:8px;padding-left:20px;}.select2-container .select2-selection--multiple{box-sizing:border-box;cursor:pointer;display:block;min-height:32px;user-select:none;-webkit-user-select:none;}.select2-container .select2-selection--multiple .select2-selection__rendered{display:inline-block;overflow:hidden;padding-left:8px;text-overflow:ellipsis;white-space:nowrap;}.select2-container .select2-search--inline{float:left;}.select2-container .select2-search--inline .select2-search__field{box-sizing:border-box;border:none;font-size:100%;margin-top:5px;}.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none;}.select2-dropdown{background-color:white;border:1px solid #aaa;border-radius:4px;box-sizing:border-box;display:block;position:absolute;left:-100000px;width:100%;z-index:1051;}.select2-results{display:block;}.select2-results__options{list-style:none;margin:0;padding:0;}.select2-results__option{padding:6px;user-select:none;-webkit-user-select:none;}.select2-results__option[aria-selected]{cursor:pointer;}.select2-container--open .select2-dropdown{left:0;}.select2-container--open .select2-dropdown--above{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;}.select2-container--open .select2-dropdown--below{border-top:none;border-top-left-radius:0;border-top-right-radius:0;}.select2-search--dropdown{display:block;padding:4px;}.select2-search--dropdown .select2-search__field{padding:4px;width:100%;box-sizing:border-box;}.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none;}.select2-search--dropdown.select2-search--hide{display:none;}.select2-close-mask{border:0;margin:0;padding:0;display:block;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:99;background-color:#fff;filter:alpha(opacity=0);}.select2-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px;}.select2-container--default .select2-selection--single{background-color:#fff;border:1px solid #aaa;border-radius:4px;}.select2-container--default .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px;}.select2-container--default .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;}.select2-container--default .select2-selection--single .select2-selection__placeholder{color:#999;}.select2-container--default .select2-selection--single .select2-selection__arrow{height:26px;position:absolute;top:1px;right:1px;width:20px;}.select2-container--default .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0;}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left;}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow{left:1px;right:auto;}.select2-container--default.select2-container--disabled .select2-selection--single{background-color:#eee;cursor:default;}.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear{display:none;}.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px;}.select2-container--default .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;}.select2-container--default .select2-selection--multiple .select2-selection__rendered{box-sizing:border-box;list-style:none;margin:0;padding:0 5px;width:100%;}.select2-container--default .select2-selection--multiple .select2-selection__placeholder{color:#999;margin-top:5px;float:left;}.select2-container--default .select2-selection--multiple .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-top:5px;margin-right:10px;}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px;}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{color:#999;cursor:poLine truncated
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB |
@@ -1,21 +0,0 @@
|
|||||||
|
|
||||||
/* Maraudes application base stylesheet */
|
|
||||||
|
|
||||||
.rencontre {
|
|
||||||
background-color: #efefef;
|
|
||||||
width:100%;
|
|
||||||
border: 5px dotted white;
|
|
||||||
margin:2px;
|
|
||||||
margin-bottom:8px;
|
|
||||||
font-family: Arial;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header td {
|
|
||||||
color: white;
|
|
||||||
background-color: black;
|
|
||||||
text-align:center;
|
|
||||||
font-weight:bold;
|
|
||||||
font-family: Arial;
|
|
||||||
padding: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
+43
@@ -0,0 +1,43 @@
|
|||||||
|
/* Lier les boutons de création
|
||||||
|
* Thanks to Derek Morgan, https://dmorgan.info/posts/django-views-bootstrap-modals/
|
||||||
|
*/
|
||||||
|
;(function($) {
|
||||||
|
|
||||||
|
var formAjaxSubmit = function(form, modal)
|
||||||
|
{
|
||||||
|
$(form).submit(function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
$.ajax({
|
||||||
|
type: $(this).attr('method'),
|
||||||
|
url: $(this).attr('action'),
|
||||||
|
data: $(this).serialize(),
|
||||||
|
success: function (xhr, ajaxOptions, thrownError) {
|
||||||
|
if ( $(xhr).find('.has-error').length > 0 || $(xhr).find('.alert-danger').length > 0) {
|
||||||
|
$(modal).find('.modal-body').html(xhr);
|
||||||
|
formAjaxSubmit(form, modal);
|
||||||
|
} else {
|
||||||
|
$(modal).modal('toggle');
|
||||||
|
// Reload page ?
|
||||||
|
location.reload()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (xhr, ajaxOptions, thrownError) {
|
||||||
|
// handle response errors here
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
$.fn.openModalEvent = function(id, href, title)
|
||||||
|
{
|
||||||
|
$('#'+id).click(function() {
|
||||||
|
$('#form-modal-body').load(href, function()
|
||||||
|
{
|
||||||
|
$('.modal-title').text(title);
|
||||||
|
$('#form-modal').modal('toggle');
|
||||||
|
formAjaxSubmit("#form-modal-body form", "#form-modal");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
})(jQuery);
|
||||||
|
|
||||||
File renamed without changes.
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
/*! jQuery v1.11.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */
|
||||||
|
!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.1",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b=a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+-new Date,v=a.document,w=0,x=0,y=gb(),z=gb(),A=gb(),B=function(a,b){return a===b&&(l=!0),0},C="undefined",D=1<<31,E={}.hasOwnProperty,F=[],G=F.pop,H=F.push,I=F.push,J=F.slice,K=F.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},L="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",N="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=N.replace("w","w#"),P="\\["+M+"*("+N+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"Line truncated
|
||||||
|
if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?m.queue(this[0],a):void 0===b?this:this.each(function(){var c=m.queue(this,a,b);m._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&m.dequeue(this,a)})},dequeue:function(a){return this.each(function(){m.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=m.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=m._data(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var S=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=["Top","Right","Bottom","Left"],U=function(a,b){return a=b||a,"none"===m.css(a,"display")||!m.contains(a.ownerDocument,a)},V=m.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===m.type(c)){e=!0;for(h in c)m.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,m.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(m(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav></:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="<input type='radio' checked='checked' name='t'/>",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function ab(){return!0}function bb(){return!1}function cb(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=rLine truncated
|
||||||
|
},cur:function(){var a=Zb.propHooks[this.prop];return a&&a.get?a.get(this):Zb.propHooks._default.get(this)},run:function(a){var b,c=Zb.propHooks[this.prop];return this.pos=b=this.options.duration?m.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Zb.propHooks._default.set(this),this}},Zb.prototype.init.prototype=Zb.prototype,Zb.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=m.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){m.fx.step[a.prop]?m.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[m.cssProps[a.prop]]||m.cssHooks[a.prop])?m.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Zb.propHooks.scrollTop=Zb.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},m.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},m.fx=Zb.prototype.init,m.fx.step={};var $b,_b,ac=/^(?:toggle|show|hide)$/,bc=new RegExp("^(?:([+-])=|)("+S+")([a-z%]*)$","i"),cc=/queueHooks$/,dc=[ic],ec={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=bc.exec(b),f=e&&e[3]||(m.cssNumber[a]?"":"px"),g=(m.cssNumber[a]||"px"!==f&&+d)&&bc.exec(m.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,m.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function fc(){return setTimeout(function(){$b=void 0}),$b=m.now()}function gc(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=T[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function hc(a,b,c){for(var d,e=(ec[b]||[]).concat(ec["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function ic(a,b,c){var d,e,f,g,h,i,j,l,n=this,o={},p=a.style,q=a.nodeType&&U(a),r=m._data(a,"fxshow");c.queue||(h=m._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,n.always(function(){n.always(function(){h.unqueued--,m.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[p.overflow,p.overflowX,p.overflowY],j=m.css(a,"display"),l="none"===j?m._data(a,"olddisplay")||Fb(a.nodeName):j,"inline"===l&&"none"===m.css(a,"float")&&(k.inlineBlockNeedsLayout&&"inline"!==Fb(a.nodeName)?p.zoom=1:p.display="inline-block")),c.overflow&&(p.overflow="hidden",k.shrinkWrapBlocks()||n.always(function(){p.overflow=c.overflow[0],p.overflowX=c.overflow[1],p.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],ac.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(q?"hide":"show")){if("show"!==e||!r||void 0===r[d])continue;q=!0}o[d]=r&&r[d]||m.style(a,d)}else j=void 0;if(m.isEmptyObject(o))"inline"===("none"===j?Fb(a.nodeName):j)&&(p.display=j);else{r?"hidden"in r&&(q=r.hidden):r=m._data(a,"fxshow",{}),f&&(r.hidden=!q),q?m(a).show():n.done(function(){m(a).hide()}),n.done(function(){var b;m._removeData(a,"fxshow");for(b in o)m.style(a,b,o[b])});for(d in o)g=hc(q?r[d]:0,d,n),d in r||(r[d]=g.start,q&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function jc(a,b){var c,d,e,f,g;for(c in a)if(d=m.camelCase(c),e=b[d],f=a[c],m.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=m.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kc(a,b,c){var d,e,f=0,g=dc.length,h=m.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=$b||fc(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:m.extend({},b),opts:m.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:$b||fc(),duration:c.duration,tweens:[],createTween:function(b,c){var d=m.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(jc(k,j.opts.specialEasing);g>f;f++)if(d=dc[f].call(j,a,k,j.opts))return d;return m.map(k,hc,j),m.isFunction(j.opts.start)&&j.opts.start.call(a,j),m.fx.timer(m.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}m.Animation=m.extend(kc,{tweener:function(a,b){m.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],ec[c]=ec[c]||[],ec[c].unshift(b)},prefilter:function(a,b){b?dc.unshift(a):dc.push(a)}}),m.speed=function(a,b,c){var d=a&&"object"==typeof a?m.extend({},a):{complete:c||!c&&b||m.isFunction(a)&&a,duration:a,easing:c&&b||b&&!m.isFunction(b)&&b};return d.duration=m.fLine truncated
|
||||||
|
//# sourceMappingURL=jquery.min.map
|
||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
/*! Select2 4.0.0 | https://github.com/select2/select2/blob/master/LICENSE.md */!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):jQuery)}(function(a){var b=function(){if(a&&a.fn&&a.fn.select2&&a.fn.select2.amd)var b=a.fn.select2.amd;var b;return function(){if(!b||!b.requirejs){b?c=b:b={};var a,c,d;!function(b){function e(a,b){return u.call(a,b)}function f(a,b){var c,d,e,f,g,h,i,j,k,l,m,n=b&&b.split("/"),o=s.map,p=o&&o["*"]||{};if(a&&"."===a.charAt(0))if(b){for(n=n.slice(0,n.length-1),a=a.split("/"),g=a.length-1,s.nodeIdCompat&&w.test(a[g])&&(a[g]=a[g].replace(w,"")),a=n.concat(a),k=0;k<a.length;k+=1)if(m=a[k],"."===m)a.splice(k,1),k-=1;else if(".."===m){if(1===k&&(".."===a[2]||".."===a[0]))break;k>0&&(a.splice(k-1,2),k-=2)}a=a.join("/")}else 0===a.indexOf("./")&&(a=a.substring(2));if((n||p)&&o){for(c=a.split("/"),k=c.length;k>0;k-=1){if(d=c.slice(0,k).join("/"),n)for(l=n.length;l>0;l-=1)if(e=o[n.slice(0,l).join("/")],e&&(e=e[d])){f=e,h=k;break}if(f)break;!i&&p&&p[d]&&(i=p[d],j=k)}!f&&i&&(f=i,h=j),f&&(c.splice(0,h,f),a=c.join("/"))}return a}function g(a,c){return function(){return n.apply(b,v.call(arguments,0).concat([a,c]))}}function h(a){return function(b){return f(b,a)}}function i(a){return function(b){q[a]=b}}function j(a){if(e(r,a)){var c=r[a];delete r[a],t[a]=!0,m.apply(b,c)}if(!e(q,a)&&!e(t,a))throw new Error("No "+a);return q[a]}function k(a){var b,c=a?a.indexOf("!"):-1;return c>-1&&(b=a.substring(0,c),a=a.substring(c+1,a.length)),[b,a]}function l(a){return function(){return s&&s.config&&s.config[a]||{}}}var m,n,o,p,q={},r={},s={},t={},u=Object.prototype.hasOwnProperty,v=[].slice,w=/\.js$/;o=function(a,b){var c,d=k(a),e=d[0];return a=d[1],e&&(e=f(e,b),c=j(e)),e?a=c&&c.normalize?c.normalize(a,h(b)):f(a,b):(a=f(a,b),d=k(a),e=d[0],a=d[1],e&&(c=j(e))),{f:e?e+"!"+a:a,n:a,pr:e,p:c}},p={require:function(a){return g(a)},exports:function(a){var b=q[a];return"undefined"!=typeof b?b:q[a]={}},module:function(a){return{id:a,uri:"",exports:q[a],config:l(a)}}},m=function(a,c,d,f){var h,k,l,m,n,s,u=[],v=typeof d;if(f=f||a,"undefined"===v||"function"===v){for(c=!c.length&&d.length?["require","exports","module"]:c,n=0;n<c.length;n+=1)if(m=o(c[n],f),k=m.f,"require"===k)u[n]=p.require(a);else if("exports"===k)u[n]=p.exports(a),s=!0;else if("module"===k)h=u[n]=p.module(a);else if(e(q,k)||e(r,k)||e(t,k))u[n]=j(k);else{if(!m.p)throw new Error(a+" missing "+k);m.p.load(m.n,g(f,!0),i(k),{}),u[n]=q[k]}l=d?d.apply(q[a],u):void 0,a&&(h&&h.exports!==b&&h.exports!==q[a]?q[a]=h.exports:l===b&&s||(q[a]=l))}else a&&(q[a]=d)},a=c=n=function(a,c,d,e,f){if("string"==typeof a)return p[a]?p[a](c):j(o(a,c).f);if(!a.splice){if(s=a,s.deps&&n(s.deps,s.callback),!c)return;c.splice?(a=c,c=d,d=null):a=b}return c=c||function(){},"function"==typeof d&&(d=e,e=f),e?m(b,a,c,d):setTimeout(function(){m(b,a,c,d)},4),n},n.config=function(a){return n(a)},a._defined=q,d=function(a,b,c){b.splice||(c=b,b=[]),e(q,a)||e(r,a)||(r[a]=[a,b,c])},d.amd={jQuery:!0}}(),b.requirejs=a,b.require=c,b.define=d}}(),b.define("almond",function(){}),b.define("jquery",[],function(){var b=a||$;return null==b&&console&&console.error&&console.error("Select2: An instance of jQuery or a jQuery-compatible library was not found. Make sure that you are including jQuery before Select2 on your web page."),b}),b.define("select2/utils",["jquery"],function(a){function b(a){var b=a.prototype,c=[];for(var d in b){var e=b[d];"function"==typeof e&&"constructor"!==d&&c.push(d)}return c}var c={};c.Extend=function(a,b){function c(){this.constructor=a}var d={}.hasOwnProperty;for(var e in b)d.call(b,e)&&(a[e]=b[e]);return c.prototype=b.prototype,a.prototype=new c,a.__super__=b.prototype,a},c.Decorate=function(a,c){function d(){var b=Array.prototype.unshift,d=c.prototype.constructor.length,e=a.prototype.constructor;d>0&&(b.call(arguments,a.prototype.constructor),e=c.prototype.constructor),e.apply(this,arguments)}function e(){this.constructor=d}var f=b(c),g=b(a);c.displayName=a.displayName,d.prototype=new e;for(var h=0;h<g.length;h++){var i=g[h];d.prototype[i]=a.prototype[i]}for(var j=(function(a){var b=function(){};a in d.prototype&&(b=d.prototype[a]);var e=c.prototype[a];return function(){var a=Array.prototype.unshift;return a.call(arguments,b),e.apply(this,arguments)}}),k=0;k<f.length;k++){var l=f[k];d.prototype[l]=j(l)}return d};var d=function(){this.listeners={}};return d.prototype.on=function(a,b){this.listeners=this.listeners||{},a in this.listeners?this.listeners[a].push(b):this.listeners[a]=[b]},d.prototype.trigger=function(a){var b=Array.prototype.slice;this.listeners=this.listeners||{},a in this.listeners&&this.invoke(this.listeners[a],b.call(arguments,1)),"*"in this.listeners&&this.invoke(this.listeners["*"],arguments)},d.prototype.invoke=function(a,b){for(var c=0,d=a.length;d>c;c++)a[c].apply(this,b)},c.Observable=d,c.generateChars=function(a){for(var b="",c=0;a>c;c++){var d=Math.floor(36*Math.random());b+=d.toString(3Line truncated
|
||||||
|
if(b=c.data(a[0],"data"),null!=b)return b;if(a.is("option"))b={id:a.val(),text:a.text(),disabled:a.prop("disabled"),selected:a.prop("selected"),title:a.prop("title")};else if(a.is("optgroup")){b={text:a.prop("label"),children:[],title:a.prop("title")};for(var d=a.children("option"),e=[],f=0;f<d.length;f++){var g=c(d[f]),h=this.item(g);e.push(h)}b.children=e}return b=this._normalizeItem(b),b.element=a[0],c.data(a[0],"data",b),b},d.prototype._normalizeItem=function(a){c.isPlainObject(a)||(a={id:a,text:a}),a=c.extend({},{text:""},a);var b={selected:!1,disabled:!1};return null!=a.id&&(a.id=a.id.toString()),null!=a.text&&(a.text=a.text.toString()),null==a._resultId&&a.id&&null!=this.container&&(a._resultId=this.generateResultId(this.container,a)),c.extend({},b,a)},d.prototype.matches=function(a,b){var c=this.options.get("matcher");return c(a,b)},d}),b.define("select2/data/array",["./select","../utils","jquery"],function(a,b,c){function d(a,b){var c=b.get("data")||[];d.__super__.constructor.call(this,a,b),this.addOptions(this.convertToOptions(c))}return b.Extend(d,a),d.prototype.select=function(a){var b=this.$element.find("option").filter(function(b,c){return c.value==a.id.toString()});0===b.length&&(b=this.option(a),this.addOptions(b)),d.__super__.select.call(this,a)},d.prototype.convertToOptions=function(a){function d(a){return function(){return c(this).val()==a.id}}for(var e=this,f=this.$element.find("option"),g=f.map(function(){return e.item(c(this)).id}).get(),h=[],i=0;i<a.length;i++){var j=this._normalizeItem(a[i]);if(c.inArray(j.id,g)>=0){var k=f.filter(d(j)),l=this.item(k),m=(c.extend(!0,{},l,j),this.option(l));k.replaceWith(m)}else{var n=this.option(j);if(j.children){var o=this.convertToOptions(j.children);b.appendMany(n,o)}h.push(n)}}return h},d}),b.define("select2/data/ajax",["./array","../utils","jquery"],function(a,b,c){function d(b,c){this.ajaxOptions=this._applyDefaults(c.get("ajax")),null!=this.ajaxOptions.processResults&&(this.processResults=this.ajaxOptions.processResults),a.__super__.constructor.call(this,b,c)}return b.Extend(d,a),d.prototype._applyDefaults=function(a){var b={data:function(a){return{q:a.term}},transport:function(a,b,d){var e=c.ajax(a);return e.then(b),e.fail(d),e}};return c.extend({},b,a,!0)},d.prototype.processResults=function(a){return a},d.prototype.query=function(a,b){function d(){var d=f.transport(f,function(d){var f=e.processResults(d,a);e.options.get("debug")&&window.console&&console.error&&(f&&f.results&&c.isArray(f.results)||console.error("Select2: The AJAX results did not return an array in the `results` key of the response.")),b(f)},function(){});e._request=d}var e=this;null!=this._request&&(c.isFunction(this._request.abort)&&this._request.abort(),this._request=null);var f=c.extend({type:"GET"},this.ajaxOptions);"function"==typeof f.url&&(f.url=f.url(a)),"function"==typeof f.data&&(f.data=f.data(a)),this.ajaxOptions.delay&&""!==a.term?(this._queryTimeout&&window.clearTimeout(this._queryTimeout),this._queryTimeout=window.setTimeout(d,this.ajaxOptions.delay)):d()},d}),b.define("select2/data/tags",["jquery"],function(a){function b(b,c,d){var e=d.get("tags"),f=d.get("createTag");if(void 0!==f&&(this.createTag=f),b.call(this,c,d),a.isArray(e))for(var g=0;g<e.length;g++){var h=e[g],i=this._normalizeItem(h),j=this.option(i);this.$element.append(j)}}return b.prototype.query=function(a,b,c){function d(a,f){for(var g=a.results,h=0;h<g.length;h++){var i=g[h],j=null!=i.children&&!d({results:i.children},!0),k=i.text===b.term;if(k||j)return f?!1:(a.data=g,void c(a))}if(f)return!0;var l=e.createTag(b);if(null!=l){var m=e.option(l);m.attr("data-select2-tag",!0),e.addOptions([m]),e.insertTag(g,l)}a.results=g,c(a)}var e=this;return this._removeOldTags(),null==b.term||null!=b.page?void a.call(this,b,c):void a.call(this,b,d)},b.prototype.createTag=function(b,c){var d=a.trim(c.term);return""===d?null:{id:d,text:d}},b.prototype.insertTag=function(a,b,c){b.unshift(c)},b.prototype._removeOldTags=function(){var b=(this._lastTag,this.$element.find("option[data-select2-tag]"));b.each(function(){this.selected||a(this).remove()})},b}),b.define("select2/data/tokenizer",["jquery"],function(a){function b(a,b,c){var d=c.get("tokenizer");void 0!==d&&(this.tokenizer=d),a.call(this,b,c)}return b.prototype.bind=function(a,b,c){a.call(this,b,c),this.$search=b.dropdown.$search||b.selection.$search||c.find(".select2-search__field")},b.prototype.query=function(a,b,c){function d(a){e.select(a)}var e=this;b.term=b.term||"";var f=this.tokenizer(b,this.options,d);f.term!==b.term&&(this.$search.length&&(this.$search.val(f.term),this.$search.focus()),b.term=f.term),a.call(this,b,c)},b.prototype.tokenizer=function(b,c,d,e){for(var f=d.get("tokenSeparators")||[],g=c.term,h=0,i=this.createTag||function(a){return{id:a.term,text:a.term}};h<g.length;){var j=g[h];if(-1!==a.inArray(j,f)){var k=g.substr(0,h),l=a.extend({},c,{term:k}),m=i(l);e(m),g=g.substr(h+1)||"",h=0}else h++}return{term:g}},b}),b.define("select2/data/Line truncated
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
{% load staticfiles %} {% load bootstrap3 %}
|
{% load staticfiles %} {% load bootstrap3 %} {% load navbar %}
|
||||||
<html lang="fr">
|
<html lang="fr">
|
||||||
<head>
|
<head>
|
||||||
<title>{% block title %}La maraude{% endblock %}</title>
|
<title>{% block title %}La maraude{% endblock %}</title>
|
||||||
{% bootstrap_css %}{% bootstrap_javascript %}
|
{% bootstrap_css %}{% bootstrap_javascript %}
|
||||||
<!-- Side Navbar from http://www.samrayner.com/bootstrap-side-navbar/inverse.html -->
|
<!-- Side Navbar from http://www.samrayner.com/bootstrap-side-navbar/inverse.html -->
|
||||||
<link href="/static/bootstrap/navbar-fixed-side.css" rel="stylesheet" />
|
<link href="/static/css/bootstrap/navbar-fixed-side.css" rel="stylesheet" />
|
||||||
{% if stylesheets %}{% for stylesheet in stylesheets %}
|
{% if stylesheets %}{% for stylesheet in stylesheets %}
|
||||||
<link rel="stylesheet" type="text/css" href="{% static stylesheet %}" />{% endfor %}{% endif %}
|
<link rel="stylesheet" type="text/css" href="{% static stylesheet %}" />{% endfor %}{% endif %}
|
||||||
</head>
|
</head>
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-3 col-lg-2">
|
<div class="col-md-3 col-lg-2">
|
||||||
{% include "navbar.html" %}
|
{% navbar %}
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-9 col-lg-10">
|
<div class="col-md-9 col-lg-10">
|
||||||
<h1 class="page-header">{% block page_header %}{% endblock %}</h1>
|
<h1 class="page-header">{% block page_header %}{% endblock %}</h1>
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{% load bootstrap3 %}
|
||||||
|
<li {% if active %}class="active"{%endif%}>
|
||||||
|
<a href="{{header.href}}">{% if header.icon %}{% bootstrap_icon header.icon %} · {% endif %}<strong>{{header.text}}</strong></a>
|
||||||
|
</li>
|
||||||
|
{% if active %}{% for link in links %}
|
||||||
|
<li class="app-menu">
|
||||||
|
<a href="{{link.href}}">{{ link.text }}
|
||||||
|
{% if link.icon %}<span class="pull-right">{% bootstrap_icon link.icon %}</span>{% endif %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}{% endif %}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
{% load bootstrap3 %}{% load staticfiles %}{% load navbar %}
|
||||||
|
<nav class="navbar navbar-inverse navbar-fixed-side" role="navigation" id="menu">
|
||||||
|
<div class="container">
|
||||||
|
<div class="navbar-header">
|
||||||
|
<button class="navbar-toggle" data-target=".navbar-collapse" data-toggle="collapse">
|
||||||
|
<span class="sr-only">Toggle navigation</span>
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
<span class="icon-bar"></span>
|
||||||
|
</button>
|
||||||
|
<a class="navbar-brand" href="{% url 'index' %}">La Maraude ALSA</a>
|
||||||
|
</div>
|
||||||
|
<div class="collapse navbar-collapse">
|
||||||
|
<ul class="nav navbar-nav">{% for app in apps %}
|
||||||
|
{% navbar_menu app %}
|
||||||
|
{% endfor %}</ul>
|
||||||
|
<ul class="nav navbar-nav navbar-right">
|
||||||
|
<li class="dropdown">
|
||||||
|
<a id="UserMenu" href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
|
||||||
|
<strong style="color:#fff;">{% bootstrap_icon "user" %} · {{user}} </strong>· {{ user_group }}<span class="caret"></span></a>
|
||||||
|
<ul class="dropdown-menu" aria-labelledby="UserMenu">
|
||||||
|
{% if next %}
|
||||||
|
<p class="well-sm text-center"><strong style="color:#980300;">Vous n'avez pas l'autorisation<br/> d'accéder à cette page.</strong></p>
|
||||||
|
{%endif%}
|
||||||
|
{% if user.is_authenticated %}
|
||||||
|
{% if user.is_superuser %}<li><a href="{% url 'admin:index' %}">{% bootstrap_icon "wrench" %} Administration</a></li>{% endif %}
|
||||||
|
<li><a href="{% url 'logout' %}">{% bootstrap_icon "log-out" %} Déconnecter</a></li>
|
||||||
|
{% else %}
|
||||||
|
<li>
|
||||||
|
<form class="navbar-form navbar-left" method="post" action="/login/">{% csrf_token %}
|
||||||
|
{% if next %}<input name="next" value="{{next}}" hidden />{% endif %}
|
||||||
|
<div class="form-group form-horizontal">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-2 sr-only control-label" for="id_username">Username</label>
|
||||||
|
<div class="col-md-10">
|
||||||
|
<input autofocus="" class="form-control" id="id_username" maxlength="254" name="username" placeholder="Username" title="" type="text" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-2 sr-only control-label" for="id_password">Password</label>
|
||||||
|
<div class="col-md-10"><input class="form-control" id="id_password" name="password" placeholder="Password" title="" type="password" required />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-center"><button class="btn btn-primary navbar-button" type="submit">Connexion</button></div>
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
from django import template
|
||||||
|
from django.utils.html import format_html
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
@register.filter
|
||||||
|
def as_icon(value):
|
||||||
|
icons = {True: "ok",
|
||||||
|
False: "remove",
|
||||||
|
None: "asterisk"
|
||||||
|
}
|
||||||
|
if not value in icons:
|
||||||
|
raise ValueError(value, 'is not a boolean or empty value !')
|
||||||
|
else:
|
||||||
|
return format_html('<span class="glyphicon glyphicon-{}"></span>', icons[value])
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
#-*- coding:utf-8 -*-
|
||||||
|
|
||||||
|
from django import template
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
|
||||||
|
|
||||||
|
class NavbarNode(template.Node):
|
||||||
|
|
||||||
|
_apps = None
|
||||||
|
|
||||||
|
def get_menus(self, view, user):
|
||||||
|
if not self._apps:
|
||||||
|
from website.navbar import registered
|
||||||
|
if not registered:
|
||||||
|
print('WARNING: No app registered into "navbar" module')
|
||||||
|
self._apps = registered.copy()
|
||||||
|
return [app_menu(view, user) for app_menu in self._apps]
|
||||||
|
|
||||||
|
def get_template(self):
|
||||||
|
return template.loader.get_template('navbar/layout.html')
|
||||||
|
|
||||||
|
def render(self, context):
|
||||||
|
request = context.get('request')
|
||||||
|
user, view = context.get('user'), context.get('view')
|
||||||
|
apps = self.get_menus(view, user)
|
||||||
|
# Add user menu
|
||||||
|
context = template.Context({
|
||||||
|
'apps': apps,
|
||||||
|
'user': user,
|
||||||
|
'user_group': context.get('user_group', None),
|
||||||
|
'next': context.get('next', None),
|
||||||
|
|
||||||
|
})
|
||||||
|
return self.get_template().render(context, request)
|
||||||
|
|
||||||
|
@register.tag
|
||||||
|
def navbar(parser, token):
|
||||||
|
return NavbarNode()
|
||||||
|
|
||||||
|
@register.inclusion_tag("navbar/app-menu.html")
|
||||||
|
def navbar_menu(app_menu):
|
||||||
|
return {
|
||||||
|
'active': app_menu.is_active,
|
||||||
|
'header': app_menu.header,
|
||||||
|
'links': app_menu.links,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -6,6 +6,7 @@ from .views import Index, login_view
|
|||||||
from maraudes import urls as maraudes_urls
|
from maraudes import urls as maraudes_urls
|
||||||
from suivi import urls as suivi_urls
|
from suivi import urls as suivi_urls
|
||||||
from sujets import urls as sujets_urls
|
from sujets import urls as sujets_urls
|
||||||
|
from utilisateurs import urls as utilisateurs_urls
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
# Authentification
|
# Authentification
|
||||||
@@ -19,4 +20,5 @@ urlpatterns = [
|
|||||||
url(r'^maraudes/', include(maraudes_urls, namespace="maraudes")),
|
url(r'^maraudes/', include(maraudes_urls, namespace="maraudes")),
|
||||||
url(r'^suivi/', include(suivi_urls, namespace="suivi")),
|
url(r'^suivi/', include(suivi_urls, namespace="suivi")),
|
||||||
url(r'^sujets/', include(sujets_urls, namespace="sujets")),
|
url(r'^sujets/', include(sujets_urls, namespace="sujets")),
|
||||||
|
url(r'^utilisateurs/', include(utilisateurs_urls, namespace="utilisateurs")),
|
||||||
]
|
]
|
||||||
+1
-1
@@ -10,7 +10,7 @@ class Index(WebsiteTemplateMixin, views.generic.TemplateView):
|
|||||||
|
|
||||||
template_name = "main.html"
|
template_name = "main.html"
|
||||||
app_menu = None
|
app_menu = None
|
||||||
|
header = ('La Maraude ALSA', 'accueil')
|
||||||
class PageInfo:
|
class PageInfo:
|
||||||
title = "La maraude ALSA"
|
title = "La maraude ALSA"
|
||||||
header = "La Maraude ALSA"
|
header = "La Maraude ALSA"
|
||||||
|
|||||||
Reference in new issue
Block a user