Initial revision

This commit is contained in:
Frédéric Péters 2005-05-19 21:26:55 +00:00
parent e1a7e00d08
commit 2126f59e09
66 changed files with 3616 additions and 0 deletions

3
MANIFEST.in Normal file
View File

@ -0,0 +1,3 @@
include po/Makefile
include po/*.po
include po/*.pot

0
README Normal file
View File

2
debian/.cvsignore vendored Normal file
View File

@ -0,0 +1,2 @@
authentic
files

6
debian/changelog vendored Normal file
View File

@ -0,0 +1,6 @@
wcs (0.0.0-0) unstable; urgency=low
* Initial package.
-- Frederic Peters <fpeters@debian.org> Sat, 23 Apr 2005 20:48:19 +0200

13
debian/control vendored Normal file
View File

@ -0,0 +1,13 @@
Source: wcs
Section: web
Priority: optional
Maintainer: Frederic Peters <fpeters@debian.org>
Build-Depends: debhelper (>> 4.0.0)
Standards-Version: 3.6.5.0
Package: wcs
Architecture: all
Depends: python2.3, quixote (>= 2.0), libapache2-mod-scgi | libapache-mod-scgi, python2.3-scgi
Description: w.c.s. (1st draft)
.

27
debian/copyright vendored Normal file
View File

@ -0,0 +1,27 @@
This package was debianized by Frederic Peters <fpeters@debian.org> on
Wed, 20 Apr 2005 11:10:00 +0200.
Upstream Author: Frederic Peters <fpeters@entrouvert.com>
Copyright (c) 2005 Entr'ouvert;
copyright (c) 2003-2005 dotclear for the graphics.
License is GNU GPL v2 or later plus OpenSSL exception clause.
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA.
On Debian GNU/Linux systems, the complete text of the GNU General Public
License can be found in `/usr/share/common-licenses/GPL'.

2
debian/dirs vendored Normal file
View File

@ -0,0 +1,2 @@
etc/apache2/sites-available
usr

1
debian/docs vendored Normal file
View File

@ -0,0 +1 @@
README

54
debian/postinst vendored Normal file
View File

@ -0,0 +1,54 @@
#! /bin/sh
# postinst script for wcs
#
# see: dh_installdeb(1)
set -e
# summary of how this script can be called:
# * <postinst> `configure' <most-recently-configured-version>
# * <old-postinst> `abort-upgrade' <new version>
# * <conflictor's-postinst> `abort-remove' `in-favour' <package>
# <new-version>
# * <deconfigured's-postinst> `abort-deconfigure' `in-favour'
# <failed-install-package> <version> `removing'
# <conflicting-package> <version>
# for details, see http://www.debian.org/doc/debian-policy/ or
# the debian-policy package
#
# quoting from the policy:
# Any necessary prompting should almost always be confined to the
# post-installation script, and should be protected with a conditional
# so that unnecessary prompting doesn't happen if a package's
# installation fails and the `postinst' is called with `abort-upgrade',
# `abort-remove' or `abort-deconfigure'.
PACKAGE=wcs
VERSION=2.3
LIB="/usr/lib/python$VERSION"
DIRLIST="$LIB/site-packages/wcs"
case "$1" in
configure|abort-upgrade|abort-remove|abort-deconfigure)
for i in $DIRLIST ; do
/usr/bin/python$VERSION -O $LIB/compileall.py -q $i
/usr/bin/python$VERSION $LIB/compileall.py -q $i
done
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

43
debian/prerm vendored Normal file
View File

@ -0,0 +1,43 @@
#! /bin/sh
# prerm script for wcs
#
# see: dh_installdeb(1)
set -e
# summary of how this script can be called:
# * <prerm> `remove'
# * <old-prerm> `upgrade' <new-version>
# * <new-prerm> `failed-upgrade' <old-version>
# * <conflictor's-prerm> `remove' `in-favour' <package> <new-version>
# * <deconfigured's-prerm> `deconfigure' `in-favour'
# <package-being-installed> <version> `removing'
# <conflicting-package> <version>
# for details, see http://www.debian.org/doc/debian-policy/ or
# the debian-policy package
PACKAGE=wcs
case "$1" in
remove|upgrade|deconfigure)
dpkg --listfiles $PACKAGE |
awk '$0~/\.py$/ {print $0"c\n" $0"o"}' |
xargs rm -f >&2
;;
failed-upgrade)
;;
*)
echo "prerm called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

63
debian/rules vendored Executable file
View File

@ -0,0 +1,63 @@
#!/usr/bin/make -f
# GNU copyright 1997 to 1999 by Joey Hess.
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
# This is the debhelper compatibility version to use.
export DH_COMPAT=3
PYTHON=/usr/bin/python2.3
ifneq (,$(findstring debug,$(DEB_BUILD_OPTIONS)))
CFLAGS += -g
endif
ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS)))
INSTALL_PROGRAM += -s
endif
build: build-stamp
build-stamp:
dh_testdir
touch build-stamp
clean:
dh_testdir
dh_testroot
rm -f build-stamp
dh_clean
install: build
dh_testdir
dh_testroot
dh_clean -k
dh_installdirs
python setup.py install --prefix=$(CURDIR)/debian/wcs/usr --no-compile
cd po && make install prefix=$(CURDIR)/debian/wcs/
cp debian/vhost-apache-wcs $(CURDIR)/debian/wcs/etc/apache2/sites-available
# Build architecture-independent files here.
binary-indep: build install
# We have nothing to do by default.
# Build architecture-dependent files here.
binary-arch: build install
dh_testdir
dh_testroot
dh_installdocs
dh_installinit
dh_link
dh_strip
dh_fixperms
dh_installdeb
dh_gencontrol
dh_md5sums
dh_builddeb
binary: binary-indep binary-arch
.PHONY: build clean binary-indep binary-arch binary install

21
debian/vhost-apache-wcs vendored Normal file
View File

@ -0,0 +1,21 @@
<VirtualHost *>
ServerAdmin webmaster@locahost
ServerName www.example.com
DocumentRoot /usr/share/wcs/web/
### <LocationMatch "^/wcs(/|$)">
### SetHandler python-program
### PythonHandler quixote.server.mod_python_handler
### PythonOption quixote-publisher-factory wcs.create_publisher
### PythonDebug On
### </LocationMatch>
<Location /wcs>
SCGIServer 127.0.0.1:3001
SCGIHandler On
</Location>
CustomLog /var/log/apache2/wcs-access.log combined
ErrorLog /var/log/apache2/wcs-error.log
</VirtualHost>

87
debian/wcs.init vendored Executable file
View File

@ -0,0 +1,87 @@
#! /bin/sh
set -e
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DESC="wcs"
NAME=wcs
DAEMON=/usr/bin/wcs_scgi_server.py
PIDFILE=/var/run/$NAME.pid
SCRIPTNAME=/etc/init.d/$NAME
# Gracefully exit if the package has been removed.
test -x $DAEMON || exit 0
# Read config file if it is present.
if [ -r /etc/default/$NAME ]
then
. /etc/default/$NAME
fi
#
# Function that starts the daemon/service.
#
d_start() {
start-stop-daemon --start --quiet --pidfile $PIDFILE \
--chuid www-data:www-data --make-pidfile --background --exec $DAEMON
}
#
# Function that stops the daemon/service.
#
d_stop() {
start-stop-daemon --stop --quiet --pidfile $PIDFILE
}
#
# Function that sends a SIGHUP to the daemon/service.
#
d_reload() {
start-stop-daemon --stop --quiet --pidfile $PIDFILE \
--make-pidfile --background --signal 1
}
case "$1" in
start)
echo -n "Starting $DESC: $NAME"
d_start
echo "."
;;
stop)
echo -n "Stopping $DESC: $NAME"
d_stop
echo "."
;;
#reload)
#
# If the daemon can reload its configuration without
# restarting (for example, when it is sent a SIGHUP),
# then implement that here.
#
# If the daemon responds to changes in its config file
# directly anyway, make this an "exit 0".
#
# echo -n "Reloading $DESC configuration..."
# d_reload
# echo "done."
#;;
restart|force-reload)
#
# If the "reload" option is implemented, move the "force-reload"
# option to the "reload" entry above. If not, "force-reload" is
# just the same as "restart".
#
echo -n "Restarting $DESC: $NAME"
d_stop
sleep 1
d_start
echo "."
;;
*)
# echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
exit 1
;;
esac
exit 0

38
po/Makefile Normal file
View File

@ -0,0 +1,38 @@
POFILES=$(wildcard *.po)
MOFILES=$(POFILES:.po=.mo)
PYFILES=$(shell find ../wcs -name '*.py' -or -name '*.ptl')
all: $(MOFILES)
install: all
for file in $(MOFILES); do \
lang=`echo $$file | sed 's/\.mo//'`; \
install -d $(prefix)/usr/share/locale/$$lang/LC_MESSAGES/; \
install -m 0644 $$file $(prefix)/usr/share/locale/$$lang/LC_MESSAGES/wcs.mo; \
done
wcs.pot: $(PYFILES)
@echo "Rebuilding the pot file"
rm -f wcs.pot tmp.*.pot
cnt=0;
for file in $(PYFILES); do \
cnt=$$(expr $$cnt + 1); \
bn=$$cnt.`basename $$file`; \
xgettext --keyword=N_ -c -L Python -o tmp.$$bn.pot $$file; \
done
msgcat tmp.*.pot > wcs.pot
rm tmp.*.pot
%.mo: %.po
msgfmt -o $@ $<
%.po: wcs.pot
@echo -n "Merging wcs.pot and $@"
@msgmerge $@ wcs.pot -o $@.new
@if [ "`diff $@ $@.new | grep '[<>]' | wc -l`" -ne 2 ]; then \
mv -f $@.new $@; \
else \
rm -f $@.new; \
fi
@msgfmt --statistics $@

436
po/fr.po Normal file
View File

@ -0,0 +1,436 @@
msgid ""
msgstr ""
"Project-Id-Version: wcs 0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2005-05-19 16:00+0200\n"
"PO-Revision-Date: 2005-04-29 12:27+0200\n"
"Last-Translator: Frederic Peters <fpeters@entrouvert.com>\n"
"Language-Team: french\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n>1;\n"
#: ../wcs/admin/users.ptl:32 ../wcs/admin/users.ptl:49
msgid "User Id"
msgstr "Identifiant de l'utilisateur"
#: ../wcs/admin/users.ptl:34 ../wcs/admin/users.ptl:51
msgid "User Name"
msgstr "Nom de l'utilisateur"
#: ../wcs/admin/users.ptl:36 ../wcs/admin/users.ptl:53
msgid "Email"
msgstr "Email"
#: ../wcs/admin/users.ptl:38 ../wcs/admin/users.ptl:55 ../wcs/admin/menu.ptl:6
msgid "Roles"
msgstr "Rôles"
#: ../wcs/admin/users.ptl:39 ../wcs/admin/users.ptl:56
msgid "Add Role"
msgstr "Ajouter un rôle"
#: ../wcs/admin/users.ptl:43 ../wcs/admin/users.ptl:61
#: ../wcs/admin/users.ptl:97 ../wcs/admin/settings.ptl:53
#: ../wcs/admin/settings.ptl:125 ../wcs/admin/settings.ptl:141
#: ../wcs/admin/forms.ptl:72 ../wcs/admin/forms.ptl:105
#: ../wcs/admin/forms.ptl:234 ../wcs/admin/roles.ptl:28
#: ../wcs/admin/roles.ptl:38 ../wcs/admin/roles.ptl:73
msgid "Submit"
msgstr "Valider"
#: ../wcs/admin/users.ptl:44 ../wcs/admin/users.ptl:60
#: ../wcs/admin/users.ptl:96 ../wcs/admin/users.ptl:111
#: ../wcs/admin/settings.ptl:142 ../wcs/admin/forms.ptl:73
#: ../wcs/admin/forms.ptl:106 ../wcs/admin/forms.ptl:235
#: ../wcs/admin/roles.ptl:29 ../wcs/admin/roles.ptl:39
#: ../wcs/admin/roles.ptl:74
msgid "Cancel"
msgstr "Annuler"
#: ../wcs/admin/users.ptl:83 ../wcs/admin/users.ptl:84
msgid "Edit User"
msgstr "Éditer l'utilisateur"
#: ../wcs/admin/users.ptl:95
msgid "You are about to irrevocably delete this user."
msgstr "Vous allez définitivement supprimer cet utilisateur."
#: ../wcs/admin/users.ptl:101
msgid "Delete User"
msgstr "Supprimer l'utilisateur"
#: ../wcs/admin/users.ptl:102
msgid "Deleting User:"
msgstr "Suppression de l'utilisateur :"
#: ../wcs/admin/users.ptl:112
msgid "Generate"
msgstr "Générer"
#: ../wcs/admin/users.ptl:118 ../wcs/admin/users.ptl:119
#: ../wcs/admin/users.ptl:127 ../wcs/admin/users.ptl:167
msgid "Identification Token"
msgstr "Jeton d'identification"
#: ../wcs/admin/users.ptl:122
#, python-format
msgid "Note that user has already been issued an identification token: %s"
msgstr "À noter que l'utilisateur a déjà un jeton d'identification : %s"
#: ../wcs/admin/users.ptl:133
#, python-format
msgid "Identification Token for %s:"
msgstr "Jeton d'identification pour %s :"
#: ../wcs/admin/users.ptl:137
msgid "Done"
msgstr ""
#: ../wcs/admin/users.ptl:139
msgid "Send by email"
msgstr "Envoyer par email"
#: ../wcs/admin/users.ptl:157 ../wcs/admin/settings.ptl:27
#: ../wcs/admin/forms.ptl:268 ../wcs/admin/roles.ptl:96
msgid "New"
msgstr "Nouveau"
#: ../wcs/admin/users.ptl:168 ../wcs/admin/settings.ptl:38
#: ../wcs/admin/forms.ptl:166 ../wcs/admin/forms.ptl:278
#: ../wcs/admin/roles.ptl:103
msgid "Edit"
msgstr "Modifier"
#: ../wcs/admin/users.ptl:169 ../wcs/admin/settings.ptl:39
#: ../wcs/admin/forms.ptl:280 ../wcs/admin/roles.ptl:104
msgid "Delete"
msgstr "Supprimer"
#: ../wcs/admin/users.ptl:181 ../wcs/admin/users.ptl:182
msgid "New User"
msgstr "Nouvel utilisateur"
#: ../wcs/forms/root.ptl:49 ../wcs/forms/root.ptl:71 ../wcs/forms/root.ptl:157
#, python-format
msgid "The form has been recorded on %s with the number %s."
msgstr "Le formulaire a été enregistré le %s avec le numéro %s."
#: ../wcs/forms/root.ptl:53
msgid "Your case is handled by:"
msgstr "Votre dossier est pris en charge par :"
#: ../wcs/forms/root.ptl:64 ../wcs/forms/root.ptl:79 ../wcs/forms/root.ptl:174
msgid "Back Home"
msgstr "Retour à l'accueil"
#: ../wcs/forms/root.ptl:95
msgid "Filling"
msgstr "Je remplis ma demande"
#: ../wcs/forms/root.ptl:95
msgid "Validating"
msgstr "Je confirme ma demande"
#: ../wcs/forms/root.ptl:95
msgid "Receipt"
msgstr "Justificatif"
#: ../wcs/forms/root.ptl:105 ../wcs/forms/root.ptl:116
#: ../wcs/forms/root.ptl:146
msgid "Next"
msgstr "Suivant"
#: ../wcs/forms/root.ptl:115 ../wcs/forms/root.ptl:145
msgid "Previous"
msgstr "Précédent"
#: ../wcs/forms/root.ptl:142
msgid "Check values then click next."
msgstr "Vérifiez le contenu du formulaire puis cliquer sur 'Suivant'"
#: ../wcs/forms/root.ptl:161
msgid "Your case will be handled by:"
msgstr "Votre dossier sera pris en charge par :"
#: ../wcs/forms/root.ptl:187 ../wcs/admin/menu.ptl:4
msgid "Forms"
msgstr "Formulaires"
#: ../wcs/forms/root.ptl:207
msgid "Your Current Forms"
msgstr "Vos formulaires en cours"
#: ../wcs/forms/root.ptl:215
msgid "Logout"
msgstr "Se déconnecter"
#: ../wcs/forms/root.ptl:218
msgid "Login"
msgstr "S'identifier"
#: ../wcs/formdef.py:81
#, python-format
msgid ""
"Hi,\n"
"\n"
"A new form has been submitted on the website, you can consult it with this\n"
"link: %(url)s\n"
msgstr ""
"Bonjour,\n"
"\n"
"Un nouveau formulaire a été enregistré sur le site web, vous pouvez en\n"
"prendre connaissance en suivant ce lien :\n"
"%(url)s\n"
#: ../wcs/formdef.py:92
msgid "A new form has been submitted"
msgstr "Un nouveau formulaire a été enregistré"
#: ../wcs/form.py:6
msgid "required field"
msgstr "champ obligatoire"
#: ../wcs/form.py:10
msgid "There were errors processing your form. See below for details."
msgstr ""
"Il y a eu un problème à la soumission du formulaire. Regardez ci-dessous "
"pour le détail."
#: ../wcs/admin/settings.ptl:25 ../wcs/admin/settings.ptl:26
#: ../wcs/admin/settings.ptl:182
msgid "Identity Providers"
msgstr "Fournisseurs d'identités"
#: ../wcs/admin/settings.ptl:37
msgid "View"
msgstr "Voir"
#: ../wcs/admin/settings.ptl:50 ../wcs/admin/settings.ptl:113
#: ../wcs/admin/settings.ptl:122
msgid "Metadata"
msgstr "Metadata"
#: ../wcs/admin/settings.ptl:51 ../wcs/admin/settings.ptl:123
#: ../wcs/admin/settings.ptl:210
msgid "Public Key"
msgstr "Clé publique"
#: ../wcs/admin/settings.ptl:52 ../wcs/admin/settings.ptl:124
msgid "CA Certificate Chain"
msgstr "Chaîne de certification"
#: ../wcs/admin/settings.ptl:56 ../wcs/admin/settings.ptl:57
msgid "New Identity Provider"
msgstr "Nouveau fournisseur d'identités"
#: ../wcs/admin/settings.ptl:108 ../wcs/admin/settings.ptl:111
#: ../wcs/admin/settings.ptl:146
msgid "Identity Provider"
msgstr "Fournisseur d'identités"
#: ../wcs/admin/settings.ptl:128 ../wcs/admin/settings.ptl:129
msgid "Edit Identity Provider"
msgstr "Modifier le fournisseur d'identités"
#: ../wcs/admin/settings.ptl:140
msgid "You are about to irrevocably remove this identity provider."
msgstr "Vous allez définitivement supprimer ce fournisseur d'identités."
#: ../wcs/admin/settings.ptl:147
msgid "Deleting"
msgstr "Suppression"
#: ../wcs/admin/settings.ptl:176
msgid "Service Provider"
msgstr "Fournisseur de service"
#: ../wcs/admin/settings.ptl:176
msgid "Configure Liberty parameters"
msgstr "Configurer les paramètres Liberty"
#: ../wcs/admin/settings.ptl:179
msgid "Service Provider Metadata"
msgstr "Metadata du fournisseur de service"
#: ../wcs/admin/settings.ptl:179
msgid "Download Service Provider Metadata file"
msgstr "Télécharger le fichier des metadata du fournisseur de service"
#: ../wcs/admin/settings.ptl:182
msgid "Add and remove identity providers"
msgstr "Ajouter et supprimer des fournisseurs d'identités"
#: ../wcs/admin/settings.ptl:203
msgid "Provider ID"
msgstr "Identifiant du fournisseur (Provider ID)"
#: ../wcs/admin/settings.ptl:205
msgid "Base URL"
msgstr "URL de la racine"
#: ../wcs/admin/settings.ptl:207
msgid "Organization Name"
msgstr "Nom de l'organisation"
#: ../wcs/admin/settings.ptl:209
msgid "Private Key"
msgstr "Clé privée"
#: ../wcs/admin/settings.ptl:213
msgid "Identity Provider Introduction, Common Domain"
msgstr "Domaine commun, pour 'Identity Provider Introduction'"
#: ../wcs/admin/settings.ptl:214
msgid "Disabled if empty"
msgstr "Désactivé si vide"
#: ../wcs/admin/settings.ptl:217 ../wcs/admin/settings.ptl:218
msgid "Service Provider Configuration"
msgstr "Configuration du fournisseur de service"
#: ../wcs/admin/menu.ptl:5
msgid "Users"
msgstr "Utilisateurs"
#: ../wcs/admin/menu.ptl:7
msgid "Settings"
msgstr "Paramètres"
#: ../wcs/admin/menu.ptl:8
msgid "WCS Form Server"
msgstr "Serveur de formulaires"
#: ../wcs/admin/menu.ptl:48
msgid "WCS Administration"
msgstr "Administration de WCS"
#: ../wcs/admin/forms.ptl:24
msgid "Text (line)"
msgstr "Texte (ligne)"
#: ../wcs/admin/forms.ptl:25
msgid "Text (block)"
msgstr "Bloc de texte"
#: ../wcs/admin/forms.ptl:26
msgid "Boolean"
msgstr "Booléen"
#: ../wcs/admin/forms.ptl:27
msgid "Item from list"
msgstr "Élément d'une liste"
#: ../wcs/admin/forms.ptl:28
msgid "Title"
msgstr "Titre"
#: ../wcs/admin/forms.ptl:29
msgid "Subtitle"
msgstr "Sous-titre"
#: ../wcs/admin/forms.ptl:61
msgid "Form Id"
msgstr "Identifiant du formulaire"
#: ../wcs/admin/forms.ptl:63
msgid "Form Name"
msgstr "Nom du formulaire"
#: ../wcs/admin/forms.ptl:65 ../wcs/admin/forms.ptl:279
msgid "Fields"
msgstr "Champs"
#: ../wcs/admin/forms.ptl:66
msgid "Add Field"
msgstr "Ajouter un champ"
#: ../wcs/admin/forms.ptl:68
msgid "Recipient"
msgstr "Destinataire"
#: ../wcs/admin/forms.ptl:70
msgid "Recipient Email"
msgstr "Adresse email du destinataire"
#: ../wcs/admin/forms.ptl:92
msgid "Name"
msgstr "Nom"
#: ../wcs/admin/forms.ptl:94
msgid "Type"
msgstr "Type"
#: ../wcs/admin/forms.ptl:97
msgid "Required"
msgstr "Obligatoire"
#: ../wcs/admin/forms.ptl:101
msgid "Items"
msgstr "Éléments"
#: ../wcs/admin/forms.ptl:176
msgid "Export to CSV format"
msgstr "Exporter au format CSV"
#: ../wcs/admin/forms.ptl:220 ../wcs/admin/forms.ptl:221
msgid "Edit Form"
msgstr "Éditer le formulaire"
#: ../wcs/admin/forms.ptl:233
msgid "You are about to irrevocably delete this form."
msgstr "Vous allez définitivement supprimer ce formulaire."
#: ../wcs/admin/forms.ptl:239
msgid "Delete Form"
msgstr "Supprimer le formulaire"
#: ../wcs/admin/forms.ptl:240
msgid "Deleting Form:"
msgstr "Suppression du formulaire :"
#: ../wcs/admin/forms.ptl:281
msgid "Listing"
msgstr "Listing"
#: ../wcs/admin/forms.ptl:297 ../wcs/admin/forms.ptl:298
msgid "New Form"
msgstr "Nouveau formulaire"
#: ../wcs/admin/roles.ptl:24 ../wcs/admin/roles.ptl:34
msgid "Role Id"
msgstr ""
#: ../wcs/admin/roles.ptl:26 ../wcs/admin/roles.ptl:36
msgid "Role Name"
msgstr "Nom du rôle"
#: ../wcs/admin/roles.ptl:60 ../wcs/admin/roles.ptl:61
msgid "Edit Role"
msgstr "Éditer le rôle"
#: ../wcs/admin/roles.ptl:72
msgid "You are about to irrevocably delete this role."
msgstr "Vous allez définitivement supprimer ce rôle."
#: ../wcs/admin/roles.ptl:78
msgid "Delete Role"
msgstr "Supprimer le rôle"
#: ../wcs/admin/roles.ptl:79
msgid "Deleting Role:"
msgstr "Suppression du rôle :"
#: ../wcs/admin/roles.ptl:116 ../wcs/admin/roles.ptl:117
msgid "New Role"
msgstr "Nouveau rôle"
#~ msgid "Forms Management"
#~ msgstr "Gestion des formulaires"
#~ msgid "Field Details"
#~ msgstr "Détail des champs"
#~ msgid "List Item"
#~ msgstr "Liste"

425
po/wcs.pot Normal file
View File

@ -0,0 +1,425 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2005-05-19 16:00+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"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: ../wcs/admin/users.ptl:32 ../wcs/admin/users.ptl:49
msgid "User Id"
msgstr ""
#: ../wcs/admin/users.ptl:34 ../wcs/admin/users.ptl:51
msgid "User Name"
msgstr ""
#: ../wcs/admin/users.ptl:36 ../wcs/admin/users.ptl:53
msgid "Email"
msgstr ""
#: ../wcs/admin/users.ptl:38 ../wcs/admin/users.ptl:55 ../wcs/admin/menu.ptl:6
msgid "Roles"
msgstr ""
#: ../wcs/admin/users.ptl:39 ../wcs/admin/users.ptl:56
msgid "Add Role"
msgstr ""
#: ../wcs/admin/users.ptl:43 ../wcs/admin/users.ptl:61
#: ../wcs/admin/users.ptl:97 ../wcs/admin/settings.ptl:53
#: ../wcs/admin/settings.ptl:125 ../wcs/admin/settings.ptl:141
#: ../wcs/admin/forms.ptl:72 ../wcs/admin/forms.ptl:105
#: ../wcs/admin/forms.ptl:234 ../wcs/admin/roles.ptl:28
#: ../wcs/admin/roles.ptl:38 ../wcs/admin/roles.ptl:73
msgid "Submit"
msgstr ""
#: ../wcs/admin/users.ptl:44 ../wcs/admin/users.ptl:60
#: ../wcs/admin/users.ptl:96 ../wcs/admin/users.ptl:111
#: ../wcs/admin/settings.ptl:142 ../wcs/admin/forms.ptl:73
#: ../wcs/admin/forms.ptl:106 ../wcs/admin/forms.ptl:235
#: ../wcs/admin/roles.ptl:29 ../wcs/admin/roles.ptl:39
#: ../wcs/admin/roles.ptl:74
msgid "Cancel"
msgstr ""
#: ../wcs/admin/users.ptl:83 ../wcs/admin/users.ptl:84
msgid "Edit User"
msgstr ""
#: ../wcs/admin/users.ptl:95
msgid "You are about to irrevocably delete this user."
msgstr ""
#: ../wcs/admin/users.ptl:101
msgid "Delete User"
msgstr ""
#: ../wcs/admin/users.ptl:102
msgid "Deleting User:"
msgstr ""
#: ../wcs/admin/users.ptl:112
msgid "Generate"
msgstr ""
#: ../wcs/admin/users.ptl:118 ../wcs/admin/users.ptl:119
#: ../wcs/admin/users.ptl:127 ../wcs/admin/users.ptl:167
msgid "Identification Token"
msgstr ""
#: ../wcs/admin/users.ptl:122
#, python-format
msgid "Note that user has already been issued an identification token: %s"
msgstr ""
#: ../wcs/admin/users.ptl:133
#, python-format
msgid "Identification Token for %s:"
msgstr ""
#: ../wcs/admin/users.ptl:137
msgid "Done"
msgstr ""
#: ../wcs/admin/users.ptl:139
msgid "Send by email"
msgstr ""
#: ../wcs/admin/users.ptl:157 ../wcs/admin/settings.ptl:27
#: ../wcs/admin/forms.ptl:268 ../wcs/admin/roles.ptl:96
msgid "New"
msgstr ""
#: ../wcs/admin/users.ptl:168 ../wcs/admin/settings.ptl:38
#: ../wcs/admin/forms.ptl:166 ../wcs/admin/forms.ptl:278
#: ../wcs/admin/roles.ptl:103
msgid "Edit"
msgstr ""
#: ../wcs/admin/users.ptl:169 ../wcs/admin/settings.ptl:39
#: ../wcs/admin/forms.ptl:280 ../wcs/admin/roles.ptl:104
msgid "Delete"
msgstr ""
#: ../wcs/admin/users.ptl:181 ../wcs/admin/users.ptl:182
msgid "New User"
msgstr ""
#: ../wcs/forms/root.ptl:49 ../wcs/forms/root.ptl:71 ../wcs/forms/root.ptl:157
#, python-format
msgid "The form has been recorded on %s with the number %s."
msgstr ""
#: ../wcs/forms/root.ptl:53
msgid "Your case is handled by:"
msgstr ""
#: ../wcs/forms/root.ptl:64 ../wcs/forms/root.ptl:79 ../wcs/forms/root.ptl:174
msgid "Back Home"
msgstr ""
#: ../wcs/forms/root.ptl:95
msgid "Filling"
msgstr ""
#: ../wcs/forms/root.ptl:95
msgid "Validating"
msgstr ""
#: ../wcs/forms/root.ptl:95
msgid "Receipt"
msgstr ""
#: ../wcs/forms/root.ptl:105 ../wcs/forms/root.ptl:116
#: ../wcs/forms/root.ptl:146
msgid "Next"
msgstr ""
#: ../wcs/forms/root.ptl:115 ../wcs/forms/root.ptl:145
msgid "Previous"
msgstr ""
#: ../wcs/forms/root.ptl:142
msgid "Check values then click next."
msgstr ""
#: ../wcs/forms/root.ptl:161
msgid "Your case will be handled by:"
msgstr ""
#: ../wcs/forms/root.ptl:187 ../wcs/admin/menu.ptl:4
msgid "Forms"
msgstr ""
#: ../wcs/forms/root.ptl:207
msgid "Your Current Forms"
msgstr ""
#: ../wcs/forms/root.ptl:215
msgid "Logout"
msgstr ""
#: ../wcs/forms/root.ptl:218
msgid "Login"
msgstr ""
#: ../wcs/formdef.py:81
#, python-format
msgid ""
"Hi,\n"
"\n"
"A new form has been submitted on the website, you can consult it with this\n"
"link: %(url)s\n"
msgstr ""
#: ../wcs/formdef.py:92
msgid "A new form has been submitted"
msgstr ""
#: ../wcs/form.py:6
msgid "required field"
msgstr ""
#: ../wcs/form.py:10
msgid "There were errors processing your form. See below for details."
msgstr ""
#: ../wcs/admin/settings.ptl:25 ../wcs/admin/settings.ptl:26
#: ../wcs/admin/settings.ptl:182
msgid "Identity Providers"
msgstr ""
#: ../wcs/admin/settings.ptl:37
msgid "View"
msgstr ""
#: ../wcs/admin/settings.ptl:50 ../wcs/admin/settings.ptl:113
#: ../wcs/admin/settings.ptl:122
msgid "Metadata"
msgstr ""
#: ../wcs/admin/settings.ptl:51 ../wcs/admin/settings.ptl:123
#: ../wcs/admin/settings.ptl:210
msgid "Public Key"
msgstr ""
#: ../wcs/admin/settings.ptl:52 ../wcs/admin/settings.ptl:124
msgid "CA Certificate Chain"
msgstr ""
#: ../wcs/admin/settings.ptl:56 ../wcs/admin/settings.ptl:57
msgid "New Identity Provider"
msgstr ""
#: ../wcs/admin/settings.ptl:108 ../wcs/admin/settings.ptl:111
#: ../wcs/admin/settings.ptl:146
msgid "Identity Provider"
msgstr ""
#: ../wcs/admin/settings.ptl:128 ../wcs/admin/settings.ptl:129
msgid "Edit Identity Provider"
msgstr ""
#: ../wcs/admin/settings.ptl:140
msgid "You are about to irrevocably remove this identity provider."
msgstr ""
#: ../wcs/admin/settings.ptl:147
msgid "Deleting"
msgstr ""
#: ../wcs/admin/settings.ptl:176
msgid "Service Provider"
msgstr ""
#: ../wcs/admin/settings.ptl:176
msgid "Configure Liberty parameters"
msgstr ""
#: ../wcs/admin/settings.ptl:179
msgid "Service Provider Metadata"
msgstr ""
#: ../wcs/admin/settings.ptl:179
msgid "Download Service Provider Metadata file"
msgstr ""
#: ../wcs/admin/settings.ptl:182
msgid "Add and remove identity providers"
msgstr ""
#: ../wcs/admin/settings.ptl:203
msgid "Provider ID"
msgstr ""
#: ../wcs/admin/settings.ptl:205
msgid "Base URL"
msgstr ""
#: ../wcs/admin/settings.ptl:207
msgid "Organization Name"
msgstr ""
#: ../wcs/admin/settings.ptl:209
msgid "Private Key"
msgstr ""
#: ../wcs/admin/settings.ptl:213
msgid "Identity Provider Introduction, Common Domain"
msgstr ""
#: ../wcs/admin/settings.ptl:214
msgid "Disabled if empty"
msgstr ""
#: ../wcs/admin/settings.ptl:217 ../wcs/admin/settings.ptl:218
msgid "Service Provider Configuration"
msgstr ""
#: ../wcs/admin/menu.ptl:5
msgid "Users"
msgstr ""
#: ../wcs/admin/menu.ptl:7
msgid "Settings"
msgstr ""
#: ../wcs/admin/menu.ptl:8
msgid "WCS Form Server"
msgstr ""
#: ../wcs/admin/menu.ptl:48
msgid "WCS Administration"
msgstr ""
#: ../wcs/admin/forms.ptl:24
msgid "Text (line)"
msgstr ""
#: ../wcs/admin/forms.ptl:25
msgid "Text (block)"
msgstr ""
#: ../wcs/admin/forms.ptl:26
msgid "Boolean"
msgstr ""
#: ../wcs/admin/forms.ptl:27
msgid "Item from list"
msgstr ""
#: ../wcs/admin/forms.ptl:28
msgid "Title"
msgstr ""
#: ../wcs/admin/forms.ptl:29
msgid "Subtitle"
msgstr ""
#: ../wcs/admin/forms.ptl:61
msgid "Form Id"
msgstr ""
#: ../wcs/admin/forms.ptl:63
msgid "Form Name"
msgstr ""
#: ../wcs/admin/forms.ptl:65 ../wcs/admin/forms.ptl:279
msgid "Fields"
msgstr ""
#: ../wcs/admin/forms.ptl:66
msgid "Add Field"
msgstr ""
#: ../wcs/admin/forms.ptl:68
msgid "Recipient"
msgstr ""
#: ../wcs/admin/forms.ptl:70
msgid "Recipient Email"
msgstr ""
#: ../wcs/admin/forms.ptl:92
msgid "Name"
msgstr ""
#: ../wcs/admin/forms.ptl:94
msgid "Type"
msgstr ""
#: ../wcs/admin/forms.ptl:97
msgid "Required"
msgstr ""
#: ../wcs/admin/forms.ptl:101
msgid "Items"
msgstr ""
#: ../wcs/admin/forms.ptl:176
msgid "Export to CSV format"
msgstr ""
#: ../wcs/admin/forms.ptl:220 ../wcs/admin/forms.ptl:221
msgid "Edit Form"
msgstr ""
#: ../wcs/admin/forms.ptl:233
msgid "You are about to irrevocably delete this form."
msgstr ""
#: ../wcs/admin/forms.ptl:239
msgid "Delete Form"
msgstr ""
#: ../wcs/admin/forms.ptl:240
msgid "Deleting Form:"
msgstr ""
#: ../wcs/admin/forms.ptl:281
msgid "Listing"
msgstr ""
#: ../wcs/admin/forms.ptl:297 ../wcs/admin/forms.ptl:298
msgid "New Form"
msgstr ""
#: ../wcs/admin/roles.ptl:24 ../wcs/admin/roles.ptl:34
msgid "Role Id"
msgstr ""
#: ../wcs/admin/roles.ptl:26 ../wcs/admin/roles.ptl:36
msgid "Role Name"
msgstr ""
#: ../wcs/admin/roles.ptl:60 ../wcs/admin/roles.ptl:61
msgid "Edit Role"
msgstr ""
#: ../wcs/admin/roles.ptl:72
msgid "You are about to irrevocably delete this role."
msgstr ""
#: ../wcs/admin/roles.ptl:78
msgid "Delete Role"
msgstr ""
#: ../wcs/admin/roles.ptl:79
msgid "Deleting Role:"
msgstr ""
#: ../wcs/admin/roles.ptl:116 ../wcs/admin/roles.ptl:117
msgid "New Role"
msgstr ""

Binary file not shown.

BIN
root/css/bg-footer.png Normal file

Binary file not shown.

BIN
root/css/deg-top.png Normal file

Binary file not shown.

BIN
root/css/dot999.png Normal file

Binary file not shown.

BIN
root/css/fond.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
root/css/ico_user.png Normal file

Binary file not shown.

BIN
root/css/img/bulle.png Normal file

Binary file not shown.

BIN
root/css/img/day-date.png Normal file

Binary file not shown.

BIN
root/css/img/footer.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
root/css/img/h2.png Normal file

Binary file not shown.

BIN
root/css/img/li.png Normal file

Binary file not shown.

BIN
root/css/img/linkscat.png Normal file

Binary file not shown.

BIN
root/css/img/page.png Normal file

Binary file not shown.

BIN
root/css/img/search.png Normal file

Binary file not shown.

BIN
root/css/img/sidebarh2.png Normal file

Binary file not shown.

BIN
root/css/img/top.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

BIN
root/css/img/top.png Normal file

Binary file not shown.

BIN
root/css/onglet_left.png Normal file

Binary file not shown.

BIN
root/css/onglet_right.png Normal file

Binary file not shown.

BIN
root/css/required.png Normal file

Binary file not shown.

Binary file not shown.

BIN
root/css/user_info_top.png Normal file

Binary file not shown.

BIN
root/css/warning.png Normal file

Binary file not shown.

260
root/css/wcs-admin.css Normal file
View File

@ -0,0 +1,260 @@
@import url(wcs-common.css);
body {
font-family: sans-serif;
background : white url(fond.jpg) repeat;
}
div#main-content {
clear: both;
max-width: 700px;
margin: 0 8em;
background: white url(deg-top.png) top left repeat-x;
border: 1px solid #999;
padding: 1em;
}
div#main-content h1 {
margin: 0 140px 1em 0;
border: 1px solid #666;
padding: 0 0.5ex;
background: white url(bg-footer.png) top right repeat-y;
font-size: 150%;
}
div.form, div.idp {
border: 1px solid #aaa;
margin-bottom: 1em;
clear: both;
}
div.form h3, div.idp h3 {
margin: 0;
border-bottom: 1px solid #aaa;
font-weight: normal;
background: #ddd;
}
div.form p, div.idp p {
margin: 3px;
}
div.form span.data, div.idp span.data {
float: left;
max-width: 70%;
}
div.form span.cmds, div.idp span.cmds {
font-size: 80%;
display: block;
text-align: right;
}
#header {
max-width: 700px;
margin: 0 8em;
padding: 0 1em;
background-image : url(dot999.png);
background-repeat : repeat-x;
background-position : 0 100%;
position: relative;
top: 1px;
}
#header ul {
margin : 0;
padding : 0;
list-style : none;
}
#header li {
float : left;
margin : 0 -1px 0 0;
padding : 0 0 0 8px;
background-repeat : no-repeat;
background-position : 0 -110px;
background-image: url(onglet_left.png);
border-bottom: 1px solid #999;
}
#header a {
float : left;
display : block;
background-image : url(onglet_right.png);
background-repeat : no-repeat;
background-position : 100% -110px;
padding : 10px 10px 4px 3px;
font-weight : bold;
text-decoration : none;
color : #000;
}
/* Commented Backslash Hack
Cache des règles à IE5-Mac \*/
#header a {float:none;}
/* Fin du hack IE5-Mac */
#header li.active, #header li.active:hover {
background-position : 0 0;
}
#header li.active a, #header li.active:hover a {
background-position : 100% 0;
}
#header li:hover {
background-position : 0 -220px;
}
#header li:hover a {
background-position : 100% -220px;
}
#header li.active {
border-bottom: 1px solid #eceade;
}
div#footer {
max-width: 700px;
margin: 0 8em;
border: 1px solid #999;
border-top: 0;
background: #eceade;
padding: 0 1em;
}
div#footer p {
margin: 0;
text-align: right;
font-size: 80%;
font-weight: bold;
}
div#login-top,
div#logout-top,
div#identity-top {
margin: 6em auto 1em auto;
border: 1px solid #999;
background: white;
padding: 0.5ex 1em;
}
div#login-top,
div#login-form {
width: 20em;
max-width: 200px;
}
div#logout-top,
div#logout-sps,
div#identity-top,
div#identity-content {
width: 40em;
max-width: 400px;
}
div#login-top h1,
div#logout-top h1,
div#identity-top h1 {
margin: 0;
text-align: center;
}
div#login-form,
div#logout-sps,
div#identity-content {
margin: 0 auto;
background: white url(deg-top.png) top left repeat-x;
border: 1px solid #999;
padding: 1em;
}
div#login-form br {
display: none;
}
div#login-form div.StringWidget {
margin-bottom: 1ex;
}
p#cookies {
margin: 0 1em;
text-align: center;
font-size: 90%;
}
form#login span.required {
display: none
}
div#logout-sps ul li {
list-style: circle;
}
div#logout-sps ul {
padding-left: 2em;
}
div#logout-sps ul li img {
padding-left: 1em;
}
ul.user-info {
float : right;
width : 102px;
position : relative;
margin : 0 0 1em 1em;
padding : 5px 0 0 30px;
background : transparent url(user_info_top.png) no-repeat top left;
list-style : none;
}
li.ui-name {
}
li.ui-logout {
display : block;
background : transparent url(user_info_bottom.png) no-repeat bottom left;
padding : 8px 0 10px 10px;
margin : 0 0 0 -30px;
}
li.ui-logout a {
padding-left: 20px;
}
table {
clear: both;
}
table th {
text-align: left;
border-bottom: 1px solid #999;
}
table td {
padding-right: 1ex;
}
dl dt {
margin : 0;
padding : 0 0 0 0;
}
dl dd {
margin : 0.3em 0 1.5em 10px;
}
div#identity-content hr {
margin: 1em 2em;
height: 1px;
background: #999;
border: 0;
}
div.FieldWidget div.StringWidget {
float: left;
width: 30%;
}
br.FieldWidget,
div.FieldWidget br {
display: none;
}

