summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordlaniel <dlaniel@3ed937ae-f919-0410-9a43-8e6f19e4ba6e>2009-03-09 14:54:38 (GMT)
committerdlaniel <dlaniel@3ed937ae-f919-0410-9a43-8e6f19e4ba6e>2009-03-09 14:54:38 (GMT)
commit4dc72b40aa23d45730e06c083bca21dad7682e3b (patch)
treeda09f55e71fb134396b63eec40977a73e1e75b39
parentd6e10f18364d4be59401ca6f475f713f23602ad3 (diff)
downloadlarpe-4dc72b40aa23d45730e06c083bca21dad7682e3b.zip
larpe-4dc72b40aa23d45730e06c083bca21dad7682e3b.tar.gz
larpe-4dc72b40aa23d45730e06c083bca21dad7682e3b.tar.bz2
1.0 release
git-svn-id: svn+ssh://labs.libre-entreprise.org/svnroot/larpe@461 3ed937ae-f919-0410-9a43-8e6f19e4ba6e
-rw-r--r--larpe/tags/release-1.0/AUTHORS5
-rw-r--r--larpe/tags/release-1.0/COPYING339
-rw-r--r--larpe/tags/release-1.0/MANIFEST.in13
-rw-r--r--larpe/tags/release-1.0/Makefile45
-rw-r--r--larpe/tags/release-1.0/NEWS17
-rw-r--r--larpe/tags/release-1.0/README32
-rw-r--r--larpe/tags/release-1.0/TODO101
-rw-r--r--larpe/tags/release-1.0/conf/apache2-vhost-larpe12
-rw-r--r--larpe/tags/release-1.0/conf/apache2-vhost-larpe-common19
-rw-r--r--larpe/tags/release-1.0/conf/output_filter_base.py30
-rw-r--r--larpe/tags/release-1.0/debian/changelog63
-rw-r--r--larpe/tags/release-1.0/debian/compat1
-rwxr-xr-xlarpe/tags/release-1.0/debian/config24
-rw-r--r--larpe/tags/release-1.0/debian/control17
-rw-r--r--larpe/tags/release-1.0/debian/copyright27
-rw-r--r--larpe/tags/release-1.0/debian/dirs4
-rw-r--r--larpe/tags/release-1.0/debian/docs2
-rw-r--r--larpe/tags/release-1.0/debian/init82
-rwxr-xr-xlarpe/tags/release-1.0/debian/larpe-reload-apache2-script3
-rw-r--r--larpe/tags/release-1.0/debian/postinst66
-rw-r--r--larpe/tags/release-1.0/debian/prerm41
-rw-r--r--larpe/tags/release-1.0/debian/pycompat1
-rwxr-xr-xlarpe/tags/release-1.0/debian/rules73
-rw-r--r--larpe/tags/release-1.0/debian/templates37
-rw-r--r--larpe/tags/release-1.0/doc/Makefile8
-rw-r--r--larpe/tags/release-1.0/doc/en/Makefile35
-rw-r--r--larpe/tags/release-1.0/doc/en/custom.tex45
-rw-r--r--larpe/tags/release-1.0/doc/en/default.css143
-rw-r--r--larpe/tags/release-1.0/doc/en/fncychap.sty490
-rw-r--r--larpe/tags/release-1.0/doc/en/larpe-admin.rst202
-rwxr-xr-xlarpe/tags/release-1.0/doc/scripts/removealpha.sh5
-rwxr-xr-xlarpe/tags/release-1.0/doc/scripts/rst2latex.py29
-rw-r--r--larpe/tags/release-1.0/exclude_from_dist12
-rwxr-xr-xlarpe/tags/release-1.0/fedora/larpe-reload-apache2-script22
-rwxr-xr-xlarpe/tags/release-1.0/fedora/larpe.init104
-rw-r--r--larpe/tags/release-1.0/fedora/larpe.spec139
-rw-r--r--larpe/tags/release-1.0/larpe-reload-apache2.c173
-rw-r--r--larpe/tags/release-1.0/larpe/Defaults.py10
-rw-r--r--larpe/tags/release-1.0/larpe/__init__.py16
-rw-r--r--larpe/tags/release-1.0/larpe/admin/__init__.py1
-rw-r--r--larpe/tags/release-1.0/larpe/admin/apache.py302
-rw-r--r--larpe/tags/release-1.0/larpe/admin/fields_prefill.ptl130
-rw-r--r--larpe/tags/release-1.0/larpe/admin/forms_prefill.ptl127
-rw-r--r--larpe/tags/release-1.0/larpe/admin/hosts.ptl1336
-rw-r--r--larpe/tags/release-1.0/larpe/admin/liberty_utils.py129
-rw-r--r--larpe/tags/release-1.0/larpe/admin/root.ptl90
-rw-r--r--larpe/tags/release-1.0/larpe/admin/settings.ptl361
-rw-r--r--larpe/tags/release-1.0/larpe/admin/users.ptl275
-rw-r--r--larpe/tags/release-1.0/larpe/ctl/__init__.py2
-rw-r--r--larpe/tags/release-1.0/larpe/ctl/start.py34
-rw-r--r--larpe/tags/release-1.0/larpe/errors.ptl14
-rw-r--r--larpe/tags/release-1.0/larpe/federations.py35
-rw-r--r--larpe/tags/release-1.0/larpe/field_prefill.py13
-rwxr-xr-xlarpe/tags/release-1.0/larpe/filter/larpe-filter.py164
-rw-r--r--larpe/tags/release-1.0/larpe/form_prefill.py10
-rw-r--r--larpe/tags/release-1.0/larpe/hosts.py136
-rw-r--r--larpe/tags/release-1.0/larpe/idwsf2.ptl199
-rw-r--r--larpe/tags/release-1.0/larpe/liberty.ptl372
-rw-r--r--larpe/tags/release-1.0/larpe/liberty_root.ptl9
-rw-r--r--larpe/tags/release-1.0/larpe/liberty_site.ptl68
-rw-r--r--larpe/tags/release-1.0/larpe/logger.py34
-rw-r--r--larpe/tags/release-1.0/larpe/misc.py105
-rw-r--r--larpe/tags/release-1.0/larpe/plugins/__init__.py0
-rw-r--r--larpe/tags/release-1.0/larpe/plugins/site_authentication/__init__.py0
-rw-r--r--larpe/tags/release-1.0/larpe/plugins/site_authentication/agirhe.py142
-rw-r--r--larpe/tags/release-1.0/larpe/plugins/site_authentication/ciril_net_rh.py38
-rw-r--r--larpe/tags/release-1.0/larpe/plugins/site_authentication/concerto.py88
-rw-r--r--larpe/tags/release-1.0/larpe/plugins/site_authentication/egroupware.py90
-rw-r--r--larpe/tags/release-1.0/larpe/plugins/site_authentication/sympa.py44
-rw-r--r--larpe/tags/release-1.0/larpe/publisher.py53
-rw-r--r--larpe/tags/release-1.0/larpe/root.ptl106
-rw-r--r--larpe/tags/release-1.0/larpe/saml2.ptl414
-rw-r--r--larpe/tags/release-1.0/larpe/sessions.py76
-rw-r--r--larpe/tags/release-1.0/larpe/site_authentication.ptl324
-rw-r--r--larpe/tags/release-1.0/larpe/users.py44
-rwxr-xr-xlarpe/tags/release-1.0/larpectl25
-rwxr-xr-xlarpe/tags/release-1.0/make_debian_package.sh17
-rw-r--r--larpe/tags/release-1.0/po/Makefile49
-rw-r--r--larpe/tags/release-1.0/po/fr.po2826
-rw-r--r--larpe/tags/release-1.0/po/larpe.pot2620
-rw-r--r--larpe/tags/release-1.0/pylintrc310
-rw-r--r--larpe/tags/release-1.0/root/index.html18
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/arrow-right-2.0.pngbin0 -> 633 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/bg-footer.pngbin0 -> 886 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/dc2/admin.css283
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/dc2/head-bg.pngbin0 -> 357 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/dc2/head-logo-empty.pngbin0 -> 830 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/dc2/head-logo.pngbin0 -> 3185 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/dc2/page-bg.pngbin0 -> 225 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/deg-top.pngbin0 -> 296 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/dot999.pngbin0 -> 200 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/fond.jpgbin0 -> 3034 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/ico_user.pngbin0 -> 1332 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/img/bulle.pngbin0 -> 200 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/img/day-date.pngbin0 -> 87 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/img/footer-500.pngbin0 -> 1750 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/img/footer.jpgbin0 -> 2586 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/img/h2.pngbin0 -> 160 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/img/li.pngbin0 -> 117 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/img/linkscat.pngbin0 -> 102 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/img/page-500.pngbin0 -> 216 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/img/page.pngbin0 -> 181 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/img/search.pngbin0 -> 136 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/img/sidebarh2.pngbin0 -> 93 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/img/top-500.pngbin0 -> 2443 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/img/top.jpgbin0 -> 3995 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/img/top.pngbin0 -> 1462 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/active-bg.gifbin0 -> 89 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/dark-bg.gifbin0 -> 85 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/hover-bg.gifbin0 -> 89 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/menuarrow.gifbin0 -> 49 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/normal-bg.gifbin0 -> 110 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/rowhover-bg.gifbin0 -> 110 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/status-bg.gifbin0 -> 116 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/theme.css236
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/title-bg.gifbin0 -> 116 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/today-bg.gifbin0 -> 1122 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/larpe-admin.css410
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/larpe-common.css260
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/larpe.css331
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/onglet_left.pngbin0 -> 891 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/onglet_right.pngbin0 -> 816 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/required.pngbin0 -> 137 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/user_info_bottom.pngbin0 -> 291 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/user_info_top.pngbin0 -> 1629 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/css/warning.pngbin0 -> 809 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/images/bar.pngbin0 -> 2284 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/images/stock_add_16.pngbin0 -> 341 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/images/stock_copy_16.pngbin0 -> 341 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/images/stock_edit_16.pngbin0 -> 492 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/images/stock_exec_16.pngbin0 -> 501 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/images/stock_file_16.pngbin0 -> 349 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/images/stock_harddisk_16.pngbin0 -> 682 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/images/stock_properties_16.pngbin0 -> 554 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/images/stock_remove_16.pngbin0 -> 173 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/images/view_16.pngbin0 -> 747 bytes
-rw-r--r--larpe/tags/release-1.0/root/larpe/js/jscalendar/README33
-rw-r--r--larpe/tags/release-1.0/root/larpe/js/jscalendar/calendar.js1806
-rw-r--r--larpe/tags/release-1.0/root/larpe/js/jscalendar/helpers.js118
-rw-r--r--larpe/tags/release-1.0/root/larpe/js/jscalendar/lang/calendar-en.js127
-rw-r--r--larpe/tags/release-1.0/root/larpe/js/jscalendar/lang/calendar-fr.js125
-rw-r--r--larpe/tags/release-1.0/root/larpe/js/listing.js41
-rw-r--r--larpe/tags/release-1.0/root/larpe/js/prototype.js1041
-rw-r--r--larpe/tags/release-1.0/root/larpe/js/scriptaculous/controls.js699
-rw-r--r--larpe/tags/release-1.0/root/larpe/js/scriptaculous/dragdrop.js545
-rw-r--r--larpe/tags/release-1.0/root/larpe/js/scriptaculous/effects.js707
-rw-r--r--larpe/tags/release-1.0/root/larpe/js/scriptaculous/scriptaculous.js26
-rw-r--r--larpe/tags/release-1.0/root/larpe/js/scriptaculous/unittest.js381
-rw-r--r--larpe/tags/release-1.0/root/larpe/js/scriptaculous/util.js429
-rw-r--r--larpe/tags/release-1.0/root/larpe/js/sorttable.js185
-rw-r--r--larpe/tags/release-1.0/setup.py39
-rw-r--r--larpe/tags/release-1.0/tests/all4dev/slo_check2
-rw-r--r--larpe/tags/release-1.0/tests/all4dev/sso_check3
-rw-r--r--larpe/tags/release-1.0/tests/blueprint/slo_check1
-rw-r--r--larpe/tags/release-1.0/tests/blueprint/sso_check1
-rwxr-xr-xlarpe/tags/release-1.0/tests/check.sh93
-rw-r--r--larpe/tags/release-1.0/tests/defederation1
-rw-r--r--larpe/tags/release-1.0/tests/dotclear/slo_check2
-rw-r--r--larpe/tags/release-1.0/tests/dotclear/sso_check4
-rw-r--r--larpe/tags/release-1.0/tests/dotclear_subdir/slo_check1
-rw-r--r--larpe/tags/release-1.0/tests/dotclear_subdir/sso_check3
-rw-r--r--larpe/tags/release-1.0/tests/federation3
-rwxr-xr-xlarpe/tags/release-1.0/tests/gen_config.sh26
-rw-r--r--larpe/tags/release-1.0/tests/idp_login3
-rw-r--r--larpe/tags/release-1.0/tests/libre-entreprise/slo_check3
-rw-r--r--larpe/tags/release-1.0/tests/libre-entreprise/sso_check4
-rw-r--r--larpe/tags/release-1.0/tests/listes_entrouvert/slo_check1
-rw-r--r--larpe/tags/release-1.0/tests/listes_entrouvert/sso_check2
-rw-r--r--larpe/tags/release-1.0/tests/listes_libre_entreprise/slo_check1
-rw-r--r--larpe/tags/release-1.0/tests/listes_libre_entreprise/sso_check2
-rw-r--r--larpe/tags/release-1.0/tests/slo1
-rw-r--r--larpe/tags/release-1.0/tests/sso1
172 files changed, 21697 insertions, 0 deletions
diff --git a/larpe/tags/release-1.0/AUTHORS b/larpe/tags/release-1.0/AUTHORS
new file mode 100644
index 0000000..b0adfde
--- /dev/null
+++ b/larpe/tags/release-1.0/AUTHORS
@@ -0,0 +1,5 @@
+Damien Laniel <dlaniel@entrouvert.com>
+
+Artwork and administrave interface design taken from DotClear, version 1.2 and
+2.0, released under the GNU General Public License; and GTK+, version 2.8,
+released under the GNU Lesser General Public License.
diff --git a/larpe/tags/release-1.0/COPYING b/larpe/tags/release-1.0/COPYING
new file mode 100644
index 0000000..d511905
--- /dev/null
+++ b/larpe/tags/release-1.0/COPYING
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/larpe/tags/release-1.0/MANIFEST.in b/larpe/tags/release-1.0/MANIFEST.in
new file mode 100644
index 0000000..06c2ffc
--- /dev/null
+++ b/larpe/tags/release-1.0/MANIFEST.in
@@ -0,0 +1,13 @@
+include Makefile
+include setup.py
+include larpectl
+include apache2-vhost-larpe
+include apache2.conf
+include larpe-reload-apache2-script
+include larpe-reload-apache2.c
+include README COPYING MANIFEST.in MANIFEST NEWS AUTHORS
+recursive-include larpe *.py *.ptl
+recursive-include data *
+recursive-include root *
+recursive-include po *.po *.pot Makefile
+recursive-include doc *.rst Makefile *.png *.sty custom.tex *.py *.sh *.css
diff --git a/larpe/tags/release-1.0/Makefile b/larpe/tags/release-1.0/Makefile
new file mode 100644
index 0000000..b0be307
--- /dev/null
+++ b/larpe/tags/release-1.0/Makefile
@@ -0,0 +1,45 @@
+prefix = /usr
+config_prefix = /var
+config_dir = $(config_prefix)/lib/larpe
+
+INSTALL = /usr/bin/install -c
+PYTHON = /usr/bin/python
+
+LARPE_VERSION = 0.2.9
+
+larpe-reload-apache2: larpe-reload-apache2.c
+
+install: larpe-reload-apache2
+ rm -rf build
+ $(MAKE) -C po install
+ $(PYTHON) setup.py install --root "$(DESTDIR)/" --prefix "$(prefix)" --no-compile
+ $(INSTALL) -d $(DESTDIR)$(prefix)/sbin/
+ $(INSTALL) larpectl $(DESTDIR)$(prefix)/sbin/
+ $(INSTALL) -m 4550 larpe-reload-apache2 $(DESTDIR)$(prefix)/sbin/
+ $(INSTALL) -d $(DESTDIR)/etc/larpe
+ $(INSTALL) -m 644 conf/apache2-vhost-larpe-common $(DESTDIR)/etc/larpe
+ $(INSTALL) -d $(DESTDIR)$(config_dir)
+ $(INSTALL) -d $(DESTDIR)$(config_dir)/vhosts.d
+ $(INSTALL) -d $(DESTDIR)$(config_dir)/vhost-locations.d
+ $(INSTALL) -d $(DESTDIR)$(config_dir)/vhosts.d.disabled
+ $(INSTALL) -d $(DESTDIR)$(config_dir)/vhost-locations.d.disabled
+
+uninstall:
+ $(MAKE) -C po uninstall
+ -rm -f $(DESTDIR)$(prefix)/sbin/larpe-reload-apache2
+ -rm -f $(DESTDIR)$(prefix)/sbin/larpe-reload-apache2-script
+ -rm -f $(DESTDIR)$(prefix)/sbin/larpectl
+ -rm -rf $(DESTDIR)$(prefix)/share/larpe/
+ @echo
+ @echo "Depending on your Python version, you will have to remove manually the files in /usr/lib/python(version)/site-packages/larpe/"
+
+clean:
+ $(MAKE) -C po clean
+ $(MAKE) -C doc clean
+ -$(PYTHON) setup.py clean
+ -rm -f larpe-reload-apache2
+
+dist: clean
+ tar czf dist/larpe-$(LARPE_VERSION).tar.gz -C .. --transform s/trunk/larpe-$(LARPE_VERSION)/ --exclude-from=exclude_from_dist trunk
+
+.PHONY: clean dist
diff --git a/larpe/tags/release-1.0/NEWS b/larpe/tags/release-1.0/NEWS
new file mode 100644
index 0000000..5e08d64
--- /dev/null
+++ b/larpe/tags/release-1.0/NEWS
@@ -0,0 +1,17 @@
+NEWS
+====
+
+Version 1.0
+-----------
+
+- Adds Liberty Alliance to some sites without modying sites code
+- SAML 2.0 and ID-FF 1.2 support (authentication and logout)
+- Form prefilling with ID-WSF 2.0
+- Configuration assistant (wizard-like) for configuring new sites
+- Fully tested for several sites with very different behaviours
+- Plugin system to handle specific behaviour of some sites
+- Automatic Apache 2 configuration
+- Automatic creation of Apache python filters to transform authentication boxes on the sites
+- Support for proxies
+- Logging and debug options
+- Packages for Debian and Fedora (and children of both) distributions
diff --git a/larpe/tags/release-1.0/README b/larpe/tags/release-1.0/README
new file mode 100644
index 0000000..f135f76
--- /dev/null
+++ b/larpe/tags/release-1.0/README
@@ -0,0 +1,32 @@
+Larpe - Liberty Alliance Reverse Proxy
+======================================
+
+Description
+-----------
+
+Larpe is a Liberty Alliance Reverse Proxy. It allows any service provider (that
+is a website) to use Liberty Alliance features (Identity federation, Single
+Sign On and Single Sign Logout) without changing the code of the service
+provider itself. It uses the Lasso library which is certified by the Liberty
+Alliance consortium.
+
+
+Documentation
+-------------
+
+* README, as you are doing;
+
+* doc/en/ for English documentation, published as HTML on
+ http://larpe.labs.libre-entreprise.org/doc/en/larpe-admin.html
+
+
+Copyright
+---------
+
+Larpe is copyrighted by Entr'ouvert and is licensed under the GNU General
+Public Licence. Artwork and administrative design are from DotClear and
+released under the GNU General Public License by Olivier Meunier and others.
+Some artwork comes from GTK+ (LGPL).
+
+Read the COPYING file for the complete license text. Read the AUTHORS file for
+additional credits.
diff --git a/larpe/tags/release-1.0/TODO b/larpe/tags/release-1.0/TODO
new file mode 100644
index 0000000..5564cc8
--- /dev/null
+++ b/larpe/tags/release-1.0/TODO
@@ -0,0 +1,101 @@
+- Tests
+ - egroupware
+ - http://labs.libre-entreprise.org/
+ - logs.entrouvert.org
+
+====== Roadmap de Larpe ======
+
+===== 0.2 =====
+
+ * Vérifier la compatibilité avec egroupware
+ * Mettre le vhosts générés dans /var/lib/larpe/vhosts.d et mettre un include /var/lib/larpe/vhosts.d/* dans la conf générale
+ * Supprimer debconf
+ * Vérification des formulaires de configuration d'hôtes
+ * Tests de valeurs erronés diverses
+ * Erreur si on donne un label qui existe deja
+ * Ne plus inclure le binaire larpe-reload-apache2 dans les sources
+ * Corriger les avertissements debian
+ * Ne pas demander la clé publique de l'idp
+ * Compléter les traductions
+ * Ajouter la possibilité de changer la langue dans l'interface d'administration
+ * Mettre à jour la documentation
+ * Ajouter un chapitre sur les sites testés et leurs options de configuration particulières
+ * Traduire la documentation en français
+
+===== 0.3 =====
+
+ * Implémenter le SLO en SOAP
+ * Supprimer un /liberty/ des urls
+ * Voir comment activer le SSLProxyEngine quand on utilise un sous répertoire
+ * Faire un site web pour présenter Larpe
+ * Ajouter la possibilité d'envoyer les exceptions par courriel à l'administrateur
+ * Améliorer la journalisation des accès et des erreurs
+
+===== 1.0 =====
+
+ * Support de SAML 2.0
+ * Implémenter l'accès à un site nécessitant une authentification préalable avant tout accès
+ * Choix de cette fonctionnalité par une option de configuration par site
+ * Lors de la création d'un site, choix d'un moteur de site connu (mediawiki, squirrelmail, ...) qui pré-remplirait un ensemble d'options nécessaire à ce moteur
+ * Documentation technique pour les développeurs ?
+
+===== Non classés =====
+
+ * Support des sites qui ont une authentification HTTP (à priori, nécessite de charger toute la configuration de larpe dans le filtre python d'apache)
+ * Création de nouveaux comptes pour les sites, avec des jetons (déjà implémenté en partie ; est-ce utile ?)
+
+Fait
+====
+
+- Serveur python principal
+ - Fonctionnalités liberty
+ - SSO (depuis le sp et depuis l'idp)
+ - Fédération
+ - SLO (depuis le sp et depuis l'idp)
+ - Défédération (depuis l'idp) en SOAP et redirect
+ - Support https
+ - Possibilité d'utiliser toutes les combinaisons de sous domaines et de sous répertoires
+ - RP par vhost (appli1.example.com, rp de appli1.interne)
+ - RP par repertoire (www.example.com/appli1, rp de appl1.interne)
+ - Récupère la configuration de l'IP des vhosts
+
+- Administration
+ - Authentification liberty sur l'admin
+ - Créer de nouveaux sites (+ modifier, supprimer)
+ - Écrire les vhosts correspondants
+ - Rechargement de la configuration d'apache
+ - Script + wrapper en C suid root
+ - Gestion d'utilisateurs pour administrer le RP (Authentification http)
+ - Gestion des traductions
+
+- Filtre Python branché en sortie sur Apache à la suite du filtre de réécriture html (proxy_html)
+ - Générique
+ - Personalisable par site pour une meilleure intégration dans les pages
+
+- Sites testés
+ - Dotclear
+ - Dacode
+ - linuxfr.org
+ - Sympa
+ - listes.entrouvert.com
+ - listes.libre-entreprise.org
+ - Mediawiki
+ - all4dev.libre-entreprise.org
+ - www.libre-entreprise.org
+ - www.besancon.com
+ - Egroupware
+ - quintine.entrouvert.org/egroupware/
+ - squirrelmail
+ - Concerto Espace-famille
+ - Ciril Net RH
+ - Agirhe
+
+- Documentation
+
+- Paquets Debian
+ - Debconf pour demander le nom de domaine et le courriel de l'admin, ainsi que le compte administrateur
+
+- Installation sur lupin
+
+- Batterie de tests de non-regression
+
diff --git a/larpe/tags/release-1.0/conf/apache2-vhost-larpe b/larpe/tags/release-1.0/conf/apache2-vhost-larpe
new file mode 100644
index 0000000..d5ff56b
--- /dev/null
+++ b/larpe/tags/release-1.0/conf/apache2-vhost-larpe
@@ -0,0 +1,12 @@
+<VirtualHost *>
+ ServerName localhost
+ ServerAdmin root@localhost
+
+ include /etc/larpe/apache2-vhost-larpe-common
+ include /var/lib/larpe/vhost-locations.d
+
+ CustomLog /var/log/apache2/larpe-access.log combined
+ ErrorLog /var/log/apache2/larpe-error.log
+</VirtualHost>
+
+include /var/lib/larpe/vhosts.d
diff --git a/larpe/tags/release-1.0/conf/apache2-vhost-larpe-common b/larpe/tags/release-1.0/conf/apache2-vhost-larpe-common
new file mode 100644
index 0000000..3e3d86d
--- /dev/null
+++ b/larpe/tags/release-1.0/conf/apache2-vhost-larpe-common
@@ -0,0 +1,19 @@
+# Static files
+DocumentRoot /usr/share/larpe/web/
+
+# Python application
+SCGIMount / 127.0.0.1:3007
+
+# Static files for larpe
+<Location /larpe/>
+ ProxyPass !
+ SCGIHandler off
+</Location>
+
+# Larpe python application
+<Location /liberty/>
+ ProxyPass !
+</Location>
+
+# No gzip compression
+RequestHeader unset Accept-Encoding
diff --git a/larpe/tags/release-1.0/conf/output_filter_base.py b/larpe/tags/release-1.0/conf/output_filter_base.py
new file mode 100644
index 0000000..aff82da
--- /dev/null
+++ b/larpe/tags/release-1.0/conf/output_filter_base.py
@@ -0,0 +1,30 @@
+import re
+
+def outputfilter(filter):
+ # Only filter html code
+ if filter.req.content_type is not None:
+ is_html = re.search('text/html', filter.req.content_type)
+ if filter.req.content_type is None or not is_html:
+ filter.pass_on()
+ else:
+ if not hasattr(filter.req, 'temp_doc'):
+ # Create a new attribute to hold the document
+ filter.req.temp_doc = []
+ # If content-length ended up wrong, Gecko browsers truncated data
+ if 'Content-Length' in filter.req.headers_out:
+ del filter.req.headers_out['Content-Length']
+
+ temp_doc = filter.req.temp_doc
+ s = filter.read()
+ # Could get '' at any point, but only get None at end
+ while s:
+ temp_doc.append(s)
+ s = filter.read()
+
+ # The end
+ if s is None:
+ page = ''.join(temp_doc)
+ page = filter_page(filter, page)
+ filter.write(page)
+ filter.close()
+
diff --git a/larpe/tags/release-1.0/debian/changelog b/larpe/tags/release-1.0/debian/changelog
new file mode 100644
index 0000000..96298e2
--- /dev/null
+++ b/larpe/tags/release-1.0/debian/changelog
@@ -0,0 +1,63 @@
+larpe (1.0-1) unstable; urgency=low
+
+ * New release
+ - SAML 2.0 and ID-FF 1.2 support (authentication and logout)
+ - Form prefilling with ID-WSF 2.0
+ - Configuration assistant (wizard-like) for configuring new sites
+ - Fully tested for several sites with very different behaviours
+ - Plugin system to handle specific behaviour of some sites
+ - Automatic Apache 2 configuration
+ - Automatic creation of Apache python filters to transform authentication boxes on the sites
+ - Support for proxies
+ - Logging and debug options
+
+ -- Damien Laniel <dlaniel@entrouvert.com> Mon, 09 Mar 2009 11:19:49 +0100
+
+larpe (0.2.1-1) unstable; urgency=low
+
+ * New release
+
+ -- Damien Laniel <dlaniel@entrouvert.com> Wed, 20 Jun 2007 15:43:16 +0200
+
+larpe (0.2.0-1) unstable; urgency=low
+
+ * New release
+
+ -- Damien Laniel <dlaniel@entrouvert.com> Tue, 30 Jan 2007 18:07:04 +0100
+
+larpe (0.1.1-2) unstable; urgency=low
+
+ * Use python2.4
+
+ -- Damien Laniel <dlaniel@entrouvert.com> Tue, 19 Dec 2006 17:21:05 +0100
+
+larpe (0.1.1-1) unstable; urgency=low
+
+ * New release
+
+ -- Damien Laniel <dlaniel@entrouvert.com> Thu, 5 Oct 2006 11:47:53 +0200
+
+larpe (0.1.0-1) unstable; urgency=low
+
+ * New release
+
+ -- Damien Laniel <dlaniel@entrouvert.com> Wed, 4 Oct 2006 10:19:26 +0200
+
+larpe (0.0.4-1) unstable; urgency=low
+
+ * New version, many improvements, more compatible sites, some bug fixes
+
+ -- Damien Laniel <dlaniel@entrouvert.com> Tue, 3 Oct 2006 20:44:06 +0200
+
+larpe (0.0.3-1) unstable; urgency=low
+
+ * New version, many improvements, more compatible sites, some bug fixes
+
+ -- Damien Laniel <dlaniel@entrouvert.com> Mon, 25 Sep 2006 11:11:36 +0200
+
+larpe (0.0.2-1) unstable; urgency=low
+
+ * Initial package.
+
+ -- Damien Laniel <dlaniel@entrouvert.com> Fri, 08 Sep 2006 16:00:00 +0200
+
diff --git a/larpe/tags/release-1.0/debian/compat b/larpe/tags/release-1.0/debian/compat
new file mode 100644
index 0000000..7ed6ff8
--- /dev/null
+++ b/larpe/tags/release-1.0/debian/compat
@@ -0,0 +1 @@
+5
diff --git a/larpe/tags/release-1.0/debian/config b/larpe/tags/release-1.0/debian/config
new file mode 100755
index 0000000..9543f2d
--- /dev/null
+++ b/larpe/tags/release-1.0/debian/config
@@ -0,0 +1,24 @@
+#!/bin/sh -e
+
+# Source debconf library.
+. /usr/share/debconf/confmodule
+
+# Hostname
+#db_input high larpe/hostname || true
+#db_go
+
+# Administrator email address
+#db_input medium larpe/admin_email || true
+#db_go
+
+# Enable this vhost
+#db_input high larpe/enable_vhost || true
+#db_go
+
+# Administrator login
+#db_input high larpe/admin_username || true
+#db_go
+
+# Administrator password
+#db_input high larpe/admin_password || true
+#db_go
diff --git a/larpe/tags/release-1.0/debian/control b/larpe/tags/release-1.0/debian/control
new file mode 100644
index 0000000..20f26b5
--- /dev/null
+++ b/larpe/tags/release-1.0/debian/control
@@ -0,0 +1,17 @@
+Source: larpe
+Section: web
+Priority: optional
+Maintainer: Damien Laniel <dlaniel@entrouvert.com>
+Build-Depends: debhelper (>= 5.0.37.2), python, python-central (>= 0.5), gettext
+Standards-Version: 3.7.2.0
+XS-Python-Version: current
+
+Package: larpe
+Architecture: any
+XB-Python-Version: ${python:Versions}
+Depends: ${python:Depends}, python-quixote | quixote (>= 2.0), python-lasso (>= 0.6.5), python-scgi, python-libxml2, apache2, libapache2-mod-scgi, libapache2-mod-python, libapache2-mod-proxy-html
+Description: Liberty Alliance Reverse Proxy
+ Larpe allows any service provider (that is a website) to use Liberty Alliance
+ identity management and Single Sign On features without changing the code of
+ the service provider itself.
+ .
diff --git a/larpe/tags/release-1.0/debian/copyright b/larpe/tags/release-1.0/debian/copyright
new file mode 100644
index 0000000..c81e494
--- /dev/null
+++ b/larpe/tags/release-1.0/debian/copyright
@@ -0,0 +1,27 @@
+This package was debianized by Damien Laniel <dlaniel@entrouvert.com> on
+Fri, 08 Sep 2006 16:00:00 +0200.
+
+Upstream Author: Damien Laniel <dlaniel@entrouvert.com>
+
+Copyright (c) 2005 Entr'ouvert;
+copyright (c) 2003-2005 dotclear for some 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+On Debian GNU/Linux systems, the complete text of the GNU General Public
+License can be found in `/usr/share/common-licenses/GPL'.
+
diff --git a/larpe/tags/release-1.0/debian/dirs b/larpe/tags/release-1.0/debian/dirs
new file mode 100644
index 0000000..cc80198
--- /dev/null
+++ b/larpe/tags/release-1.0/debian/dirs
@@ -0,0 +1,4 @@
+etc/apache2/sites-available
+etc/larpe
+usr/sbin
+var/lib/larpe
diff --git a/larpe/tags/release-1.0/debian/docs b/larpe/tags/release-1.0/debian/docs
new file mode 100644
index 0000000..55bc0a6
--- /dev/null
+++ b/larpe/tags/release-1.0/debian/docs
@@ -0,0 +1,2 @@
+README
+AUTHORS
diff --git a/larpe/tags/release-1.0/debian/init b/larpe/tags/release-1.0/debian/init
new file mode 100644
index 0000000..edca4da
--- /dev/null
+++ b/larpe/tags/release-1.0/debian/init
@@ -0,0 +1,82 @@
+#! /bin/sh
+### BEGIN INIT INFO
+# Provides: larpe
+# Required-Start: $local_fs $network
+# Required-Stop: $local_fs $network
+# Default-Start: 2 3 4 5
+# Default-Stop: 0 1 6
+# Short-Description: Start Larpe Liberty Alliance reverse proxy
+# Description: Start Larpe Liberty Alliance reverse proxy
+### END INIT INFO
+
+set -e
+
+# Gracefully exit if the package has been removed.
+test -x $DAEMON || exit 0
+
+# Source function library
+. /lib/lsb/init-functions
+
+PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
+DESC="larpe"
+NAME=larpe
+DAEMON=/usr/sbin/larpectl
+PIDFILE=/var/run/$NAME.pid
+SCRIPTNAME=/etc/init.d/$NAME
+
+
+# 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 --oknodo \
+ --chuid www-data:www-data --make-pidfile --background --exec $DAEMON -- start $OPTIONS
+}
+
+#
+# Function that stops the daemon/service.
+#
+d_stop() {
+ start-stop-daemon --stop --quiet --pidfile $PIDFILE --oknodo
+ rm -f $PIDFILE
+}
+
+case "$1" in
+ start)
+ log_begin_msg "Starting $DESC: $NAME"
+ d_start
+ log_end_msg $?
+ ;;
+
+ stop)
+ log_begin_msg "Stopping $DESC: $NAME"
+ d_stop
+ log_end_msg $?
+ ;;
+
+ 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".
+ #
+ log_begin_msg "Restarting $DESC: $NAME"
+ d_stop
+ sleep 1
+ d_start
+ log_end_msg $?
+ ;;
+
+ *)
+ echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
+ exit 1
+ ;;
+esac
+
+exit 0
diff --git a/larpe/tags/release-1.0/debian/larpe-reload-apache2-script b/larpe/tags/release-1.0/debian/larpe-reload-apache2-script
new file mode 100755
index 0000000..5431d0d
--- /dev/null
+++ b/larpe/tags/release-1.0/debian/larpe-reload-apache2-script
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+/etc/init.d/apache2 reload
diff --git a/larpe/tags/release-1.0/debian/postinst b/larpe/tags/release-1.0/debian/postinst
new file mode 100644
index 0000000..6aa61f2
--- /dev/null
+++ b/larpe/tags/release-1.0/debian/postinst
@@ -0,0 +1,66 @@
+#! /bin/sh
+# postinst script for larpe
+#
+# 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=larpe
+VERSION=2.4
+LIB="/usr/lib/python$VERSION"
+DIRLIST="/usr/share/pycentral/larpe/site-packages/larpe/"
+
+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
+
+ # Load Apache 2 modules
+ for module in "proxy" "rewrite" "headers" "proxy_http"; do
+ a2enmod ${module} > /dev/null || true
+ done
+
+ # Restart Apache 2
+ set +e
+ if [ -x /usr/sbin/invoke-rc.d ]; then
+ invoke-rc.d apache2 restart || true
+ else
+ /etc/init.d/apache2 restart || true
+ fi
+ set -e
+ ;;
+
+ *)
+ 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
diff --git a/larpe/tags/release-1.0/debian/prerm b/larpe/tags/release-1.0/debian/prerm
new file mode 100644
index 0000000..525d726
--- /dev/null
+++ b/larpe/tags/release-1.0/debian/prerm
@@ -0,0 +1,41 @@
+#! /bin/sh
+# prerm script for larpe
+#
+# 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=larpe
+
+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
diff --git a/larpe/tags/release-1.0/debian/pycompat b/larpe/tags/release-1.0/debian/pycompat
new file mode 100644
index 0000000..0cfbf08
--- /dev/null
+++ b/larpe/tags/release-1.0/debian/pycompat
@@ -0,0 +1 @@
+2
diff --git a/larpe/tags/release-1.0/debian/rules b/larpe/tags/release-1.0/debian/rules
new file mode 100755
index 0000000..2ff8134
--- /dev/null
+++ b/larpe/tags/release-1.0/debian/rules
@@ -0,0 +1,73 @@
+#!/usr/bin/make -f
+# GNU copyright 1997 to 1999 by Joey Hess.
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+PYTHON=/usr/bin/python2.4
+
+LARPE_USER = www-data
+LARPE_GROUP = www-data
+
+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
+
+ make clean
+
+ dh_clean
+
+install: build
+ dh_testdir
+ dh_testroot
+ dh_clean -k
+ dh_installdirs
+
+ make install prefix=/usr DESTDIR=$(CURDIR)/debian/larpe
+ # Apache Vhost
+ dh_install conf/apache2-vhost-larpe etc/apache2/sites-available
+ # Apache reload script
+ dh_install debian/larpe-reload-apache2-script usr/sbin
+
+ # Give files ownership to Larpe user and group
+ chown -R $(LARPE_USER):$(LARPE_GROUP) $(CURDIR)/debian/larpe/usr/share/larpe/
+ chown -R $(LARPE_USER):$(LARPE_GROUP) $(CURDIR)/debian/larpe/var/lib/larpe/
+ chgrp $(LARPE_GROUP) $(CURDIR)/debian/larpe/usr/sbin/larpe-reload-apache2
+
+# 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_installchangelogs
+ dh_link
+ dh_strip
+ dh_compress
+ dh_fixperms -X /var/lib/larpe -X /usr/sbin/larpe-reload-apache2
+ dh_pycentral
+ dh_installdeb
+ dh_gencontrol
+ dh_md5sums
+ dh_builddeb
+
+binary: binary-indep binary-arch
+.PHONY: build clean binary-indep binary-arch binary install
diff --git a/larpe/tags/release-1.0/debian/templates b/larpe/tags/release-1.0/debian/templates
new file mode 100644
index 0000000..9a6cc43
--- /dev/null
+++ b/larpe/tags/release-1.0/debian/templates
@@ -0,0 +1,37 @@
+Template: larpe/hostname
+Type: string
+Default: localhost
+Description: Hostname :
+ This is the name by which this reverse-proxy will be known on the network.
+ Your DNS server must have been configured accordingly.
+
+Template: larpe/enable_vhost
+Type: boolean
+Default: false
+Description: Enable this vhost :
+ A new virtual host for Larpe will be created with the hostname you chose. It may break
+ your Apache2 configuration.
+ .
+ If you didn't tweak your Apache2 configuration a lot
+ and you don't have vital websites on the same server, you can safely say "yes" here
+ to enable it, and fix it later if needed.
+ .
+ If you prefer checking this vhost will fit well with your Apache2 configuration first,
+ and enable it by yourself later, say "no".
+
+Template: larpe/admin_username
+Type: string
+Default: admin
+Description: Administrator login :
+ This is the login which will be used to connect to the administrator interface
+
+Template: larpe/admin_password
+Type: password
+Description: Administrator password :
+ This is the password which will be used to connect to the administrator interface
+
+Template: larpe/admin_email
+Type: string
+Default: root@localhost
+Description: Administrator email address :
+ This is the email address to which problem reports will be sent
diff --git a/larpe/tags/release-1.0/doc/Makefile b/larpe/tags/release-1.0/doc/Makefile
new file mode 100644
index 0000000..9a27348
--- /dev/null
+++ b/larpe/tags/release-1.0/doc/Makefile
@@ -0,0 +1,8 @@
+all:
+ $(MAKE) -C en
+
+clean:
+ $(MAKE) -C en clean
+
+.PHONY: clean
+
diff --git a/larpe/tags/release-1.0/doc/en/Makefile b/larpe/tags/release-1.0/doc/en/Makefile
new file mode 100644
index 0000000..295ee43
--- /dev/null
+++ b/larpe/tags/release-1.0/doc/en/Makefile
@@ -0,0 +1,35 @@
+RST2HTML = rst2html
+RST2LATEX = ../scripts/rst2latex.py
+PDFLATEX = pdflatex
+RM = rm -f
+
+all: larpe-admin.pdf larpe-admin.html
+
+%.html: %.rst
+ $(RST2HTML) --stylesheet=default.css --link-stylesheet --language=en $? > $@
+
+figures-no-alpha-stamp:
+ -$(RM) -r figures-no-alpha/
+ mkdir figures-no-alpha/
+ for F in figures/*.png; do \
+ ../scripts/removealpha.sh $$F figures-no-alpha/`basename $$F`; \
+ done
+ touch figures-no-alpha-stamp
+
+%.tex: %.rst #figures-no-alpha-stamp
+ cat $? | sed -e 's/figures\//figures-no-alpha\//' \
+ -e 's/ ::$$/ : ::/g' \
+ -e 's/.. section-numbering:://' | $(RST2LATEX) --language=en > $@
+
+%.pdf: %.tex custom.tex
+ $(PDFLATEX) $?
+ logfile=`echo "$@" |sed -r "s/(.*)....$$/\\1/"`.log; while [ -f "$$logfile" -a -n "`grep "Rerun to get cross-references right" $$logfile`" ]; do $(PDFLATEX) $< ; done
+
+clean:
+ -$(RM) *.aux *.toc *.log *.out
+ -$(RM) larpe-admin.pdf
+ -$(RM) larpe-admin.tex
+ -$(RM) larpe-admin.html
+ -$(RM) -r figures-no-alpha figures-no-alpha-stamp
+
+.PHONY: all clean
diff --git a/larpe/tags/release-1.0/doc/en/custom.tex b/larpe/tags/release-1.0/doc/en/custom.tex
new file mode 100644
index 0000000..cf42eb7
--- /dev/null
+++ b/larpe/tags/release-1.0/doc/en/custom.tex
@@ -0,0 +1,45 @@
+\usepackage{float,fancyhdr,lscape,sectsty,colortbl,color,lastpage,setspace}
+\usepackage[perpage,bottom]{footmisc}
+\usepackage[hang]{caption2}
+\usepackage{marvosym}
+
+\usepackage{float,url,listings,tocbibind,fancyhdr,calc,placeins}
+
+\usepackage{palatino}
+\usepackage[Glenn]{fncychap}
+
+\pagestyle{fancy}
+\fancyhead{}
+\fancyfoot{}
+\fancyhead[L]{Authentic}
+\fancyhead[R]{Administrator Guide}
+\fancyfoot[C]{Page \thepage}
+\addtolength{\headheight}{1.6pt}
+
+\setlength\parindent{0pt}
+\setlength{\parskip}{1ex plus 0.5ex minus 0.2ex}
+\setlength\abovecaptionskip{0.1ex}
+
+\makeatletter
+\renewcommand{\maketitle}{\begin{titlepage}%
+ \let\footnotesize\small
+ \let\footnoterule\relax
+ \parindent \z@
+ \reset@font
+ \null\vfil
+ \begin{flushleft}
+ \huge \@title
+ \end{flushleft}
+ \par
+ \hrule height 1pt
+ \par
+ \begin{flushright}
+ \LARGE \@author \par
+ \end{flushright}
+ \vskip 60\p@
+ \vfil\null
+ \end{titlepage}%
+ \setcounter{footnote}{0}%
+}
+\makeatother
+
diff --git a/larpe/tags/release-1.0/doc/en/default.css b/larpe/tags/release-1.0/doc/en/default.css
new file mode 100644
index 0000000..d198a16
--- /dev/null
+++ b/larpe/tags/release-1.0/doc/en/default.css
@@ -0,0 +1,143 @@
+body {
+ font-family: sans-serif;
+}
+
+
+h1 a, h2 a, h3 a, h4 a {
+ text-decoration: inherit;
+ color: inherit;
+}
+
+pre.literal-block {
+ background: #eee;
+ border: 1px inset black;
+ padding: 2px;
+ margin: auto 10px;
+ overflow: auto;
+}
+
+h1.title {
+ text-align: center;
+ background: #eef;
+ border: 1px solid #aaf;
+ letter-spacing: 1px;
+}
+
+div.section {
+ margin-bottom: 2em;
+}
+
+div.section h1 {
+ padding: 0 15px;
+ background: #eef;
+ border: 1px solid #aaf;
+}
+
+div.section h2 {
+ padding: 0 15px;
+ background: #eef;
+ border: 1px solid #aaf;
+}
+
+div.document {
+ margin-top: 1em;
+ border-top: 1px solid #aaf;
+ border-bottom: 1px solid #aaf;
+}
+
+div.section p,
+div.section ul {
+ text-align: justify;
+}
+
+div.contents {
+ float: right;
+ border: 1px solid black;
+ margin: 1em;
+ background: #eef;
+ max-width: 33%;
+}
+
+div#building-liberty-services-with-lasso div#table-of-contents {
+ max-width: inherit;
+ float: none;
+ background: white url(lasso.png) bottom right no-repeat;
+}
+
+div.contents ul {
+ padding-left: 1em;
+ list-style: none;
+}
+
+div.contents li {
+ padding-bottom: 2px;
+}
+
+div.contents p {
+ background: #ddf;
+ text-align: center;
+ border-bottom: 1px solid black;
+ margin: 0;
+}
+
+th.docinfo-name {
+ text-align: right;
+ padding-right: 0.5em;
+}
+
+dd {
+ margin-bottom: 1ex;
+}
+
+table.table {
+ margin: 1ex 0;
+ border-spacing: 0px;
+}
+
+
+table.table th {
+ padding: 0px 1ex;
+ background: #eef;
+ font-weight: normal;
+}
+
+
+table.table td {
+ padding: 0 0.5ex;
+}
+
+div.note, div.warning {
+ padding: 0.3ex;
+ padding-left: 60px;
+ min-height: 50px;
+ margin: 1ex 1em;
+}
+
+div.note {
+ background: #ffa url(note.png) top left no-repeat;
+ border: 1px solid #fd8;
+}
+
+div.warning {
+ background: #ffd url(warning.png) top left no-repeat;
+}
+
+p.admonition-title {
+ font-weight: bold;
+ display: inline;
+ display: none;
+ padding-right: 1em;
+}
+
+div.figure {
+ margin: 0 auto;
+ width: 70%;
+ min-width: 800px;
+ text-align: center;
+}
+
+div.figure p.caption {
+ font-style: italic;
+ margin: 1ex 0 2em 0;
+ text-align: center;
+}
diff --git a/larpe/tags/release-1.0/doc/en/fncychap.sty b/larpe/tags/release-1.0/doc/en/fncychap.sty
new file mode 100644
index 0000000..9c4ed8d
--- /dev/null
+++ b/larpe/tags/release-1.0/doc/en/fncychap.sty
@@ -0,0 +1,490 @@
+%%% Copyright Ulf A. Lindgren
+%%%
+%%% Note Premission is granted to modify this file under
+%%% the condition that it is saved using another
+%%% file and package name.
+%%%
+%%% Revision 1.1 (1997)
+%%%
+%%% Jan. 8th Modified package name base date option
+%%% Jan. 22th Modified FmN and FmTi for error in book.cls
+%%% \MakeUppercase{#}->{\MakeUppercase#}
+%%% Apr. 6th Modified Lenny option to prevent undesired
+%%% skip of line.
+%%% Nov. 8th Fixed \@chapapp for AMS
+%%%
+%%% Revision 1.2 (1998)
+%%%
+%%% Feb. 11th Fixed appendix problem related to Bjarne
+%%% Aug. 11th Fixed problem related to 11pt and 12pt
+%%% suggested by Tomas Lundberg. THANKS!
+%%%
+%%% Revision 1.3 (2004)
+%%% Sep. 20th problem with frontmatter, mainmatter and
+%%% backmatter, pointed out by Lapo Mori
+%%%
+%%% Revision 1.31 (2004)
+%%% Sep. 21th problem with the Rejne definition streched text
+%%% caused ugly gaps in the vrule aligned with the title
+%%% text. Kindly pointed out to me by Hendri Adriaens
+%%%
+%%% Revision 1.32 (2005)
+%%% Jun. 23th compatibility problem with the KOMA class 'scrbook.cls'
+%%% a remedy is a redefinition of '\@schapter' in
+%%% line with that used in KOMA. The problem was pointed
+%%% out to me by Mikkel Holm Olsen
+%%%
+%%% Revision 1.33 (2005)
+%%% Aug. 9th misspelled ``TWELV'' corrected, the error was pointed
+%%% out to me by George Pearson
+%%%
+
+
+%%% Last modified Aug. 9th 2005
+
+\NeedsTeXFormat{LaTeX2e}[1995/12/01]
+\ProvidesPackage{fncychap}
+ [2004/09/21 v1.33
+ LaTeX package (Revised chapters)]
+
+%%%% DEFINITION OF Chapapp variables
+\newcommand{\CNV}{\huge\bfseries}
+\newcommand{\ChNameVar}[1]{\renewcommand{\CNV}{#1}}
+
+
+%%%% DEFINITION OF TheChapter variables
+\newcommand{\CNoV}{\huge\bfseries}
+\newcommand{\ChNumVar}[1]{\renewcommand{\CNoV}{#1}}
+
+\newif\ifUCN
+\UCNfalse
+\newif\ifLCN
+\LCNfalse
+\def\ChNameLowerCase{\LCNtrue\UCNfalse}
+\def\ChNameUpperCase{\UCNtrue\LCNfalse}
+\def\ChNameAsIs{\UCNfalse\LCNfalse}
+
+%%%%% Fix for AMSBook 971008
+
+\@ifundefined{@chapapp}{\let\@chapapp\chaptername}{}
+
+
+%%%%% Fix for Bjarne and appendix 980211
+
+\newif\ifinapp
+\inappfalse
+\renewcommand\appendix{\par
+ \setcounter{chapter}{0}%
+ \setcounter{section}{0}%
+ \inapptrue%
+ \renewcommand\@chapapp{\appendixname}%
+ \renewcommand\thechapter{\@Alph\c@chapter}}
+
+%%%%% Fix for frontmatter, mainmatter, and backmatter 040920
+
+\@ifundefined{@mainmatter}{\newif\if@mainmatter \@mainmattertrue}{}
+
+%%%%%
+
+
+
+\newcommand{\FmN}[1]{%
+\ifUCN
+ {\MakeUppercase#1}\LCNfalse
+\else
+ \ifLCN
+ {\MakeLowercase#1}\UCNfalse
+ \else #1
+ \fi
+\fi}
+
+
+%%%% DEFINITION OF Title variables
+\newcommand{\CTV}{\Huge\bfseries}
+\newcommand{\ChTitleVar}[1]{\renewcommand{\CTV}{#1}}
+
+%%%% DEFINITION OF the basic rule width
+\newlength{\RW}
+\setlength{\RW}{1pt}
+\newcommand{\ChRuleWidth}[1]{\setlength{\RW}{#1}}
+
+\newif\ifUCT
+\UCTfalse
+\newif\ifLCT
+\LCTfalse
+\def\ChTitleLowerCase{\LCTtrue\UCTfalse}
+\def\ChTitleUpperCase{\UCTtrue\LCTfalse}
+\def\ChTitleAsIs{\UCTfalse\LCTfalse}
+\newcommand{\FmTi}[1]{%
+\ifUCT
+ {\MakeUppercase#1}\LCTfalse
+\else
+ \ifLCT
+ {\MakeLowercase#1}\UCTfalse
+ \else {#1}
+ \fi
+\fi}
+
+
+
+\newlength{\mylen}
+\newlength{\myhi}
+\newlength{\px}
+\newlength{\py}
+\newlength{\pyy}
+\newlength{\pxx}
+
+
+\def\mghrulefill#1{\leavevmode\leaders\hrule\@height #1\hfill\kern\z@}
+
+\newcommand{\DOCH}{%
+ \CNV\FmN{\@chapapp}\space \CNoV\thechapter
+ \par\nobreak
+ \vskip 20\p@
+ }
+\newcommand{\DOTI}[1]{%
+ \CTV\FmTi{#1}\par\nobreak
+ \vskip 40\p@
+ }
+\newcommand{\DOTIS}[1]{%
+ \CTV\FmTi{#1}\par\nobreak
+ \vskip 40\p@
+ }
+
+%%%%%% SONNY DEF
+
+\DeclareOption{Sonny}{%
+ \ChNameVar{\Large\sf}
+ \ChNumVar{\Huge}
+ \ChTitleVar{\Large\sf}
+ \ChRuleWidth{0.5pt}
+ \ChNameUpperCase
+ \renewcommand{\DOCH}{%
+ \raggedleft
+ \CNV\FmN{\@chapapp}\space \CNoV\thechapter
+ \par\nobreak
+ \vskip 40\p@}
+ \renewcommand{\DOTI}[1]{%
+ \CTV\raggedleft\mghrulefill{\RW}\par\nobreak
+ \vskip 5\p@
+ \CTV\FmTi{#1}\par\nobreak
+ \mghrulefill{\RW}\par\nobreak
+ \vskip 40\p@}
+ \renewcommand{\DOTIS}[1]{%
+ \CTV\raggedleft\mghrulefill{\RW}\par\nobreak
+ \vskip 5\p@
+ \CTV\FmTi{#1}\par\nobreak
+ \mghrulefill{\RW}\par\nobreak
+ \vskip 40\p@}
+}
+
+%%%%%% LENNY DEF
+
+\DeclareOption{Lenny}{%
+
+ \ChNameVar{\fontsize{14}{16}\usefont{OT1}{phv}{m}{n}\selectfont}
+ \ChNumVar{\fontsize{60}{62}\usefont{OT1}{ptm}{m}{n}\selectfont}
+ \ChTitleVar{\Huge\bfseries\rm}
+ \ChRuleWidth{1pt}
+ \renewcommand{\DOCH}{%
+ \settowidth{\px}{\CNV\FmN{\@chapapp}}
+ \addtolength{\px}{2pt}
+ \settoheight{\py}{\CNV\FmN{\@chapapp}}
+ \addtolength{\py}{1pt}
+
+ \settowidth{\mylen}{\CNV\FmN{\@chapapp}\space\CNoV\thechapter}
+ \addtolength{\mylen}{1pt}
+ \settowidth{\pxx}{\CNoV\thechapter}
+ \addtolength{\pxx}{-1pt}
+
+ \settoheight{\pyy}{\CNoV\thechapter}
+ \addtolength{\pyy}{-2pt}
+ \setlength{\myhi}{\pyy}
+ \addtolength{\myhi}{-1\py}
+ \par
+ \parbox[b]{\textwidth}{%
+ \rule[\py]{\RW}{\myhi}%
+ \hskip -\RW%
+ \rule[\pyy]{\px}{\RW}%
+ \hskip -\px%
+ \raggedright%
+ \CNV\FmN{\@chapapp}\space\CNoV\thechapter%
+ \hskip1pt%
+ \mghrulefill{\RW}%
+ \rule{\RW}{\pyy}\par\nobreak%
+ \vskip -\baselineskip%
+ \vskip -\pyy%
+ \hskip \mylen%
+ \mghrulefill{\RW}\par\nobreak%
+ \vskip \pyy}%
+ \vskip 20\p@}
+
+
+ \renewcommand{\DOTI}[1]{%
+ \raggedright
+ \CTV\FmTi{#1}\par\nobreak
+ \vskip 40\p@}
+
+ \renewcommand{\DOTIS}[1]{%
+ \raggedright
+ \CTV\FmTi{#1}\par\nobreak
+ \vskip 40\p@}
+ }
+
+
+%%%%%%% GLENN DEF
+
+
+\DeclareOption{Glenn}{%
+ \ChNameVar{\bfseries\Large\sf}
+ \ChNumVar{\Huge}
+ \ChTitleVar{\bfseries\Large\rm}
+ \ChRuleWidth{1pt}
+ \ChNameUpperCase
+ \ChTitleUpperCase
+ \renewcommand{\DOCH}{%
+ \settoheight{\myhi}{\CTV\FmTi{Test}}
+ \setlength{\py}{\baselineskip}
+ \addtolength{\py}{\RW}
+ \addtolength{\py}{\myhi}
+ \setlength{\pyy}{\py}
+ \addtolength{\pyy}{-1\RW}
+
+ \raggedright
+ \CNV\FmN{\@chapapp}\space\CNoV\thechapter
+ \hskip 3pt\mghrulefill{\RW}\rule[-1\pyy]{2\RW}{\py}\par\nobreak}
+
+ \renewcommand{\DOTI}[1]{%
+ \addtolength{\pyy}{-4pt}
+ \settoheight{\myhi}{\CTV\FmTi{#1}}
+ \addtolength{\myhi}{\py}
+ \addtolength{\myhi}{-1\RW}
+ \vskip -1\pyy
+ \rule{2\RW}{\myhi}\mghrulefill{\RW}\hskip 2pt
+ \raggedleft\CTV\FmTi{#1}\par\nobreak
+ \vskip 80\p@}
+
+\newlength{\backskip}
+ \renewcommand{\DOTIS}[1]{%
+% \setlength{\py}{10pt}
+% \setlength{\pyy}{\py}
+% \addtolength{\pyy}{\RW}
+% \setlength{\myhi}{\baselineskip}
+% \addtolength{\myhi}{\pyy}
+% \mghrulefill{\RW}\rule[-1\py]{2\RW}{\pyy}\par\nobreak
+% \addtolength{}{}
+%\vskip -1\baselineskip
+% \rule{2\RW}{\myhi}\mghrulefill{\RW}\hskip 2pt
+% \raggedleft\CTV\FmTi{#1}\par\nobreak
+% \vskip 60\p@}
+%% Fix suggested by Tomas Lundberg
+ \setlength{\py}{25pt} % eller vad man vill
+ \setlength{\pyy}{\py}
+ \setlength{\backskip}{\py}
+ \addtolength{\backskip}{2pt}
+ \addtolength{\pyy}{\RW}
+ \setlength{\myhi}{\baselineskip}
+ \addtolength{\myhi}{\pyy}
+ \mghrulefill{\RW}\rule[-1\py]{2\RW}{\pyy}\par\nobreak
+ \vskip -1\backskip
+ \rule{2\RW}{\myhi}\mghrulefill{\RW}\hskip 3pt %
+ \raggedleft\CTV\FmTi{#1}\par\nobreak
+ \vskip 40\p@}
+ }
+
+%%%%%%% CONNY DEF
+
+\DeclareOption{Conny}{%
+ \ChNameUpperCase
+ \ChTitleUpperCase
+ \ChNameVar{\centering\Huge\rm\bfseries}
+ \ChNumVar{\Huge}
+ \ChTitleVar{\centering\Huge\rm}
+ \ChRuleWidth{2pt}
+
+ \renewcommand{\DOCH}{%
+ \mghrulefill{3\RW}\par\nobreak
+ \vskip -0.5\baselineskip
+ \mghrulefill{\RW}\par\nobreak
+ \CNV\FmN{\@chapapp}\space \CNoV\thechapter
+ \par\nobreak
+ \vskip -0.5\baselineskip
+ }
+ \renewcommand{\DOTI}[1]{%
+ \mghrulefill{\RW}\par\nobreak
+ \CTV\FmTi{#1}\par\nobreak
+ \vskip 60\p@
+ }
+ \renewcommand{\DOTIS}[1]{%
+ \mghrulefill{\RW}\par\nobreak
+ \CTV\FmTi{#1}\par\nobreak
+ \vskip 60\p@
+ }
+ }
+
+%%%%%%% REJNE DEF
+
+\DeclareOption{Rejne}{%
+
+ \ChNameUpperCase
+ \ChTitleUpperCase
+ \ChNameVar{\centering\Large\rm}
+ \ChNumVar{\Huge}
+ \ChTitleVar{\centering\Huge\rm}
+ \ChRuleWidth{1pt}
+ \renewcommand{\DOCH}{%
+ \settoheight{\py}{\CNoV\thechapter}
+ \parskip=0pt plus 1pt % Set parskip to default, just in case v1.31
+ \addtolength{\py}{-1pt}
+ \CNV\FmN{\@chapapp}\par\nobreak
+ \vskip 20\p@
+ \setlength{\myhi}{2\baselineskip}
+ \setlength{\px}{\myhi}
+ \addtolength{\px}{-1\RW}
+ \rule[-1\px]{\RW}{\myhi}\mghrulefill{\RW}\hskip
+ 10pt\raisebox{-0.5\py}{\CNoV\thechapter}\hskip 10pt\mghrulefill{\RW}\rule[-1\px]{\RW}{\myhi}\par\nobreak
+ \vskip -3\p@% Added -2pt vskip to correct for streched text v1.31
+ }
+ \renewcommand{\DOTI}[1]{%
+ \setlength{\mylen}{\textwidth}
+ \parskip=0pt plus 1pt % Set parskip to default, just in case v1.31
+ \addtolength{\mylen}{-2\RW}
+ {\vrule width\RW}\parbox{\mylen}{\CTV\FmTi{#1}}{\vrule width\RW}\par\nobreak%
+ \vskip -3pt\rule{\RW}{2\baselineskip}\mghrulefill{\RW}\rule{\RW}{2\baselineskip}%
+ \vskip 60\p@% Added -2pt in vskip to correct for streched text v1.31
+ }
+ \renewcommand{\DOTIS}[1]{%
+ \setlength{\py}{\fboxrule}
+ \setlength{\fboxrule}{\RW}
+ \setlength{\mylen}{\textwidth}
+ \addtolength{\mylen}{-2\RW}
+ \fbox{\parbox{\mylen}{\vskip 2\baselineskip\CTV\FmTi{#1}\par\nobreak\vskip \baselineskip}}
+ \setlength{\fboxrule}{\py}
+ \vskip 60\p@
+ }
+ }
+
+
+%%%%%%% BJARNE DEF
+
+\DeclareOption{Bjarne}{%
+ \ChNameUpperCase
+ \ChTitleUpperCase
+ \ChNameVar{\raggedleft\normalsize\rm}
+ \ChNumVar{\raggedleft \bfseries\Large}
+ \ChTitleVar{\raggedleft \Large\rm}
+ \ChRuleWidth{1pt}
+
+
+%% Note thechapter -> c@chapter fix appendix bug
+%% Fixed misspelled 12
+
+ \newcounter{AlphaCnt}
+ \newcounter{AlphaDecCnt}
+ \newcommand{\AlphaNo}{%
+ \ifcase\number\theAlphaCnt
+ \ifnum\c@chapter=0
+ ZERO\else{}\fi
+ \or ONE\or TWO\or THREE\or FOUR\or FIVE
+ \or SIX\or SEVEN\or EIGHT\or NINE\or TEN
+ \or ELEVEN\or TWELVE\or THIRTEEN\or FOURTEEN\or FIFTEEN
+ \or SIXTEEN\or SEVENTEEN\or EIGHTEEN\or NINETEEN\fi
+}
+
+ \newcommand{\AlphaDecNo}{%
+ \setcounter{AlphaDecCnt}{0}
+ \@whilenum\number\theAlphaCnt>0\do
+ {\addtocounter{AlphaCnt}{-10}
+ \addtocounter{AlphaDecCnt}{1}}
+ \ifnum\number\theAlphaCnt=0
+ \else
+ \addtocounter{AlphaDecCnt}{-1}
+ \addtocounter{AlphaCnt}{10}
+ \fi
+
+
+ \ifcase\number\theAlphaDecCnt\or TEN\or TWENTY\or THIRTY\or
+ FORTY\or FIFTY\or SIXTY\or SEVENTY\or EIGHTY\or NINETY\fi
+ }
+ \newcommand{\TheAlphaChapter}{%
+
+ \ifinapp
+ \thechapter
+ \else
+ \setcounter{AlphaCnt}{\c@chapter}
+ \ifnum\c@chapter<20
+ \AlphaNo
+ \else
+ \AlphaDecNo\AlphaNo
+ \fi
+ \fi
+ }
+ \renewcommand{\DOCH}{%
+ \mghrulefill{\RW}\par\nobreak
+ \CNV\FmN{\@chapapp}\par\nobreak
+ \CNoV\TheAlphaChapter\par\nobreak
+ \vskip -1\baselineskip\vskip 5pt\mghrulefill{\RW}\par\nobreak
+ \vskip 20\p@
+ }
+ \renewcommand{\DOTI}[1]{%
+ \CTV\FmTi{#1}\par\nobreak
+ \vskip 40\p@
+ }
+ \renewcommand{\DOTIS}[1]{%
+ \CTV\FmTi{#1}\par\nobreak
+ \vskip 40\p@
+ }
+}
+
+\DeclareOption*{%
+ \PackageWarning{fancychapter}{unknown style option}
+ }
+
+\ProcessOptions* \relax
+
+\def\@makechapterhead#1{%
+ \vspace*{50\p@}%
+ {\parindent \z@ \raggedright \normalfont
+ \ifnum \c@secnumdepth >\m@ne
+ \if@mainmatter%%%%% Fix for frontmatter, mainmatter, and backmatter 040920
+ \DOCH
+ \fi
+ \fi
+ \interlinepenalty\@M
+ \DOTI{#1}
+ }}
+
+
+%%% Begin: To avoid problem with scrbook.cls (fncychap version 1.32)
+
+%%OUT:
+%\def\@schapter#1{\if@twocolumn
+% \@topnewpage[\@makeschapterhead{#1}]%
+% \else
+% \@makeschapterhead{#1}%
+% \@afterheading
+% \fi}
+
+%%IN:
+\def\@schapter#1{%
+\if@twocolumn%
+ \@makeschapterhead{#1}%
+\else%
+ \@makeschapterhead{#1}%
+ \@afterheading%
+\fi}
+
+%%% End: To avoid problem with scrbook.cls (fncychap version 1.32)
+
+\def\@makeschapterhead#1{%
+ \vspace*{50\p@}%
+ {\parindent \z@ \raggedright
+ \normalfont
+ \interlinepenalty\@M
+ \DOTIS{#1}
+ \vskip 40\p@
+ }}
+
+\endinput
+
+
diff --git a/larpe/tags/release-1.0/doc/en/larpe-admin.rst b/larpe/tags/release-1.0/doc/en/larpe-admin.rst
new file mode 100644
index 0000000..32ac1c9
--- /dev/null
+++ b/larpe/tags/release-1.0/doc/en/larpe-admin.rst
@@ -0,0 +1,202 @@
+=====================================
+Larpe - Administrator Guide
+=====================================
+
+:author: Damien Laniel
+:contact: dlaniel@entrouvert.com
+:copyright: Copyright © 2006 Entr'ouvert
+
+.. contents:: Table of contents
+
+Overview
+========
+
+Larpe is a Liberty Alliance Reverse Proxy. It allows any service provider
+(that is a website) to use Liberty Alliance features (Identity federation,
+Single Sign On and Single Logout) without changing the code of
+the service provider itself. It uses the Lasso_ library
+which is certified by the `Liberty Alliance`_ consortium. Lasso_ and Larpe
+are released under the terms of the `GNU GPL license`_.
+
+
+How to get and install Larpe
+============================
+
+Installation under Debian_ Sarge
+++++++++++++++++++++++++++++++++
+
+To work correctly Larpe relies on :
+
+* Apache2_ ;
+
+* Lasso_ (0.6.3) ;
+
+* Quixote_ (2.0) ;
+
+* SCGI_ ;
+
+* mod_python_ ;
+
+* libxml2 ;
+
+* mod_proxy_html.
+
+You will also need a Liberty Alliance Identity Provider, be it on the same server or not.
+We recommend Authentic_ for that need.
+
+Package Installation
+--------------------
+
+You need to add the following line to your /etc/apt/sources.list; this will
+give you access to the repository where Larpe is stored::
+
+ deb http://deb.entrouvert.org/ sarge main
+
+As root type::
+
+ apt-get update
+ apt-get install larpe
+
+And follow the debconf wizard to set it up.
+
+All the required packages are now installed and configured.
+
+You might need to change the "<VirtualHost \*>" in your apache2 configuration
+(/etc/apache2/sites-available/apache2-vhost-larpe) depending on how you
+previously configured apache.
+
+Don't forget to modify your /etc/hosts file if necessary. Larpe now works, the
+administration interface is reachable at http://your_domain_name/admin. The username
+and password are the ones you entered during the installation wizard.
+
+If you don't want to modify your sources.list file, you can manually dowload and
+install the required packages with the dpkg -i command :
+
+* Larpe, Authentic and Lasso on http://deb.entrouvert.org/ ;
+
+* Quixote 2.0 on http://authentic.labs.libre-entreprise.org/.
+
+Installation with another Linux distribution
+++++++++++++++++++++++++++++++++++++++++++++
+
+We suppose Apache2_, SCGI_, mod_python_, libxml2 and mod_proxy_html are already installed. You need then to
+download and install the following sources :
+
+* Lasso http://lasso.entrouvert.org ;
+
+* Quixote http://www.mems-exchange.org/software/Quixote/ ;
+
+* Authentic http://authentic.labs.libre-entreprise.org/ ;
+
+* Larpe http://labs.libre-entreprise.org/frs/?group_id=108.
+
+To install Larpe, uncompress the sources you have downloaded and launch the
+setup.py script ::
+
+ tar xzf larpe*.tar.gz
+ cd larpe*
+ python setup.py install
+
+You need then to configure Apache2_ correctly. You should use the provided apache2-vhost-larpe template and adapt to your configuration.
+
+Don't forget to modify your /etc/hosts file if necessary. Larpe now works, the
+administration interface is reachable at http://your_domain_name/admin.
+
+Basic Larpe configuration
+=========================
+
+Identity Provider configuration
++++++++++++++++++++++++++++++++
+
+If you don't have a configured Identity Provider yet, please read Authentic
+manual to set it up. Then you must have the metadata and public key of the Identity
+Provider to begin with Larpe.
+
+Then in Larpe administration interface, click on "Settings", then "Identity Provider".
+Fill in the metadata and public key that you've got from your Identity Provider then
+click Submit.
+Your Identity Provider is now configured in Larpe, you can then configure as many Service
+Providers as you want.
+
+Service Provider Configuration
+++++++++++++++++++++++++++++++
+
+Service Provider configuration
+------------------------------
+
+Click on "Hosts" then "New Host".
+
+Fill in the following parameters :
+
+* Label : the name you want to give to your Service Provider ;
+
+* Original Site Address : the root URL of your Service Provider ;
+
+* Authentication Page : if the page which contains the authentication form for
+ your Service Provider is on a separate page, fill the url of this page here ;
+
+* Authentication Form Page : if you didn't fill the previous field and if the
+ authentication form if not on the first page of your Service Provider either,
+ fill the url of the page which contains the authentication form here ;
+
+* Logout Address : when you want Single Sign On and Identity Federation, you probably
+ want Single Logout too. If so, fill the logout url of your original site here ;
+
+* Reversed Host Name : the domain name where you want to access your Service Provider
+ through the reverse proxy. It can be the domain name of Larpe or not ;
+
+Then click "Submit". Wait a few seconds then go to http://reversed_host_name/reverse_directory/
+to check if it works. If not, wait a bit more and try again. If it really doesn't work,
+please submit a bug report at http://labs.libre-entreprise.org/tracker/?func=add&group_id=108&atid=512
+
+Service Provider Example: Linuxfr
+---------------------------------
+
+To help you setup your own Service Provider, we provide an example of a working Service Provider
+to guide you.
+
+To setup Linuxfr, fill in the following parameters :
+
+* Label : Linuxfr ;
+
+* Original Site Address : http://linuxfr.org/ ;
+
+* Authentication Page : Nothing here ;
+
+* Authentication Form Page : http://linuxfr.org/pub/ ;
+
+* Logout Address : http://linuxfr.org/close_session.html ;
+
+* Reversed Host Name : linuxfr.reverse-proxy.example.com.
+
+With "reverse-proxy.example.com" being the hostname you've set up before for your reverse-proxy
+
+Don't forget to add this new hostname to your /etc/hosts as well.
+
+You can then go to the reversed Linuxfr at http://linuxfr.reverse-proxy.example.com/
+
+Service Provider Liberty Alliance final setup
+---------------------------------------------
+
+Now that you can access your Service Provider, you need a final step to use Liberty Alliance
+features. Click on "Hosts", the click on the "Edit" icon of the Service Provider you've
+just configured. Save the Service Provider Metadata (for ID-FF 1.2) and the Public Key
+(right click then "Save as"). Configure this Service Provider on your Identity Provider
+with these two files.
+
+Licenses
+========
+
+Larpe, Authentic_, Candle_ and Lasso_ are released under the terms of the
+`GNU GPL license`_.
+
+.. _Lasso: http://lasso.entrouvert.org/
+.. _`Liberty Alliance`: http://projectliberty.org/
+.. _`GNU GPL License`: http://www.gnu.org/copyleft/gpl.html
+.. _Debian: http://www.debian.org/
+.. _Apache2: http://httpd.apache.org/
+.. _Quixote: http://www.mems-exchange.org/software/Quixote
+.. _mod_python: http://www.modpython.org/
+.. _SCGI: http://www.mems-exchange.org/software/scgi/
+.. _Candle: http://candle.labs.libre-entreprise.org/
+.. _Authentic: http://www.entrouvert.com/fr/authentic/
diff --git a/larpe/tags/release-1.0/doc/scripts/removealpha.sh b/larpe/tags/release-1.0/doc/scripts/removealpha.sh
new file mode 100755
index 0000000..f29ee67
--- /dev/null
+++ b/larpe/tags/release-1.0/doc/scripts/removealpha.sh
@@ -0,0 +1,5 @@
+#! /bin/sh
+
+size=$(identify $1 | cut -d ' ' -f 3)
+composite $1 -size $(identify $1 | cut -d ' ' -f3) xc:white $2
+
diff --git a/larpe/tags/release-1.0/doc/scripts/rst2latex.py b/larpe/tags/release-1.0/doc/scripts/rst2latex.py
new file mode 100755
index 0000000..4036fbf
--- /dev/null
+++ b/larpe/tags/release-1.0/doc/scripts/rst2latex.py
@@ -0,0 +1,29 @@
+#! /usr/bin/python
+
+"""A minimal reST frontend, to create appropriate LaTeX files."""
+
+try:
+ import locale
+ locale.setlocale(locale.LC_ALL, '')
+except:
+ pass
+
+from docutils.core import publish_cmdline, Publisher
+
+def set_io(self, source_path=None, destination_path=None):
+ Publisher.set_io_orig(self, source_path, destination_path='/dev/null')
+
+Publisher.set_io_orig, Publisher.set_io = Publisher.set_io, set_io
+
+output = publish_cmdline(writer_name='latex',
+ settings_overrides = {
+ 'documentclass': 'report',
+ 'documentoptions': '11pt,a4paper,titlepage',
+ 'use_latex_toc': True,
+ 'use_latex_docinfo': True,
+ 'stylesheet': 'custom.tex'})
+
+output = output.replace('\\includegraphics',
+ '\\includegraphics[width=.9\\textwidth,height=15cm,clip,keepaspectratio]')
+output = output.replace('\\begin{figure}[htbp]', '\\begin{figure}[H]')
+print output
diff --git a/larpe/tags/release-1.0/exclude_from_dist b/larpe/tags/release-1.0/exclude_from_dist
new file mode 100644
index 0000000..d6e841d
--- /dev/null
+++ b/larpe/tags/release-1.0/exclude_from_dist
@@ -0,0 +1,12 @@
+.svn
+*.pyc
+*.pyo
+*.pye
+*.ptle
+*.swp
+debian.sarge
+make_debian_package.sh
+build
+dist
+tests
+larpe/filter
diff --git a/larpe/tags/release-1.0/fedora/larpe-reload-apache2-script b/larpe/tags/release-1.0/fedora/larpe-reload-apache2-script
new file mode 100755
index 0000000..928b0b7
--- /dev/null
+++ b/larpe/tags/release-1.0/fedora/larpe-reload-apache2-script
@@ -0,0 +1,22 @@
+#!/bin/sh
+#
+# The command "/etc/init.d/httpd reload" on Fedora actually _restarts_ Apache
+# We need to _reload_ it without closing existing connections
+
+APACHE2CTL=/usr/sbin/apachectl
+
+echo -n "Testing Apache config... "
+if ! $APACHE2CTL configtest > /dev/null 2>&1; then
+ $APACHE2CTL configtest || true
+ echo "[FAILED]"
+ exit 1
+else
+ echo "[OK]"
+fi
+echo -n "Reloading Apache config... "
+if $APACHE2CTL graceful $2 ; then
+ echo "[OK]"
+else
+ echo "[FAILED]"
+fi
+
diff --git a/larpe/tags/release-1.0/fedora/larpe.init b/larpe/tags/release-1.0/fedora/larpe.init
new file mode 100755
index 0000000..a41681f
--- /dev/null
+++ b/larpe/tags/release-1.0/fedora/larpe.init
@@ -0,0 +1,104 @@
+#! /bin/bash
+#
+# larpe Startup script for the Larpe reverse proxy
+#
+# description: Larpe is Liberty Alliance reverse proxy. It is used to add Liberty Alliance \
+# features to some sites without modifying the sites themselves.
+# processname: larpe
+# config: /etc/httpd/conf.d/larpe.conf
+# config: /etc/larpe/apache2-vhost-larpe-common
+# config: /etc/sysconfig/larpe
+# pidfile: /var/run/larpe.pid
+#
+
+### BEGIN INIT INFO
+# Provides: larpe
+# Required-Start: $local_fs $network
+# Required-Stop: $local_fs $network
+# Default-Start:
+# Default-Stop: 0 1 2 3 4 5 6
+# Short-Description: Start Larpe Liberty Alliance reverse proxy
+# Description: Start Larpe Liberty Alliance reverse proxy
+### END INIT INFO
+
+prog=larpe
+LARPECTL=/usr/sbin/larpectl
+PIDFILE=/var/run/larpe.pid
+
+# Source function library.
+. /etc/rc.d/init.d/functions
+
+# Source networking configuration.
+. /etc/sysconfig/network
+
+# Check that networking is up.
+[ ${NETWORKING} = "no" ] && exit 0
+
+[ -x $LARPECTL ] || exit 5
+
+# Read config file if it is present.
+if [ -f /etc/sysconfig/larpe ]; then
+ . /etc/sysconfig/larpe
+fi
+
+RETVAL=0
+
+#
+# Function that starts the daemon/service.
+#
+start() {
+ echo -n $"Starting $prog: "
+ $LARPECTL start &
+ RETVAL=$?
+ if [ $RETVAL -eq 0 ]
+ then
+ touch /var/lock/subsys/$prog
+ fi
+ echo
+ return $RETVAL
+}
+
+#
+# Function that stops the daemon/service.
+#
+stop() {
+ echo -n $"Stopping $prog: "
+ killproc $LARPECTL
+ RETVAL=$?
+ if [ $RETVAL -eq 0 ]
+ then
+ rm -rf /var/lock/subsys/$prog
+ fi
+ echo
+ return $RETVAL
+}
+
+# See how we were called.
+case "$1" in
+ start)
+ start
+ ;;
+ stop)
+ stop
+ ;;
+ status)
+ status $prog
+ RETVAL=$?
+ ;;
+ restart)
+ stop
+ start
+ ;;
+ condrestart)
+ if [ -f ${PIDFILE} ] ; then
+ stop
+ start
+ fi
+ ;;
+ *)
+ echo $"Usage: $prog {start|stop|restart|condrestart|status}"
+ exit 1
+esac
+
+exit $RETVAL
+
diff --git a/larpe/tags/release-1.0/fedora/larpe.spec b/larpe/tags/release-1.0/fedora/larpe.spec
new file mode 100644
index 0000000..21932dc
--- /dev/null
+++ b/larpe/tags/release-1.0/fedora/larpe.spec
@@ -0,0 +1,139 @@
+%{!?python_sitearch: %define python_sitearch %(%{__python} -c 'from distutils import sysconfig; print sysconfig.get_python_lib(1)')}
+# eval to 2.3 if python isn't yet present, workaround for no python in fc4 minimal buildroot
+%{!?python_version: %define python_version %(%{__python} -c 'import sys; print sys.version.split(" ")[0]' || echo "2.3")}
+%define apacheconfdir %{_sysconfdir}/httpd/conf.d
+
+Summary: Liberty Alliance Reverse Proxy
+Name: larpe
+Version: 0.2.9
+Release: 2%{?dist}
+License: GPL
+Group: System Environment/Applications
+Url: http://larpe.labs.libre-entreprise.org/
+Source0: http://labs.libre-entreprise.org/frs/download.php/591/%{name}-%{version}.tar.gz
+Buildroot: %{_tmppath}/%{name}-%{version}-%(id -u -n)
+BuildRequires: python >= 2.3, python-quixote >= 2.0
+BuildRequires: gettext
+Requires: httpd >= 2.0, mod_scgi, mod_proxy_html
+Requires: lasso-python >= 0.6.3, python-quixote >= 2.0, python-scgi
+Requires: initscripts
+Requires(post): /sbin/chkconfig
+Requires(preun):/sbin/chkconfig
+Requires(preun): /sbin/service
+
+%description
+Larpe is a Liberty Alliance Reverse Proxy. It allows any service provider (that is a website)
+to use Liberty Alliance features (Identity federation, Single Sign On and Single Logout) without
+changing the code of the service provider itself.
+
+It uses the Lasso library which is certified by the Liberty Alliance consortium.
+
+It is a quixote application and is commonly runned inside Apache web server.
+
+%package doc
+Summary: Documentation files for %{name} development.
+Group: Documentation
+BuildRequires: python-docutils, tetex-latex
+
+%description doc
+This package contains development documentation for %{name}.
+
+%prep
+%setup -q
+
+# Change Apache vhost path in Larpe config
+sed -i s#"/var/log/apache2/larpe-access.log"#"logs/larpe_access_log combined\n TransferLog logs/larpe_access_log"# conf/apache2-vhost-larpe
+sed -i s#"/var/log/apache2/larpe-error.log"#"logs/larpe_error_log"# conf/apache2-vhost-larpe
+sed -i s#"APACHE_MAIN_VHOST.*$"#"APACHE_MAIN_VHOST='/etc/httpd/conf.d/larpe.conf'"# larpe/Defaults.py
+
+%build
+
+%install
+rm -rf %{buildroot}
+
+# install generic files
+make install prefix=%{_prefix} DESTDIR=%{buildroot}
+
+# install init script
+install -d %{buildroot}/%{_initrddir}
+install -p -m 0755 fedora/larpe.init %{buildroot}%{_initrddir}/larpe
+
+# apache configuration
+mkdir -p %{buildroot}%{apacheconfdir}
+install -p -m 644 conf/apache2-vhost-larpe %{buildroot}%{apacheconfdir}/larpe.conf
+
+# apache reload script
+install -p -m 0755 fedora/larpe-reload-apache2-script %{buildroot}%{_sbindir}/
+
+# install doc files
+install -d -m 0755 %{buildroot}%{_datadir}/gtk-doc/html/larpe
+make -C doc DESTDIR=%{buildroot}%{_datadir}/gtk-doc/html/larpe
+
+%clean
+rm -fr %{buildroot}
+
+%post
+/sbin/chkconfig --add %{name}
+
+# manual post-installation
+cat <<_EOF_
+You must edit first %{apacheconfdir}/larpe.conf
+
+You must enable Larpe with "chkconfig larpe on ; service larpe start"
+
+You must also restart Apache with "service httpd restart"!
+_EOF_
+
+%preun
+if [ $1 = 0 ]; then
+ /sbin/service %{name} stop > /dev/null 2>&1
+ /sbin/chkconfig --del %{name}
+fi
+
+%files
+%defattr(-,root,root,755)
+%config %{_initrddir}/larpe
+%config(noreplace) %{apacheconfdir}/larpe.conf
+%config(noreplace) %{_sysconfdir}/larpe/apache2-vhost-larpe-common
+%{_sbindir}/larpectl
+%{_sbindir}/larpe-reload-apache2
+%{_sbindir}/larpe-reload-apache2-script
+%{python_sitearch}/%{name}
+%{_datadir}/%{name}
+%{_datadir}/locale/fr/LC_MESSAGES/larpe.mo
+/var/lib/larpe
+%defattr(644,root,root,755)
+%doc AUTHORS COPYING NEWS README
+
+%files doc
+%defattr(-,root,root)
+%doc %{_datadir}/gtk-doc/html/%{name}
+
+%changelog
+* Tue Mar 05 2009 Jean-Marc Liger <jmliger@siris.sorbonne.fr> 0.2.9-2
+- Added missing BuildRequires gettext
+- Enabled larpe init script
+
+* Mon Jan 19 2009 Damien Laniel <dlaniel@entrouvert.com> 0.2.9-1
+- Updated to 0.2.9
+- Use Larpe Makefile to install generic files
+- Copy fedora specific files
+
+* Sat Jan 17 2009 Jean-Marc Liger <jmliger@siris.sorbonne.fr> 0.2.1-2
+- Added missing BuildRequires tetex-latex for doc subpackage
+- Rebuilt on CentOS 4,5
+
+* Wed Jan 14 2009 Jean-Marc Liger <jmliger@siris.sorbonne.fr> 0.2.1-1
+- Updated to 0.2.1
+- Added missing Requires lasso-python
+- Added missing Requires python-scgi
+- Rebuilt on CentOS 4,5
+
+* Fri Mar 02 2007 Jean-Marc Liger <jmliger@siris.sorbonne.fr> 0.2.0-1
+- Updated to 0.2.0
+- Added BuildRequires python-quixote
+- Built on Fedora Core 3 / RHEL 4 and Fedora Core 6 / RHEL 5
+
+* Wed Jan 24 2007 Jean-Marc Liger <jmliger@siris.sorbonne.fr> 0.1.0-1
+- First 0.1.0
+- Built on Fedora Core 3 / RHEL 4 and Fedora Core 6 / RHEL 5
diff --git a/larpe/tags/release-1.0/larpe-reload-apache2.c b/larpe/tags/release-1.0/larpe-reload-apache2.c
new file mode 100644
index 0000000..73f681a
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe-reload-apache2.c
@@ -0,0 +1,173 @@
+/*
+ Template for a setuid program that calls a script.
+
+ The script should be in an unwritable directory and should itself
+ be unwritable. In fact all parent directories up to the root
+ should be unwritable. The script must not be setuid, that's what
+ this program is for.
+
+ This is a template program. You need to fill in the name of the
+ script that must be executed. This is done by changing the
+ definition of FULL_PATH below.
+
+ There are also some rules that should be adhered to when writing
+ the script itself.
+
+ The first and most important rule is to never, ever trust that the
+ user of the program will behave properly. Program defensively.
+ Check your arguments for reasonableness. If the user is allowed to
+ create files, check the names of the files. If the program depends
+ on argv[0] for the action it should perform, check it.
+
+ Assuming the script is a Bourne shell script, the first line of the
+ script should be
+ #!/bin/sh -
+ The - is important, don't omit it. If you're using esh, the first
+ line should be
+ #!/usr/local/bin/esh -f
+ and for ksh, the first line should be
+ #!/usr/local/bin/ksh -p
+ The script should then set the variable IFS to the string
+ consisting of <space>, <tab>, and <newline>. After this (*not*
+ before!), the PATH variable should be set to a reasonable value and
+ exported. Do not expect the PATH to have a reasonable value, so do
+ not trust the old value of PATH. You should then set the umask of
+ the program by calling
+ umask 077 # or 022 if you want the files to be readable
+ If you plan to change directories, you should either unset CDPATH
+ or set it to a good value. Setting CDPATH to just ``.'' (dot) is a
+ good idea.
+ If, for some reason, you want to use csh, the first line should be
+ #!/bin/csh -fb
+ You should then set the path variable to something reasonable,
+ without trusting the inherited path. Here too, you should set the
+ umask using the command
+ umask 077 # or 022 if you want the files to be readable
+*/
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+
+/* CONFIGURATION SECTION */
+
+#ifndef FULL_PATH/* so that this can be specified from the Makefile */
+ #define FULL_PATH "/usr/sbin/larpe-reload-apache2-script"
+#endif
+#ifndef UMASK
+ #define UMASK 077
+#endif
+
+/* END OF CONFIGURATION SECTION */
+
+#if defined(__STDC__) && defined(__sgi)
+#define environ _environ
+#endif
+
+/* don't change def_IFS */
+char def_IFS[] = "IFS= \t\n";
+/* you may want to change def_PATH, but you should really change it in */
+/* your script */
+#ifdef __sgi
+char def_PATH[] = "PATH=/usr/bsd:/usr/bin:/bin:/usr/local/bin:/usr/sbin";
+#else
+char def_PATH[] = "PATH=/usr/ucb:/usr/bin:/bin:/usr/local/bin";
+#endif
+/* don't change def_CDPATH */
+char def_CDPATH[] = "CDPATH=.";
+/* don't change def_ENV */
+char def_ENV[] = "ENV=:";
+
+/*
+ This function changes all environment variables that start with LD_
+ into variables that start with XD_. This is important since we
+ don't want the script that is executed to use any funny shared
+ libraries.
+
+ The other changes to the environment are, strictly speaking, not
+ needed here. They can safely be done in the script. They are done
+ here because we don't trust the script writer (just like the script
+ writer shouldn't trust the user of the script).
+ If IFS is set in the environment, set it to space,tab,newline.
+ If CDPATH is set in the environment, set it to ``.''.
+ Set PATH to a reasonable default.
+*/
+void
+clean_environ(void)
+{
+ char **p;
+ extern char **environ;
+
+ for (p = environ; *p; p++) {
+ if (strncmp(*p, "LD_", 3) == 0)
+ **p = 'X';
+ else if (strncmp(*p, "_RLD", 4) == 0)
+ **p = 'X';
+ else if (strncmp(*p, "PYTHON", 6) == 0)
+ **p = 'X';
+ else if (strncmp(*p, "IFS=", 4) == 0)
+ *p = def_IFS;
+ else if (strncmp(*p, "CDPATH=", 7) == 0)
+ *p = def_CDPATH;
+ else if (strncmp(*p, "ENV=", 4) == 0)
+ *p = def_ENV;
+ }
+ putenv(def_PATH);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct stat statb;
+ gid_t egid = getegid();
+ uid_t euid = geteuid();
+
+ /*
+ Sanity check #1.
+ This check should be made compile-time, but that's not possible.
+ If you're sure that you specified a full path name for FULL_PATH,
+ you can omit this check.
+ */
+ if (FULL_PATH[0] != '/') {
+ fprintf(stderr, "%s: %s is not a full path name\n", argv[0],
+ FULL_PATH);
+ fprintf(stderr, "You can only use this wrapper if you\n");
+ fprintf(stderr, "compile it with an absolute path.\n");
+ exit(1);
+ }
+
+ /*
+ Sanity check #2.
+ Check that the owner of the script is equal to either the
+ effective uid or the super user.
+ */
+ if (stat(FULL_PATH, &statb) < 0) {
+ perror("stat");
+ exit(1);
+ }
+ if (statb.st_uid != 0 && statb.st_uid != euid) {
+ fprintf(stderr, "%s: %s has the wrong owner\n", argv[0],
+ FULL_PATH);
+ fprintf(stderr, "The script should be owned by root,\n");
+ fprintf(stderr, "and shouldn't be writeable by anyone.\n");
+ exit(1);
+ }
+
+ if (setregid(egid, egid) < 0)
+ perror("setregid");
+ if (setreuid(euid, euid) < 0)
+ perror("setreuid");
+
+ clean_environ();
+
+ umask(UMASK);
+
+ while (**argv == '-')/* don't let argv[0] start with '-' */
+ (*argv)++;
+ execv(FULL_PATH, argv);
+ fprintf(stderr, "%s: could not execute the script\n", argv[0]);
+ exit(1);
+}
diff --git a/larpe/tags/release-1.0/larpe/Defaults.py b/larpe/tags/release-1.0/larpe/Defaults.py
new file mode 100644
index 0000000..b19e629
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/Defaults.py
@@ -0,0 +1,10 @@
+'''Default global variables'''
+
+APP_DIR = '/var/lib/larpe'
+DATA_DIR = '/usr/share/larpe'
+ERROR_LOG = None #'/var/log/larpe.log'
+WEB_ROOT = 'larpe'
+APACHE_MAIN_VHOST = '/etc/apache2/sites-available/apache2-vhost-larpe'
+APACHE_VHOST_COMMON = '/etc/larpe/apache2-vhost-larpe-common'
+APACHE_RELOAD = '/usr/sbin/larpe-reload-apache2'
+OUTPUT_FILTER_BASE = '/usr/share/larpe/output_filter_base.py'
diff --git a/larpe/tags/release-1.0/larpe/__init__.py b/larpe/tags/release-1.0/larpe/__init__.py
new file mode 100644
index 0000000..f89239d
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/__init__.py
@@ -0,0 +1,16 @@
+'''Setup path and load Lasso library'''
+
+import sys
+import os
+sys.path.insert(0, os.path.dirname(__file__))
+
+import qommon
+
+try:
+ import lasso
+except ImportError:
+ lasso = None
+
+if lasso and not hasattr(lasso, 'SAML2_SUPPORT'):
+ lasso.SAML2_SUPPORT = False
+
diff --git a/larpe/tags/release-1.0/larpe/admin/__init__.py b/larpe/tags/release-1.0/larpe/admin/__init__.py
new file mode 100644
index 0000000..53f1b4b
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/admin/__init__.py
@@ -0,0 +1 @@
+from root import RootDirectory
diff --git a/larpe/tags/release-1.0/larpe/admin/apache.py b/larpe/tags/release-1.0/larpe/admin/apache.py
new file mode 100644
index 0000000..060b0f8
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/admin/apache.py
@@ -0,0 +1,302 @@
+import os
+import re
+import urllib
+import base64
+
+from quixote import get_publisher, get_request
+
+from qommon import get_cfg
+
+from larpe.hosts import Host
+from larpe.Defaults import APP_DIR, APACHE_MAIN_VHOST, APACHE_VHOST_COMMON, APACHE_RELOAD
+
+def write_apache2_vhosts():
+ hosts = Host.select(lambda x: x.name != 'larpe')
+ hosts.sort()
+ vhosts_dir = os.path.join(APP_DIR, 'vhosts.d')
+ vhost_locations_dir = os.path.join(APP_DIR, 'vhost-locations.d')
+ vhosts_dir_disabled = os.path.join(APP_DIR, 'vhosts.d.disabled')
+ vhost_locations_dir_disabled = os.path.join(APP_DIR, 'vhost-locations.d.disabled')
+ vhost_file_name = get_request().get_server().split(':')[0]
+ vhost_file = None
+ reversed_hostname = ''
+ vhost = None
+
+ if get_publisher().cfg.get(str('allow_config_generation'), True):
+ vhost_file = open(os.path.join(vhosts_dir, vhost_file_name), 'w')
+ locations_file = open(os.path.join(vhost_locations_dir, vhost_file_name), 'w')
+ else:
+ vhost_file = open(os.path.join(vhosts_dir_disabled, vhost_file_name), 'w')
+ locations_file = open(os.path.join(vhost_locations_dir_disabled, vhost_file_name), 'w')
+ try:
+ main_vhost = open(APACHE_MAIN_VHOST, 'r')
+ file_content = main_vhost.read()
+ regexp = re.compile('<VirtualHost (.*?)>')
+ vhost_ip = regexp.findall(file_content)[0]
+ except (IOError, IndexError):
+ vhost_ip = '*'
+
+ for host in hosts:
+ if host.orig_site is None:
+ # This site hasn't been fully configured
+ continue
+ if host.reversed_hostname != reversed_hostname \
+ and host.reversed_hostname != get_cfg('proxy_hostname'):
+ if vhost is not None:
+ vhost.close()
+ vhost = Vhost(host, vhost_ip)
+ vhost.write(vhost_file)
+ reversed_hostname = host.reversed_hostname
+
+ if host.reversed_hostname == get_cfg('proxy_hostname'):
+ conf_file = locations_file
+ else:
+ conf_file = vhost_file
+
+ Location(host).write(conf_file)
+
+ if vhost_file is not None:
+ if vhost is not None:
+ vhost.close()
+ vhost_file.close()
+ if locations_file is not None:
+ locations_file.close()
+
+ if get_publisher().cfg.get(str('allow_config_generation'), True):
+ os.system(APACHE_RELOAD)
+
+
+class Vhost(object):
+ def __init__(self, host, main_ip_port):
+ self.host = host
+ self.main_ip_port = main_ip_port
+ self.conf_file = None
+
+ def get_ip_port(self):
+ if self.host.scheme == 'https':
+ return self.main_ip_port.replace(':80', ':443')
+ else:
+ return self.main_ip_port.replace(':443', ':80')
+ ip_port = property(get_ip_port)
+
+ def get_proxy_url(self):
+ if get_cfg('proxy', {}).get('enabled') and self.host.use_proxy == True:
+ return 'http://%(ip)s:%(port)s' % get_cfg('proxy', {})
+ return None
+ proxy_url = property(get_proxy_url)
+
+ def get_proxy_auth(self):
+ if self.get_proxy_url() and get_cfg('proxy', {}).get('user'):
+ credentials = base64.encodestring(
+ '%(user)s:%(password)s' % get_cfg('proxy', {}))[:-1]
+ return '"Basic %s"' % credentials
+ return None
+ proxy_auth = property(get_proxy_auth)
+
+ def get_cfg(self):
+ return { 'ip_port': self.ip_port,
+ 'reversed_hostname': self.host.reversed_hostname,
+ 'proxy_url': self.proxy_url,
+ 'proxy_auth': self.proxy_auth }
+ cfg = property(get_cfg)
+
+ def write(self, conf_file):
+ self.conf_file = conf_file
+ conf_lines = []
+ # Start Virtual Host
+ conf_lines.append('<VirtualHost %(ip_port)s>' % self.cfg)
+ # Server name and administrator
+ conf_lines.append('ServerName %(reversed_hostname)s' % self.cfg)
+ conf_lines.append('# ServerAdmin root@localhost\n')
+ # Include common vhost configuration
+ conf_lines.append('include %s\n' % APACHE_VHOST_COMMON)
+ # SSL
+ if self.host.scheme == 'https':
+ conf_lines.append('SSLEngine On\n')
+ if self.host.orig_site.startswith('https'):
+ conf_lines.append('SSLProxyEngine On\n')
+ # Remote proxy configuration
+ if self.proxy_url is not None:
+ conf_lines.append('ProxyRemote * %(proxy_url)s' % self.cfg)
+ if self.proxy_auth is not None:
+ conf_lines.append(
+ 'RequestHeader set Proxy-Authorization %(proxy_auth)s\n' % self.cfg)
+ # Write it all
+ conf_file.write('\n\t'.join(conf_lines))
+
+ def close(self):
+ if self.conf_file:
+ self.conf_file.write('</VirtualHost>\n\n')
+
+
+def apache_escape_chars(url):
+ special_characters = ('\\', '.', '?', '*', '+', '^', '$', '|', '(', ')', '[', ']')
+ for char in special_characters:
+ url = url.replace(char, '\%s' % char)
+ return url
+
+class Location(object):
+ def __init__(self, host):
+ self.host = host
+
+ def get_reversed_directory(self):
+ if not self.host.reversed_directory:
+ return '%s/' % get_request().environ['SCRIPT_NAME']
+ else:
+ return '%s/%s/' % (get_request().environ['SCRIPT_NAME'], self.host.reversed_directory)
+ reversed_directory = property(get_reversed_directory)
+
+ def get_python_path(self):
+ if hasattr(self.host, 'apache_output_python_filters') and \
+ hasattr(self.host, 'apache_python_paths') and self.host.apache_python_paths:
+ python_path = 'PythonPath "sys.path'
+ for path in self.host.apache_python_paths:
+ python_path += "+['%s']" % path
+ python_path += '"'
+ return python_path
+ else:
+ return None
+ python_path = property(get_python_path)
+
+ def get_output_filters(self):
+ python_filters = ''
+ output_filters = []
+ if hasattr(self.host, 'apache_output_python_filters'):
+ i = 0
+ for filter_file in self.host.apache_output_python_filters:
+ filter_name = 'filter%d' % i
+ python_filters += 'PythonOutputFilter %s %s\n\t\t' % (filter_file, filter_name)
+ output_filters.append(filter_name)
+ i += 1
+ if hasattr(self.host, 'apache_output_filters'):
+ for output_filter in self.host.apache_output_filters:
+ output_filters.append(output_filter)
+ if output_filters:
+ return python_filters + 'SetOutputFilter ' + ';'.join(output_filters)
+ else:
+ return None
+ output_filters = property(get_output_filters)
+
+ def get_old_auth_url(self):
+ old_auth_url = None
+ if self.host.initiate_sso_url:
+ old_auth_url = self.host.initiate_sso_url
+ elif self.host.auth_url is not None:
+ if self.host.auth_url.startswith('http://'):
+ chars_to_skip = 5
+ else:
+ chars_to_skip = 6
+ regexp = re.compile(self.host.orig_site[chars_to_skip:])
+ old_auth_url_short = regexp.sub('', self.host.auth_url[chars_to_skip:])
+ if old_auth_url_short.startswith('/'):
+ old_auth_url = old_auth_url_short
+ else:
+ old_auth_url = '/' + old_auth_url_short
+ if old_auth_url:
+ old_auth_url = apache_escape_chars(old_auth_url)
+ return old_auth_url
+ old_auth_url = property(get_old_auth_url)
+
+ def get_new_auth_url(self):
+ if not hasattr(self.host, 'base_url'):
+ return None
+ base_url_tokens = self.host.base_url.split('/')
+ base_url_tokens[-1] = 'login'
+ return '/'.join(base_url_tokens)
+ new_auth_url = property(get_new_auth_url)
+
+ def get_old_logout_url(self):
+ old_logout_url = None
+ if self.host.logout_url is not None:
+ if self.host.logout_url.startswith('http://'):
+ chars_to_skip = 5
+ else:
+ chars_to_skip = 6
+ regexp = re.compile(self.host.orig_site[chars_to_skip:])
+ old_logout_url_short = regexp.sub('', self.host.logout_url[chars_to_skip:])
+ if old_logout_url_short.startswith('/'):
+ old_logout_url = old_logout_url_short
+ else:
+ old_logout_url = '/' + old_logout_url_short
+ old_logout_url = apache_escape_chars(old_logout_url)
+ return old_logout_url
+ old_logout_url = property(get_old_logout_url)
+
+ def get_new_logout_url(self):
+ if not hasattr(self.host, 'base_url'):
+ return None
+ base_url_tokens = self.host.base_url.split('/')
+ base_url_tokens[-1] = 'logout'
+ return '/'.join(base_url_tokens)
+ new_logout_url = property(get_new_logout_url)
+
+ def get_orig_site_url_and_dir(self):
+ # Split url
+ if self.host.orig_site.startswith('http://'):
+ orig_host, orig_query = urllib.splithost(self.host.orig_site[5:])
+ else:
+ orig_host, orig_query = urllib.splithost(self.host.orig_site[6:])
+ # Add a trailing slash if necessary
+ if self.host.orig_site.endswith('/'):
+ orig_url = self.host.orig_site
+ orig_dir = orig_query
+ else:
+ orig_url = self.host.orig_site + '/'
+ orig_dir = orig_query + '/'
+ return orig_url, orig_dir
+
+ def get_orig_url(self):
+ orig_url, orig_dir = self.get_orig_site_url_and_dir()
+ return orig_url
+ orig_url = property(get_orig_url)
+
+ def get_orig_dir(self):
+ orig_url, orig_dir = self.get_orig_site_url_and_dir()
+ return orig_dir
+ orig_dir = property(get_orig_dir)
+
+ def get_cfg(self):
+ return { 'reversed_directory': self.reversed_directory,
+ 'old_auth_url': self.old_auth_url,
+ 'new_auth_url': self.new_auth_url,
+ 'old_logout_url': self.old_logout_url,
+ 'new_logout_url': self.new_logout_url,
+ 'orig_url': self.orig_url,
+ 'orig_dir': self.orig_dir }
+ cfg = property(get_cfg)
+
+ def write(self, conf_file):
+ conf_lines = []
+ # Start Location
+ conf_lines.append('\n\t<Location %(reversed_directory)s>' % self.cfg)
+ # No user restriction
+ conf_lines.append('Allow from all')
+ # Apache output filters
+ if self.python_path:
+ conf_lines.append(self.python_path)
+ if self.output_filters:
+ conf_lines.append(self.output_filters)
+ # Enable rewrite module
+ if self.old_auth_url is not None or self.old_logout_url is not None:
+ conf_lines.append('RewriteEngine On')
+ # Redirect old authentication url to the new one
+ if self.old_auth_url is not None and self.host.auth_form_places == 'form_once':
+ conf_lines.append(
+ 'RewriteRule %(old_auth_url)s %(new_auth_url)s [R]' % self.cfg)
+ # Redirect old logout url to the new one
+ if self.old_logout_url is not None:
+ conf_lines.append(
+ 'RewriteRule %(old_logout_url)s %(new_logout_url)s [R]' % self.cfg)
+ # Redirect the home page to the login page
+ if self.host.redirect_root_to_login is True:
+ conf_lines.append('RewriteRule /$ %(new_auth_url)s [R]' % self.cfg)
+ # Convert urls in http headers to/from the new domain
+ conf_lines.append('ProxyPass %(orig_url)s' % self.cfg)
+ conf_lines.append('ProxyPassReverse %(orig_url)s' % self.cfg)
+ # Convert urls in html pages to/from the new domain
+ conf_lines.append('ProxyHTMLURLMap %(orig_dir)s %(reversed_directory)s' % self.cfg)
+ conf_lines.append('ProxyHTMLURLMap %(orig_url)s %(reversed_directory)s' % self.cfg)
+ # Write it all and close the Location
+ conf_file.write('\n\t\t'.join(conf_lines))
+ conf_file.write('\n\t</Location>\n')
+
diff --git a/larpe/tags/release-1.0/larpe/admin/fields_prefill.ptl b/larpe/tags/release-1.0/larpe/admin/fields_prefill.ptl
new file mode 100644
index 0000000..c927388
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/admin/fields_prefill.ptl
@@ -0,0 +1,130 @@
+from quixote import get_response, redirect
+from quixote.directory import Directory
+
+from qommon.form import *
+from qommon.admin.menu import html_top, command_icon
+
+from field_prefill import FieldPrefill
+
+class FieldUI:
+ def __init__(self, field_prefill):
+ self.field_prefill = field_prefill
+
+ def form_edit(self):
+ form = Form(enctype='multipart/form-data')
+ form.add(StringWidget, 'name', title = _('Field name'), required = True,
+ size = 50, value = self.field_prefill.name)
+ form.add(StringWidget, 'xpath', title = _('Xpath of the attribute'), required = True,
+ size = 50, value = self.field_prefill.xpath, hint=_('Example: /pp:PP/pp:InformalName'))
+ form.add(IntWidget, 'number', title = _('Number of the field in the data'), required = True,
+ size = 3, value = self.field_prefill.number,
+ hint=_('Change it if there are multiple fields corresponding to the same Xpath and you want to get another than the first one'))
+ form.add(CheckboxWidget, 'raw_xml', title=_('Get raw XML value'),
+ value = self.field_prefill.raw_xml)
+ form.add(StringWidget, 'regexp_match', title = _('Python regexp of a string to match'),
+ size = 50, value = self.field_prefill.regexp_match)
+ form.add(StringWidget, 'regexp_replacing', title = _('Python regexp of the replacing string'),
+ size = 50, value = self.field_prefill.regexp_replacing)
+ form.add(WidgetDict, 'select_options', title = _('Options mapping for a select field'),
+ value = self.field_prefill.select_options, add_element_label = _('Add item'))
+ form.add_submit('submit', _('Submit'))
+ form.add_submit('cancel', _('Cancel'))
+ return form
+
+ def submit_edit(self, form):
+ for f in ('name', 'xpath', 'number', 'raw_xml', 'regexp_match', 'regexp_replacing', 'select_options'):
+ widget = form.get_widget(f)
+ setattr(self.field_prefill, f, widget.parse())
+ self.field_prefill.store()
+
+class FieldPage(Directory):
+ _q_exports = ['', 'delete']
+
+ def __init__(self, field_id):
+ self.field_prefill = FieldPrefill.get(field_id)
+ self.field_ui = FieldUI(self.field_prefill)
+ get_response().breadcrumb.append((field_id + '/', field_id))
+
+ def _q_index [html] (self):
+ form = self.field_ui.form_edit()
+ redo = False
+
+ if form.get_widget('cancel').parse():
+ return redirect('..')
+
+ if form.get_widget('select_options') and form.get_widget('select_options').get_widget('add_element').parse():
+ form.clear_errors()
+ redo = True
+
+ if redo is False and form.is_submitted() and not form.has_errors():
+ self.field_ui.submit_edit(form)
+ return redirect('..')
+
+ get_response().breadcrumb.append( ('edit', _('Edit')) )
+ html_top('edit', title = _('Edit'))
+ '<h2>%s</h2>' % _('Edit')
+
+ form.render()
+
+ def delete [html] (self):
+ form = Form(enctype='multipart/form-data')
+ form.widgets.append(HtmlWidget('<p>%s</p>' % _(
+ 'You are about to irrevocably delete this field.')))
+ 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():
+ get_response().breadcrumb.append(('delete', _('Delete')))
+ html_top('delete_form', title = _('Delete Field'))
+ '<h2>%s : %s</h2>' % (_('Delete Field'), self.field_prefill.id)
+ form.render()
+ else:
+ self.field_prefill.remove_self()
+ return redirect('..')
+
+
+class FieldsDirectory(Directory):
+ _q_exports = ['', 'new']
+
+ def __init__(self, form_prefill):
+ get_response().breadcrumb.append(('fields/', _('Fields')))
+ self.form_prefill = form_prefill
+
+ def _q_lookup(self, component):
+ return FieldPage(component)
+
+ def _q_index [html] (self):
+ html_top('fields', title = _('Fields'))
+ """<ul id="nav-fields-admin">
+ <li><a href="new">%s</a></li>
+ </ul>""" % _('New Field')
+
+ '<ul class="biglist">'
+
+ for field_prefill in FieldPrefill.select(lambda x: x.form_id == self.form_prefill.id):
+ if not field_prefill.name:
+ continue
+
+ # Split too long xpath
+ xpath = field_prefill.xpath
+ xpath_tokens = xpath.split(str('/'))
+ if len(xpath_tokens) > 3:
+ xpath = str('.../') + str('/').join(xpath_tokens[-3:])
+
+ '<li>'
+ '<strong class="label">%s</strong>' % field_prefill.name
+ '<br />%s' % xpath
+ '<p class="commands">'
+ command_icon('%s/' % field_prefill.id, 'edit')
+ command_icon('%s/delete' % field_prefill.id, 'remove')
+ '</p></li>'
+ '</ul>'
+
+ def new [html] (self):
+ get_response().breadcrumb.append(('new', _('New')) )
+ field_prefill = FieldPrefill()
+ field_prefill.form_id = self.form_prefill.id
+ field_prefill.store()
+ return redirect('%s/' % field_prefill.id)
+
diff --git a/larpe/tags/release-1.0/larpe/admin/forms_prefill.ptl b/larpe/tags/release-1.0/larpe/admin/forms_prefill.ptl
new file mode 100644
index 0000000..0f34eff
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/admin/forms_prefill.ptl
@@ -0,0 +1,127 @@
+from quixote import get_response, redirect
+from quixote.directory import Directory
+
+from qommon.form import *
+from qommon.admin.menu import html_top, command_icon
+
+from form_prefill import FormPrefill
+from fields_prefill import FieldsDirectory
+
+class FormUI:
+ def __init__(self, form_prefill):
+ self.form_prefill = form_prefill
+
+ def form_edit(self):
+ form = Form(enctype='multipart/form-data')
+ form.add(StringWidget, 'name', title = _('Form name'), required = True,
+ size = 50, value = self.form_prefill.name, hint=_('Only used for display'))
+ form.add(UrlWidget, 'url', title = _('Form address'), required = True,
+ size = 50, value = self.form_prefill.url)
+ form.add(StringWidget, 'profile', title = _('ID-WSF data profile'), required = True,
+ size = 50, value = self.form_prefill.profile, hint=_('Example: urn:liberty:id-sis-pp:2005-05'))
+ form.add(StringWidget, 'prefix', title = _('ID-WSF data XML prefix'), required = True,
+ size = 50, value = self.form_prefill.prefix, hint=_('Example: pp'))
+ form.add_submit('submit', _('Submit'))
+ form.add_submit('cancel', _('Cancel'))
+ return form
+
+ def submit_edit(self, form):
+ for f in ('name', 'url', 'profile', 'prefix'):
+ widget = form.get_widget(f)
+ setattr(self.form_prefill, f, widget.parse())
+ self.form_prefill.store()
+
+class FormPage(Directory):
+ _q_exports = ['', 'edit', 'delete']
+
+ def __init__(self, form_id):
+ self.form_prefill = FormPrefill.get(form_id)
+ self.form_ui = FormUI(self.form_prefill)
+ get_response().breadcrumb.append((form_id + '/', form_id))
+
+ def _q_index [html] (self):
+ html_top('forms_prefill', title = 'Form prefilling')
+
+ '<h2>%s</h2>' % _('Form prefilling configuration')
+ '<dl>'
+ '<dt><a href="edit">%s</a></dt> <dd>%s</dd>' % (
+ _('Edit'), _('Configure this form'))
+ '<dt><a href="fields/">%s</a></dt> <dd>%s</dd>' % (
+ _('Fields'), _('Configure the fields of this form'))
+ '</dl>'
+
+ def _q_lookup(self, component):
+ if component == 'fields':
+ return FieldsDirectory(self.form_prefill)
+
+ def edit [html] (self):
+ form = self.form_ui.form_edit()
+ if form.get_widget('cancel').parse():
+ return redirect('.')
+
+ if form.is_submitted() and not form.has_errors():
+ self.form_ui.submit_edit(form)
+ return redirect('.')
+
+ get_response().breadcrumb.append( ('edit', _('Edit')) )
+ html_top('edit', title = _('Edit'))
+ '<h2>%s</h2>' % _('Edit')
+
+ form.render()
+
+ 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():
+ get_response().breadcrumb.append(('delete', _('Delete')))
+ html_top('delete_form', title = _('Delete Form'))
+ '<h2>%s : %s</h2>' % (_('Delete Form'), self.form_prefill.id)
+ form.render()
+ else:
+ self.form_prefill.remove_self()
+ return redirect('..')
+
+
+class FormsDirectory(Directory):
+ _q_exports = ['', 'new']
+
+ def __init__(self, host):
+ get_response().breadcrumb.append(('forms_prefill/', _('Forms')))
+ self.host = host
+
+ def _q_lookup(self, component):
+ return FormPage(component)
+
+ def _q_index [html] (self):
+ html_top('forms', title = _('Forms'))
+ """<ul id="nav-forms-admin">
+ <li><a href="new">%s</a></li>
+ </ul>""" % _('New Form')
+
+ '<ul class="biglist">'
+
+ for form_prefill in FormPrefill.select(lambda x: x.host_id == self.host.id):
+ if not form_prefill.name:
+ continue
+ '<li>'
+ '<strong class="label">%s</strong>' % form_prefill.name
+ url = form_prefill.url
+ '<br /><a href="%s">%s</a>' % (url, url)
+ '<p class="commands">'
+ command_icon('%s/' % form_prefill.id, 'edit')
+ command_icon('%s/delete' % form_prefill.id, 'remove')
+ '</p></li>'
+ '</ul>'
+
+ def new [html] (self):
+ get_response().breadcrumb.append(('new', _('New')) )
+ form_prefill = FormPrefill()
+ form_prefill.host_id = self.host.id
+ form_prefill.store()
+ return redirect('%s/edit' % form_prefill.id)
+
diff --git a/larpe/tags/release-1.0/larpe/admin/hosts.ptl b/larpe/tags/release-1.0/larpe/admin/hosts.ptl
new file mode 100644
index 0000000..e9ca69b
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/admin/hosts.ptl
@@ -0,0 +1,1336 @@
+import os
+import urllib
+import urlparse
+
+from quixote import redirect, get_request, get_response, get_publisher
+from quixote.directory import Directory
+
+import lasso
+
+from qommon.admin.menu import html_top, command_icon
+from qommon import get_cfg
+from qommon.form import *
+from qommon.misc import http_get_page, get_abs_path
+
+from larpe import site_authentication
+from larpe import errors
+from larpe import misc
+from larpe.hosts import Host
+from larpe.admin.liberty_utils import *
+#from larpe.filter import filter_misc
+from larpe.admin.apache import write_apache2_vhosts
+from larpe.admin.forms_prefill import FormsDirectory
+from larpe.Defaults import OUTPUT_FILTER_BASE
+
+def check_basic_configuration(form):
+ get_publisher().reload_cfg()
+ # Check reversed_hostname and reversed_directory
+ reversed_hostname = form.get_widget('reversed_hostname').parse()
+ reversed_directory = form.get_widget('reversed_directory').parse()
+ if reversed_hostname == get_publisher().cfg['proxy_hostname'] and not reversed_directory:
+ form.set_error('reversed_hostname',
+ _('You must either choose a different hostname from Larpe or specify a reversed directory'))
+
+def convert_label_to_name(label):
+ '''Build host name from host label'''
+ name = label.lower()
+ invalid_characters = [' ', "'"]
+ for char in invalid_characters:
+ name = name.replace(char, '_')
+ return name
+
+class DictWidget(Widget):
+ def render_content [html] (self):
+ self.render_br = False
+ if self.value['enabled'] is True:
+ htmltag('input', xml_end=True, type='checkbox', name=self.name + '_enabled', checked='checked')
+ else:
+ htmltag('input', xml_end=True, type='checkbox', name=self.name + '_enabled')
+ ' ' + self.name + ' &nbsp;'
+ htmltag('input', xml_end=True, type='text', name=self.name, value=self.value['value'], size='35', **self.attrs)
+
+ def _parse(self, request):
+ enabled = request.form.get(self.name + '_enabled')
+ value = request.form.get(self.name)
+ self.value = { 'enabled': enabled, 'value': value }
+
+
+class ConfigurationAssistant(Directory):
+ _q_exports = ['start', 'check_new_address', 'modify_site_address_and_name',
+ 'authentication_and_logout_adresses', 'check_auto_detected_configuration',
+ 'credentials', 'send_authentication_request', 'check_authentication',
+ 'see_authentication_response', 'see_response_html_page',
+ 'see_bad_response_html_page', 'authentication_success_criteria',
+ 'modify_authentication_request', 'auth_request_post_parameters',
+ 'auth_request_http_headers', 'sso_init_link', 'metadatas',
+ 'check_full_configuration', 'advanced_options']
+
+ def __init__(self, host):
+ self.host = host
+
+ def html_top [html] (self, title):
+ html_top('hosts', title)
+ '<h2>%s</h2>' % title
+
+ def start [html] (self):
+ # Check the global domain name has been previously set
+ get_publisher().reload_cfg()
+ if not get_cfg('domain_names') or not get_cfg('domain_names')[0]:
+ get_response().breadcrumb.append(('start', _('Basic configuration')))
+ self.html_top(_('Need domain name configuration'))
+ return htmltext(_('''Before configuring hosts, you must
+<a href="../../../settings/domain_names">setup a global domain name</a> in
+%(settings)s menu.''') % { 'settings': _('Settings') })
+
+ form = self.form_start()
+
+ if form.get_widget('cancel').parse():
+ return redirect('../..')
+
+ connection_failure = None
+ if form.is_submitted() and not form.has_errors():
+ try:
+ self.submit_start_form(form)
+ except Exception, e:
+ connection_failure = e
+ else:
+ if form.get_widget('terminate').parse():
+ return redirect('..')
+ return redirect('check_new_address')
+
+ get_response().breadcrumb.append(('start', _('Basic configuration')))
+ self.html_top(_('Step 1 - Basic configuration'))
+
+ if connection_failure:
+ '<div class="errornotice">%s</div>' % connection_failure
+
+ form.render()
+
+ def form_start(self):
+ form = Form(enctype='multipart/form-data')
+ form.add(UrlWidget, 'orig_site', title = _('Original site root address'), required = True,
+ size = 50, value = self.host.orig_site,
+ hint = _('If your site address is http://test.org/index.php, put http://test.org/ here'))
+ get_publisher().reload_cfg()
+ if get_cfg('proxy', {}).get('enabled'):
+ form.add(CheckboxWidget, 'use_proxy', title = _('Use a proxy'),
+ hint = _("Uncheck it if Larpe doesn't need to use the proxy to connect to this site"),
+ value = self.host.use_proxy)
+ else:
+ form.add(HtmlWidget, htmltext('<p>%s</p>' % \
+ _('''If Larpe needs to use a proxy to connect to this site, you must first configure
+ it in <a href="../../../settings/proxy">global proxy parameters</a>.''')))
+ form.add_submit('cancel', _('Cancel'))
+ form.add_submit('submit', _('Next'))
+ form.add_submit('terminate', _('Terminate'))
+ return form
+
+ def submit_start_form(self, form):
+ fields = ['orig_site']
+ use_proxy = get_cfg('proxy', {}).get('enabled')
+ if use_proxy:
+ fields += ['use_proxy']
+ for f in fields:
+ setattr(self.host, f, form.get_widget(f).parse())
+
+ # If no proxy is setup yet, set use_proxy to False for new hosts in case a proxy is later configured
+ if not use_proxy:
+ self.host.use_proxy = False
+
+ # Remove what is after the last '/'
+ #self.host.orig_site = '/'.join(self.host.orig_site.split('/')[:-1])
+
+ html_page = self.get_data_after_redirects(self.host.orig_site)
+
+ if not self.host.label:
+ # Look for html title in original site index page
+ regexp = re.compile("""<title.*?>(.*?)</title>""", re.DOTALL | re.IGNORECASE)
+ title = regexp.findall(html_page)
+ if title:
+ self.host.label = title[0]
+ else:
+ self.host.label = 'Untitled'
+ # If another site already uses this site title, add trailings "_" until we find an available name
+ existing_label = True
+ while existing_label is True:
+ for any_host in Host.select():
+ if any_host.id != self.host.id and self.host.label == any_host.label:
+ self.host.label += '_'
+ break
+ else:
+ existing_label = False
+
+ # Fill host.name attribute
+ self.host.name = convert_label_to_name(self.host.label)
+
+ if not self.host.scheme:
+ # Get tokens from orig site url
+ orig_scheme, rest = urllib.splittype(self.host.orig_site)
+ orig_host, rest = urllib.splithost(rest)
+
+ get_publisher().reload_cfg()
+ # Set url scheme (HTTP or HTTPS)
+ # TODO: Handle the option "Both"
+ if get_cfg('sites_url_scheme'):
+ self.host.scheme = get_cfg('sites_url_scheme')
+ else:
+ self.host.scheme = orig_scheme
+
+ if not self.host.reversed_hostname:
+ # Build a new domain name
+ short_name = orig_host.split('.')[0]
+ if short_name == 'www':
+ short_name = orig_host.split('.')[1]
+ self.host.reversed_hostname = '%s.%s' % (short_name, get_cfg('domain_names')[0])
+ # If another site already uses this domain name, add some trailing "_" until we find an available name
+ existing_domain = True
+ while existing_domain is True:
+ for any_host in Host.select():
+ if any_host.id != self.host.id and self.host.reversed_hostname == any_host.reversed_hostname:
+ self.host.reversed_hostname += '-'
+ break
+ else:
+ existing_domain = False
+ self.host.reversed_directory = None
+
+ if not self.host.new_url:
+ # New url for this host
+ self.host.new_url = '%s://%s%s/' % (self.host.scheme, self.host.reversed_hostname, get_request().environ['SCRIPT_NAME'])
+ # FIXME: Check if the new domain name already exists
+
+ # New url for this host
+ # self.host.new_url = '%s://%s%s/' % (self.host.scheme, self.host.reversed_hostname, get_request().environ['SCRIPT_NAME'])
+ # if self.host.reversed_directory is not None:
+ # self.host.new_url += '%s/' % self.host.reversed_directory
+
+ self.host.store()
+ write_apache2_vhosts()
+
+ # XXX: Should use the FancyURLopener class instead when it supports proxies
+ def get_data_after_redirects(self, start_url):
+ if not start_url:
+ return ''
+ status = 302
+ location = None
+ while status // 100 == 3:
+ if location is None:
+ url = start_url
+ elif location.startswith('http'):
+ # Location is an absolute path
+ url = location
+ else:
+ # Location is a relative path
+ url = urlparse.urljoin(start_url, location)
+ response, status, data, auth_headers = http_get_page(url, use_proxy=self.host.use_proxy)
+ location = response.getheader('Location', None)
+ return data
+
+ def create_dirs(self):
+ # Hack : sites must use the configuration which is stored in main Larpe directory,
+ # but they need to have a directory named with their hostname, which will contain the
+ # main domain name for Larpe so they know where is the main configuration
+ hostname_dir = get_abs_path(os.path.join('..', self.host.reversed_hostname))
+ if not os.path.exists(hostname_dir):
+ os.mkdir(hostname_dir)
+ # Load the configuration from the main directory
+ get_publisher().reload_cfg()
+ # Write it in the site directory
+ get_publisher().write_cfg(hostname_dir)
+
+ # Storage directories
+ if not self.host.reversed_directory:
+ reversed_dir = 'default'
+ else:
+ reversed_dir = self.host.reversed_directory
+ self.host.site_dir = \
+ os.path.join(get_publisher().app_dir, 'sp', self.host.reversed_hostname, reversed_dir)
+ user_dir = os.path.join(self.host.site_dir, 'users')
+ token_dir = os.path.join(self.host.site_dir, 'tokens')
+ filter_dir = os.path.join(self.host.site_dir, 'filters')
+ for dir in (self.host.site_dir, user_dir, token_dir, filter_dir):
+ if not os.path.isdir(dir):
+ os.makedirs(dir)
+
+ def generate_ssl_keys(self):
+ # Generate SSL keys
+ private_key_path = os.path.join(self.host.site_dir, 'private_key.pem')
+ public_key_path = os.path.join(self.host.site_dir, 'public_key.pem')
+ if not os.path.isfile(private_key_path) or not os.path.isfile(public_key_path):
+ set_provider_keys(private_key_path, public_key_path)
+ self.host.private_key = private_key_path
+ self.host.public_key = public_key_path
+
+ def generate_metadatas(self):
+ metadata_cfg = {}
+
+ # Organization name
+ self.host.organization_name = self.host.label
+ metadata_cfg['organization_name'] = self.host.organization_name
+
+ # Base URL
+ base_url = '%s://%s%s/liberty/%s/liberty' % (self.host.scheme,
+ self.host.reversed_hostname,
+ get_request().environ['SCRIPT_NAME'],
+ self.host.name)
+ metadata_cfg['base_url'] = base_url
+ self.host.base_url = base_url
+
+ if lasso.SAML2_SUPPORT:
+ saml2_base_url = '%s://%s%s/liberty/%s/saml' % (self.host.scheme,
+ self.host.reversed_hostname,
+ get_request().environ['SCRIPT_NAME'],
+ self.host.name)
+ metadata_cfg['saml2_base_url'] = saml2_base_url
+ self.host.saml2_base_url = saml2_base_url
+
+ # Provider Id
+ provider_id = '%s/metadata' % base_url
+ metadata_cfg['provider_id'] = provider_id
+ self.host.provider_id = provider_id
+
+ if lasso.SAML2_SUPPORT:
+ saml2_provider_id = '%s/metadata' % saml2_base_url
+ metadata_cfg['saml2_provider_id'] = saml2_provider_id
+ self.host.saml2_provider_id = saml2_provider_id
+
+ # Read public key
+ public_key = ''
+ if self.host.public_key is not None and os.path.exists(self.host.public_key):
+ metadata_cfg['signing_public_key'] = open(self.host.public_key).read()
+
+ # Write metadatas
+ metadata_path = os.path.join(self.host.site_dir, 'metadata.xml')
+ open(metadata_path, 'w').write(get_metadata(metadata_cfg))
+ self.host.metadata = metadata_path
+
+ if lasso.SAML2_SUPPORT:
+ saml2_metadata_path = os.path.join(self.host.site_dir, 'saml2_metadata.xml')
+ open(saml2_metadata_path, 'w').write(get_saml2_metadata(metadata_cfg))
+ self.host.saml2_metadata = saml2_metadata_path
+
+ def check_new_address [html] (self):
+ form = Form(enctype='multipart/form-data')
+ form.add_submit('cancel', _('Previous'))
+ form.add_submit('submit', _('Next'))
+ form.add_submit('terminate', _('Terminate'))
+
+ if form.get_widget('cancel').parse():
+ return redirect('start')
+
+ if form.is_submitted():
+ self.create_dirs()
+ if self.host.private_key is None:
+ self.generate_ssl_keys()
+ self.generate_metadatas()
+ self.host.store()
+
+ if form.get_widget('terminate').parse():
+ return redirect('..')
+ return redirect('authentication_and_logout_adresses')
+
+ get_response().breadcrumb.append(('check_new_address', _('Check site address and name')))
+ self.html_top(_('Step 2 - Check the new site address works'))
+
+ '<h3>%s</h3>' % _('DNS configuration')
+
+ '<p>%s</p>' % _('''Before opening the following link, ensure you have configured your DNS
+for this address. If you don't have a DNS server and you just want to test Larpe, add this
+domain name in the file "/etc/hosts".''')
+
+ '<p>%s</p>' % _('''Then you can open this link in a new window or tab and see if your site
+is displayed. If it's ok, you can click the "%(next)s" button. Otherwise, click the "%(previous)s"
+button and check your settings.''') % {'next': _('Next'), 'previous': _('Previous')}
+
+ '<h3>%s</h3>' % _('Site adress and name')
+
+ '<p>%s' % _('The new address of this site is ')
+ '<a href="%s">%s</a><br/>' % (self.host.new_url, self.host.new_url)
+ '%s</p>' % _('The name of this site is "%s".') % self.host.label
+ '<p>%s</p>' % htmltext(_('''You can also <a href="modify_site_address_and_name">
+modify the address or the name of this site</a>'''))
+
+ form.render()
+
+ def modify_site_address_and_name [html] (self):
+ form = self.form_modify_site_address_and_name()
+
+ if form.get_widget('cancel').parse():
+ return redirect('check_new_address')
+
+ if form.is_submitted() and not form.has_errors():
+ label = form.get_widget('label').parse()
+ name = convert_label_to_name(label)
+ for any_host in Host.select():
+ if any_host.id != self.host.id and name == any_host.name:
+ form.set_error('label', _('An host with the same name already exists'))
+ break
+
+ if form.is_submitted() and not form.has_errors():
+ self.submit_modify_site_address_and_name_form(form)
+ return redirect('check_new_address')
+
+ get_response().breadcrumb.append(('modify_site_address_and_name', _('Modify site address and name')))
+ self.html_top(_('Modify site address and name'))
+
+ form.render()
+
+ def form_modify_site_address_and_name(self):
+ form = Form(enctype='multipart/form-data')
+ form.add(UrlWidget, 'new_url', title = _('Address'), required = True,
+ size = 50, value = self.host.new_url)
+ form.add(StringWidget, 'label', title = _('Name'), required = True,
+ size = 50, value = self.host.label)
+ form.add_submit('submit', _('Submit'))
+ form.add_submit('cancel', _('Cancel'))
+ return form
+
+ def submit_modify_site_address_and_name_form(self, form):
+ fields = ['new_url', 'label']
+ for f in fields:
+ setattr(self.host, f, form.get_widget(f).parse())
+
+ # Split url to retrieve components
+ tokens = urlparse.urlparse(self.host.new_url)
+ self.host.scheme = tokens[0]
+ self.host.reversed_hostname = tokens[1]
+ self.host.reversed_directory = tokens[2]
+ if self.host.reversed_directory.startswith('/'):
+ self.host.reversed_directory = self.host.reversed_directory[1:]
+
+ # Fill host.name attribute
+ self.host.name = convert_label_to_name(self.host.label)
+
+ self.host.store()
+ write_apache2_vhosts()
+
+ def authentication_and_logout_adresses [html] (self):
+ form = self.form_authentication_and_logout_adresses()
+
+ if form.get_widget('cancel').parse():
+ return redirect('check_new_address')
+
+ if form.is_submitted() and not form.has_errors():
+ self.submit_authentication_and_logout_adresses_form(form)
+ if form.get_widget('terminate').parse():
+ return redirect('..')
+ return redirect('check_auto_detected_configuration')
+
+ get_response().breadcrumb.append(('authentication_and_logout_adresses', _('Authentication and logout')))
+ self.html_top(_('Step 3 - Configure authentication and logout pages'))
+
+ form.render()
+
+ def form_authentication_and_logout_adresses(self):
+ form = Form(enctype='multipart/form-data')
+ form.add(ValidUrlWidget, 'auth_url', title = _('Authentication form page address'),
+ hint = _('Address of a page on the site which contains the authentication form'),
+ required = True, size = 50, value = self.host.auth_url)
+ form.add(ValidUrlWidget, 'logout_url', title = _('Logout address'), required = False,
+ hint = _('Address of the logout link on the site'),
+ size = 50, value = self.host.logout_url)
+ form.add_submit('cancel', _('Previous'))
+ form.add_submit('submit', _('Next'))
+ form.add_submit('terminate', _('Terminate'))
+ return form
+
+ def submit_authentication_and_logout_adresses_form(self, form):
+ fields = ['auth_url', 'logout_url']
+ for f in fields:
+ setattr(self.host, f, form.get_widget(f).parse())
+ self.host.auth_form_url = self.host.auth_url
+
+ if not self.host.http_headers:
+ self.host.http_headers = {
+ 'Content-Type': { 'enabled': True, 'value': 'application/x-www-form-urlencoded', 'immutable': False },
+ 'X-Forwarded-For': { 'enabled': True, 'value': _('(computed automatically)'), 'immutable': True },
+ 'X-Forwarded-Host': { 'enabled': True, 'value': self.host.reversed_hostname, 'immutable': False },
+ }
+
+ self.auto_detect_configuration()
+ self.host.store()
+ write_apache2_vhosts()
+
+ def check_auto_detected_configuration [html] (self):
+ form = Form(enctype='multipart/form-data')
+ form.add_submit('cancel', _('Previous'))
+ form.add_submit('submit', _('Next'))
+ form.add_submit('terminate', _('Terminate'))
+
+ if form.get_widget('cancel').parse():
+ return redirect('authentication_and_logout_adresses')
+
+ if form.is_submitted():
+ if form.get_widget('terminate').parse():
+ return redirect('..')
+ return redirect('credentials')
+
+ get_response().breadcrumb.append(('check_auto_detected_configuration', _('Auto detected configuration')))
+ self.html_top(_('Step 4 - Check automatically detected configuration for the authentication form'))
+
+ host_attrs = (
+ ('auth_check_url', _('Address where the authentication form must be sent')),
+ ('login_field_name', _('Name of the login field')),
+ ('password_field_name', _('Name of the password field')),
+ )
+
+ html_fields = ''
+ success = True
+ for attr, name in host_attrs:
+ color = 'black'
+ if attr in ('auth_check_url', 'login_field_name', 'password_field_name') and \
+ not getattr(self.host, str(attr)):
+ color = 'red'
+ success = False
+ html_fields += '<div style="margin-bottom: 0.1em; font-weight: bold; color: %s;">%s</div>' % (color, name)
+ html_fields += '<div style="margin-left: 1em; margin-bottom: 0.3em; color: %s;">%s</div>' % \
+ (color, getattr(self.host, str(attr)))
+ if getattr(self.host, str(attr)) == '':
+ html_fields += '<br />'
+
+ '<p>'
+ if success:
+ htmltext(_('''\
+The following authentication form parameters have been detected. If they look right, you can go to the next step.
+If you think they are wrong, go back and check your settings then try again.
+'''))
+ else:
+ htmltext(_('''\
+The following authentication form parameters in red haven't been correctly detected. Go back and check
+your settings then try again.
+'''))
+ '</p>'
+
+ html_fields
+ form.render()
+
+ def credentials [html] (self):
+ form = self.form_credentials()
+
+ if form.get_widget('cancel').parse():
+ return redirect('check_auto_detected_configuration')
+
+ if form.is_submitted() and not form.has_errors():
+ self.submit_credentials_form(form)
+ if form.get_widget('terminate').parse():
+ return redirect('..')
+ return redirect('send_authentication_request')
+
+ get_response().breadcrumb.append(('credentials', _('Credentials')))
+ self.html_top(_('Step 5 - Fill in a valid username/password for this site'))
+
+ form.render()
+
+ def form_credentials(self):
+ form = Form(enctype='multipart/form-data')
+ form.add(StringWidget, 'username', title = _('Username'), required = True,
+ size = 30, value = self.host.valid_username)
+ form.add(PasswordWidget, 'password', title = _('Password'), required = True,
+ size = 30, value = self.host.valid_password)
+ for name, values in self.host.select_fields.iteritems():
+ options = []
+ if values:
+ for value in values:
+ options.append(value)
+ form.add(SingleSelectWidget, name, title = name.capitalize(),
+ value = values[0], options = options)
+ form.add_submit('cancel', _('Previous'))
+ form.add_submit('submit', _('Next'))
+ form.add_submit('terminate', _('Terminate'))
+ return form
+
+ def submit_credentials_form(self, form):
+ self.host.valid_username = form.get_widget('username').parse()
+ self.host.valid_password = form.get_widget('password').parse()
+ self.host.valid_select = {}
+ for name, values in self.host.select_fields.iteritems():
+ if form.get_widget(name):
+ self.host.valid_select[name] = form.get_widget(name).parse()
+
+ self.host.store()
+
+ def send_authentication_request(self):
+ site_auth = site_authentication.get_site_authentication(self.host)
+
+ # Request with good credentials
+ self.host.auth_request_status, self.host.auth_request_data = site_auth.local_auth_check_dispatch(
+ self.host.valid_username, self.host.valid_password, self.host.valid_select)
+ self.host.auth_request_success, self.host.auth_request_return_content = \
+ site_auth.check_auth(self.host.auth_request_status, self.host.auth_request_data)
+
+ # Request with bad credentials
+ self.host.auth_bad_request_status, self.host.auth_bad_request_data = site_auth.local_auth_check_dispatch(
+ 'this_is_a_bad_login', 'this_is_a_bad_password', {})
+ self.host.auth_bad_request_success, self.host.auth_bad_request_return_content = \
+ site_auth.check_auth(self.host.auth_bad_request_status, self.host.auth_bad_request_data)
+
+ self.host.store()
+
+ return redirect('check_authentication')
+
+ def check_authentication [html] (self):
+ form = Form(enctype='multipart/form-data')
+ form.add_submit('cancel', _('Previous'))
+ form.add_submit('submit', _('Next'))
+ form.add_submit('terminate', _('Terminate'))
+
+ if form.get_widget('cancel').parse():
+ return redirect('credentials')
+
+ if form.is_submitted():
+ if form.get_widget('terminate').parse():
+ return redirect('..')
+ return redirect('sso_init_link')
+
+ get_response().breadcrumb.append(('check_authentication', _('Check authentication')))
+ self.html_top(_('Step 6 - Check the authentication process'))
+
+ if self.host.auth_request_success:
+ good_cred_status = 'Success <span style="color: green">[OK]</span>'
+ else:
+ good_cred_status = 'Failed <span style="color: red">[KO]</span'
+
+ if not self.host.auth_bad_request_success:
+ bad_cred_status = 'Failed <span style="color: green">[OK]</span>'
+ else:
+ bad_cred_status = 'Success <span style="color: red">[KO]</span>'
+
+ '<p>Results of authentication requests :</p>\n'
+ '<ul>\n'
+ '\t<li>With good credentials : %s</li>' % good_cred_status
+ '\t<li>With bad credentials : %s</li>' % bad_cred_status
+ '</ul>\n'
+
+ if self.host.auth_request_success and not self.host.auth_bad_request_success :
+ '<p>%s</p>\n' % _('Authentication succeeded ! You can go to the next step.')
+ else:
+ '<p>%s</p>\n' % _('Authentication has failed. To resolve this problem, you can :')
+ '<ul>\n'
+ '\t<li><a href="send_authentication_request">%s</a></li>\n' % \
+ _('Try authentication again')
+ '\t<li><a href="see_authentication_response">%s</a></li>\n' % \
+ _('See the response of the authentication requests')
+ '\t<li><a href="modify_authentication_request">%s</a></li>\n' % \
+ _('Modify the parameters of the authentication requests')
+ '\t<li><a href="authentication_success_criteria">%s</a></li>\n' % \
+ _('Change the way Larpe detects the authentication is successful or not')
+ '\t<li>%s</li>\n' % _('Go back and change your username and/or password')
+ '</ul>\n'
+
+ form.render()
+
+ def see_authentication_response [html] (self):
+ get_response().breadcrumb.append(('see_authentication_response', _('Authentication response')))
+ self.html_top(_('Authentication response'))
+
+ '<h3>%s</h3>' % _('Response of the request with good credentials')
+
+ '<div style="margin-bottom: 0.1em; font-weight: bold; color: black;">%s</div>' % \
+ str(_('HTTP status code'))
+ '<div style="margin-left: 1em; margin-bottom: 0.3em; color: black;">%s (%s)</div>' % \
+ (self.host.auth_request_status, status_reasons[self.host.auth_request_status])
+
+ '<dl>'
+ '<dt><a href="see_bad_response_html_page">%s</a></dt>' % _('See HTML page')
+ '</dl>'
+
+ '<h3>%s</h3>' % _('Response of the request with bad credentials')
+
+ '<div style="margin-bottom: 0.1em; font-weight: bold; color: black;">%s</div>' % \
+ str(_('HTTP status code'))
+ '<div style="margin-left: 1em; margin-bottom: 0.3em; color: black;">%s (%s)</div>' % \
+ (self.host.auth_bad_request_status, status_reasons[self.host.auth_bad_request_status])
+
+ '<dl>'
+ '<dt><a href="see_response_html_page">%s</a></dt>' % _('See HTML page')
+ '</dl>'
+
+ '<div class="buttons"><a href="check_authentication"><input type="button" value="%s" /></a></div><br />' % _('Back')
+
+ def see_response_html_page (self):
+ return self.host.auth_request_data
+
+ def see_bad_response_html_page (self):
+ return self.host.auth_bad_request_data
+
+ def authentication_success_criteria [html] (self):
+ form = self.form_authentication_success_criteria()
+
+ if form.get_widget('cancel').parse():
+ return redirect('check_authentication')
+
+ if form.is_submitted() and not form.has_errors():
+ self.submit_authentication_success_criteria_form(form)
+ return redirect('check_authentication')
+
+ get_response().breadcrumb.append(('authentication_success_criteria', _('Authentication success criteria')))
+ self.html_top(_('Authentication success criteria'))
+
+ form.render()
+
+ def form_authentication_success_criteria(self):
+ form = Form(enctype='multipart/form-data')
+ form.add(RadiobuttonsWidget, 'auth_system', title = _('Authentication system of the original site'),
+ options=[
+ ('password', _('Check the existence of a password field'), 'password'),
+ ('match_text', _('Match some text to detect an authentication failure'), 'match_text'),
+ ],
+ sort=False,
+ delim=htmltext('<br />'),
+ value = self.host.auth_system)
+ form.add(RegexStringWidget, 'auth_match_text', title = _('Text to match in case of authentication failure'),
+ required = False, size = 50, value = self.host.auth_match_text)
+ form.add_submit('submit', _('Submit'))
+ form.add_submit('cancel', _('Cancel'))
+ return form
+
+ def submit_authentication_success_criteria_form(self, form):
+ for f in ('auth_system', 'auth_match_text'):
+ value = form.get_widget(f).parse()
+ setattr(self.host, f, value)
+
+ self.host.store()
+
+ def modify_authentication_request [html] (self):
+ get_response().breadcrumb.append(('modify_authentication_request', _('Authentication request')))
+ self.html_top(_('Modify the parameters of the authentication requests'))
+
+ '<dl>'
+ '<dt><a href="auth_request_post_parameters">%s</a></dt> <dd>%s</dd>' % (
+ _('Modify POST parameters'), _('Configure the form attributes that will be sent within the authentication POST requests'))
+ '<dt><a href="auth_request_http_headers">%s</a></dt> <dd>%s</dd>' % (
+ _('Modify HTTP headers'), _('Configure the HTTP headers of the authentication requests made by Larpe'))
+ '</dl>'
+ '<div class="buttons"><a href="check_authentication"><input type="button" value="%s" /></a></div><br />' % _('Back')
+
+ def auth_request_post_parameters [html] (self):
+ form = self.form_auth_request_post_parameters()
+
+ if form.get_widget('cancel').parse():
+ return redirect('modify_authentication_request')
+
+ if form.is_submitted() and not form.has_errors():
+ self.submit_auth_request_post_parameters_form(form)
+ return redirect('modify_authentication_request')
+
+ get_response().breadcrumb.append(('auth_request_post_parameters', _('POST parameters')))
+ self.html_top(_('Configure POST parameters'))
+
+ '<p>%s</p>' % _('''Here are the detected form fields that will be sent as parameters of the
+authentication POST request. You can desactivate some or all of them, or change their value.''')
+
+ form.render()
+
+ def form_auth_request_post_parameters(self):
+ form = Form(enctype='multipart/form-data')
+ for name, value in self.host.post_parameters.iteritems():
+ if value['immutable']:
+ form.add(DictWidget, name, value, disabled = 'disabled')
+ else:
+ form.add(DictWidget, name, value)
+ form.add_submit('submit', _('Submit'))
+ form.add_submit('cancel', _('Cancel'))
+ return form
+
+ def submit_auth_request_post_parameters_form(self, form):
+ for name, old_value in self.host.post_parameters.iteritems():
+ value = form.get_widget(name).parse()
+ if value['enabled'] == 'on':
+ old_value['enabled'] = True
+ else:
+ old_value['enabled'] = False
+ if old_value['immutable'] is False:
+ old_value['value'] = value['value']
+ self.host.post_parameters[name] = old_value
+ self.host.store()
+
+ def auth_request_http_headers [html] (self):
+ form = self.form_auth_request_http_headers()
+
+ if form.get_widget('cancel').parse():
+ return redirect('modify_authentication_request')
+
+ if form.is_submitted() and not form.has_errors():
+ self.submit_auth_request_http_headers_form(form)
+ return redirect('modify_authentication_request')
+
+ get_response().breadcrumb.append(('auth_request_http_headers', _('HTTP headers')))
+ self.html_top(_('Configure HTTP headers'))
+
+ '<p>%s</p>' % _('''Here are the HTTP headers that will be sent within the authentication
+POST request. You can desactivate some or all of them, or change their value.''')
+
+ form.render()
+
+ def form_auth_request_http_headers(self):
+ form = Form(enctype='multipart/form-data')
+ for name, value in self.host.http_headers.iteritems():
+ if value['immutable']:
+ form.add(DictWidget, name, value, disabled = 'disabled')
+ else:
+ form.add(DictWidget, name, value)
+ form.add(HtmlWidget, htmltext('<p>%s</p>' % \
+ _('The headers "Host", "Accept-Encoding" and "Content-Length" will also automatically be sent.')))
+ if get_cfg('proxy', {}).get('enabled') and self.host.use_proxy:
+ form.add(HtmlWidget, htmltext('<p>%s</p>' % \
+ _('''As Larpe uses a proxy for this site, the headers "Proxy-Authorization",
+"Proxy-Connection" and "Keep-Alive" will be sent as well.''')))
+ form.add_submit('submit', _('Submit'))
+ form.add_submit('cancel', _('Cancel'))
+ return form
+
+ def submit_auth_request_http_headers_form(self, form):
+ for name, old_value in self.host.http_headers.iteritems():
+ value = form.get_widget(name).parse()
+ if value['enabled'] == 'on':
+ old_value['enabled'] = True
+ else:
+ old_value['enabled'] = False
+ if old_value['immutable'] is False:
+ old_value['value'] = value['value']
+ self.host.http_headers[name] = old_value
+ self.host.store()
+
+ def generate_apache_filter(self):
+ # Set Python filter path for Apache configuration
+ python_path = os.path.join(self.host.site_dir, 'filters')
+ if python_path not in self.host.apache_python_paths:
+ self.host.apache_python_paths.append(python_path)
+
+ # Write Python filter
+ python_file = open(os.path.join(self.host.site_dir, 'filters', 'output_replace_form.py'), 'w')
+ python_file.write(open(OUTPUT_FILTER_BASE).read())
+ python_file.write('''\
+def filter_page(filter, page):
+ current_form = re.compile('<form [^>]*?action="%(auth_form_action)s".*?>.*?</form>', re.DOTALL)
+ return current_form.sub('<form method="post" action="/liberty/%(name)s/login"><input type="submit" value="Connexion" /></form>', page)
+''' % { 'auth_form_action': self.host.auth_form_action, 'name': self.host.name })
+ python_file.close()
+
+ # Set Python filter for Apache configuration
+ if not 'output_replace_form' in self.host.apache_output_python_filters:
+ self.host.apache_output_python_filters.append('output_replace_form')
+
+ def sso_init_link [html] (self):
+ form = self.form_sso_init_link()
+
+ if form.get_widget('cancel').parse():
+ return redirect('check_authentication')
+
+ if form.is_submitted() and not form.has_errors():
+ self.submit_sso_init_link_form(form)
+ if form.get_widget('terminate').parse():
+ return redirect('..')
+ return redirect('metadatas')
+
+ get_response().breadcrumb.append(('sso_init_link', _('SSO initiation')))
+ self.html_top(_('Step 7 - Configure how a Single Sign On can be initiated'))
+
+ '<p>%s\n' % _('Most sites use one of the following 2 ways to allow users to initialise an authentication :')
+ '\t<ol>\n'
+ '\t\t<li>%s</li>\n' % \
+ _('''The site has a single authentication page. It redirects users to this page when
+they click a "Login" button or try to access a page which require users to be authenticated.''')
+ '\t\t<li>%s</li>\n' % \
+ _('''The site includes an authentication form in most or all of his pages. Users can
+authenticate on any of these pages, and don't need to be redirected to a separate authentication page.''')
+ '\t</ol>\n'
+ '</p>\n'
+
+ '<p>%s</p>' % _('Select the way your site works :')
+
+ form.render()
+
+ def form_sso_init_link(self):
+ form = Form(enctype='multipart/form-data')
+ form.add(RadiobuttonsWidget, 'auth_form_places',
+ options=[
+ ('form_once', _('The site has a single authentication page'), 'form_once'),
+ ('form_everywhere', _('The site includes an authentication form in most or all pages'), 'form_everywhere'),
+ ],
+ sort=False, required = True, delim=htmltext('<br />'), value = self.host.auth_form_places)
+ form.add_submit('cancel', _('Previous'))
+ form.add_submit('submit', _('Next'))
+ form.add_submit('terminate', _('Terminate'))
+ return form
+
+ def submit_sso_init_link_form(self, form):
+ fields = [ 'auth_form_places', ]
+ for f in fields:
+ setattr(self.host, f, form.get_widget(f).parse())
+ self.host.auth_form_url = self.host.auth_url
+
+ if self.host.auth_form_places == 'form_everywhere' and self.host.auth_form_action:
+ self.generate_apache_filter()
+ else:
+ if 'output_replace_form' in self.host.apache_output_python_filters:
+ self.host.apache_output_python_filters.remove('output_replace_form')
+
+ self.host.store()
+ write_apache2_vhosts()
+
+ def metadatas [html] (self):
+ form = Form(enctype='multipart/form-data')
+ form.add_submit('cancel', _('Previous'))
+ form.add_submit('submit', _('Next'))
+ form.add_submit('terminate', _('Terminate'))
+
+ if form.get_widget('cancel').parse():
+ return redirect('sso_init_link')
+
+ if form.is_submitted():
+ if form.get_widget('terminate').parse():
+ return redirect('..')
+ return redirect('advanced_options')
+
+ get_response().breadcrumb.append(('metadatas', _('Metadatas')))
+ self.html_top(_('Step 8 - Metadatas of %(site_name)s' % {'site_name': self.host.name}))
+
+ '<p>%s</p>' % \
+ _('''Download the metadatas and the public key for this site and
+upload them on your identity provider in order to use Liberty Alliance features.''')
+
+ '<dl>'
+ if hasattr(self.host, str('base_url')):
+ if lasso.SAML2_SUPPORT:
+ saml2_metadata_url = '%s/metadata.xml' % self.host.saml2_base_url
+ '<dt><a href="%s">%s</a></dt> <dd>%s</dd>' % (
+ saml2_metadata_url,
+ _('SAML 2.0 Metadata'),
+ _('Download SAML 2.0 metadata file'))
+ metadata_url = '%s/metadata.xml' % self.host.base_url
+ '<dt><a href="%s">%s</a></dt> <dd>%s</dd>' % (
+ metadata_url,
+ _('ID-FF 1.2 Metadata'),
+ _('Download ID-FF 1.2 metadata file'))
+ else:
+ '<p>%s</p>' % _('No metadata has been generated for this host.')
+
+ if hasattr(self.host, str('base_url')) and self.host.public_key and os.path.exists(self.host.public_key):
+ public_key_url = '%s/public_key' % self.host.base_url
+ '<dt><a href="%s">%s</a></dt> <dd>%s</dd>' % (
+ public_key_url,
+ _('Public key'),
+ _('Download SSL Public Key file'))
+ else:
+ '<p>%s</p>' % _('No public key has been generated for this host.')
+ '</dl>'
+
+ form.render()
+
+ def advanced_options [html] (self):
+ form = self.form_advanced_options()
+
+ if form.get_widget('cancel').parse():
+ return redirect('metadatas')
+
+ if not form.is_submitted() or form.has_errors():
+ get_response().breadcrumb.append(('advanced_options', _('Advanced options')))
+ self.html_top(_('Step 9 - Advanced options'))
+
+ '<p>%s</p>' % _('Configure advanced options to setup the last details of your site.')
+ '<p>%s</p>' % _('''If you don't know what to configure here, just click %(next)s and
+come here later if needed.''') % {'next': _('Next')}
+
+ form.render()
+ else:
+ self.submit_advanced_options_form(form)
+ if form.get_widget('terminate').parse():
+ return redirect('..')
+ return redirect('check_full_configuration')
+
+ def form_advanced_options(self):
+ form = Form(enctype='multipart/form-data')
+ form.add(CheckboxWidget, 'redirect_root_to_login',
+ title=_('Redirect the root URL of the site to the login page.'),
+ value = self.host.redirect_root_to_login)
+ form.add(UrlOrAbsPathWidget, 'return_url', title = _('Return address'),
+ hint = _('Where the user will be redirected after a successful authentication'),
+ required = False, size = 50, value = self.host.return_url)
+ form.add(UrlOrAbsPathWidget, 'root_url', title = _('Error address'),
+ hint = _('Where the user will be redirected after a disconnection or an error'),
+ required = False, size = 50, value = self.host.root_url)
+ form.add(UrlOrAbsPathWidget, 'initiate_sso_url', title = _('URL which must initiate the SSO'),
+ hint = _('''Address which must initiate the SSO. If empty, defaults to the previously
+specified "%s"''') % _('Authentication form page address'),
+ required = False, size = 50, value = self.host.initiate_sso_url)
+ form.add(CheckboxWidget, 'proxy-html', title = _('Apache HTML proxy'),
+ hint = _('''Converts urls in the HTML pages according to the host new domain name.
+Disabled by default because it makes some sites not work correctly.'''),
+ value = 'proxy-html' in self.host.apache_output_filters)
+
+ form.add_submit('cancel', _('Previous'))
+ form.add_submit('submit', _('Next'))
+ form.add_submit('terminate', _('Terminate'))
+ return form
+
+ def submit_advanced_options_form(self, form):
+ old_redirect_root_to_login = self.host.redirect_root_to_login
+
+ for f in ('redirect_root_to_login', 'return_url', 'root_url', 'initiate_sso_url'):
+ value = form.get_widget(f).parse()
+ setattr(self.host, f, value)
+
+ f = 'proxy-html'
+ value = form.get_widget(f).parse()
+ if value is True and f not in self.host.apache_output_filters:
+ self.host.apache_output_filters.append(f)
+ if value is False and f in self.host.apache_output_filters:
+ self.host.apache_output_filters.remove(f)
+
+ self.host.store()
+
+ if self.host.initiate_sso_url or self.host.redirect_root_to_login is not old_redirect_root_to_login:
+ write_apache2_vhosts()
+
+ def check_full_configuration [html] (self):
+ form = Form(enctype='multipart/form-data')
+ form.add_submit('cancel', _('Previous'))
+ form.add_submit('submit', _('Finish'))
+
+ if form.get_widget('cancel').parse():
+ return redirect('advanced_options')
+
+ if form.is_submitted():
+ return redirect('../..')
+
+ get_response().breadcrumb.append(('check_full_configuration', _('Check everything works')))
+ self.html_top(_('Step 10 - Check everything works'))
+
+ '<p>%s</p>' % \
+ _('''Now you can fully test your site, start from the home page, initiate a
+Single Sign On, federate your identities and do a Single Logout.''')
+
+ '<p>%s' % _('The address of your site is : ')
+ '<a href="%s">%s</a>' % (self.host.new_url, self.host.new_url)
+ '</p>'
+
+ '<p>%s</p>' % \
+ _('''If everything works, click the "%(finish)s" button, otherwise you can go
+back and check your settings.''') % { 'finish': _('Finish') }
+
+ form.render()
+
+ def auto_detect_configuration(self):
+ # Reset previous detected values
+ self.host.auth_form = None
+ self.host.auth_check_url = None
+ self.host.login_field_name = None
+ self.host.password_field_name = None
+ if not self.host.post_parameters:
+ self.host.post_parameters = {}
+
+ self.parse_page(self.host.auth_form_url)
+
+ def parse_page(self, page_url):
+ # Get the authentication page
+ try:
+ response, status, page, auth_header = http_get_page(page_url, use_proxy=self.host.use_proxy)
+ except Exception, msg:
+ print msg
+ return
+
+ if page is None:
+ return
+ #raise FormError, ('auth_check_url', '%s : %s' % (_('Failed to get page'), self.host.auth_form_url))
+
+ # Default authentication mode
+ self.host.auth_mode = 'form'
+
+ self.host.site_authentication_plugin = site_authentication.guess_site_authentication_class(page)
+ self.parse_frames(page)
+ self.parse_forms(page)
+ if self.host.auth_form is not None:
+ self.parse_form_action()
+ input_fields = self.parse_input_fields()
+ self.parse_login_field(input_fields)
+ self.parse_password_field()
+ self.parse_select_fields()
+ self.parse_other_fields()
+
+ def parse_frames(self, page):
+ '''If there are frames, parse them recursively'''
+ regexp = re.compile("""<frame.*?src=["'](.*?)["'][^>]*?>""", re.DOTALL | re.IGNORECASE)
+ found_frames = regexp.findall(page)
+ if found_frames:
+ for frame_url in found_frames:
+ if frame_url.startswith('http'):
+ frame_full_url = frame_url
+ else:
+ page_url_tokens = page_url.split('/')
+ page_url_tokens[-1] = frame_url
+ frame_full_url = '/'.join(page_url_tokens)
+ self.parse_page(frame_full_url)
+
+ def parse_forms(self, page):
+ '''Search for an authentication form'''
+ # Get all forms
+ regexp = re.compile("""<form.*?</form>""", re.DOTALL | re.IGNORECASE)
+ found_forms = regexp.findall(page)
+ if not found_forms:
+ return
+ #raise FormError, ('auth_check_url', '%s : %s' % (_('Failed to find any form'), self.host.auth_form_url))
+
+ # Get the first form with a password field
+ for found_form in found_forms:
+ regexp = re.compile("""<input[^>]*?type=["']?password["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
+ if regexp.search(found_form) is not None:
+ self.host.auth_form = found_form
+ break
+
+ def parse_form_action(self):
+ '''Get the action url of the form'''
+ regexp = re.compile("""<form.*?action=["']?(.*?)["']?[\s>].*?>""", re.DOTALL | re.IGNORECASE)
+ self.host.auth_form_action = regexp.findall(self.host.auth_form)[0]
+ # FIXME: Find a Python module which unescapes html entities
+ self.host.auth_check_url = self.host.auth_form_action.replace('&amp;', '&')
+ if not self.host.auth_check_url.startswith('http'):
+ if self.host.auth_check_url.startswith('/'):
+ if self.host.orig_site.startswith('https'):
+ orig_site_root = 'https://%s' % urllib.splithost(self.host.orig_site[6:])[0]
+ else:
+ orig_site_root = 'http://%s' % urllib.splithost(self.host.orig_site[5:])[0]
+ self.host.auth_check_url = orig_site_root + self.host.auth_check_url
+ else:
+ auth_form_url_tokens = self.host.auth_form_url.split('/')
+ auth_form_url_tokens[-1] = self.host.auth_check_url
+ self.host.auth_check_url = '/'.join(auth_form_url_tokens)
+
+ def parse_input_fields(self):
+ '''Get all input fields'''
+ regexp = re.compile("""<input[^>]*?>""", re.DOTALL | re.IGNORECASE)
+ return regexp.findall(self.host.auth_form)
+
+ def parse_login_field(self, input_fields):
+ '''Get login field name'''
+ try:
+ regexp = re.compile("""<input[^>]*?type=["']?text["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
+ text_fields = regexp.findall(self.host.auth_form)
+ login_field = ''
+ if text_fields:
+ login_field = text_fields[0]
+ else:
+ for field in input_fields:
+ if re.search("""type=["']?""", field, re.DOTALL | re.IGNORECASE) is None:
+ login_field = field
+ break
+ regexp = re.compile("""name=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE)
+ self.host.login_field_name = regexp.findall(login_field)[0]
+ if not self.host.post_parameters.has_key(self.host.login_field_name):
+ self.host.post_parameters[self.host.login_field_name] = \
+ { 'enabled': True, 'value': _('(filled by users)'), 'immutable': True }
+ self.host.store()
+ except IndexError, e:
+ self.host.login_field_name = None
+ print 'Error handling login field : %s' % e
+
+ def parse_password_field(self):
+ '''Get password field name'''
+ try:
+ regexp = re.compile("""<input[^>]*?type=["']?password["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
+ password_field = regexp.findall(self.host.auth_form)[0]
+ regexp = re.compile("""name=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE)
+ self.host.password_field_name = regexp.findall(password_field)[0]
+ if not self.host.post_parameters.has_key(self.host.password_field_name):
+ self.host.post_parameters[self.host.password_field_name] = \
+ { 'enabled': True, 'value': _('(filled by users)'), 'immutable': True }
+ except IndexError, e:
+ self.host.password_field_name = None
+ print 'Error handling password field : %s' % e
+
+ def parse_select_fields(self):
+ '''Add select fields to host attributes'''
+ # First added for Imuse (Rennes)
+ regexp = re.compile("""<select.*?</select>""", re.DOTALL | re.IGNORECASE)
+ self.host.select_fields = {}
+ for field in regexp.findall(self.host.auth_form):
+ try:
+ regexp = re.compile("""name=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE)
+ name = regexp.findall(field)[0]
+ regexp = re.compile("""<option[^>]*?>.*?</option>""", re.DOTALL | re.IGNORECASE)
+ options = regexp.findall(field)
+ values = []
+ for option in options:
+ regexp = re.compile("""<option[^>]*?>(.*?)</option>""", re.DOTALL | re.IGNORECASE)
+ option_label = regexp.findall(option)
+ regexp = re.compile("""value=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE)
+ option_value = regexp.findall(option)
+ if option_label:
+ if not option_value:
+ option_value = option_label
+ values.append((option_value[0], option_label[0]))
+ else:
+ print >> sys.stderr, 'W: Could not parse select options'
+ self.host.select_fields[name] = values
+ if not self.host.post_parameters.has_key(name):
+ self.host.post_parameters[name] = \
+ { 'enabled': True, 'value': _('(filled by users)'), 'immutable': True }
+ except IndexError, e:
+ continue
+
+ def parse_other_fields(self):
+ '''Get the default value of all other fields'''
+ self.host.other_fields = {}
+
+ # Get hidden fields
+ regexp = re.compile("""<input[^>]*?type=["']?hidden["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
+ other_fields = regexp.findall(self.host.auth_form)
+
+ # Only get first submit field
+ regexp = re.compile("""<input[^>]*?type=["']?submit["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
+ found = regexp.findall(self.host.auth_form)
+ if found:
+ if other_fields:
+ other_fields.append(found[0])
+ else:
+ other_fields = found[0]
+
+ for field in other_fields:
+ try:
+ regexp = re.compile("""name=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE)
+ name = regexp.findall(field)[0]
+ regexp = re.compile("""value=["'](.*?)["'][\s/>]""", re.DOTALL | re.IGNORECASE)
+ value = regexp.findall(field)[0]
+ self.host.other_fields[name] = value
+ if not self.host.post_parameters.has_key(name):
+ self.host.post_parameters[name] = { 'enabled': True, 'value': value, 'immutable': False }
+ except IndexError, e:
+ continue
+
+
+class HostPage(Directory):
+ _q_exports = ['', 'delete']
+
+ def __init__(self, host_id):
+ self.host = Host.get(host_id)
+ get_response().breadcrumb.append((host_id + '/', self.host.label))
+
+ def _q_lookup(self, component):
+ if component == 'configuration_assistant':
+ return ConfigurationAssistant(self.host)
+ elif component == 'forms_prefill':
+ return FormsDirectory(self.host)
+
+ def _q_index [html] (self):
+ get_publisher().reload_cfg()
+ html_top('hosts', title = self.host.label)
+
+ '<h2>%s</h2>' % _('Configuration assistant')
+
+ '<dl>'
+
+ '<dt><a href="configuration_assistant/start">%s</a></dt> <dd>%s</dd>' % (
+ _('Address of the original site'), _('Configure the root address of the site'))
+
+ '<dt><a href="configuration_assistant/check_new_address">%s</a></dt> <dd>%s</dd>' % (
+ _('New address and name'), _('Configure the new address and name of this site'))
+
+ '<dt><a href="configuration_assistant/authentication_and_logout_adresses">%s</a></dt> <dd>%s</dd>' % (
+ _('Authentication and logout addresses'), _('Configure the authentication and logout addresses of the original site'))
+
+ '<dt><a href="configuration_assistant/check_auto_detected_configuration">%s</a></dt> <dd>%s</dd>' % (
+ _('Check auto detected configuration'), _('Check the automatically detected configuration is right'))
+
+ '<dt><a href="configuration_assistant/credentials">%s</a></dt> <dd>%s</dd>' % (
+ _('Credentials'), _('Configure some valid credentials to authenticate on the original site'))
+
+ '<dt><a href="configuration_assistant/send_authentication_request">%s</a></dt> <dd>%s</dd>' % (
+ _('Retry authentication'), _('Retry sending an authentication request to the site to check if your new parameters work well'))
+
+ '<dt><a href="configuration_assistant/see_authentication_response">%s</a></dt> <dd>%s</dd>' % (
+ _('Check authentication response'), _('Check the response from the latest authentication request'))
+
+ '<dt><a href="configuration_assistant/authentication_success_criteria">%s</a></dt> <dd>%s</dd>' % (
+ _('Configure authentication success criteria'), _('Specify how Larpe knows if the authentication has succeeded or not'))
+
+ '<dt><a href="configuration_assistant/modify_authentication_request">%s</a></dt> <dd>%s</dd>' % (
+ _('Modify authentication request'), _('Modify POST fields or HTTP headers of the authentication request'))
+
+ '<dt><a href="configuration_assistant/sso_init_link">%s</a></dt> <dd>%s</dd>' % (
+ _('Configure how a Single Sign On can be initiated'), _('Configure how a Single Sign On can be initiated'))
+
+ '<dt><a href="configuration_assistant/metadatas">%s</a></dt> <dd>%s</dd>' % (
+ _('Metadatas and key'), _('Download SAML 2.0 or ID-FF metadatas and SSL public key'))
+
+ '<dt><a href="configuration_assistant/advanced_options">%s</a></dt> <dd>%s</dd>' % (
+ _('Adavanced options'), _('Configure advanced options to setup the last details of your site'))
+
+ '</dl>'
+
+ '<h2>%s</h2>' % _('Form prefilling with ID-WSF')
+
+ '<dl>'
+ '<dt><a href="forms_prefill/">%s</a></dt> <dd>%s</dd>' % (
+ _('Forms'), _('Configure the forms to prefill'))
+ '</dl>'
+
+ def delete [html] (self):
+ form = Form(enctype='multipart/form-data')
+ form.widgets.append(HtmlWidget('<p>%s</p>' % _(
+ 'You are about to irrevocably delete this host.')))
+ 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():
+ get_response().breadcrumb.append(('delete', _('Delete')))
+ html_top('hosts', title = _('Delete Host'))
+ '<h2>%s : %s</h2>' % (_('Delete Host'), self.host.label)
+ form.render()
+ else:
+ self.host.remove_self()
+ write_apache2_vhosts()
+ return redirect('..')
+
+
+class HostsDirectory(Directory):
+ _q_exports = ['', 'new']
+
+ def _q_index [html] (self):
+ get_response().breadcrumb.append(('hosts/', _('Hosts')))
+ html_top('hosts', title = _('Hosts'))
+ """<ul id="nav-hosts-admin">
+ <li><a href="new">%s</a></li>
+ </ul>""" % _('New Host')
+
+ '<ul class="biglist">'
+
+ for host in Host.select(lambda x: x.name != 'larpe', order_by = 'label'):
+ if not host.name:
+ continue
+ if not hasattr(host, str('scheme')):
+ host.scheme = str('http')
+ '<li>'
+ '<strong class="label">%s</strong>' % host.label
+ if hasattr(host, str('new_url')) and host.new_url:
+ url = host.new_url
+ else:
+ # Compat with older Larpe versions
+ url = '%s://%s%s/' % (host.scheme, host.reversed_hostname, get_request().environ['SCRIPT_NAME'])
+ if host.reversed_directory is not None:
+ url += '%s/' % host.reversed_directory
+ '<br /><a href="%s">%s</a>' % (url, url)
+ '<p class="commands">'
+ command_icon('%s/' % host.id, 'edit')
+ command_icon('%s/delete' % host.id, 'remove')
+ '</p></li>'
+ '</ul>'
+
+ def new [html] (self):
+ if not os.path.isdir(os.path.join(get_publisher().app_dir, str('idp'))):
+ html_top('hosts', title = _('New Host'))
+ html = '<h2>%s</h2>' % _('New Host')
+ html += 'You must <a href="%s/admin/settings/liberty_idp/">' % misc.get_root_url()
+ html += 'configure an Identity Provider</a> first<br /><br />'
+ html += '<a href="."><input type="button" value="%s" /></a>' % _('Back')
+ return html
+
+ get_response().breadcrumb.append(('hosts/', _('Hosts')))
+ get_response().breadcrumb.append(('new', _('New')) )
+ host = Host()
+ host.store()
+ return redirect('%s/configuration_assistant/start' % host.id)
+
+ def _q_lookup(self, component):
+ get_response().breadcrumb.append(('hosts/', _('Hosts')))
+ return HostPage(component)
+
diff --git a/larpe/tags/release-1.0/larpe/admin/liberty_utils.py b/larpe/tags/release-1.0/larpe/admin/liberty_utils.py
new file mode 100644
index 0000000..de324c4
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/admin/liberty_utils.py
@@ -0,0 +1,129 @@
+import os
+
+def set_provider_keys(private_key_path, public_key_path):
+ # use system calls for openssl since PyOpenSSL doesn't expose the
+ # necessary functions.
+ if os.system('openssl version > /dev/null 2>&1') == 0:
+ os.system('openssl genrsa -out %s 2048' % private_key_path)
+ os.system('openssl rsa -in %s -pubout -out %s' % (private_key_path, public_key_path))
+
+
+def get_metadata(cfg):
+ prologue = """\
+<?xml version="1.0"?>
+<EntityDescriptor
+ providerID="%(provider_id)s"
+ xmlns="urn:liberty:metadata:2003-08">""" % cfg
+
+ sp_head = """
+ <SPDescriptor protocolSupportEnumeration="urn:liberty:iff:2003-08">"""
+
+ signing_public_key = ''
+ if cfg.has_key('signing_public_key') and cfg['signing_public_key']:
+ if 'CERTIF' in cfg['signing_public_key']:
+ signing_public_key = """
+ <KeyDescriptor use="signing">
+ <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+ <ds:X509Data><ds:X509Certificate>%s</ds:X509Certificate></ds:X509Data>
+ </ds:KeyInfo>
+ </KeyDescriptor>""" % cfg['signing_public_key']
+ elif 'KEY' in cfg['signing_public_key']:
+ signing_public_key = """
+ <KeyDescriptor use="signing">
+ <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+ <ds:KeyValue>%s</ds:KeyValue>
+ </ds:KeyInfo>
+ </KeyDescriptor>""" % cfg['signing_public_key']
+
+ sp_body = """
+ <AssertionConsumerServiceURL id="AssertionConsumerServiceURL1" isDefault="true">%(base_url)s/assertionConsumer</AssertionConsumerServiceURL>
+
+ <SoapEndpoint>%(base_url)s/soapEndpoint</SoapEndpoint>
+
+ <SingleLogoutServiceURL>%(base_url)s/singleLogout</SingleLogoutServiceURL>
+ <SingleLogoutServiceReturnURL>%(base_url)s/singleLogoutReturn</SingleLogoutServiceReturnURL>
+ <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>
+
+ <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>
+
+ <AuthnRequestsSigned>true</AuthnRequestsSigned>
+
+ </SPDescriptor>""" % cfg
+
+ orga = ''
+ if cfg.get('organization_name'):
+ orga = """
+ <Organization>
+ <OrganizationName>%s</OrganizationName>
+ </Organization>""" % unicode(cfg['organization_name'], 'iso-8859-1').encode('utf-8')
+
+ epilogue = """
+</EntityDescriptor>"""
+
+ return '\n'.join([prologue, sp_head, signing_public_key, sp_body, orga, epilogue])
+
+
+
+def get_saml2_metadata(cfg):
+ prologue = """\
+<?xml version="1.0"?>
+<EntityDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
+ xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
+ xmlns:ds="http://www.w3.org/2000/09/xmldsig#"
+ entityID="%(saml2_provider_id)s">""" % cfg
+
+ sp_head = """
+ <SPSSODescriptor
+ AuthnRequestsSigned="true"
+ protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">"""
+
+ signing_public_key = ''
+ if cfg.has_key('signing_public_key') and cfg['signing_public_key']:
+ if 'CERTIF' in cfg['signing_public_key']:
+ signing_public_key = """
+ <KeyDescriptor use="signing">
+ <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+ <ds:X509Data><ds:X509Certificate>%s</ds:X509Certificate></ds:X509Data>
+ </ds:KeyInfo>
+ </KeyDescriptor>""" % cfg['signing_public_key']
+ elif 'KEY' in cfg['signing_public_key']:
+ signing_public_key = """
+ <KeyDescriptor use="signing">
+ <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
+ <ds:KeyValue>%s</ds:KeyValue>
+ </ds:KeyInfo>
+ </KeyDescriptor>""" % cfg['signing_public_key']
+
+ sp_body = """
+ <AssertionConsumerService isDefault="true" index="0"
+ Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact"
+ Location="%(saml2_base_url)s/singleSignOnArtifact" />
+ <SingleLogoutService
+ Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP"
+ Location="%(saml2_base_url)s/singleLogoutSOAP" />
+ <SingleLogoutService
+ Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
+ Location="%(saml2_base_url)s/singleLogout"
+ ResponseLocation="%(saml2_base_url)s/singleLogoutReturn" />
+
+ </SPSSODescriptor>""" % cfg
+
+ orga = ''
+ if cfg.get('organization_name'):
+ orga = """
+ <Organization>
+ <OrganizationName>%s</OrganizationName>
+ </Organization>""" % unicode(cfg['organization_name'], 'iso-8859-1').encode('utf-8')
+
+ epilogue = """
+</EntityDescriptor>"""
+
+ return '\n'.join([prologue, sp_head, signing_public_key, sp_body, orga, epilogue])
+
diff --git a/larpe/tags/release-1.0/larpe/admin/root.ptl b/larpe/tags/release-1.0/larpe/admin/root.ptl
new file mode 100644
index 0000000..8a65592
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/admin/root.ptl
@@ -0,0 +1,90 @@
+import os
+
+import lasso
+
+from quixote import get_session, get_session_manager, get_publisher, get_request, get_response
+from quixote.directory import Directory, AccessControlled
+
+from qommon.admin.menu import html_top
+from qommon.admin import logger
+
+from larpe import errors
+from larpe import misc
+
+import hosts
+import users
+import settings
+
+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):
+ _q_exports = ['', 'hosts', 'users', 'settings', 'logger']
+
+ hosts = hosts.HostsDirectory()
+ users = users.UsersDirectory()
+ settings = settings.SettingsDirectory()
+ logger = logger.LoggerDirectory()
+
+ menu_items = [
+ ('hosts/', N_('Hosts')),
+ ('users/', N_('Users')),
+ ('settings/', N_('Settings')),
+ ('logger/', N_('Logs')),
+ ('/', N_('Liberty Alliance Reverse Proxy'))]
+
+ def _q_access(self):
+ # FIXME : this block should be moved somewhere else
+ get_publisher().reload_cfg()
+ if not get_publisher().cfg.has_key('proxy_hostname'):
+ get_publisher().cfg['proxy_hostname'] = get_request().get_server().split(':')[0]
+ get_publisher().write_cfg()
+
+ response = get_response()
+ if not hasattr(response, 'breadcrumb'):
+ response.breadcrumb = [ ('../admin/', _('Administration')) ]
+
+ # Cheater
+ if os.path.exists(os.path.join(get_publisher().app_dir, 'ADMIN_FOR_ALL')):
+ return
+
+ # No admin user created yet, free access
+ user_list = users.User.select(lambda x: x.is_admin)
+ if not user_list:
+ return
+
+ host_list = hosts.Host.select(lambda x: x.name == 'larpe')
+ if host_list:
+ host = host_list[0]
+ else:
+ raise errors.AccessForbiddenError()
+
+ if misc.get_current_protocol() == lasso.PROTOCOL_SAML_2_0:
+ user = get_session().get_user(host.saml2_provider_id)
+ else:
+ user = get_session().get_user(host.provider_id)
+ if user:
+ if not user.name or not user.is_admin:
+ raise errors.AccessForbiddenError()
+ else:
+ raise errors.AccessUnauthorizedError()
+
+
+ def _q_index [html] (self):
+ html_top('')
+ gpl()
+
diff --git a/larpe/tags/release-1.0/larpe/admin/settings.ptl b/larpe/tags/release-1.0/larpe/admin/settings.ptl
new file mode 100644
index 0000000..a1a36f1
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/admin/settings.ptl
@@ -0,0 +1,361 @@
+import cStringIO
+import cPickle
+import re
+import os
+import lasso
+import glob
+import zipfile
+
+from quixote import get_publisher, get_request, get_response, redirect
+from quixote.directory import Directory, AccessControlled
+
+from qommon.form import *
+from qommon.misc import get_abs_path
+from qommon.admin.cfg import cfg_submit
+from qommon.admin.menu import html_top, error_page
+from qommon.admin.emails import EmailsDirectory as QommonEmailsDirectory
+from qommon.admin.settings import SettingsDirectory as QommonSettingsDirectory
+
+from larpe import misc
+from larpe.hosts import Host
+from larpe.admin.liberty_utils import *
+
+class LibertyIDPDir(Directory):
+ _q_exports = ['', ('metadata.xml', 'metadata')]
+
+ def _q_index [html] (self):
+ form = Form(enctype="multipart/form-data")
+ form.add(FileWidget, "metadata", title = _("Metadata"), required=True)
+ form.add(FileWidget, "publickey", title = _("Public Key"), required=False)
+ 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()
+ else:
+ self.submit_new(form)
+
+ def submit_new(self, form, key_provider_id = None):
+ 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:
+ try:
+ provider_id = re.findall(r'(provider|entity)ID="(.*?)"', metadata)[0][1]
+ except IndexError:
+ return error_page('settings', _('Bad metadata'))
+ key_provider_id = provider_id.replace(str('://'), str('-')).replace(str('/'), str('-'))
+
+ dir = get_abs_path(os.path.join('idp', key_provider_id))
+ if not os.path.isdir(dir):
+ os.makedirs(dir)
+
+ if metadata:
+ metadata_fn = os.path.join(dir, 'metadata.xml')
+ open(metadata_fn, 'w').write(metadata)
+ if publickey:
+ publickey_fn = os.path.join(dir, 'public_key')
+ open(publickey_fn, 'w').write(publickey)
+ else:
+ publickey_fn = None
+ if cacertchain:
+ cacertchain_fn = os.path.join(dir, 'ca_cert_chain.pem')
+ open(cacertchain_fn, 'w').write(cacertchain)
+ else:
+ cacertchain_fn = None
+
+ p = lasso.Provider(lasso.PROVIDER_ROLE_IDP, metadata_fn, publickey_fn, None)
+
+ try:
+ misc.get_provider_label(p)
+ get_publisher().cfg['idp'] = key_provider_id
+ get_publisher().write_cfg()
+ except TypeError:
+ if metadata:
+ os.unlink(metadata_fn)
+ if publickey:
+ os.unlink(publickey_fn)
+ if cacertchain:
+ os.unlink(cacertchain_fn)
+ return error_page('settings', _('Bad metadata'))
+
+ redirect('..')
+
+ def metadata(self):
+ response = get_response()
+ response.set_content_type('text/xml', 'utf-8')
+ get_publisher().reload_cfg()
+ if get_publisher().cfg['idp']:
+ idp_metadata = os.path.join(get_abs_path('idp'), get_publisher().cfg['idp'], 'metadata.xml')
+ return unicode(open(idp_metadata).read(), 'utf-8')
+ return 'No IDP is configured'
+
+
+class EmailsDirectory(QommonEmailsDirectory):
+ def _q_index [html] (self):
+ # Don't use custom emails
+ html_top('settings', title = _('Emails'))
+ '<h2>%s</h2>' % _('Emails')
+
+ '<ul>'
+ '<li><a href="options">%s</a></li>' % _('General Options')
+ '</ul>'
+
+ '<p>'
+ '<a href="..">%s</a>' % _('Back')
+ '</p>'
+
+
+class SettingsDirectory(QommonSettingsDirectory):
+ _q_exports = ['', 'liberty_sp', 'liberty_idp', 'domain_names', 'apache2_configuration_generation',
+ 'proxy', 'language', 'emails', 'debug_options' ]
+
+ liberty_idp = LibertyIDPDir()
+ emails = EmailsDirectory()
+
+ def _q_index [html] (self):
+ get_publisher().reload_cfg()
+ html_top('settings', title = _('Settings'))
+
+ if lasso.SAML2_SUPPORT:
+ '<h2>%s</h2>' % _('Liberty Alliance & SAML 2.0 Service Provider')
+ else:
+ '<h2>%s</h2>' % _('Liberty Alliance Service Provider')
+ '<dl> <dt><a href="liberty_sp">%s</a></dt> <dd>%s</dd>' % (
+ _('Service Provider'), _('Configure Larpe as a Service Provider'))
+
+ hosts = Host.select(lambda x: x.name == 'larpe')
+ if hosts:
+ self.host = hosts[0]
+
+ if lasso.SAML2_SUPPORT and self.host.saml2_metadata is not None:
+ metadata_url = '%s/metadata.xml' % self.host.saml2_base_url
+ '<dt><a href="%s">%s</a></dt> <dd>%s</dd>' % (
+ metadata_url,
+ _('SAML 2.0 Metadata'),
+ _('Download SAML 2.0 metadata file for Larpe'))
+
+ if self.host.metadata is not None:
+ metadata_url = '%s/metadata.xml' % self.host.base_url
+ '<dt><a href="%s">%s</a></dt> <dd>%s</dd>' % (
+ metadata_url,
+ _('ID-FF 1.2 Metadata'),
+ _('Download ID-FF 1.2 metadata file for Larpe'))
+
+ if self.host.public_key is not None:
+ public_key_url = '%s/public_key' % self.host.base_url
+ '<dt><a href="%s">%s</a></dt> <dd>%s</dd>' % (
+ public_key_url,
+ _('Public key'),
+ _('Download SSL Public Key file'))
+
+ if lasso.SAML2_SUPPORT:
+ '<h2>%s</h2>' % _('Liberty Alliance & SAML 2.0 Identity Provider')
+ else:
+ '<h2>%s</h2>' % _('Liberty Alliance Identity Provider')
+
+ '<dl>'
+
+ '<dt><a href="liberty_idp/">%s</a></dt> <dd>%s</dd>' % (
+ _('Identity Provider'), _('Configure an identity provider'))
+
+ if get_publisher().cfg.has_key('idp'):
+ '<dt><a href="liberty_idp/metadata.xml">%s</a></dt> <dd>%s</dd>' % (
+ _('Identity Provider metadatas'), _('See current identity provider metadatas'))
+
+ '</dl>'
+
+ '<h2>%s</h2>' % _('Global parameters for the sites')
+
+ '<dl>'
+ '<dt><a href="domain_names">%s</a></dt> <dd>%s</dd>' % (
+ _('Domain name'), _('Configure the base domain name for the sites'))
+ '<dt><a href="apache2_configuration_generation">%s</a></dt> <dd>%s</dd>' % (
+ _('Apache 2 configuration generation'), _('Customise Apache 2 configuration generation'))
+ '<dt><a href="proxy">%s</a></dt> <dd>%s</dd>' % (
+ _('Proxy'), _('Connect to the sites through a web proxy'))
+ '</dl>'
+
+ '<h2>%s</h2>' % _('Customisation')
+
+ '<dl>'
+ '<dt><a href="language">%s</a></dt> <dd>%s</dd>' % (
+ _('Language'), _('Configure site language'))
+ '<dt><a href="emails/">%s</a></dt> <dd>%s</dd>' % (
+ _('Emails'), _('Configure email settings'))
+ '</dl>'
+
+ '<h2>%s</h2>' % _('Misc')
+
+ '<dl>'
+ '<dt><a href="debug_options">%s</a></dt> <dd>%s</dd>' % (
+ _('Debug Options'), _('Configure options useful for debugging'))
+ '</dl>'
+
+
+ def liberty_sp [html] (self):
+ get_publisher().reload_cfg()
+
+ # Get the host object for the reverse proxy
+ hosts = Host.select(lambda x: x.name == 'larpe')
+ if hosts:
+ self.host = hosts[0]
+ else:
+ self.host = Host()
+ self.host.reversed_hostname = get_publisher().cfg[str('proxy_hostname')]
+
+ form = Form(enctype='multipart/form-data')
+ form.add(StringWidget, 'organization_name', title=_('Organisation Name'), size=50,
+ required = True, value = self.host.organization_name)
+ 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 = _('Service Provider Configuration'))
+ '<h2>%s</h2>' % _('Service Provider Configuration')
+ form.render()
+ else:
+ self.liberty_sp_submit(form)
+ redirect('.')
+
+ def liberty_sp_submit(self, form):
+ get_publisher().reload_cfg()
+ metadata_cfg = {}
+
+ f = 'organization_name'
+ if form.get_widget(f):
+ setattr(self.host, f, form.get_widget(f).parse())
+
+ metadata_cfg['organization_name'] = self.host.organization_name
+
+ self.host.name = 'larpe'
+
+ # Liberty Alliance / SAML parameters
+ base_url = '%s/liberty/%s/liberty' % (misc.get_root_url(), self.host.name)
+ metadata_cfg['base_url'] = base_url
+ self.host.base_url = base_url
+
+ if lasso.SAML2_SUPPORT:
+ saml2_base_url = '%s/liberty/%s/saml' % (misc.get_root_url(), self.host.name)
+ metadata_cfg['saml2_base_url'] = saml2_base_url
+ self.host.saml2_base_url = saml2_base_url
+
+ provider_id = '%s/metadata' % base_url
+ metadata_cfg['provider_id'] = provider_id
+ self.host.provider_id = provider_id
+
+ if lasso.SAML2_SUPPORT:
+ saml2_provider_id = '%s/metadata' % saml2_base_url
+ metadata_cfg['saml2_provider_id'] = saml2_provider_id
+ self.host.saml2_provider_id = saml2_provider_id
+
+ # Storage directories
+ site_dir = os.path.join(get_publisher().app_dir, 'sp',
+ self.host.reversed_hostname, self.host.name)
+ user_dir = os.path.join(site_dir, 'users')
+ token_dir = os.path.join(site_dir, 'tokens')
+ for dir in (site_dir, user_dir, token_dir):
+ if not os.path.isdir(dir):
+ os.makedirs(dir)
+ metadata_cfg['site_dir'] = site_dir
+ self.host.site_dir = site_dir
+
+ # Generate SSL keys
+ private_key_path = os.path.join(site_dir, 'private_key.pem')
+ public_key_path = os.path.join(site_dir, 'public_key')
+ if not os.path.isfile(private_key_path) or not os.path.isfile(public_key_path):
+ set_provider_keys(private_key_path, public_key_path)
+ self.host.private_key = private_key_path
+ metadata_cfg['signing_public_key'] = open(public_key_path).read()
+ self.host.public_key = public_key_path
+
+ # Write metadatas
+ metadata_path = os.path.join(site_dir, 'metadata.xml')
+ open(metadata_path, 'w').write(get_metadata(metadata_cfg))
+ self.host.metadata = metadata_path
+
+ if hasattr(self.host, 'saml2_provider_id'):
+ saml2_metadata_path = os.path.join(site_dir, 'saml2_metadata.xml')
+ open(saml2_metadata_path, 'w').write(get_saml2_metadata(metadata_cfg))
+ self.host.saml2_metadata = saml2_metadata_path
+
+ self.host.root_url = '%s/' % misc.get_root_url()
+ self.host.return_url = '%s/admin/' % misc.get_root_url()
+
+ self.host.store()
+
+ def domain_names [html] (self):
+ form = self.form_domain_name()
+
+ if form.get_widget('cancel').parse():
+ return redirect('.')
+
+ if not form.is_submitted() or form.has_errors():
+ html_top('settings', title = _('Domain name'))
+ '<h2>%s</h2>' % _('Domain name')
+ form.render()
+ else:
+ self.submit_domain_name(form)
+ redirect('.')
+
+ def form_domain_name(self):
+ get_publisher().reload_cfg()
+ if get_cfg('domain_names'):
+ domain_name = get_cfg('domain_names')[0]
+ else:
+ domain_name = None
+
+ form = Form(enctype='multipart/form-data')
+ form.add(StringWidget, 'domain_name',
+ title=_('Domain name for the sites'),
+ value = domain_name)
+ # TODO: Add the option "Both" and handle it in hosts configuration
+ form.add(SingleSelectWidget, 'sites_url_scheme', title = _('Use HTTP or HTTPS'),
+ value = get_cfg('sites_url_scheme'),
+ options = [ (None, _('Same as the site')),
+ ('http', 'HTTP'),
+ ('https', 'HTTPS') ] )
+ form.add_submit('submit', _('Submit'))
+ form.add_submit('cancel', _('Cancel'))
+ return form
+
+ def submit_domain_name(self, form):
+ get_publisher().reload_cfg()
+ get_publisher().cfg['domain_names'] = [ form.get_widget('domain_name').parse() ]
+ get_publisher().cfg['sites_url_scheme'] = form.get_widget('sites_url_scheme').parse()
+ get_publisher().write_cfg()
+
+ def apache2_configuration_generation [html] (self):
+ get_publisher().reload_cfg()
+
+ form = Form(enctype='multipart/form-data')
+ form.add(CheckboxWidget, 'allow_config_generation',
+ title=_('Automatically generate Apache 2 configuration for new hosts and reload Apache 2 after changes'),
+ value = get_publisher().cfg.get(str('allow_config_generation'), True))
+ 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 = _('Apache 2 configuration generation'))
+ '<h2>%s</h2>' % _('Apache 2 configuration generation')
+ form.render()
+ else:
+ self.apache2_configuration_generation_submit(form)
+ redirect('.')
+
+ def apache2_configuration_generation_submit(self, form):
+ get_publisher().reload_cfg()
+
+ f = 'allow_config_generation'
+ get_publisher().cfg[f] = form.get_widget(f).parse()
+
+ get_publisher().write_cfg()
diff --git a/larpe/tags/release-1.0/larpe/admin/users.ptl b/larpe/tags/release-1.0/larpe/admin/users.ptl
new file mode 100644
index 0000000..6f07b87
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/admin/users.ptl
@@ -0,0 +1,275 @@
+import random
+
+import lasso
+
+from quixote import get_request, get_session, redirect, get_publisher
+from quixote.directory import Directory
+
+from qommon.admin.menu import html_top, error_page, command_icon
+from qommon.form import *
+from qommon import emails
+
+from larpe import errors
+from larpe import misc
+from larpe.users import User
+from larpe.hosts import Host
+
+class UserUI:
+ def __init__(self, user):
+ self.user = user
+
+ def form_new(self):
+ form = Form(enctype="multipart/form-data")
+ form.add(StringWidget, "name", title = _('User Name'), required = True, size=30)
+ form.add(StringWidget, "email", title = _('Email'), required = False, size=30)
+ 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, "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_submit("submit", _("Submit"))
+ form.add_submit("cancel", _("Cancel"))
+ return form
+
+ def submit_form(self, form):
+ if not self.user:
+ self.user = User()
+ for f in ('name', 'email'):
+ widget = form.get_widget(f)
+ if widget:
+ setattr(self.user, f, widget.parse())
+ self.user.is_admin = True
+ self.user.store()
+
+
+class UserPage(Directory):
+ _q_exports = ['', 'edit', 'delete', 'token']
+
+ def __init__(self, component):
+ self.user = User.get(component)
+ self.user_ui = UserUI(self.user)
+ get_response().breadcrumb.append((component + '/', self.user.name))
+
+ def _q_index [html] (self):
+ html_top('users', '%s - %s' % (_('User'), self.user.name))
+ '<h2>%s - %s</h2>' % (_('User'), self.user.name)
+ '<div class="form">'
+ '<div class="title">%s</div>' % _('Name')
+ '<div class="StringWidget content">%s</div>' % self.user.name
+ if self.user.email:
+ '<div class="title">%s</div>' % _('Email')
+ '<div class="StringWidget content">%s</div>' % self.user.email
+# if self.user.lasso_dump:
+# identity = lasso.Identity.newFromDump(self.user.lasso_dump)
+# server = misc.get_lasso_server()
+# if len(identity.providerIds) and server:
+# '<h3>%s</h3>' % _('Liberty Alliance Details')
+# '<div class="StringWidget content"><ul>'
+# for pid in identity.providerIds:
+# provider = server.getProvider(pid)
+# label = misc.get_provider_label(provider)
+# if label:
+# label = '%s (%s)' % (label, pid)
+# else:
+# label = pid
+# federation = identity.getFederation(pid)
+# '<li>'
+# _('Account federated with %s') % label
+# '<br />'
+# if federation.localNameIdentifier:
+# _("local: ") + federation.localNameIdentifier.content
+# if federation.remoteNameIdentifier:
+# _("remote: ") + federation.remoteNameIdentifier.content
+# '</li>'
+# '</ul></div>'
+
+# # XXX: only display this in debug mode:
+# '<h4>%s</h4>' % _('Lasso Identity Dump')
+# '<pre>%s</pre>' % self.user.lasso_dump
+ '</div>'
+
+ def debug [html] (self):
+ get_response().breadcrumb.append( ('debug', _('Debug')) )
+ html_top('users', 'Debug')
+ "<h2>Debug - %s</h2>" % self.user.name
+ "<pre>"
+ self.user.lasso_dump
+ "</pre>"
+
+ 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():
+ get_response().breadcrumb.append( ('edit', _('Edit')) )
+ html_top('users', title = _('Edit User'))
+ '<h2>%s</h2>' % _('Edit User')
+ form.render()
+ else:
+ self.user_ui.submit_form(form)
+ 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("submit", _("Submit"))
+ form.add_submit("cancel", _("Cancel"))
+ if form.get_widget('cancel').parse():
+ return redirect('..')
+ if not form.is_submitted() or form.has_errors():
+ get_response().breadcrumb.append(('delete', _('Delete')))
+ html_top('users', title = _('Delete User'))
+ '<h2>%s %s</h2>' % (_('Deleting User :'), self.user.name)
+ form.render()
+ else:
+ self.user.remove_self()
+ return redirect('..')
+
+ def token [html] (self):
+ form = Form(enctype="multipart/form-data", use_tokens = False)
+ form.add_submit("submit", _("Generate"))
+ form.add_submit("cancel", _("Cancel"))
+ request = get_request()
+ if request.form.has_key('cancel') or request.form.has_key('done'):
+ return redirect('..')
+
+ get_response().breadcrumb.append(('token', _('Identification Token')))
+
+ if not form.is_submitted() or form.has_errors():
+ html_top('users', title = _('Identification Token'))
+ '<h2>%s</h2>' % _('Identification Token')
+ '<p>%s</p>' % _('You are about to generate a token than can be used to federate the account.')
+ '<p>%s</p>' % _('After that, you will have the choice to send it to the user by email so that he can federate his accounts.')
+ 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()
+ 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)
+ self.user.store()
+
+ '<p>'
+ _('Identification Token for %s') % self.user.name
+ ' : %s</p>' % self.user.identification_token
+
+ form = Form(enctype="multipart/form-data", use_tokens = False)
+ form.add_submit('done', _('Done'))
+ if self.user.email:
+ form.add_submit("submit-email", _("Send by email"))
+ form.render()
+ else:
+ site_url = '%s://%s%s/token?token=%s' \
+ % (request.get_scheme(), request.get_server(),
+ get_request().environ['SCRIPT_NAME'], self.user.identification_token)
+ body = _("""You have been given an identification token.
+
+Your token is %(token)s
+
+Click on %(url)s to use it.
+""") % {'token': self.user.identification_token, 'url': site_url}
+ try:
+ emails.email(_('Identification Token'), body, self.user.email)
+ except errors.EmailError, e:
+ html_top('users', title = _('Identification Token'))
+ _('Failed sending email. Check your email configuration.')
+ '<div class="buttons"><a href=".."><input type="button" value="%s" /></a></div><br />' % _('Back')
+ else:
+ return redirect('..')
+
+class UsersDirectory(Directory):
+
+ _q_exports = ['', 'new']
+
+ def _q_index [html] (self):
+ get_publisher().reload_cfg()
+ get_response().breadcrumb.append( ('users/', _('Users')) )
+ html_top('users', title = _('Users'))
+
+
+ if not list(Host.select(lambda x: x.name == 'larpe')):
+ '<p>%s</p>' % _('Liberty support must be setup before creating users.')
+ else:
+ """<ul id="nav-users-admin">
+ <li><a href="new">%s</a></li>
+ </ul>""" % _('New User')
+
+ debug_cfg = get_publisher().cfg.get('debug', {})
+
+ users = User.select(lambda x: x.name is not None, order_by = 'name')
+
+ '<ul class="biglist">'
+ for user in users:
+ '<li>'
+ '<strong class="label">%s</strong>' % user.name
+ if user.email:
+ '<p class="details">'
+ user.email
+ '</p>'
+
+ '<p class="commands">'
+ command_icon('%s/' % user.id, 'view')
+ if not user.name_identifiers:
+ if not user.identification_token:
+ command_icon('%s/token' % user.id, 'token',
+ label = _('Identification Token'), icon = 'stock_exec_16.png')
+ else:
+ command_icon('%s/token' % user.id, 'token',
+ label = _('Identification Token (current: %s)') % \
+ user.identification_token,
+ icon = 'stock_exec_16.png')
+ command_icon('%s/edit' % user.id, 'edit')
+ command_icon('%s/delete' % user.id, 'remove')
+ if debug_cfg.get('logger', False):
+ command_icon('../logger/by_user/%s/' % user.id, 'logs',
+ label = _('Logs'), icon = 'stock_harddisk_16.png')
+ '</p></li>'
+ '</ul>'
+
+ def new [html] (self):
+ get_response().breadcrumb.append( ('users/', _('Users')) )
+ get_response().breadcrumb.append( ('new', _('New')) )
+ hosts = list(Host.select(lambda x: x.name == 'larpe'))
+ if not hosts:
+ return error_page('users', _('Liberty support must be setup before creating users.'))
+ host = hosts[0]
+ # XXX: user must be logged in to get here
+ user_ui = UserUI(None)
+ # FIXME : should be able to use User.count(). Track fake user creations.
+ users = User.select(lambda x: x.name is not None)
+ first_user = (len(users) == 0)
+ 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()
+ else:
+ user_ui.submit_form(form)
+ if first_user:
+ session = get_session()
+ if hasattr(session, str('lasso_dump')):
+ user_ui.user.name_identifiers = [ session.name_identifier ]
+ user_ui.user.lasso_dumps = [ session.lasso_anonymous_identity_dump ]
+ user_ui.user.store()
+ if misc.get_current_protocol() == lasso.PROTOCOL_SAML_2_0:
+ get_session().set_user(user_ui.user.id, host.saml2_provider_id)
+ else:
+ get_session().set_user(user_ui.user.id, host.provider_id)
+ return redirect('.')
+
+ def _q_lookup(self, component):
+ get_response().breadcrumb.append( ('users/', _('Users')) )
+ try:
+ return UserPage(component)
+ except KeyError:
+ raise errors.TraversalError()
diff --git a/larpe/tags/release-1.0/larpe/ctl/__init__.py b/larpe/tags/release-1.0/larpe/ctl/__init__.py
new file mode 100644
index 0000000..35a5923
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/ctl/__init__.py
@@ -0,0 +1,2 @@
+from start import start
+
diff --git a/larpe/tags/release-1.0/larpe/ctl/start.py b/larpe/tags/release-1.0/larpe/ctl/start.py
new file mode 100644
index 0000000..de41324
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/ctl/start.py
@@ -0,0 +1,34 @@
+import socket
+import sys
+
+from qommon.scgi_server import run
+
+import publisher
+
+def start(args):
+ port = 3007
+ script_name = ''
+
+ i = 0
+ while i < len(args):
+ if args[i] == '--port':
+ port = int(args[i+1])
+ i += 1
+ elif args[i] == '--silent':
+ sys.stdout = open('/dev/null', 'w')
+ sys.stderr = open('/dev/null', 'w')
+ elif args[i] == '--script-name':
+ script_name = args[i+1]
+ i += 1
+ i += 1
+
+ try:
+ run(publisher.LarpePublisher.create_publisher, port=port, script_name=script_name)
+ except socket.error, err:
+ if err[0] == 98:
+ print >> sys.stderr, 'address already in use'
+ sys.exit(1)
+ raise
+ except KeyboardInterrupt:
+ sys.exit(1)
+
diff --git a/larpe/tags/release-1.0/larpe/errors.ptl b/larpe/tags/release-1.0/larpe/errors.ptl
new file mode 100644
index 0000000..dff4ee3
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/errors.ptl
@@ -0,0 +1,14 @@
+from quixote import get_session, get_request, redirect
+
+from qommon.errors import *
+
+class AccessUnauthorizedError(AccessError):
+ def render [html] (self):
+ session = get_session()
+ request = get_request()
+ query = request.get_query()
+ session.after_url = request.get_url()
+ if query:
+ session.after_url += '?' + query
+ login_url = '%s/liberty/larpe/login' % request.environ['SCRIPT_NAME']
+ redirect(login_url)
diff --git a/larpe/tags/release-1.0/larpe/federations.py b/larpe/tags/release-1.0/larpe/federations.py
new file mode 100644
index 0000000..e76328f
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/federations.py
@@ -0,0 +1,35 @@
+'''Federation object. Configuration variables and utilities'''
+
+from qommon.storage import StorableObject
+
+class Federation(StorableObject):
+ _names = 'federations'
+
+ username = None
+ password = None
+ host_id = None
+ name_identifiers = None
+ cookies = None
+ select_fields = {}
+
+ def __init__(self, username, password, host_id, name_identifier, cookies=None, select=None):
+ select = select or {}
+ StorableObject.__init__(self)
+ self.username = username
+ self.password = password
+ self.host_id = host_id
+ self.name_identifiers = [ name_identifier ]
+ self.cookies = cookies
+ self.select_fields = select
+
+ def remove_name_identifier(self, name_identifier):
+ self.name_identifiers.remove(name_identifier)
+ if not self.name_identifiers:
+ self.remove_self()
+
+ def set_cookies(self, cookies):
+ self.cookies = cookies
+
+ def __str__(self):
+ return 'Federation username : %s, name identifiers : %s, cookies : %s' \
+ % (self.username, self.name_identifiers, self.cookies)
diff --git a/larpe/tags/release-1.0/larpe/field_prefill.py b/larpe/tags/release-1.0/larpe/field_prefill.py
new file mode 100644
index 0000000..389d887
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/field_prefill.py
@@ -0,0 +1,13 @@
+from qommon.storage import StorableObject
+
+class FieldPrefill(StorableObject):
+ _names = 'field_prefill'
+
+ form_id = 0
+ name = None
+ xpath = None
+ number = 1
+ raw_xml = False
+ regexp_match = None
+ regexp_replacing = None
+ select_options = {}
diff --git a/larpe/tags/release-1.0/larpe/filter/larpe-filter.py b/larpe/tags/release-1.0/larpe/filter/larpe-filter.py
new file mode 100755
index 0000000..2d9973b
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/filter/larpe-filter.py
@@ -0,0 +1,164 @@
+import os
+import re
+
+from mod_python import apache
+
+#import larpe.hosts
+
+app_dir = '/var/lib/larpe'
+
+def outputfilter(filter):
+ # Only filter html code
+ if filter.req.content_type is not None:
+ is_html = re.search('text/html', filter.req.content_type)
+ if filter.req.content_type is not None and not is_html:
+ filter.pass_on()
+ else:
+ if not hasattr(filter.req, 'temp_doc'): # the start
+ filter.req.temp_doc = [] # create new attribute to hold document
+ # If content-length ended up wrong, Gecko browsers truncated data, so
+ if 'Content-Length' in filter.req.headers_out:
+ del filter.req.headers_out['Content-Length']
+
+# filter.write(filter.req.headers_in['Cookie'])
+# delete_cookies(filter)
+ #filter.req.headers_out['Set-Cookie'] = 'dc_admin="deleted"; max-age=0; expires=Thu, 01-Jan-1970 00:00:00 GMT; path=/'
+
+ temp_doc = filter.req.temp_doc
+ s = filter.read()
+ while s: # could get '' at any point, but only get None at end
+ temp_doc.append(s)
+ s = filter.read()
+
+ if s is None: # the end
+ page = ''.join(temp_doc)
+
+ page = filter_dispatch(filter, page)
+
+ filter.write(page)
+ filter.close()
+
+def filter_dispatch(filter, page):
+# host = get_host_from_url(filter)
+# if host is None:
+# apache.log_error('Host not found')
+# return page
+ try:
+# function_name = 'filter_' + host.label.lowercase()
+ host_name = filter.req.hostname.split('.')[-3]
+ function_name = 'filter_' + host_name
+ return eval(function_name + '(filter, page)')
+ except:
+ return page
+# return filter_default(filter, page)
+
+
+def filter_default(filter, page):
+# host = get_host_from_url(filter)
+# if host is None:
+# apache.log_error('Host not found')
+# return page
+# if host.auth_url is not None or host.auth_form is None:
+# return page
+ form = find_auth_form(page)
+ if form is not None:
+ try:
+ host_name = filter.req.hostname.split('.')[-3]
+ return page.replace(form, """
+ <form method="post" action="/liberty/%s/login">
+ <input type="submit" value="Connexion" />
+ </form>""" % host_name)
+ except:
+ pass
+ return page
+
+def find_auth_form(page):
+ regexp = re.compile("""<form.*?</form>""", re.DOTALL | re.IGNORECASE)
+ found_forms = regexp.findall(page)
+
+ for found_form in found_forms:
+ regexp = re.compile("""<input[^>]*?type="password"[^>]*?>""", re.DOTALL | re.IGNORECASE)
+ if regexp.search(found_form) is not None:
+ return found_form
+ return None
+
+#def get_host_from_url(filter):
+# try:
+# return list(Host.select(lambda x: x.reversed_hostname == filter.req.hostname \
+# and x.reversed_directory == get_proxied_site_name(filter)))[0]
+# except:
+# return None
+
+
+def filter_linuxfr(filter, page):
+ str_to_replace = re.compile(str('<form method="post" action="/login.html" id="formulaire">.*?</form>'), re.DOTALL)
+ return str_to_replace.sub(str(r"""<form method="post" action="/liberty/linuxfr/login" id="formulaire">
+ <div style="text-align: center; font-size: 13px;" class="loginbox">
+ <input type="submit" value="Connexion" />
+ <br />
+ <a href="/user_new.html">Cr&eacute;er un compte</a>
+ </div>
+ </form>"""
+ ), page)
+
+def filter_dotclear(filter, page):
+ if filter.req.uri == '/dot/ecrire/redac_list.php':
+ str_to_replace = re.compile(str('(\[[^\?]+\?id=)([^"]+)(">[^\]]*)\]'), re.DOTALL)
+ return str_to_replace.sub(str(r'\1\2\3 - <a href="/liberty/dot/admin_token?id=\2">token</a> ]'), page)
+ if filter.req.uri == '/dot/ecrire/redacteur.php':
+ str_to_replace = re.compile(str('(<form action=")redacteur.php'))
+ page = str_to_replace.sub(str(r'\1/liberty/dot/admin_new_user'), page)
+ str_to_replace = re.compile(str('<p class="field"><label class="float" for="user_pwd">.*?</p>'), re.DOTALL)
+ return str_to_replace.sub(r'', page)
+ return page
+
+def filter_concerto(filter, page):
+ str_to_replace = re.compile(str('<form action="login.do" method="post">.*?</form>'), re.DOTALL)
+ return str_to_replace.sub(str(r"""<form method="post" action="/liberty/concerto/login" id="formulaire">
+ <div style="text-align: center; font-size: 13px;" class="loginbox">
+ <input type="submit" value="Connexion" />
+ </div>
+ </form>"""
+ ), page)
+
+def get_abs_path(s):
+ if not s:
+ return s
+ if s[0] == '/':
+ return s
+ return os.path.join(app_dir, s)
+
+def get_proxied_site_path(filter):
+ proxy_domain_name = filter.req.hostname
+ proxied_site_dir = get_proxied_site_name(filter)
+ return get_abs_path(os.path.join('sp', proxy_domain_name, proxied_site_dir))
+
+def get_proxied_site_name(filter):
+ uri_tokens = filter.req.uri.split('/')
+ if uri_tokens[1] != 'liberty':
+ return uri_tokens[1]
+ return uri_tokens[2]
+
+def delete_cookies(filter):
+ success = False
+ cookies_file_name = get_abs_path(os.path.join(get_proxied_site_path(filter), 'cookies_to_delete'))
+ cookies_file = open(cookies_file_name, 'r')
+ for cookie in cookies_file.read().split():
+ if filter.req.headers_in.has_key('Cookie'):
+ cookies_header = filter.req.headers_in['Cookie']
+# filter.req.temp_doc.append(filter.req.headers_in['Cookie'])
+ #filter.req.temp_doc.append(cookie[len(cookie) -1:])
+ cookies_match = re.findall(cookie[:len(cookie) -1], cookies_header)
+ if len(cookies_match) > 0:
+ filter.req.temp_doc.append('User-Agent')
+# filter.req.temp_doc.append('%s="deleted"; max-age=0; expires=Thu, 01-Jan-1970 00:00:00 GMT; path=/' % cookie.split('=')[0])
+ filter.req.headers_out['Set-Cookie'] = '%s="deleted"; max-age=0; expires=Thu, 01-Jan-1970 00:00:00 GMT; path=/' % cookie.split('=')[0]
+# cookies_file.close()
+# cookies_file = open(cookies_file_name, 'w')
+ break
+# else:
+# filter.req.temp_doc.append('dommage')
+ cookies_file.close()
+# if success:
+# cookies_file = open(cookies_file_name, 'w')
+# cookies_file.close()
diff --git a/larpe/tags/release-1.0/larpe/form_prefill.py b/larpe/tags/release-1.0/larpe/form_prefill.py
new file mode 100644
index 0000000..2c4727a
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/form_prefill.py
@@ -0,0 +1,10 @@
+from qommon.storage import StorableObject
+
+class FormPrefill(StorableObject):
+ _names = 'form_prefill'
+
+ host_id = 0
+ name = None
+ url = None
+ profile = None
+ prefix = None
diff --git a/larpe/tags/release-1.0/larpe/hosts.py b/larpe/tags/release-1.0/larpe/hosts.py
new file mode 100644
index 0000000..9b48ebb
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/hosts.py
@@ -0,0 +1,136 @@
+'''Host object. Configuration variables and utilities'''
+
+import os
+from shutil import rmtree
+
+from quixote import get_request
+
+from qommon.storage import StorableObject
+
+from Defaults import APP_DIR
+
+def get_proxied_site_name():
+ nb_subdirs = get_request().environ['SCRIPT_NAME'].count('/')
+ return get_request().get_path().split('/')[nb_subdirs + 2]
+
+class Host(StorableObject):
+ '''Host object. Configuration variables and utilities'''
+ _names = 'hosts'
+
+ # Main settings
+ label = None
+ name = None
+ orig_site = None
+ new_url = None
+ scheme = None
+ auth_url = None
+ auth_form_places = 'form_once'
+ auth_form_page_url = None
+ auth_form = None
+ auth_form_url = None
+ logout_url = None
+ reversed_hostname = None
+ reversed_directory = None
+ organization_name = None
+ use_ssl = False
+ private_key = None
+ public_key = None
+ site_dir = None
+
+ # Auto detected settings
+ auth_mode = 'form'
+ auth_form_action = None
+ auth_check_url = None
+ login_field_name = None
+ password_field_name = None
+ select_fields = {}
+ post_parameters = {}
+ http_headers = {}
+
+ # Advanced settings
+ return_url = '/'
+ root_url = '/'
+ auth_system = 'password'
+ auth_match_text = ''
+ send_hidden_fields = True
+ initiate_sso_url = None
+ redirect_root_to_login = False
+
+ # Other attributes
+ provider_id = None
+ # Default value that indicates the proxy (if configured) is not disabled for this host yet
+ use_proxy = True
+
+ valid_username = None
+ valid_password = None
+ apache_output_filters = []
+ apache_output_python_filters = []
+ apache_python_paths = []
+
+ # Plugins
+ # If name is set to None, use the default site authentication class
+ site_authentication_plugin = None
+
+ def get_host_from_url(cls):
+ try:
+ host = list(Host.select(lambda x: x.name == get_proxied_site_name()))[0]
+ if hasattr(host, 'site_authentication_instance'):
+ del host.site_authentication_instance
+ return list(Host.select(lambda x: x.name == get_proxied_site_name()))[0]
+ except IndexError:
+ return None
+ get_host_from_url = classmethod(get_host_from_url)
+
+ def get_host_with_provider_id(cls, provider_id):
+ try:
+ return list(Host.select(lambda x: x.provider_id == provider_id))[0]
+ except IndexError:
+ return None
+ get_host_with_provider_id = classmethod(get_host_with_provider_id)
+
+ def get_root_url(self):
+ if self.root_url.startswith('/'):
+ if self.reversed_directory:
+ return '%s/%s%s' % (get_request().environ['SCRIPT_NAME'],
+ self.reversed_directory,
+ self.root_url)
+ else:
+ return '%s%s' % (get_request().environ['SCRIPT_NAME'], self.root_url)
+ # In this case, must be a full url
+ return self.root_url
+
+ def get_return_url(self):
+ if self.return_url.startswith('/'):
+ if self.reversed_directory:
+ return '%s/%s%s' % (get_request().environ['SCRIPT_NAME'],
+ self.reversed_directory,
+ self.return_url)
+ else:
+ return '%s%s' % (get_request().environ['SCRIPT_NAME'], self.return_url)
+ # In this case, must be a full url
+ return self.return_url
+
+ def __cmp__(self, other):
+ hostname_cmp = cmp(self.reversed_hostname, other.reversed_hostname)
+ if hostname_cmp != 0:
+ return hostname_cmp
+ return cmp(self.reversed_directory, other.reversed_directory)
+
+ def remove_self(self):
+ # Main configuration file
+ StorableObject.remove_self(self)
+ # Other generated files
+ if self.site_dir and os.path.exists(self.site_dir):
+ rmtree(self.site_dir, ignore_errors=1)
+ # Also remove hostname directory if empty (meaning there was no other subdirectory
+ # for this hostname)
+ try:
+ os.rmdir('/'.join(self.site_dir.split('/')[:-1]))
+ except OSError:
+ pass
+ # Virtual host directory
+ if self.reversed_hostname:
+ path = os.path.join(APP_DIR, self.reversed_hostname)
+ if os.path.exists(path):
+ rmtree(path, ignore_errors=1)
+
diff --git a/larpe/tags/release-1.0/larpe/idwsf2.ptl b/larpe/tags/release-1.0/larpe/idwsf2.ptl
new file mode 100644
index 0000000..fbf807f
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/idwsf2.ptl
@@ -0,0 +1,199 @@
+import os
+import sys
+import re
+
+try:
+ import lasso
+except ImportError:
+ print >> sys.stderr, 'Missing Lasso module, IdWsf 2.0 support disabled'
+else:
+ if not lasso.WSF_SUPPORT:
+ print >> sys.stderr, 'Found Lasso module, but IdWsf 2.0 support not enabled'
+
+from quixote import get_publisher, get_session, get_request, get_response, redirect
+from quixote.directory import Directory
+
+from qommon.liberty import SOAPException, soap_call
+from qommon import template
+from qommon.misc import http_get_page
+
+import misc
+from form_prefill import FormPrefill
+from field_prefill import FieldPrefill
+
+def cleanup_html_value(value):
+ # Ensure the field value can be properly integrated in HTML code
+ value = value.replace('"', "'")
+ # Conversion to iso-8859-1
+ try:
+ value = unicode(value, 'utf-8').encode('iso-8859-1')
+ except UnicodeEncodeError:
+ return None
+
+class IdWsf2(Directory):
+ _q_exports = []
+
+ def _q_lookup(self, component):
+ if not hasattr(get_session(), 'prefill_form'):
+ get_session().prefill_form = component
+ get_session().after_url = get_request().get_url()
+ if get_request().get_query():
+ get_session().after_url += '?' + get_request().get_query()
+ return redirect('../saml/login')
+ else:
+ prefill_form = FormPrefill.get(get_session().prefill_form)
+ del get_session().prefill_form
+ if prefill_form:
+ try:
+ response, status, page, auth_header = http_get_page(prefill_form.url)
+ except:
+ return template.error_page(_('Failed connecting to the original site.'))
+ try:
+ fields = self.do_prefill_form(prefill_form)
+ if not fields:
+ raise lasso.Error
+ for key, value in get_request().get_fields().iteritems():
+ value = cleanup_html_value(value)
+ if value:
+ fields[key] = value
+ except lasso.Error:
+ return page + '<script type="text/javascript">alert("%s")</script>' % \
+ _('Failed getting attributes from the attribute provider.')
+ except:
+ return page + '<script type="text/javascript">alert("%s")</script>' % \
+ _('Failed getting attributes for an unknown reason.')
+
+ return self.send_prefilled_form(prefill_form, page, fields)
+
+ def do_prefill_form(self, prefill_form):
+ server = misc.get_lasso_server(protocol = 'saml2')
+ disco = lasso.IdWsf2Discovery(server)
+ if not get_session().lasso_session_dumps or not get_session().lasso_session_dumps[server.providerId]:
+ return None
+ disco.setSessionFromDump(get_session().lasso_session_dumps[server.providerId])
+
+ disco.initQuery()
+ disco.addRequestedServiceType(prefill_form.profile)
+ disco.buildRequestMsg()
+
+ try:
+ soap_answer = soap_call(disco.msgUrl, disco.msgBody)
+ except SOAPException:
+ return None
+ disco.processQueryResponseMsg(soap_answer)
+
+ service = disco.getService()
+ lasso.registerIdWsf2DstService(prefill_form.prefix, prefill_form.profile)
+
+ service.initQuery()
+
+ fields = FieldPrefill.select(lambda x: x.form_id == prefill_form.id)
+ for field in fields:
+ if field.xpath and field.name:
+ service.addQueryItem(field.xpath, field.name)
+
+ service.buildRequestMsg()
+
+ try:
+ soap_answer = soap_call(service.msgUrl, service.msgBody)
+ except SOAPException:
+ return None
+ service.processQueryResponseMsg(soap_answer)
+
+ fields_dict = {}
+ for field in fields:
+ if not field.xpath or not field.name:
+ continue
+ if field.number > 0:
+ number = field.number -1
+ try:
+ if field.raw_xml:
+ value = service.getAttributeNodes(field.name)[number]
+ else:
+ value = service.getAttributeStrings(field.name)[number]
+ except (IndexError, TypeError):
+ value = ''
+ # Log
+ if value:
+ # Regexp transformation
+ if field.regexp_match:
+ value = re.sub(field.regexp_match, field.regexp_replacing, value)
+ value = cleanup_html_value(value)
+ # Conversion of select field options
+ if field.select_options:
+ try:
+ value = field.select_options[value]
+ except (IndexError, KeyError):
+ pass
+ if not value:
+ continue
+ fields_dict[field.name] = value
+
+ return fields_dict
+
+ def send_prefilled_form(self, prefill_form, page, fields):
+ for field_name, new_value in fields.iteritems():
+ # Input
+ regex = re.compile('(.*)(<input[^>]*? id="%s".*?>)(.*)' % field_name,
+ re.DOTALL | re.IGNORECASE)
+ match = regex.match(page)
+ if not match:
+ regex = re.compile('(.*)(<input[^>]*? name="%s".*?>)(.*)' % field_name,
+ re.DOTALL | re.IGNORECASE)
+ match = regex.match(page)
+ if match:
+ before, input_field, after = match.groups()
+ if 'value="' in input_field.lower():
+ regex_sub = re.compile('value=".*?"', re.DOTALL | re.IGNORECASE)
+ input_field = regex_sub.sub('value="%s"' % new_value, input_field)
+ else:
+ input_field = input_field.replace('<input', '<input value="%s"' % new_value)
+ page = ''.join([before, input_field, after])
+ continue
+
+ # Textarea
+ regex = re.compile('(.*<textarea[^>]*? id="%s".*?>)[^<]*(</textarea>.*)' % field_name,
+ re.DOTALL | re.IGNORECASE)
+ match = regex.match(page)
+ if not match:
+ regex = re.compile('(.*<textarea[^>]*? name="%s".*?>)[^<]*(</textarea>.*)' % field_name,
+ re.DOTALL | re.IGNORECASE)
+ match = regex.match(page)
+ if match:
+ before, after = match.groups()
+ page = ''.join([before, new_value, after])
+ continue
+
+ # Select
+ regex = re.compile('(.*<select[^>]*? id="%s".*?>)(.*?)(</select>.*)' % field_name,
+ re.DOTALL | re.IGNORECASE)
+ match = regex.match(page)
+ if not match:
+ regex = re.compile('(.*<select[^>]*? name="%s".*?>)(.*?)(</select>.*)' % field_name,
+ re.DOTALL | re.IGNORECASE)
+ match = regex.match(page)
+ if match:
+ before, options, after = match.groups()
+ # If the option to select is found, first unselect the previoulsy selected one
+ regex2 = re.compile('(.*<option[^>]*? value="%s".*?)(>[^<]*</option>.*)' % new_value,
+ re.DOTALL | re.IGNORECASE)
+ match2 = regex2.match(options)
+ if match2:
+ before2, after2 = match2.groups()
+ regex3 = re.compile('(.*<option[^>]*?)( selected(="selected")?)(.*?>[^<]*</option>.*)',
+ re.DOTALL | re.IGNORECASE)
+ match3 = regex3.match(options)
+ if match3:
+ before3, selected, selected_value, after3 = match3.groups()
+ options = ''.join([before3, after3])
+ regex2 = re.compile('(.*<option[^>]*? value="%s".*?)(>[^<]*</option>.*)' % new_value,
+ re.DOTALL | re.IGNORECASE)
+ match2 = regex2.match(options)
+ if match2:
+ before2, after2 = match2.groups()
+ options = ''.join([before2, ' selected="selected"', after2])
+
+ page = ''.join([before, options, after])
+
+ return page
+
diff --git a/larpe/tags/release-1.0/larpe/liberty.ptl b/larpe/tags/release-1.0/larpe/liberty.ptl
new file mode 100644
index 0000000..193dd19
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/liberty.ptl
@@ -0,0 +1,372 @@
+import libxml2
+import urllib
+import urlparse
+import httplib
+import re
+import os
+
+from quixote import get_field, get_request, get_response, get_session, get_session_manager, redirect
+from quixote.directory import Directory
+from quixote.http_request import parse_header
+
+import lasso
+
+from qommon import get_logger
+from qommon.form import *
+from qommon.template import *
+from qommon.liberty import soap_call, SOAPException
+
+import misc
+from users import User
+from hosts import Host
+from federations import Federation
+import site_authentication
+
+class Liberty(Directory):
+ _q_exports = ['', 'login', 'assertionConsumer', 'soapEndpoint',
+ 'singleLogout', 'singleLogoutReturn',
+ 'federationTermination', 'federationTerminationReturn',
+ ('metadata.xml', 'metadata'), 'public_key', 'local_auth']
+
+ def perform_login(self, idp = None):
+ server = misc.get_lasso_server()
+ login = lasso.Login(server)
+ login.initAuthnRequest(idp, 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 = misc.get_lasso_server()
+ if not server:
+ return error_page(_('Liberty support is not yet configured'))
+ 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()
+ try:
+ soap_answer = soap_call(login.msgUrl, login.msgBody)
+ except SOAPException:
+ return error_page(_('Failure to communicate with identity provider'))
+ try:
+ login.processResponseMsg(soap_answer)
+ except lasso.Error, error:
+ if error[0] == lasso.LOGIN_ERROR_STATUS_NOT_SUCCESS:
+ return error_page(_('Unknown authentication failure'))
+ if hasattr(lasso, 'LOGIN_ERROR_UNKNOWN_PRINCIPAL'):
+ if error[0] == lasso.LOGIN_ERROR_UNKNOWN_PRINCIPAL:
+ return error_page(_('Authentication failure; unknown principal'))
+ return error_page(_("Identity Provider didn't accept artifact transaction."))
+ else:
+ login.processAuthnResponseMsg(get_field('LARES'))
+ login.acceptSso()
+ session = get_session()
+ if login.isSessionDirty:
+ if login.session:
+ session.lasso_session_dumps[server.providerId] = login.session.dump()
+ else:
+ session.lasso_session_dumps[server.providerId] = None
+
+ # Look for an existing user
+ user = self.lookup_user(session, login)
+
+ # Check if it is for Larpe administration or token
+ host = Host.get_host_from_url()
+ if host is None:
+ return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
+ if host.name == 'larpe':
+ if user:
+ session.set_user(user.id, server.providerId)
+ else:
+ session.name_identifier = login.nameIdentifier.content
+ session.lasso_anonymous_identity_dump = login.identity.dump()
+ session.provider_id = server.providerId
+
+ if session.after_url:
+ # Access to an admin page or token url with parameter
+ after_url = session.after_url
+ session.after_url = None
+ return redirect(after_url)
+
+ if user and user.is_admin:
+ return redirect('%s/admin/' % get_request().environ['SCRIPT_NAME'])
+ else:
+ return redirect('%s/token' % get_request().environ['SCRIPT_NAME'])
+
+ # Set session user
+ if not user:
+ user = User()
+ user.name_identifiers = [ login.nameIdentifier.content ]
+ user.lasso_dumps = [ login.identity.dump() ]
+ user.store()
+ session.set_user(user.id, server.providerId)
+
+ federations = Federation.select(lambda x: host.id == x.host_id \
+ and user.name_identifiers[0] in x.name_identifiers)
+
+ if federations:
+ return site_authentication.get_site_authentication(host).sso_local_login(federations[0])
+ else:
+ response = get_response()
+ if session.after_url:
+ after_url = session.after_url
+ session.after_url = None
+ return redirect(after_url)
+ response.set_status(303)
+ response.headers['location'] = urlparse.urljoin(request.get_url(), str('local_auth'))
+ response.content_type = 'text/plain'
+ return 'Your browser should redirect you'
+
+ def lookup_user(self, session, login):
+ found_users = list(User.select(lambda x: login.nameIdentifier.content in x.name_identifiers))
+ if found_users:
+ return found_users[0]
+ return None
+
+ def singleLogout(self):
+ request = get_request()
+ logout = lasso.Logout(misc.get_lasso_server())
+ if lasso.isLibertyQuery(request.get_query()):
+ try:
+ logout.processRequestMsg(request.get_query())
+ except lasso.Error, error:
+ if error[0] == lasso.DS_ERROR_INVALID_SIGNATURE:
+ return error_page(_('Failed to check single logout request signature.'))
+ raise
+ session = get_session()
+ if not session.id:
+ # session has not been found, this may be because the user has
+ # its browser configured so that cookies are not sent for
+ # remote queries and IdP is using image-based SLO.
+ # so we look up a session with the appropriate name identifier
+ for session in get_session_manager().values():
+ # This block differs from qommon
+ user = session.get_user(logout.server.providerId)
+ if user and logout.nameIdentifier.content in user.name_identifiers:
+ break
+ else:
+ session = get_session()
+ return self.slo_idp(logout, session)
+ else:
+ return self.slo_sp(logout, get_session())
+
+ def singleLogoutReturn(self):
+ logout = lasso.Logout(misc.get_lasso_server())
+ host = Host.get_host_from_url()
+ if host is None:
+ return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
+
+ try:
+ logout.processResponseMsg(get_request().get_query())
+ except lasso.Error, error:
+ if error[0] == lasso.PROFILE_ERROR_INVALID_QUERY:
+ raise AccessError()
+ if error[0] == lasso.DS_ERROR_INVALID_SIGNATURE:
+ return error_page(_('Failed to check single logout request signature.'))
+ if hasattr(lasso, 'LOGOUT_ERROR_REQUEST_DENIED') and \
+ error[0] == lasso.LOGOUT_ERROR_REQUEST_DENIED:
+ return redirect(host.get_root_url()) # ignore silently
+ elif error[0] == lasso.ERROR_UNDEFINED:
+ # XXX: unknown status; ignoring for now.
+ return redirect(host.get_root_url()) # ignore silently
+ raise
+ return redirect(host.get_root_url())
+
+ def slo_idp(self, logout, session):
+ '''Single Logout initiated by IdP'''
+ # This block differs from qommon
+ if session.lasso_session_dumps.has_key(logout.server.providerId):
+ logout.setSessionFromDump(session.lasso_session_dumps[logout.server.providerId])
+ user = session.get_user(logout.server.providerId)
+ if user and user.lasso_dumps:
+ logout.setIdentityFromDump(user.lasso_dumps[0])
+ if user and logout.nameIdentifier.content not in user.name_identifiers:
+ raise 'No appropriate name identifier in user (%s and %s)' % (
+ logout.nameIdentifier.content, user.name_identifiers)
+
+ host = Host.get_host_with_provider_id(logout.server.providerId)
+ if host is not None:
+ site_authentication.get_site_authentication(host).local_logout(user=user)
+
+ try:
+ logout.validateRequest()
+ except lasso.Error, error:
+ if error[0] != lasso.PROFILE_ERROR_SESSION_NOT_FOUND:
+ raise
+ else:
+ get_session_manager().expire_session(logout.server.providerId)
+
+ logout.buildResponseMsg()
+ if logout.msgBody: # soap answer
+ return logout.msgBody
+ else:
+ return redirect(logout.msgUrl)
+
+ def slo_sp(self, logout, session):
+ host = Host.get_host_from_url()
+ if host is None:
+ return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
+
+ if not session.id or not session.users.has_key(logout.server.providerId) \
+ or not session.lasso_session_dumps.has_key(logout.server.providerId):
+ get_session_manager().expire_session(logout.server.providerId)
+ return redirect(host.get_root_url())
+
+ logout.setSessionFromDump(session.lasso_session_dumps[logout.server.providerId])
+ user = session.get_user(logout.server.providerId)
+
+ if host.name != 'larpe' and user:
+ site_authentication.get_site_authentication(host).local_logout(user=user)
+
+ if user and user.lasso_dumps:
+ logout.setIdentityFromDump(user.lasso_dumps[0])
+ else:
+ get_session_manager().expire_session(logout.server.providerId)
+ return redirect(host.get_root_url())
+
+ return self.slo_sp_redirect(logout, host)
+
+ def slo_sp_redirect(self, logout, host):
+ try:
+ logout.initRequest(None, lasso.HTTP_METHOD_REDIRECT)
+ except lasso.Error, error:
+ if error[0] == lasso.PROFILE_ERROR_NAME_IDENTIFIER_NOT_FOUND:
+ get_session_manager().expire_session()
+ return redirect(host.get_root_url())
+ raise
+ logout.buildRequestMsg()
+ get_session_manager().expire_session(logout.server.providerId)
+ return redirect(logout.msgUrl)
+
+ def soapEndpoint(self):
+ request = get_request()
+ ctype = request.environ.get('CONTENT_TYPE')
+ if not ctype:
+ return
+
+ ctype, ctype_params = parse_header(ctype)
+ if ctype != 'text/xml':
+ return
+
+ response = get_response()
+ response.set_content_type('text/xml')
+
+ length = int(request.environ.get('CONTENT_LENGTH'))
+ soap_message = request.stdin.read(length)
+
+ request_type = lasso.getRequestTypeFromSoapMsg(soap_message)
+
+ if request_type == lasso.REQUEST_TYPE_LOGOUT:
+ logout = lasso.Logout(misc.get_lasso_server())
+ logout.processRequestMsg(soap_message)
+ name_identifier = logout.nameIdentifier.content
+ for session in get_session_manager().values():
+ user = session.get_user(logout.server.providerId)
+ if user and logout.nameIdentifier.content in user.name_identifiers:
+ break
+ else:
+ session = None
+ return self.slo_idp(logout, session)
+
+ if request_type == lasso.REQUEST_TYPE_DEFEDERATION:
+ defederation = lasso.Defederation(misc.get_lasso_server())
+ defederation.processNotificationMsg(soap_message)
+ for session in get_session_manager().values():
+ user = session.get_user(defederation.server.providerId)
+ if user and defederation.nameIdentifier.content in user.name_identifiers:
+ break
+ else:
+ session = None
+ return self.fedterm(defederation, session)
+
+ def federationTermination(self):
+ request = get_request()
+ if not lasso.isLibertyQuery(request.get_query()):
+ return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
+
+ defederation = lasso.Defederation(misc.get_lasso_server())
+ defederation.processNotificationMsg(request.get_query())
+ return self.fedterm(defederation, get_session())
+
+ def fedterm(self, defederation, session):
+ if session is not None:
+ host = Host.get_host_with_provider_id(defederation.server.providerId)
+ if host is not None:
+ site_authentication.get_site_authentication(host).local_defederate(session, defederation.server.providerId)
+ if session.lasso_session_dumps.has_key(defederation.server.providerId):
+ defederation.setSessionFromDump(session.lasso_session_dumps[defederation.server.providerId])
+ user = session.get_user(defederation.server.providerId)
+ if user and user.lasso_dumps:
+ defederation.setIdentityFromDump(user.lasso_dumps[0])
+ else:
+ user = None
+
+ try:
+ defederation.validateNotification()
+ except lasso.Error, error:
+ pass # ignore failure (?)
+ else:
+ if user:
+ if not defederation.identity:
+ # if it was the last federation the whole identity dump collapsed
+ del user.lasso_dumps[0]
+ else:
+ user.lasso_dumps[0] = defederation.identity.dump()
+ user.store()
+
+ if user and defederation.nameIdentifier.content:
+ user.remove_name_identifier(defederation.nameIdentifier.content)
+ user.store()
+
+ if defederation.isSessionDirty and session is not None:
+ if not defederation.session:
+ del session.lasso_session_dumps[defederation.server.providerId]
+ else:
+ session.lasso_session_dumps[defederation.server.providerId] = defederation.session.dump()
+ session.store()
+
+ get_session_manager().expire_session(defederation.server.providerId)
+
+ if defederation.msgUrl:
+ return redirect(defederation.msgUrl)
+ else:
+ response = get_response()
+ response.set_status(204)
+ return ''
+
+ def federationTerminationReturn(self):
+ host = Host.get_host_from_url()
+ if host is None:
+ return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
+ return redirect(host.get_return_url())
+
+ def local_auth(self):
+ host = Host.get_host_from_url()
+ if host is None:
+ return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
+ return site_authentication.get_site_authentication(host).local_auth
+ local_auth = property(local_auth)
+
+ def metadata(self):
+ host = Host.get_host_from_url()
+ if host is None:
+ return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
+ get_response().set_content_type('text/xml', 'utf-8')
+ metadata = unicode(open(host.metadata).read(), 'utf-8')
+ return metadata
+
+ def public_key(self):
+ host = Host.get_host_from_url()
+ if host is None:
+ return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
+ get_response().set_content_type('text/plain')
+ public_key = open(host.public_key).read()
+ return public_key
diff --git a/larpe/tags/release-1.0/larpe/liberty_root.ptl b/larpe/tags/release-1.0/larpe/liberty_root.ptl
new file mode 100644
index 0000000..f15c953
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/liberty_root.ptl
@@ -0,0 +1,9 @@
+from quixote.directory import Directory
+from quixote import get_response
+
+from liberty_site import LibertySite
+
+class LibertyRootDirectory(Directory):
+
+ def _q_lookup(self, component):
+ return LibertySite(component)
diff --git a/larpe/tags/release-1.0/larpe/liberty_site.ptl b/larpe/tags/release-1.0/larpe/liberty_site.ptl
new file mode 100644
index 0000000..b247a4c
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/liberty_site.ptl
@@ -0,0 +1,68 @@
+import sys
+import random
+
+from quixote import get_publisher, get_response, redirect, get_request
+from quixote.directory import Directory
+from quixote.errors import TraversalError
+
+import lasso
+
+import admin
+import liberty
+import saml2
+import idwsf2
+import httplib
+import urllib
+
+from qommon.form import *
+from qommon.misc import get_abs_path, get_current_protocol
+from qommon import template, get_logger
+
+import errors
+import misc
+
+from users import User
+from hosts import Host
+
+class LibertySite(Directory):
+
+ _q_exports = ['', 'login', 'logout', 'liberty', 'saml', 'idwsf2']
+
+ liberty = liberty.Liberty()
+ saml = saml2.Saml2()
+ idwsf2 = idwsf2.IdWsf2()
+
+ def __init__(self, component):
+ self.name = component
+
+ def _q_index (self):
+ raise errors.TraversalError()
+
+ def login [html] (self):
+ get_logger().info('login')
+ get_publisher().reload_cfg()
+
+ if not get_publisher().cfg.has_key('idp'):
+ return template.error_page(_('SSO support is not yet configured'))
+ else:
+ server = misc.get_lasso_server('liberty')
+ if server is not None:
+ return self.liberty.perform_login()
+
+ server = misc.get_lasso_server('saml2')
+ if server is not None:
+ return self.saml.perform_login()
+
+ return template.error_page(_('SSO support is not yet configured'))
+
+ def logout(self):
+ get_logger().info('logout')
+ session = get_session()
+ if not session:
+ return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
+
+ if misc.get_current_protocol() == lasso.PROTOCOL_SAML_2_0:
+ return self.saml.slo_sp()
+ else:
+ return self.liberty.singleLogout()
+
diff --git a/larpe/tags/release-1.0/larpe/logger.py b/larpe/tags/release-1.0/larpe/logger.py
new file mode 100644
index 0000000..3849613
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/logger.py
@@ -0,0 +1,34 @@
+import logging
+
+from quixote import get_request, get_session
+
+from qommon.logger import BotFilter
+
+from hosts import Host
+
+class Formatter(logging.Formatter):
+ def format(self, record):
+ request = get_request()
+
+ record.address = request.get_environ('REMOTE_ADDR', '-')
+ record.path = request.get_path()
+ record.session_id = get_session().get_session_id() or '[nosession]'
+
+ user = None
+ host = Host.get_host_from_url()
+ if not host:
+ host = Host.select(lambda x: x.name == 'larpe')[0]
+ if host:
+ user = get_session().get_user(host.provider_id)
+ if not user:
+ user = get_session().get_user(host.saml2_provider_id)
+
+ if user:
+ user_id = user.id
+ else:
+ user_id = 'unlogged'
+ if BotFilter.is_bot():
+ user_id = 'bot'
+ record.user_id = user_id
+
+ return logging.Formatter.format(self, record)
diff --git a/larpe/tags/release-1.0/larpe/misc.py b/larpe/tags/release-1.0/larpe/misc.py
new file mode 100644
index 0000000..15492a5
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/misc.py
@@ -0,0 +1,105 @@
+import re
+import os
+
+import lasso
+
+from quixote import get_publisher, get_request
+
+from qommon.misc import get_abs_path
+
+from hosts import Host
+
+def get_root_url():
+ req = get_request()
+ return '%s://%s%s' % (req.get_scheme(), req.get_server(), req.environ['SCRIPT_NAME'])
+
+def get_proxied_site_path():
+ host = Host.get_host_from_url()
+ if host is None:
+ return None
+ return host.site_dir
+
+def get_proxied_site_domain():
+ return get_request().get_server().split(':')[0]
+
+def get_identity_provider_config():
+ get_publisher().reload_cfg()
+ idps_dir = get_abs_path('idp')
+ if get_publisher().cfg.has_key('idp'):
+ idp_dir = os.path.join(idps_dir, get_publisher().cfg['idp'])
+
+ metadata_path = os.path.join(idp_dir, 'metadata.xml')
+
+ public_key_path = os.path.join(idp_dir, 'public_key')
+ if not os.path.isfile(public_key_path):
+ public_key_path = None
+
+ ca_cert_chain_path = os.path.join(idp_dir, 'ca_cert_chain.pem')
+ if not os.path.isfile(ca_cert_chain_path):
+ ca_cert_chain_path = None
+
+ return metadata_path, public_key_path, ca_cert_chain_path
+ return None, None, None
+
+def get_lasso_server(protocol='liberty'):
+ proxied_site_path = get_proxied_site_path()
+ if proxied_site_path is None:
+ return None
+ if protocol == 'liberty':
+ server = lasso.Server(
+ os.path.join(proxied_site_path, 'metadata.xml'),
+ os.path.join(proxied_site_path, 'private_key.pem'),
+ None, None)
+ elif protocol == 'saml2':
+ server = lasso.Server(
+ os.path.join(proxied_site_path, 'saml2_metadata.xml'),
+ os.path.join(proxied_site_path, 'private_key.pem'),
+ None, None)
+ else:
+ raise 'Unknown protocol'
+
+ metadata_path, public_key_path, ca_cert_chain_path = get_identity_provider_config()
+ if metadata_path:
+ try:
+ server.addProvider(
+ lasso.PROVIDER_ROLE_IDP,
+ metadata_path,
+ public_key_path,
+ ca_cert_chain_path)
+ except lasso.Error, error:
+ if error[0] == lasso.SERVER_ERROR_ADD_PROVIDER_PROTOCOL_MISMATCH:
+ return None
+ if error[0] == lasso.SERVER_ERROR_ADD_PROVIDER_FAILED:
+ return None
+ raise
+
+ return server
+
+def get_provider_label(provider):
+ if not provider:
+ return None
+ 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]
+
+def get_current_protocol():
+ metadata_path, public_key_path, ca_cert_chain_path = get_identity_provider_config()
+ if not metadata_path:
+ return None
+ try:
+ provider = lasso.Provider(lasso.PROVIDER_ROLE_IDP, metadata_path, public_key_path, None)
+ except lasso.Error:
+ return None
+ else:
+ return provider.getProtocolConformance()
+
diff --git a/larpe/tags/release-1.0/larpe/plugins/__init__.py b/larpe/tags/release-1.0/larpe/plugins/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/plugins/__init__.py
diff --git a/larpe/tags/release-1.0/larpe/plugins/site_authentication/__init__.py b/larpe/tags/release-1.0/larpe/plugins/site_authentication/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/plugins/site_authentication/__init__.py
diff --git a/larpe/tags/release-1.0/larpe/plugins/site_authentication/agirhe.py b/larpe/tags/release-1.0/larpe/plugins/site_authentication/agirhe.py
new file mode 100644
index 0000000..e131e95
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/plugins/site_authentication/agirhe.py
@@ -0,0 +1,142 @@
+import re
+import urllib
+
+from quixote import get_request, get_response, get_session
+
+from qommon.misc import http_post_request
+from qommon.errors import ConnectionError
+from qommon import get_logger
+
+from larpe.qommon.misc import http_get_page
+import site_authentication
+
+class AgirheSiteAuthentication(site_authentication.SiteAuthentication):
+ plugin_name = 'agirhe'
+
+ def auto_detect_site(cls, html_doc):
+ if re.search(
+ """<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/""",
+ html_doc):
+ return True
+ return False
+ auto_detect_site = classmethod(auto_detect_site)
+
+ def local_auth_check_post(self, username, password, select=None):
+ select = select or {}
+ url = self.host.auth_check_url
+
+ # Build request body
+ body = '%s=%s&%s=%s' % (
+ self.host.login_field_name, username, self.host.password_field_name, password)
+ # Add select fields to the body
+ for name, value in select.iteritems():
+ body += '&%s=%s' % (name, value)
+
+ # Get the authentication page
+ try:
+ response, status, page, auth_headers = http_get_page(
+ self.host.auth_form_url, use_proxy=self.host.use_proxy)
+ except ConnectionError, err:
+ get_logger().warn(err)
+ return None, None
+
+ # Get current hidden fields everytime
+ self.parse_forms(page)
+ if self.host.auth_form is not None:
+ self.parse_other_fields()
+
+ # Add hidden fields to the body
+ for key, value in self.host.other_fields.iteritems():
+ value = urllib.quote_plus(value)
+ body += '&%s=%s' % (key, value)
+
+ # Build request HTTP headers
+ headers = {'Content-Type': 'application/x-www-form-urlencoded',
+ 'X-Forwarded-For': get_request().get_environ('REMOTE_ADDR', '-'),
+ 'X-Forwarded-Host': self.host.reversed_hostname}
+
+ # Send request
+ response, status, data, auth_headers = http_post_request(
+ url, body, headers, self.host.use_proxy)
+
+ cookies = response.getheader('Set-Cookie', None)
+ self.host.cookies = []
+ if cookies is not None:
+ cookies_list = []
+ cookies_set_list = []
+ for cookie in cookies.split(', '):
+ # Drop the path and other attributes
+ cookie_only = cookie.split('; ')[0]
+ regexp = re.compile('=')
+ if regexp.search(cookie_only) is None:
+ continue
+ # Split name and value
+ cookie_split = cookie_only.split('=')
+ cookie_name = cookie_split[0]
+ cookie_value = cookie_split[1]
+ cookies_list.append('%s=%s' % (cookie_name, cookie_value))
+ set_cookie = '%s=%s; path=/' % (cookie_name, cookie_value)
+ cookies_set_list.append(set_cookie)
+ self.host.cookies.append(cookie_name)
+ cookies_headers = '\r\nSet-Cookie: '.join(cookies_set_list)
+ get_response().set_header('Set-Cookie', cookies_headers)
+ self.host.store()
+ get_session().cookies = '; '.join(cookies_list)
+ else:
+ get_logger().warn('No cookie from local authentication')
+
+ return response.status, data
+
+ # The 3 following functions have been copied from admin/hosts.ptl
+
+ def parse_forms(self, page):
+ '''Search for an authentication form'''
+ # Get all forms
+ regexp = re.compile("""<form.*?</form>""", re.DOTALL | re.IGNORECASE)
+ found_forms = regexp.findall(page)
+ if not found_forms:
+ return
+
+ # Get the first form with a password field
+ for found_form in found_forms:
+ regexp = re.compile(
+ """<input[^>]*?type=["']?password["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
+ if regexp.search(found_form) is not None:
+ self.host.auth_form = found_form
+ break
+
+ def parse_other_fields(self):
+ '''Get the default value of all other fields'''
+ self.host.other_fields = {}
+
+ # Get hidden fields
+ regexp = re.compile(
+ """<input[^>]*?type=["']?hidden["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
+ other_fields = regexp.findall(self.host.auth_form)
+
+ # Only get first submit field
+ regexp = re.compile(
+ """<input[^>]*?type=["']?submit["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
+ found = regexp.findall(self.host.auth_form)
+ if found:
+ if other_fields:
+ other_fields.append(found[0])
+ else:
+ other_fields = found[0]
+
+ for field in other_fields:
+ try:
+ regexp = re.compile("""name=["']?(.*?)["']?[\s/>]""", re.DOTALL | re.IGNORECASE)
+ name = regexp.findall(field)[0]
+ regexp = re.compile("""value=["'](.*?)["'][\s/>]""", re.DOTALL | re.IGNORECASE)
+ value = regexp.findall(field)[0]
+ self.host.other_fields[name] = value
+ if not self.host.post_parameters.has_key(name):
+ self.host.post_parameters[name] = { 'enabled': True,
+ 'value': value,
+ 'immutable': False }
+ except IndexError:
+ continue
+
+site_authentication.register_site_authentication_class(AgirheSiteAuthentication)
+
diff --git a/larpe/tags/release-1.0/larpe/plugins/site_authentication/ciril_net_rh.py b/larpe/tags/release-1.0/larpe/plugins/site_authentication/ciril_net_rh.py
new file mode 100644
index 0000000..0d706da
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/plugins/site_authentication/ciril_net_rh.py
@@ -0,0 +1,38 @@
+import re
+
+from quixote import redirect
+
+import site_authentication
+
+class CirilSiteAuthentication(site_authentication.SiteAuthentication):
+ plugin_name = 'ciril'
+
+ def auto_detect_site(cls, html_doc):
+ if re.search(
+ """<form name="myForm" id="myForm" method="post" target="choixAppli" action="/cgi-bin/acces.exe" """,
+ html_doc):
+ return True
+ return False
+ auto_detect_site = classmethod(auto_detect_site)
+
+ def check_auth(self, status, data):
+ success = False
+ return_content = ''
+
+ # If status is 500, fail without checking other criterias
+ if status // 100 == 5:
+ success = False
+ return_content = redirect(self.host.get_return_url())
+
+ regexp = re.compile(
+ """javascript\:window\.open\('(/net_rh/accueil.php\?.*?)', '_blank'\)""",
+ re.DOTALL | re.IGNORECASE)
+ match = regexp.findall(data)
+ if match:
+ success = True
+ return_content = redirect(match[0])
+
+ return success, return_content
+
+site_authentication.register_site_authentication_class(CirilSiteAuthentication)
+
diff --git a/larpe/tags/release-1.0/larpe/plugins/site_authentication/concerto.py b/larpe/tags/release-1.0/larpe/plugins/site_authentication/concerto.py
new file mode 100644
index 0000000..0e60acf
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/plugins/site_authentication/concerto.py
@@ -0,0 +1,88 @@
+import re
+
+from quixote import get_request, get_response, get_session
+
+from qommon.misc import http_post_request
+from qommon import get_logger
+
+import site_authentication
+
+class ConcertoSiteAuthentication(site_authentication.SiteAuthentication):
+ plugin_name = 'concerto'
+
+ def auto_detect_site(cls, html_doc):
+ if re.search(
+ """<meta name="description" content="Page d'accueil du site Espace-Famille" />""",
+ html_doc):
+ return True
+ return False
+ auto_detect_site = classmethod(auto_detect_site)
+
+ def local_auth_check_post(self, username, password, select=None, session_cookies=False):
+ select = select or {}
+ url = self.host.auth_check_url
+
+ # Build request body
+ body = '%s=%s&%s=%s' % (
+ self.host.login_field_name, username, self.host.password_field_name, password)
+ # Add select fields to the body
+ for name, value in select.iteritems():
+ body += '&%s=%s' % (name, value)
+ # Add hidden fields to the body
+ if self.host.send_hidden_fields:
+ for key, value in self.host.other_fields.iteritems():
+ body += '&%s=%s' % (key, value)
+
+ # Build request HTTP headers
+ headers = {'Content-Type': 'application/x-www-form-urlencoded',
+ 'X-Forwarded-For': get_request().get_environ('REMOTE_ADDR', '-'),
+ 'X-Forwarded-Host': self.host.reversed_hostname}
+
+ # Add session id cookie
+ if session_cookies is True:
+ for key, value in self.host.other_fields.iteritems():
+ headers['Cookie'] = 'JSESSIONID=' + value
+
+ # Send request
+ response, status, data, auth_headers = http_post_request(
+ url, body, headers, self.host.use_proxy)
+
+ cookies = response.getheader('Set-Cookie', None)
+ self.host.cookies = []
+ new_session_id = None
+ if cookies is not None:
+ cookies_list = []
+ cookies_set_list = []
+ for cookie in cookies.split(', '):
+ # Drop the path and other attributes
+ cookie_only = cookie.split('; ')[0]
+ regexp = re.compile('=')
+ if regexp.search(cookie_only) is None:
+ continue
+ # Split name and value
+ cookie_split = cookie_only.split('=')
+ cookie_name = cookie_split[0]
+ cookie_value = cookie_split[1]
+ if cookie_name == 'JSESSIONID':
+ new_session_id = cookie_value
+ cookies_list.append('%s=%s' % (cookie_name, cookie_value))
+ set_cookie = '%s=%s; path=/demo' % (cookie_name, cookie_value)
+ cookies_set_list.append(set_cookie)
+ self.host.cookies.append(cookie_name)
+ cookies_headers = '\r\nSet-Cookie: '.join(cookies_set_list)
+ get_response().set_header('Set-Cookie', cookies_headers)
+ self.host.store()
+ get_session().cookies = '; '.join(cookies_list)
+ else:
+ get_logger().warn('No cookie from local authentication')
+
+ if session_cookies is False:
+ # Change idSession hidden field with new session id
+ self.host.other_fields['idSession'] = new_session_id
+ # Retry the request with the new session id
+ return self.local_auth_check_post(username, password, select, session_cookies=True)
+ else:
+ return response.status, data
+
+site_authentication.register_site_authentication_class(ConcertoSiteAuthentication)
+
diff --git a/larpe/tags/release-1.0/larpe/plugins/site_authentication/egroupware.py b/larpe/tags/release-1.0/larpe/plugins/site_authentication/egroupware.py
new file mode 100644
index 0000000..bf0179a
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/plugins/site_authentication/egroupware.py
@@ -0,0 +1,90 @@
+import re
+import urlparse
+
+from quixote import get_request, get_response, get_session
+
+from qommon.misc import http_post_request, http_get_page
+from qommon import get_logger
+
+import site_authentication
+
+class EgroupwareSiteAuthentication(site_authentication.SiteAuthentication):
+ plugin_name = 'egroupware'
+
+ def auto_detect_site(cls, html_doc):
+ if re.search("""<meta name="description" content="eGroupWare" />""", html_doc):
+ return True
+ return False
+ auto_detect_site = classmethod(auto_detect_site)
+
+ def local_auth_check_post(self, username, password, select=None):
+ select = select or {}
+ url = self.host.auth_check_url
+
+ # Build request body
+ body = '%s=%s&%s=%s' % (
+ self.host.login_field_name, username, self.host.password_field_name, password)
+ # Add select fields to the body
+ for name, value in select.iteritems():
+ body += '&%s=%s' % (name, value)
+ # Add hidden fields to the body
+ if self.host.send_hidden_fields:
+ for key, value in self.host.other_fields.iteritems():
+ body += '&%s=%s' % (key, value)
+
+ # Build request HTTP headers
+ headers = {'Content-Type': 'application/x-www-form-urlencoded',
+ 'X-Forwarded-For': get_request().get_environ('REMOTE_ADDR', '-'),
+ 'X-Forwarded-Host': self.host.reversed_hostname}
+
+ # Send request
+ response, status, data, auth_headers = http_post_request(
+ url, body, headers, self.host.use_proxy)
+
+ # The specific code is these 2 lines and the called function
+ if self.host.name.startswith('egroupware'):
+ data = self.get_data_after_redirects(response, data)
+
+ cookies = response.getheader('Set-Cookie', None)
+ self.host.cookies = []
+ if cookies is not None:
+ cookies_list = []
+ cookies_set_list = []
+ for cookie in cookies.split(', '):
+ # Drop the path and other attributes
+ cookie_only = cookie.split('; ')[0]
+ regexp = re.compile('=')
+ if regexp.search(cookie_only) is None:
+ continue
+ # Split name and value
+ cookie_split = cookie_only.split('=')
+ cookie_name = cookie_split[0]
+ cookie_value = cookie_split[1]
+ cookies_list.append('%s=%s' % (cookie_name, cookie_value))
+ set_cookie = '%s=%s; path=/' % (cookie_name, cookie_value)
+ cookies_set_list.append(set_cookie)
+ self.host.cookies.append(cookie_name)
+ cookies_headers = '\r\nSet-Cookie: '.join(cookies_set_list)
+ get_response().set_header('Set-Cookie', cookies_headers)
+ self.host.store()
+ get_session().cookies = '; '.join(cookies_list)
+ else:
+ get_logger().warn('No cookie from local authentication')
+
+ return response.status, data
+
+ def get_data_after_redirects(self, response, data):
+ status = response.status
+ headers = {'X-Forwarded-For': get_request().get_environ('REMOTE_ADDR', '-'),
+ 'X-Forwarded-Host': self.host.reversed_hostname}
+ while status == 302:
+ location = response.getheader('Location', None)
+ if location is not None:
+ url_tokens = urlparse.urlparse(self.host.auth_check_url)
+ url = '%s://%s%s'% (url_tokens[0], url_tokens[1], location)
+ response, status, data, auth_headers = http_get_page(
+ url, headers, self.host.use_proxy)
+ return data
+
+site_authentication.register_site_authentication_class(EgroupwareSiteAuthentication)
+
diff --git a/larpe/tags/release-1.0/larpe/plugins/site_authentication/sympa.py b/larpe/tags/release-1.0/larpe/plugins/site_authentication/sympa.py
new file mode 100644
index 0000000..9e96e42
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/plugins/site_authentication/sympa.py
@@ -0,0 +1,44 @@
+import re
+
+from quixote import get_response, redirect
+from quixote.html import htmltext
+
+import site_authentication
+
+class SympaSiteAuthentication(site_authentication.SiteAuthentication):
+ plugin_name = 'sympa'
+
+ def auto_detect_site(cls, html_doc):
+ if re.search("""<FORM ACTION="/wwsympa.fcgi" METHOD=POST>""", html_doc):
+ return True
+ return False
+ auto_detect_site = classmethod(auto_detect_site)
+
+ def check_auth(self, status, data):
+ success = False
+ return_content = ''
+
+ if self.host.auth_system == 'password':
+ # If there is a password field, authentication probably failed
+ regexp = re.compile(
+ """<input[^>]*?type=["']?password["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
+ if not regexp.findall(data):
+ success = True
+ # The specific part is only these 2 lines
+ get_response().filter.update({'no_template': True})
+ return_content = htmltext(data)
+ elif self.host.auth_system == 'status':
+ match_status = int(self.host.auth_match_status)
+ if match_status == status:
+ success = True
+ return_content = redirect(self.host.return_url)
+ elif self.host.auth_system == 'match_text':
+ # If the auth_match_text is not matched, it means the authentication is successful
+ regexp = re.compile(self.host.auth_match_text, re.DOTALL)
+ if not regexp.findall(data):
+ success = True
+ return_content = redirect(self.host.get_return_url())
+
+ return success, return_content
+
+site_authentication.register_site_authentication_class(SympaSiteAuthentication)
diff --git a/larpe/tags/release-1.0/larpe/publisher.py b/larpe/tags/release-1.0/larpe/publisher.py
new file mode 100644
index 0000000..6c9932f
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/publisher.py
@@ -0,0 +1,53 @@
+import os
+import cPickle
+
+from quixote import get_request
+
+from Defaults import *
+
+from qommon.publisher import set_publisher_class, QommonPublisher
+
+from root import RootDirectory
+from admin import RootDirectory as AdminRootDirectory
+
+from sessions import StorageSessionManager
+from users import User
+
+class LarpePublisher(QommonPublisher):
+ APP_NAME = 'larpe'
+ APP_DIR = APP_DIR
+ DATA_DIR = DATA_DIR
+ ERROR_LOG = ERROR_LOG
+ WEB_ROOT = WEB_ROOT
+
+ supported_languages = ['fr']
+
+ root_directory_class = RootDirectory
+ admin_directory_class = AdminRootDirectory
+
+ session_manager_class = StorageSessionManager
+ user_class = User
+
+ def get_application_static_files_root_url(self):
+ return '%s/%s/' % (get_request().environ['SCRIPT_NAME'], WEB_ROOT)
+
+ def set_app_dir(self, request):
+ self.app_dir = os.path.join(self.APP_DIR, request.get_server().lower().split(':')[0])
+ self.reload_cfg()
+ if self.cfg.has_key('proxy_hostname'):
+ self.app_dir = os.path.join(self.APP_DIR, self.cfg['proxy_hostname'])
+ if self.app_dir is not None and not os.path.exists(self.app_dir):
+ os.mkdir(self.app_dir)
+ return True
+
+ cfg = None
+ def write_cfg(self, directory=None):
+ dump = cPickle.dumps(self.cfg)
+ if directory is None:
+ directory = self.app_dir
+ filename = os.path.join(directory, 'config.pck')
+ open(filename, 'w').write(dump)
+
+set_publisher_class(LarpePublisher)
+extra_dir = os.path.join(os.path.dirname(__file__), 'plugins','site_authentication')
+LarpePublisher.register_extra_dir(extra_dir)
diff --git a/larpe/tags/release-1.0/larpe/root.ptl b/larpe/tags/release-1.0/larpe/root.ptl
new file mode 100644
index 0000000..db8408b
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/root.ptl
@@ -0,0 +1,106 @@
+import os
+import httplib
+
+import lasso
+
+from quixote import get_request, get_response, get_session, redirect
+from quixote.directory import Directory
+
+from qommon.form import *
+from qommon import template
+
+import admin
+import liberty_root
+import errors
+
+from hosts import Host
+from users import User
+from Defaults import WEB_ROOT
+
+class RootDirectory(Directory):
+ _q_exports = ['', 'admin', 'liberty', 'logout', 'token']
+
+ admin = admin.RootDirectory()
+ liberty = liberty_root.LibertyRootDirectory()
+
+ def _q_index [html] (self):
+ template.html_top(_('Welcome to Larpe reverse proxy'))
+ '<ul><li><a href="%s/admin/">%s</a></li></ul>' % (get_request().environ['SCRIPT_NAME'],
+ _('Configure Larpe'))
+
+ def _q_traverse(self, path):
+ response = get_response()
+ response.filter = {}
+
+ return Directory._q_traverse(self, path)
+
+ def _q_lookup(self, component):
+ return redirect(component + '/')
+
+ def logout(self):
+ return redirect(get_publisher().get_root_url() + 'liberty/larpe/logout')
+
+ def token [html] (self):
+ session = get_session()
+
+ if not session.name_identifier or not session.lasso_anonymous_identity_dump:
+ raise errors.AccessUnauthorizedError()
+
+ # If the token is in the query string, use it
+ query_string = get_request().get_query()
+ if query_string:
+ parameters = query_string.split(str('&'))
+ for param in parameters:
+ values = param.split(str('='))
+ if len(values) < 2:
+ continue
+ if values[0] == str('token'):
+ return self._federate_token(values[1])
+
+ # Otherwise, display a form to ask for the token
+ form = Form(enctype='multipart/form-data')
+ form.add(StringWidget, 'token', title = _('Identification Token'),
+ required = True, size = 30)
+ 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():
+ template.html_top(_('Identification Token'))
+ '<p>'
+ _('Please enter your identification token. ')
+ _('Your local account will be federated with your Liberty Alliance account.')
+ '</p>'
+ form.render()
+ else:
+ token = form.get_widget('token').parse()
+ return self._federate_token(token)
+
+ def _federate_token(self, token):
+ session = get_session()
+
+ # Get the user who owns this token
+ users_with_token = list(User.select(lambda x: x.identification_token == token))
+ if len(users_with_token) == 0:
+ return template.error_page(_('Unknown Token'))
+
+ # Fill user attributes
+ user = users_with_token[0]
+ user.name_identifiers = [ session.name_identifier ]
+ user.lasso_dumps = [ session.lasso_anonymous_identity_dump ]
+ user.identification_token = None
+ user.is_admin = True
+ user.store()
+
+ # Set this user in the session
+ session.set_user(user.id, session.provider_id)
+
+ # Delete now useless session attributes
+ session.name_identifier = None
+ session.lasso_anonymous_identity_dump = None
+ session.provider_id = None
+
+ return redirect('%s/admin/' % get_request().environ['SCRIPT_NAME'])
+
diff --git a/larpe/tags/release-1.0/larpe/saml2.ptl b/larpe/tags/release-1.0/larpe/saml2.ptl
new file mode 100644
index 0000000..0357c7c
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/saml2.ptl
@@ -0,0 +1,414 @@
+import os
+import sys
+import urlparse
+
+try:
+ import lasso
+except ImportError:
+ print >> sys.stderr, 'Missing Lasso module, SAMLv2 support disabled'
+
+from quixote import get_publisher, get_request, get_response, get_session, get_session_manager, redirect
+
+from qommon.liberty import SOAPException, soap_call
+from qommon.saml2 import Saml2Directory
+from qommon import template
+from qommon import get_logger
+
+import misc
+from users import User
+from hosts import Host
+from federations import Federation
+import site_authentication
+
+class Saml2(Saml2Directory):
+ _q_exports = Saml2Directory._q_exports + ['local_auth']
+
+ def login(self):
+ return self.perform_login()
+
+ def perform_login(self, idp = None):
+ server = misc.get_lasso_server(protocol = 'saml2')
+ if not server:
+ return template.error_page(_('SAML 2.0 support not yet configured.'))
+ login = lasso.Login(server)
+ login.initAuthnRequest(idp, lasso.HTTP_METHOD_REDIRECT)
+ login.request.nameIDPolicy.format = lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT
+ login.request.nameIDPolicy.allowCreate = True
+ login.request.forceAuthn = False
+ login.request.isPassive = False
+ login.request.consent = 'urn:oasis:names:tc:SAML:2.0:consent:current-implicit'
+ login.buildAuthnRequestMsg()
+ return redirect(login.msgUrl)
+
+ def singleSignOnArtifact(self):
+ server = misc.get_lasso_server(protocol = 'saml2')
+ if not server:
+ return template.error_page(_('SAML 2.0 support not yet configured.'))
+ login = lasso.Login(server)
+ request = get_request()
+ try:
+ login.initRequest(request.get_query(), lasso.HTTP_METHOD_ARTIFACT_GET)
+ except lasso.Error, error:
+ if error[0] == lasso.PROFILE_ERROR_MISSING_ARTIFACT:
+ return template.error_page(_('Missing SAML Artifact'))
+ else:
+ raise
+
+ login.buildRequestMsg()
+ #remote_provider_cfg = get_cfg('idp', {}).get(misc.get_provider_key(login.remoteProviderId))
+ #client_cert = remote_provider_cfg.get('clientcertificate')
+
+ try:
+ soap_answer = soap_call(login.msgUrl, login.msgBody)
+ except SOAPException:
+ return template.error_page(_('Failure to communicate with identity provider'))
+
+ try:
+ login.processResponseMsg(soap_answer)
+ except lasso.Error, error:
+ if error[0] == lasso.LOGIN_ERROR_STATUS_NOT_SUCCESS:
+ return template.error_page(_('Unknown authentication failure'))
+ if error[0] == lasso.LOGIN_ERROR_UNKNOWN_PRINCIPAL:
+ return template.error_page(_('Authentication failure; unknown principal'))
+ if error[0] == lasso.LOGIN_ERROR_FEDERATION_NOT_FOUND:
+ return template.error_page('there was no federation')
+ raise
+
+ return self.sso_after_response(login)
+
+ def sso_after_response(self, login):
+ try:
+ assertion = login.response.assertion[0]
+ if assertion.subject.subjectConfirmation.subjectConfirmationData.recipient != \
+ get_request().get_url():
+ return template.error_page('SubjectConfirmation Recipient Mismatch')
+ except:
+ return template.error_page('SubjectConfirmation Recipient Mismatch')
+
+ assertions_dir = os.path.join(get_publisher().app_dir, 'assertions')
+ if not os.path.exists(assertions_dir):
+ os.mkdir(assertions_dir)
+
+ assertion_fn = os.path.join(assertions_dir, assertion.iD)
+ if os.path.exists(assertion_fn):
+ return template.error_page('Assertion replay')
+
+ try:
+ if assertion.subject.subjectConfirmation.method != \
+ 'urn:oasis:names:tc:SAML:2.0:cm:bearer':
+ return template.error_page('Unknown SubjectConfirmation Method')
+ except:
+ return template.error_page('Unknown SubjectConfirmation Method')
+
+ try:
+ audience_ok = False
+ for audience_restriction in assertion.conditions.audienceRestriction:
+ if audience_restriction.audience != login.server.providerId:
+ return template.error_page('Incorrect AudienceRestriction')
+ audience_ok = True
+ if not audience_ok:
+ return template.error_page('Incorrect AudienceRestriction')
+ except:
+ return template.error_page('Incorrect AudienceRestriction')
+
+# try:
+# current_time = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime())
+# not_before = assertion.subject.subjectConfirmation.subjectConfirmationData.notBefore
+# not_on_or_after = assertion.subject.subjectConfirmation.subjectConfirmationData.notOnOrAfter
+# if not_before and current_time < not_before:
+# return template.error_page('Assertion received too early')
+# if not_on_or_after and current_time > not_on_or_after:
+# return template.error_page('Assertion expired')
+# except:
+# return template.error_page('Error checking Assertion Time')
+
+ # TODO: check for unknown conditions
+
+ login.acceptSso()
+
+ session = get_session()
+ if login.isSessionDirty:
+ if login.session:
+ session.lasso_session_dumps[login.server.providerId] = login.session.dump()
+ else:
+ session.lasso_session_dumps[login.server.providerId] = None
+
+ if assertion.authnStatement[0].sessionIndex:
+ session.lasso_session_index = assertion.authnStatement[0].sessionIndex
+
+ user = self.lookup_user(session, login)
+
+ # Check if it is for Larpe administration or token
+ host = Host.get_host_from_url()
+ if host is None:
+ return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
+ if host.name == 'larpe':
+ if user:
+ session.set_user(user.id, login.server.providerId)
+ else:
+ session.name_identifier = login.nameIdentifier.content
+ session.lasso_anonymous_identity_dump = login.identity.dump()
+ session.provider_id = login.server.providerId
+
+ if session.after_url:
+ # Access to an admin page or token url with parameter
+ after_url = session.after_url
+ session.after_url = None
+ return redirect(after_url)
+
+ if user and user.is_admin:
+ return redirect('%s/admin/' % get_request().environ['SCRIPT_NAME'])
+ else:
+ return redirect('%s/token' % get_request().environ['SCRIPT_NAME'])
+
+ # Set session user
+ if not user:
+ user = User()
+ user.name_identifiers = [ login.nameIdentifier.content ]
+ user.lasso_dumps = [ login.identity.dump() ]
+ user.store()
+ session.set_user(user.id, login.server.providerId)
+
+ # Check if a federation already exist
+ federations = Federation.select(lambda x: host.id == x.host_id \
+ and user.name_identifiers[0] in x.name_identifiers)
+
+ if federations:
+ return site_authentication.get_site_authentication(host).sso_local_login(federations[0])
+ else:
+ # Build response redirection
+ response = get_response()
+ if session.after_url:
+ after_url = session.after_url
+ session.after_url = None
+ return redirect(after_url)
+ response.set_status(303)
+ response.headers['location'] = urlparse.urljoin(get_request().get_url(), str('local_auth'))
+ response.content_type = 'text/plain'
+ return 'Your browser should redirect you'
+
+ def lookup_user(self, session, login):
+ found_users = list(User.select(lambda x: login.nameIdentifier.content in x.name_identifiers))
+ if found_users:
+ return found_users[0]
+ return None
+
+ def slo_sp(self, method = None):
+ host = Host.get_host_from_url()
+ if host is None:
+ return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
+
+ if method is None:
+ method = lasso.HTTP_METHOD_REDIRECT
+
+ logout = lasso.Logout(misc.get_lasso_server(protocol = 'saml2'))
+ session = get_session()
+
+ if not session.id or not session.users.has_key(logout.server.providerId) \
+ or not session.lasso_session_dumps.has_key(logout.server.providerId):
+ get_session_manager().expire_session(logout.server.providerId)
+ return redirect(host.get_root_url())
+ logout.setSessionFromDump(session.lasso_session_dumps[logout.server.providerId])
+ user = session.get_user(logout.server.providerId)
+
+ if host.name != 'larpe' and user:
+ site_authentication.get_site_authentication(host).local_logout(user=user)
+
+ if user and user.lasso_dumps:
+ logout.setIdentityFromDump(user.lasso_dumps[0])
+ else:
+ get_session_manager().expire_session(logout.server.providerId)
+ return redirect(host.get_root_url())
+
+ if method == lasso.HTTP_METHOD_REDIRECT:
+ return self.slo_sp_redirect(logout)
+
+ # Not implemented yet
+ if method == lasso.HTTP_METHOD_SOAP:
+ return self.slo_sp_soap(logout)
+
+ def slo_sp_redirect(self, logout):
+ session = get_session()
+ try:
+ logout.initRequest(None, lasso.HTTP_METHOD_REDIRECT)
+ except lasso.Error, error:
+ if error[0] == lasso.PROFILE_ERROR_NAME_IDENTIFIER_NOT_FOUND:
+ get_session_manager().expire_session(logout.server.providerId)
+ return redirect(host.get_root_url())
+ if error[0] == lasso.PROFILE_ERROR_SESSION_NOT_FOUND:
+ get_session_manager().expire_session(logout.server.providerId)
+ return redirect(host.get_root_url())
+ raise
+
+ logout.buildRequestMsg()
+ return redirect(logout.msgUrl)
+
+ def singleLogoutReturn(self):
+ host = Host.get_host_from_url()
+ if host is None:
+ return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
+
+ logout = lasso.Logout(misc.get_lasso_server(protocol = 'saml2'))
+ session = get_session()
+
+ if not session.id or not session.users.has_key(logout.server.providerId) \
+ or not session.lasso_session_dumps.has_key(logout.server.providerId):
+ get_session_manager().expire_session(logout.server.providerId)
+ return redirect(host.get_root_url())
+ logout.setSessionFromDump(session.lasso_session_dumps[logout.server.providerId])
+
+ message = get_request().get_query()
+ return self.slo_return(logout, message)
+
+ def slo_return(self, logout, message):
+ host = Host.get_host_from_url()
+
+ session = get_session()
+
+ try:
+ logout.processResponseMsg(message)
+ except lasso.Error, error:
+ if error[0] == lasso.PROFILE_ERROR_INVALID_QUERY:
+ get_logger().warn('Invalid response')
+ elif error[0] == lasso.DS_ERROR_INVALID_SIGNATURE:
+ get_logger().warn('Failed to check single logout request signature')
+ elif error[0] == lasso.LOGOUT_ERROR_REQUEST_DENIED:
+ get_logger().warn('Request Denied')
+ elif error[0] == lasso.LOGOUT_ERROR_UNKNOWN_PRINCIPAL:
+ get_logger().warn('Unknown principal on logout, probably session stopped already on IdP')
+ # XXX: wouldn't work when logged on two IdP
+ del session.lasso_session_dumps[logout.server.providerId]
+ else:
+ raise
+
+ get_session_manager().expire_session(logout.server.providerId)
+
+ return redirect(host.get_root_url())
+
+ def singleLogoutSOAP(self):
+ try:
+ soap_message = self.get_soap_message()
+ except:
+ return
+
+ response = get_response()
+ response.set_content_type('text/xml')
+
+ request_type = lasso.getRequestTypeFromSoapMsg(soap_message)
+
+ if request_type != lasso.REQUEST_TYPE_LOGOUT:
+ get_logger().warn('SOAP message on single logout url not a slo message')
+ return
+
+ logout = lasso.Logout(misc.get_lasso_server(protocol = 'saml2'))
+ logout.processRequestMsg(soap_message)
+ name_identifier = logout.nameIdentifier.content
+ for session in get_session_manager().values():
+ user = session.get_user(logout.server.providerId)
+ if user and logout.nameIdentifier.content in user.name_identifiers:
+ get_logger().info('SLO/SOAP from %s' % logout.remoteProviderId)
+ break
+ else:
+ # no session, build straight failure answer
+ logout.buildResponseMsg()
+ return logout.msgBody
+
+ return self.slo_idp(logout, session)
+
+ def singleLogout(self):
+ logout = lasso.Logout(misc.get_lasso_server(protocol = 'saml2'))
+ try:
+ logout.processRequestMsg(get_request().get_query())
+ except lasso.Error, error:
+ if error[0] == lasso.DS_ERROR_INVALID_SIGNATURE:
+ return template.error_page(_('Failed to check single logout request signature.'))
+ raise
+ session = get_session()
+ if not session.id:
+ # session has not been found, this may be because the user has
+ # its browser configured so that cookies are not sent for
+ # remote queries and IdP is using image-based SLO.
+ # so we look up a session with the appropriate name identifier
+ name_identifier = logout.nameIdentifier.content
+ for session in get_session_manager().values():
+ # This block differs from qommon
+ user = session.get_user(logout.server.providerId)
+ if user and logout.nameIdentifier.content in user.name_identifiers:
+ break
+ else:
+ session = get_session()
+
+ return self.slo_idp(logout, session)
+
+ def slo_idp(self, logout, session):
+ # This block differs from qommon
+ if session.lasso_session_dumps.has_key(logout.server.providerId):
+ logout.setSessionFromDump(session.lasso_session_dumps[logout.server.providerId])
+ user = session.get_user(logout.server.providerId)
+ if user and user.lasso_dumps:
+ logout.setIdentityFromDump(user.lasso_dumps[0])
+
+ if user and logout.nameIdentifier.content not in user.name_identifiers:
+ raise 'no appropriate name identifier in session (%s and %s)' % (
+ logout.nameIdentifier.content, session.name_identifier)
+
+ try:
+ assertion = logout.session.getAssertions(logout.remoteProviderId)[0]
+ if logout.request.sessionIndex and (
+ assertion.authnStatement[0].sessionIndex != logout.request.sessionIndex):
+ logout.setSessionFromDump('<Session />')
+ except:
+ pass
+
+ try:
+ logout.validateRequest()
+ except lasso.Error, error:
+ if error[0] == lasso.PROFILE_ERROR_SESSION_NOT_FOUND:
+ pass
+ elif error[0] == lasso.PROFILE_ERROR_IDENTITY_NOT_FOUND:
+ pass
+ elif error[0] == lasso.PROFILE_ERROR_MISSING_ASSERTION:
+ pass
+ else:
+ raise
+ else:
+ get_session_manager().expire_session(logout.server.providerId)
+
+ try:
+ if not logout.request.sessionIndex:
+ for session2 in get_session_manager().values():
+ if name_identifier == session2.name_identifier:
+ del get_session_manager()[session2.id]
+ except:
+ # killing all session failed, ignoring silently
+ pass
+
+ logout.buildResponseMsg()
+ if logout.msgBody: # soap answer
+ return logout.msgBody
+ else:
+ return redirect(logout.msgUrl)
+
+ def local_auth(self):
+ host = Host.get_host_from_url()
+ if host is None:
+ return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
+ return site_authentication.get_site_authentication(host).local_auth
+ local_auth = property(local_auth)
+
+ def metadata(self):
+ host = Host.get_host_from_url()
+ if host is None:
+ return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
+ get_response().set_content_type('text/xml', 'utf-8')
+ metadata = unicode(open(host.saml2_metadata).read(), 'utf-8')
+ return metadata
+
+ def public_key(self):
+ host = Host.get_host_from_url()
+ if host is None:
+ return redirect('%s/' % get_request().environ['SCRIPT_NAME'])
+ get_response().set_content_type('text/plain')
+ public_key = open(host.public_key).read()
+ return public_key
+
diff --git a/larpe/tags/release-1.0/larpe/sessions.py b/larpe/tags/release-1.0/larpe/sessions.py
new file mode 100644
index 0000000..f7e2221
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/sessions.py
@@ -0,0 +1,76 @@
+'''Session and SessionManager objects. Configuration variables and utilities'''
+
+from quixote import get_request
+
+import qommon.sessions
+from qommon.sessions import Session
+from qommon.sessions import StorageSessionManager as SessionManager
+
+from users import User
+from hosts import Host
+
+class BasicSession(Session):
+ '''Session object. Configuration variables and utilities'''
+ _names = 'sessions'
+
+ users = {}
+ lasso_session_dumps = {}
+ provider_id = None
+
+ def has_info(self):
+ return self.users or self.lasso_session_dumps or self.provider_id or Session.has_info(self)
+ is_dirty = has_info
+
+ def get_user(self, provider_id=None):
+ # Defaults to getting Larpe user.
+ # It allows get_request().user to work in administration interface.
+ if not provider_id:
+ user_id = None
+ host = Host.get_host_from_url()
+ if not host:
+ host = Host.select(lambda x: x.name == 'larpe')[0]
+ if host:
+ user_id = self.users.get(host.provider_id)
+ if not user_id:
+ user_id = self.users.get(host.saml2_provider_id)
+ else:
+ user_id = self.users.get(provider_id)
+
+ if user_id:
+ try:
+ user = User.get(user_id)
+ except KeyError:
+ user = User()
+# if str(user_id).startswith('anonymous-'):
+ user.id = user_id
+# user.anonymous = True
+# if self.name_identifiers.has_key(providerId):
+# if not user.name_identifiers.has_key(providerId):
+# user.name_identifiers[providerId] = [ self.name_identifiers[providerId] ]
+
+# if self.name_identifiers.has_key(providerId):
+# user.name_identifiers[providerId] = [ self.name_identifiers[providerId] ]
+# else:
+# user.name_identifiers[providerId] = []
+# user.lasso_dumps[providerId] = self.lasso_anonymous_identity_dump
+ return user
+ return None
+
+ def set_user(self, user_id, provider_id):
+ self.users[provider_id] = user_id
+
+class StorageSessionManager(SessionManager):
+ '''SessionManager object. Subclass with multi-hosts specific features.'''
+ def expire_session(self, provider_id=None):
+ session = get_request().session
+ if session.id is not None:
+ if provider_id:
+ if session.users.has_key(provider_id):
+ del session.users[provider_id]
+ if session.lasso_session_dumps.has_key(provider_id):
+ del session.lasso_session_dumps[provider_id]
+ session.store()
+ if not session.users:
+ SessionManager.expire_session(self)
+
+qommon.sessions.BasicSession = BasicSession
diff --git a/larpe/tags/release-1.0/larpe/site_authentication.ptl b/larpe/tags/release-1.0/larpe/site_authentication.ptl
new file mode 100644
index 0000000..be6a7ec
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/site_authentication.ptl
@@ -0,0 +1,324 @@
+import libxml2
+import urllib
+import urlparse
+import httplib
+import re
+import os
+import socket
+import base64
+
+from quixote import get_request, get_response, get_session, redirect, get_publisher
+from quixote.directory import Directory
+from quixote.http_request import parse_header
+
+import lasso
+
+from qommon import get_logger
+from qommon.form import *
+from qommon.errors import ConnectionError, ConfigurationError, LoginError
+from qommon.misc import http_post_request, http_get_page
+from qommon.template import *
+
+import misc
+from users import User
+from federations import Federation
+
+class SiteAuthentication:
+ def __init__(self, host):
+ self.host = host
+
+ def federate(self, username, password, provider_id, cookies, select):
+ user = get_session().get_user(provider_id)
+ if user is not None:
+ Federation(username, password, self.host.id, user.name_identifiers[0], cookies, select).store()
+
+ def sso_local_login(self, federation):
+ status, data = self.local_auth_check_dispatch(
+ federation.username, federation.password, federation.select_fields)
+ success, return_content = self.check_auth(status, data)
+ if success:
+ session = get_session()
+ if hasattr(session, 'cookies'):
+ federation.set_cookies(session.cookies)
+ federation.store()
+ return return_content
+ else:
+ return redirect('local_auth')
+
+ def local_auth [html] (self, first_time=True):
+ response = get_response()
+ response.set_content_type('text/html')
+
+ if hasattr(get_response(), str('breadcrumb')):
+ del get_response().breadcrumb
+
+ get_response().filter['default_org'] = '%s - %s' % (self.host.label, _('Local authentication'))
+ get_response().filter['body_class'] = 'login'
+
+ form = self.form_local_auth()
+ form.add_submit('submit', _('Submit'))
+ #form.add_submit('cancel', _('Cancel'))
+
+# if form.get_widget('cancel').parse():
+# return redirect('.')
+ authentication_failure = None
+ if form.is_submitted() and not form.has_errors():
+ try:
+ return self.submit_local_auth_form(form)
+ except LoginError:
+ authentication_failure = _('Authentication failure')
+ get_logger().info('local auth page : %s' % authentication_failure)
+ except ConnectionError, err:
+ authentication_failure = _('Connection failed : %s') % err
+ get_logger().info('local auth page : %s' % authentication_failure)
+ except ConfigurationError, err:
+ authentication_failure = _('This service provider is not fully configured : %s') % err
+ get_logger().info('local auth page : %s' % authentication_failure)
+ except Exception, err:
+ authentication_failure = _('Unknown error : %s' % err)
+ get_logger().info('local auth page : %s' % authentication_failure)
+
+ if authentication_failure:
+ '<div class="errornotice">%s</div>' % authentication_failure
+ '<p>'
+ _('Please type your login and password for this Service Provider.')
+ _('Your local account will be federated with your Liberty Alliance account.')
+ '</p>'
+
+ form.render()
+
+ # Also used in admin/hosts.ptl
+ def form_local_auth(self):
+ form = Form(enctype='multipart/form-data')
+ form.add(StringWidget, 'username', title = _('Username'), required = True,
+ size = 30)
+ form.add(PasswordWidget, 'password', title = _('Password'), required = True,
+ size = 30)
+ for name, values in self.host.select_fields.iteritems():
+ options = []
+ if values:
+ for value in values:
+ options.append(value)
+ form.add(SingleSelectWidget, name, title = name.capitalize(),
+ value = values[0], options = options)
+ return form
+
+ def submit_local_auth_form(self, form):
+ username = form.get_widget('username').parse()
+ password = form.get_widget('password').parse()
+ select = {}
+ for name, values in self.host.select_fields.iteritems():
+ if form.get_widget(name):
+ select[name] = form.get_widget(name).parse()
+ return self.local_auth_check(username, password, select)
+
+ def local_auth_check(self, username, password, select=None):
+ select = select or {}
+ status, data = self.local_auth_check_dispatch(username, password, select)
+ if status == 0:
+ raise
+ success, return_content = self.check_auth(status, data)
+ if success:
+ if misc.get_current_protocol() == lasso.PROTOCOL_SAML_2_0:
+ provider_id = self.host.saml2_provider_id
+ else:
+ provider_id = self.host.provider_id
+ session = get_session()
+
+ if hasattr(session, 'cookies'):
+ self.federate(username, password, provider_id, session.cookies, select)
+ else:
+ self.federate(username, password, provider_id, None, select)
+ return return_content
+ raise LoginError()
+
+ def local_auth_check_dispatch(self, username, password, select=None):
+ select = select or {}
+ if self.host.auth_mode == 'http_basic':
+ return self.local_auth_check_http_basic(username, password)
+ elif self.host.auth_mode == 'form' and hasattr(self.host, 'auth_check_url') \
+ and self.host.auth_check_url is not None:
+ return self.local_auth_check_post(username, password, select)
+ else:
+ raise ConfigurationError('No authentication form was found')
+
+ def local_auth_check_post(self, username, password, select=None):
+ select = select or {}
+ url = self.host.auth_check_url
+
+ # Build request body
+ if self.host.post_parameters:
+ body_params = {}
+ # Login field
+ if self.host.post_parameters[self.host.login_field_name]['enabled'] is True:
+ body_params[self.host.login_field_name] = username
+ # Password field
+ if self.host.post_parameters[self.host.password_field_name]['enabled'] is True:
+ body_params[self.host.password_field_name] = password
+ # Select fields
+ for name, value in select.iteritems():
+ if self.host.post_parameters[name]['enabled'] is True:
+ body_params[name] = value
+ # Other fields (hidden, submit and custom)
+ for name, value in self.host.other_fields.iteritems():
+ if self.host.post_parameters[name]['enabled'] is True:
+ body_params[name] = self.host.post_parameters[name]['value']
+ body = urllib.urlencode(body_params)
+ else:
+ # XXX: Legacy (to be removed later) Send all parameters for sites configured with a previous version of Larpe
+ body = '%s=%s&%s=%s' % (self.host.login_field_name, username, self.host.password_field_name, password)
+ # Add select fields to the body
+ for name, value in select.iteritems():
+ body += '&%s=%s' % (name, value)
+ # Add hidden fields to the body
+ if self.host.send_hidden_fields:
+ for name, value in self.host.other_fields.iteritems():
+ body += '&%s=%s' % (name, value)
+
+ # Build request HTTP headers
+ if self.host.http_headers:
+ headers = {}
+ if self.host.http_headers['X-Forwarded-For']['enabled'] is True:
+ headers['X-Forwarded-For'] = get_request().get_environ('REMOTE_ADDR', '-')
+ for name, value in self.host.http_headers.iteritems():
+ if value['enabled'] is True and value['immutable'] is False:
+ headers[name] = value['value']
+ else:
+ # XXX: (to be removed later) Send default headers for sites configured with a previous version of Larpe
+ headers = { 'Content-Type': 'application/x-www-form-urlencoded',
+ 'X-Forwarded-For': get_request().get_environ('REMOTE_ADDR', '-'),
+ 'X-Forwarded-Host': self.host.reversed_hostname }
+
+ # Send request
+ response, status, data, auth_headers = http_post_request(url, body, headers, self.host.use_proxy)
+
+ cookies = response.getheader('Set-Cookie', None)
+ self.host.cookies = []
+ if cookies is not None:
+ cookies_list = []
+ cookies_set_list = []
+ for cookie in cookies.split(', '):
+ # Drop the path and other attributes
+ cookie_only = cookie.split('; ')[0]
+ regexp = re.compile('=')
+ if regexp.search(cookie_only) is None:
+ continue
+ # Split name and value
+ cookie_split = cookie_only.split('=')
+ cookie_name = cookie_split[0]
+ cookie_value = cookie_split[1]
+ cookies_list.append('%s=%s' % (cookie_name, cookie_value))
+ set_cookie = '%s=%s; path=/' % (cookie_name, cookie_value)
+ cookies_set_list.append(set_cookie)
+ self.host.cookies.append(cookie_name)
+ cookies_headers = '\r\nSet-Cookie: '.join(cookies_set_list)
+ get_response().set_header('Set-Cookie', cookies_headers)
+ self.host.store()
+ get_session().cookies = '; '.join(cookies_list)
+ else:
+ get_logger().warn('No cookie from local authentication')
+
+ return response.status, data
+
+ def local_auth_check_http_basic(self, username, password):
+ url = self.host.auth_form_url
+ hostname, query = urllib.splithost(url[5:])
+ conn = httplib.HTTPConnection(hostname)
+
+ auth_header = 'Basic %s' % base64.encodestring('%s:%s' % (username, password))
+
+ try:
+ conn.request('GET', query, headers={'Authorization': auth_header})
+ except socket.gaierror, err:
+ print err
+ conn.close()
+ return 0, None
+ else:
+ response = conn.getresponse()
+ conn.close()
+ return response.status, response.read()
+
+ def check_auth(self, status, data):
+ success = False
+ return_content = ''
+
+ # If status is 500, fail without checking other criterias
+ if status // 100 == 5:
+ success = False
+ return_content = redirect(self.host.get_return_url())
+
+
+ # For http auth, only check status code
+ elif self.host.auth_mode == 'http_basic':
+ # If failed, status code should be 401
+ if status // 100 == 2 or status // 100 == 3:
+ success = True
+ return_content = redirect(self.host.get_return_url())
+
+ else:
+ if self.host.auth_system == 'password':
+ # If there is a password field, authentication probably failed
+ regexp = re.compile("""<input[^>]*?type=["']?password["']?[^>]*?>""", re.DOTALL | re.IGNORECASE)
+ if not regexp.findall(data):
+ success = True
+ return_content = redirect(self.host.get_return_url())
+ elif self.host.auth_system == 'status':
+ match_status = int(self.host.auth_match_status)
+ if match_status == status:
+ success = True
+ return_content = redirect(self.host.get_return_url())
+ elif self.host.auth_system == 'match_text':
+ # If the auth_match_text is not matched, it means the authentication is successful
+ regexp = re.compile(self.host.auth_match_text, re.DOTALL)
+ if not regexp.findall(data):
+ success = True
+ return_content = redirect(self.host.get_return_url())
+
+ return success, return_content
+
+ def local_logout(self, federation=None, user=None):
+ if federation is None and user is not None:
+ federations = Federation.select(lambda x: user.name_identifiers[0] in x.name_identifiers)
+ if federations:
+ federation = federations[0]
+
+ # Logout request to the site
+ url = self.host.logout_url
+ if url is not None and federation is not None and federation.cookies is not None:
+ try:
+ http_get_page(url, {'Cookie': federation.cookies})
+ except:
+ pass
+
+ # Remove cookies from the browser
+ if hasattr(self.host, 'cookies'):
+ for cookie in self.host.cookies:
+ get_response().expire_cookie(cookie, path='/')
+
+ def local_defederate(self, session, provider_id):
+ if session is None:
+ return
+ user = session.get_user(provider_id)
+ if user is not None:
+ federations = Federation.select(lambda x: user.name_identifiers[0] in x.name_identifiers)
+ for federation in federations:
+ self.local_logout(provider_id, federation)
+ federation.remove_name_identifier(user.name_identifiers[0])
+ federation.store()
+
+site_authentication_classes = {}
+
+def register_site_authentication_class(klass):
+ site_authentication_classes[klass.plugin_name] = klass
+
+def guess_site_authentication_class(html_doc):
+ for name, klass in site_authentication_classes.iteritems():
+ if klass.auto_detect_site(html_doc):
+ return klass.plugin_name
+ return None
+
+def get_site_authentication(host):
+ if host.site_authentication_plugin is None:
+ return SiteAuthentication(host)
+ return site_authentication_classes[host.site_authentication_plugin](host)
+
diff --git a/larpe/tags/release-1.0/larpe/users.py b/larpe/tags/release-1.0/larpe/users.py
new file mode 100644
index 0000000..1a387a9
--- /dev/null
+++ b/larpe/tags/release-1.0/larpe/users.py
@@ -0,0 +1,44 @@
+'''User object. Configuration variables and utilities'''
+
+from qommon.storage import StorableObject
+
+class User(StorableObject):
+ '''User object. Configuration variables and utilities'''
+ _names = 'users'
+
+ name = None
+ email = None
+ name_identifiers = None
+ identification_token = None
+ lasso_dumps = None
+ is_admin = False
+ anonymous = False
+
+ def __init__(self, name=None):
+ StorableObject.__init__(self)
+ self.name = name
+ self.name_identifiers = []
+ self.lasso_dumps = []
+
+ def migrate(self):
+ pass
+
+ def remove_name_identifier(self, name_identifier):
+ self.name_identifiers.remove(name_identifier)
+ if not self.name_identifiers:
+ self.remove_self()
+ else:
+ self.store()
+
+ def get_display_name(self):
+ if self.name:
+ return self.name
+ if self.email:
+ return self.email
+ return _('Unknown User')
+ display_name = property(get_display_name)
+
+ def __str__(self):
+ return 'User %s, name : %s, name identifiers : %s, lasso_dumps : %s, token : %s' \
+ % (self.id, self.name, self.name_identifiers, self.lasso_dumps,
+ self.identification_token)
diff --git a/larpe/tags/release-1.0/larpectl b/larpe/tags/release-1.0/larpectl
new file mode 100755
index 0000000..a7c7237
--- /dev/null
+++ b/larpe/tags/release-1.0/larpectl
@@ -0,0 +1,25 @@
+#!/usr/bin/python
+
+import sys
+
+from larpe import ctl
+
+def print_usage():
+ print 'Usage: larpectl command [...]'
+ print ''
+ print 'Commands:'
+ print ' start start server'
+ print ' cache_modulesets parse and cache jhbuild module sets'
+
+if len(sys.argv) < 2:
+ print_usage()
+ sys.exit(1)
+else:
+ command = sys.argv[1]
+
+ if command == 'start':
+ ctl.start(sys.argv[2:])
+ elif command == 'cache_modulesets':
+ ctl.cache_modulesets()
+ else:
+ print_usage()
diff --git a/larpe/tags/release-1.0/make_debian_package.sh b/larpe/tags/release-1.0/make_debian_package.sh
new file mode 100755
index 0000000..b9807a4
--- /dev/null
+++ b/larpe/tags/release-1.0/make_debian_package.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+VERSION="0.2.0"
+
+svn export svn://labs.libre-entreprise.org/svnroot/larpe/larpe/tags/release-${VERSION}
+cd release-${VERSION}
+cvs -d :pserver:anonymous@labs.libre-entreprise.org:/cvsroot/wcs login
+cvs -d :pserver:anonymous@labs.libre-entreprise.org:/cvsroot/wcs export -r HEAD -d larpe/qommon wcs/wcs/qommon
+cd ..
+mv release-${VERSION} larpe-${VERSION}
+mv larpe-${VERSION}/debian .
+tar czf larpe_${VERSION}.orig.tar.gz larpe-${VERSION}
+mv debian larpe-${VERSION}
+cd larpe-${VERSION}
+debuild
+cd ..
+rm -rf larpe-${VERSION}
diff --git a/larpe/tags/release-1.0/po/Makefile b/larpe/tags/release-1.0/po/Makefile
new file mode 100644
index 0000000..0653e37
--- /dev/null
+++ b/larpe/tags/release-1.0/po/Makefile
@@ -0,0 +1,49 @@
+prefix = /usr
+
+POFILES=$(wildcard *.po)
+MOFILES=$(POFILES:.po=.mo)
+PYFILES=$(shell find -L ../larpe -name '*.py' -or -name '*.ptl')
+RM=rm -f
+
+all: $(MOFILES)
+
+install: all
+ for file in $(MOFILES); do \
+ lang=`echo $$file | sed 's/\.mo//'`; \
+ install -d $(DESTDIR)$(prefix)/share/locale/$$lang/LC_MESSAGES/; \
+ install -m 0644 $$file $(DESTDIR)$(prefix)/share/locale/$$lang/LC_MESSAGES/larpe.mo; \
+ done
+
+uninstall:
+ @for file in $(MOFILES); do \
+ lang=`echo $$file | sed 's/\.mo//'`; \
+ $(RM) $(DESTDIR)$(prefix)/share/locale/$$lang/LC_MESSAGES/larpe.mo; \
+ done
+
+clean:
+ -$(RM) messages.mo $(MOFILES)
+
+larpe.pot: $(PYFILES)
+ @echo "Rebuilding the pot file"
+ $(RM) larpe.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 > larpe.pot
+ $(RM) tmp.*.pot
+
+%.mo: %.po
+ msgfmt -o $@ $<
+
+%.po: larpe.pot
+ @echo -n "Merging larpe.pot and $@"
+ @msgmerge $@ larpe.pot -o $@.new
+ @if [ "`diff $@ $@.new | grep '[<>]' | wc -l`" -ne 2 ]; then \
+ mv -f $@.new $@; \
+ else \
+ $(RM) $@.new; \
+ fi
+ @msgfmt --statistics $@
diff --git a/larpe/tags/release-1.0/po/fr.po b/larpe/tags/release-1.0/po/fr.po
new file mode 100644
index 0000000..3592344
--- /dev/null
+++ b/larpe/tags/release-1.0/po/fr.po
@@ -0,0 +1,2826 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: Larpe 0.2.0\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2009-03-09 15:32+0100\n"
+"PO-Revision-Date: 2007-01-29 16:57+0200\n"
+"Last-Translator: Damien Laniel <dlaniel@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"
+
+#: ../larpe/liberty.ptl:45 ../larpe/qommon/liberty.ptl:44
+msgid "Liberty support is not yet configured"
+msgstr "Le support Liberty n'est pas encore configuré"
+
+#: ../larpe/liberty.ptl:58 ../larpe/qommon/liberty.ptl:59
+#: ../larpe/qommon/saml2.ptl:97 ../larpe/qommon/saml2.ptl:376
+#: ../larpe/qommon/saml2.ptl:542 ../larpe/saml2.ptl:64
+msgid "Failure to communicate with identity provider"
+msgstr "Impossible de communiquer avec le fournisseur d'identités."
+
+#: ../larpe/liberty.ptl:63 ../larpe/qommon/liberty.ptl:64
+#: ../larpe/qommon/saml2.ptl:103 ../larpe/qommon/saml2.ptl:214
+#: ../larpe/qommon/saml2.ptl:241 ../larpe/saml2.ptl:70
+msgid "Unknown authentication failure"
+msgstr "Erreur d'authentification inconnue"
+
+#: ../larpe/liberty.ptl:66 ../larpe/qommon/liberty.ptl:67
+#: ../larpe/qommon/saml2.ptl:105 ../larpe/saml2.ptl:72
+msgid "Authentication failure; unknown principal"
+msgstr "Erreur d'authentification: utilisateur inconnu"
+
+#: ../larpe/liberty.ptl:67 ../larpe/qommon/liberty.ptl:70
+msgid "Identity Provider didn't accept artifact transaction."
+msgstr "Le fournisseur d'identité n'a pas accepté l'artifact."
+
+#: ../larpe/liberty.ptl:142 ../larpe/liberty.ptl:173
+#: ../larpe/qommon/liberty.ptl:120 ../larpe/qommon/liberty.ptl:146
+#: ../larpe/qommon/saml2.ptl:456 ../larpe/saml2.ptl:324
+msgid "Failed to check single logout request signature."
+msgstr "Erreur à la vérification de la signature de la demande de déconnexion"
+
+#: ../larpe/liberty_site.ptl:46 ../larpe/liberty_site.ptl:56
+#: ../larpe/qommon/ident/idp.ptl:36
+msgid "SSO support is not yet configured"
+msgstr "Le support SSO n'est pas encore configuré"
+
+#: ../larpe/qommon/admin/menu.ptl:46
+msgid "backoffice"
+msgstr "backoffice"
+
+#: ../larpe/qommon/admin/menu.ptl:47 ../larpe/qommon/backoffice/menu.ptl:40
+msgid "logout"
+msgstr "déconnexion"
+
+#: ../larpe/qommon/admin/menu.ptl:51 ../larpe/qommon/backoffice/menu.ptl:45
+msgid "help"
+msgstr "aide"
+
+#: ../larpe/qommon/admin/menu.ptl:73
+#, python-format
+msgid "Administration of %s"
+msgstr "Administration de %s"
+
+#: ../larpe/qommon/admin/menu.ptl:75 ../larpe/admin/root.ptl:59
+msgid "Administration"
+msgstr "Administration"
+
+#: ../larpe/qommon/admin/menu.ptl:95
+msgid "Add"
+msgstr "Ajouter"
+
+#: ../larpe/qommon/admin/menu.ptl:96 ../larpe/qommon/ident/idp.ptl:498
+#: ../larpe/admin/users.ptl:109 ../larpe/admin/fields_prefill.ptl:63
+#: ../larpe/admin/fields_prefill.ptl:64 ../larpe/admin/fields_prefill.ptl:65
+#: ../larpe/admin/forms_prefill.ptl:48 ../larpe/admin/forms_prefill.ptl:66
+#: ../larpe/admin/forms_prefill.ptl:67 ../larpe/admin/forms_prefill.ptl:68
+msgid "Edit"
+msgstr "Modifier"
+
+#: ../larpe/qommon/admin/menu.ptl:97
+msgid "Remove"
+msgstr "Supprimer"
+
+#: ../larpe/qommon/admin/menu.ptl:98
+msgid "Duplicate"
+msgstr "Dupliquer"
+
+#: ../larpe/qommon/admin/menu.ptl:99
+msgid "View"
+msgstr "Voir"
+
+#: ../larpe/qommon/admin/menu.ptl:100 ../larpe/admin/hosts.ptl:313
+#: ../larpe/admin/hosts.ptl:342 ../larpe/admin/hosts.ptl:431
+#: ../larpe/admin/hosts.ptl:455 ../larpe/admin/hosts.ptl:536
+#: ../larpe/admin/hosts.ptl:572 ../larpe/admin/hosts.ptl:851
+#: ../larpe/admin/hosts.ptl:873 ../larpe/admin/hosts.ptl:961
+#: ../larpe/admin/hosts.ptl:987
+msgid "Previous"
+msgstr "Précédent"
+
+#: ../larpe/qommon/admin/menu.ptl:101 ../larpe/admin/hosts.ptl:124
+#: ../larpe/admin/hosts.ptl:314 ../larpe/admin/hosts.ptl:342
+#: ../larpe/admin/hosts.ptl:432 ../larpe/admin/hosts.ptl:456
+#: ../larpe/admin/hosts.ptl:537 ../larpe/admin/hosts.ptl:573
+#: ../larpe/admin/hosts.ptl:852 ../larpe/admin/hosts.ptl:874
+#: ../larpe/admin/hosts.ptl:932 ../larpe/admin/hosts.ptl:962
+msgid "Next"
+msgstr "Suivant"
+
+#: ../larpe/qommon/admin/menu.ptl:102
+msgid "Export"
+msgstr "Exporter"
+
+#: ../larpe/qommon/admin/menu.ptl:103 ../larpe/qommon/admin/emails.ptl:140
+#: ../larpe/qommon/ident/password.ptl:121
+#: ../larpe/qommon/ident/password.ptl:208
+#: ../larpe/qommon/ident/password.ptl:392
+#: ../larpe/qommon/ident/password.ptl:767 ../larpe/qommon/ident/idp.ptl:115
+#: ../larpe/admin/users.ptl:24 ../larpe/admin/users.ptl:33
+#: ../larpe/admin/users.ptl:65
+msgid "Email"
+msgstr "Courriel"
+
+#: ../larpe/qommon/admin/menu.ptl:132 ../larpe/qommon/admin/menu.ptl:134
+#: ../larpe/qommon/template.ptl:162
+msgid "Error"
+msgstr "Erreur"
+
+#: ../larpe/qommon/admin/menu.ptl:136 ../larpe/qommon/admin/texts.ptl:62
+#: ../larpe/qommon/admin/emails.ptl:99 ../larpe/admin/settings.ptl:112
+#: ../larpe/admin/hosts.ptl:647 ../larpe/admin/hosts.ptl:703
+#: ../larpe/admin/hosts.ptl:1324 ../larpe/admin/users.ptl:183
+msgid "Back"
+msgstr "Retour"
+
+#: ../larpe/qommon/admin/texts.ptl:48 ../larpe/qommon/admin/texts.ptl:49
+#: ../larpe/qommon/admin/texts.ptl:106 ../larpe/qommon/admin/texts.ptl:143
+msgid "Texts"
+msgstr "Textes"
+
+#: ../larpe/qommon/admin/texts.ptl:53 ../larpe/qommon/admin/texts.ptl:54
+#: ../larpe/qommon/admin/texts.ptl:58 ../larpe/qommon/admin/texts.ptl:139
+#: ../larpe/qommon/admin/emails.ptl:90 ../larpe/qommon/admin/emails.ptl:91
+#: ../larpe/qommon/admin/emails.ptl:95 ../larpe/qommon/admin/emails.ptl:177
+msgid "description"
+msgstr "description"
+
+#: ../larpe/qommon/admin/texts.ptl:58
+msgid "Custom Text:"
+msgstr "Texte personnalisé :"
+
+#. #-#-#-#-# tmp.25.idp.ptl.pot (PACKAGE VERSION) #-#-#-#-#
+#. TODO
+#: ../larpe/qommon/admin/texts.ptl:88 ../larpe/qommon/admin/emails.ptl:68
+#: ../larpe/qommon/admin/emails.ptl:121 ../larpe/qommon/admin/logger.ptl:120
+#: ../larpe/qommon/admin/settings.ptl:22 ../larpe/qommon/admin/settings.ptl:50
+#: ../larpe/qommon/admin/settings.ptl:78
+#: ../larpe/qommon/ident/password.ptl:305
+#: ../larpe/qommon/ident/password.ptl:573
+#: ../larpe/qommon/ident/password.ptl:619
+#: ../larpe/qommon/ident/password.ptl:830
+#: ../larpe/qommon/ident/password.ptl:857 ../larpe/qommon/ident/idp.ptl:77
+#: ../larpe/qommon/ident/idp.ptl:229 ../larpe/qommon/ident/idp.ptl:315
+#: ../larpe/qommon/ident/idp.ptl:513 ../larpe/qommon/ident/idp.ptl:537
+#: ../larpe/qommon/ident/idp.ptl:762 ../larpe/qommon/ident/idp.ptl:1047
+#: ../larpe/root.ptl:64 ../larpe/admin/settings.ptl:31
+#: ../larpe/admin/settings.ptl:217 ../larpe/admin/settings.ptl:326
+#: ../larpe/admin/settings.ptl:343 ../larpe/admin/hosts.ptl:383
+#: ../larpe/admin/hosts.ptl:682 ../larpe/admin/hosts.ptl:730
+#: ../larpe/admin/hosts.ptl:777 ../larpe/admin/hosts.ptl:1270
+#: ../larpe/admin/users.ptl:25 ../larpe/admin/users.ptl:35
+#: ../larpe/admin/users.ptl:121 ../larpe/admin/fields_prefill.ptl:30
+#: ../larpe/admin/fields_prefill.ptl:73 ../larpe/admin/forms_prefill.ptl:24
+#: ../larpe/admin/forms_prefill.ptl:76 ../larpe/site_authentication.ptl:59
+msgid "Submit"
+msgstr "Valider"
+
+#: ../larpe/qommon/admin/texts.ptl:90
+msgid "Restore default text"
+msgstr "Restaurer le texte par défaut"
+
+#: ../larpe/qommon/admin/texts.ptl:91 ../larpe/qommon/admin/emails.ptl:69
+#: ../larpe/qommon/admin/emails.ptl:124 ../larpe/qommon/admin/settings.ptl:23
+#: ../larpe/qommon/admin/settings.ptl:51 ../larpe/qommon/admin/settings.ptl:79
+#: ../larpe/qommon/ident/password.ptl:306
+#: ../larpe/qommon/ident/password.ptl:574
+#: ../larpe/qommon/ident/password.ptl:620
+#: ../larpe/qommon/ident/password.ptl:832
+#: ../larpe/qommon/ident/password.ptl:858 ../larpe/qommon/ident/idp.ptl:78
+#: ../larpe/qommon/ident/idp.ptl:316 ../larpe/qommon/ident/idp.ptl:538
+#: ../larpe/qommon/ident/idp.ptl:763 ../larpe/qommon/ident/idp.ptl:1048
+#: ../larpe/root.ptl:65 ../larpe/admin/settings.ptl:218
+#: ../larpe/admin/settings.ptl:327 ../larpe/admin/settings.ptl:344
+#: ../larpe/admin/hosts.ptl:123 ../larpe/admin/hosts.ptl:384
+#: ../larpe/admin/hosts.ptl:683 ../larpe/admin/hosts.ptl:731
+#: ../larpe/admin/hosts.ptl:778 ../larpe/admin/hosts.ptl:1271
+#: ../larpe/admin/users.ptl:26 ../larpe/admin/users.ptl:36
+#: ../larpe/admin/users.ptl:122 ../larpe/admin/users.ptl:137
+#: ../larpe/admin/fields_prefill.ptl:31 ../larpe/admin/fields_prefill.ptl:74
+#: ../larpe/admin/forms_prefill.ptl:25 ../larpe/admin/forms_prefill.ptl:77
+msgid "Cancel"
+msgstr "Annuler"
+
+#: ../larpe/qommon/admin/texts.ptl:103 ../larpe/qommon/admin/emails.ptl:136
+msgid "Invalid template"
+msgstr "Squelette invalide"
+
+#: ../larpe/qommon/admin/texts.ptl:107
+msgid "Text"
+msgstr "Texte"
+
+#: ../larpe/qommon/admin/emails.ptl:55
+msgid "SMTP Server"
+msgstr "Serveur SMTP"
+
+#: ../larpe/qommon/admin/emails.ptl:57
+msgid "Email Sender"
+msgstr "Émetteur des courriels"
+
+#: ../larpe/qommon/admin/emails.ptl:59
+msgid "Reply-To Address"
+msgstr "Adresse de réponse (Reply-To)"
+
+#: ../larpe/qommon/admin/emails.ptl:61
+msgid "Handle Bounces"
+msgstr "Gérer les rebonds"
+
+#: ../larpe/qommon/admin/emails.ptl:64
+msgid "Check DNS for domain name"
+msgstr "Vérifier le nom de domaine à l'aide d'une requête DNS"
+
+#: ../larpe/qommon/admin/emails.ptl:66
+msgid "Use a DNS request to check domain names used in email fields"
+msgstr ""
+"Utiliser une requête DNS pour vérifier les noms de domaine utilisés dans les "
+"champs courriel"
+
+#. #-#-#-#-# tmp.55.settings.ptl.pot (PACKAGE VERSION) #-#-#-#-#
+#. Don't use custom emails
+#: ../larpe/qommon/admin/emails.ptl:74 ../larpe/qommon/admin/emails.ptl:84
+#: ../larpe/qommon/admin/emails.ptl:85 ../larpe/qommon/admin/emails.ptl:139
+#: ../larpe/qommon/admin/emails.ptl:182 ../larpe/admin/settings.ptl:104
+#: ../larpe/admin/settings.ptl:105 ../larpe/admin/settings.ptl:192
+msgid "Emails"
+msgstr "Courriels"
+
+#: ../larpe/qommon/admin/emails.ptl:75 ../larpe/qommon/admin/emails.ptl:76
+#: ../larpe/qommon/admin/emails.ptl:88 ../larpe/admin/settings.ptl:108
+msgid "General Options"
+msgstr "Options générales"
+
+#: ../larpe/qommon/admin/emails.ptl:95
+msgid "Custom Email:"
+msgstr "Courriel personnalisé :"
+
+#: ../larpe/qommon/admin/emails.ptl:115
+msgid "Enabled Email"
+msgstr "Courriel activé"
+
+#: ../larpe/qommon/admin/emails.ptl:117 ../larpe/qommon/ident/password.ptl:828
+#: ../larpe/qommon/ident/password.ptl:854
+msgid "Subject"
+msgstr "Sujet"
+
+#: ../larpe/qommon/admin/emails.ptl:123
+msgid "Restore default email"
+msgstr "Restaurer le courriel par défaut"
+
+#: ../larpe/qommon/admin/emails.ptl:178
+msgid "hint"
+msgstr "astuce"
+
+#: ../larpe/qommon/admin/logger.ptl:23 ../larpe/qommon/admin/logger.ptl:24
+#: ../larpe/qommon/admin/logger.ptl:145 ../larpe/admin/root.ptl:47
+#: ../larpe/admin/users.ptl:232
+msgid "Logs"
+msgstr "Journaux"
+
+#: ../larpe/qommon/admin/logger.ptl:28 ../larpe/qommon/admin/logger.ptl:127
+#, python-format
+msgid "Bad log file: %s"
+msgstr "Mauvais fichier journal : %s"
+
+#: ../larpe/qommon/admin/logger.ptl:32
+msgid "Nothing to show"
+msgstr "Rien à afficher"
+
+#: ../larpe/qommon/admin/logger.ptl:35 ../larpe/qommon/admin/logger.ptl:37
+msgid "Download Raw Log File"
+msgstr "Télécharger le fichier journal"
+
+#: ../larpe/qommon/admin/logger.ptl:43 ../larpe/qommon/admin/logger.ptl:151
+msgid "Time"
+msgstr "Horodatage"
+
+#: ../larpe/qommon/admin/logger.ptl:44 ../larpe/qommon/admin/logger.ptl:146
+#: ../larpe/admin/users.ptl:59 ../larpe/admin/users.ptl:60
+msgid "User"
+msgstr "Utilisateur"
+
+#: ../larpe/qommon/admin/logger.ptl:45 ../larpe/qommon/admin/logger.ptl:152
+#: ../larpe/qommon/ident/password.ptl:829
+#: ../larpe/qommon/ident/password.ptl:856
+msgid "Message"
+msgstr "Message"
+
+#: ../larpe/qommon/admin/logger.ptl:74
+msgid "Anonymous"
+msgstr "Anonyme"
+
+#: ../larpe/qommon/admin/logger.ptl:78
+msgid "Unlogged"
+msgstr "Non authentifié"
+
+#: ../larpe/qommon/admin/logger.ptl:85 ../larpe/qommon/backoffice/menu.ptl:31
+msgid "Unknown"
+msgstr "Inconnu"
+
+#: ../larpe/qommon/admin/logger.ptl:112
+msgid "Select another logfile:"
+msgstr "Choisir un autre fichier journal :"
+
+#: ../larpe/qommon/admin/logger.ptl:117
+#, python-format
+msgid "Since: %s"
+msgstr "Depuis: %s"
+
+#: ../larpe/qommon/admin/settings.ptl:10 ../larpe/admin/root.ptl:46
+#: ../larpe/admin/settings.ptl:125 ../larpe/admin/hosts.ptl:83
+msgid "Settings"
+msgstr "Paramètres"
+
+#: ../larpe/qommon/admin/settings.ptl:16 ../larpe/qommon/admin/settings.ptl:28
+#: ../larpe/qommon/admin/settings.ptl:29 ../larpe/admin/settings.ptl:190
+msgid "Language"
+msgstr "Langue"
+
+#: ../larpe/qommon/admin/settings.ptl:18
+msgid "System Default"
+msgstr "Valeur du système"
+
+#: ../larpe/qommon/admin/settings.ptl:19
+msgid "English"
+msgstr "Anglais"
+
+#: ../larpe/qommon/admin/settings.ptl:20
+msgid "French"
+msgstr "Français"
+
+#: ../larpe/qommon/admin/settings.ptl:39
+msgid "Use a web proxy"
+msgstr "Utiliser un mandataire (proxy) web"
+
+#: ../larpe/qommon/admin/settings.ptl:41
+msgid "Proxy IP address or domain name"
+msgstr "Adresse IP or nom de domaine du mandataire"
+
+#: ../larpe/qommon/admin/settings.ptl:43
+msgid "Proxy port"
+msgstr "Port du mandataire (proxy)"
+
+#: ../larpe/qommon/admin/settings.ptl:45
+msgid "User name"
+msgstr "Identifiant"
+
+#: ../larpe/qommon/admin/settings.ptl:47
+msgid "User password"
+msgstr "Mot de passe"
+
+#: ../larpe/qommon/admin/settings.ptl:56 ../larpe/qommon/admin/settings.ptl:57
+#: ../larpe/admin/settings.ptl:183
+msgid "Proxy"
+msgstr "Mandataire (proxy)"
+
+#: ../larpe/qommon/admin/settings.ptl:66
+msgid "Email for Tracebacks"
+msgstr "Courriel pour les exceptions"
+
+#: ../larpe/qommon/admin/settings.ptl:68
+msgid "Display Exceptions"
+msgstr "Afficher les exceptions"
+
+#: ../larpe/qommon/admin/settings.ptl:70
+msgid "No display"
+msgstr "Aucun affichage"
+
+#: ../larpe/qommon/admin/settings.ptl:71
+msgid "Display as Text"
+msgstr "Afficher sous forme de texte"
+
+#: ../larpe/qommon/admin/settings.ptl:72
+msgid "Display as Text in an HTML error page"
+msgstr "Afficher sous forme de texte dans une page d'erreur HTML"
+
+#: ../larpe/qommon/admin/settings.ptl:73
+msgid "Display as HTML"
+msgstr "Afficher sous de forme de HTML"
+
+#: ../larpe/qommon/admin/settings.ptl:74
+msgid "Logger"
+msgstr "Journalisation"
+
+#: ../larpe/qommon/admin/settings.ptl:76
+msgid "Enable debug mode"
+msgstr "Activer le mode de débogage"
+
+#: ../larpe/qommon/admin/settings.ptl:85 ../larpe/qommon/admin/settings.ptl:86
+#: ../larpe/qommon/admin/settings.ptl:87 ../larpe/admin/settings.ptl:199
+msgid "Debug Options"
+msgstr "Options de débogage"
+
+#: ../larpe/qommon/ident/password.ptl:61
+#, python-format
+msgid "Password is too short. It must be at least %d characters."
+msgstr ""
+"Le mot de passe est trop court. Il doit contenir au moins %d caractères."
+
+#: ../larpe/qommon/ident/password.ptl:65
+#, python-format
+msgid "Password is too long. It must be at most %d characters."
+msgstr "Le mot de passe est trop long. Il doit contenir au plus %d caractères."
+
+#: ../larpe/qommon/ident/password.ptl:76
+msgid "Account Creation Confirmed"
+msgstr "Création du compte confirmée"
+
+#: ../larpe/qommon/ident/password.ptl:123
+#: ../larpe/qommon/ident/password.ptl:210
+#: ../larpe/qommon/ident/password.ptl:389
+#: ../larpe/qommon/ident/password.ptl:647
+#: ../larpe/qommon/ident/password.ptl:763 ../larpe/admin/hosts.ptl:525
+#: ../larpe/site_authentication.ptl:93
+msgid "Username"
+msgstr "Identifiant"
+
+#: ../larpe/qommon/ident/password.ptl:124
+#: ../larpe/qommon/ident/password.ptl:395
+#: ../larpe/qommon/ident/password.ptl:653 ../larpe/admin/hosts.ptl:527
+#: ../larpe/site_authentication.ptl:95
+msgid "Password"
+msgstr "Mot de passe"
+
+#: ../larpe/qommon/ident/password.ptl:125
+msgid "Log in"
+msgstr "S'identifier"
+
+#: ../larpe/qommon/ident/password.ptl:131
+#: ../larpe/qommon/ident/password.ptl:132 ../larpe/qommon/ident/idp.ptl:93
+msgid "Login"
+msgstr "S'identifier"
+
+#: ../larpe/qommon/ident/password.ptl:158
+msgid "Invalid credentials"
+msgstr "Identifiant ou mot de passe invalide"
+
+#: ../larpe/qommon/ident/password.ptl:169
+msgid "This account is waiting for moderation"
+msgstr "Ce compte est en attente de modération"
+
+#: ../larpe/qommon/ident/password.ptl:174
+msgid "This account is waiting for confirmation"
+msgstr "Ce compte est en attente de confirmation"
+
+#: ../larpe/qommon/ident/password.ptl:179
+msgid "This account has been disabled"
+msgstr "Ce compte a été désactivé"
+
+#: ../larpe/qommon/ident/password.ptl:211
+msgid "Submit Request"
+msgstr "Valider la requête"
+
+#: ../larpe/qommon/ident/password.ptl:220
+#: ../larpe/qommon/ident/password.ptl:221
+msgid "Forgotten password"
+msgstr "Mot de passe oublié"
+
+#: ../larpe/qommon/ident/password.ptl:238
+msgid "There is no user with that name or it has no email contact."
+msgstr ""
+"Il n'y a pas d'utilisateur avec ce nom ou cet utilisateur n'a pas d'adresse "
+"de courriel de contact"
+
+#: ../larpe/qommon/ident/password.ptl:260
+msgid "Failed to send email (server error)"
+msgstr "Échec lors de l'envoi du courriel (erreur du serveur)"
+
+#: ../larpe/qommon/ident/password.ptl:265
+msgid "Forgotten Password"
+msgstr "Mot de passe oublié"
+
+#: ../larpe/qommon/ident/password.ptl:279
+msgid ""
+"The token you submitted does not exist, has expired, or has been cancelled."
+msgstr "Le jeton que vous avez entré n'existe pas, a expiré, ou a été annulé"
+
+#: ../larpe/qommon/ident/password.ptl:280
+#: ../larpe/qommon/ident/password.ptl:285
+msgid "home page"
+msgstr "page d'accueil"
+
+#: ../larpe/qommon/ident/password.ptl:284
+msgid "The token you submitted is not appropriate for the requested task."
+msgstr ""
+"Le jeton que vous avez entré n'est pas approprié pour la tâche demandée"
+
+#: ../larpe/qommon/ident/password.ptl:290
+msgid "Request Cancelled"
+msgstr "Requête annulée"
+
+#: ../larpe/qommon/ident/password.ptl:291
+msgid "Your request has been cancelled"
+msgstr "Votre requête a été annulée"
+
+#: ../larpe/qommon/ident/password.ptl:292
+msgid "Continue to <a href=\"/\">home page</a></p>"
+msgstr "Continuez vers <a href=\"/\">page d'accueil</a></p>"
+
+#: ../larpe/qommon/ident/password.ptl:301
+msgid "New Password"
+msgstr "Nouveau mot de passe"
+
+#: ../larpe/qommon/ident/password.ptl:303
+msgid "New Password (confirm)"
+msgstr "Nouveau mot de passe (confirmation)"
+
+#: ../larpe/qommon/ident/password.ptl:317
+msgid "Passwords do not match"
+msgstr "Les mots de passe sont différents"
+
+#: ../larpe/qommon/ident/password.ptl:367
+msgid "New password sent by email"
+msgstr "Un nouveau mot de passe a été envoyé par courriel"
+
+#: ../larpe/qommon/ident/password.ptl:397 ../larpe/qommon/ident/idp.ptl:117
+msgid "Create Account"
+msgstr "Création un compte"
+
+#: ../larpe/qommon/ident/password.ptl:404
+#: ../larpe/qommon/ident/password.ptl:405 ../larpe/qommon/ident/idp.ptl:124
+#: ../larpe/qommon/ident/idp.ptl:125
+msgid "New Account"
+msgstr "Nouveau compte"
+
+#: ../larpe/qommon/ident/password.ptl:426
+msgid "There is already a user with that username"
+msgstr "Il y a déjà un utilisateur avec ce nom"
+
+#: ../larpe/qommon/ident/password.ptl:429
+msgid "There is already a user with that email address"
+msgstr "Il y a déjà un utilisateur avec cette adresse de courriel"
+
+#: ../larpe/qommon/ident/password.ptl:462
+msgid ""
+"Accounts are configured to require confirmation but accounts can be created "
+"without emails"
+msgstr ""
+"Les comptes sont configurés pour nécessiter une confirmation mais les "
+"comptes peuvent être crées sans courriel"
+
+#: ../larpe/qommon/ident/password.ptl:481
+msgid "Account created, waiting for moderation"
+msgstr "Compte créé, en attente de modération"
+
+#: ../larpe/qommon/ident/password.ptl:484
+msgid "A site administrator will now review then activate your account."
+msgstr ""
+"Un administrateur du site va maintenant vérifier puis activer votre compte"
+
+#: ../larpe/qommon/ident/password.ptl:488
+msgid "You will then get your password by email."
+msgstr "Votre mot de passe vous sera ensuite envoyé par courriel"
+
+#: ../larpe/qommon/ident/password.ptl:492
+msgid "Back to home page"
+msgstr "Retour à la page d'accueil"
+
+#: ../larpe/qommon/ident/password.ptl:498
+msgid "Email sent"
+msgstr "Courriel envoyé"
+
+#: ../larpe/qommon/ident/password.ptl:501
+msgid "An email has been sent to you so you can confirm your account creation."
+msgstr ""
+"Un courriel vous a été envoyé afin que vous confirmiez la création de votre "
+"compte."
+
+#: ../larpe/qommon/ident/password.ptl:528
+msgid "Username / Password"
+msgstr "Identifiant / Mot de passe"
+
+#: ../larpe/qommon/ident/password.ptl:532
+msgid "Configure username/password identification method"
+msgstr "Configurer la méthode d'identification par identifiant/mot de passe"
+
+#: ../larpe/qommon/ident/password.ptl:543 ../larpe/qommon/ident/idp.ptl:710
+msgid "Identities"
+msgstr "Identités"
+
+#: ../larpe/qommon/ident/password.ptl:543 ../larpe/qommon/ident/idp.ptl:710
+msgid "Configure identities creation"
+msgstr "Configurer la création des identités"
+
+#: ../larpe/qommon/ident/password.ptl:545
+#: ../larpe/qommon/ident/password.ptl:582
+#: ../larpe/qommon/ident/password.ptl:583
+msgid "Passwords"
+msgstr "Mots de passe"
+
+#: ../larpe/qommon/ident/password.ptl:545
+msgid "Configure all password things"
+msgstr "Configurer les paramètres relatifs aux mots de passe"
+
+#: ../larpe/qommon/ident/password.ptl:551
+msgid "Users can change their password"
+msgstr "Les utilisateurs peuvent changer leur mot de passe"
+
+#: ../larpe/qommon/ident/password.ptl:553
+msgid "Generate initial password"
+msgstr "Générer le mot de passe initial"
+
+#: ../larpe/qommon/ident/password.ptl:556
+msgid "Lost Password Behaviour"
+msgstr "Comportement en cas de perte de mot de passe"
+
+#: ../larpe/qommon/ident/password.ptl:558
+msgid "Nothing (contact admin)"
+msgstr "Rien (contactez l'administrateur)"
+
+#: ../larpe/qommon/ident/password.ptl:559
+msgid "Email reminder"
+msgstr "Rappel par courriel"
+
+#: ../larpe/qommon/ident/password.ptl:560
+msgid "Question selected by user"
+msgstr "Question selectionnée par l'utilisateur"
+
+#: ../larpe/qommon/ident/password.ptl:561
+msgid "Minimum password length"
+msgstr "Taille minimum des mots de passe"
+
+#: ../larpe/qommon/ident/password.ptl:563
+msgid "Maximum password length"
+msgstr "Taille maximum des mots de passe"
+
+#: ../larpe/qommon/ident/password.ptl:565
+msgid "0 for unlimited length"
+msgstr "0 pour une taille illimitée"
+
+#: ../larpe/qommon/ident/password.ptl:566
+msgid "Email address (for questions...)"
+msgstr "Addresse de courriel (pour des questions ...)"
+
+#: ../larpe/qommon/ident/password.ptl:569
+msgid "Password Hashing Algorithm"
+msgstr "Algorithm de hachage de mots de passe"
+
+#: ../larpe/qommon/ident/password.ptl:571
+msgid "None"
+msgstr "Aucun"
+
+#: ../larpe/qommon/ident/password.ptl:596 ../larpe/qommon/ident/idp.ptl:1033
+msgid "Identity Creation"
+msgstr "Création d'identité"
+
+#: ../larpe/qommon/ident/password.ptl:598 ../larpe/qommon/ident/idp.ptl:1035
+msgid "Site Administrator"
+msgstr "Administrateur du site"
+
+#: ../larpe/qommon/ident/password.ptl:599 ../larpe/qommon/ident/idp.ptl:1036
+msgid "Self-registration"
+msgstr "Inscription par l'utilisateur"
+
+#: ../larpe/qommon/ident/password.ptl:600
+msgid "Moderated user registration"
+msgstr "Inscription par l'utilisateur avec modération"
+
+#: ../larpe/qommon/ident/password.ptl:602 ../larpe/qommon/ident/idp.ptl:1039
+msgid "Require email confirmation for new accounts"
+msgstr "Demander une confirmation par courriel pour les nouveaux comptes"
+
+#: ../larpe/qommon/ident/password.ptl:605 ../larpe/qommon/ident/idp.ptl:1043
+msgid "Notify Administrators on Registration"
+msgstr "Notifier les administrateurs lors d'inscriptions"
+
+#: ../larpe/qommon/ident/password.ptl:607
+msgid "Use email as username"
+msgstr "Utiliser le courriel comme identifiant"
+
+#: ../larpe/qommon/ident/password.ptl:610
+msgid "Warn about unused account after so many days"
+msgstr "Avertir lorsque des comptes sont inutilisés depuis plusieurs jours"
+
+#: ../larpe/qommon/ident/password.ptl:611
+msgid "0 for no warning"
+msgstr "0 pour aucun avertissement"
+
+#: ../larpe/qommon/ident/password.ptl:614
+msgid "Removed unused account after so many days"
+msgstr "Compte inutilisés qui ont été supprimés après plusieurs jours"
+
+#: ../larpe/qommon/ident/password.ptl:615
+msgid "0 for no automatic removal"
+msgstr "0 pour désactiver la suppression automatique"
+
+#: ../larpe/qommon/ident/password.ptl:629
+#: ../larpe/qommon/ident/password.ptl:630 ../larpe/qommon/ident/idp.ptl:1057
+#: ../larpe/qommon/ident/idp.ptl:1058
+msgid "Identities Interface"
+msgstr "Interface des identités"
+
+#: ../larpe/qommon/ident/password.ptl:657
+msgid "Awaiting Confirmation"
+msgstr "En attente de confirmation"
+
+#: ../larpe/qommon/ident/password.ptl:659
+msgid "Awaiting Moderation"
+msgstr "En attente de modération"
+
+#: ../larpe/qommon/ident/password.ptl:661
+msgid "Disabled Account"
+msgstr "Nouvel hôte"
+
+#: ../larpe/qommon/ident/password.ptl:675
+msgid "Username / password"
+msgstr "Identifiant / mot de passe"
+
+#: ../larpe/qommon/ident/password.ptl:690
+msgid "Duplicate user name"
+msgstr "Nom d'utilisateur en double"
+
+#: ../larpe/qommon/ident/password.ptl:734
+msgid "Accounts"
+msgstr "Comptes"
+
+#: ../larpe/qommon/ident/password.ptl:757
+#, python-format
+msgid "Account - %s"
+msgstr "Compte - %s"
+
+#: ../larpe/qommon/ident/password.ptl:758
+msgid "Moderation of account"
+msgstr "Modération de compte"
+
+#: ../larpe/qommon/ident/password.ptl:788
+#: ../larpe/qommon/ident/password.ptl:864
+#: ../larpe/qommon/ident/password.ptl:865
+#: ../larpe/qommon/ident/password.ptl:866
+#: ../larpe/qommon/ident/password.ptl:922
+msgid "Reply by email"
+msgstr "Répondre par courriel"
+
+#: ../larpe/qommon/ident/password.ptl:789
+#: ../larpe/qommon/ident/password.ptl:924
+msgid "Accept"
+msgstr "Accepter"
+
+#: ../larpe/qommon/ident/password.ptl:791
+#: ../larpe/qommon/ident/password.ptl:926
+msgid "Reject"
+msgstr "Rejeter"
+
+#: ../larpe/qommon/ident/password.ptl:827
+#: ../larpe/qommon/ident/password.ptl:853
+msgid "To"
+msgstr "À"
+
+#: ../larpe/qommon/ident/password.ptl:831
+msgid "Submit and don't send email"
+msgstr "Valider et ne pas envoyer de courriel"
+
+#: ../larpe/qommon/ident/password.ptl:838
+#: ../larpe/qommon/ident/password.ptl:839
+msgid "Rejection"
+msgstr "Rejet"
+
+#: ../larpe/qommon/ident/password.ptl:855
+msgid "About your account request"
+msgstr "À propos de votre demande de compte"
+
+#: ../larpe/qommon/ident/password.ptl:886
+msgid "You are not allowed to access Accounts Management"
+msgstr "Vous n'êtes pas autorisé à accéder à la gestion des comptes"
+
+#: ../larpe/qommon/ident/password.ptl:889
+#: ../larpe/qommon/ident/password.ptl:898
+msgid "Accounts Management"
+msgstr "Gestion des comptes"
+
+#: ../larpe/qommon/ident/password.ptl:899
+msgid "New accounts waiting for moderation"
+msgstr "De nouveaux comptes sont en attente de modération"
+
+#: ../larpe/qommon/ident/password.ptl:914
+msgid "Username:"
+msgstr "Identifiant :"
+
+#: ../larpe/qommon/ident/password.ptl:939
+msgid "Subscription notification for password account"
+msgstr "Notification d'inscription pour un compte à mot de passe"
+
+#: ../larpe/qommon/ident/password.ptl:940
+msgid ""
+"Available variables: email, website, token_url, admin_email, username, "
+"password"
+msgstr ""
+"Variables disponibles : email, website, token_url, admin_email, username, "
+"password"
+
+#: ../larpe/qommon/ident/password.ptl:941
+msgid "Subscription Confirmation"
+msgstr "Configuration de souscription"
+
+#: ../larpe/qommon/ident/password.ptl:942
+msgid ""
+"We have received a request for subscription of your email address,\n"
+"\"[email]\", to the [website] web site.\n"
+"\n"
+"To confirm that you want to be subscribed to the web site, simply\n"
+"visit this web page:\n"
+"\n"
+"[token_url]\n"
+"\n"
+"If you do not wish to be subscribed to the web site, pleasy simply\n"
+"disregard this message. If you think you are being maliciously\n"
+"subscribed to the web site, or have any other questions, send them\n"
+"to [admin_email].\n"
+msgstr ""
+"Nous avons reçu une demande d'inscription provenant de votre adresse\n"
+"de courriel,\"[email]\", pour le site internet [website].\n"
+"\n"
+"Pour confirmer votre inscription à ce site, visitez simplement cette\n"
+"page :\n"
+"\n"
+"[token_url]\n"
+"\n"
+"Si vous ne souhaitez pas être inscrit au site internet, ignorez ce\n"
+"message. Si vous pensez que vous avez été inscrit à votre insu, ou\n"
+"avez toute autre question à nous poser, envoyer nous un courriel\n"
+"à [admin_email].\n"
+
+#: ../larpe/qommon/ident/password.ptl:958
+msgid "Request for password change"
+msgstr "Demande de changement de mot de passe"
+
+#: ../larpe/qommon/ident/password.ptl:959
+msgid "Available variables: change_url, cancel_url, time"
+msgstr "Variables disponibles : change_url, cancel_url, time"
+
+#: ../larpe/qommon/ident/password.ptl:960
+msgid "Change Password Request"
+msgstr "Requête de changement de mot de passe"
+
+#: ../larpe/qommon/ident/password.ptl:961
+msgid ""
+"You have (or someone impersonating you has) requested to change your\n"
+"password. To complete the change, visit the following link:\n"
+"\n"
+"[change_url]\n"
+"\n"
+"If you are not the person who made this request, or you wish to cancel\n"
+"this request, visit the following link:\n"
+"\n"
+"[cancel_url]\n"
+"\n"
+"If you do nothing, the request will lapse after 3 days (precisely on\n"
+"[time]).\n"
+msgstr ""
+"Vous avez (ou quelqu'un se faisant passer pour vous) demandé à changer de\n"
+"mot de passe. Pour accomplir ce changement, visitez cette page :\n"
+"\n"
+"[change_url]\n"
+"\n"
+"Si vous n'êtes pas la personne qui a fait cette requête, ou si vous voulez\n"
+"l'annuler, allez sur la page suivante :\n"
+"\n"
+"[cancel_url]\n"
+"\n"
+"Si vous ne faites rien, la demande expirera automatiquement dans 3 jours,\n"
+"précisement le [time]).\n"
+
+#: ../larpe/qommon/ident/password.ptl:978
+msgid "New generated password"
+msgstr "Nouveau mot de passe généré"
+
+#: ../larpe/qommon/ident/password.ptl:979
+msgid "Available variable: password"
+msgstr "Variables disponibles : password"
+
+#: ../larpe/qommon/ident/password.ptl:980
+msgid "Your new password"
+msgstr "Votre nouveau mot de passe"
+
+#: ../larpe/qommon/ident/password.ptl:981
+msgid "Your new password: [password]\n"
+msgstr "Votre nouveau mot de passe : [password]\n"
+
+#: ../larpe/qommon/ident/password.ptl:987
+msgid "Approval of new account"
+msgstr "Acceptation du nouveau compte"
+
+#: ../larpe/qommon/ident/password.ptl:988
+msgid "Available variables: username, password"
+msgstr "Variables disponibles : username, password"
+
+#: ../larpe/qommon/ident/password.ptl:989
+msgid "Your account has been approved"
+msgstr "Votre compte a été validé"
+
+#: ../larpe/qommon/ident/password.ptl:990
+msgid ""
+"Your account has been approved.\n"
+"\n"
+"Account details:\n"
+"\n"
+"- username: [username]\n"
+"[if-any password]- password: [password][end]\n"
+msgstr ""
+"Votre compte a été validé.\n"
+"\n"
+"Détails du compte :\n"
+"\n"
+"- identifiant: [username]\n"
+"[if-any password]- mot de passe: [password][end]\n"
+
+#: ../larpe/qommon/ident/password.ptl:1000
+msgid "Warning about unusued account"
+msgstr "Avertissement pour les comptes inutilisés"
+
+#: ../larpe/qommon/ident/password.ptl:1001
+#: ../larpe/qommon/ident/password.ptl:1009
+msgid "Available variables: username"
+msgstr "Variables disponibles : username"
+
+#: ../larpe/qommon/ident/password.ptl:1002
+msgid "Your account is unused"
+msgstr "Votre compte est inutilisé"
+
+#: ../larpe/qommon/ident/password.ptl:1003
+msgid "Your account ([username]) is not being used.\n"
+msgstr "Votre compte ([username]) n'est pas utilisé.\n"
+
+#: ../larpe/qommon/ident/password.ptl:1008
+msgid "Notification of removal of unused account"
+msgstr "Notification de suppression de compte inutilisé"
+
+#: ../larpe/qommon/ident/password.ptl:1010
+msgid "Your account has been removed"
+msgstr "Votre compte a été supprimé"
+
+#: ../larpe/qommon/ident/password.ptl:1011
+msgid ""
+"Your account ([username]) was not being used, it has therefore been "
+"removed.\n"
+msgstr ""
+"Votre compte ([username]) n'était plus utilisé et a donc été supprimé.\n"
+
+#: ../larpe/qommon/ident/password.ptl:1016
+msgid "Notification of new registration to administrators"
+msgstr "Notification des nouvelles inscriptions aux administrateurs"
+
+#: ../larpe/qommon/ident/password.ptl:1017
+msgid "Available variables: hostname, email_as_username, username"
+msgstr "Variables disponibles : hostname, email_as_username, username"
+
+#: ../larpe/qommon/ident/password.ptl:1018
+msgid "New Registration"
+msgstr "Nouvelle inscription"
+
+#: ../larpe/qommon/ident/password.ptl:1019
+msgid ""
+"Hello,\n"
+"\n"
+"A new account has been created on [hostname].\n"
+"\n"
+" - name: [name]\n"
+" - username: [username]\n"
+msgstr ""
+"Bonjour,\n"
+"\n"
+"Un nouveau compte a été créé sur [hostname].\n"
+"\n"
+" - nom : [name]\n"
+" - identifiant : [username]\n"
+
+#: ../larpe/qommon/ident/password.ptl:1030
+msgid "Text when account confirmed by user but waiting moderator approval"
+msgstr ""
+"Texte quand le compte a été confirmé par l'utilisateur mais en attente de "
+"validation d'un modérateur"
+
+#: ../larpe/qommon/ident/password.ptl:1031
+msgid ""
+"<p>\n"
+"Your account has been created. In order to be effective\n"
+"it must be activated by a moderator. You will receive an\n"
+"email when this is done.\n"
+"</p>"
+msgstr ""
+"<p>\n"
+"Votre compte a été créé. Afin d'être utilisable,\n"
+"il doit être activé par un modérateur. Vous recevrez un\n"
+"courriel quand ce sera fait.\n"
+"</p>"
+
+#: ../larpe/qommon/ident/password.ptl:1038
+msgid "Text when account confirmed by user"
+msgstr "Texte quand le compte a été confirmé par l'utilisateur"
+
+#: ../larpe/qommon/ident/password.ptl:1039
+msgid ""
+"<p>\n"
+"Your account has been created.\n"
+"</p>"
+msgstr ""
+"<p>\n"
+"Votre compte a été créé.\n"
+"</p>"
+
+#: ../larpe/qommon/ident/password.ptl:1044
+msgid "Text when an email with a change password token has been sent"
+msgstr ""
+"Texte quand un courriel avec un jeton de changement de mot de passe a été "
+"envoyé"
+
+#: ../larpe/qommon/ident/password.ptl:1045
+msgid ""
+"<p>\n"
+"A token for changing your password has been emailed to you. Follow the "
+"instructions in that email to change your password.\n"
+"</p>\n"
+"<p>\n"
+" <a href=\"login\">Log In</a>\n"
+"</p>"
+msgstr ""
+"<p>\n"
+"Un jeton de changement de mot de passe vous a été envoyé par courriel. "
+"Suivez les instructions dans celui-ci pour changer votre mot de passe.\n"
+"</p>\n"
+"<p>\n"
+" <a href=\"login\">S'identifier</a>\n"
+"</p>"
+
+#: ../larpe/qommon/ident/password.ptl:1053
+msgid "Text when new password has been sent"
+msgstr "Texte quand un nouveau mot de passe a été envoyé"
+
+#: ../larpe/qommon/ident/password.ptl:1054
+msgid ""
+"<p>\n"
+"Your new password has been sent to you by email.\n"
+"</p>\n"
+"<p>\n"
+" <a href=\"login\">Login</a>\n"
+"</p>"
+msgstr ""
+"<p>\n"
+"Votre nouveau mot de passe vous a été envoyé par courriel.\n"
+"</p>\n"
+"<p>\n"
+" <a href=\"login\">S'identifier</a>\n"
+"</p>"
+
+#: ../larpe/qommon/ident/password.ptl:1061
+msgid "Text on top of registration form"
+msgstr "Texte en haut du formulaire d'enregistrement"
+
+#: ../larpe/qommon/ident/password.ptl:1064
+msgid "Text on forgotten password request page"
+msgstr "Texte sur la page de demande de mot de passe oublié"
+
+#: ../larpe/qommon/ident/password.ptl:1065
+msgid ""
+"<p>\n"
+"If you have an account, but have forgotten your password, enter your user "
+"name\n"
+"below and submit a request to change your password.\n"
+"</p>"
+msgstr ""
+"<p>\n"
+"Si vous avez un compte, mais avez oublié votre mot de passe, entrez votre "
+"nom d'utilisateur\n"
+"ci-dessous et validez la requête de changement de mot de passe.\n"
+"</p>"
+
+#: ../larpe/qommon/ident/idp.ptl:27
+msgid "Liberty/SAML2"
+msgstr "Liberty/SAML2"
+
+#: ../larpe/qommon/ident/idp.ptl:94
+msgid "Select the identity provider you want to use."
+msgstr "Choisissez le fournisseur d'identités que vous voulez utiliser."
+
+#: ../larpe/qommon/ident/idp.ptl:114 ../larpe/admin/hosts.ptl:381
+#: ../larpe/admin/users.ptl:62
+msgid "Name"
+msgstr "Nom"
+
+#. #-#-#-#-# tmp.25.idp.ptl.pot (PACKAGE VERSION) #-#-#-#-#
+#. XXX: better error page
+#: ../larpe/qommon/ident/idp.ptl:162 ../larpe/root.ptl:87
+msgid "Unknown Token"
+msgstr "Jeton inconnu"
+
+#: ../larpe/qommon/ident/idp.ptl:176 ../larpe/qommon/ident/idp.ptl:180
+#: ../larpe/qommon/ident/idp.ptl:181 ../larpe/qommon/ident/idp.ptl:707
+msgid "Identity Providers"
+msgstr "Fournisseurs d'identité"
+
+#: ../larpe/qommon/ident/idp.ptl:183 ../larpe/qommon/ident/idp.ptl:221
+#: ../larpe/qommon/ident/idp.ptl:405 ../larpe/admin/hosts.ptl:1328
+#: ../larpe/admin/users.ptl:238 ../larpe/admin/fields_prefill.ptl:125
+#: ../larpe/admin/forms_prefill.ptl:122
+msgid "New"
+msgstr "Nouveau"
+
+#: ../larpe/qommon/ident/idp.ptl:184
+msgid "Create new from remote URL"
+msgstr "Nouveau créé à partir d'une URL distante"
+
+#: ../larpe/qommon/ident/idp.ptl:205
+msgid "Broken"
+msgstr "Cassé"
+
+#: ../larpe/qommon/ident/idp.ptl:223 ../larpe/qommon/ident/idp.ptl:491
+#: ../larpe/qommon/ident/idp.ptl:507 ../larpe/admin/settings.ptl:28
+msgid "Metadata"
+msgstr "Méta-données"
+
+#: ../larpe/qommon/ident/idp.ptl:224 ../larpe/qommon/ident/idp.ptl:508
+#: ../larpe/admin/settings.ptl:29
+msgid "Public Key"
+msgstr "Clé publique"
+
+#: ../larpe/qommon/ident/idp.ptl:225 ../larpe/qommon/ident/idp.ptl:509
+#: ../larpe/admin/settings.ptl:30
+msgid "CA Certificate Chain"
+msgstr "Chaîne de certification"
+
+#: ../larpe/qommon/ident/idp.ptl:226 ../larpe/qommon/ident/idp.ptl:510
+msgid "Client Key and Certificate"
+msgstr "Clé et certificat du client"
+
+#: ../larpe/qommon/ident/idp.ptl:227 ../larpe/qommon/ident/idp.ptl:511
+msgid "Hide this provider from user lists"
+msgstr "Cacher ce fournisseur de la liste présentée à l'utilisateur"
+
+#: ../larpe/qommon/ident/idp.ptl:232 ../larpe/qommon/ident/idp.ptl:233
+#: ../larpe/qommon/ident/idp.ptl:406 ../larpe/qommon/ident/idp.ptl:407
+#: ../larpe/admin/settings.ptl:34 ../larpe/admin/settings.ptl:35
+msgid "New Identity Provider"
+msgstr "Nouveau fournisseur d'identité"
+
+#: ../larpe/qommon/ident/idp.ptl:257 ../larpe/qommon/ident/idp.ptl:306
+#: ../larpe/qommon/ident/idp.ptl:348 ../larpe/qommon/ident/idp.ptl:379
+#: ../larpe/qommon/ident/idp.ptl:591 ../larpe/qommon/ident/idp.ptl:648
+#: ../larpe/qommon/ident/idp.ptl:661 ../larpe/admin/settings.ptl:53
+#: ../larpe/admin/settings.ptl:87
+msgid "Bad metadata"
+msgstr "Mauvaises méta-données"
+
+#: ../larpe/qommon/ident/idp.ptl:313
+msgid "URL to metadata"
+msgstr "URL vers les méta-données"
+
+#: ../larpe/qommon/ident/idp.ptl:330 ../larpe/qommon/ident/idp.ptl:358
+msgid "Resource not found"
+msgstr "Ressource non trouvée"
+
+#: ../larpe/qommon/ident/idp.ptl:332 ../larpe/qommon/ident/idp.ptl:360
+#, python-format
+msgid "HTTP error on retrieval: %s"
+msgstr "Erreur HTTP à la récupération : %s"
+
+#: ../larpe/qommon/ident/idp.ptl:334 ../larpe/qommon/ident/idp.ptl:362
+msgid "Failed to retrieve file"
+msgstr "Échec à la récupération du fichier"
+
+#. ok when provided with a public key -> adding it for real
+#: ../larpe/qommon/ident/idp.ptl:352 ../larpe/qommon/ident/idp.ptl:395
+msgid "URL to public key"
+msgstr "URL de la clé publique"
+
+#: ../larpe/qommon/ident/idp.ptl:373
+msgid "Error in this metadata file"
+msgstr "Fichier de méta-données erroné"
+
+#: ../larpe/qommon/ident/idp.ptl:392
+msgid "File looks like a bad metadata file"
+msgstr "Le fichier de méta-données semble invalide"
+
+#: ../larpe/qommon/ident/idp.ptl:397
+msgid "The metadata file does not embed a public key, please provide it here."
+msgstr ""
+"Ce fichier de meta-données ne contient pas de clé publique, vous devez donc "
+"en fournir une ici."
+
+#: ../larpe/qommon/ident/idp.ptl:484 ../larpe/qommon/ident/idp.ptl:489
+#: ../larpe/qommon/ident/idp.ptl:542 ../larpe/admin/settings.ptl:167
+msgid "Identity Provider"
+msgstr "Fournisseur d'identité"
+
+#: ../larpe/qommon/ident/idp.ptl:500
+msgid "Update from remote URL"
+msgstr "Mettre à jour à partir de l'URL distante"
+
+#: ../larpe/qommon/ident/idp.ptl:516 ../larpe/qommon/ident/idp.ptl:517
+msgid "Edit Identity Provider"
+msgstr "Modifier le fournisseur d'identités"
+
+#: ../larpe/qommon/ident/idp.ptl:536
+msgid "You are about to irrevocably remove this identity provider."
+msgstr "Vous allez définitivement supprimer ce fournisseur d'identités."
+
+#: ../larpe/qommon/ident/idp.ptl:544
+msgid "Deleting"
+msgstr "Suppression"
+
+#: ../larpe/qommon/ident/idp.ptl:546
+msgid "Deleting Identity Provider"
+msgstr "Suppression du fournisseur d'identité"
+
+#: ../larpe/qommon/ident/idp.ptl:650
+msgid "Bad metadata or missing public key"
+msgstr "Mauvaises méta-données ou clé publique manquante"
+
+#: ../larpe/qommon/ident/idp.ptl:673
+msgid "Configure Liberty/SAML identification method"
+msgstr "Configurer la méthode d'identification Liberty/SAML"
+
+#: ../larpe/qommon/ident/idp.ptl:685 ../larpe/qommon/ident/idp.ptl:689
+#: ../larpe/admin/settings.ptl:132
+msgid "Service Provider"
+msgstr "Fournisseur de service"
+
+#: ../larpe/qommon/ident/idp.ptl:685
+msgid "Configure Liberty / SAML 2.0 parameters"
+msgstr "Configurer les paramètres Liberty / SAML 2.0"
+
+#: ../larpe/qommon/ident/idp.ptl:689
+msgid "Configure Liberty parameters"
+msgstr "Configurer les paramètres Liberty"
+
+#: ../larpe/qommon/ident/idp.ptl:696
+msgid "ID-FF 1.2 Service Provider Metadata"
+msgstr "Méta-données ID-FF 1.2 du fournisseur de service"
+
+#: ../larpe/qommon/ident/idp.ptl:697
+msgid "Download Service Provider ID-FF 1.2 Metadata file"
+msgstr ""
+"Télécharger le fichier des méta-données ID-FF 1.2 du fournisseur de service"
+
+#: ../larpe/qommon/ident/idp.ptl:703
+msgid "SAML 2.0 Service Provider Metadata"
+msgstr "Méta-données du fournisseur de service SAML 2.0"
+
+#: ../larpe/qommon/ident/idp.ptl:704
+msgid "Download Service Provider SAML 2.0 Metadata file"
+msgstr ""
+"Télécharger le fichier des méta-données SAML 2.0 du fournisseur de service"
+
+#: ../larpe/qommon/ident/idp.ptl:707
+msgid "Add and remove identity providers"
+msgstr "Ajouter et supprimer des fournisseurs d'identités"
+
+#: ../larpe/qommon/ident/idp.ptl:728
+msgid "Liberty Provider ID"
+msgstr "Identifiant du fournisseur Liberty (Provider ID)"
+
+#: ../larpe/qommon/ident/idp.ptl:731
+msgid "Liberty Base URL"
+msgstr "URL de la racine Liberty"
+
+#: ../larpe/qommon/ident/idp.ptl:733
+msgid "SAML 2.0 Provider ID"
+msgstr "Identifiant du fournisseur SAML 2.0 (Provider ID)"
+
+#: ../larpe/qommon/ident/idp.ptl:737
+msgid "SAML 2.0 Base URL"
+msgstr "URL de la racine SAML 2.0"
+
+#: ../larpe/qommon/ident/idp.ptl:740
+msgid "Provider ID"
+msgstr "Identifiant du fournisseur (Provider ID)"
+
+#: ../larpe/qommon/ident/idp.ptl:742
+msgid "Base URL"
+msgstr "URL de la racine"
+
+#: ../larpe/qommon/ident/idp.ptl:745 ../larpe/admin/settings.ptl:215
+msgid "Organisation Name"
+msgstr "Nom de l'organisation"
+
+#: ../larpe/qommon/ident/idp.ptl:747
+msgid "Signing Private Key"
+msgstr "Clé privée de signature"
+
+#: ../larpe/qommon/ident/idp.ptl:748
+msgid "Signing Public Key"
+msgstr "Clé publique de signature"
+
+#: ../larpe/qommon/ident/idp.ptl:749
+msgid "Encryption Private Key"
+msgstr "Clé privée de chiffrement"
+
+#: ../larpe/qommon/ident/idp.ptl:750
+msgid "Encryption Public Key"
+msgstr "Clé publique de chiffrement"
+
+#: ../larpe/qommon/ident/idp.ptl:753
+msgid "Identity Provider Introduction, Common Domain"
+msgstr "Domaine commun, pour 'Identity Provider Introduction'"
+
+#: ../larpe/qommon/ident/idp.ptl:754 ../larpe/qommon/ident/idp.ptl:759
+msgid "Disabled if empty"
+msgstr "Désactivé si vide"
+
+#: ../larpe/qommon/ident/idp.ptl:758
+msgid "Identity Provider Introduction, URL of Cookie Getter"
+msgstr "Identity Provider Introduction, URL de récupération"
+
+#: ../larpe/qommon/ident/idp.ptl:767 ../larpe/qommon/ident/idp.ptl:768
+#: ../larpe/admin/settings.ptl:222 ../larpe/admin/settings.ptl:223
+msgid "Service Provider Configuration"
+msgstr "Configuration du fournisseur d'identité"
+
+#: ../larpe/qommon/ident/idp.ptl:1025
+msgid "Grab user details with ID-WSF on first logon"
+msgstr ""
+"Récupérer les détails de l'utilisateur avec ID-WSF à la première connexion"
+
+#: ../larpe/qommon/ident/idp.ptl:1029
+msgid "Lasso version is too old for this support."
+msgstr "La version de Lasso est trop vieille pour ce support"
+
+#: ../larpe/qommon/ident/idp.ptl:1070
+msgid "Liberty/SAML2 identity provider"
+msgstr "Fournisseur d'identité Liberty Alliance/SAML2"
+
+#: ../larpe/qommon/backoffice/menu.ptl:39
+msgid "admin"
+msgstr "admin"
+
+#: ../larpe/qommon/backoffice/menu.ptl:59
+#, python-format
+msgid "Back Office of %s"
+msgstr "Back Office de %s"
+
+#: ../larpe/qommon/misc.py:289
+msgid "January"
+msgstr "Janvier"
+
+#: ../larpe/qommon/misc.py:289
+msgid "February"
+msgstr "Février"
+
+#: ../larpe/qommon/misc.py:289
+msgid "March"
+msgstr "Mars"
+
+#: ../larpe/qommon/misc.py:289
+msgid "April"
+msgstr "Avril"
+
+#: ../larpe/qommon/misc.py:290
+msgid "May"
+msgstr "Mai"
+
+#: ../larpe/qommon/misc.py:290
+msgid "June"
+msgstr "Juin"
+
+#: ../larpe/qommon/misc.py:290
+msgid "July"
+msgstr "Juillet"
+
+#: ../larpe/qommon/misc.py:290
+msgid "August"
+msgstr "Août"
+
+#: ../larpe/qommon/misc.py:291
+msgid "September"
+msgstr "Septembre"
+
+#: ../larpe/qommon/misc.py:291
+msgid "October"
+msgstr "Octobre"
+
+#: ../larpe/qommon/misc.py:291
+msgid "November"
+msgstr "Novembre"
+
+#: ../larpe/qommon/misc.py:291
+msgid "December"
+msgstr "Décembre"
+
+#: ../larpe/qommon/misc.py:311
+msgid "Monday"
+msgstr "Lundi"
+
+#: ../larpe/qommon/misc.py:311
+msgid "Tuesday"
+msgstr "Mardi"
+
+#: ../larpe/qommon/misc.py:311
+msgid "Wednesday"
+msgstr "Mercredi"
+
+#: ../larpe/qommon/misc.py:312
+msgid "Thursday"
+msgstr "Jeudi"
+
+#: ../larpe/qommon/misc.py:312
+msgid "Friday"
+msgstr "Vendredi"
+
+#: ../larpe/qommon/misc.py:312
+msgid "Saturday"
+msgstr "Samedi"
+
+#: ../larpe/qommon/misc.py:312
+msgid "Sunday"
+msgstr "Dimanche"
+
+#: ../larpe/qommon/publisher.py:97
+msgid "This feature is not yet implemented."
+msgstr "Cette fonctionnalité n'a pas encore été implémentée."
+
+#: ../larpe/qommon/publisher.py:98
+msgid "Sorry"
+msgstr "Désolé"
+
+#: ../larpe/qommon/publisher.py:119
+msgid ""
+"The server encountered an internal error and was unable to complete your "
+"request."
+msgstr ""
+"Une erreur s'est produite sur le serveur; votre requête n'a ainsi pas pu "
+"complètement être traîtée."
+
+#: ../larpe/qommon/publisher.py:120
+msgid "Internal Server Error"
+msgstr "Erreur interne du serveur"
+
+#: ../larpe/idwsf2.ptl:50
+msgid "Failed connecting to the original site."
+msgstr "Échec de connexion au site d'origine"
+
+#: ../larpe/idwsf2.ptl:61
+msgid "Failed getting attributes from the attribute provider."
+msgstr ""
+"Échec lors de la récupération d'attributs depuis le fournisseur d'attributs."
+
+#: ../larpe/idwsf2.ptl:64
+msgid "Failed getting attributes for an unknown reason."
+msgstr "Échec lors de la récupération d'attributs pour une raison inconnue"
+
+#: ../larpe/qommon/form.py:38
+msgid "required field"
+msgstr "champ obligatoire"
+
+#: ../larpe/qommon/form.py:74
+msgid ""
+"The form you have submitted is invalid. Most likely it has been "
+"successfully submitted once already. Please review the form data and submit "
+"the form again."
+msgstr ""
+"Le formulaire que vous avez transmis n'est pas valide. Une raison probable "
+"est qu'il ait déjà été transmis une fois. Vérifiez les données du "
+"formulaire avant de le soumettre à nouveau."
+
+#: ../larpe/qommon/form.py:78
+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."
+
+#: ../larpe/qommon/form.py:284 ../larpe/qommon/form.py:286
+#: ../larpe/qommon/form.py:288
+msgid "must be a valid email address"
+msgstr "doit être une adresse électronique valide"
+
+#: ../larpe/qommon/form.py:298
+msgid "invalid address domain"
+msgstr "adresse du domaine invalide"
+
+#: ../larpe/qommon/form.py:314
+msgid "Prefill"
+msgstr "Préremplir"
+
+#: ../larpe/qommon/form.py:329 ../larpe/qommon/form.py:570
+msgid "wrong format"
+msgstr "format invalide"
+
+#: ../larpe/qommon/form.py:377 ../larpe/qommon/form.py:381
+msgid "invalid date"
+msgstr "date invalide"
+
+#: ../larpe/qommon/form.py:384
+#, python-format
+msgid "invalid date: date must be on or after %s"
+msgstr "date invalide ; la date doit être antérieure à %s"
+
+#: ../larpe/qommon/form.py:387
+#, python-format
+msgid "invalid date; date must be on or before %s"
+msgstr "date invalide ; la date doit être postérieure à %s"
+
+#: ../larpe/qommon/form.py:419
+msgid "Previous Year"
+msgstr "Année précédente"
+
+#: ../larpe/qommon/form.py:420
+msgid "Previous Month"
+msgstr "Mois précédent"
+
+#: ../larpe/qommon/form.py:421
+msgid "Next Year"
+msgstr "Année Suivante"
+
+#: ../larpe/qommon/form.py:422
+msgid "Next Month"
+msgstr "Mois suivant"
+
+#: ../larpe/qommon/form.py:423
+msgid "Close"
+msgstr "Fermer"
+
+#: ../larpe/qommon/form.py:424
+msgid "Choose Date"
+msgstr "Choisir la date"
+
+#: ../larpe/qommon/form.py:466
+msgid "invalid regular expression"
+msgstr "expression rationnelle invalide"
+
+#: ../larpe/qommon/form.py:526
+#, python-format
+msgid "You must select at most %d answers."
+msgstr "Vous devez sélectionner au maximum %d réponses."
+
+#: ../larpe/qommon/form.py:579
+msgid "must start with http:// or https:// and have a domain name"
+msgstr "doit commencer par http:// ou https:// et avoir un nom de domaine"
+
+#: ../larpe/qommon/form.py:607
+msgid "client error: access forbidden (error 403)"
+msgstr "erreur du client : accès interdit (erreur 403)"
+
+#: ../larpe/qommon/form.py:609
+msgid "client error: page not found (error 404)"
+msgstr "erreur du client : page non trouvée (erreur 404)"
+
+#: ../larpe/qommon/form.py:611
+#, python-format
+msgid "client error: %(reason)s (error %(code)s)"
+msgstr "erreur du client : %(reason)s (erreur %(code)s)"
+
+#: ../larpe/qommon/form.py:614
+#, python-format
+msgid "server error: %(reason)s (error %(code)s)"
+msgstr "erreur du serveur : %(reason)s (erreur %(code)s)"
+
+#: ../larpe/qommon/form.py:628
+msgid ""
+"must start with http:// or https:// and have a domain name or start with /"
+msgstr ""
+"doit commencer par http:// ou https:// et avoir un nom de domaine ou "
+"commencer par /"
+
+#: ../larpe/qommon/form.py:646
+msgid "times"
+msgstr "fois"
+
+#: ../larpe/qommon/form.py:646
+msgid "plus"
+msgstr "plus"
+
+#: ../larpe/qommon/form.py:646
+msgid "minus"
+msgstr "moins"
+
+#: ../larpe/qommon/form.py:655
+#, python-format
+msgid "What is the result of %(a)d %(op)s %(b)d?"
+msgstr "Quel est le résultat de %(a)d %(op)s %(b)d ?"
+
+#: ../larpe/qommon/form.py:657
+msgid ""
+"Please answer this simple mathematical question as proof you are not a bot."
+msgstr ""
+"Répondez à cette simple question mathématique comme preuve que vous n'êtes "
+"pas un robot'"
+
+#: ../larpe/qommon/form.py:666
+msgid "wrong answer"
+msgstr "mauvaise réponse"
+
+#: ../larpe/root.ptl:27
+msgid "Welcome to Larpe reverse proxy"
+msgstr "Bienvenue sur le reverse proxy Larpe"
+
+#: ../larpe/root.ptl:29
+msgid "Configure Larpe"
+msgstr "Configurer Larpe"
+
+#: ../larpe/root.ptl:62 ../larpe/root.ptl:71 ../larpe/admin/users.ptl:142
+#: ../larpe/admin/users.ptl:145 ../larpe/admin/users.ptl:146
+#: ../larpe/admin/users.ptl:154 ../larpe/admin/users.ptl:179
+#: ../larpe/admin/users.ptl:181 ../larpe/admin/users.ptl:222
+msgid "Identification Token"
+msgstr "Jeton d'identification"
+
+#: ../larpe/root.ptl:73
+msgid "Please enter your identification token. "
+msgstr "Veuillez saisir votre jeton d'identification. "
+
+#: ../larpe/root.ptl:74 ../larpe/site_authentication.ptl:85
+msgid ""
+"Your local account will be federated with your Liberty Alliance account."
+msgstr "Votre compte local sera fédéré avec votre compte Liberty Alliance."
+
+#: ../larpe/qommon/template.ptl:185
+msgid "It has been sent to the site administrator for analyse."
+msgstr "Elle a été envoyée à l'administrateur du site pour analyse."
+
+#: ../larpe/qommon/template.ptl:189
+#, python-format
+msgid "Continue to %s"
+msgstr "Continuer vers %s"
+
+#: ../larpe/qommon/template.ptl:192
+msgid "View Error Details"
+msgstr "Afficher le détail de l'erreur"
+
+#: ../larpe/qommon/template.ptl:297
+#, python-format
+msgid "Logged in as %s."
+msgstr "Identifié en tant que %s."
+
+#: ../larpe/qommon/errors.ptl:16 ../larpe/qommon/errors.ptl:20
+msgid "Access Forbidden"
+msgstr "Accès interdit"
+
+#: ../larpe/qommon/errors.ptl:17 ../larpe/qommon/errors.ptl:21
+msgid "the homepage"
+msgstr "la page d'accueil"
+
+#: ../larpe/qommon/errors.ptl:45
+msgid "Oops, the server borked severely"
+msgstr "Oups, le serveur s'est méchamment planté"
+
+#: ../larpe/qommon/errors.ptl:48
+msgid ""
+"This is bad bad bad; perhaps you will have more luck if you retry in a few "
+"minutes ? "
+msgstr ""
+"C'est mal mal mal; peut-être aurez-vous plus de chance en réessayant dans "
+"quelques minutes ? "
+
+#: ../larpe/qommon/errors.ptl:50
+msgid ""
+"Alternatively you could harass the webmaster (who may have been emailed "
+"automatically with this incident but you can't be sure about this."
+msgstr ""
+"Autrement vous pouvez harceler le webmestre (qui devrait avoir été prévenu "
+"automatiquement de cet incident par un courriel, mais en êtes vous sûr ?)."
+
+#: ../larpe/qommon/errors.ptl:80
+msgid "Page not found"
+msgstr "Page non trouvée"
+
+#: ../larpe/qommon/errors.ptl:82
+msgid ""
+"The requested link does not exist on this site. If you arrived here by "
+"following a link from an external page, please inform that page's maintainer."
+msgstr ""
+"La page demandée n'existe pas sur ce site. Si vous êtes arrivé ici en "
+"suivant un lien depuis un autre site, veuillez informer le propriétaire de "
+"cette autre page."
+
+#: ../larpe/qommon/saml2.ptl:37 ../larpe/qommon/saml2.ptl:62
+#: ../larpe/qommon/saml2.ptl:76 ../larpe/qommon/saml2.ptl:204
+#: ../larpe/qommon/saml2.ptl:230 ../larpe/saml2.ptl:32 ../larpe/saml2.ptl:46
+msgid "SAML 2.0 support not yet configured."
+msgstr "Le support SAML 2.0 n'est pas encore configuré"
+
+#: ../larpe/qommon/saml2.ptl:83 ../larpe/saml2.ptl:53
+msgid "Missing SAML Artifact"
+msgstr "Artifact SAML manquant"
+
+#: ../larpe/qommon/saml2.ptl:86
+msgid "Authentication request initiated by an unaffiliated provider."
+msgstr "Requête d'authentification initié par un fournisseur non-affilié"
+
+#: ../larpe/qommon/saml2.ptl:107
+msgid "Authentication failure; federation not found"
+msgstr "Erreur d'authentification: pas de fédération"
+
+#: ../larpe/qommon/saml2.ptl:208
+msgid "No SAML Response"
+msgstr "Pas de réponse SAML"
+
+#: ../larpe/qommon/saml2.ptl:216 ../larpe/qommon/saml2.ptl:243
+msgid "Request from unknown provider ID"
+msgstr "Requête d'un fournisseur inconnu"
+
+#: ../larpe/qommon/saml2.ptl:218 ../larpe/qommon/saml2.ptl:220
+#: ../larpe/qommon/saml2.ptl:222 ../larpe/qommon/saml2.ptl:245
+#: ../larpe/qommon/saml2.ptl:247 ../larpe/qommon/saml2.ptl:249
+msgid "Error checking signature"
+msgstr "Erreur à la vérification de signature"
+
+#: ../larpe/qommon/saml2.ptl:235
+msgid "No SAML Response in query string"
+msgstr "Pas de réponse SAML dans la 'query string'"
+
+#: ../larpe/qommon/http_response.py:91
+msgid "registered"
+msgstr "enregistré"
+
+#: ../larpe/qommon/http_response.py:104
+msgid "running"
+msgstr "en fonctionnement"
+
+#: ../larpe/qommon/http_response.py:108
+msgid "completed"
+msgstr "terminé"
+
+#: ../larpe/admin/root.ptl:44 ../larpe/admin/hosts.ptl:1289
+#: ../larpe/admin/hosts.ptl:1290 ../larpe/admin/hosts.ptl:1327
+#: ../larpe/admin/hosts.ptl:1334
+msgid "Hosts"
+msgstr "Hôtes"
+
+#: ../larpe/admin/root.ptl:45 ../larpe/admin/users.ptl:193
+#: ../larpe/admin/users.ptl:194 ../larpe/admin/users.ptl:237
+#: ../larpe/admin/users.ptl:271
+msgid "Users"
+msgstr "Utilisateurs"
+
+#: ../larpe/admin/root.ptl:48
+msgid "Liberty Alliance Reverse Proxy"
+msgstr "Relais inverse Liberty Alliance"
+
+#: ../larpe/admin/settings.ptl:128
+msgid "Liberty Alliance & SAML 2.0 Service Provider"
+msgstr "Fournisseur de service Liberty Alliance et SAML 2.0"
+
+#: ../larpe/admin/settings.ptl:130
+msgid "Liberty Alliance Service Provider"
+msgstr "Fournisseur de service Liberty Alliance"
+
+#: ../larpe/admin/settings.ptl:132
+msgid "Configure Larpe as a Service Provider"
+msgstr "Configurer Larpe en tant que fournisseur de service"
+
+#: ../larpe/admin/settings.ptl:142 ../larpe/admin/hosts.ptl:898
+msgid "SAML 2.0 Metadata"
+msgstr "Méta-données SAML 2.0"
+
+#: ../larpe/admin/settings.ptl:143
+msgid "Download SAML 2.0 metadata file for Larpe"
+msgstr "Télécharger le fichier des méta-données SAML 2.0 pour Larpe"
+
+#: ../larpe/admin/settings.ptl:149 ../larpe/admin/hosts.ptl:903
+msgid "ID-FF 1.2 Metadata"
+msgstr "Méta-données ID-FF 1.2"
+
+#: ../larpe/admin/settings.ptl:150
+msgid "Download ID-FF 1.2 metadata file for Larpe"
+msgstr "Télécharger le fichier des méta-données ID-FF 1.2 pour Larpe"
+
+#: ../larpe/admin/settings.ptl:156 ../larpe/admin/hosts.ptl:912
+msgid "Public key"
+msgstr "Clé publique"
+
+#: ../larpe/admin/settings.ptl:157 ../larpe/admin/hosts.ptl:913
+msgid "Download SSL Public Key file"
+msgstr "Télécharger le fichier de clé SSL publique"
+
+#: ../larpe/admin/settings.ptl:160
+msgid "Liberty Alliance & SAML 2.0 Identity Provider"
+msgstr "Fournisseur d'identité Liberty Alliance et SAML 2.0"
+
+#: ../larpe/admin/settings.ptl:162
+msgid "Liberty Alliance Identity Provider"
+msgstr "Fournisseur d'identité Liberty Alliance"
+
+#: ../larpe/admin/settings.ptl:167
+msgid "Configure an identity provider"
+msgstr "Configurer un fournisseur d'identité"
+
+#: ../larpe/admin/settings.ptl:171
+msgid "Identity Provider metadatas"
+msgstr "Méta-données du fournisseur d'identité"
+
+#: ../larpe/admin/settings.ptl:171
+msgid "See current identity provider metadatas"
+msgstr "Voir les méta-données du fournisseur d'identité actuel"
+
+#: ../larpe/admin/settings.ptl:175
+msgid "Global parameters for the sites"
+msgstr "Paramètres globaux des sites"
+
+#: ../larpe/admin/settings.ptl:179 ../larpe/admin/settings.ptl:302
+#: ../larpe/admin/settings.ptl:303
+msgid "Domain name"
+msgstr "Nom de domaine"
+
+#: ../larpe/admin/settings.ptl:179
+msgid "Configure the base domain name for the sites"
+msgstr "Configurer le nom de domaine de base pour les sites"
+
+#: ../larpe/admin/settings.ptl:181 ../larpe/admin/settings.ptl:348
+#: ../larpe/admin/settings.ptl:349
+msgid "Apache 2 configuration generation"
+msgstr "Génération de la configuration d'Apache 2"
+
+#: ../larpe/admin/settings.ptl:181
+msgid "Customise Apache 2 configuration generation"
+msgstr "Personnaliser la génération de la configuration d'Apache 2"
+
+#: ../larpe/admin/settings.ptl:183
+msgid "Connect to the sites through a web proxy"
+msgstr "Se connecter aux sites en passant par un mandataire (proxy)"
+
+#: ../larpe/admin/settings.ptl:186
+msgid "Customisation"
+msgstr "Personnalisation"
+
+#: ../larpe/admin/settings.ptl:190
+msgid "Configure site language"
+msgstr "Configurer la langue du site"
+
+#: ../larpe/admin/settings.ptl:192
+msgid "Configure email settings"
+msgstr "Configurer les paramètres des courriels"
+
+#: ../larpe/admin/settings.ptl:195
+msgid "Misc"
+msgstr "Divers"
+
+#: ../larpe/admin/settings.ptl:199
+msgid "Configure options useful for debugging"
+msgstr "Configurer les options utiles au debogage"
+
+#: ../larpe/admin/settings.ptl:318
+msgid "Domain name for the sites"
+msgstr "Noms de domaine pour les sites"
+
+#. TODO: Add the option "Both" and handle it in hosts configuration
+#: ../larpe/admin/settings.ptl:321
+msgid "Use HTTP or HTTPS"
+msgstr "Utiliser HTTP ou HTTPS"
+
+#: ../larpe/admin/settings.ptl:323
+msgid "Same as the site"
+msgstr "Identique au site d'origine"
+
+#: ../larpe/admin/settings.ptl:341
+msgid ""
+"Automatically generate Apache 2 configuration for new hosts and reload "
+"Apache 2 after changes"
+msgstr ""
+"Générer automatiquement la configuration Apache 2 pour les nouveaux hôtes et "
+"recharger Apache 2 après les changements"
+
+#: ../larpe/admin/hosts.ptl:32
+msgid ""
+"You must either choose a different hostname from Larpe or specify a reversed "
+"directory"
+msgstr ""
+"Vous devez soit choisir un nom d'hôte différent de celui de Larpe, soit "
+"spécifier un répertoire inversé"
+
+#: ../larpe/admin/hosts.ptl:79 ../larpe/admin/hosts.ptl:101
+msgid "Basic configuration"
+msgstr "Configuration de base"
+
+#: ../larpe/admin/hosts.ptl:80
+msgid "Need domain name configuration"
+msgstr "Nécessite la configuration du nom de domaine"
+
+#: ../larpe/admin/hosts.ptl:81
+#, python-format
+msgid ""
+"Before configuring hosts, you must\n"
+"<a href=\"../../../settings/domain_names\">setup a global domain name</a> "
+"in\n"
+"%(settings)s menu."
+msgstr ""
+"Avant des configurer les sites, vous devez <a href=\"../../../settings/"
+"domain_names\">définir un nom de domaine global</a> dans le menu %(settings)"
+"s."
+
+#: ../larpe/admin/hosts.ptl:102
+msgid "Step 1 - Basic configuration"
+msgstr "Étape 1 - Configuration de base"
+
+#: ../larpe/admin/hosts.ptl:111
+msgid "Original site root address"
+msgstr "Adresse racine du site d'origine"
+
+#: ../larpe/admin/hosts.ptl:113
+msgid ""
+"If your site address is http://test.org/index.php, put http://test.org/ here"
+msgstr ""
+"Si l'adresse de votre site est http://test.org/index.php, mettez http://test."
+"org/ ici"
+
+#: ../larpe/admin/hosts.ptl:116
+msgid "Use a proxy"
+msgstr "Utiliser un mandataire (proxy)"
+
+#: ../larpe/admin/hosts.ptl:117
+msgid ""
+"Uncheck it if Larpe doesn't need to use the proxy to connect to this site"
+msgstr ""
+"Décocher cette case si Larpe ne doit pas utiliser un mandataire (proxy) pour "
+"se connecter à ce site"
+
+#: ../larpe/admin/hosts.ptl:121
+msgid ""
+"If Larpe needs to use a proxy to connect to this site, you must first "
+"configure\n"
+" it in <a href=\"../../../settings/proxy\">global proxy parameters</a>."
+msgstr ""
+"Si Larpe doit utiliser un mandataire (proxy) pour se connecter à ce site, "
+"vous devez d'abord le configurer dans les <a href=\"../../../settings/proxy"
+"\">paramètres globaux du mandataire</a>."
+
+#: ../larpe/admin/hosts.ptl:125 ../larpe/admin/hosts.ptl:315
+#: ../larpe/admin/hosts.ptl:433 ../larpe/admin/hosts.ptl:457
+#: ../larpe/admin/hosts.ptl:538 ../larpe/admin/hosts.ptl:574
+#: ../larpe/admin/hosts.ptl:853 ../larpe/admin/hosts.ptl:875
+#: ../larpe/admin/hosts.ptl:963
+msgid "Terminate"
+msgstr "Terminer"
+
+#: ../larpe/admin/hosts.ptl:331
+msgid "Check site address and name"
+msgstr "Vérifier l'adresse et le nom du site"
+
+#: ../larpe/admin/hosts.ptl:332
+msgid "Step 2 - Check the new site address works"
+msgstr "Étape 2 - Vérifier que la nouvelle adresse du site fonctionne"
+
+#: ../larpe/admin/hosts.ptl:334
+msgid "DNS configuration"
+msgstr "Configuration DNS"
+
+#: ../larpe/admin/hosts.ptl:336
+msgid ""
+"Before opening the following link, ensure you have configured your DNS\n"
+"for this address. If you don't have a DNS server and you just want to test "
+"Larpe, add this\n"
+"domain name in the file \"/etc/hosts\"."
+msgstr ""
+"Avant d'ouvrir le lien suivant, assurez vous d'avoir configuré votre DNS "
+"pour cette adresse. Si vous n'avez pas de serveur DNS et que vous souhaitez "
+"seulement tester Larpe, ajoutez ce nom de domaine dans le fichier \"/etc/"
+"hosts\"."
+
+#: ../larpe/admin/hosts.ptl:340
+#, python-format
+msgid ""
+"Then you can open this link in a new window or tab and see if your site\n"
+"is displayed. If it's ok, you can click the \"%(next)s\" button. Otherwise, "
+"click the \"%(previous)s\"\n"
+"button and check your settings."
+msgstr ""
+"Vous pouvez ensuite ouvrir ce lien dans une nouvelle fenêtre ou un nouvel "
+"onglet et voir si votre site est affiché. Si cela fonctionne, vous pouvez "
+"cliquer sur le bouton \"%(next)s\". Sinon, cliquez sur le bouton\"%(previous)"
+"s\" et vérifiez vos paramètres."
+
+#: ../larpe/admin/hosts.ptl:344
+msgid "Site adress and name"
+msgstr "Adresse et nom du site"
+
+#: ../larpe/admin/hosts.ptl:346
+msgid "The new address of this site is "
+msgstr "La nouvelle adresse du site est "
+
+#: ../larpe/admin/hosts.ptl:348
+#, python-format
+msgid "The name of this site is \"%s\"."
+msgstr "Le nom du site est \"%s\"."
+
+#: ../larpe/admin/hosts.ptl:349
+msgid ""
+"You can also <a href=\"modify_site_address_and_name\">\n"
+"modify the address or the name of this site</a>"
+msgstr ""
+"Vous pouvez aussi <a href=\"modify_site_address_and_name\">modifier "
+"l'adresse ou le nom du site</a>"
+
+#: ../larpe/admin/hosts.ptl:365
+msgid "An host with the same name already exists"
+msgstr "Un hôte avec le même nom existe déjà"
+
+#: ../larpe/admin/hosts.ptl:372 ../larpe/admin/hosts.ptl:373
+msgid "Modify site address and name"
+msgstr "Modifier l'adresse et le nom du site"
+
+#: ../larpe/admin/hosts.ptl:379
+msgid "Address"
+msgstr "Adresse"
+
+#: ../larpe/admin/hosts.ptl:418
+msgid "Authentication and logout"
+msgstr "Authentification et déconnexion"
+
+#: ../larpe/admin/hosts.ptl:419
+msgid "Step 3 - Configure authentication and logout pages"
+msgstr "Étape 3 - Configurer les pages d'authentification et de déconnexion"
+
+#: ../larpe/admin/hosts.ptl:425 ../larpe/admin/hosts.ptl:954
+msgid "Authentication form page address"
+msgstr "Adresse de la page du formulaire d'authentification"
+
+#: ../larpe/admin/hosts.ptl:426
+msgid "Address of a page on the site which contains the authentication form"
+msgstr "Adresse d'une page du site contenant le formulaire d'authentification"
+
+#: ../larpe/admin/hosts.ptl:428
+msgid "Logout address"
+msgstr "Adresse de déconnexion"
+
+#: ../larpe/admin/hosts.ptl:429
+msgid "Address of the logout link on the site"
+msgstr "Adresse du lien de déconnexion du site"
+
+#: ../larpe/admin/hosts.ptl:445
+msgid "(computed automatically)"
+msgstr "(calculé automatiquement)"
+
+#: ../larpe/admin/hosts.ptl:467
+msgid "Auto detected configuration"
+msgstr "Configuration détectée automatiquement"
+
+#: ../larpe/admin/hosts.ptl:468
+msgid ""
+"Step 4 - Check automatically detected configuration for the authentication "
+"form"
+msgstr ""
+"Étape 4 - Vérifier la configuration détectée automatiquement pour "
+"l'authentification"
+
+#: ../larpe/admin/hosts.ptl:471
+msgid "Address where the authentication form must be sent"
+msgstr "Adresse à laquelle le formulaire d'authentification doit être envoyé"
+
+#: ../larpe/admin/hosts.ptl:472
+msgid "Name of the login field"
+msgstr "Nom du champ identifiant"
+
+#: ../larpe/admin/hosts.ptl:473
+msgid "Name of the password field"
+msgstr "Nom du champ mot de passe"
+
+#: ../larpe/admin/hosts.ptl:492
+msgid ""
+"The following authentication form parameters have been detected. If they "
+"look right, you can go to the next step.\n"
+"If you think they are wrong, go back and check your settings then try "
+"again.\n"
+msgstr ""
+"Les paramètres d'authentification suivants ont été détectés. Si ils semblent "
+"corrects, vous pouvez passer à l'étape suivante.\n"
+"Si vous pensez qu'ils sont incorrects, retournez à l'étape précédente et "
+"vérifiez vos paramètres, puis essayez à nouveau.\n"
+
+#: ../larpe/admin/hosts.ptl:497
+msgid ""
+"The following authentication form parameters in red haven't been correctly "
+"detected. Go back and check\n"
+"your settings then try again.\n"
+msgstr ""
+"Les paramètres d'authentification suivants en rouge n'ont pas été détectés "
+"correctement. Retournez à une étape précédente vérifiez vos paramètres, puis "
+"essayez à nouveau.\n"
+
+#: ../larpe/admin/hosts.ptl:518 ../larpe/admin/hosts.ptl:1234
+msgid "Credentials"
+msgstr "Identifiant et mot de passe"
+
+#: ../larpe/admin/hosts.ptl:519
+msgid "Step 5 - Fill in a valid username/password for this site"
+msgstr "Étape 5 - Nom d'utilisateur et mot de passe pour ce site"
+
+#: ../larpe/admin/hosts.ptl:584
+msgid "Check authentication"
+msgstr "Vérifier l'authentification"
+
+#: ../larpe/admin/hosts.ptl:585
+msgid "Step 6 - Check the authentication process"
+msgstr "Étape 6 - Vérifier la procédure d'authentification"
+
+#: ../larpe/admin/hosts.ptl:604
+msgid "Authentication succeeded ! You can go to the next step."
+msgstr ""
+"L'authentification a réussi ! Vous pouvez continuer à l'étape suivante."
+
+#: ../larpe/admin/hosts.ptl:606
+msgid "Authentication has failed. To resolve this problem, you can :"
+msgstr "L'authentification a échoué. Pour résoudre ce problème, vous pouvez :"
+
+#: ../larpe/admin/hosts.ptl:609
+msgid "Try authentication again"
+msgstr "Retenter une authentification"
+
+#: ../larpe/admin/hosts.ptl:611
+msgid "See the response of the authentication requests"
+msgstr "Voir la réponse de la requête d'authentification"
+
+#: ../larpe/admin/hosts.ptl:613 ../larpe/admin/hosts.ptl:695
+msgid "Modify the parameters of the authentication requests"
+msgstr "Modifier les paramètres des requêtes d'authentification"
+
+#: ../larpe/admin/hosts.ptl:615
+msgid "Change the way Larpe detects the authentication is successful or not"
+msgstr ""
+"Changer la façon donc Larpe détecte que l'authentification a réussi ou pas"
+
+#: ../larpe/admin/hosts.ptl:616
+msgid "Go back and change your username and/or password"
+msgstr "Revenir en arrière et changer vos nom d'utilisateur et/ou mot de passe"
+
+#: ../larpe/admin/hosts.ptl:622 ../larpe/admin/hosts.ptl:623
+msgid "Authentication response"
+msgstr "Réponse de l'authentification"
+
+#: ../larpe/admin/hosts.ptl:625
+msgid "Response of the request with good credentials"
+msgstr "Réponse de la requête avec un bon couple identifiant/mot de passe"
+
+#: ../larpe/admin/hosts.ptl:628 ../larpe/admin/hosts.ptl:639
+msgid "HTTP status code"
+msgstr "Code de status HTTP"
+
+#: ../larpe/admin/hosts.ptl:633 ../larpe/admin/hosts.ptl:644
+msgid "See HTML page"
+msgstr "Voir la page HTML"
+
+#: ../larpe/admin/hosts.ptl:636
+msgid "Response of the request with bad credentials"
+msgstr "Réponse de la requête avec un mauvais couple identifiant/mot de passe"
+
+#: ../larpe/admin/hosts.ptl:665 ../larpe/admin/hosts.ptl:666
+msgid "Authentication success criteria"
+msgstr "Critères de succès de l'authentification"
+
+#: ../larpe/admin/hosts.ptl:672
+msgid "Authentication system of the original site"
+msgstr "Système d'authentification du site d'origine"
+
+#: ../larpe/admin/hosts.ptl:674
+msgid "Check the existence of a password field"
+msgstr "Vérifier l'existence d'un champ mot de passe"
+
+#: ../larpe/admin/hosts.ptl:675
+msgid "Match some text to detect an authentication failure"
+msgstr "Trouver du texte pour détecter un échec d'authentification"
+
+#: ../larpe/admin/hosts.ptl:680
+msgid "Text to match in case of authentication failure"
+msgstr "Texte à trouver dans le cas d'un échec d'authentification"
+
+#: ../larpe/admin/hosts.ptl:694
+msgid "Authentication request"
+msgstr "Requête d'authentification"
+
+#: ../larpe/admin/hosts.ptl:699
+msgid "Modify POST parameters"
+msgstr "Modifier les parameters du POST"
+
+#: ../larpe/admin/hosts.ptl:699
+msgid ""
+"Configure the form attributes that will be sent within the authentication "
+"POST requests"
+msgstr ""
+"Configurer les attributes du formulaire qui seront envoyés dans les requêtes "
+"POST d'authentification"
+
+#: ../larpe/admin/hosts.ptl:701
+msgid "Modify HTTP headers"
+msgstr "Modifier les entêtes HTTP"
+
+#: ../larpe/admin/hosts.ptl:701
+msgid "Configure the HTTP headers of the authentication requests made by Larpe"
+msgstr ""
+"Configurer les entêtes HTTP des requêtes d'authentification envoyées par "
+"Larpe"
+
+#: ../larpe/admin/hosts.ptl:715
+msgid "POST parameters"
+msgstr "Paramètres POST"
+
+#: ../larpe/admin/hosts.ptl:716
+msgid "Configure POST parameters"
+msgstr "Configurer les paramètres POST"
+
+#: ../larpe/admin/hosts.ptl:718
+msgid ""
+"Here are the detected form fields that will be sent as parameters of the\n"
+"authentication POST request. You can desactivate some or all of them, or "
+"change their value."
+msgstr ""
+"Voici les champs du formulaire détectés et qui seront envoyés en tant que "
+"paramètres de la requête POST\n"
+"d'authentification. Vous pouvez désactiver certains ou tous ces champs, ou "
+"modifier leur valeur."
+
+#: ../larpe/admin/hosts.ptl:756
+msgid "HTTP headers"
+msgstr "Entêtes HTTP"
+
+#: ../larpe/admin/hosts.ptl:757
+msgid "Configure HTTP headers"
+msgstr "Configurer les entêtes HTTP"
+
+#: ../larpe/admin/hosts.ptl:759
+msgid ""
+"Here are the HTTP headers that will be sent within the authentication\n"
+"POST request. You can desactivate some or all of them, or change their value."
+msgstr ""
+"Voici les entêtes HTTP qui seront envoyés dans la requête POST\n"
+"d'authentification. Vous pouvez désactiver certains ou tous ces champs, ou "
+"modifier leur valeur."
+
+#: ../larpe/admin/hosts.ptl:772
+msgid ""
+"The headers \"Host\", \"Accept-Encoding\" and \"Content-Length\" will also "
+"automatically be sent."
+msgstr ""
+"Les entêtes \"Host\", \"Accept-Encoding\" et \"Content-Length\" seront "
+"également envoyées automatiquement."
+
+#: ../larpe/admin/hosts.ptl:775
+msgid ""
+"As Larpe uses a proxy for this site, the headers \"Proxy-Authorization\",\n"
+"\"Proxy-Connection\" and \"Keep-Alive\" will be sent as well."
+msgstr ""
+"Comme Larpe utilise un mandataire (proxy) pour ce site, les entêtes\"Proxy-"
+"Authorization\", \"Proxy-Connection\" et \"Keep-Alive\" seront aussienvoyées."
+
+#: ../larpe/admin/hosts.ptl:825
+msgid "SSO initiation"
+msgstr "Initiation de l'authentification unique"
+
+#: ../larpe/admin/hosts.ptl:826
+msgid "Step 7 - Configure how a Single Sign On can be initiated"
+msgstr ""
+"Étape 7 - Configurer la façon dont l'authentification unique peut être "
+"initiée"
+
+#: ../larpe/admin/hosts.ptl:828
+msgid ""
+"Most sites use one of the following 2 ways to allow users to initialise an "
+"authentication :"
+msgstr ""
+"La plupart des sites utilisent une des 2 méthodes suivantes pour permettre "
+"aux utilisateursd'initier une authentification :"
+
+#: ../larpe/admin/hosts.ptl:831
+msgid ""
+"The site has a single authentication page. It redirects users to this page "
+"when\n"
+"they click a \"Login\" button or try to access a page which require users to "
+"be authenticated."
+msgstr ""
+"Le site a une page unique d'authentification. Il redirige les utilisateurs "
+"vers cettepas lorsqu'ils cliquent sur un bouton \"Connexion\" ou essaient "
+"d'accéder à une pagequi nécessitent que les utilisateurs soient authentifiés"
+
+#: ../larpe/admin/hosts.ptl:834
+msgid ""
+"The site includes an authentication form in most or all of his pages. Users "
+"can\n"
+"authenticate on any of these pages, and don't need to be redirected to a "
+"separate authentication page."
+msgstr ""
+"Le site inclut un formulaire d'authentification dans toutes ou la plupart de "
+"ses pages.Les utilisateurs peuvent s'authentifier sur n'importe laquelle de "
+"ces pages, et n'ont pasbesoin d'être redirigés vers une page "
+"d'authentification séparée"
+
+#: ../larpe/admin/hosts.ptl:839
+msgid "Select the way your site works :"
+msgstr "Sélectionner la façon dont votre site fonctionne :"
+
+#: ../larpe/admin/hosts.ptl:847
+msgid "The site has a single authentication page"
+msgstr "Le site a une page d'authentification séparée"
+
+#: ../larpe/admin/hosts.ptl:848
+msgid "The site includes an authentication form in most or all pages"
+msgstr ""
+"Le site inclut un formulaire d'authentification imbriqué dans les pages"
+
+#: ../larpe/admin/hosts.ptl:885
+msgid "Metadatas"
+msgstr "Méta-données"
+
+#: ../larpe/admin/hosts.ptl:886
+#, python-format
+msgid "Step 8 - Metadatas of %(site_name)s"
+msgstr "Étape 8- Méta-données de %(site_name)s"
+
+#: ../larpe/admin/hosts.ptl:889
+msgid ""
+"Download the metadatas and the public key for this site and\n"
+"upload them on your identity provider in order to use Liberty Alliance "
+"features."
+msgstr ""
+"Téléchargez les méta-données et la clé publique de ce site et\n"
+"fournissez-les au fournisseur d'identité afin d'utiliser les fonctionnalités "
+"Liberty Alliance"
+
+#: ../larpe/admin/hosts.ptl:899
+msgid "Download SAML 2.0 metadata file"
+msgstr "Télécharger le fichier des méta-données SAML 2.0"
+
+#: ../larpe/admin/hosts.ptl:904
+msgid "Download ID-FF 1.2 metadata file"
+msgstr "Télécharger le fichier des méta-données ID-FF 1.2"
+
+#: ../larpe/admin/hosts.ptl:906
+msgid "No metadata has been generated for this host."
+msgstr "Les méta-données pour ce site n'ont pas été générées."
+
+#: ../larpe/admin/hosts.ptl:915
+msgid "No public key has been generated for this host."
+msgstr "La clé publique pour ce site n'a pas été générée."
+
+#: ../larpe/admin/hosts.ptl:927
+msgid "Advanced options"
+msgstr "Options avancées"
+
+#: ../larpe/admin/hosts.ptl:928
+msgid "Step 9 - Advanced options"
+msgstr "Étape 9 - Options avancées"
+
+#: ../larpe/admin/hosts.ptl:930
+msgid "Configure advanced options to setup the last details of your site."
+msgstr ""
+"Configurer les options avancées afin de paramétrer les derniers détails de "
+"votre site"
+
+#: ../larpe/admin/hosts.ptl:931
+#, python-format
+msgid ""
+"If you don't know what to configure here, just click %(next)s and\n"
+"come here later if needed."
+msgstr ""
+"Si vous ne savez pas quoi configurer ici, cliquez simplement sur %(next)s "
+"etrevenez sur cette page plus tard si besoin"
+
+#: ../larpe/admin/hosts.ptl:944
+msgid "Redirect the root URL of the site to the login page."
+msgstr "Rediriger la racine du site vers la page de d'authentification unique"
+
+#: ../larpe/admin/hosts.ptl:946
+msgid "Return address"
+msgstr "Adresse de retour"
+
+#: ../larpe/admin/hosts.ptl:947
+msgid "Where the user will be redirected after a successful authentication"
+msgstr ""
+"Adresse vers laquelle l'utilisateur sera redirigé après s'être authentifié"
+
+#: ../larpe/admin/hosts.ptl:949
+msgid "Error address"
+msgstr "Addresse en cas d'erreur"
+
+#: ../larpe/admin/hosts.ptl:950
+msgid "Where the user will be redirected after a disconnection or an error"
+msgstr "Où l'utilisateur sera redirigé après une déconnexion ou une erreur"
+
+#: ../larpe/admin/hosts.ptl:952
+msgid "URL which must initiate the SSO"
+msgstr "URL qui doit initier l'authentification unique"
+
+#: ../larpe/admin/hosts.ptl:953
+#, python-format
+msgid ""
+"Address which must initiate the SSO. If empty, defaults to the previously\n"
+"specified \"%s\""
+msgstr ""
+"Adresse qui doit initier l'authentification unique. Si ce champ est vide, "
+"l'adresse utilisée sera celle qui a été précédemment renseignée en tant que "
+"\"%s\""
+
+#: ../larpe/admin/hosts.ptl:956
+msgid "Apache HTML proxy"
+msgstr "Mandataire (proxy) HTML d'Apache"
+
+#: ../larpe/admin/hosts.ptl:957
+msgid ""
+"Converts urls in the HTML pages according to the host new domain name.\n"
+"Disabled by default because it makes some sites not work correctly."
+msgstr ""
+"Convertir les urls des pages HTML en fonction du nouveau nom de domaine du "
+"site. Cette option est désactivée par défaut car elle empêche certains sites "
+"de fonctionner correctement."
+
+#: ../larpe/admin/hosts.ptl:988 ../larpe/admin/hosts.ptl:1009
+msgid "Finish"
+msgstr "Terminer"
+
+#: ../larpe/admin/hosts.ptl:996
+msgid "Check everything works"
+msgstr "Vérifier que tout fonctionne"
+
+#: ../larpe/admin/hosts.ptl:997
+msgid "Step 10 - Check everything works"
+msgstr "Étape 10 - Vérifier que tout fonctionne"
+
+#: ../larpe/admin/hosts.ptl:1000
+msgid ""
+"Now you can fully test your site, start from the home page, initiate a\n"
+"Single Sign On, federate your identities and do a Single Logout."
+msgstr ""
+"Vous pouvez maintenant tester vote nouveau site complètement.\n"
+"Commencez par la page d'accueil, initiez une authentification unique,\n"
+"fédérez vos identités puis faites une déconnexion unique."
+
+#: ../larpe/admin/hosts.ptl:1003
+msgid "The address of your site is : "
+msgstr "L'adresse du site est : "
+
+#: ../larpe/admin/hosts.ptl:1008
+#, python-format
+msgid ""
+"If everything works, click the \"%(finish)s\" button, otherwise you can go\n"
+"back and check your settings."
+msgstr ""
+"Si tout fonctionne, cliquez sur le bouton \"%(finish)s\", sinon vous pouvez "
+"revenir en arrière et vérifier votre configuration"
+
+#: ../larpe/admin/hosts.ptl:1120 ../larpe/admin/hosts.ptl:1135
+#: ../larpe/admin/hosts.ptl:1166
+msgid "(filled by users)"
+msgstr "(rempli par les utilisateurs)"
+
+#: ../larpe/admin/hosts.ptl:1217
+msgid "Configuration assistant"
+msgstr "Assistant de configuration"
+
+#: ../larpe/admin/hosts.ptl:1222
+msgid "Address of the original site"
+msgstr "Adresse du site d'origine"
+
+#: ../larpe/admin/hosts.ptl:1222
+msgid "Configure the root address of the site"
+msgstr "Configurer l'adresse racine de ce site"
+
+#: ../larpe/admin/hosts.ptl:1225
+msgid "New address and name"
+msgstr "Nouvelle adresse et nouveau nom du site"
+
+#: ../larpe/admin/hosts.ptl:1225
+msgid "Configure the new address and name of this site"
+msgstr "Configurer la nouvelle adresse et le nouveau nom du site"
+
+#: ../larpe/admin/hosts.ptl:1228
+msgid "Authentication and logout addresses"
+msgstr "Adresses des pages d'authentification et de déconnexion"
+
+#: ../larpe/admin/hosts.ptl:1228
+msgid "Configure the authentication and logout addresses of the original site"
+msgstr ""
+"Configurer les adresses des pages d'authentification et de déconnexion du "
+"site d'origine"
+
+#: ../larpe/admin/hosts.ptl:1231
+msgid "Check auto detected configuration"
+msgstr "Vérifier la configuration détectée automatiquement"
+
+#: ../larpe/admin/hosts.ptl:1231
+msgid "Check the automatically detected configuration is right"
+msgstr "Vérifier que la configuration détectée automatiquement est correcte"
+
+#: ../larpe/admin/hosts.ptl:1234
+msgid "Configure some valid credentials to authenticate on the original site"
+msgstr ""
+"Configurer un couple identifiant / mot de passe pour s'authentifier sur le "
+"site d'origine"
+
+#: ../larpe/admin/hosts.ptl:1237
+msgid "Retry authentication"
+msgstr "Retenter l'authentification"
+
+#: ../larpe/admin/hosts.ptl:1237
+msgid ""
+"Retry sending an authentication request to the site to check if your new "
+"parameters work well"
+msgstr ""
+"Retenter d'envoyer une requête d'authentification au site pour vérifier si "
+"vos nouveaux paramètres fonctionnent bien"
+
+#: ../larpe/admin/hosts.ptl:1240
+msgid "Check authentication response"
+msgstr "Vérifier la réponse de l'authentification"
+
+#: ../larpe/admin/hosts.ptl:1240
+msgid "Check the response from the latest authentication request"
+msgstr "Vérifier la réponse de la dernière requête d'authentification"
+
+#: ../larpe/admin/hosts.ptl:1243
+msgid "Configure authentication success criteria"
+msgstr "Configurer les critères de succès de l'authentification"
+
+#: ../larpe/admin/hosts.ptl:1243
+msgid "Specify how Larpe knows if the authentication has succeeded or not"
+msgstr ""
+"Spécifier comment Larpe détermine si l'authentification a réussi ou pas"
+
+#: ../larpe/admin/hosts.ptl:1246
+msgid "Modify authentication request"
+msgstr "Modifier la requête d'authentification"
+
+#: ../larpe/admin/hosts.ptl:1246
+msgid "Modify POST fields or HTTP headers of the authentication request"
+msgstr ""
+"Modifier les champs POST ou les entêtes HTTP de la requête d'authentification"
+
+#: ../larpe/admin/hosts.ptl:1249
+msgid "Configure how a Single Sign On can be initiated"
+msgstr "Configurer la façon dont l'authentification unique peut être initiée"
+
+#: ../larpe/admin/hosts.ptl:1252
+msgid "Metadatas and key"
+msgstr "Méta-données et clé"
+
+#: ../larpe/admin/hosts.ptl:1252
+msgid "Download SAML 2.0 or ID-FF metadatas and SSL public key"
+msgstr "Télécharger les méta-données SAML 2.0 ou ID-FF et la clé SSL publique"
+
+#: ../larpe/admin/hosts.ptl:1255
+msgid "Adavanced options"
+msgstr "Options avancées"
+
+#: ../larpe/admin/hosts.ptl:1255
+msgid "Configure advanced options to setup the last details of your site"
+msgstr ""
+"Configurer les options avancées afin de paramétrer les derniers détails de "
+"votre site"
+
+#: ../larpe/admin/hosts.ptl:1259
+msgid "Form prefilling with ID-WSF"
+msgstr "Pré-remplissage de formulaire avec ID-WSF"
+
+#: ../larpe/admin/hosts.ptl:1263 ../larpe/admin/forms_prefill.ptl:94
+#: ../larpe/admin/forms_prefill.ptl:101
+msgid "Forms"
+msgstr "Formulaires"
+
+#: ../larpe/admin/hosts.ptl:1263
+msgid "Configure the forms to prefill"
+msgstr "Configurer les formulaires à pré-remplir"
+
+#: ../larpe/admin/hosts.ptl:1269
+msgid "You are about to irrevocably delete this host."
+msgstr "Vous allez définitivement supprimer cet hôte."
+
+#: ../larpe/admin/hosts.ptl:1275 ../larpe/admin/users.ptl:126
+#: ../larpe/admin/fields_prefill.ptl:78 ../larpe/admin/forms_prefill.ptl:81
+msgid "Delete"
+msgstr "Supprimer"
+
+#: ../larpe/admin/hosts.ptl:1276 ../larpe/admin/hosts.ptl:1277
+msgid "Delete Host"
+msgstr "Supprimer l'hôte"
+
+#: ../larpe/admin/hosts.ptl:1293 ../larpe/admin/hosts.ptl:1320
+#: ../larpe/admin/hosts.ptl:1321
+msgid "New Host"
+msgstr "Nouvel hôte"
+
+#: ../larpe/admin/users.ptl:23 ../larpe/admin/users.ptl:31
+msgid "User Name"
+msgstr "Nom de l'utilisateur"
+
+#: ../larpe/admin/users.ptl:97
+msgid "Debug"
+msgstr "Debug"
+
+#: ../larpe/admin/users.ptl:110 ../larpe/admin/users.ptl:111
+msgid "Edit User"
+msgstr "Modifier l'utilisateur"
+
+#: ../larpe/admin/users.ptl:120
+msgid "You are about to irrevocably delete this user."
+msgstr "Vous allez définitivement supprimer cet utilisateur."
+
+#: ../larpe/admin/users.ptl:127
+msgid "Delete User"
+msgstr "Supprimer l'utilisateur"
+
+#: ../larpe/admin/users.ptl:128
+msgid "Deleting User :"
+msgstr "Suppression de l'utilisateur :"
+
+#: ../larpe/admin/users.ptl:136
+msgid "Generate"
+msgstr "Générer"
+
+#: ../larpe/admin/users.ptl:147
+msgid ""
+"You are about to generate a token than can be used to federate the account."
+msgstr ""
+"Vous allez générer un jeton qui pourra être utilisé pour fédérer le compte."
+
+#: ../larpe/admin/users.ptl:148
+msgid ""
+"After that, you will have the choice to send it to the user by email so that "
+"he can federate his accounts."
+msgstr ""
+"Ensuite, vous aurez la possibilité de l'envoyer à l'utilisateur par courriel "
+"pour qu'il puisse fédérer ses comptes."
+
+#: ../larpe/admin/users.ptl:150
+#, 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"
+
+#: ../larpe/admin/users.ptl:160
+#, python-format
+msgid "Identification Token for %s"
+msgstr "Jeton d'identification pour %s"
+
+#: ../larpe/admin/users.ptl:164
+msgid "Done"
+msgstr "Fait"
+
+#: ../larpe/admin/users.ptl:166
+msgid "Send by email"
+msgstr "Envoyer par courriel"
+
+#: ../larpe/admin/users.ptl:172
+#, python-format
+msgid ""
+"You have been given an identification token.\n"
+"\n"
+"Your token is %(token)s\n"
+"\n"
+"Click on %(url)s to use it.\n"
+msgstr ""
+"Un jeton d'identification vous a été attribué.\n"
+"\n"
+"Votre jeton est %(token)s\n"
+"\n"
+"Cliquez sur %(url)s pour l'utiliser.\n"
+
+#: ../larpe/admin/users.ptl:182
+msgid "Failed sending email. Check your email configuration."
+msgstr ""
+"Échec de l'envoi du courriel. Vérifiez votre configuration des courriels."
+
+#: ../larpe/admin/users.ptl:198 ../larpe/admin/users.ptl:241
+msgid "Liberty support must be setup before creating users."
+msgstr ""
+"Le support Liberty doit être configuré avant de créer des utilisateurs."
+
+#: ../larpe/admin/users.ptl:202 ../larpe/admin/users.ptl:253
+#: ../larpe/admin/users.ptl:254
+msgid "New User"
+msgstr "Nouvel utilisateur"
+
+#: ../larpe/admin/users.ptl:225
+#, python-format
+msgid "Identification Token (current: %s)"
+msgstr "Jeton d'identification (actuellement: %s)"
+
+#: ../larpe/admin/fields_prefill.ptl:15
+msgid "Field name"
+msgstr "Nom du champ"
+
+#: ../larpe/admin/fields_prefill.ptl:17
+msgid "Xpath of the attribute"
+msgstr "Xpath de l'attribut"
+
+#: ../larpe/admin/fields_prefill.ptl:18
+msgid "Example: /pp:PP/pp:InformalName"
+msgstr "Exemple : /pp:PP/pp:InformalName"
+
+#: ../larpe/admin/fields_prefill.ptl:19
+msgid "Number of the field in the data"
+msgstr "Numéro du champ dans les données"
+
+#: ../larpe/admin/fields_prefill.ptl:21
+msgid ""
+"Change it if there are multiple fields corresponding to the same Xpath and "
+"you want to get another than the first one"
+msgstr ""
+"À changer s'il existe plusieurs champs correspondants au même Xpath et que "
+"vous voulez obtenir un autre élément que le premier"
+
+#: ../larpe/admin/fields_prefill.ptl:22
+msgid "Get raw XML value"
+msgstr "Récupérer la valeur XML brute"
+
+#: ../larpe/admin/fields_prefill.ptl:24
+msgid "Python regexp of a string to match"
+msgstr ""
+"Expression régulière en Python correspondant à la chaîne de caractères à "
+"chercher"
+
+#: ../larpe/admin/fields_prefill.ptl:26
+msgid "Python regexp of the replacing string"
+msgstr ""
+"Expression régulière en Python de la chaîne de caractères de remplacement"
+
+#: ../larpe/admin/fields_prefill.ptl:28
+msgid "Options mapping for a select field"
+msgstr "Correspondance d'options pour un champ select"
+
+#: ../larpe/admin/fields_prefill.ptl:29
+msgid "Add item"
+msgstr "Ajouter un élément"
+
+#: ../larpe/admin/fields_prefill.ptl:72
+msgid "You are about to irrevocably delete this field."
+msgstr "Vous allez définitivement supprimer ce champ."
+
+#: ../larpe/admin/fields_prefill.ptl:79 ../larpe/admin/fields_prefill.ptl:80
+msgid "Delete Field"
+msgstr "Supprimer le champ"
+
+#: ../larpe/admin/fields_prefill.ptl:91 ../larpe/admin/fields_prefill.ptl:98
+#: ../larpe/admin/forms_prefill.ptl:50
+msgid "Fields"
+msgstr "Champs"
+
+#: ../larpe/admin/fields_prefill.ptl:101
+msgid "New Field"
+msgstr "Nouveau champ"
+
+#: ../larpe/admin/forms_prefill.ptl:16
+msgid "Form name"
+msgstr "Nom du formulaire"
+
+#: ../larpe/admin/forms_prefill.ptl:17
+msgid "Only used for display"
+msgstr "Utilisé uniquement pour l'affichage"
+
+#: ../larpe/admin/forms_prefill.ptl:18
+msgid "Form address"
+msgstr "Addresse du formulaire"
+
+#: ../larpe/admin/forms_prefill.ptl:20
+msgid "ID-WSF data profile"
+msgstr "Profile des données ID-WSF"
+
+#: ../larpe/admin/forms_prefill.ptl:21
+msgid "Example: urn:liberty:id-sis-pp:2005-05"
+msgstr "Exemple : urn:liberty:id-sis-pp:2005-05"
+
+#: ../larpe/admin/forms_prefill.ptl:22
+msgid "ID-WSF data XML prefix"
+msgstr "Préfixe XML des données ID-WSF"
+
+#: ../larpe/admin/forms_prefill.ptl:23
+msgid "Example: pp"
+msgstr "Exemple : pp"
+
+#: ../larpe/admin/forms_prefill.ptl:45
+msgid "Form prefilling configuration"
+msgstr "Configuration du pré-remplissage de formulaire"
+
+#: ../larpe/admin/forms_prefill.ptl:48
+msgid "Configure this form"
+msgstr "Configurer ce formulaire"
+
+#: ../larpe/admin/forms_prefill.ptl:50
+msgid "Configure the fields of this form"
+msgstr "Configurer les champs de ce formulaire"
+
+#: ../larpe/admin/forms_prefill.ptl:75
+msgid "You are about to irrevocably delete this form."
+msgstr "Vous allez définitivement supprimer ce fomulaire."
+
+#: ../larpe/admin/forms_prefill.ptl:82 ../larpe/admin/forms_prefill.ptl:83
+msgid "Delete Form"
+msgstr "Supprimer le formulaire"
+
+#: ../larpe/admin/forms_prefill.ptl:104
+msgid "New Form"
+msgstr "Nouveau formulaire"
+
+#: ../larpe/site_authentication.ptl:55
+msgid "Local authentication"
+msgstr "Authentification locale"
+
+#: ../larpe/site_authentication.ptl:69
+msgid "Authentication failure"
+msgstr "Erreur d'authentification"
+
+#: ../larpe/site_authentication.ptl:72
+#, python-format
+msgid "Connection failed : %s"
+msgstr "Échec de la connexion : %s"
+
+#: ../larpe/site_authentication.ptl:75
+#, python-format
+msgid "This service provider is not fully configured : %s"
+msgstr "Ce fournisseur de service n'est pas complètement configuré : %s"
+
+#: ../larpe/site_authentication.ptl:78
+#, python-format
+msgid "Unknown error : %s"
+msgstr "Erreur inconnue : %s"
+
+#: ../larpe/site_authentication.ptl:84
+msgid "Please type your login and password for this Service Provider."
+msgstr ""
+"Entrez votre identifiant et votre mot de passe pour ce fournisseur de "
+"service."
+
+#: ../larpe/users.py:38
+msgid "Unknown User"
+msgstr "Utilisateur inconnu"
diff --git a/larpe/tags/release-1.0/po/larpe.pot b/larpe/tags/release-1.0/po/larpe.pot
new file mode 100644
index 0000000..3272bcf
--- /dev/null
+++ b/larpe/tags/release-1.0/po/larpe.pot
@@ -0,0 +1,2620 @@
+# 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: 2009-03-09 15:32+0100\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"
+
+#: ../larpe/liberty.ptl:45 ../larpe/qommon/liberty.ptl:44
+msgid "Liberty support is not yet configured"
+msgstr ""
+
+#: ../larpe/liberty.ptl:58 ../larpe/qommon/liberty.ptl:59
+#: ../larpe/qommon/saml2.ptl:97 ../larpe/qommon/saml2.ptl:376
+#: ../larpe/qommon/saml2.ptl:542 ../larpe/saml2.ptl:64
+msgid "Failure to communicate with identity provider"
+msgstr ""
+
+#: ../larpe/liberty.ptl:63 ../larpe/qommon/liberty.ptl:64
+#: ../larpe/qommon/saml2.ptl:103 ../larpe/qommon/saml2.ptl:214
+#: ../larpe/qommon/saml2.ptl:241 ../larpe/saml2.ptl:70
+msgid "Unknown authentication failure"
+msgstr ""
+
+#: ../larpe/liberty.ptl:66 ../larpe/qommon/liberty.ptl:67
+#: ../larpe/qommon/saml2.ptl:105 ../larpe/saml2.ptl:72
+msgid "Authentication failure; unknown principal"
+msgstr ""
+
+#: ../larpe/liberty.ptl:67 ../larpe/qommon/liberty.ptl:70
+msgid "Identity Provider didn't accept artifact transaction."
+msgstr ""
+
+#: ../larpe/liberty.ptl:142 ../larpe/liberty.ptl:173
+#: ../larpe/qommon/liberty.ptl:120 ../larpe/qommon/liberty.ptl:146
+#: ../larpe/qommon/saml2.ptl:456 ../larpe/saml2.ptl:324
+msgid "Failed to check single logout request signature."
+msgstr ""
+
+#: ../larpe/liberty_site.ptl:46 ../larpe/liberty_site.ptl:56
+#: ../larpe/qommon/ident/idp.ptl:36
+msgid "SSO support is not yet configured"
+msgstr ""
+
+#: ../larpe/qommon/admin/menu.ptl:46
+msgid "backoffice"
+msgstr ""
+
+#: ../larpe/qommon/admin/menu.ptl:47 ../larpe/qommon/backoffice/menu.ptl:40
+msgid "logout"
+msgstr ""
+
+#: ../larpe/qommon/admin/menu.ptl:51 ../larpe/qommon/backoffice/menu.ptl:45
+msgid "help"
+msgstr ""
+
+#: ../larpe/qommon/admin/menu.ptl:73
+#, python-format
+msgid "Administration of %s"
+msgstr ""
+
+#: ../larpe/qommon/admin/menu.ptl:75 ../larpe/admin/root.ptl:59
+msgid "Administration"
+msgstr ""
+
+#: ../larpe/qommon/admin/menu.ptl:95
+msgid "Add"
+msgstr ""
+
+#: ../larpe/qommon/admin/menu.ptl:96 ../larpe/qommon/ident/idp.ptl:498
+#: ../larpe/admin/users.ptl:109 ../larpe/admin/fields_prefill.ptl:63
+#: ../larpe/admin/fields_prefill.ptl:64 ../larpe/admin/fields_prefill.ptl:65
+#: ../larpe/admin/forms_prefill.ptl:48 ../larpe/admin/forms_prefill.ptl:66
+#: ../larpe/admin/forms_prefill.ptl:67 ../larpe/admin/forms_prefill.ptl:68
+msgid "Edit"
+msgstr ""
+
+#: ../larpe/qommon/admin/menu.ptl:97
+msgid "Remove"
+msgstr ""
+
+#: ../larpe/qommon/admin/menu.ptl:98
+msgid "Duplicate"
+msgstr ""
+
+#: ../larpe/qommon/admin/menu.ptl:99
+msgid "View"
+msgstr ""
+
+#: ../larpe/qommon/admin/menu.ptl:100 ../larpe/admin/hosts.ptl:313
+#: ../larpe/admin/hosts.ptl:342 ../larpe/admin/hosts.ptl:431
+#: ../larpe/admin/hosts.ptl:455 ../larpe/admin/hosts.ptl:536
+#: ../larpe/admin/hosts.ptl:572 ../larpe/admin/hosts.ptl:851
+#: ../larpe/admin/hosts.ptl:873 ../larpe/admin/hosts.ptl:961
+#: ../larpe/admin/hosts.ptl:987
+msgid "Previous"
+msgstr ""
+
+#: ../larpe/qommon/admin/menu.ptl:101 ../larpe/admin/hosts.ptl:124
+#: ../larpe/admin/hosts.ptl:314 ../larpe/admin/hosts.ptl:342
+#: ../larpe/admin/hosts.ptl:432 ../larpe/admin/hosts.ptl:456
+#: ../larpe/admin/hosts.ptl:537 ../larpe/admin/hosts.ptl:573
+#: ../larpe/admin/hosts.ptl:852 ../larpe/admin/hosts.ptl:874
+#: ../larpe/admin/hosts.ptl:932 ../larpe/admin/hosts.ptl:962
+msgid "Next"
+msgstr ""
+
+#: ../larpe/qommon/admin/menu.ptl:102
+msgid "Export"
+msgstr ""
+
+#: ../larpe/qommon/admin/menu.ptl:103 ../larpe/qommon/admin/emails.ptl:140
+#: ../larpe/qommon/ident/password.ptl:121
+#: ../larpe/qommon/ident/password.ptl:208
+#: ../larpe/qommon/ident/password.ptl:392
+#: ../larpe/qommon/ident/password.ptl:767 ../larpe/qommon/ident/idp.ptl:115
+#: ../larpe/admin/users.ptl:24 ../larpe/admin/users.ptl:33
+#: ../larpe/admin/users.ptl:65
+msgid "Email"
+msgstr ""
+
+#: ../larpe/qommon/admin/menu.ptl:132 ../larpe/qommon/admin/menu.ptl:134
+#: ../larpe/qommon/template.ptl:162
+msgid "Error"
+msgstr ""
+
+#: ../larpe/qommon/admin/menu.ptl:136 ../larpe/qommon/admin/texts.ptl:62
+#: ../larpe/qommon/admin/emails.ptl:99 ../larpe/admin/settings.ptl:112
+#: ../larpe/admin/hosts.ptl:647 ../larpe/admin/hosts.ptl:703
+#: ../larpe/admin/hosts.ptl:1324 ../larpe/admin/users.ptl:183
+msgid "Back"
+msgstr ""
+
+#: ../larpe/qommon/admin/texts.ptl:48 ../larpe/qommon/admin/texts.ptl:49
+#: ../larpe/qommon/admin/texts.ptl:106 ../larpe/qommon/admin/texts.ptl:143
+msgid "Texts"
+msgstr ""
+
+#: ../larpe/qommon/admin/texts.ptl:53 ../larpe/qommon/admin/texts.ptl:54
+#: ../larpe/qommon/admin/texts.ptl:58 ../larpe/qommon/admin/texts.ptl:139
+#: ../larpe/qommon/admin/emails.ptl:90 ../larpe/qommon/admin/emails.ptl:91
+#: ../larpe/qommon/admin/emails.ptl:95 ../larpe/qommon/admin/emails.ptl:177
+msgid "description"
+msgstr ""
+
+#: ../larpe/qommon/admin/texts.ptl:58
+msgid "Custom Text:"
+msgstr ""
+
+#. #-#-#-#-# tmp.25.idp.ptl.pot (PACKAGE VERSION) #-#-#-#-#
+#. TODO
+#: ../larpe/qommon/admin/texts.ptl:88 ../larpe/qommon/admin/emails.ptl:68
+#: ../larpe/qommon/admin/emails.ptl:121 ../larpe/qommon/admin/logger.ptl:120
+#: ../larpe/qommon/admin/settings.ptl:22 ../larpe/qommon/admin/settings.ptl:50
+#: ../larpe/qommon/admin/settings.ptl:78
+#: ../larpe/qommon/ident/password.ptl:305
+#: ../larpe/qommon/ident/password.ptl:573
+#: ../larpe/qommon/ident/password.ptl:619
+#: ../larpe/qommon/ident/password.ptl:830
+#: ../larpe/qommon/ident/password.ptl:857 ../larpe/qommon/ident/idp.ptl:77
+#: ../larpe/qommon/ident/idp.ptl:229 ../larpe/qommon/ident/idp.ptl:315
+#: ../larpe/qommon/ident/idp.ptl:513 ../larpe/qommon/ident/idp.ptl:537
+#: ../larpe/qommon/ident/idp.ptl:762 ../larpe/qommon/ident/idp.ptl:1047
+#: ../larpe/root.ptl:64 ../larpe/admin/settings.ptl:31
+#: ../larpe/admin/settings.ptl:217 ../larpe/admin/settings.ptl:326
+#: ../larpe/admin/settings.ptl:343 ../larpe/admin/hosts.ptl:383
+#: ../larpe/admin/hosts.ptl:682 ../larpe/admin/hosts.ptl:730
+#: ../larpe/admin/hosts.ptl:777 ../larpe/admin/hosts.ptl:1270
+#: ../larpe/admin/users.ptl:25 ../larpe/admin/users.ptl:35
+#: ../larpe/admin/users.ptl:121 ../larpe/admin/fields_prefill.ptl:30
+#: ../larpe/admin/fields_prefill.ptl:73 ../larpe/admin/forms_prefill.ptl:24
+#: ../larpe/admin/forms_prefill.ptl:76 ../larpe/site_authentication.ptl:59
+msgid "Submit"
+msgstr ""
+
+#: ../larpe/qommon/admin/texts.ptl:90
+msgid "Restore default text"
+msgstr ""
+
+#: ../larpe/qommon/admin/texts.ptl:91 ../larpe/qommon/admin/emails.ptl:69
+#: ../larpe/qommon/admin/emails.ptl:124 ../larpe/qommon/admin/settings.ptl:23
+#: ../larpe/qommon/admin/settings.ptl:51 ../larpe/qommon/admin/settings.ptl:79
+#: ../larpe/qommon/ident/password.ptl:306
+#: ../larpe/qommon/ident/password.ptl:574
+#: ../larpe/qommon/ident/password.ptl:620
+#: ../larpe/qommon/ident/password.ptl:832
+#: ../larpe/qommon/ident/password.ptl:858 ../larpe/qommon/ident/idp.ptl:78
+#: ../larpe/qommon/ident/idp.ptl:316 ../larpe/qommon/ident/idp.ptl:538
+#: ../larpe/qommon/ident/idp.ptl:763 ../larpe/qommon/ident/idp.ptl:1048
+#: ../larpe/root.ptl:65 ../larpe/admin/settings.ptl:218
+#: ../larpe/admin/settings.ptl:327 ../larpe/admin/settings.ptl:344
+#: ../larpe/admin/hosts.ptl:123 ../larpe/admin/hosts.ptl:384
+#: ../larpe/admin/hosts.ptl:683 ../larpe/admin/hosts.ptl:731
+#: ../larpe/admin/hosts.ptl:778 ../larpe/admin/hosts.ptl:1271
+#: ../larpe/admin/users.ptl:26 ../larpe/admin/users.ptl:36
+#: ../larpe/admin/users.ptl:122 ../larpe/admin/users.ptl:137
+#: ../larpe/admin/fields_prefill.ptl:31 ../larpe/admin/fields_prefill.ptl:74
+#: ../larpe/admin/forms_prefill.ptl:25 ../larpe/admin/forms_prefill.ptl:77
+msgid "Cancel"
+msgstr ""
+
+#: ../larpe/qommon/admin/texts.ptl:103 ../larpe/qommon/admin/emails.ptl:136
+msgid "Invalid template"
+msgstr ""
+
+#: ../larpe/qommon/admin/texts.ptl:107
+msgid "Text"
+msgstr ""
+
+#: ../larpe/qommon/admin/emails.ptl:55
+msgid "SMTP Server"
+msgstr ""
+
+#: ../larpe/qommon/admin/emails.ptl:57
+msgid "Email Sender"
+msgstr ""
+
+#: ../larpe/qommon/admin/emails.ptl:59
+msgid "Reply-To Address"
+msgstr ""
+
+#: ../larpe/qommon/admin/emails.ptl:61
+msgid "Handle Bounces"
+msgstr ""
+
+#: ../larpe/qommon/admin/emails.ptl:64
+msgid "Check DNS for domain name"
+msgstr ""
+
+#: ../larpe/qommon/admin/emails.ptl:66
+msgid "Use a DNS request to check domain names used in email fields"
+msgstr ""
+
+#. #-#-#-#-# tmp.55.settings.ptl.pot (PACKAGE VERSION) #-#-#-#-#
+#. Don't use custom emails
+#: ../larpe/qommon/admin/emails.ptl:74 ../larpe/qommon/admin/emails.ptl:84
+#: ../larpe/qommon/admin/emails.ptl:85 ../larpe/qommon/admin/emails.ptl:139
+#: ../larpe/qommon/admin/emails.ptl:182 ../larpe/admin/settings.ptl:104
+#: ../larpe/admin/settings.ptl:105 ../larpe/admin/settings.ptl:192
+msgid "Emails"
+msgstr ""
+
+#: ../larpe/qommon/admin/emails.ptl:75 ../larpe/qommon/admin/emails.ptl:76
+#: ../larpe/qommon/admin/emails.ptl:88 ../larpe/admin/settings.ptl:108
+msgid "General Options"
+msgstr ""
+
+#: ../larpe/qommon/admin/emails.ptl:95
+msgid "Custom Email:"
+msgstr ""
+
+#: ../larpe/qommon/admin/emails.ptl:115
+msgid "Enabled Email"
+msgstr ""
+
+#: ../larpe/qommon/admin/emails.ptl:117 ../larpe/qommon/ident/password.ptl:828
+#: ../larpe/qommon/ident/password.ptl:854
+msgid "Subject"
+msgstr ""
+
+#: ../larpe/qommon/admin/emails.ptl:123
+msgid "Restore default email"
+msgstr ""
+
+#: ../larpe/qommon/admin/emails.ptl:178
+msgid "hint"
+msgstr ""
+
+#: ../larpe/qommon/admin/logger.ptl:23 ../larpe/qommon/admin/logger.ptl:24
+#: ../larpe/qommon/admin/logger.ptl:145 ../larpe/admin/root.ptl:47
+#: ../larpe/admin/users.ptl:232
+msgid "Logs"
+msgstr ""
+
+#: ../larpe/qommon/admin/logger.ptl:28 ../larpe/qommon/admin/logger.ptl:127
+#, python-format
+msgid "Bad log file: %s"
+msgstr ""
+
+#: ../larpe/qommon/admin/logger.ptl:32
+msgid "Nothing to show"
+msgstr ""
+
+#: ../larpe/qommon/admin/logger.ptl:35 ../larpe/qommon/admin/logger.ptl:37
+msgid "Download Raw Log File"
+msgstr ""
+
+#: ../larpe/qommon/admin/logger.ptl:43 ../larpe/qommon/admin/logger.ptl:151
+msgid "Time"
+msgstr ""
+
+#: ../larpe/qommon/admin/logger.ptl:44 ../larpe/qommon/admin/logger.ptl:146
+#: ../larpe/admin/users.ptl:59 ../larpe/admin/users.ptl:60
+msgid "User"
+msgstr ""
+
+#: ../larpe/qommon/admin/logger.ptl:45 ../larpe/qommon/admin/logger.ptl:152
+#: ../larpe/qommon/ident/password.ptl:829
+#: ../larpe/qommon/ident/password.ptl:856
+msgid "Message"
+msgstr ""
+
+#: ../larpe/qommon/admin/logger.ptl:74
+msgid "Anonymous"
+msgstr ""
+
+#: ../larpe/qommon/admin/logger.ptl:78
+msgid "Unlogged"
+msgstr ""
+
+#: ../larpe/qommon/admin/logger.ptl:85 ../larpe/qommon/backoffice/menu.ptl:31
+msgid "Unknown"
+msgstr ""
+
+#: ../larpe/qommon/admin/logger.ptl:112
+msgid "Select another logfile:"
+msgstr ""
+
+#: ../larpe/qommon/admin/logger.ptl:117
+#, python-format
+msgid "Since: %s"
+msgstr ""
+
+#: ../larpe/qommon/admin/settings.ptl:10 ../larpe/admin/root.ptl:46
+#: ../larpe/admin/settings.ptl:125 ../larpe/admin/hosts.ptl:83
+msgid "Settings"
+msgstr ""
+
+#: ../larpe/qommon/admin/settings.ptl:16 ../larpe/qommon/admin/settings.ptl:28
+#: ../larpe/qommon/admin/settings.ptl:29 ../larpe/admin/settings.ptl:190
+msgid "Language"
+msgstr ""
+
+#: ../larpe/qommon/admin/settings.ptl:18
+msgid "System Default"
+msgstr ""
+
+#: ../larpe/qommon/admin/settings.ptl:19
+msgid "English"
+msgstr ""
+
+#: ../larpe/qommon/admin/settings.ptl:20
+msgid "French"
+msgstr ""
+
+#: ../larpe/qommon/admin/settings.ptl:39
+msgid "Use a web proxy"
+msgstr ""
+
+#: ../larpe/qommon/admin/settings.ptl:41
+msgid "Proxy IP address or domain name"
+msgstr ""
+
+#: ../larpe/qommon/admin/settings.ptl:43
+msgid "Proxy port"
+msgstr ""
+
+#: ../larpe/qommon/admin/settings.ptl:45
+msgid "User name"
+msgstr ""
+
+#: ../larpe/qommon/admin/settings.ptl:47
+msgid "User password"
+msgstr ""
+
+#: ../larpe/qommon/admin/settings.ptl:56 ../larpe/qommon/admin/settings.ptl:57
+#: ../larpe/admin/settings.ptl:183
+msgid "Proxy"
+msgstr ""
+
+#: ../larpe/qommon/admin/settings.ptl:66
+msgid "Email for Tracebacks"
+msgstr ""
+
+#: ../larpe/qommon/admin/settings.ptl:68
+msgid "Display Exceptions"
+msgstr ""
+
+#: ../larpe/qommon/admin/settings.ptl:70
+msgid "No display"
+msgstr ""
+
+#: ../larpe/qommon/admin/settings.ptl:71
+msgid "Display as Text"
+msgstr ""
+
+#: ../larpe/qommon/admin/settings.ptl:72
+msgid "Display as Text in an HTML error page"
+msgstr ""
+
+#: ../larpe/qommon/admin/settings.ptl:73
+msgid "Display as HTML"
+msgstr ""
+
+#: ../larpe/qommon/admin/settings.ptl:74
+msgid "Logger"
+msgstr ""
+
+#: ../larpe/qommon/admin/settings.ptl:76
+msgid "Enable debug mode"
+msgstr ""
+
+#: ../larpe/qommon/admin/settings.ptl:85 ../larpe/qommon/admin/settings.ptl:86
+#: ../larpe/qommon/admin/settings.ptl:87 ../larpe/admin/settings.ptl:199
+msgid "Debug Options"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:61
+#, python-format
+msgid "Password is too short. It must be at least %d characters."
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:65
+#, python-format
+msgid "Password is too long. It must be at most %d characters."
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:76
+msgid "Account Creation Confirmed"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:123
+#: ../larpe/qommon/ident/password.ptl:210
+#: ../larpe/qommon/ident/password.ptl:389
+#: ../larpe/qommon/ident/password.ptl:647
+#: ../larpe/qommon/ident/password.ptl:763 ../larpe/admin/hosts.ptl:525
+#: ../larpe/site_authentication.ptl:93
+msgid "Username"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:124
+#: ../larpe/qommon/ident/password.ptl:395
+#: ../larpe/qommon/ident/password.ptl:653 ../larpe/admin/hosts.ptl:527
+#: ../larpe/site_authentication.ptl:95
+msgid "Password"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:125
+msgid "Log in"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:131
+#: ../larpe/qommon/ident/password.ptl:132 ../larpe/qommon/ident/idp.ptl:93
+msgid "Login"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:158
+msgid "Invalid credentials"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:169
+msgid "This account is waiting for moderation"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:174
+msgid "This account is waiting for confirmation"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:179
+msgid "This account has been disabled"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:211
+msgid "Submit Request"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:220
+#: ../larpe/qommon/ident/password.ptl:221
+msgid "Forgotten password"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:238
+msgid "There is no user with that name or it has no email contact."
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:260
+msgid "Failed to send email (server error)"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:265
+msgid "Forgotten Password"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:279
+msgid ""
+"The token you submitted does not exist, has expired, or has been cancelled."
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:280
+#: ../larpe/qommon/ident/password.ptl:285
+msgid "home page"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:284
+msgid "The token you submitted is not appropriate for the requested task."
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:290
+msgid "Request Cancelled"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:291
+msgid "Your request has been cancelled"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:292
+msgid "Continue to <a href=\"/\">home page</a></p>"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:301
+msgid "New Password"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:303
+msgid "New Password (confirm)"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:317
+msgid "Passwords do not match"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:367
+msgid "New password sent by email"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:397 ../larpe/qommon/ident/idp.ptl:117
+msgid "Create Account"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:404
+#: ../larpe/qommon/ident/password.ptl:405 ../larpe/qommon/ident/idp.ptl:124
+#: ../larpe/qommon/ident/idp.ptl:125
+msgid "New Account"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:426
+msgid "There is already a user with that username"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:429
+msgid "There is already a user with that email address"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:462
+msgid ""
+"Accounts are configured to require confirmation but accounts can be created "
+"without emails"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:481
+msgid "Account created, waiting for moderation"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:484
+msgid "A site administrator will now review then activate your account."
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:488
+msgid "You will then get your password by email."
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:492
+msgid "Back to home page"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:498
+msgid "Email sent"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:501
+msgid "An email has been sent to you so you can confirm your account creation."
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:528
+msgid "Username / Password"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:532
+msgid "Configure username/password identification method"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:543 ../larpe/qommon/ident/idp.ptl:710
+msgid "Identities"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:543 ../larpe/qommon/ident/idp.ptl:710
+msgid "Configure identities creation"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:545
+#: ../larpe/qommon/ident/password.ptl:582
+#: ../larpe/qommon/ident/password.ptl:583
+msgid "Passwords"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:545
+msgid "Configure all password things"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:551
+msgid "Users can change their password"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:553
+msgid "Generate initial password"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:556
+msgid "Lost Password Behaviour"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:558
+msgid "Nothing (contact admin)"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:559
+msgid "Email reminder"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:560
+msgid "Question selected by user"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:561
+msgid "Minimum password length"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:563
+msgid "Maximum password length"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:565
+msgid "0 for unlimited length"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:566
+msgid "Email address (for questions...)"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:569
+msgid "Password Hashing Algorithm"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:571
+msgid "None"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:596 ../larpe/qommon/ident/idp.ptl:1033
+msgid "Identity Creation"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:598 ../larpe/qommon/ident/idp.ptl:1035
+msgid "Site Administrator"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:599 ../larpe/qommon/ident/idp.ptl:1036
+msgid "Self-registration"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:600
+msgid "Moderated user registration"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:602 ../larpe/qommon/ident/idp.ptl:1039
+msgid "Require email confirmation for new accounts"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:605 ../larpe/qommon/ident/idp.ptl:1043
+msgid "Notify Administrators on Registration"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:607
+msgid "Use email as username"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:610
+msgid "Warn about unused account after so many days"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:611
+msgid "0 for no warning"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:614
+msgid "Removed unused account after so many days"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:615
+msgid "0 for no automatic removal"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:629
+#: ../larpe/qommon/ident/password.ptl:630 ../larpe/qommon/ident/idp.ptl:1057
+#: ../larpe/qommon/ident/idp.ptl:1058
+msgid "Identities Interface"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:657
+msgid "Awaiting Confirmation"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:659
+msgid "Awaiting Moderation"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:661
+msgid "Disabled Account"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:675
+msgid "Username / password"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:690
+msgid "Duplicate user name"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:734
+msgid "Accounts"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:757
+#, python-format
+msgid "Account - %s"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:758
+msgid "Moderation of account"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:788
+#: ../larpe/qommon/ident/password.ptl:864
+#: ../larpe/qommon/ident/password.ptl:865
+#: ../larpe/qommon/ident/password.ptl:866
+#: ../larpe/qommon/ident/password.ptl:922
+msgid "Reply by email"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:789
+#: ../larpe/qommon/ident/password.ptl:924
+msgid "Accept"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:791
+#: ../larpe/qommon/ident/password.ptl:926
+msgid "Reject"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:827
+#: ../larpe/qommon/ident/password.ptl:853
+msgid "To"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:831
+msgid "Submit and don't send email"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:838
+#: ../larpe/qommon/ident/password.ptl:839
+msgid "Rejection"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:855
+msgid "About your account request"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:886
+msgid "You are not allowed to access Accounts Management"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:889
+#: ../larpe/qommon/ident/password.ptl:898
+msgid "Accounts Management"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:899
+msgid "New accounts waiting for moderation"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:914
+msgid "Username:"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:939
+msgid "Subscription notification for password account"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:940
+msgid ""
+"Available variables: email, website, token_url, admin_email, username, "
+"password"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:941
+msgid "Subscription Confirmation"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:942
+msgid ""
+"We have received a request for subscription of your email address,\n"
+"\"[email]\", to the [website] web site.\n"
+"\n"
+"To confirm that you want to be subscribed to the web site, simply\n"
+"visit this web page:\n"
+"\n"
+"[token_url]\n"
+"\n"
+"If you do not wish to be subscribed to the web site, pleasy simply\n"
+"disregard this message. If you think you are being maliciously\n"
+"subscribed to the web site, or have any other questions, send them\n"
+"to [admin_email].\n"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:958
+msgid "Request for password change"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:959
+msgid "Available variables: change_url, cancel_url, time"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:960
+msgid "Change Password Request"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:961
+msgid ""
+"You have (or someone impersonating you has) requested to change your\n"
+"password. To complete the change, visit the following link:\n"
+"\n"
+"[change_url]\n"
+"\n"
+"If you are not the person who made this request, or you wish to cancel\n"
+"this request, visit the following link:\n"
+"\n"
+"[cancel_url]\n"
+"\n"
+"If you do nothing, the request will lapse after 3 days (precisely on\n"
+"[time]).\n"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:978
+msgid "New generated password"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:979
+msgid "Available variable: password"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:980
+msgid "Your new password"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:981
+msgid "Your new password: [password]\n"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:987
+msgid "Approval of new account"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:988
+msgid "Available variables: username, password"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:989
+msgid "Your account has been approved"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:990
+msgid ""
+"Your account has been approved.\n"
+"\n"
+"Account details:\n"
+"\n"
+"- username: [username]\n"
+"[if-any password]- password: [password][end]\n"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:1000
+msgid "Warning about unusued account"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:1001
+#: ../larpe/qommon/ident/password.ptl:1009
+msgid "Available variables: username"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:1002
+msgid "Your account is unused"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:1003
+msgid "Your account ([username]) is not being used.\n"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:1008
+msgid "Notification of removal of unused account"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:1010
+msgid "Your account has been removed"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:1011
+msgid ""
+"Your account ([username]) was not being used, it has therefore been "
+"removed.\n"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:1016
+msgid "Notification of new registration to administrators"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:1017
+msgid "Available variables: hostname, email_as_username, username"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:1018
+msgid "New Registration"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:1019
+msgid ""
+"Hello,\n"
+"\n"
+"A new account has been created on [hostname].\n"
+"\n"
+" - name: [name]\n"
+" - username: [username]\n"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:1030
+msgid "Text when account confirmed by user but waiting moderator approval"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:1031
+msgid ""
+"<p>\n"
+"Your account has been created. In order to be effective\n"
+"it must be activated by a moderator. You will receive an\n"
+"email when this is done.\n"
+"</p>"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:1038
+msgid "Text when account confirmed by user"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:1039
+msgid ""
+"<p>\n"
+"Your account has been created.\n"
+"</p>"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:1044
+msgid "Text when an email with a change password token has been sent"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:1045
+msgid ""
+"<p>\n"
+"A token for changing your password has been emailed to you. Follow the "
+"instructions in that email to change your password.\n"
+"</p>\n"
+"<p>\n"
+" <a href=\"login\">Log In</a>\n"
+"</p>"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:1053
+msgid "Text when new password has been sent"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:1054
+msgid ""
+"<p>\n"
+"Your new password has been sent to you by email.\n"
+"</p>\n"
+"<p>\n"
+" <a href=\"login\">Login</a>\n"
+"</p>"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:1061
+msgid "Text on top of registration form"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:1064
+msgid "Text on forgotten password request page"
+msgstr ""
+
+#: ../larpe/qommon/ident/password.ptl:1065
+msgid ""
+"<p>\n"
+"If you have an account, but have forgotten your password, enter your user "
+"name\n"
+"below and submit a request to change your password.\n"
+"</p>"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:27
+msgid "Liberty/SAML2"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:94
+msgid "Select the identity provider you want to use."
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:114 ../larpe/admin/hosts.ptl:381
+#: ../larpe/admin/users.ptl:62
+msgid "Name"
+msgstr ""
+
+#. #-#-#-#-# tmp.25.idp.ptl.pot (PACKAGE VERSION) #-#-#-#-#
+#. XXX: better error page
+#: ../larpe/qommon/ident/idp.ptl:162 ../larpe/root.ptl:87
+msgid "Unknown Token"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:176 ../larpe/qommon/ident/idp.ptl:180
+#: ../larpe/qommon/ident/idp.ptl:181 ../larpe/qommon/ident/idp.ptl:707
+msgid "Identity Providers"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:183 ../larpe/qommon/ident/idp.ptl:221
+#: ../larpe/qommon/ident/idp.ptl:405 ../larpe/admin/hosts.ptl:1328
+#: ../larpe/admin/users.ptl:238 ../larpe/admin/fields_prefill.ptl:125
+#: ../larpe/admin/forms_prefill.ptl:122
+msgid "New"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:184
+msgid "Create new from remote URL"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:205
+msgid "Broken"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:223 ../larpe/qommon/ident/idp.ptl:491
+#: ../larpe/qommon/ident/idp.ptl:507 ../larpe/admin/settings.ptl:28
+msgid "Metadata"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:224 ../larpe/qommon/ident/idp.ptl:508
+#: ../larpe/admin/settings.ptl:29
+msgid "Public Key"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:225 ../larpe/qommon/ident/idp.ptl:509
+#: ../larpe/admin/settings.ptl:30
+msgid "CA Certificate Chain"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:226 ../larpe/qommon/ident/idp.ptl:510
+msgid "Client Key and Certificate"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:227 ../larpe/qommon/ident/idp.ptl:511
+msgid "Hide this provider from user lists"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:232 ../larpe/qommon/ident/idp.ptl:233
+#: ../larpe/qommon/ident/idp.ptl:406 ../larpe/qommon/ident/idp.ptl:407
+#: ../larpe/admin/settings.ptl:34 ../larpe/admin/settings.ptl:35
+msgid "New Identity Provider"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:257 ../larpe/qommon/ident/idp.ptl:306
+#: ../larpe/qommon/ident/idp.ptl:348 ../larpe/qommon/ident/idp.ptl:379
+#: ../larpe/qommon/ident/idp.ptl:591 ../larpe/qommon/ident/idp.ptl:648
+#: ../larpe/qommon/ident/idp.ptl:661 ../larpe/admin/settings.ptl:53
+#: ../larpe/admin/settings.ptl:87
+msgid "Bad metadata"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:313
+msgid "URL to metadata"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:330 ../larpe/qommon/ident/idp.ptl:358
+msgid "Resource not found"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:332 ../larpe/qommon/ident/idp.ptl:360
+#, python-format
+msgid "HTTP error on retrieval: %s"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:334 ../larpe/qommon/ident/idp.ptl:362
+msgid "Failed to retrieve file"
+msgstr ""
+
+#. ok when provided with a public key -> adding it for real
+#: ../larpe/qommon/ident/idp.ptl:352 ../larpe/qommon/ident/idp.ptl:395
+msgid "URL to public key"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:373
+msgid "Error in this metadata file"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:392
+msgid "File looks like a bad metadata file"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:397
+msgid "The metadata file does not embed a public key, please provide it here."
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:484 ../larpe/qommon/ident/idp.ptl:489
+#: ../larpe/qommon/ident/idp.ptl:542 ../larpe/admin/settings.ptl:167
+msgid "Identity Provider"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:500
+msgid "Update from remote URL"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:516 ../larpe/qommon/ident/idp.ptl:517
+msgid "Edit Identity Provider"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:536
+msgid "You are about to irrevocably remove this identity provider."
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:544
+msgid "Deleting"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:546
+msgid "Deleting Identity Provider"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:650
+msgid "Bad metadata or missing public key"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:673
+msgid "Configure Liberty/SAML identification method"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:685 ../larpe/qommon/ident/idp.ptl:689
+#: ../larpe/admin/settings.ptl:132
+msgid "Service Provider"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:685
+msgid "Configure Liberty / SAML 2.0 parameters"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:689
+msgid "Configure Liberty parameters"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:696
+msgid "ID-FF 1.2 Service Provider Metadata"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:697
+msgid "Download Service Provider ID-FF 1.2 Metadata file"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:703
+msgid "SAML 2.0 Service Provider Metadata"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:704
+msgid "Download Service Provider SAML 2.0 Metadata file"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:707
+msgid "Add and remove identity providers"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:728
+msgid "Liberty Provider ID"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:731
+msgid "Liberty Base URL"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:733
+msgid "SAML 2.0 Provider ID"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:737
+msgid "SAML 2.0 Base URL"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:740
+msgid "Provider ID"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:742
+msgid "Base URL"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:745 ../larpe/admin/settings.ptl:215
+msgid "Organisation Name"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:747
+msgid "Signing Private Key"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:748
+msgid "Signing Public Key"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:749
+msgid "Encryption Private Key"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:750
+msgid "Encryption Public Key"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:753
+msgid "Identity Provider Introduction, Common Domain"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:754 ../larpe/qommon/ident/idp.ptl:759
+msgid "Disabled if empty"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:758
+msgid "Identity Provider Introduction, URL of Cookie Getter"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:767 ../larpe/qommon/ident/idp.ptl:768
+#: ../larpe/admin/settings.ptl:222 ../larpe/admin/settings.ptl:223
+msgid "Service Provider Configuration"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:1025
+msgid "Grab user details with ID-WSF on first logon"
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:1029
+msgid "Lasso version is too old for this support."
+msgstr ""
+
+#: ../larpe/qommon/ident/idp.ptl:1070
+msgid "Liberty/SAML2 identity provider"
+msgstr ""
+
+#: ../larpe/qommon/backoffice/menu.ptl:39
+msgid "admin"
+msgstr ""
+
+#: ../larpe/qommon/backoffice/menu.ptl:59
+#, python-format
+msgid "Back Office of %s"
+msgstr ""
+
+#: ../larpe/qommon/misc.py:289
+msgid "January"
+msgstr ""
+
+#: ../larpe/qommon/misc.py:289
+msgid "February"
+msgstr ""
+
+#: ../larpe/qommon/misc.py:289
+msgid "March"
+msgstr ""
+
+#: ../larpe/qommon/misc.py:289
+msgid "April"
+msgstr ""
+
+#: ../larpe/qommon/misc.py:290
+msgid "May"
+msgstr ""
+
+#: ../larpe/qommon/misc.py:290
+msgid "June"
+msgstr ""
+
+#: ../larpe/qommon/misc.py:290
+msgid "July"
+msgstr ""
+
+#: ../larpe/qommon/misc.py:290
+msgid "August"
+msgstr ""
+
+#: ../larpe/qommon/misc.py:291
+msgid "September"
+msgstr ""
+
+#: ../larpe/qommon/misc.py:291
+msgid "October"
+msgstr ""
+
+#: ../larpe/qommon/misc.py:291
+msgid "November"
+msgstr ""
+
+#: ../larpe/qommon/misc.py:291
+msgid "December"
+msgstr ""
+
+#: ../larpe/qommon/misc.py:311
+msgid "Monday"
+msgstr ""
+
+#: ../larpe/qommon/misc.py:311
+msgid "Tuesday"
+msgstr ""
+
+#: ../larpe/qommon/misc.py:311
+msgid "Wednesday"
+msgstr ""
+
+#: ../larpe/qommon/misc.py:312
+msgid "Thursday"
+msgstr ""
+
+#: ../larpe/qommon/misc.py:312
+msgid "Friday"
+msgstr ""
+
+#: ../larpe/qommon/misc.py:312
+msgid "Saturday"
+msgstr ""
+
+#: ../larpe/qommon/misc.py:312
+msgid "Sunday"
+msgstr ""
+
+#: ../larpe/qommon/publisher.py:97
+msgid "This feature is not yet implemented."
+msgstr ""
+
+#: ../larpe/qommon/publisher.py:98
+msgid "Sorry"
+msgstr ""
+
+#: ../larpe/qommon/publisher.py:119
+msgid ""
+"The server encountered an internal error and was unable to complete your "
+"request."
+msgstr ""
+
+#: ../larpe/qommon/publisher.py:120
+msgid "Internal Server Error"
+msgstr ""
+
+#: ../larpe/idwsf2.ptl:50
+msgid "Failed connecting to the original site."
+msgstr ""
+
+#: ../larpe/idwsf2.ptl:61
+msgid "Failed getting attributes from the attribute provider."
+msgstr ""
+
+#: ../larpe/idwsf2.ptl:64
+msgid "Failed getting attributes for an unknown reason."
+msgstr ""
+
+#: ../larpe/qommon/form.py:38
+msgid "required field"
+msgstr ""
+
+#: ../larpe/qommon/form.py:74
+msgid ""
+"The form you have submitted is invalid. Most likely it has been "
+"successfully submitted once already. Please review the form data and submit "
+"the form again."
+msgstr ""
+
+#: ../larpe/qommon/form.py:78
+msgid "There were errors processing your form. See below for details."
+msgstr ""
+
+#: ../larpe/qommon/form.py:284 ../larpe/qommon/form.py:286
+#: ../larpe/qommon/form.py:288
+msgid "must be a valid email address"
+msgstr ""
+
+#: ../larpe/qommon/form.py:298
+msgid "invalid address domain"
+msgstr ""
+
+#: ../larpe/qommon/form.py:314
+msgid "Prefill"
+msgstr ""
+
+#: ../larpe/qommon/form.py:329 ../larpe/qommon/form.py:570
+msgid "wrong format"
+msgstr ""
+
+#: ../larpe/qommon/form.py:377 ../larpe/qommon/form.py:381
+msgid "invalid date"
+msgstr ""
+
+#: ../larpe/qommon/form.py:384
+#, python-format
+msgid "invalid date: date must be on or after %s"
+msgstr ""
+
+#: ../larpe/qommon/form.py:387
+#, python-format
+msgid "invalid date; date must be on or before %s"
+msgstr ""
+
+#: ../larpe/qommon/form.py:419
+msgid "Previous Year"
+msgstr ""
+
+#: ../larpe/qommon/form.py:420
+msgid "Previous Month"
+msgstr ""
+
+#: ../larpe/qommon/form.py:421
+msgid "Next Year"
+msgstr ""
+
+#: ../larpe/qommon/form.py:422
+msgid "Next Month"
+msgstr ""
+
+#: ../larpe/qommon/form.py:423
+msgid "Close"
+msgstr ""
+
+#: ../larpe/qommon/form.py:424
+msgid "Choose Date"
+msgstr ""
+
+#: ../larpe/qommon/form.py:466
+msgid "invalid regular expression"
+msgstr ""
+
+#: ../larpe/qommon/form.py:526
+#, python-format
+msgid "You must select at most %d answers."
+msgstr ""
+
+#: ../larpe/qommon/form.py:579
+msgid "must start with http:// or https:// and have a domain name"
+msgstr ""
+
+#: ../larpe/qommon/form.py:607
+msgid "client error: access forbidden (error 403)"
+msgstr ""
+
+#: ../larpe/qommon/form.py:609
+msgid "client error: page not found (error 404)"
+msgstr ""
+
+#: ../larpe/qommon/form.py:611
+#, python-format
+msgid "client error: %(reason)s (error %(code)s)"
+msgstr ""
+
+#: ../larpe/qommon/form.py:614
+#, python-format
+msgid "server error: %(reason)s (error %(code)s)"
+msgstr ""
+
+#: ../larpe/qommon/form.py:628
+msgid ""
+"must start with http:// or https:// and have a domain name or start with /"
+msgstr ""
+
+#: ../larpe/qommon/form.py:646
+msgid "times"
+msgstr ""
+
+#: ../larpe/qommon/form.py:646
+msgid "plus"
+msgstr ""
+
+#: ../larpe/qommon/form.py:646
+msgid "minus"
+msgstr ""
+
+#: ../larpe/qommon/form.py:655
+#, python-format
+msgid "What is the result of %(a)d %(op)s %(b)d?"
+msgstr ""
+
+#: ../larpe/qommon/form.py:657
+msgid ""
+"Please answer this simple mathematical question as proof you are not a bot."
+msgstr ""
+
+#: ../larpe/qommon/form.py:666
+msgid "wrong answer"
+msgstr ""
+
+#: ../larpe/root.ptl:27
+msgid "Welcome to Larpe reverse proxy"
+msgstr ""
+
+#: ../larpe/root.ptl:29
+msgid "Configure Larpe"
+msgstr ""
+
+#: ../larpe/root.ptl:62 ../larpe/root.ptl:71 ../larpe/admin/users.ptl:142
+#: ../larpe/admin/users.ptl:145 ../larpe/admin/users.ptl:146
+#: ../larpe/admin/users.ptl:154 ../larpe/admin/users.ptl:179
+#: ../larpe/admin/users.ptl:181 ../larpe/admin/users.ptl:222
+msgid "Identification Token"
+msgstr ""
+
+#: ../larpe/root.ptl:73
+msgid "Please enter your identification token. "
+msgstr ""
+
+#: ../larpe/root.ptl:74 ../larpe/site_authentication.ptl:85
+msgid ""
+"Your local account will be federated with your Liberty Alliance account."
+msgstr ""
+
+#: ../larpe/qommon/template.ptl:185
+msgid "It has been sent to the site administrator for analyse."
+msgstr ""
+
+#: ../larpe/qommon/template.ptl:189
+#, python-format
+msgid "Continue to %s"
+msgstr ""
+
+#: ../larpe/qommon/template.ptl:192
+msgid "View Error Details"
+msgstr ""
+
+#: ../larpe/qommon/template.ptl:297
+#, python-format
+msgid "Logged in as %s."
+msgstr ""
+
+#: ../larpe/qommon/errors.ptl:16 ../larpe/qommon/errors.ptl:20
+msgid "Access Forbidden"
+msgstr ""
+
+#: ../larpe/qommon/errors.ptl:17 ../larpe/qommon/errors.ptl:21
+msgid "the homepage"
+msgstr ""
+
+#: ../larpe/qommon/errors.ptl:45
+msgid "Oops, the server borked severely"
+msgstr ""
+
+#: ../larpe/qommon/errors.ptl:48
+msgid ""
+"This is bad bad bad; perhaps you will have more luck if you retry in a few "
+"minutes ? "
+msgstr ""
+
+#: ../larpe/qommon/errors.ptl:50
+msgid ""
+"Alternatively you could harass the webmaster (who may have been emailed "
+"automatically with this incident but you can't be sure about this."
+msgstr ""
+
+#: ../larpe/qommon/errors.ptl:80
+msgid "Page not found"
+msgstr ""
+
+#: ../larpe/qommon/errors.ptl:82
+msgid ""
+"The requested link does not exist on this site. If you arrived here by "
+"following a link from an external page, please inform that page's maintainer."
+msgstr ""
+
+#: ../larpe/qommon/saml2.ptl:37 ../larpe/qommon/saml2.ptl:62
+#: ../larpe/qommon/saml2.ptl:76 ../larpe/qommon/saml2.ptl:204
+#: ../larpe/qommon/saml2.ptl:230 ../larpe/saml2.ptl:32 ../larpe/saml2.ptl:46
+msgid "SAML 2.0 support not yet configured."
+msgstr ""
+
+#: ../larpe/qommon/saml2.ptl:83 ../larpe/saml2.ptl:53
+msgid "Missing SAML Artifact"
+msgstr ""
+
+#: ../larpe/qommon/saml2.ptl:86
+msgid "Authentication request initiated by an unaffiliated provider."
+msgstr ""
+
+#: ../larpe/qommon/saml2.ptl:107
+msgid "Authentication failure; federation not found"
+msgstr ""
+
+#: ../larpe/qommon/saml2.ptl:208
+msgid "No SAML Response"
+msgstr ""
+
+#: ../larpe/qommon/saml2.ptl:216 ../larpe/qommon/saml2.ptl:243
+msgid "Request from unknown provider ID"
+msgstr ""
+
+#: ../larpe/qommon/saml2.ptl:218 ../larpe/qommon/saml2.ptl:220
+#: ../larpe/qommon/saml2.ptl:222 ../larpe/qommon/saml2.ptl:245
+#: ../larpe/qommon/saml2.ptl:247 ../larpe/qommon/saml2.ptl:249
+msgid "Error checking signature"
+msgstr ""
+
+#: ../larpe/qommon/saml2.ptl:235
+msgid "No SAML Response in query string"
+msgstr ""
+
+#: ../larpe/qommon/http_response.py:91
+msgid "registered"
+msgstr ""
+
+#: ../larpe/qommon/http_response.py:104
+msgid "running"
+msgstr ""
+
+#: ../larpe/qommon/http_response.py:108
+msgid "completed"
+msgstr ""
+
+#: ../larpe/admin/root.ptl:44 ../larpe/admin/hosts.ptl:1289
+#: ../larpe/admin/hosts.ptl:1290 ../larpe/admin/hosts.ptl:1327
+#: ../larpe/admin/hosts.ptl:1334
+msgid "Hosts"
+msgstr ""
+
+#: ../larpe/admin/root.ptl:45 ../larpe/admin/users.ptl:193
+#: ../larpe/admin/users.ptl:194 ../larpe/admin/users.ptl:237
+#: ../larpe/admin/users.ptl:271
+msgid "Users"
+msgstr ""
+
+#: ../larpe/admin/root.ptl:48
+msgid "Liberty Alliance Reverse Proxy"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:128
+msgid "Liberty Alliance & SAML 2.0 Service Provider"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:130
+msgid "Liberty Alliance Service Provider"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:132
+msgid "Configure Larpe as a Service Provider"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:142 ../larpe/admin/hosts.ptl:898
+msgid "SAML 2.0 Metadata"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:143
+msgid "Download SAML 2.0 metadata file for Larpe"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:149 ../larpe/admin/hosts.ptl:903
+msgid "ID-FF 1.2 Metadata"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:150
+msgid "Download ID-FF 1.2 metadata file for Larpe"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:156 ../larpe/admin/hosts.ptl:912
+msgid "Public key"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:157 ../larpe/admin/hosts.ptl:913
+msgid "Download SSL Public Key file"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:160
+msgid "Liberty Alliance & SAML 2.0 Identity Provider"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:162
+msgid "Liberty Alliance Identity Provider"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:167
+msgid "Configure an identity provider"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:171
+msgid "Identity Provider metadatas"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:171
+msgid "See current identity provider metadatas"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:175
+msgid "Global parameters for the sites"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:179 ../larpe/admin/settings.ptl:302
+#: ../larpe/admin/settings.ptl:303
+msgid "Domain name"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:179
+msgid "Configure the base domain name for the sites"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:181 ../larpe/admin/settings.ptl:348
+#: ../larpe/admin/settings.ptl:349
+msgid "Apache 2 configuration generation"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:181
+msgid "Customise Apache 2 configuration generation"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:183
+msgid "Connect to the sites through a web proxy"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:186
+msgid "Customisation"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:190
+msgid "Configure site language"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:192
+msgid "Configure email settings"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:195
+msgid "Misc"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:199
+msgid "Configure options useful for debugging"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:318
+msgid "Domain name for the sites"
+msgstr ""
+
+#. TODO: Add the option "Both" and handle it in hosts configuration
+#: ../larpe/admin/settings.ptl:321
+msgid "Use HTTP or HTTPS"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:323
+msgid "Same as the site"
+msgstr ""
+
+#: ../larpe/admin/settings.ptl:341
+msgid ""
+"Automatically generate Apache 2 configuration for new hosts and reload "
+"Apache 2 after changes"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:32
+msgid ""
+"You must either choose a different hostname from Larpe or specify a reversed "
+"directory"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:79 ../larpe/admin/hosts.ptl:101
+msgid "Basic configuration"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:80
+msgid "Need domain name configuration"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:81
+#, python-format
+msgid ""
+"Before configuring hosts, you must\n"
+"<a href=\"../../../settings/domain_names\">setup a global domain name</a> "
+"in\n"
+"%(settings)s menu."
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:102
+msgid "Step 1 - Basic configuration"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:111
+msgid "Original site root address"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:113
+msgid ""
+"If your site address is http://test.org/index.php, put http://test.org/ here"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:116
+msgid "Use a proxy"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:117
+msgid ""
+"Uncheck it if Larpe doesn't need to use the proxy to connect to this site"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:121
+msgid ""
+"If Larpe needs to use a proxy to connect to this site, you must first "
+"configure\n"
+" it in <a href=\"../../../settings/proxy\">global proxy parameters</a>."
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:125 ../larpe/admin/hosts.ptl:315
+#: ../larpe/admin/hosts.ptl:433 ../larpe/admin/hosts.ptl:457
+#: ../larpe/admin/hosts.ptl:538 ../larpe/admin/hosts.ptl:574
+#: ../larpe/admin/hosts.ptl:853 ../larpe/admin/hosts.ptl:875
+#: ../larpe/admin/hosts.ptl:963
+msgid "Terminate"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:331
+msgid "Check site address and name"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:332
+msgid "Step 2 - Check the new site address works"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:334
+msgid "DNS configuration"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:336
+msgid ""
+"Before opening the following link, ensure you have configured your DNS\n"
+"for this address. If you don't have a DNS server and you just want to test "
+"Larpe, add this\n"
+"domain name in the file \"/etc/hosts\"."
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:340
+#, python-format
+msgid ""
+"Then you can open this link in a new window or tab and see if your site\n"
+"is displayed. If it's ok, you can click the \"%(next)s\" button. Otherwise, "
+"click the \"%(previous)s\"\n"
+"button and check your settings."
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:344
+msgid "Site adress and name"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:346
+msgid "The new address of this site is "
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:348
+#, python-format
+msgid "The name of this site is \"%s\"."
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:349
+msgid ""
+"You can also <a href=\"modify_site_address_and_name\">\n"
+"modify the address or the name of this site</a>"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:365
+msgid "An host with the same name already exists"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:372 ../larpe/admin/hosts.ptl:373
+msgid "Modify site address and name"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:379
+msgid "Address"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:418
+msgid "Authentication and logout"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:419
+msgid "Step 3 - Configure authentication and logout pages"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:425 ../larpe/admin/hosts.ptl:954
+msgid "Authentication form page address"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:426
+msgid "Address of a page on the site which contains the authentication form"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:428
+msgid "Logout address"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:429
+msgid "Address of the logout link on the site"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:445
+msgid "(computed automatically)"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:467
+msgid "Auto detected configuration"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:468
+msgid ""
+"Step 4 - Check automatically detected configuration for the authentication "
+"form"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:471
+msgid "Address where the authentication form must be sent"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:472
+msgid "Name of the login field"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:473
+msgid "Name of the password field"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:492
+msgid ""
+"The following authentication form parameters have been detected. If they "
+"look right, you can go to the next step.\n"
+"If you think they are wrong, go back and check your settings then try "
+"again.\n"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:497
+msgid ""
+"The following authentication form parameters in red haven't been correctly "
+"detected. Go back and check\n"
+"your settings then try again.\n"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:518 ../larpe/admin/hosts.ptl:1234
+msgid "Credentials"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:519
+msgid "Step 5 - Fill in a valid username/password for this site"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:584
+msgid "Check authentication"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:585
+msgid "Step 6 - Check the authentication process"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:604
+msgid "Authentication succeeded ! You can go to the next step."
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:606
+msgid "Authentication has failed. To resolve this problem, you can :"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:609
+msgid "Try authentication again"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:611
+msgid "See the response of the authentication requests"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:613 ../larpe/admin/hosts.ptl:695
+msgid "Modify the parameters of the authentication requests"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:615
+msgid "Change the way Larpe detects the authentication is successful or not"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:616
+msgid "Go back and change your username and/or password"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:622 ../larpe/admin/hosts.ptl:623
+msgid "Authentication response"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:625
+msgid "Response of the request with good credentials"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:628 ../larpe/admin/hosts.ptl:639
+msgid "HTTP status code"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:633 ../larpe/admin/hosts.ptl:644
+msgid "See HTML page"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:636
+msgid "Response of the request with bad credentials"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:665 ../larpe/admin/hosts.ptl:666
+msgid "Authentication success criteria"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:672
+msgid "Authentication system of the original site"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:674
+msgid "Check the existence of a password field"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:675
+msgid "Match some text to detect an authentication failure"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:680
+msgid "Text to match in case of authentication failure"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:694
+msgid "Authentication request"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:699
+msgid "Modify POST parameters"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:699
+msgid ""
+"Configure the form attributes that will be sent within the authentication "
+"POST requests"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:701
+msgid "Modify HTTP headers"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:701
+msgid "Configure the HTTP headers of the authentication requests made by Larpe"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:715
+msgid "POST parameters"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:716
+msgid "Configure POST parameters"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:718
+msgid ""
+"Here are the detected form fields that will be sent as parameters of the\n"
+"authentication POST request. You can desactivate some or all of them, or "
+"change their value."
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:756
+msgid "HTTP headers"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:757
+msgid "Configure HTTP headers"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:759
+msgid ""
+"Here are the HTTP headers that will be sent within the authentication\n"
+"POST request. You can desactivate some or all of them, or change their value."
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:772
+msgid ""
+"The headers \"Host\", \"Accept-Encoding\" and \"Content-Length\" will also "
+"automatically be sent."
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:775
+msgid ""
+"As Larpe uses a proxy for this site, the headers \"Proxy-Authorization\",\n"
+"\"Proxy-Connection\" and \"Keep-Alive\" will be sent as well."
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:825
+msgid "SSO initiation"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:826
+msgid "Step 7 - Configure how a Single Sign On can be initiated"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:828
+msgid ""
+"Most sites use one of the following 2 ways to allow users to initialise an "
+"authentication :"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:831
+msgid ""
+"The site has a single authentication page. It redirects users to this page "
+"when\n"
+"they click a \"Login\" button or try to access a page which require users to "
+"be authenticated."
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:834
+msgid ""
+"The site includes an authentication form in most or all of his pages. Users "
+"can\n"
+"authenticate on any of these pages, and don't need to be redirected to a "
+"separate authentication page."
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:839
+msgid "Select the way your site works :"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:847
+msgid "The site has a single authentication page"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:848
+msgid "The site includes an authentication form in most or all pages"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:885
+msgid "Metadatas"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:886
+#, python-format
+msgid "Step 8 - Metadatas of %(site_name)s"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:889
+msgid ""
+"Download the metadatas and the public key for this site and\n"
+"upload them on your identity provider in order to use Liberty Alliance "
+"features."
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:899
+msgid "Download SAML 2.0 metadata file"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:904
+msgid "Download ID-FF 1.2 metadata file"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:906
+msgid "No metadata has been generated for this host."
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:915
+msgid "No public key has been generated for this host."
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:927
+msgid "Advanced options"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:928
+msgid "Step 9 - Advanced options"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:930
+msgid "Configure advanced options to setup the last details of your site."
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:931
+#, python-format
+msgid ""
+"If you don't know what to configure here, just click %(next)s and\n"
+"come here later if needed."
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:944
+msgid "Redirect the root URL of the site to the login page."
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:946
+msgid "Return address"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:947
+msgid "Where the user will be redirected after a successful authentication"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:949
+msgid "Error address"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:950
+msgid "Where the user will be redirected after a disconnection or an error"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:952
+msgid "URL which must initiate the SSO"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:953
+#, python-format
+msgid ""
+"Address which must initiate the SSO. If empty, defaults to the previously\n"
+"specified \"%s\""
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:956
+msgid "Apache HTML proxy"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:957
+msgid ""
+"Converts urls in the HTML pages according to the host new domain name.\n"
+"Disabled by default because it makes some sites not work correctly."
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:988 ../larpe/admin/hosts.ptl:1009
+msgid "Finish"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:996
+msgid "Check everything works"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:997
+msgid "Step 10 - Check everything works"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1000
+msgid ""
+"Now you can fully test your site, start from the home page, initiate a\n"
+"Single Sign On, federate your identities and do a Single Logout."
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1003
+msgid "The address of your site is : "
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1008
+#, python-format
+msgid ""
+"If everything works, click the \"%(finish)s\" button, otherwise you can go\n"
+"back and check your settings."
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1120 ../larpe/admin/hosts.ptl:1135
+#: ../larpe/admin/hosts.ptl:1166
+msgid "(filled by users)"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1217
+msgid "Configuration assistant"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1222
+msgid "Address of the original site"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1222
+msgid "Configure the root address of the site"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1225
+msgid "New address and name"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1225
+msgid "Configure the new address and name of this site"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1228
+msgid "Authentication and logout addresses"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1228
+msgid "Configure the authentication and logout addresses of the original site"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1231
+msgid "Check auto detected configuration"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1231
+msgid "Check the automatically detected configuration is right"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1234
+msgid "Configure some valid credentials to authenticate on the original site"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1237
+msgid "Retry authentication"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1237
+msgid ""
+"Retry sending an authentication request to the site to check if your new "
+"parameters work well"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1240
+msgid "Check authentication response"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1240
+msgid "Check the response from the latest authentication request"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1243
+msgid "Configure authentication success criteria"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1243
+msgid "Specify how Larpe knows if the authentication has succeeded or not"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1246
+msgid "Modify authentication request"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1246
+msgid "Modify POST fields or HTTP headers of the authentication request"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1249
+msgid "Configure how a Single Sign On can be initiated"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1252
+msgid "Metadatas and key"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1252
+msgid "Download SAML 2.0 or ID-FF metadatas and SSL public key"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1255
+msgid "Adavanced options"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1255
+msgid "Configure advanced options to setup the last details of your site"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1259
+msgid "Form prefilling with ID-WSF"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1263 ../larpe/admin/forms_prefill.ptl:94
+#: ../larpe/admin/forms_prefill.ptl:101
+msgid "Forms"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1263
+msgid "Configure the forms to prefill"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1269
+msgid "You are about to irrevocably delete this host."
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1275 ../larpe/admin/users.ptl:126
+#: ../larpe/admin/fields_prefill.ptl:78 ../larpe/admin/forms_prefill.ptl:81
+msgid "Delete"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1276 ../larpe/admin/hosts.ptl:1277
+msgid "Delete Host"
+msgstr ""
+
+#: ../larpe/admin/hosts.ptl:1293 ../larpe/admin/hosts.ptl:1320
+#: ../larpe/admin/hosts.ptl:1321
+msgid "New Host"
+msgstr ""
+
+#: ../larpe/admin/users.ptl:23 ../larpe/admin/users.ptl:31
+msgid "User Name"
+msgstr ""
+
+#: ../larpe/admin/users.ptl:97
+msgid "Debug"
+msgstr ""
+
+#: ../larpe/admin/users.ptl:110 ../larpe/admin/users.ptl:111
+msgid "Edit User"
+msgstr ""
+
+#: ../larpe/admin/users.ptl:120
+msgid "You are about to irrevocably delete this user."
+msgstr ""
+
+#: ../larpe/admin/users.ptl:127
+msgid "Delete User"
+msgstr ""
+
+#: ../larpe/admin/users.ptl:128
+msgid "Deleting User :"
+msgstr ""
+
+#: ../larpe/admin/users.ptl:136
+msgid "Generate"
+msgstr ""
+
+#: ../larpe/admin/users.ptl:147
+msgid ""
+"You are about to generate a token than can be used to federate the account."
+msgstr ""
+
+#: ../larpe/admin/users.ptl:148
+msgid ""
+"After that, you will have the choice to send it to the user by email so that "
+"he can federate his accounts."
+msgstr ""
+
+#: ../larpe/admin/users.ptl:150
+#, python-format
+msgid "Note that user has already been issued an identification token : %s"
+msgstr ""
+
+#: ../larpe/admin/users.ptl:160
+#, python-format
+msgid "Identification Token for %s"
+msgstr ""
+
+#: ../larpe/admin/users.ptl:164
+msgid "Done"
+msgstr ""
+
+#: ../larpe/admin/users.ptl:166
+msgid "Send by email"
+msgstr ""
+
+#: ../larpe/admin/users.ptl:172
+#, python-format
+msgid ""
+"You have been given an identification token.\n"
+"\n"
+"Your token is %(token)s\n"
+"\n"
+"Click on %(url)s to use it.\n"
+msgstr ""
+
+#: ../larpe/admin/users.ptl:182
+msgid "Failed sending email. Check your email configuration."
+msgstr ""
+
+#: ../larpe/admin/users.ptl:198 ../larpe/admin/users.ptl:241
+msgid "Liberty support must be setup before creating users."
+msgstr ""
+
+#: ../larpe/admin/users.ptl:202 ../larpe/admin/users.ptl:253
+#: ../larpe/admin/users.ptl:254
+msgid "New User"
+msgstr ""
+
+#: ../larpe/admin/users.ptl:225
+#, python-format
+msgid "Identification Token (current: %s)"
+msgstr ""
+
+#: ../larpe/admin/fields_prefill.ptl:15
+msgid "Field name"
+msgstr ""
+
+#: ../larpe/admin/fields_prefill.ptl:17
+msgid "Xpath of the attribute"
+msgstr ""
+
+#: ../larpe/admin/fields_prefill.ptl:18
+msgid "Example: /pp:PP/pp:InformalName"
+msgstr ""
+
+#: ../larpe/admin/fields_prefill.ptl:19
+msgid "Number of the field in the data"
+msgstr ""
+
+#: ../larpe/admin/fields_prefill.ptl:21
+msgid ""
+"Change it if there are multiple fields corresponding to the same Xpath and "
+"you want to get another than the first one"
+msgstr ""
+
+#: ../larpe/admin/fields_prefill.ptl:22
+msgid "Get raw XML value"
+msgstr ""
+
+#: ../larpe/admin/fields_prefill.ptl:24
+msgid "Python regexp of a string to match"
+msgstr ""
+
+#: ../larpe/admin/fields_prefill.ptl:26
+msgid "Python regexp of the replacing string"
+msgstr ""
+
+#: ../larpe/admin/fields_prefill.ptl:28
+msgid "Options mapping for a select field"
+msgstr ""
+
+#: ../larpe/admin/fields_prefill.ptl:29
+msgid "Add item"
+msgstr ""
+
+#: ../larpe/admin/fields_prefill.ptl:72
+msgid "You are about to irrevocably delete this field."
+msgstr ""
+
+#: ../larpe/admin/fields_prefill.ptl:79 ../larpe/admin/fields_prefill.ptl:80
+msgid "Delete Field"
+msgstr ""
+
+#: ../larpe/admin/fields_prefill.ptl:91 ../larpe/admin/fields_prefill.ptl:98
+#: ../larpe/admin/forms_prefill.ptl:50
+msgid "Fields"
+msgstr ""
+
+#: ../larpe/admin/fields_prefill.ptl:101
+msgid "New Field"
+msgstr ""
+
+#: ../larpe/admin/forms_prefill.ptl:16
+msgid "Form name"
+msgstr ""
+
+#: ../larpe/admin/forms_prefill.ptl:17
+msgid "Only used for display"
+msgstr ""
+
+#: ../larpe/admin/forms_prefill.ptl:18
+msgid "Form address"
+msgstr ""
+
+#: ../larpe/admin/forms_prefill.ptl:20
+msgid "ID-WSF data profile"
+msgstr ""
+
+#: ../larpe/admin/forms_prefill.ptl:21
+msgid "Example: urn:liberty:id-sis-pp:2005-05"
+msgstr ""
+
+#: ../larpe/admin/forms_prefill.ptl:22
+msgid "ID-WSF data XML prefix"
+msgstr ""
+
+#: ../larpe/admin/forms_prefill.ptl:23
+msgid "Example: pp"
+msgstr ""
+
+#: ../larpe/admin/forms_prefill.ptl:45
+msgid "Form prefilling configuration"
+msgstr ""
+
+#: ../larpe/admin/forms_prefill.ptl:48
+msgid "Configure this form"
+msgstr ""
+
+#: ../larpe/admin/forms_prefill.ptl:50
+msgid "Configure the fields of this form"
+msgstr ""
+
+#: ../larpe/admin/forms_prefill.ptl:75
+msgid "You are about to irrevocably delete this form."
+msgstr ""
+
+#: ../larpe/admin/forms_prefill.ptl:82 ../larpe/admin/forms_prefill.ptl:83
+msgid "Delete Form"
+msgstr ""
+
+#: ../larpe/admin/forms_prefill.ptl:104
+msgid "New Form"
+msgstr ""
+
+#: ../larpe/site_authentication.ptl:55
+msgid "Local authentication"
+msgstr ""
+
+#: ../larpe/site_authentication.ptl:69
+msgid "Authentication failure"
+msgstr ""
+
+#: ../larpe/site_authentication.ptl:72
+#, python-format
+msgid "Connection failed : %s"
+msgstr ""
+
+#: ../larpe/site_authentication.ptl:75
+#, python-format
+msgid "This service provider is not fully configured : %s"
+msgstr ""
+
+#: ../larpe/site_authentication.ptl:78
+#, python-format
+msgid "Unknown error : %s"
+msgstr ""
+
+#: ../larpe/site_authentication.ptl:84
+msgid "Please type your login and password for this Service Provider."
+msgstr ""
+
+#: ../larpe/users.py:38
+msgid "Unknown User"
+msgstr ""
diff --git a/larpe/tags/release-1.0/pylintrc b/larpe/tags/release-1.0/pylintrc
new file mode 100644
index 0000000..18f2afd
--- /dev/null
+++ b/larpe/tags/release-1.0/pylintrc
@@ -0,0 +1,310 @@
+# lint Python modules using external checkers.
+#
+# This is the main checker controlling the other ones and the reports
+# generation. It is itself both a raw checker and an astng checker in order
+# to:
+# * handle message activation / deactivation at the module level
+# * handle some basic but necessary stats'data (number of classes, methods...)
+#
+[MASTER]
+
+# Specify a configuration file.
+#rcfile=
+
+# Python code to execute, usually for sys.path manipulation such as
+# pygtk.require().
+#init-hook=
+
+# Profiled execution.
+profile=no
+
+# Add <file or directory> to the black list. It should be a base name, not a
+# path. You may set this option multiple times.
+ignore=.svn,qommon
+
+# Pickle collected data for later comparisons.
+persistent=yes
+
+# Set the cache size for astng objects.
+cache-size=500
+
+# List of plugins (as comma separated values of python modules names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+
+[MESSAGES CONTROL]
+
+# Enable only checker(s) with the given id(s). This option conflicts with the
+# disable-checker option
+#enable-checker=
+
+# Enable all checker(s) except those with the given id(s). This option
+# conflicts with the enable-checker option
+#disable-checker=
+
+# Enable all messages in the listed categories.
+#enable-msg-cat=
+
+# Disable all messages in the listed categories.
+#disable-msg-cat=
+
+# Enable the message(s) with the given id(s).
+#enable-msg=
+
+# Disable the message(s) with the given id(s).
+disable-msg=C0111,R0904,W0403
+
+
+[REPORTS]
+
+# Set the output format. Available formats are text, parseable, colorized, msvs
+# (visual studio) and html
+output-format=text
+
+# Include message's id in output
+include-ids=yes
+
+# Put messages in a separate file for each module / package specified on the
+# command line instead of printing them on stdout. Reports (if any) will be
+# written in a file name "pylint_global.[txt|html]".
+files-output=no
+
+# Tells wether to display a full report or only the messages
+reports=no
+
+# Python expression which should return a note less than 10 (10 is the highest
+# note). You have access to the variables errors warning, statement which
+# respectivly contain the number of errors / warnings messages and the total
+# number of statements analyzed. This is used by the global evaluation report
+# (R0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Add a comment according to your evaluation note. This is used by the global
+# evaluation report (R0004).
+comment=no
+
+# Enable the report(s) with the given id(s).
+#enable-report=
+
+# Disable the report(s) with the given id(s).
+#disable-report=
+
+
+# try to find bugs in the code using type inference
+#
+[TYPECHECK]
+
+# Tells wether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# List of classes names for which member attributes should not be checked
+# (useful for classes with attributes dynamicaly set).
+ignored-classes=SQLObject
+
+# When zope mode is activated, add a predefined set of Zope acquired attributes
+# to generated-members.
+zope=no
+
+# List of members which are set dynamically and missed by pylint inference
+# system, and so shouldn't trigger E0201 when accessed.
+generated-members=REQUEST,acl_users,aq_parent
+
+
+# checks for
+# * unused variables / imports
+# * undefined variables
+# * redefinition of variable from builtins or from an outer scope
+# * use of variable before assigment
+#
+[VARIABLES]
+
+# Tells wether we should check for unused import in __init__ files.
+init-import=no
+
+# A regular expression matching names used for dummy variables (i.e. not used).
+dummy-variables-rgx=_|dummy
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid to define new builtins when possible.
+additional-builtins=
+
+
+# checks for :
+# * doc strings
+# * modules / classes / functions / methods / arguments / variables name
+# * number of arguments, local variables, branchs, returns and statements in
+# functions, methods
+# * required module attributes
+# * dangerous default values as arguments
+# * redefinition of function / method / class
+# * uses of the global statement
+#
+[BASIC]
+
+# Required attributes for module, separated by a comma
+required-attributes=
+
+# Regular expression which should only match functions or classes name which do
+# not require a docstring
+no-docstring-rgx=__.*__
+
+# Regular expression which should only match correct module names
+module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
+
+# Regular expression which should only match correct module level names
+#const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$
+const-rgx=[a-z\_][a-z0-9\_]{2,30}$
+
+# Regular expression which should only match correct class names
+class-rgx=[A-Z_][a-zA-Z0-9]+$
+
+# Regular expression which should only match correct function names
+function-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct method names
+method-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct instance attribute names
+attr-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct argument names
+argument-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct variable names
+variable-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct list comprehension /
+# generator expression variable names
+inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
+
+# Good variable names which should always be accepted, separated by a comma
+good-names=i,j,k,ex,Run,_
+
+# Bad variable names which should always be refused, separated by a comma
+bad-names=foo,bar,baz,toto,tutu,tata
+
+# List of builtins function names that should not be used, separated by a comma
+bad-functions=map,filter,apply,input
+
+
+# checks for sign of poor/misdesign:
+# * number of methods, attributes, local variables...
+# * size, complexity of functions, methods
+#
+[DESIGN]
+
+# Maximum number of arguments for function / method
+max-args=5
+
+# Maximum number of locals for function / method body
+max-locals=15
+
+# Maximum number of return / yield for function / method body
+max-returns=6
+
+# Maximum number of branch for function / method body
+max-branchs=12
+
+# Maximum number of statements in function / method body
+max-statements=50
+
+# Maximum number of parents for a class (see R0901).
+max-parents=7
+
+# Maximum number of attributes for a class (see R0902).
+max-attributes=7
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=2
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=20
+
+
+# checks for :
+# * methods without self as first argument
+# * overridden methods signature
+# * access only to existant members via self
+# * attributes not defined in the __init__ method
+# * supported interfaces implementation
+# * unreachable code
+#
+[CLASSES]
+
+# List of interface methods to ignore, separated by a comma. This is used for
+# instance to not check methods defines in Zope's Interface base class.
+ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,__new__,setUp
+
+
+# checks for
+# * external modules dependencies
+# * relative / wildcard imports
+# * cyclic imports
+# * uses of deprecated modules
+#
+[IMPORTS]
+
+# Deprecated modules which should not be used, separated by a comma
+deprecated-modules=regsub,string,TERMIOS,Bastion,rexec
+
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report R0402 must not be disabled)
+import-graph=
+
+# Create a graph of external dependencies in the given file (report R0402 must
+# not be disabled)
+ext-import-graph=
+
+# Create a graph of internal dependencies in the given file (report R0402 must
+# not be disabled)
+int-import-graph=
+
+
+# checks for :
+# * unauthorized constructions
+# * strict indentation
+# * line length
+# * use of <> instead of !=
+#
+[FORMAT]
+
+# Maximum number of characters on a single line.
+max-line-length=100
+
+# Maximum number of lines in a module
+max-module-lines=1000
+
+# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
+# tab).
+indent-string=' '
+
+
+# checks for similarities and duplicated code. This computation may be
+# memory / CPU intensive, so you should disable it if you experiments some
+# problems.
+#
+[SIMILARITIES]
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
+
+
+# checks for:
+# * warning notes in the code like FIXME, XXX
+# * PEP 263: source code with non ascii character but no encoding declaration
+#
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,XXX,TODO
diff --git a/larpe/tags/release-1.0/root/index.html b/larpe/tags/release-1.0/root/index.html
new file mode 100644
index 0000000..cf449e7
--- /dev/null
+++ b/larpe/tags/release-1.0/root/index.html
@@ -0,0 +1,18 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>Larpe</title>
+ <link rel="stylesheet" type="text/css" href="/css/larpe.css"/>
+ <style type="text/css">
+p#larpe {
+ margin-top: 30%;
+ text-align: center;
+ font-weight: bold;
+}
+</style>
+ </head>
+ <body>
+ <p id="larpe">
+ Larpe
+ </p>
+ </body>
+</html>
diff --git a/larpe/tags/release-1.0/root/larpe/css/arrow-right-2.0.png b/larpe/tags/release-1.0/root/larpe/css/arrow-right-2.0.png
new file mode 100644
index 0000000..f6a66f5
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/arrow-right-2.0.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/bg-footer.png b/larpe/tags/release-1.0/root/larpe/css/bg-footer.png
new file mode 100644
index 0000000..812ee25
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/bg-footer.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/dc2/admin.css b/larpe/tags/release-1.0/root/larpe/css/dc2/admin.css
new file mode 100644
index 0000000..7a7caac
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/dc2/admin.css
@@ -0,0 +1,283 @@
+@import url(../larpe-common.css);
+
+html, body {
+ margin: 0;
+ background: white url(page-bg.png) repeat-y;
+}
+
+div#main-content {
+ margin-left: 160px;
+ margin-top: -10px;
+ margin-right: 20px;
+}
+
+div#main-content h1 {
+ color: #006699;
+ font-size: 120%;
+}
+
+div#main-content h2 {
+ color: #006699;
+ font-size: 115%;
+}
+
+div#main-content h3 {
+ color: #006699;
+ font-size: 108%
+}
+
+
+
+div#header {
+ margin: 0;
+ background: white url(head-bg.png) repeat-x;
+ height: 58px;
+}
+
+ul#menu {
+ background: transparent url(head-logo.png) no-repeat;
+ width: 177px;
+ margin: 0;
+ padding: 80px 0 0 5px;
+}
+
+a {
+ color: #0066cc;
+ text-decoration: none;
+ border-bottom: 1px dotted #ff9900;
+}
+
+p.commands a {
+ border: 0;
+}
+
+ul#menu a {
+ font-weight: bold;
+}
+
+ul#menu li.active a {
+ border-bottom: 1px solid #ff9900;
+}
+
+ul#menu li {
+ font-size: 90%;
+ margin-bottom: 1em;
+ max-width: 130px;
+}
+
+div#footer {
+ display: none;
+}
+
+ul.user-info {
+ position: absolute;
+ margin: 0;
+ padding: 0;
+ right: 25px;
+ top: 13px;
+ font-size: 70%;
+ font-weight: bold;
+}
+
+ul.user-info li {
+ display: inline;
+ padding-left: 10px;
+}
+
+/** end of dc2 changes **/
+
+
+
+ul.biglist {
+ margin: 0;
+ padding: 0;
+}
+
+ul.biglist li {
+ list-style-type: none;
+ margin: 4px 0;
+ padding: 0 2px;
+ border: 1px solid #888;
+ background: #ffe;
+ clear: both;
+}
+
+ul.biglist li p.details {
+ display: block;
+ margin: 0;
+ color: #555;
+ font-size: 80%;
+}
+
+
+ul.biglist li p.commands {
+ float: right;
+ margin-top: -17px;
+}
+
+ul.biglist li p.commands img {
+ padding-right: 5px;
+}
+
+a img {
+ border: 0;
+}
+
+td.time {
+ text-align: right;
+}
+
+ul.biglist li.disabled, ul.biglist li.disabled p.details {
+ color: #999;
+ background: #ddd;
+}
+
+
+dl dt {
+ margin : 0;
+ padding : 0 0 0 0;
+}
+
+dl dd {
+ margin : 0.3em 0 1.5em 10px;
+}
+
+
+img.theme-icon {
+ float: right;
+ margin: -16px 4px 0px 3px;
+ border: 1px solid #999;
+}
+
+div#new-field table {
+ margin: 0;
+ padding: 0;
+}
+
+div#new-field div.widget {
+ margin: 0;
+ padding: 0;
+}
+
+div#new-field div.buttons {
+ margin: 0;
+ padding: 0;
+}
+
+div#new-field div.buttons input {
+ margin: 0;
+ padding: 0;
+}
+
+div#new-field {
+ border: 1px solid #888;
+ background: #ffe;
+ margin: 2em 0 4px 0;
+ padding: 0 2px;
+}
+
+div#new-field div.widget {
+}
+
+div#new-field h3 {
+ margin: 0;
+ font-size: 100%;
+}
+
+div#new-field br {
+ display: none;
+}
+
+div#new-field p.commands {
+ float: right;
+ margin-top: -17px;
+ margin-right: 3px;
+}
+
+div.WorkflowStatusWidget {
+ border-left: 1px solid black;
+}
+
+p#breadcrumb {
+ background: #e6e6e6;
+ -moz-border-radius: 6px;
+ padding: 3px 8px;
+ font-size: 80%;
+ border: 1px solid #bfbfbf;
+}
+
+/** steps **/
+#steps {
+ height: 32px;
+ margin-bottom: 1em;
+ background: #f0f0f0;
+ color: #aaa;
+}
+
+#steps ol {
+ list-style: none;
+ padding: 0 20px;
+}
+
+#steps li {
+ display: inline;
+ padding-right: 1em;
+ display: block;
+ float: left;
+ width: 30%;
+ list-style: none;
+}
+
+#steps ol ul {
+ display: none;
+}
+
+#steps span.marker {
+ font-size: 26px;
+ padding: 2px 9px;
+ font-weight: bold;
+ color: white;
+ text-align: center;
+ background: #ddd;
+ border: 1px solid #ddd;
+ -moz-border-radius: 0.7ex;
+}
+
+#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 ol ul {
+ display: none;
+}
+
+
+/** logs **/
+form#other-log-select {
+ margin-top: 2em;
+ padding-top: 1em;
+ border-top: 1px solid #999;
+}
+
+form#other-log-select select {
+ margin: 0 1em;
+}
+
+tr.level-error td {
+ border: 1px solid #800;
+ background: red;
+}
+
+tr.level-error td.message {
+ font-weight: bold;
+}
+
diff --git a/larpe/tags/release-1.0/root/larpe/css/dc2/head-bg.png b/larpe/tags/release-1.0/root/larpe/css/dc2/head-bg.png
new file mode 100644
index 0000000..1622ef8
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/dc2/head-bg.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/dc2/head-logo-empty.png b/larpe/tags/release-1.0/root/larpe/css/dc2/head-logo-empty.png
new file mode 100644
index 0000000..d347825
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/dc2/head-logo-empty.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/dc2/head-logo.png b/larpe/tags/release-1.0/root/larpe/css/dc2/head-logo.png
new file mode 100644
index 0000000..215542f
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/dc2/head-logo.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/dc2/page-bg.png b/larpe/tags/release-1.0/root/larpe/css/dc2/page-bg.png
new file mode 100644
index 0000000..729154e
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/dc2/page-bg.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/deg-top.png b/larpe/tags/release-1.0/root/larpe/css/deg-top.png
new file mode 100644
index 0000000..fab62e3
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/deg-top.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/dot999.png b/larpe/tags/release-1.0/root/larpe/css/dot999.png
new file mode 100644
index 0000000..a78020f
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/dot999.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/fond.jpg b/larpe/tags/release-1.0/root/larpe/css/fond.jpg
new file mode 100644
index 0000000..bfd94dc
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/fond.jpg
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/ico_user.png b/larpe/tags/release-1.0/root/larpe/css/ico_user.png
new file mode 100644
index 0000000..560be6f
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/ico_user.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/img/bulle.png b/larpe/tags/release-1.0/root/larpe/css/img/bulle.png
new file mode 100644
index 0000000..171402b
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/img/bulle.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/img/day-date.png b/larpe/tags/release-1.0/root/larpe/css/img/day-date.png
new file mode 100644
index 0000000..cbad44d
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/img/day-date.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/img/footer-500.png b/larpe/tags/release-1.0/root/larpe/css/img/footer-500.png
new file mode 100644
index 0000000..74002d7
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/img/footer-500.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/img/footer.jpg b/larpe/tags/release-1.0/root/larpe/css/img/footer.jpg
new file mode 100644
index 0000000..a1c8131
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/img/footer.jpg
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/img/h2.png b/larpe/tags/release-1.0/root/larpe/css/img/h2.png
new file mode 100644
index 0000000..add8cb6
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/img/h2.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/img/li.png b/larpe/tags/release-1.0/root/larpe/css/img/li.png
new file mode 100644
index 0000000..01f038a
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/img/li.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/img/linkscat.png b/larpe/tags/release-1.0/root/larpe/css/img/linkscat.png
new file mode 100644
index 0000000..59516c5
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/img/linkscat.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/img/page-500.png b/larpe/tags/release-1.0/root/larpe/css/img/page-500.png
new file mode 100644
index 0000000..9ff0f0f
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/img/page-500.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/img/page.png b/larpe/tags/release-1.0/root/larpe/css/img/page.png
new file mode 100644
index 0000000..b436915
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/img/page.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/img/search.png b/larpe/tags/release-1.0/root/larpe/css/img/search.png
new file mode 100644
index 0000000..6d1f037
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/img/search.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/img/sidebarh2.png b/larpe/tags/release-1.0/root/larpe/css/img/sidebarh2.png
new file mode 100644
index 0000000..b187493
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/img/sidebarh2.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/img/top-500.png b/larpe/tags/release-1.0/root/larpe/css/img/top-500.png
new file mode 100644
index 0000000..1fb77e7
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/img/top-500.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/img/top.jpg b/larpe/tags/release-1.0/root/larpe/css/img/top.jpg
new file mode 100644
index 0000000..bab94c3
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/img/top.jpg
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/img/top.png b/larpe/tags/release-1.0/root/larpe/css/img/top.png
new file mode 100644
index 0000000..f2adbb0
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/img/top.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/active-bg.gif b/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/active-bg.gif
new file mode 100644
index 0000000..d608c54
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/active-bg.gif
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/dark-bg.gif b/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/dark-bg.gif
new file mode 100644
index 0000000..1dea48a
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/dark-bg.gif
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/hover-bg.gif b/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/hover-bg.gif
new file mode 100644
index 0000000..fbf94fc
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/hover-bg.gif
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/menuarrow.gif b/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/menuarrow.gif
new file mode 100644
index 0000000..40c0aad
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/menuarrow.gif
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/normal-bg.gif b/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/normal-bg.gif
new file mode 100644
index 0000000..bdb5068
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/normal-bg.gif
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/rowhover-bg.gif b/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/rowhover-bg.gif
new file mode 100644
index 0000000..7715342
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/rowhover-bg.gif
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/status-bg.gif b/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/status-bg.gif
new file mode 100644
index 0000000..857108c
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/status-bg.gif
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/theme.css b/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/theme.css
new file mode 100644
index 0000000..d8b6af2
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/theme.css
@@ -0,0 +1,236 @@
+/* Distributed as part of The Coolest DHTML Calendar
+ Author: Mihai Bazon, www.bazon.net/mishoo
+ Copyright Dynarch.com 2005, www.dynarch.com
+*/
+
+/* The main calendar widget. DIV containing a table. */
+
+div.calendar { position: relative; }
+
+.calendar, .calendar table {
+ border: 1px solid #bdb2bf;
+ font-size: 11px;
+ color: #000;
+ cursor: default;
+ background: url("/css/jscalendar/aqua/normal-bg.gif");
+ font-family: "trebuchet ms",verdana,tahoma,sans-serif;
+}
+
+.calendar {
+ border-color: #797979;
+}
+
+/* Header part -- contains navigation buttons and day names. */
+
+.calendar .button { /* "<<", "<", ">", ">>" buttons have this class */
+ text-align: center; /* They are the navigation buttons */
+ padding: 2px; /* Make the buttons seem like they're pressing */
+ background: url("/css/jscalendar/aqua/title-bg.gif") repeat-x 0 100%; color: #000;
+ font-weight: bold;
+}
+
+.calendar .nav {
+ font-family: verdana,tahoma,sans-serif;
+}
+
+.calendar .nav div {
+ background: transparent url("/css/jscalendar/aqua/menuarrow.gif") no-repeat 100% 100%;
+}
+
+.calendar thead tr { background: url("/css/jscalendar/aqua/title-bg.gif") repeat-x 0 100%; color: #000; }
+
+.calendar thead .title { /* This holds the current "month, year" */
+ font-weight: bold; /* Pressing it will take you to the current date */
+ text-align: center;
+ padding: 2px;
+ background: url("/css/jscalendar/aqua/title-bg.gif") repeat-x 0 100%; color: #000;
+}
+
+.calendar thead .headrow { /* Row <TR> containing navigation buttons */
+}
+
+.calendar thead .name { /* Cells <TD> containing the day names */
+ border-bottom: 1px solid #797979;
+ padding: 2px;
+ text-align: center;
+ color: #000;
+}
+
+.calendar thead .weekend { /* How a weekend day name shows in header */
+ color: #c44;
+}
+
+.calendar thead .hilite { /* How do the buttons in header appear when hover */
+ background: url("/css/jscalendar/aqua/hover-bg.gif");
+ border-bottom: 1px solid #797979;
+ padding: 2px 2px 1px 2px;
+}
+
+.calendar thead .active { /* Active (pressed) buttons in header */
+ background: url("/css/jscalendar/aqua/active-bg.gif"); color: #fff;
+ padding: 3px 1px 0px 3px;
+ border-bottom: 1px solid #797979;
+}
+
+.calendar thead .daynames { /* Row <TR> containing the day names */
+ background: url("/css/jscalendar/aqua/dark-bg.gif");
+}
+
+/* The body part -- contains all the days in month. */
+
+.calendar tbody .day { /* Cells <TD> containing month days dates */
+ font-family: verdana,tahoma,sans-serif;
+ width: 2em;
+ color: #000;
+ text-align: right;
+ padding: 2px 4px 2px 2px;
+}
+.calendar tbody .day.othermonth {
+ font-size: 80%;
+ color: #999;
+}
+.calendar tbody .day.othermonth.oweekend {
+ color: #f99;
+}
+
+.calendar table .wn {
+ padding: 2px 3px 2px 2px;
+ border-right: 1px solid #797979;
+ background: url("/css/jscalendar/aqua/dark-bg.gif");
+}
+
+.calendar tbody .rowhilite td,
+.calendar tbody .rowhilite td.wn {
+ background: url("/css/jscalendar/aqua/rowhover-bg.gif");
+}
+
+.calendar tbody td.today { font-weight: bold; /* background: url("/css/jscalendar/aqua/today-bg.gif") no-repeat 70% 50%; */ }
+
+.calendar tbody td.hilite { /* Hovered cells <TD> */
+ background: url("/css/jscalendar/aqua/hover-bg.gif");
+ padding: 1px 3px 1px 1px;
+ border: 1px solid #bbb;
+}
+
+.calendar tbody td.active { /* Active (pressed) cells <TD> */
+ padding: 2px 2px 0px 2px;
+}
+
+.calendar tbody td.weekend { /* Cells showing weekend days */
+ color: #c44;
+}
+
+.calendar tbody td.selected { /* Cell showing selected date */
+ font-weight: bold;
+ border: 1px solid #797979;
+ padding: 1px 3px 1px 1px;
+ background: url("/css/jscalendar/aqua/active-bg.gif"); color: #fff;
+}
+
+.calendar tbody .disabled { color: #999; }
+
+.calendar tbody .emptycell { /* Empty cells (the best is to hide them) */
+ visibility: hidden;
+}
+
+.calendar tbody .emptyrow { /* Empty row (some months need less than 6 rows) */
+ display: none;
+}
+
+/* The footer part -- status bar and "Close" button */
+
+.calendar tfoot .footrow { /* The <TR> in footer (only one right now) */
+ text-align: center;
+ background: #565;
+ color: #fff;
+}
+
+.calendar tfoot .ttip { /* Tooltip (status bar) cell <TD> */
+ padding: 2px;
+ background: url("/css/jscalendar/aqua/status-bg.gif") repeat-x 0 0; color: #000;
+}
+
+.calendar tfoot .hilite { /* Hover style for buttons in footer */
+ background: #afa;
+ border: 1px solid #084;
+ color: #000;
+ padding: 1px;
+}
+
+.calendar tfoot .active { /* Active (pressed) style for buttons in footer */
+ background: #7c7;
+ padding: 2px 0px 0px 2px;
+}
+
+/* Combo boxes (menus that display months/years for direct selection) */
+
+.calendar .combo {
+ position: absolute;
+ display: none;
+ top: 0px;
+ left: 0px;
+ width: 4em;
+ cursor: default;
+ border-width: 0 1px 1px 1px;
+ border-style: solid;
+ border-color: #797979;
+ background: url("/css/jscalendar/aqua/normal-bg.gif"); color: #000;
+ z-index: 100;
+ font-size: 90%;
+}
+
+.calendar .combo .label,
+.calendar .combo .label-IEfix {
+ text-align: center;
+ padding: 1px;
+}
+
+.calendar .combo .label-IEfix {
+ width: 4em;
+}
+
+.calendar .combo .hilite {
+ background: url("/css/jscalendar/aqua/hover-bg.gif"); color: #000;
+}
+
+.calendar .combo .active {
+ background: url("/css/jscalendar/aqua/active-bg.gif"); color: #fff;
+ font-weight: bold;
+}
+
+.calendar td.time {
+ border-top: 1px solid #797979;
+ padding: 1px 0px;
+ text-align: center;
+ background: url("/css/jscalendar/aqua/dark-bg.gif");
+}
+
+.calendar td.time .hour,
+.calendar td.time .minute,
+.calendar td.time .ampm {
+ padding: 0px 5px 0px 6px;
+ font-weight: bold;
+ background: url("/css/jscalendar/aqua/normal-bg.gif"); color: #000;
+}
+
+.calendar td.time .hour,
+.calendar td.time .minute {
+ font-family: monospace;
+}
+
+.calendar td.time .ampm {
+ text-align: center;
+}
+
+.calendar td.time .colon {
+ padding: 0px 2px 0px 3px;
+ font-weight: bold;
+}
+
+.calendar td.time span.hilite {
+ background: url("/css/jscalendar/aqua/hover-bg.gif"); color: #000;
+}
+
+.calendar td.time span.active {
+ background: url("/css/jscalendar/aqua/active-bg.gif"); color: #fff;
+}
diff --git a/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/title-bg.gif b/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/title-bg.gif
new file mode 100644
index 0000000..6a541b3
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/title-bg.gif
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/today-bg.gif b/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/today-bg.gif
new file mode 100644
index 0000000..7161538
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/jscalendar/aqua/today-bg.gif
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/larpe-admin.css b/larpe/tags/release-1.0/root/larpe/css/larpe-admin.css
new file mode 100644
index 0000000..5109ae3
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/larpe-admin.css
@@ -0,0 +1,410 @@
+@import url(larpe-common.css);
+
+body {
+ font-family: sans-serif;
+ background : white url(fond.jpg) repeat;
+}
+
+div#main-content {
+ clear: both;
+ max-width: 800px;
+ margin: 0 4em;
+ 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%;
+}
+
+#header {
+ max-width: 800px;
+ margin: 0 4em;
+ 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;
+}
+
+/*
+#header li.inactive,
+#header li.inactive:hover {
+ background-position: 0 0;
+}
+
+#header li.inactive a,
+#header li.inactive:hover a {
+ background-position: 100% 0;
+ color: gray;
+}
+*/
+
+div#footer {
+ max-width: 800px;
+ margin: 0 4em;
+ 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;
+}
+
+pre {
+ overflow: scroll;
+}
+
+div.explanation {
+ margin: 0 140px 1em 0;
+ background: white url(bg-footer.png) top right repeat-y;
+ border: 1px solid #ccc;
+ padding: 3px;
+}
+
+div.explanation ol,
+div.explanation p {
+ margin: 0;
+}
+
+div#error-page {
+ border: 1px solid #a00;
+}
+
+div#error-page h2 {
+ margin: 0;
+ border-bottom: 1px solid #a00;
+ background: #f52;
+ padding: 0 3px;
+}
+
+div#error-page p {
+ padding: 0 3px;
+}
+
+img.theme-icon {
+ float: right;
+ margin: -16px 4px 0px 3px;
+ border: 1px solid #999;
+}
+
+a.arrow {
+ text-decoration: none;
+}
+
+ul.biglist {
+ margin: 0;
+ padding: 0;
+}
+
+#fields-list li {
+ cursor: move;
+}
+
+ul.biglist li {
+ list-style-type: none;
+ margin: 4px 0;
+ padding: 0 2px;
+ border: 1px solid #888;
+ background: #ffe;
+ clear: both;
+}
+
+ul.biglist li p.details {
+ display: block;
+ margin: 0;
+ color: #555;
+ font-size: 80%;
+}
+
+
+ul.biglist li p.commands {
+ float: right;
+ margin-top: -17px;
+}
+
+ul.biglist li p.commands img {
+ padding-right: 5px;
+}
+
+a img {
+ border: 0;
+}
+
+td.time {
+ text-align: right;
+}
+
+ul.biglist li.disabled, ul.biglist li.disabled p.details {
+ color: #999;
+ background: #ddd;
+}
+
+form#other-log-select {
+ margin-top: 2em;
+ padding-top: 1em;
+ border-top: 1px solid #999;
+}
+
+form#other-log-select select {
+ margin: 0 1em;
+}
+
+tr.level-error td {
+ border: 1px solid #800;
+ background: red;
+}
+
+tr.level-error td.message {
+ font-weight: bold;
+}
+
+div#new-field table {
+ margin: 0;
+ padding: 0;
+}
+
+div#new-field div.widget {
+ margin: 0;
+ padding: 0;
+}
+
+div#new-field div.buttons {
+ margin: 0;
+ padding: 0;
+}
+
+div#new-field div.buttons input {
+ margin: 0;
+ padding: 0;
+}
+
+div#new-field {
+ border: 1px solid #888;
+ background: #ffe;
+ margin: 2em 0 4px 0;
+ padding: 0 2px;
+}
+
+div#new-field div.widget {
+}
+
+div#new-field h3 {
+ margin: 0;
+ font-size: 100%;
+}
+
+div#new-field br {
+ display: none;
+}
+
+div#new-field p.commands {
+ float: right;
+ margin-top: -17px;
+ margin-right: 3px;
+}
+
+div.WorkflowStatusWidget {
+ border-left: 1px solid black;
+}
+
+hr {
+ border: none;
+ border-top: 1px solid #666;
+ height: 1px;
+ width: 80%;
+}
+
+
diff --git a/larpe/tags/release-1.0/root/larpe/css/larpe-common.css b/larpe/tags/release-1.0/root/larpe/css/larpe-common.css
new file mode 100644
index 0000000..498b2af
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/larpe-common.css
@@ -0,0 +1,260 @@
+a {
+ color: #028;
+}
+
+div.content {
+ margin-left: 5px;
+}
+
+div.TextWidget textarea,
+div.StringWidget input,
+div.DateWidget input,
+div.WcsExtraStringWidget input,
+div.RegexStringWidget input,
+div.EmailWidget input,
+div.PasswordWidget input,
+div.UrlWidget input,
+div.ValidUrlWidget input {
+ border: 1px inset #ccc;
+ margin: 1px;
+ padding: 2px 3px;
+}
+
+div.SingleSelectWidget select {
+ margin: 1px;
+}
+
+div.widget input.prefill-button {
+ border: 1px outset #ccc;
+ margin: 0 0 0 1em;
+ padding: 0px 0px;
+}
+
+div.widget input.prefill-button:focus {
+ border: 1px outset #ccc;
+ margin: 0 0 0 1em;
+ padding: 0;
+}
+
+
+div.widget textarea.readonly,
+div.widget input.readonly {
+ border: 1px solid #ccc;
+ background: #eee;
+ margin: 0 0 0 1em;
+}
+
+div.TextWidget textarea:focus,
+div.DateWidget input:focus,
+div.StringWidget input:focus,
+div.WcsExtraStringWidget input:focus,
+div.RegexStringWidget input:focus,
+div.EmailWidget input:focus,
+div.PasswordWidget input:focus,
+div.UrlWidget input:focus,
+div.ValidUrlWidget input:focus {
+ border: 2px solid #ccf;
+ /*margin: 0px; */
+ padding: 1px 2px;
+}
+
+div.AccountSettingWidget label {
+ padding-right: 2em;
+}
+
+div.SubmitWidget input, input[type=submit] {
+ margin-top: 1em;
+ border: 1px outset #ccc;
+}
+
+div.form .title, form.quixote .title {
+ font-weight: bold;
+}
+
+div.errornotice {
+ background: #fd6;
+ border: 1px solid #ffae15;
+ margin: 0em 1em 1em 1em;
+ padding: 5px;
+}
+
+div.infonotice {
+ background: #7b95a6;
+ border: 1px solid #153eaf;
+ margin: 0em 1em 1em 1em;
+ padding: 5px;
+}
+
+div.error {
+ color: black;
+ font-weight: bold;
+ background: transparent 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;
+ clear: both;
+}
+
+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: transparent url(required.png) 0px 0.5ex no-repeat;
+ padding: 0 0 0 24px;
+ margin-left: 1ex;
+ overflow: hidden;
+ color: white;
+}
+
+div.buttons {
+ margin-top: 1em;
+}
+
+div.RadiobuttonsWidget div.content {
+ display: block;
+}
+
+div.error-page {
+ margin: 1em;
+}
+
+pre#exception {
+ overflow: scroll;
+ padding: 1em;
+ border: 1px solid #bbb;
+ background: #f0f0f0;
+ font-size: 90%;
+}
+
+div.StringWidget ul {
+ margin: 0;
+ padding-left: 2em;
+ list-style: circle;
+}
+
+div.inline-first div.title,
+div.inline div.title {
+ display: inline;
+ float: left;
+ max-width: 20em;
+ text-align: left;
+ padding-top: 6px;
+}
+
+div.inline-first div.title span.required,
+div.inline div.title span.required {
+ margin-left: 1ex;
+ padding-left: 12px;
+}
+
+div.inline-first div.content,
+div.inline div.content {
+ margin-left: 1ex;
+}
+
+div.inline-first div.hint,
+div.inline div.hint {
+ display: none;
+}
+
+div.inline-first {
+ float: left;
+ clear: both;
+}
+
+div.inline {
+ float: left;
+ clear: none;
+}
+
+div.inline-first div.content,
+div.inline div.content {
+ margin-right: 1.5em;
+}
+
+
+
+div.inline-first div.content,
+div.inline div.content {
+ display: inline;
+}
+
+.clear-both {
+ clear: both;
+}
+
+div.CheckboxesWidget div.content ul {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+}
+
+div.CheckboxesWidget div.content ul li {
+ display: inline;
+ margin-right: 2em;
+}
+
+div#receipt {
+ clear: both;
+}
+
+div#receipt span.label {
+ font-weight: bold;
+ display: block;
+}
+
+div#receipt span.value {
+ display: block;
+ margin-left: 1em;
+}
+
+form div.page,
+div#receipt div.page {
+ border: 1px solid #aaa;
+ padding: 1ex;
+ margin-bottom: 1em;
+}
+
+form div.page p,
+div#receipt div.page p {
+ margin-top: 0;
+}
+
+form div.page h3,
+div#receipt div.page h3 {
+ margin: 0;
+ margin-bottom: 1ex;
+}
+
diff --git a/larpe/tags/release-1.0/root/larpe/css/larpe.css b/larpe/tags/release-1.0/root/larpe/css/larpe.css
new file mode 100644
index 0000000..b582d9f
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/larpe.css
@@ -0,0 +1,331 @@
+@import url(larpe-common.css);
+@import url(jscalendar/aqua/theme.css);
+/* derived from soFresh, a DotClear theme by Maurice Svay (GPL)
+ * http://www.svay.com/files/soFresh/theme-sofresh-1.2.zip */
+
+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;
+}
+
+body.login div#page {
+ width: 500px;
+ background: white url(img/page-500.png) repeat-y;
+}
+#top {
+ color: #09F;
+ background: #FFF url(img/top.jpg) no-repeat;
+ height: 100px;
+ margin: 0;
+}
+
+body.login #top {
+ background: white url(img/top-500.png) no-repeat;
+}
+
+#top h1 {
+ margin: 0;
+ padding-left: 30px;
+ padding-right: 30px;
+ line-height: 100px;
+ height: 100px;
+ overflow: hidden;
+}
+
+#footer {
+ background: #FFF url(img/footer.jpg) no-repeat;
+ color: #999;
+ text-align: center;
+ min-height: 30px;
+}
+
+body.login #footer {
+ background: white url(img/footer-500.png) no-repeat;
+}
+
+div#main-content {
+ margin: 0 2em;
+}
+
+div#main-content h1 {
+ color: #09F;
+}
+
+#steps {
+ height: 32px;
+ margin-bottom: 1em;
+}
+
+#steps ol {
+ list-style: none;
+ padding: 0 20px;
+}
+
+#steps li {
+ display: inline;
+ padding-right: 1em;
+ display: block;
+ float: left;
+ width: 30%;
+ list-style: none;
+}
+
+#steps ol ul {
+ border: 1px solid #ffa500;
+ margin-left: 2em;
+ background: #ffdb94;
+ margin-bottom: 1em;
+ padding: 0;
+ width: auto;
+}
+
+#steps li li {
+ display: block;
+ width: auto;
+ float: none;
+ text-align: left;
+ margin-left: 1em;
+ font-size: 90%;
+}
+
+#steps li li.current {
+ color: black;
+}
+
+
+#steps span.marker {
+ font-size: 26px;
+ padding: 2px 9px;
+ font-weight: bold;
+ color: white;
+ text-align: center;
+ background: #ddd;
+ border: 1px solid #ddd;
+ -moz-border-radius: 0.7ex;
+}
+
+#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;
+}
+
+#steps ol ul {
+ display: none;
+}
+
+#steps ol li.current ul {
+ display: block;
+}
+
+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;
+}
+
+p#login,
+p#logout {
+ margin-top: 2em;
+ text-align: right;
+}
+
+p#login a, p#logout a {
+ text-decoration: underline;
+ /*
+ text-decoration: none;
+ -moz-border-radius: 2em !important;
+ padding: 1px 6px !important;
+ border: 1px solid #ccc !important;
+ border-bottom: 2px solid #999 !important;*/
+}
+
+p#login a:hover {
+ background-color: #ddeeff;
+}
+
+h2#submitted, h2#done {
+ margin-top: 2em;
+ color: #09F;
+ font-size: 130%;
+}
+
+h2#done {
+ margin-top: 1em;
+}
+
+ul li {
+ list-style-image: url(img/li.png);
+}
+
+h2, h3 {
+ color: #09F;
+ margin-left: 0.5em;
+ margin-bottom: 0;
+}
+
+h3 {
+ margin-left: 1em;
+}
+
+table#listing {
+ margin: 1em 0;
+}
+
+table#listing th {
+ text-align: left;
+ border-bottom: 1px solid #999;
+ background: #eee;
+}
+
+table#listing td {
+ padding-right: 1ex;
+}
+
+table#listing th a {
+ text-decoration: none;
+ color: #666;
+}
+
+table.sortable span.sortarrow {
+ color: black;
+ text-decoration: none;
+}
+
+table#listing tr.status-new {
+ background: #aea;
+}
+
+table#listing tr.status-finished,
+table#listing tr.status-rejected {
+ color: #444;
+}
+
+table#listing tr.status-rejected td {
+ text-decoration: line-through;
+}
+
+div.question p.label {
+ font-weight: bold;
+}
+
+img.bar {
+ border: 1px solid black;
+}
+
+div.question table {
+ margin-left: 10px;
+}
+
+div.question table td {
+}
+
+div.question table td.percent {
+ text-align: right;
+ padding: 0 1em;
+ width: 6em;
+}
+
+div.question table td.label {
+ width: 6em;
+}
+
+span.user {
+ margin-left: 1em;
+ font-style: italic;
+}
+
+a.listing {
+ font-size: 80%;
+ padding-left: 1em;
+}
+
+a {
+ text-decoration: none;
+ color: #113;
+}
+
+a:hover {
+ color: #06C;
+ text-decoration: underline;
+}
+
+
+#prelude {
+ color: #aaa;
+ background: transparent;
+ text-align: right;
+ position: relative;
+ top: -85px;
+ margin: 0;
+}
+
+p#breadcrumb {
+ margin-top: 0;
+}
+
+div.error-page {
+ margin-bottom: 2em;
+}
+
+div.hint {
+ display: inline;
+ padding-left: 1ex;
+ font-style: italic;
+}
+
+a.standalone {
+ background: white url(img/h2.png) top left no-repeat;
+ padding-left: 20px;
+}
+
+div#welcome-message a {
+ text-decoration: underline;
+}
+
+input.cancel {
+ margin-left: 5em;
+}
+
+hr {
+ border: none;
+ border-top: 1px solid #666;
+ height: 1px;
+ width: 80%;
+}
+
+div.buttons {
+ clear: both;
+}
+
diff --git a/larpe/tags/release-1.0/root/larpe/css/onglet_left.png b/larpe/tags/release-1.0/root/larpe/css/onglet_left.png
new file mode 100644
index 0000000..dc1ce47
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/onglet_left.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/onglet_right.png b/larpe/tags/release-1.0/root/larpe/css/onglet_right.png
new file mode 100644
index 0000000..72a6497
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/onglet_right.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/required.png b/larpe/tags/release-1.0/root/larpe/css/required.png
new file mode 100644
index 0000000..95c2219
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/required.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/user_info_bottom.png b/larpe/tags/release-1.0/root/larpe/css/user_info_bottom.png
new file mode 100644
index 0000000..92da7de
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/user_info_bottom.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/user_info_top.png b/larpe/tags/release-1.0/root/larpe/css/user_info_top.png
new file mode 100644
index 0000000..c91b707
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/user_info_top.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/css/warning.png b/larpe/tags/release-1.0/root/larpe/css/warning.png
new file mode 100644
index 0000000..9a8c5bb
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/css/warning.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/images/bar.png b/larpe/tags/release-1.0/root/larpe/images/bar.png
new file mode 100644
index 0000000..f460fd1
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/images/bar.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/images/stock_add_16.png b/larpe/tags/release-1.0/root/larpe/images/stock_add_16.png
new file mode 100644
index 0000000..41e14de
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/images/stock_add_16.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/images/stock_copy_16.png b/larpe/tags/release-1.0/root/larpe/images/stock_copy_16.png
new file mode 100644
index 0000000..72bb707
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/images/stock_copy_16.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/images/stock_edit_16.png b/larpe/tags/release-1.0/root/larpe/images/stock_edit_16.png
new file mode 100644
index 0000000..7c7d597
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/images/stock_edit_16.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/images/stock_exec_16.png b/larpe/tags/release-1.0/root/larpe/images/stock_exec_16.png
new file mode 100644
index 0000000..aafc029
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/images/stock_exec_16.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/images/stock_file_16.png b/larpe/tags/release-1.0/root/larpe/images/stock_file_16.png
new file mode 100644
index 0000000..db8b087
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/images/stock_file_16.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/images/stock_harddisk_16.png b/larpe/tags/release-1.0/root/larpe/images/stock_harddisk_16.png
new file mode 100644
index 0000000..af068e8
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/images/stock_harddisk_16.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/images/stock_properties_16.png b/larpe/tags/release-1.0/root/larpe/images/stock_properties_16.png
new file mode 100644
index 0000000..b2ba923
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/images/stock_properties_16.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/images/stock_remove_16.png b/larpe/tags/release-1.0/root/larpe/images/stock_remove_16.png
new file mode 100644
index 0000000..04156d5
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/images/stock_remove_16.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/images/view_16.png b/larpe/tags/release-1.0/root/larpe/images/view_16.png
new file mode 100644
index 0000000..346afce
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/images/view_16.png
Binary files differ
diff --git a/larpe/tags/release-1.0/root/larpe/js/jscalendar/README b/larpe/tags/release-1.0/root/larpe/js/jscalendar/README
new file mode 100644
index 0000000..455ab3d
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/js/jscalendar/README
@@ -0,0 +1,33 @@
+The DHTML Calendar
+-------------------
+
+ Author: Mihai Bazon, <mihai_bazon@yahoo.com>
+ http://dynarch.com/mishoo/
+
+ This program is free software published under the
+ terms of the GNU Lesser General Public License.
+
+ For the entire license text please refer to
+ http://www.gnu.org/licenses/lgpl.html
+
+Contents
+---------
+
+ calendar.js -- the main program file
+ lang/*.js -- internalization files
+ *.css -- color themes
+ cal.html -- example usage file
+ doc/ -- documentation, in PDF and HTML
+ simple-1.html -- quick setup examples [popup calendars]
+ simple-2.html -- quick setup example for flat calendar
+ calendar.php -- PHP wrapper
+ test.php -- test file for the PHP wrapper
+
+Homepage
+---------
+
+ For details and latest versions please refer to calendar
+ homepage, located on my website:
+
+ http://dynarch.com/mishoo/calendar.epl
+
diff --git a/larpe/tags/release-1.0/root/larpe/js/jscalendar/calendar.js b/larpe/tags/release-1.0/root/larpe/js/jscalendar/calendar.js
new file mode 100644
index 0000000..1860d19
--- /dev/null
+++ b/larpe/tags/release-1.0/root/larpe/js/jscalendar/calendar.js
@@ -0,0 +1,1806 @@
+/* Copyright Mihai Bazon, 2002-2005 | www.bazon.net/mishoo
+ * -----------------------------------------------------------
+ *
+ * The DHTML Calendar, version 1.0 "It is happening again"
+ *
+ * Details and latest version at:
+ * www.dynarch.com/projects/calendar
+ *
+ * This script is developed by Dynarch.com. Visit us at www.dynarch.com.
+ *
+ * This script is distributed under the GNU Lesser General Public License.
+ * Read the entire license text here: http://www.gnu.org/licenses/lgpl.html
+ */
+
+// $Id: calendar.js,v 1.1 2006/04/03 19:10:44 rchantereau Exp $
+
+/** The Calendar object constructor. */
+Calendar = function (firstDayOfWeek, dateStr, onSelected, onClose) {
+ // member variables
+ this.activeDiv = null;
+ this.currentDateEl = null;
+ this.getDateStatus = null;
+ this.getDateToolTip = null;
+ this.getDateText = null;
+ this.timeout = null;
+ this.onSelected = onSelected || null;
+ this.onClose = onClose || null;
+ this.dragging = false;
+ this.hidden = false;
+ this.minYear = 1970;
+ this.maxYear = 2050;
+ this.dateFormat = Calendar._TT["DEF_DATE_FORMAT"];
+ this.ttDateFormat = Calendar._TT["TT_DATE_FORMAT"];
+ this.isPopup = true;
+ this.weekNumbers = true;
+ this.firstDayOfWeek = typeof firstDayOfWeek == "number" ? firstDayOfWeek : Calendar._FD; // 0 for Sunday, 1 for Monday, etc.
+ this.showsOtherMonths = false;
+ this.dateStr = dateStr;
+ this.ar_days = null;
+ this.showsTime = false;
+ this.time24 = true;
+ this.yearStep = 2;
+ this.hiliteToday = true;
+ this.multiple = null;
+ // HTML elements
+ this.table = null;
+ this.element = null;
+ this.tbody = null;
+ this.firstdayname = null;
+ // Combo boxes
+ this.monthsCombo = null;
+ this.yearsCombo = null;
+ this.hilitedMonth = null;
+ this.activeMonth = null;
+ this.hilitedYear = null;
+ this.activeYear = null;
+ // Information
+ this.dateClicked = false;
+
+ // one-time initializations
+ if (typeof Calendar._SDN == "undefined") {
+ // table of short day names
+ if (typeof Calendar._SDN_len == "undefined")
+ Calendar._SDN_len = 3;
+ var ar = new Array();
+ for (var i = 8; i > 0;) {
+ ar[--i] = Calendar._DN[i].substr(0, Calendar._SDN_len);
+ }
+ Calendar._SDN = ar;
+ // table of short month names
+ if (typeof Calendar._SMN_len == "undefined")
+ Calendar._SMN_len = 3;
+ ar = new Array();
+ for (var i = 12; i > 0;) {
+ ar[--i] = Calendar._MN[i].substr(0, Calendar._SMN_len);
+ }
+ Calendar._SMN = ar;
+ }
+};
+
+// ** constants
+
+/// "static", needed for event handlers.
+Calendar._C = null;
+
+/// detect a special case of "web browser"
+Calendar.is_ie = ( /msie/i.test(navigator.userAgent) &&
+ !/opera/i.test(navigator.userAgent) );
+
+Calendar.is_ie5 = ( Calendar.is_ie && /msie 5\.0/i.test(navigator.userAgent) );
+
+/// detect Opera browser
+Calendar.is_opera = /opera/i.test(navigator.userAgent);
+
+/// detect KHTML-based browsers
+Calendar.is_khtml = /Konqueror|Safari|KHTML/i.test(navigator.userAgent);
+
+// BEGIN: UTILITY FUNCTIONS; beware that these might be moved into a separate
+// library, at some point.
+
+Calendar.getAbsolutePos = function(el) {
+ var SL = 0, ST = 0;
+ var is_div = /^div$/i.test(el.tagName);
+ if (is_div && el.scrollLeft)
+ SL = el.scrollLeft;
+ if (is_div && el.scrollTop)
+ ST = el.scrollTop;
+ var r = { x: el.offsetLeft - SL, y: el.offsetTop - ST };
+ if (el.offsetParent) {
+ var tmp = this.getAbsolutePos(el.offsetParent);
+ r.x += tmp.x;
+ r.y += tmp.y;
+ }
+ return r;
+};
+
+Calendar.isRelated = function (el, evt) {
+ var related = evt.relatedTarget;
+ if (!related) {
+ var type = evt.type;
+ if (type == "mouseover") {
+ related = evt.fromElement;
+ } else if (type == "mouseout") {
+ related = evt.toElement;
+ }
+ }
+ while (related) {
+ if (related == el) {
+ return true;
+ }
+ related = related.parentNode;
+ }
+ return false;
+};
+
+Calendar.removeClass = function(el, className) {
+ if (!(el && el.className)) {
+ return;
+ }
+ var cls = el.className.split(" ");
+ var ar = new Array();
+ for (var i = cls.length; i > 0;) {
+ if (cls[--i] != className) {
+ ar[ar.length] = cls[i];
+ }
+ }
+ el.className = ar.join(" ");
+};
+
+Calendar.addClass = function(el, className) {
+ Calendar.removeClass(el, className);
+ el.className += " " + className;
+};
+
+// FIXME: the following 2 functions totally suck, are useless and should be replaced immediately.
+Calendar.getElement = function(ev) {
+ var f = Calendar.is_ie ? window.event.srcElement : ev.currentTarget;
+ while (f.nodeType != 1 || /^div$/i.test(f.tagName))
+ f = f.parentNode;
+ return f;
+};
+
+Calendar.getTargetElement = function(ev) {
+ var f = Calendar.is_ie ? window.event.srcElement : ev.target;
+ while (f.nodeType != 1)
+ f = f.parentNode;
+ return f;
+};
+
+Calendar.stopEvent = function(ev) {
+ ev || (ev = window.event);
+ if (Calendar.is_ie) {
+ ev.cancelBubble = true;
+ ev.returnValue = false;
+ } else {
+ ev.preventDefault();
+ ev.stopPropagation();
+ }
+ return false;
+};
+
+Calendar.addEvent = function(el, evname, func) {
+ if (el.attachEvent) { // IE
+ el.attachEvent("on" + evname, func);
+ } else if (el.addEventListener) { // Gecko / W3C
+ el.addEventListener(evname, func, true);
+ } else {
+ el["on" + evname] = func;
+ }
+};
+
+Calendar.removeEvent = function(el, evname, func) {
+ if (el.detachEvent) { // IE
+ el.detachEvent("on" + evname, func);
+ } else if (el.removeEventListener) { // Gecko / W3C
+ el.removeEventListener(evname, func, true);
+ } else {
+ el["on" + evname] = null;
+ }
+};
+
+Calendar.createElement = function(type, parent) {
+ var el = null;
+ if (document.createElementNS) {
+ // use the XHTML namespace; IE won't normally get here unless
+ // _they_ "fix" the DOM2 implementation.
+ el = document.createElementNS("http://www.w3.org/1999/xhtml", type);
+ } else {
+ el = document.createElement(type);
+ }
+ if (typeof parent != "undefined") {
+ parent.appendChild(el);
+ }
+ return el;
+};
+
+// END: UTILITY FUNCTIONS
+
+// BEGIN: CALENDAR STATIC FUNCTIONS
+
+/** Internal -- adds a set of events to make some element behave like a button. */
+Calendar._add_evs = function(el) {
+ with (Calendar) {
+ addEvent(el, "mouseover", dayMouseOver);
+ addEvent(el, "mousedown", dayMouseDown);
+ addEvent(el, "mouseout", dayMouseOut);
+ if (is_ie) {
+ addEvent(el, "dblclick", dayMouseDblClick);
+ el.setAttribute("unselectable", true);
+ }
+ }
+};
+
+Calendar.findMonth = function(el) {
+ if (typeof el.month != "undefined") {
+ return el;
+ } else if (typeof el.parentNode.month != "undefined") {
+ return el.parentNode;
+ }
+ return null;
+};
+
+Calendar.findYear = function(el) {
+ if (typeof el.year != "undefined") {
+ return el;
+ } else if (typeof el.parentNode.year != "undefined") {
+ return el.parentNode;
+ }
+ return null;
+};
+
+Calendar.showMonthsCombo = function () {
+ var cal = Calendar._C;
+ if (!cal) {
+ return false;
+ }
+ var cal = cal;
+ var cd = cal.activeDiv;
+ var mc = cal.monthsCombo;
+ if (cal.hilitedMonth) {
+ Calendar.removeClass(cal.hilitedMonth, "hilite");
+ }
+ if (cal.activeMonth) {
+ Calendar.removeClass(cal.activeMonth, "active");
+ }
+ var mon = cal.monthsCombo.getElementsByTagName("div")[cal.date.getMonth()];
+ Calendar.addClass(mon, "active");
+ cal.activeMonth = mon;
+ var s = mc.style;
+ s.display = "block";
+ if (cd.navtype < 0)
+ s.left = cd.offsetLeft + "px";
+ else {
+ var mcw = mc.offsetWidth;
+ if (typeof mcw == "undefined")
+ // Konqueror brain-dead techniques
+ mcw = 50;
+ s.left = (cd.offsetLeft + cd.offsetWidth - mcw) + "px";
+ }
+ s.top = (cd.offsetTop + cd.offsetHeight) + "px";
+};
+
+Calendar.showYearsCombo = function (fwd) {
+ var cal = Calendar._C;
+ if (!cal) {
+ return false;
+ }
+ var cal = cal;
+ var cd = cal.activeDiv;
+ var yc = cal.yearsCombo;
+ if (cal.hilitedYear) {
+ Calendar.removeClass(cal.hilitedYear, "hilite");
+ }
+ if (cal.activeYear) {
+ Calendar.removeClass(cal.activeYear, "active");
+ }
+ cal.activeYear = null;
+ var Y = cal.date.getFullYear() + (fwd ? 1 : -1);
+ var yr = yc.firstChild;
+ var show = false;
+ for (var i = 12; i > 0; --i) {
+ if (Y >= cal.minYear && Y <= cal.maxYear) {
+ yr.innerHTML = Y;
+ yr.year = Y;
+ yr.style.display = "block";
+ show = true;
+ } else {
+ yr.style.display = "none";
+ }
+ yr = yr.nextSibling;
+ Y += fwd ? cal.yearStep : -cal.yearStep;
+ }
+ if (show) {
+ var s = yc.style;
+ s.display = "block";
+ if (cd.navtype < 0)
+ s.left = cd.offsetLeft + "px";
+ else {
+ var ycw = yc.offsetWidth;
+ if (typeof ycw == "undefined")
+ // Konqueror brain-dead techniques
+ ycw = 50;
+ s.left = (cd.offsetLeft + cd.offsetWidth - ycw) + "px";
+ }
+ s.top = (cd.offsetTop + cd.offsetHeight) + "px";
+ }
+};
+
+// event handlers
+
+Calendar.tableMouseUp = function(ev) {
+ var cal = Calendar._C;
+ if (!cal) {
+ return false;
+ }
+ if (cal.timeout) {
+ clearTimeout(cal.timeout);
+ }
+ var el = cal.activeDiv;
+ if (!el) {
+ return false;
+ }
+ var target = Calendar.getTargetElement(ev);
+ ev || (ev = window.event);
+ Calendar.removeClass(el, "active");
+ if (target == el || target.parentNode == el) {
+ Calendar.cellClick(el, ev);
+ }
+ var mon = Calendar.findMonth(target);
+ var date = null;
+ if (mon) {
+ date = new Date(cal.date);
+ if (mon.month != date.getMonth()) {
+ date.setMonth(mon.month);
+ cal.setDate(date);
+ cal.dateClicked = false;
+ cal.callHandler();
+ }
+ } else {
+ var year = Calendar.findYear(target);
+ if (year) {
+ date = new Date(cal.date);
+ if (year.year != date.getFullYear()) {
+ date.setFullYear(year.year);
+ cal.setDate(date);
+ cal.dateClicked = false;
+ cal.callHandler();
+ }
+ }
+ }
+ with (Calendar) {
+ removeEvent(document, "mouseup", tableMouseUp);
+ removeEvent(document, "mouseover", tableMouseOver);
+ removeEvent(document, "mousemove", tableMouseOver);
+ cal._hideCombos();
+ _C = null;
+ return stopEvent(ev);
+ }
+};
+
+Calendar.tableMouseOver = function (ev) {
+ var cal = Calendar._C;
+ if (!cal) {
+ return;
+ }
+ var el = cal.activeDiv;
+ var target = Calendar.getTargetElement(ev);
+ if (target == el || target.parentNode == el) {
+ Calendar.addClass(el, "hilite active");
+ Calendar.addClass(el.parentNode, "rowhilite");
+ } else {
+ if (typeof el.navtype == "undefined" || (el.navtype != 50 && (el.navtype == 0 || Math.abs(el.navtype) > 2)))
+ Calendar.removeClass(el, "active");
+ Calendar.removeClass(el, "hilite");
+ Calendar.removeClass(el.parentNode, "rowhilite");
+ }
+ ev || (ev = window.event);
+ if (el.navtype == 50 && target != el) {
+ var pos = Calendar.getAbsolutePos(el);
+ var w = el.offsetWidth;
+ var x = ev.clientX;
+ var dx;
+ var decrease = true;
+ if (x > pos.x + w) {
+ dx = x - pos.x - w;
+ decrease = false;
+ } else
+ dx = pos.x - x;
+
+ if (dx < 0) dx = 0;
+ var range = el._range;
+ var current = el._current;
+ var count = Math.floor(dx / 10) % range.length;
+ for (var i = range.length; --i >= 0;)
+ if (range[i] == current)
+ break;
+ while (count-- > 0)
+ if (decrease) {
+ if (--i < 0)
+ i = range.length - 1;
+ } else if ( ++i >= range.length )
+ i = 0;
+ var newval = range[i];
+ el.innerHTML = newval;
+
+ cal.onUpdateTime();
+ }
+ var mon = Calendar.findMonth(target);
+ if (mon) {
+ if (mon.month != cal.date.getMonth()) {
+ if (cal.hilitedMonth) {
+ Calendar.removeClass(cal.hilitedMonth, "hilite");
+ }
+ Calendar.addClass(mon, "hilite");
+ cal.hilitedMonth = mon;
+ } else if (cal.hilitedMonth) {
+ Calendar.removeClass(cal.hilitedMonth, "hilite");
+ }
+ } else {
+ if (cal.hilitedMonth) {
+ Calendar.removeClass(cal.hilitedMonth, "hilite");
+ }
+ var year = Calendar.findYear(target);
+ if (year) {
+ if (year.year != cal.date.getFullYear()) {
+ if (cal.hilitedYear) {
+ Calendar.removeClass(cal.hilitedYear, "hilite");
+ }
+ Calendar.addClass(year, "hilite");
+ cal.hilitedYear = year;
+ } else if (cal.hilitedYear) {
+ Calendar.removeClass(cal.hilitedYear, "hilite");
+ }
+ } else if (cal.hilitedYear) {
+ Calendar.removeClass(cal.hilitedYear, "hilite");
+ }
+ }
+ return Calendar.stopEvent(ev);
+};
+
+Calendar.tableMouseDown = function (ev) {
+ if (Calendar.getTargetElement(ev) == Calendar.getElement(ev)) {
+ return Calendar.stopEvent(ev);
+ }
+};
+
+Calendar.calDragIt = function (ev) {
+ var cal = Calendar._C;
+ if (!(cal && cal.dragging)) {
+ return false;
+ }
+ var posX;
+ var posY;
+ if (Calendar.is_ie) {
+ posY = window.event.clientY + document.body.scrollTop;
+ posX = window.event.clientX + document.body.scrollLeft;
+ } else {
+ posX = ev.pageX;
+ posY = ev.pageY;
+ }
+ cal.hideShowCovered();
+ var st = cal.element.style;
+ st.left = (posX - cal.xOffs) + "px";
+ st.top = (posY - cal.yOffs) + "px";
+ return Calendar.stopEvent(ev);
+};
+
+Calendar.calDragEnd = function (ev) {
+ var cal = Calendar._C;
+ if (!cal) {
+ return false;
+ }
+ cal.dragging = false;
+ with (Calendar) {
+ removeEvent(document, "mousemove", calDragIt);
+ removeEvent(document, "mouseup", calDragEnd);
+ tableMouseUp(ev);
+ }
+ cal.hideShowCovered();
+};
+
+Calendar.dayMouseDown = function(ev) {
+ var el = Calendar.getElement(ev);
+ if (el.disabled) {
+ return false;
+ }
+ var cal = el.calendar;
+ cal.activeDiv = el;
+ Calendar._C = cal;
+ if (el.navtype != 300) with (Calendar) {
+ if (el.navtype == 50) {
+ el._current = el.innerHTML;
+ addEvent(document, "mousemove", tableMouseOver);
+ } else
+ addEvent(document, Calendar.is_ie5 ? "mousemove" : "mouseover", tableMouseOver);
+ addClass(el, "hilite active");
+ addEvent(document, "mouseup", tableMouseUp);
+ } else if (cal.isPopup) {
+ cal._dragStart(ev);
+ }
+ if (el.navtype == -1 || el.navtype == 1) {
+ if (cal.timeout) clearTimeout(cal.timeout);
+ cal.timeout = setTimeout("Calendar.showMonthsCombo()", 250);
+ } else if (el.navtype == -2 || el.navtype == 2) {
+ if (cal.timeout) clearTimeout(cal.timeout);
+ cal.timeout = setTimeout((el.navtype > 0) ? "Calendar.showYearsCombo(true)" : "Calendar.showYearsCombo(false)", 250);
+ } else {
+ cal.timeout = null;
+ }
+ return Calendar.stopEvent(ev);
+};
+
+Calendar.dayMouseDblClick = function(ev) {
+ Calendar.cellClick(Calendar.getElement(ev), ev || window.event);
+ if (Calendar.is_ie) {
+ document.selection.empty();
+ }
+};
+
+Calendar.dayMouseOver = function(ev) {
+ var el = Calendar.getElement(ev);
+ if (Calendar.isRelated(el, ev) || Calendar._C || el.disabled) {
+ return false;
+ }
+ if (el.ttip) {
+ if (el.ttip.substr(0, 1) == "_") {
+ el.ttip = el.caldate.print(el.calendar.ttDateFormat) + el.ttip.substr(1);
+ }
+ el.calendar.tooltips.innerHTML = el.ttip;
+ }
+ if (el.navtype != 300) {
+ Calendar.addClass(el, "hilite");
+ if (el.caldate) {
+ Calendar.addClass(el.parentNode, "rowhilite");
+ }
+ }
+ return Calendar.stopEvent(ev);
+};
+
+Calendar.dayMouseOut = function(ev) {
+ with (Calendar) {
+ var el = getElement(ev);
+ if (isRelated(el, ev) || _C || el.disabled)
+ return false;
+ removeClass(el, "hilite");
+ if (el.caldate)
+ removeClass(el.parentNode, "rowhilite");
+ if (el.calendar)
+ el.calendar.tooltips.innerHTML = _TT["SEL_DATE"];
+ return stopEvent(ev);
+ }
+};
+
+/**
+ * A generic "click" handler :) handles all types of buttons defined in this
+ * calendar.
+ */
+Calendar.cellClick = function(el, ev) {
+ var cal = el.calendar;
+ var closing = false;
+ var newdate = false;
+ var date = null;
+ if (typeof el.navtype == "undefined") {
+ if (cal.currentDateEl) {
+ Calendar.removeClass(cal.currentDateEl, "selected");
+ Calendar.addClass(el, "selected");
+ closing = (cal.currentDateEl == el);
+ if (!closing) {
+ cal.currentDateEl = el;
+ }
+ }
+ cal.date.setDateOnly(el.caldate);
+ date = cal.date;
+ var other_month = !(cal.dateClicked = !el.otherMonth);
+ if (!other_month && !cal.currentDateEl)
+ cal._toggleMultipleDate(new Date(date));
+ else
+ newdate = !el.disabled;
+ // a date was clicked
+ if (other_month)
+ cal._init(cal.firstDayOfWeek, date);
+ } else {
+ if (el.navtype == 200) {
+ Calendar.removeClass(el, "hilite");
+ cal.callCloseHandler();
+ return;
+ }
+ date = new Date(cal.date);
+ if (el.navtype == 0)
+ date.setDateOnly(new Date()); // TODAY
+ // unless "today" was clicked, we assume no date was clicked so
+ // the selected handler will know not to close the calenar when
+ // in single-click mode.
+ // cal.dateClicked = (el.navtype == 0);
+ cal.dateClicked = false;
+ var year = date.getFullYear();
+ var mon = date.getMonth();
+ function setMonth(m) {
+ var day = date.getDate();
+ var max = date.getMonthDays(m);
+ if (day > max) {
+ date.setDate(max);
+ }
+ date.setMonth(m);
+ };
+ switch (el.navtype) {
+ case 400:
+ Calendar.removeClass(el, "hilite");
+ var text = Calendar._TT["ABOUT"];
+ if (typeof text != "undefined") {
+ text += cal.showsTime ? Calendar._TT["ABOUT_TIME"] : "";
+ } else {
+ // FIXME: this should be removed as soon as lang files get updated!
+ text = "Help and about box text is not translated into this language.\n" +
+ "If you know this language and you feel generous please update\n" +
+ "the corresponding file in \"lang\" subdir to match calendar-en.js\n" +
+ "and send it back to <mihai_bazon@yahoo.com> to get it into the distribution ;-)\n\n" +
+ "Thank you!\n" +
+ "http://dynarch.com/mishoo/calendar.epl\n";
+ }
+ alert(text);
+ return;
+ case -2:
+ if (year > cal.minYear) {
+ date.setFullYear(year - 1);
+ }
+ break;
+ case -1:
+ if (mon > 0) {
+ setMonth(mon - 1);
+ } else if (year-- > cal.minYear) {
+ date.setFullYear(year);
+ setMonth(11);
+ }
+ break;
+ case 1:
+ if (mon < 11) {
+ setMonth(mon + 1);
+ } else if (year < cal.maxYear) {
+ date.setFullYear(year + 1);
+ setMonth(0);
+ }
+ break;
+ case 2:
+ if (year < cal.maxYear) {
+ date.setFullYear(year + 1);
+ }
+ break;
+ case 100:
+ cal.setFirstDayOfWeek(el.fdow);
+ return;
+ case 50:
+ var range = el._range;
+ var current = el.innerHTML;
+ for (var i = range.length; --i >= 0;)
+ if (range[i] == current)
+ break;
+ if (ev && ev.shiftKey) {
+ if (--i < 0)
+ i = range.length - 1;
+ } else if ( ++i >= range.length )
+ i = 0;
+ var newval = range[i];
+ el.innerHTML = newval;
+ cal.onUpdateTime();
+ return;
+ case 0:
+ // TODAY will bring us here
+ if ((typeof cal.getDateStatus == "function") &&
+ cal.getDateStatus(date, date.getFullYear(), date.getMonth(), date.getDate())) {
+ return false;
+ }
+ break;
+ }
+ if (!date.equalsTo(cal.date)) {
+ cal.setDate(date);
+ newdate = true;
+ } else if (el.navtype == 0)
+ newdate = closing = true;
+ }
+ if (newdate) {
+ ev && cal.callHandler();
+ }
+ if (closing) {
+ Calendar.removeClass(el, "hilite");
+ ev && cal.callCloseHandler();
+ }
+};
+
+// END: CALENDAR STATIC FUNCTIONS
+
+// BEGIN: CALENDAR OBJECT FUNCTIONS
+
+/**
+ * This function creates the calendar inside the given parent. If _par is
+ * null than it creates a popup calendar inside the BODY element. If _par is
+ * an element, be it BODY, then it creates a non-popup calendar (still
+ * hidden). Some properties need to be set before calling this function.
+ */
+Calendar.prototype.create = function (_par) {
+ var parent = null;
+ if (! _par) {
+ // default parent is the document body, in which case we create
+ // a popup calendar.
+ parent = document.getElementsByTagName("body")[0];
+ this.isPopup = true;
+ } else {
+ parent = _par;
+ this.isPopup = false;
+ }
+ this.date = this.dateStr ? new Date(this.dateStr) : new Date();
+
+ var table = Calendar.createElement("table");
+ this.table = table;
+ table.cellSpacing = 0;
+ table.cellPadding = 0;
+ table.calendar = this;
+ Calendar.addEvent(table, "mousedown", Calendar.tableMouseDown);
+
+ var div = Calendar.createElement("div");
+ this.element = div;
+ div.className = "calendar";
+ if (this.isPopup) {
+ div.style.position = "absolute";
+ div.style.display = "none";
+ }
+ div.appendChild(table);
+
+ var thead = Calendar.createElement("thead", table);
+ var cell = null;
+ var row = null;
+
+ var cal = this;
+ var hh = function (text, cs, navtype) {
+ cell = Calendar.createElement("td", row);
+ cell.colSpan = cs;
+ cell.className = "button";
+ if (navtype != 0 && Math.abs(navtype) <= 2)
+ cell.className += " nav";
+ Calendar._add_evs(cell);
+ cell.calendar = cal;
+ cell.navtype = navtype;
+ cell.innerHTML = "<div unselectable='on'>" + text + "</div>";
+ return cell;
+ };
+
+ row = Calendar.createElement("tr", thead);
+ var title_length = 6;
+ (this.isPopup) && --title_length;
+ (this.weekNumbers) && ++title_length;
+
+ hh("?", 1, 400).ttip = Calendar._TT["INFO"];
+ this.title = hh("", title_length, 300);
+ this.title.className = "title";
+ if (this.isPopup) {
+ this.title.ttip = Calendar._TT["DRAG_TO_MOVE"];
+ this.title.style.cursor = "move";
+ hh("&#x00d7;", 1, 200).ttip = Calendar._TT["CLOSE"];
+ }
+
+ row = Calendar.createElement("tr", thead);
+ row.className = "headrow";
+
+ this._nav_py = hh("&#x00ab;", 1, -2);
+ this._nav_py.ttip = Calendar._TT["PREV_YEAR"];
+
+ this._nav_pm = hh("&#x2039;", 1, -1);
+ this._nav_pm.ttip = Calendar._TT["PREV_MONTH"];
+
+ this._nav_now = hh(Calendar._TT["TODAY"], this.weekNumbers ? 4 : 3, 0);
+ this._nav_now.ttip = Calendar._TT["GO_TODAY"];
+
+ this._nav_nm = hh("&#x203a;", 1, 1);
+ this._nav_nm.ttip = Calendar._TT["NEXT_MONTH"];
+
+ this._nav_ny = hh("&#x00bb;", 1, 2);
+ this._nav_ny.ttip = Calendar._TT["NEXT_YEAR"];
+
+ // day names
+ row = Calendar.createElement("tr", thead);
+ row.className = "daynames";
+ if (this.weekNumbers) {
+ cell = Calendar.createElement("td", row);
+ cell.className = "name wn";
+ cell.innerHTML = Calendar._TT["WK"];
+ }
+ for (var i = 7; i > 0; --i) {
+ cell = Calendar.createElement("td", row);
+ if (!i) {
+ cell.navtype = 100;
+ cell.calendar = this;
+ Calendar._add_evs(cell);
+ }
+ }
+ this.firstdayname = (this.weekNumbers) ? row.firstChild.nextSibling : row.firstChild;
+ this._displayWeekdays();
+
+ var tbody = Calendar.createElement("tbody", table);
+ this.tbody = tbody;
+
+ for (i = 6; i > 0; --i) {
+ row = Calendar.createElement("tr", tbody);
+ if (this.weekNumbers) {
+ cell = Calendar.createElement("td", row);
+ }
+ for (var j = 7; j > 0; --j) {
+ cell = Calendar.createElement("td", row);
+ cell.calendar = this;
+ Calendar._add_evs(cell);
+ }
+ }
+
+ if (this.showsTime) {
+ row = Calendar.createElement("tr", tbody);
+ row.className = "time";
+
+ cell = Calendar.createElement("td", row);
+ cell.className = "time";
+ cell.colSpan = 2;
+ cell.innerHTML = Calendar._TT["TIME"] || "&nbsp;";
+
+ cell = Calendar.createElement("td", row);
+ cell.className = "time";
+ cell.colSpan = this.weekNumbers ? 4 : 3;
+
+ (function(){
+ function makeTimePart(className, init, range_start, range_end) {
+ var part = Calendar.createElement("span", cell);
+ part.className = className;
+ part.innerHTML = init;
+ part.calendar = cal;
+ part.ttip = Calendar._TT["TIME_PART"];
+ part.navtype = 50;
+ part._range = [];
+ if (typeof range_start != "number")
+ part._range = range_start;
+ else {
+ for (var i = range_start; i <= range_end; ++i) {
+ var txt;
+ if (i < 10 && range_end >= 10) txt = '0' + i;
+ else txt = '' + i;
+ part._range[part._range.length] = txt;
+ }
+ }
+ Calendar._add_evs(part);
+ return part;
+ };
+ var hrs = cal.date.getHours();
+ var mins = cal.date.getMinutes();
+ var t12 = !cal.time24;
+ var pm = (hrs > 12);
+ if (t12 && pm) hrs -= 12;
+ var H = makeTimePart("hour", hrs, t12 ? 1 : 0, t12 ? 12 : 23);
+ var span = Calendar.createElement("span", cell);
+ span.innerHTML = ":";
+ span.className = "colon";
+ var M = makeTimePart("minute", mins, 0, 59);
+ var AP = null;
+ cell = Calendar.createElement("td", row);
+ cell.className = "time";
+ cell.colSpan = 2;
+ if (t12)
+ AP = makeTimePart("ampm", pm ? "pm" : "am", ["am", "pm"]);
+ else
+ cell.innerHTML = "&nbsp;";
+
+ cal.onSetTime = function() {
+ var pm, hrs = this.date.getHours(),
+ mins = this.date.getMinutes();
+ if (t12) {
+ pm = (hrs >= 12);
+ if (pm) hrs -= 12;
+ if (hrs == 0) hrs = 12;
+ AP.innerHTML = pm ? "pm" : "am";
+ }
+ H.innerHTML = (hrs < 10) ? ("0" + hrs) : hrs;
+ M.innerHTML = (mins < 10) ? ("0" + mins) : mins;
+ };
+
+ cal.onUpdateTime = function() {
+ var date = this.date;
+ var h = parseInt(H.innerHTML, 10);
+ if (t12) {
+ if (/pm/i.test(AP.innerHTML) && h < 12)
+ h += 12;
+ else if (/am/i.test(AP.innerHTML) && h == 12)
+ h = 0;
+ }
+ var d = date.getDate();
+ var m = date.getMonth();
+ var y = date.getFullYear();
+ date.setHours(h);
+ date.setMinutes(parseInt(M.innerHTML, 10));
+ date.setFullYear(y);
+ date.setMonth(m);
+ date.setDate(d);
+ this.dateClicked = false;
+ this.callHandler();
+ };
+ })();
+ } else {
+ this.onSetTime = this.onUpdateTime = function() {};
+ }
+
+ var tfoot = Calendar.createElement("tfoot", table);
+
+ row = Calendar.createElement("tr", tfoot);
+ row.className = "footrow";
+
+ cell = hh(Calendar._TT["SEL_DATE"], this.weekNumbers ? 8 : 7, 300);
+ cell.className = "ttip";
+ if (this.isPopup) {
+ cell.ttip = Calendar._TT["DRAG_TO_MOVE"];
+ cell.style.cursor = "move";
+ }
+ this.tooltips = cell;
+
+ div = Calendar.createElement("div", this.element);
+ this.monthsCombo = div;
+ div.className = "combo";
+ for (i = 0; i < Calendar._MN.length; ++i) {
+ var mn = Calendar.createElement("div");
+ mn.className = Calendar.is_ie ? "label-IEfix" : "label";
+ mn.month = i;
+ mn.innerHTML = Calendar._SMN[i];
+ div.appendChild(mn);
+ }
+
+ div = Calendar.createElement("div", this.element);
+ this.yearsCombo = div;
+ div.className = "combo";
+ for (i = 12; i > 0; --i) {
+ var yr = Calendar.createElement("div");
+ yr.className = Calendar.is_ie ? "label-IEfix" : "label";
+ div.appendChild(yr);
+ }
+
+ this._init(this.firstDayOfWeek, this.date);
+ parent.appendChild(this.element);
+};
+
+/** keyboard navigation, only for popup calendars */
+Calendar._keyEvent = function(ev) {
+ var cal = window._dynarch_popupCalendar;
+ if (!cal || cal.multiple)
+ return false;
+ (Calendar.is_ie) && (ev = window.event);
+ var act = (Calendar.is_ie || ev.type == "keypress"),
+ K = ev.keyCode;
+ if (ev.ctrlKey) {
+ switch (K) {
+ case 37: // KEY left
+ act && Calendar.cellClick(cal._nav_pm);
+ break;
+ case 38: // KEY up
+ act && Calendar.cellClick(cal._nav_py);
+ break;
+ case 39: // KEY right
+ act && Calendar.cellClick(cal._nav_nm);
+ break;
+ case 40: // KEY down
+ act && Calendar.cellClick(cal._nav_ny);
+ break;
+ default:
+ return false;
+ }
+ } else switch (K) {
+ case 32: // KEY space (now)
+ Calendar.cellClick(cal._nav_now);
+ break;
+ case 27: // KEY esc
+ act && cal.callCloseHandler();
+ break;
+ case 37: // KEY left
+ case 38: // KEY up
+ case 39: // KEY right
+ case 40: // KEY down
+ if (act) {
+ var prev, x, y, ne, el, step;
+ prev = K == 37 || K == 38;
+ step = (K == 37 || K == 39) ? 1 : 7;
+ function setVars() {
+ el = cal.currentDateEl;
+ var p = el.pos;
+ x = p & 15;
+ y = p >> 4;
+ ne = cal.ar_days[y][x];
+ };setVars();
+ function prevMonth() {
+ var date = new Date(cal.date);
+ date.setDate(date.getDate() - step);
+ cal.setDate(date);
+ };
+ function nextMonth() {
+ var date = new Date(cal.date);
+ date.setDate(date.getDate() + step);
+ cal.setDate(date);
+ };
+ while (1) {
+ switch (K) {
+ case 37: // KEY left
+ if (--x >= 0)
+ ne = cal.ar_days[y][x];
+ else {
+ x = 6;
+ K = 38;
+ continue;
+ }
+ break;
+ case 38: // KEY up
+ if (--y >= 0)
+ ne = cal.ar_days[y][x];
+ else {
+ prevMonth();
+ setVars();
+ }
+ break;
+ case 39: // KEY right
+ if (++x < 7)
+ ne = cal.ar_days[y][x];
+ else {
+ x = 0;
+ K = 40;
+ continue;
+ }
+ break;
+ case 40: // KEY down
+ if (++y < cal.ar_days.length)
+ ne = cal.ar_days[y][x];
+ else {
+ nextMonth();
+ setVars();
+ }
+ break;
+ }
+ break;
+ }
+ if (ne) {
+ if (!ne.disabled)
+ Calendar.cellClick(ne);
+ else if (prev)
+ prevMonth();
+ else
+ nextMonth();
+ }
+ }
+ break;
+ case 13: // KEY enter
+ if (act)
+ Calendar.cellClick(cal.currentDateEl, ev);
+ break;
+ default:
+ return false;
+ }
+ return Calendar.stopEvent(ev);
+};
+
+/**
+ * (RE)Initializes the calendar to the given date and firstDayOfWeek
+ */
+Calendar.prototype._init = function (firstDayOfWeek, date) {
+ var today = new Date(),
+ TY = today.getFullYear(),
+ TM = today.getMonth(),
+ TD = today.getDate();
+ this.table.style.visibility = "hidden";
+ var year = date.getFullYear();
+ if (year < this.minYear) {
+ year = this.minYear;
+ date.setFullYear(year);
+ } else if (year > this.maxYear) {
+ year = this.maxYear;
+ date.setFullYear(year);
+ }
+ this.firstDayOfWeek = firstDayOfWeek;
+ this.date = new Date(date);
+ var month = date.getMonth();
+ var mday = date.getDate();
+ var no_days = date.getMonthDays();
+
+ // calendar voodoo for computing the first day that would actually be
+ // displayed in the calendar, even if it's from the previous month.
+ // WARNING: this is magic. ;-)
+ date.setDate(1);
+ var day1 = (date.getDay() - this.firstDayOfWeek) % 7;
+ if (day1 < 0)
+ day1 += 7;
+ date.setDate(-day1);
+ date.setDate(date.getDate() + 1);
+
+ var row = this.tbody.firstChild;
+ var MN = Calendar._SMN[month];
+ var ar_days = this.ar_days = new Array();
+ var weekend = Calendar._TT["WEEKEND"];
+ var dates = this.multiple ? (this.datesCells = {}) : null;
+ for (var i = 0; i < 6; ++i, row = row.nextSibling) {
+ var cell = row.firstChild;
+ if (this.weekNumbers) {
+ cell.className = "day wn";
+ cell.innerHTML = date.getWeekNumber();
+ cell = cell.nextSibling;
+ }
+ row.className = "daysrow";
+ var hasdays = false, iday, dpos = ar_days[i] = [];
+ for (var j = 0; j < 7; ++j, cell = cell.nextSibling, date.setDate(iday + 1)) {
+ iday = date.getDate();
+ var wday = date.getDay();
+ cell.className = "day";
+ cell.pos = i << 4 | j;
+ dpos[j] = cell;
+ var current_month = (date.getMonth() == month);
+ if (!current_month) {
+ if (this.showsOtherMonths) {
+ cell.className += " othermonth";
+ cell.otherMonth = true;
+ } else {
+ cell.className = "emptycell";
+ cell.innerHTML = "&nbsp;";
+ cell.disabled = true;
+ continue;
+ }
+ } else {
+ cell.otherMonth = false;
+ hasdays = true;
+ }
+ cell.disabled = false;
+ cell.innerHTML = this.getDateText ? this.getDateText(date, iday) : iday;
+ if (dates)
+ dates[date.print("%Y%m%d")] = cell;
+ if (this.getDateStatus) {
+ var status = this.getDateStatus(date, year, month, iday);
+ if (this.getDateToolTip) {
+ var toolTip = this.getDateToolTip(date, year, month, iday);
+ if (toolTip)
+ cell.title = toolTip;
+ }
+ if (status === true) {
+ cell.className += " disabled";
+ cell.disabled = true;
+ } else {
+ if (/disabled/i.test(status))
+ cell.disabled = true;
+ cell.className += " " + status;
+ }
+ }
+ if (!cell.disabled) {
+ cell.caldate = new Date(date);
+ cell.ttip = "_";
+ if (!this.multiple && current_month
+ && iday == mday && this.hiliteToday) {
+ cell.className += " selected";
+ this.currentDateEl = cell;
+ }
+ if (date.getFullYear() == TY &&
+ date.getMonth() == TM &&
+ iday == TD) {
+ cell.className += " today";
+ cell.ttip += Calendar._TT["PART_TODAY"];
+ }
+ if (weekend.indexOf(wday.toString()) != -1)
+ cell.className += cell.otherMonth ? " oweekend" : " weekend";
+ }
+ }
+ if (!(hasdays || this.showsOtherMonths))
+ row.className = "emptyrow";
+ }
+ this.title.innerHTML = Calendar._MN[month] + ", " + year;
+ this.onSetTime();
+ this.table.style.visibility = "visible";
+ this._initMultipleDates();
+ // PROFILE
+ // this.tooltips.innerHTML = "Generated in " + ((new Date()) - today) + " ms";
+};
+
+Calendar.prototype._initMultipleDates = function() {
+ if (this.multiple) {
+ for (var i in this.multiple) {
+ var cell = this.datesCells[i];
+ var d = this.multiple[i];
+ if (!d)
+ continue;
+ if (cell)
+ cell.className += " selected";
+ }
+ }
+};
+
+Calendar.prototype._toggleMultipleDate = function(date) {
+ if (this.multiple) {
+ var ds = date.print("%Y%m%d");
+ var cell = this.datesCells[ds];
+ if (cell) {
+ var d = this.multiple[ds];
+ if (!d) {
+ Calendar.addClass(cell, "selected");
+ this.multiple[ds] = date;
+ } else {
+ Calendar.removeClass(cell, "selected");
+ delete this.multiple[ds];
+ }
+ }
+ }
+};
+
+Calendar.prototype.setDateToolTipHandler = function (unaryFunction) {
+ this.getDateToolTip = unaryFunction;
+};
+
+/**
+ * Calls _init function above for going to a certain date (but only if the
+ * date is different than the currently selected one).
+ */
+Calendar.prototype.setDate = function (date) {
+ if (!date.equalsTo(this.date)) {
+ this._init(this.firstDayOfWeek, date);
+ }
+};
+
+/**
+ * Refreshes the calendar. Useful if the "disabledHandler" function is
+ * dynamic, meaning that the list of disabled date can change at runtime.
+ * Just * call this function if you think that the list of disabled dates
+ * should * change.
+ */
+Calendar.prototype.refresh = function () {
+ this._init(this.firstDayOfWeek, this.date);
+};
+
+/** Modifies the "firstDayOfWeek" parameter (pass 0 for Synday, 1 for Monday, etc.). */
+Calendar.prototype.setFirstDayOfWeek = function (firstDayOfWeek) {
+ this._init(firstDayOfWeek, this.date);
+ this._displayWeekdays();
+};
+
+/**
+ * Allows customization of what dates are enabled. The "unaryFunction"
+ * parameter must be a function object that receives the date (as a JS Date
+ * object) and returns a boolean value. If the returned value is true then
+ * the passed date will be marked as disabled.
+ */
+Calendar.prototype.setDateStatusHandler = Calendar.prototype.setDisabledHandler = function (unaryFunction) {
+ this.getDateStatus = unaryFunction;
+};
+
+/** Customization of allowed year range for the calendar. */
+Calendar.prototype.setRange = function (a, z) {
+ this.minYear = a;
+ this.maxYear = z;
+};
+
+/** Calls the first user handler (selectedHandler). */
+Calendar.prototype.callHandler = function () {
+ if (this.onSelected) {
+ this.onSelected(this, this.date.print(this.dateFormat));
+ }
+};
+
+/** Calls the second user handler (closeHandler). */
+Calendar.prototype.callCloseHandler = function () {
+ if (this.onClose) {
+ this.onClose(this);
+ }
+ this.hideShowCovered();
+};
+
+/** Removes the calendar object from the DOM tree and destroys it. */
+Calendar.prototype.destroy = function () {
+ var el = this.element.parentNode;
+ el.removeChild(this.element);
+ Calendar._C = null;
+ window._dynarch_popupCalendar = null;
+};
+
+/**
+ * Moves the calendar element to a different section in the DOM tree (changes
+ * its parent).
+ */
+Calendar.prototype.reparent = function (new_parent) {
+ var el = this.element;
+ el.parentNode.removeChild(el);
+ new_parent.appendChild(el);
+};
+
+// This gets called when the user presses a mouse button anywhere in the
+// document, if the calendar is shown. If the click was outside the open
+// calendar this function closes it.
+Calendar._checkCalendar = function(ev) {
+ var calendar = window._dynarch_popupCalendar;
+ if (!calendar) {
+ return false;
+ }
+ var el = Calendar.is_ie ? Calendar.getElement(ev) : Calendar.getTargetElement(ev);
+ for (; el != null && el != calendar.element; el = el.parentNode);
+ if (el == null) {
+ // calls closeHandler which should hide the calendar.
+ window._dynarch_popupCalendar.callCloseHandler();
+ return Calendar.stopEvent(ev);
+ }
+};
+
+/** Shows the calendar. */
+Calendar.prototype.show = function () {
+ var rows = this.table.getElementsByTagName("tr");
+ for (var i = rows.length; i > 0;) {
+ var row = rows[--i];
+ Calendar.removeClass(row, "rowhilite");
+ var cells = row.getElementsByTagName("td");
+ for (var j = cells.length; j > 0;) {
+ var cell = cells[--j];
+ Calendar.removeClass(cell, "hilite");
+ Calendar.removeClass(cell, "active");
+ }
+ }
+ this.element.style.display = "block";
+ this.hidden = false;
+ if (this.isPopup) {
+ window._dynarch_popupCalendar = this;
+ Calendar.addEvent(document, "keydown", Calendar._keyEvent);
+ Calendar.addEvent(document, "keypress", Calendar._keyEvent);
+ Calendar.addEvent(document, "mousedown", Calendar._checkCalendar);
+ }
+ this.hideShowCovered();
+};
+
+/**
+ * Hides the calendar. Also removes any "hilite" from the class of any TD
+ * element.
+ */
+Calendar.prototype.hide = function () {
+ if (this.isPopup) {
+ Calendar.removeEvent(document, "keydown", Calendar._keyEvent);
+ Calendar.removeEvent(document, "keypress", Calendar._keyEvent);
+ Calendar.removeEvent(document, "mousedown", Calendar._checkCalendar);
+ }
+ this.element.style.display = "none";
+ this.hidden = true;
+ this.hideShowCovered();
+};
+
+/**
+ * Shows the calendar at a given absolute position (beware that, depending on
+ * the calendar element style -- position property -- this might be relative
+ * to the parent's containing rectangle).
+ */
+Calendar.prototype.showAt = function (x, y) {
+ var s = this.element.style;
+ s.left = x + "px";
+ s.top = y + "px";
+ this.show();
+};
+
+/** Shows the calendar near a given element. */
+Calendar.prototype.showAtElement = function (el, opts) {
+ var self = this;
+ var p = Calendar.getAbsolutePos(el);
+ if (!opts || typeof opts != "string") {
+ this.showAt(p.x, p.y + el.offsetHeight);
+ return true;
+ }
+ function fixPosition(box) {
+ if (box.x < 0)
+ box.x = 0;
+ if (box.y < 0)
+ box.y = 0;
+ var cp = document.createElement("div");
+ var s = cp.style;
+ s.position = "absolute";
+ s.right = s.bottom = s.width = s.height = "0px";
+ document.body.appendChild(cp);
+ var br = Calendar.getAbsolutePos(cp);
+ document.body.removeChild(cp);
+ if (Calendar.is_ie) {
+ br.y += document.body.scrollTop;
+ br.x += document.body.scrollLeft;
+ } else {
+ br.y += window.scrollY;
+ br.x += window.scrollX;
+ }
+ var tmp = box.x + box.width - br.x;
+ if (tmp > 0) box.x -= tmp;
+ tmp = box.y + box.height - br.y;
+ if (tmp > 0) box.y -= tmp;
+ };
+ this.element.style.display = "block";
+ Calendar.continuation_for_the_fucking_khtml_browser = function() {
+ var w = self.element.offsetWidth;
+ var h = self.element.offsetHeight;
+ self.element.style.display = "none";
+ var valign = opts.substr(0, 1);
+ var halign = "l";
+ if (opts.length > 1) {
+ halign = opts.substr(1, 1);
+ }
+ // vertical alignment
+ switch (valign) {
+ case "T": p.y -= h; break;
+ case "B": p.y += el.offsetHeight; break;
+ case "C": p.y += (el.offsetHeight - h) / 2; break;
+ case "t": p.y += el.offsetHeight - h; break;
+ case "b": break; // already there