Add a screen in the admin to launch commands. Fix bug #4 (https://github.com/IRI-Research/plan4learning/issues/4).
--- a/doc/administration.rst Tue Oct 08 17:25:39 2013 +0200
+++ b/doc/administration.rst Fri Oct 11 11:46:18 2013 +0200
@@ -27,7 +27,7 @@
Commande d'administration
=========================
-Voici une liste des commandes ser vant à gérer l'import et l'export des notices (``Record``).
+Voici une liste des commandes servant à gérer l'import et l'export des notices (``Record``).
.. _admin-import-record:
@@ -242,13 +242,16 @@
Cette commande est fournie par le module Django ``Haystack``. Sa documentation se trouve à l'adresse suivante : http://django-haystack.readthedocs.org/en/v2.1.0/management_commands.html
-console d'administration / gestion des utilisateurs
-===================================================
+console d'administration
+========================
Le back-office offre une console d'administration donnant accès en particulier à la gestion des utilisateurs.
On y accède par le lien ``admin`` dans l'en-tête des pages si on est connecté en tant qu'administrateur ou bien en allant directement à l'adresse ``<racine du site>/p4l/admin/``.
+gestion des utilisateurs
+------------------------
+
L'administration des utilisateurs se fait à l'adresse suivante : ``<racine du site>/p4l/admin/p4l/user/``.
L'administration des groupes d'utilisateurs se fait à l'adresse suivante: ``<racine du site>/p4l/admin/auth/group/``.
@@ -263,4 +266,45 @@
Pour faciliter la gestion de ces permissions, le plus simple est de créer un groupe ``utilisateurs``. On affectera à ce groupe toutes les permissions sur les objects de l'application ``p4l``.
il suffira ensuite de mettre les utilisateurs dans ce groupe (champ ``Groupes`` dans l'interface d'édition des utilisateurs). L'utilisateur héritera alors des parmissions du groupe.
-
+
+
+Lancement d'un script
+---------------------
+Il est possible de lancer un script à partir de l'adresse suivante : ``<racine du site>/p4l/admin/confirm_script``.
+
+Le script qui est exécuté est configuré par la propriété ``ADMIN_SCRIPT`` dans la configuration de l'application (``src/p4l/config.py``).
+Cette propriété est un dictionnaire dont les clés sont les arguments du constructeur de subprocess.Popen.
+Tous les arguments et le fonctionnement de cet objet sont détaillés à l'adresse suivante : http://docs.python.org/2/library/subprocess.html#popen-constructor
+Tous les arguments sont configurables sauf les suivants : ``stdout``, ``stderr``, ``bufsize``, ``close_fds``, ``preexec_fn``.
+Cependant les quatres suivants seront les plus utiles:
+ * `args`: soit une séquence d'arguments de programme, soit une chaine de caractères
+ * `cwd`: le chemin du reepertoire de travail. Par défaut : ``None``
+ * `env`: dictionnire donnant les variables d'evironement positinnées durant l'éxeecution du script.
+
+Il est recommandé que ``args`` soit une liste d'arguments et non une simple chaîne de caractères.
+
+L'example suivant démontre comment on peut configurer cette propriété pour lancer le dump des notices avec la commande ``dump_record``.
+
+.. code-block :: python
+
+ADMIN_SCRIPT = {
+ 'args' : [ sys.executable, "manage.py", "dump_record", "--newline", "-j", "/tmp/script_dump.rdf.bz2"],
+ 'cwd' : "<chemin absolu des sources l'application>/src",
+ 'env' : {'PYTHONPATH': '<chemin absolu de l'environement virtuel>/lib/python2.7/site-packages'}
+}
+
+
+Plusieurs points sont à noter:
+
+ * L'utilisation de cette fonctionnalité est à priori réservé pour une application installé sous Unix. (cela peut fonctionner sous Windows, mais cela n'a pas été testé)
+ * La fermeture de la fenêtre du navigateur ne stoppe pas la commande
+ * En particulier si la session de l'utilisateur expire ou bien que la fenêtre du browser est fermée, il n'y a plus possibilité de stopper le processus à partir d'un browser.
+ Le processus devra être interompu par les moyens habituels directement sur le serveur
+ * La commande est lancée dans le contexte du serveur web. Elle est donc executé par l'utilisateur du serveur web et hérite de ces droits d'accès.
+ * Tout démarrage du serveur web stoppe la commande.
+ * La commande partage les ressources du serveurs web. Attention donc à ne pas lancer des commandes trop gourmandes en ressources, cela peut avoir des conséquences sur la stabilité du serveur web et sa disponibilité.
+ * L'affichage de la sortie de la commande dans le browser se fait ligne par ligne.
+ Si la sortie de la commande ne comporte pas de caractère de retour à la ligne (``"\n"``) rien ne s'affichera avant la fin de la commande.
+ * Les sorties erreur et standard sont affichée ensemble sans différentiation.
+
+
\ No newline at end of file
--- a/src/p4l/admin.py Tue Oct 08 17:25:39 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,48 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright IRI (c) 2013
-#
-# contact@iri.centrepompidou.fr
-#
-# This software is governed by the CeCILL-B license under French law and
-# abiding by the rules of distribution of free software. You can use,
-# modify and/ or redistribute the software under the terms of the CeCILL-B
-# license as circulated by CEA, CNRS and INRIA at the following URL
-# "http://www.cecill.info".
-#
-# As a counterpart to the access to the source code and rights to copy,
-# modify and redistribute granted by the license, users are provided only
-# with a limited warranty and the software's author, the holder of the
-# economic rights, and the successive licensors have only limited
-# liability.
-#
-# In this respect, the user's attention is drawn to the risks associated
-# with loading, using, modifying and/or developing or reproducing the
-# software by the user in light of its specific status of free software,
-# that may mean that it is complicated to manipulate, and that also
-# therefore means that it is reserved for developers and experienced
-# professionals having in-depth computer knowledge. Users are therefore
-# encouraged to load and test the software's suitability as regards their
-# requirements in conditions enabling the security of their systems and/or
-# data to be ensured and, more generally, to use and operate it in the
-# same conditions as regards security.
-#
-# The fact that you are presently reading this means that you have had
-# knowledge of the CeCILL-B license and that you accept its terms.
-#
-
-from django.contrib import admin
-from django.contrib.auth.admin import UserAdmin as AuthUserAdmin
-from django.utils.translation import gettext_lazy as _
-from p4l.models import User
-from p4l.forms import UserChangeForm, UserCreationform
-
-class UserAdmin(AuthUserAdmin):
- form = UserChangeForm
- add_form = UserCreationform
- fieldsets = tuple(list(AuthUserAdmin.fieldsets) + [(_('language'), {'fields':('language',)})])
-
-
-admin.site.register(User, UserAdmin)
-
-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/p4l/admin/__init__.py Fri Oct 11 11:46:18 2013 +0200
@@ -0,0 +1,46 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright IRI (c) 2013
+#
+# contact@iri.centrepompidou.fr
+#
+# This software is governed by the CeCILL-B license under French law and
+# abiding by the rules of distribution of free software. You can use,
+# modify and/ or redistribute the software under the terms of the CeCILL-B
+# license as circulated by CEA, CNRS and INRIA at the following URL
+# "http://www.cecill.info".
+#
+# As a counterpart to the access to the source code and rights to copy,
+# modify and redistribute granted by the license, users are provided only
+# with a limited warranty and the software's author, the holder of the
+# economic rights, and the successive licensors have only limited
+# liability.
+#
+# In this respect, the user's attention is drawn to the risks associated
+# with loading, using, modifying and/or developing or reproducing the
+# software by the user in light of its specific status of free software,
+# that may mean that it is complicated to manipulate, and that also
+# therefore means that it is reserved for developers and experienced
+# professionals having in-depth computer knowledge. Users are therefore
+# encouraged to load and test the software's suitability as regards their
+# requirements in conditions enabling the security of their systems and/or
+# data to be ensured and, more generally, to use and operate it in the
+# same conditions as regards security.
+#
+# The fact that you are presently reading this means that you have had
+# knowledge of the CeCILL-B license and that you accept its terms.
+#
+
+__all__ = ['site']
+
+from django.contrib.auth.admin import GroupAdmin
+from django.contrib.auth.models import Group
+
+from p4l.admin.components import UserAdmin
+from p4l.admin.sites import site
+from p4l.models import User
+
+
+site.register(User, UserAdmin)
+site.register(Group, GroupAdmin)
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/p4l/admin/components.py Fri Oct 11 11:46:18 2013 +0200
@@ -0,0 +1,50 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright IRI (c) 2013
+#
+# contact@iri.centrepompidou.fr
+#
+# This software is governed by the CeCILL-B license under French law and
+# abiding by the rules of distribution of free software. You can use,
+# modify and/ or redistribute the software under the terms of the CeCILL-B
+# license as circulated by CEA, CNRS and INRIA at the following URL
+# "http://www.cecill.info".
+#
+# As a counterpart to the access to the source code and rights to copy,
+# modify and redistribute granted by the license, users are provided only
+# with a limited warranty and the software's author, the holder of the
+# economic rights, and the successive licensors have only limited
+# liability.
+#
+# In this respect, the user's attention is drawn to the risks associated
+# with loading, using, modifying and/or developing or reproducing the
+# software by the user in light of its specific status of free software,
+# that may mean that it is complicated to manipulate, and that also
+# therefore means that it is reserved for developers and experienced
+# professionals having in-depth computer knowledge. Users are therefore
+# encouraged to load and test the software's suitability as regards their
+# requirements in conditions enabling the security of their systems and/or
+# data to be ensured and, more generally, to use and operate it in the
+# same conditions as regards security.
+#
+# The fact that you are presently reading this means that you have had
+# knowledge of the CeCILL-B license and that you accept its terms.
+#
+
+
+'''
+Created on Oct 9, 2013
+
+@author: ymh
+'''
+
+from django.contrib.auth.admin import UserAdmin as AuthUserAdmin
+from django.utils.translation import gettext_lazy as _
+
+from p4l.admin.forms import UserChangeForm, UserCreationform
+
+
+class UserAdmin(AuthUserAdmin):
+ form = UserChangeForm
+ add_form = UserCreationform
+ fieldsets = tuple(list(AuthUserAdmin.fieldsets) + [(_('language'), {'fields':('language',)})])
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/p4l/admin/forms.py Fri Oct 11 11:46:18 2013 +0200
@@ -0,0 +1,64 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright IRI (c) 2013
+#
+# contact@iri.centrepompidou.fr
+#
+# This software is governed by the CeCILL-B license under French law and
+# abiding by the rules of distribution of free software. You can use,
+# modify and/ or redistribute the software under the terms of the CeCILL-B
+# license as circulated by CEA, CNRS and INRIA at the following URL
+# "http://www.cecill.info".
+#
+# As a counterpart to the access to the source code and rights to copy,
+# modify and redistribute granted by the license, users are provided only
+# with a limited warranty and the software's author, the holder of the
+# economic rights, and the successive licensors have only limited
+# liability.
+#
+# In this respect, the user's attention is drawn to the risks associated
+# with loading, using, modifying and/or developing or reproducing the
+# software by the user in light of its specific status of free software,
+# that may mean that it is complicated to manipulate, and that also
+# therefore means that it is reserved for developers and experienced
+# professionals having in-depth computer knowledge. Users are therefore
+# encouraged to load and test the software's suitability as regards their
+# requirements in conditions enabling the security of their systems and/or
+# data to be ensured and, more generally, to use and operate it in the
+# same conditions as regards security.
+#
+# The fact that you are presently reading this means that you have had
+# knowledge of the CeCILL-B license and that you accept its terms.
+#
+
+
+from django.conf import settings
+from django.contrib.auth import get_user_model
+from django.contrib.auth.forms import (UserChangeForm as AuthUserChangeForm,
+ UserCreationForm as AuthUserCreationForm)
+from django.core.exceptions import ValidationError
+from django.forms.fields import ChoiceField
+from django.utils.translation import ugettext as _
+
+
+User = get_user_model()
+
+class UserCreationform(AuthUserCreationForm):
+ class Meta:
+ model = User
+
+ def clean_username(self):
+ # Since User.username is unique, this check is redundant,
+ # but it sets a nicer error message than the ORM. See #13147.
+ username = self.cleaned_data["username"]
+ try:
+ User.objects.get(username=username)
+ except User.DoesNotExist:
+ return username
+ raise ValidationError(self.error_messages['duplicate_username'])
+
+
+class UserChangeForm(AuthUserChangeForm):
+ language = ChoiceField(label=_("language"), choices=[(k,_(v)) for k,v in settings.LANGUAGES], initial=settings.LANGUAGE_CODE[:2])
+ class Meta:
+ model = User
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/p4l/admin/sites.py Fri Oct 11 11:46:18 2013 +0200
@@ -0,0 +1,65 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright IRI (c) 2013
+#
+# contact@iri.centrepompidou.fr
+#
+# This software is governed by the CeCILL-B license under French law and
+# abiding by the rules of distribution of free software. You can use,
+# modify and/ or redistribute the software under the terms of the CeCILL-B
+# license as circulated by CEA, CNRS and INRIA at the following URL
+# "http://www.cecill.info".
+#
+# As a counterpart to the access to the source code and rights to copy,
+# modify and redistribute granted by the license, users are provided only
+# with a limited warranty and the software's author, the holder of the
+# economic rights, and the successive licensors have only limited
+# liability.
+#
+# In this respect, the user's attention is drawn to the risks associated
+# with loading, using, modifying and/or developing or reproducing the
+# software by the user in light of its specific status of free software,
+# that may mean that it is complicated to manipulate, and that also
+# therefore means that it is reserved for developers and experienced
+# professionals having in-depth computer knowledge. Users are therefore
+# encouraged to load and test the software's suitability as regards their
+# requirements in conditions enabling the security of their systems and/or
+# data to be ensured and, more generally, to use and operate it in the
+# same conditions as regards security.
+#
+# The fact that you are presently reading this means that you have had
+# knowledge of the CeCILL-B license and that you accept its terms.
+#
+
+
+'''
+Created on Oct 9, 2013
+
+@author: ymh
+'''
+
+from django.conf.urls import patterns, url
+from django.contrib.admin import AdminSite as DjangoAdminSite
+
+from p4l.admin.views import RunScriptView, ConfirmScriptView, KillScriptView
+from p4l.decorators import is_staff
+
+
+class AdminSite(DjangoAdminSite):
+
+ login_template = "registration/login.html"
+
+
+ def get_urls(self):
+ urlpatterns = DjangoAdminSite.get_urls(self)
+
+ urlpatterns += patterns('',
+ url(r'^confirm_script$', is_staff(ConfirmScriptView.as_view()), name='confirm_script'),
+ url(r'^run_script$', is_staff(RunScriptView.as_view()), name='run_script'),
+ url(r'^kill_script$', is_staff(KillScriptView.as_view()), name='kill_script')
+ )
+
+ return urlpatterns
+
+site = AdminSite()
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/p4l/admin/views.py Fri Oct 11 11:46:18 2013 +0200
@@ -0,0 +1,156 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright IRI (c) 2013
+#
+# contact@iri.centrepompidou.fr
+#
+# This software is governed by the CeCILL-B license under French law and
+# abiding by the rules of distribution of free software. You can use,
+# modify and/ or redistribute the software under the terms of the CeCILL-B
+# license as circulated by CEA, CNRS and INRIA at the following URL
+# "http://www.cecill.info".
+#
+# As a counterpart to the access to the source code and rights to copy,
+# modify and redistribute granted by the license, users are provided only
+# with a limited warranty and the software's author, the holder of the
+# economic rights, and the successive licensors have only limited
+# liability.
+#
+# In this respect, the user's attention is drawn to the risks associated
+# with loading, using, modifying and/or developing or reproducing the
+# software by the user in light of its specific status of free software,
+# that may mean that it is complicated to manipulate, and that also
+# therefore means that it is reserved for developers and experienced
+# professionals having in-depth computer knowledge. Users are therefore
+# encouraged to load and test the software's suitability as regards their
+# requirements in conditions enabling the security of their systems and/or
+# data to be ensured and, more generally, to use and operate it in the
+# same conditions as regards security.
+#
+# The fact that you are presently reading this means that you have had
+# knowledge of the CeCILL-B license and that you accept its terms.
+#
+'''
+Created on Oct 9, 2013
+
+@author: ymh
+'''
+
+import itertools
+import os
+import signal
+from subprocess import PIPE, Popen, STDOUT
+import time
+import uuid
+
+from django.conf import settings
+from django.http.response import StreamingHttpResponse, HttpResponse
+from django.utils.translation import ugettext
+from django.views.generic.base import TemplateView, View
+
+
+class ConfirmScriptView(TemplateView):
+ template_name = "p4l/admin/confirm_run_script.html"
+
+ def get_context_data(self, **kwargs):
+ return {
+ 'command_line' : " ".join(getattr(settings,"ADMIN_SCRIPT", {}).get('command',"")),
+ 'env' : repr(getattr(settings,"ADMIN_SCRIPT", {}).get('env',{})),
+ 'cwd' : repr(getattr(settings,"ADMIN_SCRIPT", {}).get('cwd',"")),
+ }
+
+class RunScriptView(View):
+
+ def __init__(self, **kwargs):
+ View.__init__(self, **kwargs)
+ self.boundary = "--BOUNDARY--==--%s" % str(uuid.uuid4())
+
+
+ def get(self, request):
+ resp = StreamingHttpResponse()
+
+ command_kwargs = {
+ 'shell':False,
+ 'env':None,
+ 'cwd':None
+ }
+ admin_script = getattr(settings,"ADMIN_SCRIPT", {})
+ command = admin_script.get('args',"")
+
+ if not command:
+ return resp
+
+ command_kwargs.update(admin_script,
+ stdout=PIPE,
+ stderr=STDOUT,
+ bufsize=0,
+ close_fds=True,
+ preexec_fn=os.setsid
+ )
+
+ resp['Connection'] = "Keep-Alive"
+ doc_start = [
+ '<!DOCTYPE html>',
+ '<html lang="en">',
+ '<head>',
+ '<meta charset="utf-8">',
+ '<title>output</title>',
+ '<style>body {font-family: monospace; white-space: pre;}</style>',
+ '</head>',
+ '<body>',
+ ]
+
+ doc_end = [
+ '<script>parent.done();</script>',
+ '</html>',
+ '</body>'
+ ]
+
+ scroll_to_bottom = '<script type="text/javascript">window.scrollBy(0,50);</script>'
+
+ process = Popen(**command_kwargs)
+
+ # Save the pid in the user's session (a thread-safe place)
+ request.session['pid'] = process.pid
+
+ def read_output():
+ for line in iter(process.stdout.readline, b''):
+ yield "%s%s" %(line, scroll_to_bottom)
+
+ resp.streaming_content = itertools.chain(doc_start, read_output(), doc_end)
+
+ return resp
+
+
+def check_pid(pid):
+ """ Check For the existence of a unix pid. """
+ try:
+ os.kill(pid, 0)
+ except OSError:
+ return False
+ else:
+ return True
+
+class KillScriptView(View):
+
+ def get(self, request):
+
+ resp = HttpResponse()
+
+ pid = request.session.get('pid', None)
+
+ if not pid:
+ resp.content = ugettext("No active process to kill")
+ else:
+ os.kill(pid, signal.SIGINT)
+ i = 0
+ while i <= settings.SCRIPT_MAX_WAIT and check_pid(pid):
+ time.sleep(settings.SCRIPT_WAIT)
+ i += 1
+
+ if check_pid(pid):
+ os.killpg(pid, signal.SIGKILL)
+
+ resp.content = ugettext("Success: The process was killed successfully.")
+
+ return resp
--- a/src/p4l/config.py.tmpl Tue Oct 08 17:25:39 2013 +0200
+++ b/src/p4l/config.py.tmpl Fri Oct 11 11:46:18 2013 +0200
@@ -165,3 +165,5 @@
# url of the sesame repository containing all the rdf referentials
SPARQL_QUERY_ENDPOINT = "http://localhost:8080/openrdf-sesame/repositories/plan4learning"
+# cf http://docs.python.org/2/library/subprocess.html#popen-constructor
+ADMIN_COMMAND_SCRIPT = {}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/p4l/decorators.py Fri Oct 11 11:46:18 2013 +0200
@@ -0,0 +1,55 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright IRI (c) 2013
+#
+# contact@iri.centrepompidou.fr
+#
+# This software is governed by the CeCILL-B license under French law and
+# abiding by the rules of distribution of free software. You can use,
+# modify and/ or redistribute the software under the terms of the CeCILL-B
+# license as circulated by CEA, CNRS and INRIA at the following URL
+# "http://www.cecill.info".
+#
+# As a counterpart to the access to the source code and rights to copy,
+# modify and redistribute granted by the license, users are provided only
+# with a limited warranty and the software's author, the holder of the
+# economic rights, and the successive licensors have only limited
+# liability.
+#
+# In this respect, the user's attention is drawn to the risks associated
+# with loading, using, modifying and/or developing or reproducing the
+# software by the user in light of its specific status of free software,
+# that may mean that it is complicated to manipulate, and that also
+# therefore means that it is reserved for developers and experienced
+# professionals having in-depth computer knowledge. Users are therefore
+# encouraged to load and test the software's suitability as regards their
+# requirements in conditions enabling the security of their systems and/or
+# data to be ensured and, more generally, to use and operate it in the
+# same conditions as regards security.
+#
+# The fact that you are presently reading this means that you have had
+# knowledge of the CeCILL-B license and that you accept its terms.
+#
+from django.contrib.auth import REDIRECT_FIELD_NAME
+
+'''
+Created on Oct 10, 2013
+
+@author: ymh
+'''
+from django.contrib.auth.decorators import user_passes_test
+
+
+def is_staff(function=None, redirect_field_name=REDIRECT_FIELD_NAME, login_url=None):
+ """
+ Decorator for views that checks that the user is logged in, redirecting
+ to the log-in page if necessary.
+ """
+ actual_decorator = user_passes_test(
+ lambda u: u.is_staff,
+ login_url=login_url,
+ redirect_field_name=redirect_field_name
+ )
+ if function:
+ return actual_decorator(function)
+ return actual_decorator
--- a/src/p4l/forms.py Tue Oct 08 17:25:39 2013 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,65 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright IRI (c) 2013
-#
-# contact@iri.centrepompidou.fr
-#
-# This software is governed by the CeCILL-B license under French law and
-# abiding by the rules of distribution of free software. You can use,
-# modify and/ or redistribute the software under the terms of the CeCILL-B
-# license as circulated by CEA, CNRS and INRIA at the following URL
-# "http://www.cecill.info".
-#
-# As a counterpart to the access to the source code and rights to copy,
-# modify and redistribute granted by the license, users are provided only
-# with a limited warranty and the software's author, the holder of the
-# economic rights, and the successive licensors have only limited
-# liability.
-#
-# In this respect, the user's attention is drawn to the risks associated
-# with loading, using, modifying and/or developing or reproducing the
-# software by the user in light of its specific status of free software,
-# that may mean that it is complicated to manipulate, and that also
-# therefore means that it is reserved for developers and experienced
-# professionals having in-depth computer knowledge. Users are therefore
-# encouraged to load and test the software's suitability as regards their
-# requirements in conditions enabling the security of their systems and/or
-# data to be ensured and, more generally, to use and operate it in the
-# same conditions as regards security.
-#
-# The fact that you are presently reading this means that you have had
-# knowledge of the CeCILL-B license and that you accept its terms.
-#
-
-
-from django.conf import settings
-from django.contrib.auth import get_user_model
-from django.contrib.auth.forms import (UserChangeForm as AuthUserChangeForm,
- UserCreationForm as AuthUserCreationForm)
-from django.core.exceptions import ValidationError
-from django.forms.fields import ChoiceField
-from django.utils.translation import ugettext as _
-
-
-User = get_user_model()
-
-class UserCreationform(AuthUserCreationForm):
- class Meta:
- model = User
-
- def clean_username(self):
- # Since User.username is unique, this check is redundant,
- # but it sets a nicer error message than the ORM. See #13147.
- username = self.cleaned_data["username"]
- try:
- User.objects.get(username=username)
- except User.DoesNotExist:
- return username
- raise ValidationError(self.error_messages['duplicate_username'])
-
-
-class UserChangeForm(AuthUserChangeForm):
- language = ChoiceField(label=_("language"), choices=[(k,_(v)) for k,v in settings.LANGUAGES], initial=settings.LANGUAGE_CODE[:2])
- class Meta:
- model = User
-
Binary file src/p4l/locale/en/LC_MESSAGES/django.mo has changed
--- a/src/p4l/locale/en/LC_MESSAGES/django.po Tue Oct 08 17:25:39 2013 +0200
+++ b/src/p4l/locale/en/LC_MESSAGES/django.po Fri Oct 11 11:46:18 2013 +0200
@@ -7,7 +7,7 @@
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-09-24 06:09-0500\n"
+"POT-Creation-Date: 2013-10-11 10:22+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -16,27 +16,35 @@
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: admin.py:10 forms.py:34 templates/p4l/record_view.html:56
-msgid "language"
-msgstr "language"
-
-#: settings.py:42
+#: settings.py:76
msgid "French"
msgstr "French"
-#: settings.py:43
+#: settings.py:77
msgid "English"
msgstr "English"
-#: settings.py:44
+#: settings.py:78
msgid "Spanish"
msgstr "Spanish"
-#: semantictree/forms/widgets.py:39
+#: admin/components.py:50 admin/forms.py:62 templates/p4l/record_view.html:56
+msgid "language"
+msgstr "language"
+
+#: admin/views.py:143
+msgid "No active process to kill"
+msgstr "No active process to kill"
+
+#: admin/views.py:154
+msgid "Success: The process was killed successfully."
+msgstr "Success: The process was killed successfully."
+
+#: semantictree/forms/widgets.py:68
msgid "Search"
msgstr "Search"
-#: semantictree/forms/widgets.py:43 semantictree/forms/widgets.py:80
+#: semantictree/forms/widgets.py:72 semantictree/forms/widgets.py:109
msgid ""
"\"data-url\", \"data-query\", \"data-root-query\", \"data-childs-query\" and "
"\"data-child-count-query\" must be set in CharField configuration"
@@ -44,69 +52,91 @@
"\"data-url\", \"data-query\", \"data-root-query\", \"data-childs-query\" and "
"\"data-child-count-query\" must be set in CharField configuration"
-#: semantictree/forms/widgets.py:44 semantictree/forms/widgets.py:81
+#: semantictree/forms/widgets.py:73 semantictree/forms/widgets.py:110
msgid "Browse"
msgstr "Browse"
-#: semantictree/forms/widgets.py:49 semantictree/forms/widgets.py:86
+#: semantictree/forms/widgets.py:78 semantictree/forms/widgets.py:115
msgid "or"
msgstr "or"
-#: templates/p4l/base.html:35
+#: templates/admin/app_index.html:7
+#: templates/p4l/admin/confirm_run_script.html:13
+msgid "Home"
+msgstr "Home"
+
+#: templates/admin/app_index.html:10
+#, python-format
+msgid "%(name)s"
+msgstr "%(name)s"
+
+#: templates/admin/index.html:10
+msgid "Commands"
+msgstr "Commands"
+
+#: templates/admin/index.html:12
+msgid "Launch Script"
+msgstr "Launch Script"
+
+#: templates/admin/index.html:13
+msgid "Launch"
+msgstr "Launch"
+
+#: templates/p4l/base.html:36
msgid "logout"
msgstr "logout"
-#: templates/p4l/base.html:37 templates/registration/login.html:4
+#: templates/p4l/base.html:38 templates/registration/login.html:4
#: templates/registration/login.html:27
msgid "login"
msgstr "login"
-#: templates/p4l/home.html:6 templates/p4l/home.html.py:10
+#: templates/p4l/home.html:6 templates/p4l/home.html.py:15
msgid "Record List"
msgstr "Record list"
-#: templates/p4l/home.html:6 templates/p4l/home.html.py:30
-#: templates/p4l/home.html:70
+#: templates/p4l/home.html:6 templates/p4l/home.html.py:35
+#: templates/p4l/home.html:75
msgid "Page"
msgstr "Page"
-#: templates/p4l/home.html:14
+#: templates/p4l/home.html:19
msgid "Query"
msgstr "Query"
-#: templates/p4l/home.html:19
+#: templates/p4l/home.html:24
msgid "New record"
msgstr "New record"
-#: templates/p4l/home.html:27 templates/p4l/home.html.py:67
+#: templates/p4l/home.html:32 templates/p4l/home.html.py:72
msgid "Previous"
msgstr "Previous"
-#: templates/p4l/home.html:30 templates/p4l/home.html.py:70
+#: templates/p4l/home.html:35 templates/p4l/home.html.py:75
msgid "on"
msgstr "on"
-#: templates/p4l/home.html:33 templates/p4l/home.html.py:73
+#: templates/p4l/home.html:38 templates/p4l/home.html.py:78
msgid "Next"
msgstr "Next"
-#: templates/p4l/home.html:43 templates/p4l/record_view.html:36
+#: templates/p4l/home.html:48 templates/p4l/record_view.html:36
msgid "identifier"
msgstr "identifier"
-#: templates/p4l/home.html:43 templates/p4l/record_view.html:64
+#: templates/p4l/home.html:48 templates/p4l/record_view.html:64
msgid "titles"
msgstr "titles"
-#: templates/p4l/home.html:43
+#: templates/p4l/home.html:48
msgid "dates"
msgstr "dates"
-#: templates/p4l/home.html:43
+#: templates/p4l/home.html:48
msgid "actions"
msgstr "actions"
-#: templates/p4l/home.html:59
+#: templates/p4l/home.html:64
msgid "No record"
msgstr "No record"
@@ -118,30 +148,30 @@
msgid "View"
msgstr "View"
-#: templates/p4l/record_update_form.html:52 templates/p4l/translations.html:4
+#: templates/p4l/record_update_form.html:55 templates/p4l/translations.html:4
msgid "Save"
msgstr "Save"
-#: templates/p4l/record_update_form.html:56
+#: templates/p4l/record_update_form.html:59
msgid "Cancel"
msgstr "Cancel"
-#: templates/p4l/record_update_form.html:60
-#: templates/p4l/record_update_form.html:248
+#: templates/p4l/record_update_form.html:63
+#: templates/p4l/record_update_form.html:251
#: templates/p4l/translations.html:20
msgid "View the record"
msgstr "View the record"
-#: templates/p4l/record_update_form.html:61
-#: templates/p4l/record_update_form.html:249 templates/p4l/record_view.html:12
+#: templates/p4l/record_update_form.html:64
+#: templates/p4l/record_update_form.html:252 templates/p4l/record_view.html:12
#: templates/p4l/record_view.html.py:238
msgid ""
"Are your sure you want to delete this record ? This action is irreversible."
msgstr ""
"Are your sure you want to delete this record ? This action is irreversible."
-#: templates/p4l/record_update_form.html:61
-#: templates/p4l/record_update_form.html:249 templates/p4l/record_view.html:12
+#: templates/p4l/record_update_form.html:64
+#: templates/p4l/record_update_form.html:252 templates/p4l/record_view.html:12
#: templates/p4l/record_view.html.py:238
msgid "Delete the record"
msgstr "Delete the record"
@@ -393,6 +423,46 @@
msgid "An error occured. Somes datas may be incorrect or incomplete."
msgstr "An error occured. Somes fields may be incorrect or incomplete."
+#: templates/p4l/admin/confirm_run_script.html:14
+msgid "Run script"
+msgstr "Run script"
+
+#: templates/p4l/admin/confirm_run_script.html:20
+msgid "Are you sure ?"
+msgstr "Are you sure ?"
+
+#: templates/p4l/admin/confirm_run_script.html:22
+msgid ""
+"The ADMIN_SCRIPT setting is not correctly configured. Please configure it to "
+"launch commands."
+msgstr ""
+"The ADMIN_SCRIPT setting is not correctly configured. Please configure it to "
+"launch commands."
+
+#: templates/p4l/admin/confirm_run_script.html:24
+msgid "Are you sure you want to run the script"
+msgstr "Are you sure you want to run the script"
+
+#: templates/p4l/admin/confirm_run_script.html:25
+msgid "cwd:"
+msgstr "cwd:"
+
+#: templates/p4l/admin/confirm_run_script.html:25
+msgid "env:"
+msgstr "env:"
+
+#: templates/p4l/admin/confirm_run_script.html:28
+msgid "Yes, I'm sure"
+msgstr "Yes, I'm sure"
+
+#: templates/p4l/admin/confirm_run_script.html:30
+msgid "Click to stop process (sends SIGINT then SIGKILL)"
+msgstr "Click to stop process (sends SIGINT then SIGKILL)"
+
+#: templates/p4l/admin/confirm_run_script.html:31
+msgid "Done"
+msgstr "Done"
+
#: templates/registration/login.html:8
msgid "Sorry, that's not a valid username or password."
msgstr "Sorry, that's not a valid username or password."
Binary file src/p4l/locale/es/LC_MESSAGES/django.mo has changed
--- a/src/p4l/locale/es/LC_MESSAGES/django.po Tue Oct 08 17:25:39 2013 +0200
+++ b/src/p4l/locale/es/LC_MESSAGES/django.po Fri Oct 11 11:46:18 2013 +0200
@@ -7,7 +7,7 @@
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-09-24 06:09-0500\n"
+"POT-Creation-Date: 2013-10-11 10:22+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,27 +17,35 @@
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-#: admin.py:10 forms.py:34 templates/p4l/record_view.html:56
-msgid "language"
-msgstr "language"
-
-#: settings.py:42
+#: settings.py:76
msgid "French"
msgstr "French"
-#: settings.py:43
+#: settings.py:77
msgid "English"
msgstr "English"
-#: settings.py:44
+#: settings.py:78
msgid "Spanish"
msgstr "Spanish"
-#: semantictree/forms/widgets.py:39
+#: admin/components.py:50 admin/forms.py:62 templates/p4l/record_view.html:56
+msgid "language"
+msgstr "language"
+
+#: admin/views.py:143
+msgid "No active process to kill"
+msgstr "No active process to kill"
+
+#: admin/views.py:154
+msgid "Success: The process was killed successfully."
+msgstr "Success: The process was killed successfully."
+
+#: semantictree/forms/widgets.py:68
msgid "Search"
msgstr "Search"
-#: semantictree/forms/widgets.py:43 semantictree/forms/widgets.py:80
+#: semantictree/forms/widgets.py:72 semantictree/forms/widgets.py:109
msgid ""
"\"data-url\", \"data-query\", \"data-root-query\", \"data-childs-query\" and "
"\"data-child-count-query\" must be set in CharField configuration"
@@ -45,69 +53,91 @@
"\"data-url\", \"data-query\", \"data-root-query\", \"data-childs-query\" and "
"\"data-child-count-query\" must be set in CharField configuration"
-#: semantictree/forms/widgets.py:44 semantictree/forms/widgets.py:81
+#: semantictree/forms/widgets.py:73 semantictree/forms/widgets.py:110
msgid "Browse"
msgstr "Browse"
-#: semantictree/forms/widgets.py:49 semantictree/forms/widgets.py:86
+#: semantictree/forms/widgets.py:78 semantictree/forms/widgets.py:115
msgid "or"
msgstr "or"
-#: templates/p4l/base.html:35
+#: templates/admin/app_index.html:7
+#: templates/p4l/admin/confirm_run_script.html:13
+msgid "Home"
+msgstr "Home"
+
+#: templates/admin/app_index.html:10
+#, python-format
+msgid "%(name)s"
+msgstr "%(name)s"
+
+#: templates/admin/index.html:10
+msgid "Commands"
+msgstr "Commands"
+
+#: templates/admin/index.html:12
+msgid "Launch Script"
+msgstr "Launch Script"
+
+#: templates/admin/index.html:13
+msgid "Launch"
+msgstr "Launch"
+
+#: templates/p4l/base.html:36
msgid "logout"
msgstr "logout"
-#: templates/p4l/base.html:37 templates/registration/login.html:4
+#: templates/p4l/base.html:38 templates/registration/login.html:4
#: templates/registration/login.html:27
msgid "login"
msgstr "login"
-#: templates/p4l/home.html:6 templates/p4l/home.html.py:10
+#: templates/p4l/home.html:6 templates/p4l/home.html.py:15
msgid "Record List"
msgstr "Record list"
-#: templates/p4l/home.html:6 templates/p4l/home.html.py:30
-#: templates/p4l/home.html:70
+#: templates/p4l/home.html:6 templates/p4l/home.html.py:35
+#: templates/p4l/home.html:75
msgid "Page"
msgstr "Page"
-#: templates/p4l/home.html:14
+#: templates/p4l/home.html:19
msgid "Query"
msgstr "Query"
-#: templates/p4l/home.html:19
+#: templates/p4l/home.html:24
msgid "New record"
msgstr "New record"
-#: templates/p4l/home.html:27 templates/p4l/home.html.py:67
+#: templates/p4l/home.html:32 templates/p4l/home.html.py:72
msgid "Previous"
msgstr "Previous"
-#: templates/p4l/home.html:30 templates/p4l/home.html.py:70
+#: templates/p4l/home.html:35 templates/p4l/home.html.py:75
msgid "on"
msgstr "on"
-#: templates/p4l/home.html:33 templates/p4l/home.html.py:73
+#: templates/p4l/home.html:38 templates/p4l/home.html.py:78
msgid "Next"
msgstr "Next"
-#: templates/p4l/home.html:43 templates/p4l/record_view.html:36
+#: templates/p4l/home.html:48 templates/p4l/record_view.html:36
msgid "identifier"
msgstr "identifier"
-#: templates/p4l/home.html:43 templates/p4l/record_view.html:64
+#: templates/p4l/home.html:48 templates/p4l/record_view.html:64
msgid "titles"
msgstr "titles"
-#: templates/p4l/home.html:43
+#: templates/p4l/home.html:48
msgid "dates"
msgstr "dates"
-#: templates/p4l/home.html:43
+#: templates/p4l/home.html:48
msgid "actions"
msgstr "actions"
-#: templates/p4l/home.html:59
+#: templates/p4l/home.html:64
msgid "No record"
msgstr "No record"
@@ -119,30 +149,30 @@
msgid "View"
msgstr "View"
-#: templates/p4l/record_update_form.html:52 templates/p4l/translations.html:4
+#: templates/p4l/record_update_form.html:55 templates/p4l/translations.html:4
msgid "Save"
msgstr "Save"
-#: templates/p4l/record_update_form.html:56
+#: templates/p4l/record_update_form.html:59
msgid "Cancel"
msgstr "Cancel"
-#: templates/p4l/record_update_form.html:60
-#: templates/p4l/record_update_form.html:248
+#: templates/p4l/record_update_form.html:63
+#: templates/p4l/record_update_form.html:251
#: templates/p4l/translations.html:20
msgid "View the record"
msgstr "View the record"
-#: templates/p4l/record_update_form.html:61
-#: templates/p4l/record_update_form.html:249 templates/p4l/record_view.html:12
+#: templates/p4l/record_update_form.html:64
+#: templates/p4l/record_update_form.html:252 templates/p4l/record_view.html:12
#: templates/p4l/record_view.html.py:238
msgid ""
"Are your sure you want to delete this record ? This action is irreversible."
msgstr ""
"Are your sure you want to delete this record ? This action is irreversible."
-#: templates/p4l/record_update_form.html:61
-#: templates/p4l/record_update_form.html:249 templates/p4l/record_view.html:12
+#: templates/p4l/record_update_form.html:64
+#: templates/p4l/record_update_form.html:252 templates/p4l/record_view.html:12
#: templates/p4l/record_view.html.py:238
msgid "Delete the record"
msgstr "Delete the record"
@@ -394,6 +424,46 @@
msgid "An error occured. Somes datas may be incorrect or incomplete."
msgstr "An error occured. Somes fields may be incorrect or incomplete."
+#: templates/p4l/admin/confirm_run_script.html:14
+msgid "Run script"
+msgstr "Run script"
+
+#: templates/p4l/admin/confirm_run_script.html:20
+msgid "Are you sure ?"
+msgstr "Are you sure ?"
+
+#: templates/p4l/admin/confirm_run_script.html:22
+msgid ""
+"The ADMIN_SCRIPT setting is not correctly configured. Please configure it to "
+"launch commands."
+msgstr ""
+"The ADMIN_SCRIPT setting is not correctly configured. Please configure it to "
+"launch commands."
+
+#: templates/p4l/admin/confirm_run_script.html:24
+msgid "Are you sure you want to run the script"
+msgstr "Are you sure you want to run the script"
+
+#: templates/p4l/admin/confirm_run_script.html:25
+msgid "cwd:"
+msgstr "cwd:"
+
+#: templates/p4l/admin/confirm_run_script.html:25
+msgid "env:"
+msgstr "env:"
+
+#: templates/p4l/admin/confirm_run_script.html:28
+msgid "Yes, I'm sure"
+msgstr "Yes, I'm sure"
+
+#: templates/p4l/admin/confirm_run_script.html:30
+msgid "Click to stop process (sends SIGINT then SIGKILL)"
+msgstr "Click to stop process (sends SIGINT then SIGKILL)"
+
+#: templates/p4l/admin/confirm_run_script.html:31
+msgid "Done"
+msgstr "Done"
+
#: templates/registration/login.html:8
msgid "Sorry, that's not a valid username or password."
msgstr "Sorry, that's not a valid username or password."
Binary file src/p4l/locale/fr/LC_MESSAGES/django.mo has changed
--- a/src/p4l/locale/fr/LC_MESSAGES/django.po Tue Oct 08 17:25:39 2013 +0200
+++ b/src/p4l/locale/fr/LC_MESSAGES/django.po Fri Oct 11 11:46:18 2013 +0200
@@ -7,7 +7,7 @@
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2013-09-24 06:09-0500\n"
+"POT-Creation-Date: 2013-10-11 10:22+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,27 +17,35 @@
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-#: admin.py:10 forms.py:34 templates/p4l/record_view.html:56
-msgid "language"
-msgstr "langue"
-
-#: settings.py:42
+#: settings.py:76
msgid "French"
msgstr "Français"
-#: settings.py:43
+#: settings.py:77
msgid "English"
msgstr "Anglais"
-#: settings.py:44
+#: settings.py:78
msgid "Spanish"
msgstr "Espagnol"
-#: semantictree/forms/widgets.py:39
+#: admin/components.py:50 admin/forms.py:62 templates/p4l/record_view.html:56
+msgid "language"
+msgstr "langue"
+
+#: admin/views.py:143
+msgid "No active process to kill"
+msgstr "Pas de processus actif à tuer"
+
+#: admin/views.py:154
+msgid "Success: The process was killed successfully."
+msgstr "Réussi : le process a été tué avec succès."
+
+#: semantictree/forms/widgets.py:68
msgid "Search"
msgstr "Chercher"
-#: semantictree/forms/widgets.py:43 semantictree/forms/widgets.py:80
+#: semantictree/forms/widgets.py:72 semantictree/forms/widgets.py:109
msgid ""
"\"data-url\", \"data-query\", \"data-root-query\", \"data-childs-query\" and "
"\"data-child-count-query\" must be set in CharField configuration"
@@ -46,69 +54,91 @@
"\"data-child-count-query\" doivent être renseignés dans la configuration "
"CharField"
-#: semantictree/forms/widgets.py:44 semantictree/forms/widgets.py:81
+#: semantictree/forms/widgets.py:73 semantictree/forms/widgets.py:110
msgid "Browse"
msgstr "Parcourir"
-#: semantictree/forms/widgets.py:49 semantictree/forms/widgets.py:86
+#: semantictree/forms/widgets.py:78 semantictree/forms/widgets.py:115
msgid "or"
msgstr "ou"
-#: templates/p4l/base.html:35
+#: templates/admin/app_index.html:7
+#: templates/p4l/admin/confirm_run_script.html:13
+msgid "Home"
+msgstr "Accueil"
+
+#: templates/admin/app_index.html:10
+#, python-format
+msgid "%(name)s"
+msgstr "%(name)s"
+
+#: templates/admin/index.html:10
+msgid "Commands"
+msgstr "Commandes"
+
+#: templates/admin/index.html:12
+msgid "Launch Script"
+msgstr "Lancer Script"
+
+#: templates/admin/index.html:13
+msgid "Launch"
+msgstr "Lancer"
+
+#: templates/p4l/base.html:36
msgid "logout"
msgstr "deconnection"
-#: templates/p4l/base.html:37 templates/registration/login.html:4
+#: templates/p4l/base.html:38 templates/registration/login.html:4
#: templates/registration/login.html:27
msgid "login"
msgstr "connection"
-#: templates/p4l/home.html:6 templates/p4l/home.html.py:10
+#: templates/p4l/home.html:6 templates/p4l/home.html.py:15
msgid "Record List"
msgstr "Liste notice"
-#: templates/p4l/home.html:6 templates/p4l/home.html.py:30
-#: templates/p4l/home.html:70
+#: templates/p4l/home.html:6 templates/p4l/home.html.py:35
+#: templates/p4l/home.html:75
msgid "Page"
msgstr "Page"
-#: templates/p4l/home.html:14
+#: templates/p4l/home.html:19
msgid "Query"
msgstr "Requête"
-#: templates/p4l/home.html:19
+#: templates/p4l/home.html:24
msgid "New record"
msgstr "Nouvelle notice"
-#: templates/p4l/home.html:27 templates/p4l/home.html.py:67
+#: templates/p4l/home.html:32 templates/p4l/home.html.py:72
msgid "Previous"
msgstr "Préc."
-#: templates/p4l/home.html:30 templates/p4l/home.html.py:70
+#: templates/p4l/home.html:35 templates/p4l/home.html.py:75
msgid "on"
msgstr "sur"
-#: templates/p4l/home.html:33 templates/p4l/home.html.py:73
+#: templates/p4l/home.html:38 templates/p4l/home.html.py:78
msgid "Next"
msgstr "Suiv."
-#: templates/p4l/home.html:43 templates/p4l/record_view.html:36
+#: templates/p4l/home.html:48 templates/p4l/record_view.html:36
msgid "identifier"
msgstr "identifiant"
-#: templates/p4l/home.html:43 templates/p4l/record_view.html:64
+#: templates/p4l/home.html:48 templates/p4l/record_view.html:64
msgid "titles"
msgstr "titres"
-#: templates/p4l/home.html:43
+#: templates/p4l/home.html:48
msgid "dates"
msgstr "dates"
-#: templates/p4l/home.html:43
+#: templates/p4l/home.html:48
msgid "actions"
msgstr "actions"
-#: templates/p4l/home.html:59
+#: templates/p4l/home.html:64
msgid "No record"
msgstr "Pas de notice"
@@ -120,22 +150,22 @@
msgid "View"
msgstr "Vue"
-#: templates/p4l/record_update_form.html:52 templates/p4l/translations.html:4
+#: templates/p4l/record_update_form.html:55 templates/p4l/translations.html:4
msgid "Save"
msgstr "Enregistrer"
-#: templates/p4l/record_update_form.html:56
+#: templates/p4l/record_update_form.html:59
msgid "Cancel"
msgstr "Annuler"
-#: templates/p4l/record_update_form.html:60
-#: templates/p4l/record_update_form.html:248
+#: templates/p4l/record_update_form.html:63
+#: templates/p4l/record_update_form.html:251
#: templates/p4l/translations.html:20
msgid "View the record"
msgstr "Voir la notice"
-#: templates/p4l/record_update_form.html:61
-#: templates/p4l/record_update_form.html:249 templates/p4l/record_view.html:12
+#: templates/p4l/record_update_form.html:64
+#: templates/p4l/record_update_form.html:252 templates/p4l/record_view.html:12
#: templates/p4l/record_view.html.py:238
msgid ""
"Are your sure you want to delete this record ? This action is irreversible."
@@ -143,8 +173,8 @@
"Êtes-vous sûr sur vouloir effacer cette notice ? Cette action est "
"irréversible."
-#: templates/p4l/record_update_form.html:61
-#: templates/p4l/record_update_form.html:249 templates/p4l/record_view.html:12
+#: templates/p4l/record_update_form.html:64
+#: templates/p4l/record_update_form.html:252 templates/p4l/record_view.html:12
#: templates/p4l/record_view.html.py:238
msgid "Delete the record"
msgstr "Effacer la notice"
@@ -397,6 +427,46 @@
msgstr ""
"Une erreur est survenue. Certaines données sont incorrectes ou incomplètes."
+#: templates/p4l/admin/confirm_run_script.html:14
+msgid "Run script"
+msgstr "Lancer script"
+
+#: templates/p4l/admin/confirm_run_script.html:20
+msgid "Are you sure ?"
+msgstr "Êtes-vous sûr ?"
+
+#: templates/p4l/admin/confirm_run_script.html:22
+msgid ""
+"The ADMIN_SCRIPT setting is not correctly configured. Please configure it to "
+"launch commands."
+msgstr ""
+"La propriété ADMIN_SCRIPT n'est pas correctement configuré. Configurez la avant "
+"de lancer la commande."
+
+#: templates/p4l/admin/confirm_run_script.html:24
+msgid "Are you sure you want to run the script"
+msgstr "Êtes-vous sûr que vous voulez bien lancer le script"
+
+#: templates/p4l/admin/confirm_run_script.html:25
+msgid "cwd:"
+msgstr "cwd :"
+
+#: templates/p4l/admin/confirm_run_script.html:25
+msgid "env:"
+msgstr "env :"
+
+#: templates/p4l/admin/confirm_run_script.html:28
+msgid "Yes, I'm sure"
+msgstr "Oui, je suis sûr(e)"
+
+#: templates/p4l/admin/confirm_run_script.html:30
+msgid "Click to stop process (sends SIGINT then SIGKILL)"
+msgstr "Cliquer pour arrêter le processus (envoie SIGINT puis SIGKILL)"
+
+#: templates/p4l/admin/confirm_run_script.html:31
+msgid "Done"
+msgstr "Terminé"
+
#: templates/registration/login.html:8
msgid "Sorry, that's not a valid username or password."
msgstr "Désolé, utilisateur ou mot de passe invalide"
--- a/src/p4l/management/commands/dump_record.py Tue Oct 08 17:25:39 2013 +0200
+++ b/src/p4l/management/commands/dump_record.py Fri Oct 11 11:46:18 2013 +0200
@@ -90,6 +90,12 @@
default=False,
help= 'gzip compress'
),
+ make_option('--newline',
+ dest= 'newline',
+ action='store_true',
+ default=False,
+ help= 'show progress with newlines'
+ ),
)
@@ -120,6 +126,7 @@
limit = options.get("limit", -1)
skip = options.get("skip", 0)
batch = options.get("batch", 100)
+ newline = options.get("newline", False)
qs = Record.objects.all().select_related(*[field.name for field in Record._meta.fields if isinstance(field, ForeignKey)]).prefetch_related(*([field.name for field in Record._meta.many_to_many] + [obj.get_accessor_name() for obj in Record._meta.get_all_related_objects()])).order_by('identifier') # @UndefinedVariable
@@ -157,7 +164,7 @@
writer.characters("\n")
for n in range((total_records/batch)+1):
for i,r in enumerate(qs[n*batch:((n+1)*batch)]):
- progress_writer = show_progress(i+(n*batch)+1, total_records, "Exporting record %s" % r.identifier, 50, progress_writer)
+ progress_writer = show_progress(i+(n*batch)+1, total_records, "Exporting record %s" % r.identifier, 40, writer=progress_writer, newline=newline)
graph = self.get_graph_from_object(r)
do_write = False
for line in graph.serialize(format="pretty-xml", encoding="utf-8").splitlines(True):
--- a/src/p4l/management/commands/import_record.py Tue Oct 08 17:25:39 2013 +0200
+++ b/src/p4l/management/commands/import_record.py Fri Oct 11 11:46:18 2013 +0200
@@ -81,6 +81,12 @@
default=False,
help= 'index while importing'
),
+ make_option('--newline',
+ dest= 'newline',
+ action='store_true',
+ default=False,
+ help= 'show progress with newlines'
+ ),
)
def __init__(self, *args, **kwargs):
@@ -115,7 +121,7 @@
for _,elem in context:
if elem.tag == "{%s}Record" % IIEP:
i += 1
- writer = show_progress(i, total_records, "Processing record nb %d " % i, 50, writer=writer)
+ writer = show_progress(i, total_records, "Processing record nb %d " % i, 40, writer=writer, newline=self.newline)
try:
record_graph = get_empty_graph()
record_graph.parse(data=ET.tostring(elem, encoding='utf-8'), format='xml')
@@ -128,7 +134,7 @@
else:
transaction.commit()
- if i%self.batch_size == 0:
+ if i%self.batch_size == 0:
reset_queries()
return errors
@@ -155,6 +161,7 @@
self.batch_size = options.get('batch_size', 50)
self.preserve = options.get("preserve", False)
self.index = options.get("index", False)
+ self.newline = options.get("newline", False)
if not self.index:
old_realtime_indexing = getattr(settings, "REALTIME_INDEXING", None)
--- a/src/p4l/models/__init__.py Tue Oct 08 17:25:39 2013 +0200
+++ b/src/p4l/models/__init__.py Fri Oct 11 11:46:18 2013 +0200
@@ -43,4 +43,3 @@
Periodical, Meeting, SubjectMeeting, Audience, Record)
from p4l.models.user import User
-
--- a/src/p4l/settings.py Tue Oct 08 17:25:39 2013 +0200
+++ b/src/p4l/settings.py Fri Oct 11 11:46:18 2013 +0200
@@ -33,6 +33,7 @@
# Django settings for p4l project.
from django.conf import global_settings
+import os
DEBUG = True
TEMPLATE_DEBUG = DEBUG
@@ -129,6 +130,7 @@
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
+ 'p4l.templateloaders.Loader'
# 'django.template.loaders.eggs.Loader',
)
@@ -156,6 +158,7 @@
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
+ os.path.join(os.path.dirname(__file__), 'templates'),
)
INSTALLED_APPS = (
@@ -455,6 +458,12 @@
(u"Zhuang; Chuang", "za"),
(u"Zulu", "zu")]
+# cf http://docs.python.org/2/library/subprocess.html#popen-constructor
+ADMIN_SCRIPT = {}
+
+SCRIPT_WAIT = .250
+SCRIPT_MAX_WAIT = 40 # * SCRIPT_WAIT = 10 sec
+
from config import * # @UnusedWildImport
if not "SRC_BASE_URL" in locals():
Binary file src/p4l/static/p4l/css/img/ajax-loader.gif has changed
Binary file src/p4l/static/p4l/css/img/flags.png has changed
--- a/src/p4l/static/p4l/css/p4l.css Tue Oct 08 17:25:39 2013 +0200
+++ b/src/p4l/static/p4l/css/p4l.css Fri Oct 11 11:46:18 2013 +0200
@@ -1,11 +1,9 @@
/*!
* modif from bootstrap for p4l
*/
-
-html,
-body {
- height: 100%;
- /* The html and body elements cannot have any padding or margin. */
+html,body {
+ height: 100%;
+ /* The html and body elements cannot have any padding or margin. */
}
.container {
@@ -14,142 +12,159 @@
}
.record-table thead td:first-child {
- width: 190px;
+ width: 190px;
}
+
.record-table tbody td:first-child:first-letter {
- text-transform: uppercase;
+ text-transform: uppercase;
}
+
.table thead td:first-letter {
- text-transform: uppercase;
+ text-transform: uppercase;
}
+
.table thead {
- font-weight: bold;
+ font-weight: bold;
}
-.two_buttons{
- min-width: 100px;
+
+.two_buttons {
+ min-width: 100px;
}
footer {
- padding-bottom: 20px;
- border-top: 1px solid gray;
-}
-.bottom-5 {
- margin-bottom: 5px;
+ padding-bottom: 20px;
+ border-top: 1px solid gray;
}
-.after-plus {
- margin-top: 5px;
-}
-.after-plus button {
- margin-right: 5px;
+
+.bottom-5 {
+ margin-bottom: 5px;
}
-.rotate{
- transform:rotate(90deg);
- -ms-transform:rotate(90deg);
- -webkit-transform:rotate(90deg);
+.after-plus {
+ margin-top: 5px;
+}
+
+.after-plus button {
+ margin-right: 5px;
+}
+
+.rotate {
+ transform: rotate(90deg);
+ -ms-transform: rotate(90deg);
+ -webkit-transform: rotate(90deg);
}
.spinner {
- width: 14px;
- height: 14px;
- float: left;
- background:url(./img/ajax-loader.gif) no-repeat center center;
- border-radius: 50%;
- opacity: .7;
- margin-right: 4px;
+ width: 14px;
+ height: 14px;
+ float: left;
+ background: url("../img/ajax-loader.gif") no-repeat center center;
+ border-radius: 50%;
+ opacity: .7;
+ margin-right: 4px;
}
.grey-bottom {
- border-bottom: 1px dotted #CCC;
+ border-bottom: 1px dotted #CCC;
}
.well {
- margin-bottom: 5px;
- padding: 4px 5px 8px 8px;
+ margin-bottom: 5px;
+ padding: 4px 5px 8px 8px;
}
label {
- margin-bottom: 0px;
+ margin-bottom: 0px;
}
.edit-form-row:nth-child(odd) {
- background: #EEE;
+ background: #EEE;
}
.break-word {
- word-wrap: break-word;
+ word-wrap: break-word;
}
/* Flags */
.flag {
- width: 16px;
- height: 11px;
- padding: 0;
- border: none;
- background:url(./img/flags.png) no-repeat;
- /* hack to hide the button text */
- text-indent: -999em; /* Hide the text, works in most modern browsers */
- font-size: 0px; /* works well in IE7. still a black line (basically the text) in IE6. */
- /*display: block;*/ /* Negative text-indent works in IE(6? worked fine in 7) only if this is added. */
- line-height: 0px; /* Another fix for IE6. */
+ width: 16px;
+ height: 11px;
+ padding: 0;
+ border: none;
+ background: url("../img/flags.png") no-repeat;
+ /* hack to hide the button text */
+ text-indent: -999em;
+ /* Hide the text, works in most modern browsers */
+ font-size: 0px;
+ /* works well in IE7. still a black line (basically the text) in IE6. */
+ /*display: block;*/
+ /* Negative text-indent works in IE(6? worked fine in 7) only if this is added. */
+ line-height: 0px; /* Another fix for IE6. */
}
-.flag.flag-es {background-position: -16px 0}
-.flag.flag-fr {background-position: 0 -11px}
-.flag.flag-en {background-position: -16px -11px}
+
+.flag.flag-es {
+ background-position: -16px 0
+}
+
+.flag.flag-fr {
+ background-position: 0 -11px
+}
+
+.flag.flag-en {
+ background-position: -16px -11px
+}
/* css class to avoid angular flicker */
-[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
+[ng\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak
+ {
display: none !important;
}
-
.version {
- text-align: right;
- color: white;
- font-size:9px;
+ text-align: right;
+ color: white;
+ font-size: 9px;
}
.version:hover {
- color: inherit;
- text-decoration: none;
- border-bottom-width: 0px;
- border-bottom-style: none;
+ color: inherit;
+ text-decoration: none;
+ border-bottom-width: 0px;
+ border-bottom-style: none;
}
#wrapper {
- min-height: 100%;
- height: auto !important;
- height: 100%;
- /* Negative indent footer by it's height */
- margin: 0 auto -70px;
+ min-height: 100%;
+ height: auto !important;
+ height: 100%;
+ /* Negative indent footer by it's height */
+ margin: 0 auto -70px;
}
/* Set the fixed height of the footer here */
-#push,
-footer {
- height: 70px;
+#push,footer {
+ height: 70px;
}
.footer-img {
- padding: 10px 0;
+ padding: 10px 0;
}
#search-input {
- padding-right: 24px;
+ padding-right: 24px;
}
#search-input-cancel {
- text-indent: -1000em;
- top: 3px;
- right: 0.5em;
+ text-indent: -1000em;
+ top: 3px;
+ right: 0.5em;
}
#langselect {
- padding-bottom: 10px;
+ padding-bottom: 10px;
}
-
#copyright-version a {
- text-decoration: none;
- color: inherit;
+ text-decoration: none;
+ color: inherit;
}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/p4l/static/p4l/css/p4l_admin.css Fri Oct 11 11:46:18 2013 +0200
@@ -0,0 +1,41 @@
+.literal {
+ background-color: #eeeeec;
+ padding: 1px;
+ font-family: monospace;
+}
+
+.terminal {
+ position: relative;
+ top: 0;
+ left: 0;
+ display: block;
+ font-family: monospace;
+ white-space: pre;
+ width: 60%;
+ height: 30em;
+ border: 1px solid black;
+ margin-top: 1em;
+ overflow: auto;
+ display: none;
+}
+
+
+
+#done {
+ display: none;
+ margin-top: 2px;
+}
+
+#done h1 {
+ color: red;
+}
+
+.script-control {
+ float: left;
+ margin-left: 10px;
+}
+
+.clear {
+ clear: both;
+}
+
Binary file src/p4l/static/p4l/img/ajax-loader.gif has changed
Binary file src/p4l/static/p4l/img/flags.png has changed
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/p4l/templateloaders.py Fri Oct 11 11:46:18 2013 +0200
@@ -0,0 +1,71 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright IRI (c) 2013
+#
+# contact@iri.centrepompidou.fr
+#
+# This software is governed by the CeCILL-B license under French law and
+# abiding by the rules of distribution of free software. You can use,
+# modify and/ or redistribute the software under the terms of the CeCILL-B
+# license as circulated by CEA, CNRS and INRIA at the following URL
+# "http://www.cecill.info".
+#
+# As a counterpart to the access to the source code and rights to copy,
+# modify and redistribute granted by the license, users are provided only
+# with a limited warranty and the software's author, the holder of the
+# economic rights, and the successive licensors have only limited
+# liability.
+#
+# In this respect, the user's attention is drawn to the risks associated
+# with loading, using, modifying and/or developing or reproducing the
+# software by the user in light of its specific status of free software,
+# that may mean that it is complicated to manipulate, and that also
+# therefore means that it is reserved for developers and experienced
+# professionals having in-depth computer knowledge. Users are therefore
+# encouraged to load and test the software's suitability as regards their
+# requirements in conditions enabling the security of their systems and/or
+# data to be ensured and, more generally, to use and operate it in the
+# same conditions as regards security.
+#
+# The fact that you are presently reading this means that you have had
+# knowledge of the CeCILL-B license and that you accept its terms.
+#
+
+'''
+Created on Oct 9, 2013
+
+From http://djangosnippets.org/snippets/1376/
+
+@author: ymh
+'''
+from os.path import dirname, join, abspath, isdir
+
+from django.core.exceptions import ImproperlyConfigured
+from django.db.models import get_app
+from django.template import TemplateDoesNotExist
+from django.template.loaders.filesystem import Loader as FilesystemLoader
+
+
+def get_template_vars(template_name):
+ app_name, template_name = template_name.split(":", 1)
+ try:
+ template_dir = abspath(join(dirname(get_app(app_name).__file__), 'templates'))
+ except ImproperlyConfigured:
+ raise TemplateDoesNotExist()
+
+ return template_name, template_dir
+
+class Loader(FilesystemLoader):
+
+ is_usable = True
+
+ def get_template_sources(self, template_name, template_dirs=None):
+ if ":" not in template_name:
+ raise TemplateDoesNotExist()
+ template_name, template_dir = get_template_vars(template_name)
+
+ if not isdir(template_dir):
+ raise TemplateDoesNotExist()
+
+ return [join(template_dir, template_name)]
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/p4l/templates/admin/app_index.html Fri Oct 11 11:46:18 2013 +0200
@@ -0,0 +1,16 @@
+{% extends "admin:admin/index.html" %}
+{% load i18n %}
+
+{% if not is_popup %}
+{% block breadcrumbs %}
+<div class="breadcrumbs">
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+›
+{% for app in app_list %}
+{% blocktrans with app.name as name %}{{ name }}{% endblocktrans %}
+{% endfor %}
+</div>
+{% endblock %}
+{% endif %}
+
+{% block sidebar %}{% endblock %}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/p4l/templates/admin/index.html Fri Oct 11 11:46:18 2013 +0200
@@ -0,0 +1,19 @@
+{% extends "admin:admin/index.html" %}
+{% load i18n %}
+
+{% block content %}
+<div style="float: left;">
+{{ block.super }}
+<div id="content-other">
+ <div class="module">
+ <table>
+ <caption>{% trans 'Commands' %}</caption>
+ <tr>
+ <th scope="row">{% trans 'Launch Script' %}</th>
+ <td><a href="{% url 'admin:confirm_script' %}" class="addlink">{% trans 'Launch' %}</a></td>
+ </tr>
+ </table>
+ </div>
+</div>
+</div>
+{% endblock %}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/p4l/templates/p4l/admin/confirm_run_script.html Fri Oct 11 11:46:18 2013 +0200
@@ -0,0 +1,59 @@
+{% extends "admin/base_site.html" %}
+{% load i18n static %}
+
+{% block extrahead %}
+{{ block.super }}
+<script type="text/javascript" src="{% static 'p4l/lib/jquery-1.9.1.js' %}"></script>
+<link rel="stylesheet" href="{% static 'p4l/css/p4l_admin.css' %}">
+{% endblock %}
+
+{% if not is_popup %}
+{% block breadcrumbs %}
+<div class="breadcrumbs">
+<a href="{% url 'admin:index' %}">{% trans 'Home' %}</a>
+› {% trans 'Run script' %}
+</div>
+{% endblock %}
+{% endif %}
+
+{% block content %}
+<h1>{% trans 'Are you sure ?' %}</h1>
+{% if not command_line %}
+<p>{% blocktrans %}The ADMIN_SCRIPT setting is not correctly configured. Please configure it to launch commands.{% endblocktrans %}
+{% else %}
+<p>{% blocktrans %}Are you sure you want to run the script{% endblocktrans %} <span class="literal">{{ command_line }}</span> ?</p>
+<p>( {% trans 'cwd:' %} <span class="literal">{{ pwd }}</span>, {% trans 'env:' %} <span class="literal">{{ env }}</span> )</p>
+<div>
+ <form method="get" id="confirm-form" action="{% url 'admin:run_script' %}" target="script-output">
+ <div class="script-control" id="script-submit"><input type="submit" id="confirm-form-submit" value="{% trans "Yes, I'm sure" %}" /></div>
+ </form>
+ <div class="script-control" id="kill-button-div"><button id="kill-button" class="button" title="{% trans 'Click to stop process (sends SIGINT then SIGKILL)' %}">Control-C</button></div>
+ <div class="script-control" id="done"><h1>{% trans 'Done' %}</h1></div>
+</div>
+<div class="clear" id="kill-result"> </div>
+<iframe name="script-output" class="terminal" scrolling="auto"></iframe>
+<script type="text/javascript">
+function launch_script() {
+ $("#confirm-form-submit").attr("disabled","disabled");
+ $("#kill-button").removeAttr("disabled");
+ $('.terminal').show();
+};
+
+function done() {
+ $('#done').show();
+ $('#kill-button').attr("disabled","disabled");
+};
+$(function() {
+ $('#confirm-form').submit(function(e) {
+ launch_script();
+ });
+ $('#kill-button').attr("disabled","disabled").click(function(e) {
+ $("#kill-result").load("{% url 'admin:kill_script' %}");
+ });
+
+});
+</script>
+{% endif %}
+{% endblock %}
+
+
--- a/src/p4l/urls.py Tue Oct 08 17:25:39 2013 +0200
+++ b/src/p4l/urls.py Fri Oct 11 11:46:18 2013 +0200
@@ -32,12 +32,13 @@
#
from django.conf.urls import patterns, include, url
-from django.contrib import admin
+#from django.contrib import admin
from django.contrib.auth import urls as auth_urls
from django.contrib.auth.decorators import login_required
from p4l.search.views import RecordSearchView
from p4l.views import RecordDetailView, RecordEditView, RecordDeleteView
+from p4l import admin
js_info_dict = {
@@ -45,7 +46,7 @@
'domain': 'django',
}
-admin.autodiscover()
+#admin.autodiscover()
urlpatterns = patterns('',
url(r'^$', login_required(RecordSearchView.as_view()), name='p4l_home'),
--- a/src/p4l/utils.py Tue Oct 08 17:25:39 2013 +0200
+++ b/src/p4l/utils.py Fri Oct 11 11:46:18 2013 +0200
@@ -46,7 +46,7 @@
logger = logging.getLogger(__name__)
-def show_progress(current_line, total_line, label, width, writer=None):
+def show_progress(current_line, total_line, label, width, writer=None, newline=False):
if writer is None:
writer = sys.stdout
@@ -60,7 +60,7 @@
loader = u'[' + (u'=' * int(marks)) + (u' ' * int(spaces)) + u']'
- s = u"%s %3d%% %*d/%d - %*s\r" % (loader, percent, len(str(total_line)), current_line, total_line, width, label[:width])
+ s = u"%s %3d%% %*d/%d - %*s%s" % (loader, percent, len(str(total_line)), current_line, total_line, width, label[:width], "\n" if newline else "\r")
writer.write(s) #takes the header into account
if percent >= 100:
--- a/src/p4l/views.py Tue Oct 08 17:25:39 2013 +0200
+++ b/src/p4l/views.py Fri Oct 11 11:46:18 2013 +0200
@@ -52,7 +52,7 @@
'filter': 'dataquery',
'root': 'datarootquery',
'childs': 'datachildsquery',
- 'child-count': 'datachildcountquery'
+ 'child-count': 'datachildcountquery'
}
@@ -158,4 +158,5 @@
rec = get_object_or_404(Record, identifier=slug)
rec.delete()
return redirect('p4l_home')
-
\ No newline at end of file
+
+