102
root/css/wcs-common.css Normal file
View File

@ -0,0 +1,102 @@
a {
color: #00a;
}
div.content {
margin-left: 5px;
}
div.TextWidget textarea,
div.StringWidget input,
div.PasswordWidget input {
border: 1px inset #ccc;
margin: 1px;
padding: 1px 2px;
}
div.StringWidget input[readonly=readonly] {
border: 1px solid #ccc;
background: #eee;
}
div.StringWidget input:focus,
div.PasswordWidget input:focus {
border: 2px solid #ccf;
margin: 0px;
}
div.AccountSettingWidget label {
padding-right: 2em;
}
div.SubmitWidget input, input[type=submit] {
margin-top: 1em;
border: 1px outset #ccc;
}
div.form div.title, form.quixote div.title {
font-weight: bold;
}
div.errornotice {
background: #fd6;
border: 1px solid #ffae15;
margin: 0em 1em 1em 1em;
padding: 5px;
}
div.error {
color: black;
font-weight: bold;
background: white url(warning.png) top left no-repeat;
padding-left: 20px;
}
div.buttons div.SubmitWidget,
div.buttons div.SubmitWidget div.content {
display: inline;
}
div.buttons br { display: none; }
div.widget {
margin-bottom: 0.5em;
}
input[type="submit"][name="submit"] {
font-weight: bold;
}
div.form pre {
overflow: scroll;
}
div#error h1 {
margin: 0;
}
div#error {
width: 40em;
max-width: 500px;
margin: 15% auto;
background: white;
border: 1px solid #999;
padding: 1em;
}
div.hint {
font-size: 80%;
}
span.required {
background: url(required.png) top right no-repeat;
padding: 0 0 0 12px;
font-size: 0%; /* hide star */
overflow: hidden;
color: white;
}
div.buttons {
margin-top: 1em;
}

119
root/css/wcs.css Normal file
View File

@ -0,0 +1,119 @@
@import url(wcs-common.css);
html, body {
font-family: sans-serif;
text-align: center;
background: #eee;
color: black;
}
div#page {
width: 800px;
margin: 2em auto;
text-align: justify;
background: white url(img/page.png) repeat-y;
color: black;
}
#top {
color: #09F;
background: #FFF url(img/top.jpg) no-repeat;
}
#top h1 {
margin: 0;
padding-top: 30px;
padding-left: 30px;
padding-bottom: 20px;
}
#footer {
background: #FFF url(img/footer.jpg) no-repeat;
color: #999;
text-align: center;
min-height: 30px;
}
div#main-content {
margin: 0 2em;
}
#steps {
height: 32px;
margin-bottom: 1em;
}
#steps ol {
list-style: none;
padding: 0 20px;
}
#steps li {
display: inline;
padding-right: 2em;
display: block;
float: left;
}
#steps span.marker {
font-size: 26px;
padding: 2px 9px;
font-weight: bold;
color: white;
text-align: center;
background: #ddd;
border: 1px solid #ddd;
}
#steps li.current span.marker {
background: #ffa500;
border: 1px solid #ffc400;
}
#steps span.label {
font-size: 90%;
}
#steps li.current span.label {
color: black;
}
#steps {
background: #f0f0f0;
color: #aaa;
}
form {
clear: both;
}
p#receiver {
margin: 0;
margin-left: 2em;
margin-top: -0.7em;
margin-bottom: 1em;
padding: 2px 5px;
border: 1px solid #ccc;
float: left;
background: #ffe;
}
dl {
clear: both;
}
p#login,
p#logout {
text-align: right;
}
h2#submitted {
margin-top: 2em;
color: #09F;
font-size: 130%;
}
ul li {
list-style: circle;
}

18
root/index.html Normal file
View File

@ -0,0 +1,18 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>w.c.s.</title>
<link rel="stylesheet" type="text/css" href="/css/wcs.css"/>
<style type="text/css">
p#wcs {
margin-top: 30%;
text-align: center;
font-weight: bold;
}
</style>
</head>
<body>
<p id="wcs">
w.c.s
</p>
</body>
</html>

29
setup.py Normal file
View File

@ -0,0 +1,29 @@
#! /usr/bin/env python
import os
import glob
import distutils.core
import distutils.sysconfig
import distutils.command
from quixote.ptl.qx_distutils import qx_build_py
distutils.core.setup(
name = 'wcs',
version = "0.0.0",
maintainer = "Frederic Peters",
maintainer_email = "fpeters@entrouvert.com",
url = "http://wcs.labs.libre-entreprise.org",
package_dir = { 'wcs': 'wcs' },
packages = ['wcs', 'wcs.admin', 'wcs.forms', 'wcs.liberty'],
cmdclass = {'build_py': qx_build_py},
scripts = ['wcs_scgi_server.py'],
data_files = [('share/wcs/web/', ( 'root/index.html',)),
('share/wcs/web/css', tuple(
glob.glob('root/css/*.css') + \
glob.glob('root/css/*.jpg') + \
glob.glob('root/css/*.png'))),
('share/wcs/web/css/img', tuple(
glob.glob('root/css/img/*.jpg') + \
glob.glob('root/css/img/*.png'))),
]
)

74
wcs/__init__.py Normal file
View File

@ -0,0 +1,74 @@
APP_DIR = '/var/tmp/wcs'
import os
import lasso
import __builtin__
import storage
import gettext, locale
gettext.install('wcs')
_1 = _
__builtin__.__dict__['_'] = lambda x: unicode(_1(str(x)), 'utf-8').encode('iso-8859-1')
__builtin__.__dict__['N_'] = lambda x: x
if not os.path.exists(APP_DIR):
os.mkdir(APP_DIR)
from quixote import enable_ptl, errors, get_session, redirect, get_session_manager
from quixote.publish import Publisher
from quixote.directory import Directory
enable_ptl()
import wcs.misc
import sessions
import admin.root
import forms.root
import liberty.root
class RootDirectory(Directory):
_q_exports = ["", "admin", "forms", "login", "logout", "liberty"]
def _q_index(self):
return redirect('forms')
def login(self):
return self.liberty.login()
def logout(self):
get_session_manager().expire_session()
redirect('.')
admin = admin.root.RootDirectory()
forms = forms.root.RootDirectory()
liberty = liberty.root.RootDirectory()
class WcsPublisher(Publisher):
def format_publish_error(self, exc):
if isinstance(exc, errors.AccessError) and hasattr(exc, 'render'):
return exc.render()
return Publisher.format_publish_error(self, exc)
def try_publish(self, request):
# called for each request, should set APP_DIR here for a kind of
# virtual hosting (set it from HTTP header, hostname, uri, whatever)
global app_dir
app_dir = wcs.APP_DIR + '/_TEST'
wcs.misc.filename = os.path.join(app_dir, 'config.pck')
wcs.misc.reload_cfg()
storage.FilesStorage.BASEDIR = app_dir
return Publisher.try_publish(self, request)
def create_publisher():
session_manager = sessions.PickleSessionManager()
return WcsPublisher(RootDirectory(),
session_manager = session_manager,
session_cookie_name = 'wcs',
display_exceptions = 'plain',
#error_email = "fpeters@entrouvert.com",
session_cookie_path = '/')

1
wcs/admin/__init__.py Normal file
View File

@ -0,0 +1 @@

316
wcs/admin/forms.ptl Normal file
View File

@ -0,0 +1,316 @@
import re
import os
from quixote import get_request, get_response, redirect
from quixote.directory import Directory
from quixote import errors
from quixote.html import htmltext
from quixote.util import dump_request
from menu import html_top, html_foot
import wcs.misc
from wcs.formdef import FormDef
from wcs import storage
from wcs.form import *
def ellipsize(s, length = 30):
if not s or len(s) < length:
return s
return s[:length-5] + ' (...)'
def get_user_roles():
l = []
for role in storage.get_storage().values('roles'):
l.append( (role.id, role.name) )
return l
field_types = [ ('string', _('Text (line)')),
('text', _('Text (block)')),
('bool', _('Boolean')),
('item', _('Item from list')),
('title', _('Title')),
('subtitle', _('Subtitle')),
]
class FieldWidget(CompositeWidget):
def __init__(self, name, value=None, **kwargs):
CompositeWidget.__init__(self, name, value, **kwargs)
if not value:
value = {}
self.value = value
self.add(StringWidget, 'name', value.get('name', ''))
self.add(SingleSelectWidget, 'type', value.get('type', ''), required=True,
options = field_types)
def render_content [html] (self):
name_widget = self.get_widget('name')
name_widget.render()
type_widget = self.get_widget('type')
type_widget.render()
def _parse(self, request):
for k in ('name', 'type'):
self.value[k] = self.get(k)
if not self.value.has_key('required'):
self.value['required'] = True
class FormDefUI:
def __init__(self, formdef):
self.formdef = formdef
def edit_form_ui [html] (self):
form = Form(enctype="multipart/form-data")
form.add(StringWidget, "id", title = _('Form Id'), required = True, size=30,
value = self.formdef.id)
form.add(StringWidget, "name", title = _('Form Name'), required = True, size=30,
value = self.formdef.name)
form.add(WidgetList, 'fields', title = _('Fields'), element_type = FieldWidget,
value = self.formdef.fields, add_element_label = _('Add Field'),
element_kwargs = {str('render_br'): False})
form.add(TextWidget, "receiver", title = _('Recipient'), required = True,
value = self.formdef.receiver, rows = 3, cols = 40)
form.add(StringWidget, "emailrcpt", title = _('Recipient Email'), size=30,
value = self.formdef.emailrcpt)
form.add(WidgetList, 'roles', title = _('Roles'), element_type = SingleSelectWidget,
hint = _('Only show this form to the given roles.'),
value = self.formdef.roles, add_element_label = _('Add Role'),
element_kwargs = {str('render_br'): False,
str('options'): [('', '---')] + get_user_roles()})
form.add_submit("submit", _("Submit"))
form.add_submit("cancel", _("Cancel"))
return form
def submit_form(self, form):
for f in ('id', 'name', 'emailrcpt', 'fields', 'receiver'):
setattr(self.formdef, f, form.get_widget(f).parse())
self.formdef.fields = [x for x in self.formdef.fields if x['name']] # remove empty names
self.formdef.roles = [x for x in form.get_widget('roles').parse() if x]
class FormDefFieldPage(Directory):
_q_exports = [""]
def __init__(self, formdef, field_no):
self.formdef = formdef
self.field_no = field_no
def form(self):
value = self.formdef.fields[self.field_no]
form = Form(enctype="multipart/form-data")
form.add(StringWidget, 'name', title = _('Name'), value = value.get('name', ''),
readonly = "readonly")
form.add(SingleSelectWidget, 'type', title = _('Type'),
value = value.get('type', ''), required=True,
options = field_types)
form.add(CheckboxWidget, 'required', title = _('Required'),
value = value.get('required', False))
if value.get('type') == 'item':
form.add(WidgetList, 'items', title = _('Items'), element_type = StringWidget,
value = value.get('items', []), required = True,
element_kwargs = {str('render_br'): False})
form.add_submit("submit", _("Submit"))
form.add_submit("cancel", _("Cancel"))
return form
def _q_index [html] (self):
value = self.formdef.fields[self.field_no]
form = self.form()
redo = False
if form.get_widget('cancel').parse():
return redirect('../fields')
if form.get_widget('items') and form.get_widget('items').get_widget('add_element').parse():
form.clear_errors()
redo = True
if redo or not form.is_submitted() or form.has_errors():
html_top('forms', 'Form - %s' % self.formdef.name)
'<h2>%s - %s</h2>' % (self.formdef.name, value['name'])
form.render()
html_foot()
else:
self.submit(form)
if form.get_widget('items') is None and value['type'] == 'item':
return redirect('.')
return redirect('..')
def submit(self, form):
value = self.formdef.fields[self.field_no]
for f in ('name', 'type', 'required', 'items'):
w = form.get_widget(f)
if not w:
if value.has_key(f):
del value[f]
continue
value[f] = w.parse()
storage.get_storage().store(self.formdef)
class FormDefPage(Directory):
_q_exports = ["fields", "edit", "delete", "listing", "csv"]
def __init__(self, component):
self.formdef = storage.get_storage().retrieve('formdefs', component)
self.formdefui = FormDefUI(self.formdef)
def fields [html] (self):
html_top('forms', 'Form - %s' % self.formdef.name)
'<h2>%s</h2>' % self.formdef.name
'<table>'
for i, field in enumerate(self.formdef.fields):
'<tr>'
if field['type'] in ('subtitle', 'title'):
'<td colspan="4"><strong>%s</strong></td>' % field['name']
else:
type = [x[1] for x in field_types if x[0] == field['type']][0]
if field.has_key('required') and field['required']:
required = ''
else:
required = _('optional')
'<td>%s</td> <td>%s</td> <td>%s</td> <td><a href="field-%d">%s</a></td>' % (
field['name'], type, required, i, _('Edit'))
'</tr>'
'</table>'
html_foot()
def listing [html] (self):
html_top('forms', 'Form - %s' % self.formdef.name)
names = 'form-' + self.formdef.id
'<h2>%s</h2>' % self.formdef.name
"""<a href="csv">%s</a>""" % _('Export to CSV format')
"""<table id="listing"><thead><tr>"""
for f in self.formdef.fields:
if f['type'] in ('title', 'subtitle'):
continue
"<th>%s</th>" % f['name']
"</tr></thead>"
"<tbody>"
for k in storage.get_storage().keys(names):
filled = storage.get_storage().retrieve(names, k)
"<tr>"
for f in self.formdef.fields:
if f['type'] in ('title', 'subtitle'):
continue
"<td>%s</td>" % filled.data.get(f['name'], '')
"</tr>"
"</tbody></table>"
html_foot()
def csv [plain] (self):
names = 'form-' + self.formdef.id
def fixcsv(s):
if not s: return s
return s.replace('\n', ' ').replace(';', ',')
for k in storage.get_storage().keys(names):
filled = storage.get_storage().retrieve(names, k)
';'.join([fixcsv(filled.data.get(x['name'])) for x in self.formdef.fields]) + '\r\n'
response = get_response()
response.set_content_type('text/csv')
def edit [html] (self):
form = self.formdefui.edit_form_ui()
if form.get_widget('cancel').parse():
return redirect('.')
redo = False
if form.get_widget('fields').get_widget('add_element').parse():
form.clear_errors()
redo = True
if redo or not form.is_submitted() or form.has_errors():
form.get_widget('id').attrs[str('readonly')] = str('readonly')
form.get_widget('id').required = False
html_top('forms', title = _('Edit Form'))
"""<h2>%s</h2>""" % _('Edit Form')
form.render()
html_foot()
else:
self.formdefui.submit_form(form)
storage.get_storage().store(self.formdef)
return redirect('..')
def delete [html] (self):
form = Form(enctype="multipart/form-data")
form.widgets.append(HtmlWidget("""<p>%s</p>""" % _(
"You are about to irrevocably delete this form.")))
form.add_submit("submit", _("Submit"))
form.add_submit("cancel", _("Cancel"))
if form.get_widget('cancel').parse():
return redirect('..')
if not form.is_submitted() or form.has_errors():
html_top('forms', title = _('Delete Form'))
"""<h2>%s %s</h2>""" % (_('Deleting Form:'), self.formdef.name)
form.render()
html_foot()
else:
storage.get_storage().remove_id('formdefs', self.formdef.id)
storage.get_storage().remove_all('form-%s' % self.formdef.id)
return redirect('..')
def _q_lookup(self, component):
if not component.startswith('field-'):
raise TraversalError()
try:
field_no = int(component[6:])
except ValueError:
raise TraversalError()
return FormDefFieldPage(self.formdef, field_no)
class FormsDirectory(Directory):
_q_exports = ["", "new"]
def _q_index [html] (self):
wcs.misc.reload_cfg()
html_top('forms', title = 'Forms')
"""<ul id="nav-forms-admin">
<li><a href="new">%s</a></li>
</ul>""" % _('New')
for k in storage.get_storage().keys('formdefs'):
formdef = storage.get_storage().retrieve('formdefs', k)
"""<div class="form">"""
"<h3>%s</h3>" % formdef.name
"""<span class="data">
<span class="name">%s</span>
</span>""" % ellipsize(formdef.receiver, 50)
"""<span class="cmds"> [ """
"""<a href="%s/edit">%s</a> - """ % (k, _('Edit'))
"""<a href="%s/fields">%s</a> - """ % (k, _('Fields'))
"""<a href="%s/delete">%s</a> - """ % (k, _('Delete'))
"""<a href="%s/listing">%s</a> """ % (k, _('Listing'))
"]</span></p></div>"
html_foot()
def new [html] (self):
formdef = FormDef()
formdefui = FormDefUI(formdef)
form = formdefui.edit_form_ui()
if form.get_widget('cancel').parse():
return redirect('.')
redo = False
if form.get_widget('fields').get_widget('add_element').parse():
form.clear_errors()
redo = True
if redo or not form.is_submitted() or form.has_errors():
html_top('forms', title = _('New Form'))
"""<h2>%s</h2>""" % _('New Form')
form.render()
html_foot()
else:
formdefui.submit_form(form)
storage.get_storage().store(formdef)
return redirect(formdef.id + '/fields')
def _q_lookup(self, component):
return FormDefPage(component)

51
wcs/admin/menu.ptl Normal file
View File

@ -0,0 +1,51 @@
import quixote
items = [
('forms', N_('Forms')),
('users', N_('Users')),
('roles', N_('Roles')),
('settings', N_('Settings')),
('/', N_('WCS Form Server'))]
def header_menu [html] (selected = None):
s = ["""<ul id="menu">\n"""]
base_url = quixote.get_request().get_path(-1)
for k, v in items:
if k == '/':
continue # skip root
if k == selected:
s.append('<li class="active">')
else:
s.append('<li>')
s.append('<a href="%s/%s/">%s</a></li>\n' % (base_url, k, _(v)))
s.append('</ul>\n')
return ''.join(s)
def user_info [html] ():
return ''
def html_top [html] (section, title = None):
subtitle = ''
for s in items:
if s[0] == section:
subtitle = _(s[1])
if not title:
title = ''
else:
title = ' - ' + title
return """<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>%s%s</title>
<link rel="stylesheet" type="text/css" href="/css/wcs-admin.css"/>
</head>
<body>
<div id="header">%s</div>
<div id="main-content">
%s
<h1>%s</h1>
""" % (_('WCS Administration'), title, header_menu(section), user_info(), subtitle)
def html_foot [html] ():
return """</div><div id="footer"><p id="lasso">Powered by Lasso</p></div></body></html>"""

126
wcs/admin/roles.ptl Normal file
View File

@ -0,0 +1,126 @@
import re
import os
from quixote import get_request, get_response, redirect
from quixote.directory import Directory
from quixote import errors
from quixote.html import htmltext
from quixote.util import dump_request
from menu import html_top, html_foot
import wcs.misc
from wcs.roles import Role
from wcs import storage
from wcs.form import *
class RoleUI:
def __init__(self, role):
self.role = role
def form_new(self):
form = Form(enctype="multipart/form-data")
form.add(StringWidget, "id", title = _('Role Id'), required = True, size=30,
value = self.role.id)
form.add(StringWidget, "name", title = _('Role Name'), required = True, size=30,
value = self.role.name)
form.add_submit("submit", _("Submit"))
form.add_submit("cancel", _("Cancel"))
return form
def form_edit(self):
form = Form(enctype="multipart/form-data")
form.add(StringWidget, "id", title = _('Role Id'), required = False, size=30,
value = self.role.id, readonly = 'readonly')
form.add(StringWidget, "name", title = _('Role Name'), required = True, size=30,
value = self.role.name)
form.add_submit("submit", _("Submit"))
form.add_submit("cancel", _("Cancel"))
return form
def submit_form(self, form):
for f in ('id', 'name'):
setattr(self.role, f, form.get_widget(f).parse())
class RolePage(Directory):
_q_exports = ["edit", "delete"]
def __init__(self, component):
self.role = storage.get_storage().retrieve('roles', component)
self.role_ui = RoleUI(self.role)
def edit [html] (self):
form = self.role_ui.form_edit()
if form.get_widget('cancel').parse():
return redirect('.')
if not form.is_submitted() or form.has_errors():
html_top('roles', title = _('Edit Role'))
"""<h2>%s</h2>""" % _('Edit Role')
form.render()
html_foot()
else:
self.role_ui.submit_form(form)
storage.get_storage().store(self.role)
return redirect('..')
def delete [html] (self):
form = Form(enctype="multipart/form-data")
form.widgets.append(HtmlWidget("""<p>%s</p>""" % _(
"You are about to irrevocably delete this role.")))
form.add_submit("submit", _("Submit"))
form.add_submit("cancel", _("Cancel"))
if form.get_widget('cancel').parse():
return redirect('..')
if not form.is_submitted() or form.has_errors():
html_top('roles', title = _('Delete Role'))
"""<h2>%s %s</h2>""" % (_('Deleting Role:'), self.role.name)
form.render()
html_foot()
else:
storage.get_storage().remove_id('roles', self.role.id)
return redirect('..')
class RolesDirectory(Directory):
_q_exports = ["", "new"]
def _q_index [html] (self):
wcs.misc.reload_cfg()
html_top('roles', title = 'Roles')
"""<ul id="nav-roles-admin">
<li><a href="new">%s</a></li>
</ul>""" % _('New')
for k in storage.get_storage().keys('roles'):
role = storage.get_storage().retrieve('roles', k)
"""<div class="form">"""
"<h3>%s</h3>" % role.name
"""<span class="cmds"> [ """
"""<a href="%s/edit">%s</a> - """ % (k, _('Edit'))
"""<a href="%s/delete">%s</a> """ % (k, _('Delete'))
"]</span></p></div>"
html_foot()
def new [html] (self):
role = Role()
role_ui = RoleUI(role)
form = role_ui.form_new()
if form.get_widget('cancel').parse():
return redirect('.')
if not form.is_submitted() or form.has_errors():
html_top('roles', title = _('New Role'))
"""<h2>%s</h2>""" % _('New Role')
form.render()
html_foot()
else:
role_ui.submit_form(form)
storage.get_storage().store(role)
return redirect('.')
def _q_lookup(self, component):
return RolePage(component)

43
wcs/admin/root.ptl Normal file
View File

@ -0,0 +1,43 @@
from quixote import get_response, get_session
from quixote.directory import Directory, AccessControlled
import settings
import forms
import roles
import users
from menu import html_top, html_foot
import wcs.errors
def gpl [html] ():
"""<p>This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option)
any later version.</p>
<p>This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.</p>
<p>You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA.</p>
"""
class RootDirectory(AccessControlled, Directory): #, AccessControlled):
_q_exports = ["", "settings", "forms", "roles", "users"]
def _q_index [html] (self):
html_top('/')
gpl()
html_foot()
settings = settings.SettingsDirectory()
forms = forms.FormsDirectory()
roles = roles.RolesDirectory()
users = users.UsersDirectory()

303
wcs/admin/settings.ptl Normal file
View File

@ -0,0 +1,303 @@
import cPickle
import re
import os
import lasso
from quixote import get_request, get_response, redirect
from quixote.directory import Directory
from quixote import errors
from quixote.form import *
from quixote.form.widget import Widget
from quixote.html import htmltext
from quixote.util import dump_request
from menu import html_top, html_foot
import wcs.misc
from wcs.form import *
class LibertyIDPDir(Directory):
_q_exports = ["", "new"]
def _q_index [html] (self):
wcs.misc.reload_cfg()
html_top('settings', title = _('Identity Providers'))
"<h2>%s</h2>" % _('Identity Providers')
"""<ul id="nav-idp-admin"> <li><a href="new">%s</a></li> </ul>""" % _('New')
'<div id="idp-list">'
for kidp, idp in wcs.misc.cfg.get('idp', {}).items():
p = lasso.Provider(lasso.PROVIDER_ROLE_IDP, idp['metadata'], idp['publickey'], None)
"""<div class="idp">"""
"<h3>%s</h3>" % wcs.misc.get_provider_label(p)
'<p><span class="data">%s</span>' % p.providerId
"""<span class="cmds">[ """
"""<a href="%s">%s</a> - """ % (kidp, _('View'))
"""<a href="%s/edit">%s</a> - """ % (kidp, _('Edit'))
"""<a href="%s/delete">%s</a> """ % (kidp, _('Delete'))
"""]</span></p></div>"""
'</div>'
html_foot()
def _q_lookup(self, component):
wcs.misc.reload_cfg()
return LibertyIDPUI(component)
def new [html] (self):
form = Form(enctype="multipart/form-data")
form.add(FileWidget, "metadata", title = _("Metadata"), required=True)
form.add(FileWidget, "publickey", title = _("Public Key"), required=True)
form.add(FileWidget, "cacertchain", title = _("CA Certificate Chain"), required=False)
form.add_submit("submit", _("Submit"))
if not form.is_submitted() or form.has_errors():
html_top('settings', title = _('New Identity Provider'))
"<h2>%s</h2>" % _('New Identity Provider')
form.render()
html_foot()
else:
self.submit_new(form)
def submit_new(self, form, key_provider_id = None):
wcs.misc.reload_cfg()
d = wcs.misc.cfg.get('idp', {})
wcs.misc.cfg['idp'] = d
metadata, publickey, cacertchain = None, None, None
if form.get_widget('metadata').parse():
metadata = form.get_widget('metadata').parse().fp.read()
if form.get_widget('publickey').parse():
publickey = form.get_widget('publickey').parse().fp.read()
if form.get_widget('cacertchain').parse():
cacertchain = form.get_widget('cacertchain').parse().fp.read()
if not key_provider_id:
provider_id = re.findall(r'providerID="(.*?)"', metadata)[0]
key_provider_id = provider_id.replace(str('://'), str('-')).replace(str('/'), str('-'))
metadata_fn = os.path.join(wcs.app_dir, 'idp-%s-metadata.xml' % key_provider_id)
publickey_fn = os.path.join(wcs.app_dir, 'idp-%s-publickey.pem' % key_provider_id)
cacertchain_fn = os.path.join(wcs.app_dir, 'idp-%s-cacertchain.pem' % key_provider_id)
if metadata:
file(metadata_fn, 'w').write(metadata)
if publickey:
file(publickey_fn, 'w').write(publickey)
if cacertchain:
file(cacertchain_fn, 'w').write(cacertchain)
wcs.misc.cfg['idp'][key_provider_id] = {
'metadata': metadata_fn,
'publickey': publickey_fn,
'cacertchain': cacertchain_fn
}
wcs.misc.write_cfg()
redirect('.')
class LibertyIDPUI(Directory):
_q_exports = ["", "delete", "edit"]
def __init__(self, component):
self.idp = wcs.misc.cfg['idp'][component]
self.idpk = component
def _q_index [html] (self):
html_top('settings', title = _('Identity Provider'))
p = lasso.Provider(lasso.PROVIDER_ROLE_SP,
self.idp['metadata'], self.idp['publickey'], self.idp.get('cacertchain', None))
'<h2>%s - %s</h2>' % (_('Identity Provider'), p.providerId)
'<div class="form">'
'<h3>%s</h3>' % _('Metadata')
'<pre>'
file(self.idp['metadata']).read()
'</pre>'
'</div>'
html_foot()
def edit [html] (self):
form = Form(enctype="multipart/form-data")
form.add(FileWidget, "metadata", title = _("Metadata"))
form.add(FileWidget, "publickey", title = _("Public Key"))
form.add(FileWidget, "cacertchain", title = _("CA Certificate Chain"))
form.add_submit("submit", _("Submit"))
if not form.is_submitted() or form.has_errors():
html_top('settings', title = _('Edit Identity Provider'))
"<h2>%s</h2>" % _('Edit Identity Provider')
form.render()
html_foot()
else:
t = LibertyIDPDir()
t.submit_new(form, self.idpk)
def delete [html] (self):
p = lasso.Provider(lasso.PROVIDER_ROLE_SP, self.idp['metadata'], self.idp['publickey'], None)
form = Form(enctype="multipart/form-data")
form.widgets.append(HtmlWidget("<p>%s</p>" % _(
"You are about to irrevocably remove this identity provider.")))
form.add_submit("submit", _("Submit"))
form.add_submit("cancel", _("Cancel"))
if form.get_widget('cancel').parse():
return redirect('..')
if not form.is_submitted() or form.has_errors():
html_top('settings', title = _('Identity Provider'))
"""<h2>%s %s</h2>""" % (_('Deleting'), p.providerId)
form.render()
html_foot()
else:
self.delete_submitted()
def delete_submitted(self):
del wcs.misc.cfg['idp'][self.idpk]
metadata_fn = os.path.join(wcs.app_dir, 'idp-%s-metadata.xml' % self.idpk)
publickey_fn = os.path.join(wcs.app_dir, 'idp-%s-publickey.pem' % self.idpk)
cacertchain_fn = os.path.join(wcs.app_dir, 'idp-%s-cacertchain.pem' % self.idpk)
os.unlink(metadata_fn)
os.unlink(publickey_fn)
os.unlink(cacertchain_fn)
wcs.misc.write_cfg()
return redirect('..')
class SettingsDirectory(Directory):
_q_exports = ["", "liberty_idp", "liberty_sp", ("metadata.xml", "metadata")]
def _q_index [html] (self):
wcs.misc.reload_cfg()
html_top('settings', title = 'Settings')
"""<h2>Liberty Alliance</h2>
<dl>"""
"""<dt><a href="liberty_sp">%s</a></dt> <dd>%s</dd>""" % (
_('Service Provider'), _('Configure Liberty parameters'))
if wcs.misc.cfg.has_key(str('sp')):
"""<dt><a href="metadata.xml">%s</a></dt> <dd>%s</dd>""" % (
_('Service Provider Metadata'), _('Download Service Provider Metadata file'))
"""<dt><a href="liberty_idp">%s</a></dt> <dd>%s</dd> </dl>""" % (
_('Identity Providers'), _('Add and remove identity providers'))
"</dl>"
html_foot()
def liberty_sp [html] (self):
wcs.misc.reload_cfg()
base_url = wcs.misc.cfg.get("sp", {}).get('base_url', None)
common_domain = wcs.misc.cfg.get("sp", {}).get("common_domain", None)
req = get_request()
if not common_domain and not base_url: # probably the first time we get here
domain_parts = req.get_server().split(str('.'))
if len(domain_parts) >= 2:
common_domain = '.'.join(domain_parts[-2:])
if not base_url:
base_url = "%s://%s%s" % (req.get_scheme(), req.get_server(),
req.get_path(-1).replace(str('/admin'), str('/liberty')))
form = Form(enctype="multipart/form-data")
form.add(StringWidget, "providerid", title=_("Provider ID"), size=50, required=True,
value = wcs.misc.cfg.get("sp", {}).get('providerid', None))
form.add(StringWidget, "base_url", title=_("Base URL"), size=50, required=True,
value = base_url)
form.add(StringWidget, "organization_name", title=_("Organization Name"), size=50,
value = wcs.misc.cfg.get("sp", {}).get('organization_name', None))
form.add(FileWidget, "privatekey", title = _("Private Key"))
form.add(FileWidget, "publickey", title = _("Public Key"))
form.add(StringWidget, "common_domain",
title = _("Identity Provider Introduction, Common Domain"),
hint = _("Disabled if empty"), value = common_domain)
form.add_submit("submit", "Submit")
if not form.is_submitted() or form.has_errors():
html_top('settings', title = _('Service Provider Configuration'))
'<h2>%s</h2>' % _('Service Provider Configuration')
form.render()
html_foot()
else:
self.liberty_sp_save(form)
redirect('.')
def liberty_sp_save(self, form):
wcs.misc.reload_cfg()
if not wcs.misc.cfg.has_key('sp'):
wcs.misc.cfg['sp'] = {}
for k in ('providerid', 'base_url', 'organization_name', 'common_domain'):
wcs.misc.cfg['sp'][k] = form.get_widget(k).parse()
privatekey_fn = os.path.join(wcs.app_dir, 'private-key.pem')
value = form.get_widget('privatekey').parse()
if value:
file(privatekey_fn, 'w').write(value.fp.read())
if os.path.exists(privatekey_fn):
wcs.misc.cfg['sp']['privatekey'] = privatekey_fn
publickey_fn = os.path.join(wcs.app_dir, 'public-key.pem')
value = form.get_widget('publickey').parse()
if value:
file(publickey_fn, 'w').write(value.fp.read())
if os.path.exists(publickey_fn):
wcs.misc.cfg['sp']['publickey'] = publickey_fn
metadata_fn = os.path.join(wcs.app_dir, 'metadata.xml')
file(metadata_fn, 'w').write(self.get_metadata())
wcs.misc.cfg['sp']['metadata'] = metadata_fn
wcs.misc.write_cfg()
def get_metadata(self):
return """<?xml version="1.0"?>
<EntityDescriptor
providerID="%(providerid)s"
xmlns="urn:liberty:metadata:2003-08">
<SPDescriptor protocolSupportEnumeration="urn:liberty:iff:2003-08">
<SoapEndpoint>%(base_url)s/soapEndpoint</SoapEndpoint>
<SingleLogoutServiceURL>%(base_url)s/singleLogout</SingleLogoutServiceURL>
<SingleLogoutServiceReturnURL>%(base_url)s/singleLogoutReturn</SingleLogoutServiceReturnURL>
<FederationTerminationServiceURL>%(base_url)s/federationTermination</FederationTerminationServiceURL>
<FederationTerminationServiceReturnURL>%(base_url)s/federationTerminationReturn</FederationTerminationServiceReturnURL>
<FederationTerminationNotificationProtocolProfile>http://projectliberty.org/profiles/fedterm-idp-soap</FederationTerminationNotificationProtocolProfile>
<FederationTerminationNotificationProtocolProfile>http://projectliberty.org/profiles/fedterm-idp-http</FederationTerminationNotificationProtocolProfile>
<FederationTerminationNotificationProtocolProfile>http://projectliberty.org/profiles/fedterm-sp-soap</FederationTerminationNotificationProtocolProfile>
<FederationTerminationNotificationProtocolProfile>http://projectliberty.org/profiles/fedterm-sp-http</FederationTerminationNotificationProtocolProfile>
<SingleLogoutProtocolProfile>http://projectliberty.org/profiles/slo-idp-soap</SingleLogoutProtocolProfile>
<SingleLogoutProtocolProfile>http://projectliberty.org/profiles/slo-idp-http</SingleLogoutProtocolProfile>
<SingleLogoutProtocolProfile>http://projectliberty.org/profiles/slo-sp-soap</SingleLogoutProtocolProfile>
<SingleLogoutProtocolProfile>http://projectliberty.org/profiles/slo-sp-http</SingleLogoutProtocolProfile>
<RegisterNameIdentifierServiceURL>%(base_url)s/registerNameIdentifier</RegisterNameIdentifierServiceURL>
<RegisterNameIdentifierServiceReturnURL>%(base_url)s/registerNameIdentifierReturn</RegisterNameIdentifierServiceReturnURL>
<RegisterNameIdentifierProtocolProfile>http://projectliberty.org/profiles/rni-idp-soap</RegisterNameIdentifierProtocolProfile>
<RegisterNameIdentifierProtocolProfile>http://projectliberty.org/profiles/rni-idp-http</RegisterNameIdentifierProtocolProfile>
<RegisterNameIdentifierProtocolProfile>http://projectliberty.org/profiles/rni-sp-soap</RegisterNameIdentifierProtocolProfile>
<RegisterNameIdentifierProtocolProfile>http://projectliberty.org/profiles/rni-sp-http</RegisterNameIdentifierProtocolProfile>
<AuthnRequestsSigned>true</AuthnRequestsSigned>
<AssertionConsumerServiceURL id="AssertionConsumerServiceURL1" isDefault="true">%(base_url)s/assertionConsumer</AssertionConsumerServiceURL>
</SPDescriptor>
<Organization>
<OrganizationName>%(organization_name)s</OrganizationName>
</Organization>
</EntityDescriptor>""" % wcs.misc.cfg['sp']
def metadata [plain] (self):
wcs.misc.reload_cfg()
response = get_response()
response.set_content_type('text/xml')
self.get_metadata()
liberty_idp = LibertyIDPDir()

191
wcs/admin/users.ptl Normal file
View File

@ -0,0 +1,191 @@
import random
import re
import os
from quixote import get_request, get_response, redirect
from quixote.directory import Directory
from quixote import errors
from quixote.html import htmltext
from quixote.util import dump_request
from menu import html_top, html_foot
import wcs.misc
from wcs.users import User
from wcs import storage
from wcs.form import *
def get_user_roles():
l = []
for role in storage.get_storage().values('roles'):
l.append( (role.id, role.name) )
return l
class UserUI:
def __init__(self, user):
self.user = user
def form_new(self):
form = Form(enctype="multipart/form-data")
form.add(StringWidget, "id", title = _('User Id'), required = True, size=30,
value = self.user.id)
form.add(StringWidget, "name", title = _('User Name'), required = True, size=30,
value = self.user.name)
form.add(StringWidget, "email", title = _('Email'), required = False, size=30,
value = self.user.email)
form.add(WidgetList, 'roles', title = _('Roles'), element_type = SingleSelectWidget,
value = self.user.roles, add_element_label = _('Add Role'),
element_kwargs = {str('render_br'): False,
str('options'): [('', '---')] + get_user_roles()})
form.add_submit("submit", _("Submit"))
form.add_submit("cancel", _("Cancel"))
return form
def form_edit(self):
form = Form(enctype="multipart/form-data")
form.add(StringWidget, "id", title = _('User Id'), required = False, size=30,
value = self.user.id, readonly = 'readonly')
form.add(StringWidget, "name", title = _('User Name'), required = True, size=30,
value = self.user.name)
form.add(StringWidget, "email", title = _('Email'), required = False, size=30,
value = self.user.email)
form.add(WidgetList, 'roles', title = _('Roles'), element_type = SingleSelectWidget,
value = self.user.roles, add_element_label = _('Add Role'),
element_kwargs = {str('render_br'): False,
str('options'): [('', '---')] + get_user_roles()})
form.add_submit("cancel", _("Cancel"))
form.add_submit("submit", _("Submit"))
return form
def submit_form(self, form):
for f in ('id', 'name', 'email'):
setattr(self.user, f, form.get_widget(f).parse())
self.user.roles = [x for x in form.get_widget('roles').parse() if x]
class UserPage(Directory):
_q_exports = ["edit", "delete", "token"]
def __init__(self, component):
self.user = storage.get_storage().retrieve('users', component)
self.user_ui = UserUI(self.user)
def edit [html] (self):
form = self.user_ui.form_edit()
if form.get_widget('cancel').parse():
return redirect('..')
if not form.is_submitted() or form.has_errors():
html_top('users', title = _('Edit User'))
"""<h2>%s</h2>""" % _('Edit User')
form.render()
html_foot()
else:
self.user_ui.submit_form(form)
storage.get_storage().store(self.user)
return redirect('..')
def delete [html] (self):
form = Form(enctype="multipart/form-data")
form.widgets.append(HtmlWidget("""<p>%s</p>""" % _(
"You are about to irrevocably delete this user.")))
form.add_submit("cancel", _("Cancel"))
form.add_submit("submit", _("Submit"))
if form.get_widget('cancel').parse():
return redirect('..')
if not form.is_submitted() or form.has_errors():
html_top('users', title = _('Delete User'))
"""<h2>%s %s</h2>""" % (_('Deleting User:'), self.user.name)
form.render()
html_foot()
else:
storage.get_storage().remove_id('users', self.user.id)
return redirect('..')
def token [html] (self):
form = Form(enctype="multipart/form-data")
form.add_submit("cancel", _("Cancel"))
form.add_submit("submit", _("Generate"))
request = get_request()
if request.form.has_key('cancel') or request.form.has_key('done'):
return redirect('..')
if not form.is_submitted() or form.has_errors():
html_top('users', title = _('Identification Token'))
"<h2>%s</h2>" % _('Identification Token')
"<p>bla bla bla (explanation)</p>"
if self.user.identification_token:
"<p>%s</p>" % _('Note that user has already been issued an identification token: %s') % self.user.identification_token
form.render()
html_foot()
else:
if request.form.has_key('submit'):
html_top('users', title = _('Identification Token'))
token = '-'.join(['%04d' % random.randint(1, 9999) for x in range(4)])
self.user.identification_token = str(token)
storage.get_storage().store(self.user)
"<p>"
_('Identification Token for %s:') % self.user.name
" %s</p>" % self.user.identification_token
form = Form(enctype="multipart/form-data")
form.add_submit('done', _('Done'))
if self.user.email:
form.add_submit("submit-email", _("Send by email"))
form.render()
html_foot()
else:
pass # send by email
class UsersDirectory(Directory):
_q_exports = ["", "new"]
def _q_index [html] (self):
wcs.misc.reload_cfg()
html_top('users', title = 'Users')
"""<ul id="nav-users-admin">
<li><a href="new">%s</a></li>
</ul>""" % _('New')
users = storage.get_storage().values('users')
users.sort(lambda x,y: cmp(x.name, y.name))
for user in users:
"""<div class="form">"""
"<h3>%s</h3>" % user.name
"""<span class="cmds"> [ """
if not user.name_identifiers:
"""<a href="%s/token">%s</a> - """ % (user.id, _('Identification Token'))
"""<a href="%s/edit">%s</a> - """ % (user.id, _('Edit'))
"""<a href="%s/delete">%s</a> """ % (user.id, _('Delete'))
"]</span></p></div>"
html_foot()
def new [html] (self):
user = User()
user_ui = UserUI(user)
form = user_ui.form_new()
if form.get_widget('cancel').parse():
return redirect('.')
if not form.is_submitted() or form.has_errors():
html_top('users', title = _('New User'))
"""<h2>%s</h2>""" % _('New User')
form.render()
html_foot()
else:
user_ui.submit_form(form)
storage.get_storage().store(user)
return redirect('.')
def _q_lookup(self, component):
return UserPage(component)

14
wcs/errors.ptl Normal file
View File

@ -0,0 +1,14 @@
import quixote
from quixote.errors import *
class AccessUnauthorizedError(AccessError):
def render(self):
session = quixote.get_session()
request = quixote.get_request()
session.after_url = request.environ['SCRIPT_NAME'] + request.environ['PATH_INFO']
quixote.redirect('/wcs/login')
class AccessForbiddenError(AccessError):
def render [html] (self):
"AccessForbiddenError"

26
wcs/form.py Normal file
View File

@ -0,0 +1,26 @@
from quixote.form import *
from quixote.html import htmltext
QuixoteForm = Form
Widget.REQUIRED_ERROR = _('required field')
class Form(QuixoteForm):
ERROR_NOTICE = htmltext('<div class="errornotice">%s</div>' % _(
"There were errors processing your form. See below for details."))
def add(self, widget_class, name, *args, **kwargs):
if kwargs and not kwargs.has_key('render_br'):
kwargs['render_br'] = False
return QuixoteForm.add(self, widget_class, name, *args, **kwargs)
def _render_submit_widgets(self):
return htmltext('<div class="buttons">%s</div>' % QuixoteForm._render_submit_widgets(self))
class HtmlWidget:
def __init__(self, string):
self.string = string
def render(self):
return self.string

13
wcs/formdata.py Normal file
View File

@ -0,0 +1,13 @@
import storage
class FormData(storage.Storable):
key = 'id'
names = 'forms'
user_id = None
def __init__(self):
self.id = None
self.data = None
self.receipt_date = None
self.user_id = None

103
wcs/formdef.py Normal file
View File

@ -0,0 +1,103 @@
from email.MIMEText import MIMEText
from email.MIMEMultipart import MIMEMultipart
import smtplib
try:
import docutils
import docutils.core
except ImportError:
docutils = None
from quixote import get_request
import storage
from form import *
widget_classes = {
'string': StringWidget,
'text': TextWidget,
'bool': CheckboxWidget,
'item': SingleSelectWidget,
}
class FormDef(storage.Storable):
key = 'id'
names = 'formdefs'
receiver = None
roles = None
def __init__(self):
self.id = ''
self.name = ''
self.fields = []
self.receiver = ''
self.emailrcpt = ''
self.roles = []
def create_form(self): # XXX: merge create_form and create_view_form
form = Form(enctype = "multipart/form-data")
for i, field in enumerate(self.fields):
if field['type'] == 'title':
form.widgets.append(HtmlWidget(htmltext("<h3>%s</h3>" % field['name'])))
continue
if field['type'] == 'subtitle':
form.widgets.append(HtmlWidget(htmltext("<h4>%s</h4>" % field['name'])))
continue
kwargs = {'required': field.get('required', True)}
if field['type'] == 'item':
kwargs['options'] = field.get('items', ['---'])
form.add(widget_classes[field['type']], "f%d" % i, title = field['name'],
**kwargs)
return form
def create_view_form(self, dict = {}):
form = Form(enctype = "multipart/form-data")
for i, field in enumerate(self.fields):
kwargs = {}
if field['type'] == 'title':
form.widgets.append(HtmlWidget(htmltext("<h3>%s</h3>" % field['name'])))
continue
if field['type'] == 'subtitle':
form.widgets.append(HtmlWidget(htmltext("<h4>%s</h4>" % field['name'])))
continue
if field['type'] == 'item':
kwargs['options'] = field.get('items', ['---'])
form.add(widget_classes[field['type']], "f%d" % i, title = field['name'],
readonly = "readonly", value = dict.get(field['name'], ''), **kwargs)
return form
def get_data(self, form):
d = {}
for i, field in enumerate(self.fields):
if field['type'] in ('title', 'subtitle'):
continue
d[field['name']] = form.get_widget('f%d' % i).parse()
return d
def notify_new(self, formdata):
if not self.emailrcpt:
return
url = '%s/%s/status' % (get_request().get_url(1), formdata.id)
mail_body = _("""Hi,
A new form has been submitted on the website, you can consult it with this
link: %(url)s
""") % {'url': url}
try:
htmlmail = docutils.core.publish_string(mail_body, writer_name="html")
except IOError:
htmlmail = None
msg = MIMEText(mail_body, _charset = 'iso-8859-15')
msg['Subject'] = _("A new form has been submitted")
msg['To'] = self.emailrcpt
msg['From'] = 'WCS <noreply@entrouvert.com>'
msg['X-Mailer'] = 'w.c.s.'
s = smtplib.SMTP()
s.connect()
s.sendmail('noreply@entrouvert.com', [msg['To']], msg.as_string())
s.close()

1
wcs/forms/__init__.py Normal file
View File

@ -0,0 +1 @@

297
wcs/forms/root.ptl Normal file
View File

@ -0,0 +1,297 @@
import time
import os
from quixote import get_response, get_session, redirect
from quixote.directory import Directory
import wcs.errors
from wcs.form import *
import wcs.misc
import wcs.formdata
from wcs import storage
def html_top [html] (title = None):
return """<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>%s</title>
<link rel="stylesheet" type="text/css" href="/css/wcs.css"/>
</head>
<body>
<div id="page">
<div id="top">
<h1>%s</h1>
</div>
<div id="main-content">""" % (title, title)
def html_foot [html] ():
return """</div><div id="footer"><p id="lasso">Powered by Lasso</p></div></div></body></html>"""
class FormStatusPage(Directory):
_q_exports = ["", "status"]
def __init__(self, formdef, component):
self.formdef = formdef
try:
self.filled = storage.get_storage().retrieve("form-" + formdef.id, component)
except KeyError:
raise wcs.errors.TraversalError()
def _q_index [html] (self):
session = get_session()
if not session or self.filled.user_id != session.user:
raise wcs.errors.AccessError()
html_top(self.formdef.name + ' - ' + self.filled.id)
tm = time.strftime(str("%Y-%m-%d %H:%M"), self.filled.receipt_time)
"<p>"
_('The form has been recorded on %s with the number %s.') % (tm, self.filled.id)
"</p>"
if self.formdef.receiver:
"<p>"
_('Your case is handled by:')
"</p>"
'<p id="receiver">'
htmltext(self.formdef.receiver.replace(str('\n'), str('<br />')))
"</p>"
"""<dl id="receipt">"""
for f in self.formdef.fields:
if self.filled.data.has_key(f['name']):
"<dt>%s</dt>" % f['name']
"<dd>%s</dd>" % self.filled.data[f['name']]
"</dl>"
'<a href="../..">%s</a>' % _("Back Home")
html_foot()
def status [html] (self):
html_top(self.formdef.name + ' - ' + self.filled.id)
tm = time.strftime(str("%Y-%m-%d %H:%M"), self.filled.receipt_time)
"<p>"
_('The form has been recorded on %s with the number %s.') % (tm, self.filled.id)
"</p>"
"""<dl id="receipt">"""
for f in self.formdef.fields:
if self.filled.data.has_key(f['name']):
"<dt>%s</dt>" % f['name']
"<dd>%s</dd>" % self.filled.data[f['name']]
"</dl>"
'<a href="../..">%s</a>' % _("Back Home")
html_foot()
class FormPage(Directory):
_q_exports = ["", "submit"]
def __init__(self, component):
self.component = component
try:
self.formdef = storage.get_storage().retrieve('formdefs', component)
except KeyError:
raise wcs.errors.TraversalError()
session = get_session()
user = None
if session:
try:
user = storage.get_storage().retrieve('users', session.user)
except KeyError:
pass
if self.formdef.roles:
if not user:
raise wcs.errors.AccessForbiddenError()
for q in user.roles or []:
if q in self.formdef.roles:
break
else:
raise wcs.errors.AccessForbiddenError()
def step [html] (self, no):
"""<div id="steps"><ol>"""
for i, l in enumerate([_("Filling"), _("Validating"), _("Receipt")]):
if no == i:
'<li class="current step-%d">' % i
else:
'<li class="step-%d">' % i
'<span class="marker">%d</span> <span class="label">%s</span></li>' % (i+1, l)
"</ol></div>"
def _q_index [html] (self):
form = self.formdef.create_form()
form.add_submit("submit", _("Next"))
html_top(self.formdef.name)
self.step(0)
form.action = 'submit'
form.add_hidden('step', '0')
form.render()
html_foot()
def submit(self):
form = self.formdef.create_form()
form.add_submit("previous", _("Previous"))
form.add_submit("submit", _("Next"))
form.add_hidden('step', '-1')
step = form.get_widget('step').parse()
if step == '1':
if form.get_widget('previous').parse():
return self._q_index()
if not form.is_submitted() or form.has_errors():
return self._q_index()
if step == '0':
return self.validating()
else:
filled = wcs.formdata.FormData()
filled.names = 'form-' + self.component
filled.data = self.formdef.get_data(form)
filled.receipt_time = time.localtime()
session = get_session()
if session and session.user:
filled.user_id = session.user
storage.get_storage().store(filled)
self.formdef.notify_new(filled)
return self.receipt(filled)
def validating [html] (self):
html_top(self.formdef.name)
self.step(1)
"""<p>%s</p>""" % _("Check values then click next.")
form = self.formdef.create_view_form()
form.action = 'submit'
form.add_submit("previous", _("Previous"))
form.add_submit("submit", _("Next"))
form.add_hidden('step', '1')
form.render()
html_foot()
def receipt [html] (self, filled):
html_top(self.formdef.name)
self.step(2)
tm = time.strftime(str("%Y-%m-%d %H:%M"), filled.receipt_time)
"<p>"
_('The form has been recorded on %s with the number %s.') % (tm, filled.id)
"</p>"
if self.formdef.receiver:
"<p>"
_('Your case will be handled by:')
"</p>"
'<p id="receiver">'
htmltext(self.formdef.receiver.replace(str('\n'), str('<br />')))
"</p>"
"""<dl id="receipt">"""
for f in self.formdef.fields:
if filled.data.has_key(f['name']):
"<dt>%s</dt>" % f['name']
"<dd>%s</dd>" % filled.data[f['name']]
"</dl>"
'<a href="..">%s</a>' % _("Back Home")
html_foot()
def _q_lookup(self, component):
return FormStatusPage(self.formdef, component)
class RootDirectory(Directory):
_q_exports = ["", "token"]
def _q_index [html] (self):
html_top(_("Forms"))
session = get_session()
if session and session.user and not session.user.startswith(str('anonymous-')):
try:
user = storage.get_storage().retrieve('users', session.user)
"<p>%s</p>" % _('You are logged in as %s.') % user.name
except KeyError:
pass
else:
user = None
"<ul>"
for k in storage.get_storage().keys(str('formdefs')):
formdef = storage.get_storage().retrieve('formdefs', k)
if formdef.roles:
if not user:
continue
for q in user.roles or []:
if q in formdef.roles:
break
else:
continue
"""<li><a href="%s/">%s</a></li>""" % (k, formdef.name)
"</ul>"
if session and session.user:
l = []
for k in storage.get_storage().keys('formdefs'):
formdef = storage.get_storage().retrieve('formdefs', k)
formnames = "form-" + formdef.id
for q in storage.get_storage().keys(formnames):
filled = storage.get_storage().retrieve(formnames, q)
filled.formdef = formdef
if filled.user_id == session.user:
l.append(filled)
if l:
"""<h2 id="submitted">%s</h2>""" % _('Your Current Forms')
"<ul>"
l.sort(lambda x,y: -cmp(x.receipt_time, y.receipt_time))
for f in l:
"""<li><a href="%s/%s">%s</a>, %s</li>""" % (
f.formdef.id, f.id, f.formdef.name,
time.strftime(str("%Y-%m-%d %H:%M"), f.receipt_time))
"""<p id="logout">"""
if session.user.startswith(str('anonymous-')):
"""<a href="token">%s</a> - """ % _('Enter Identification Token')
"""<a href="../logout">%s</a></p>""" % _('Logout')
elif wcs.misc.cfg.has_key('sp'):
"""<p id="login"><a href="../login">%s</a></p>""" % _('Login')
html_foot()
def _q_lookup(self, component):
return FormPage(component)
def token [html] (self):
form = Form(enctype="multipart/form-data")
form.add(StringWidget, "token", title = _('Identification Token'),
required = True, size = 30)
form.add_submit("cancel", _("Cancel"))
form.add_submit("submit", _("Submit"))
if form.get_widget('cancel').parse():
return redirect('.')
if not form.is_submitted() or form.has_errors():
html_top(_("Forms"))
"<p>"
_("Bla bla bla")
"</p>"
form.render()
html_foot()
else:
token = form.get_widget('token').parse()
for user in storage.get_storage().values('users'):
if user.identification_token == token:
user.identification_token = None
user.name_identifiers = [str(get_session().name_identifier)]
old_name = get_session().user
get_session().set_user(user.id)
storage.get_storage().store(user)
for formdef in storage.get_storage().values('formdefs'):
formnames = "form-" + formdef.id
for filled in storage.get_storage().values(formnames):
if filled.user_id == old_name:
filled.user_id = user.id
storage.get_storage().store(filled)
break
return redirect('.')

0
wcs/liberty/__init__.py Normal file
View File

85
wcs/liberty/root.ptl Normal file
View File

@ -0,0 +1,85 @@
import os
import urllib
import urlparse
import httplib
from quixote import get_request, get_response, redirect, get_field
from quixote.directory import Directory
from quixote.form import *
from quixote.html import htmltext
from quixote.http_request import parse_header
from quixote import get_user, get_session, get_session_manager, get_field
import lasso
import wcs.misc
from wcs import storage
class RootDirectory(Directory):
_q_exports = ["", "login", "assertionConsumer"]
def login(self):
server = wcs.misc.get_lasso_server()
login = lasso.Login(server)
idps = wcs.misc.cfg.get('idp', {}).values()
if len(idps) > 1:
# XXX: form to select idp
pass
login.initAuthnRequest(None, lasso.HTTP_METHOD_REDIRECT)
login.request.nameIdPolicy = "federated"
login.request.forceAuthn = False
login.request.isPassive = False
login.request.consent = "urn:liberty:consent:obtained"
login.buildAuthnRequestMsg()
return redirect(login.msgUrl)
def assertionConsumer(self):
server = wcs.misc.get_lasso_server()
login = lasso.Login(server)
request = get_request()
if request.get_method() == 'GET' or get_field('LAREQ'):
if request.get_method() == 'GET':
login.initRequest(request.get_query(), lasso.HTTP_METHOD_REDIRECT)
else:
login.initRequest(get_field('LAREQ'), lasso.HTTP_METHOD_POST)
login.buildRequestMsg()
soap_answer = soap_call(login.msgUrl, login.msgBody)
login.processResponseMsg(soap_answer)
else:
login.processAuthnResponseMsg(get_field('LARES'))
login.acceptSso()
session = get_session()
ni = login.nameIdentifier.content
for user in storage.get_storage().values('users'):
if ni in user.name_identifiers:
session.name_identifier = ni
session.set_user(user.id)
break
else:
session.name_identifier = ni
session.set_user('anonymous-%s' % ni)
response = get_response()
response.set_status(303)
response.headers['location'] = urlparse.urljoin(request.get_url(), str('..'))
response.content_type = 'text/plain'
return "Your browser should redirect you"
def soap_call(url, msg):
if url.startswith('http://'):
host, query = urllib.splithost(url[5:])
conn = httplib.HTTPConnection(host)
else:
host, query = urllib.splithost(url[6:])
conn = httplib.HTTPSConnection(host)
conn.request("POST", query, msg, {'Content-Type': 'text/xml'})
response = conn.getresponse()
data = response.read()
conn.close()
if response.status not in (200, 204): # 204 ok for federation termination
raise "SOAPError"
return data

48
wcs/misc.py Normal file
View File

@ -0,0 +1,48 @@
import cPickle
import re
import lasso
import wcs
def get_lasso_server():
reload_cfg()
if not cfg.has_key('sp'):
return None
server = lasso.Server(
cfg['sp']['metadata'],
cfg['sp']['privatekey'],
None, None)
for idp in cfg.get('idp', {}).values():
server.addProvider(
lasso.PROVIDER_ROLE_IDP,
idp['metadata'],
idp['publickey'],
None)
return server
def write_cfg():
s = cPickle.dumps(cfg)
file(filename, 'w').write(s)
def reload_cfg():
global cfg
try:
cfg = cPickle.load(file(filename))
except:
cfg = {}
def get_provider_label(provider):
if not hasattr(provider, str('getOrganization')):
return provider.providerId
organization = provider.getOrganization()
if not organization:
return provider.providerId
name = re.findall("<OrganizationDisplayName.*>(.*?)</OrganizationDisplayName>", organization)
if not name:
name = re.findall("<OrganizationName.*>(.*?)</OrganizationName>", organization)
if not name:
return provider.providerId
return name[0]

12
wcs/roles.py Normal file
View File

@ -0,0 +1,12 @@
from quixote import get_request
import storage
class Role(storage.Storable):
key = 'id'
names = 'roles'
def __init__(self):
self.id = ''
self.name = ''

62
wcs/sessions.py Normal file
View File

@ -0,0 +1,62 @@
import os
import cPickle
from quixote.session import Session, SessionManager
import storage
class BasicSession(Session, storage.Storable):
key = 'id'
names = 'sessions'
def __init__(self, id):
Session.__init__(self, id)
self.name_identifier = None
def has_info(self):
return self.name_identifier or Session.has_info(self)
is_dirty = has_info
class PickleSessionManager(SessionManager):
def __init__(self):
self.storage = storage.get_storage()
SessionManager.__init__(self, session_class=BasicSession)
def forget_changes(self, session):
pass
def __getitem__(self, session_id):
try:
return storage.get_storage().retrieve('sessions', session_id)
except IOError:
raise KeyError
def get(self, session_id, default = None):
try:
return storage.get_storage().retrieve('sessions', session_id)
except KeyError:
return default
def commit_changes(self, session):
if session and session.id:
storage.get_storage().store(session)
def keys(self):
return storage.get_storage().keys('sessions')
def values(self):
return [self.get(k) for k in self.keys()]
def items(self):
return [(k, self.get(k)) for k in self.keys()]
def has_key(self, session_id):
return session_id in self.keys()
def __setitem__(self, session_id, session):
storage.get_storage().store(session)
def __delitem__(self, session_id):
try:
storage.get_storage().remove_id('sessions', session_id)
except OSError:
raise KeyError

81
wcs/storage.py Normal file
View File

@ -0,0 +1,81 @@
import os
import cPickle
class Storage:
pass
class Storable:
def get_table_name(self):
if hasattr(self, 'names'):
return self.names
return self.__class__.__name__.lower()
def fixkey(k):
# insure key can be inserted in filesystem
if not k: return k
return str(k).replace('/', '-')
class FilesStorage:
BASEDIR = None # must be initialized
def __init__(self):
if self.BASEDIR and not os.path.exists(self.BASEDIR):
os.mkdir(self.BASEDIR)
def keys(self, obname):
dirname = os.path.join(self.BASEDIR, str(obname))
if not os.path.exists(dirname):
return []
return os.listdir(dirname)
def store(self, object):
obname = object.get_table_name()
dirname = os.path.join(self.BASEDIR, obname)
if not os.path.exists(dirname):
os.mkdir(dirname)
if not getattr(object, object.key):
keys = self.keys(obname)
key = len(keys) + 1
while str(key) in keys:
key += 1
key = str(key)
setattr(object, object.key, key)
s = cPickle.dumps(object)
file(os.path.join(dirname, fixkey(getattr(object, object.key))), 'w').write(s)
def retrieve(self, obname, key):
if key is None:
raise KeyError()
dirname = os.path.join(self.BASEDIR, str(obname))
try:
return cPickle.load(open(os.path.join(dirname, fixkey(key))))
except IOError:
raise KeyError()
def values(self, obname):
return [self.retrieve(obname, k) for k in self.keys(obname)]
def items(self, obname):
return [(k, self.retrieve(obname, k)) for k in self.keys(obname)]
def remove(self, object = None):
obname = object.get_table_name()
return self.remove_id(obname, fixkey(getattr(object, object.key)))
def remove_id(self, obname, key):
dirname = os.path.join(self.BASEDIR, str(obname))
os.unlink(os.path.join(dirname, fixkey(key)))
def remove_all(self, obname):
dirname = os.path.join(self.BASEDIR, str(obname))
if not os.path.exists(dirname):
return
for k in os.listdir(dirname):
os.unlink(os.path.join(dirname, k))
os.rmdir(dirname)
def get_storage():
return FilesStorage()

14
wcs/users.py Normal file
View File

@ -0,0 +1,14 @@
import storage
class User(storage.Storable):
key = 'id'
names = 'users'
def __init__(self):
self.id = ''
self.name = ''
self.email = ''
self.roles = []
self.name_identifiers = []
self.identification_token = None

6
wcs_scgi_server.py Normal file
View File

@ -0,0 +1,6 @@
#! /usr/bin/env python
from quixote.server.scgi_server import run
import wcs
run(wcs.create_publisher, port=3001, script_name = '')