Index: branches/1/Authors
===================================================================
--- branches/1/Authors	(revision 155)
+++ branches/1/Authors	(revision 155)
@@ -0,0 +1,15 @@
+Olivier Thauvin <Olivier dot thauvin at latmos dot ipsl dot fr>
+
+******************
+* Special Thanks *
+******************
+
+* CSS + HTML:
+  Yann Delcambre
+
+* Tests and bug report:
+  Florence Goutail
+  Christian Malique
+  Eric D'Almeida
+  Philippe Weill
+  Francis Vivat
Index: branches/1/CeCILL.txt
===================================================================
--- branches/1/CeCILL.txt	(revision 155)
+++ branches/1/CeCILL.txt	(revision 155)
@@ -0,0 +1,506 @@
+
+CeCILL FREE SOFTWARE LICENSE AGREEMENT
+
+
+    Notice
+
+This Agreement is a Free Software license agreement that is the result
+of discussions between its authors in order to ensure compliance with
+the two main principles guiding its drafting:
+
+    * firstly, compliance with the principles governing the distribution
+      of Free Software: access to source code, broad rights granted to
+      users,
+    * secondly, the election of a governing law, French law, with which
+      it is conformant, both as regards the law of torts and
+      intellectual property law, and the protection that it offers to
+      both authors and holders of the economic rights over software.
+
+The authors of the CeCILL (for Ce[a] C[nrs] I[nria] L[ogiciel] L[ibre])
+license are:
+
+Commissariat à l'Energie Atomique - CEA, a public scientific, technical
+and industrial research establishment, having its principal place of
+business at 25 rue Leblanc, immeuble Le Ponant D, 75015 Paris, France.
+
+Centre National de la Recherche Scientifique - CNRS, a public scientific
+and technological establishment, having its principal place of business
+at 3 rue Michel-Ange, 75794 Paris cedex 16, France.
+
+Institut National de Recherche en Informatique et en Automatique -
+INRIA, a public scientific and technological establishment, having its
+principal place of business at Domaine de Voluceau, Rocquencourt, BP
+105, 78153 Le Chesnay cedex, France.
+
+
+    Preamble
+
+The purpose of this Free Software license agreement is to grant users
+the right to modify and redistribute the software governed by this
+license within the framework of an open source distribution model.
+
+The exercising of these rights is conditional upon certain obligations
+for users so as to preserve this status for all subsequent redistributions.
+
+In consideration of access to the source code and the rights to copy,
+modify and redistribute granted by the license, users are provided only
+with a limited warranty and the software's author, the holder of the
+economic rights, and the successive licensors only have limited liability.
+
+In this respect, the risks associated with loading, using, modifying
+and/or developing or reproducing the software by the user are brought to
+the user's attention, given its Free Software status, which may make it
+complicated to use, with the result that its use is reserved for
+developers and experienced professionals having in-depth computer
+knowledge. Users are therefore encouraged to load and test the
+suitability of the software as regards their requirements in conditions
+enabling the security of their systems and/or data to be ensured and,
+more generally, to use and operate it in the same conditions of
+security. This Agreement may be freely reproduced and published,
+provided it is not altered, and that no provisions are either added or
+removed herefrom.
+
+This Agreement may apply to any or all software for which the holder of
+the economic rights decides to submit the use thereof to its provisions.
+
+
+    Article 1 - DEFINITIONS
+
+For the purpose of this Agreement, when the following expressions
+commence with a capital letter, they shall have the following meaning:
+
+Agreement: means this license agreement, and its possible subsequent
+versions and annexes.
+
+Software: means the software in its Object Code and/or Source Code form
+and, where applicable, its documentation, "as is" when the Licensee
+accepts the Agreement.
+
+Initial Software: means the Software in its Source Code and possibly its
+Object Code form and, where applicable, its documentation, "as is" when
+it is first distributed under the terms and conditions of the Agreement.
+
+Modified Software: means the Software modified by at least one
+Contribution.
+
+Source Code: means all the Software's instructions and program lines to
+which access is required so as to modify the Software.
+
+Object Code: means the binary files originating from the compilation of
+the Source Code.
+
+Holder: means the holder(s) of the economic rights over the Initial
+Software.
+
+Licensee: means the Software user(s) having accepted the Agreement.
+
+Contributor: means a Licensee having made at least one Contribution.
+
+Licensor: means the Holder, or any other individual or legal entity, who
+distributes the Software under the Agreement.
+
+Contribution: means any or all modifications, corrections, translations,
+adaptations and/or new functions integrated into the Software by any or
+all Contributors, as well as any or all Internal Modules.
+
+Module: means a set of sources files including their documentation that
+enables supplementary functions or services in addition to those offered
+by the Software.
+
+External Module: means any or all Modules, not derived from the
+Software, so that this Module and the Software run in separate address
+spaces, with one calling the other when they are run.
+
+Internal Module: means any or all Module, connected to the Software so
+that they both execute in the same address space.
+
+GNU GPL: means the GNU General Public License version 2 or any
+subsequent version, as published by the Free Software Foundation Inc.
+
+Parties: mean both the Licensee and the Licensor.
+
+These expressions may be used both in singular and plural form.
+
+
+    Article 2 - PURPOSE
+
+The purpose of the Agreement is the grant by the Licensor to the
+Licensee of a non-exclusive, transferable and worldwide license for the
+Software as set forth in Article 5 hereinafter for the whole term of the
+protection granted by the rights over said Software. 
+
+
+    Article 3 - ACCEPTANCE
+
+3.1 The Licensee shall be deemed as having accepted the terms and
+conditions of this Agreement upon the occurrence of the first of the
+following events:
+
+    * (i) loading the Software by any or all means, notably, by
+      downloading from a remote server, or by loading from a physical
+      medium;
+    * (ii) the first time the Licensee exercises any of the rights
+      granted hereunder.
+
+3.2 One copy of the Agreement, containing a notice relating to the
+characteristics of the Software, to the limited warranty, and to the
+fact that its use is restricted to experienced users has been provided
+to the Licensee prior to its acceptance as set forth in Article 3.1
+hereinabove, and the Licensee hereby acknowledges that it has read and
+understood it.
+
+
+    Article 4 - EFFECTIVE DATE AND TERM
+
+
+      4.1 EFFECTIVE DATE
+
+The Agreement shall become effective on the date when it is accepted by
+the Licensee as set forth in Article 3.1.
+
+
+      4.2 TERM
+
+The Agreement shall remain in force for the entire legal term of
+protection of the economic rights over the Software.
+
+
+    Article 5 - SCOPE OF RIGHTS GRANTED
+
+The Licensor hereby grants to the Licensee, who accepts, the following
+rights over the Software for any or all use, and for the term of the
+Agreement, on the basis of the terms and conditions set forth hereinafter.
+
+Besides, if the Licensor owns or comes to own one or more patents
+protecting all or part of the functions of the Software or of its
+components, the Licensor undertakes not to enforce the rights granted by
+these patents against successive Licensees using, exploiting or
+modifying the Software. If these patents are transferred, the Licensor
+undertakes to have the transferees subscribe to the obligations set
+forth in this paragraph.
+
+
+      5.1 RIGHT OF USE
+
+The Licensee is authorized to use the Software, without any limitation
+as to its fields of application, with it being hereinafter specified
+that this comprises:
+
+   1. permanent or temporary reproduction of all or part of the Software
+      by any or all means and in any or all form.
+
+   2. loading, displaying, running, or storing the Software on any or
+      all medium.
+
+   3. entitlement to observe, study or test its operation so as to
+      determine the ideas and principles behind any or all constituent
+      elements of said Software. This shall apply when the Licensee
+      carries out any or all loading, displaying, running, transmission
+      or storage operation as regards the Software, that it is entitled
+      to carry out hereunder.
+
+
+      5.2 ENTITLEMENT TO MAKE CONTRIBUTIONS
+
+The right to make Contributions includes the right to translate, adapt,
+arrange, or make any or all modifications to the Software, and the right
+to reproduce the resulting software.
+
+The Licensee is authorized to make any or all Contributions to the
+Software provided that it includes an explicit notice that it is the
+author of said Contribution and indicates the date of the creation thereof.
+
+
+      5.3 RIGHT OF DISTRIBUTION
+
+In particular, the right of distribution includes the right to publish,
+transmit and communicate the Software to the general public on any or
+all medium, and by any or all means, and the right to market, either in
+consideration of a fee, or free of charge, one or more copies of the
+Software by any means.
+
+The Licensee is further authorized to distribute copies of the modified
+or unmodified Software to third parties according to the terms and
+conditions set forth hereinafter.
+
+
+        5.3.1 DISTRIBUTION OF SOFTWARE WITHOUT MODIFICATION
+
+The Licensee is authorized to distribute true copies of the Software in
+Source Code or Object Code form, provided that said distribution
+complies with all the provisions of the Agreement and is accompanied by:
+
+   1. a copy of the Agreement,
+
+   2. a notice relating to the limitation of both the Licensor's
+      warranty and liability as set forth in Articles 8 and 9,
+
+and that, in the event that only the Object Code of the Software is
+redistributed, the Licensee allows future Licensees unhindered access to
+the full Source Code of the Software by indicating how to access it, it
+being understood that the additional cost of acquiring the Source Code
+shall not exceed the cost of transferring the data.
+
+
+        5.3.2 DISTRIBUTION OF MODIFIED SOFTWARE
+
+When the Licensee makes a Contribution to the Software, the terms and
+conditions for the distribution of the resulting Modified Software
+become subject to all the provisions of this Agreement.
+
+The Licensee is authorized to distribute the Modified Software, in
+source code or object code form, provided that said distribution
+complies with all the provisions of the Agreement and is accompanied by:
+
+   1. a copy of the Agreement,
+
+   2. a notice relating to the limitation of both the Licensor's
+      warranty and liability as set forth in Articles 8 and 9,
+
+and that, in the event that only the object code of the Modified
+Software is redistributed, the Licensee allows future Licensees
+unhindered access to the full source code of the Modified Software by
+indicating how to access it, it being understood that the additional
+cost of acquiring the source code shall not exceed the cost of
+transferring the data.
+
+
+        5.3.3 DISTRIBUTION OF EXTERNAL MODULES
+
+When the Licensee has developed an External Module, the terms and
+conditions of this Agreement do not apply to said External Module, that
+may be distributed under a separate license agreement.
+
+
+        5.3.4 COMPATIBILITY WITH THE GNU GPL
+
+The Licensee can include a code that is subject to the provisions of one
+of the versions of the GNU GPL in the Modified or unmodified Software,
+and distribute that entire code under the terms of the same version of
+the GNU GPL.
+
+The Licensee can include the Modified or unmodified Software in a code
+that is subject to the provisions of one of the versions of the GNU GPL,
+and distribute that entire code under the terms of the same version of
+the GNU GPL.
+
+
+    Article 6 - INTELLECTUAL PROPERTY
+
+
+      6.1 OVER THE INITIAL SOFTWARE
+
+The Holder owns the economic rights over the Initial Software. Any or
+all use of the Initial Software is subject to compliance with the terms
+and conditions under which the Holder has elected to distribute its work
+and no one shall be entitled to modify the terms and conditions for the
+distribution of said Initial Software.
+
+The Holder undertakes that the Initial Software will remain ruled at
+least by this Agreement, for the duration set forth in Article 4.2.
+
+
+      6.2 OVER THE CONTRIBUTIONS
+
+The Licensee who develops a Contribution is the owner of the
+intellectual property rights over this Contribution as defined by
+applicable law.
+
+
+      6.3 OVER THE EXTERNAL MODULES
+
+The Licensee who develops an External Module is the owner of the
+intellectual property rights over this External Module as defined by
+applicable law and is free to choose the type of agreement that shall
+govern its distribution.
+
+
+      6.4 JOINT PROVISIONS
+
+The Licensee expressly undertakes:
+
+   1. not to remove, or modify, in any manner, the intellectual property
+      notices attached to the Software;
+
+   2. to reproduce said notices, in an identical manner, in the copies
+      of the Software modified or not.
+
+The Licensee undertakes not to directly or indirectly infringe the
+intellectual property rights of the Holder and/or Contributors on the
+Software and to take, where applicable, vis-à-vis its staff, any and all
+measures required to ensure respect of said intellectual property rights
+of the Holder and/or Contributors.
+
+
+    Article 7 - RELATED SERVICES
+
+7.1 Under no circumstances shall the Agreement oblige the Licensor to
+provide technical assistance or maintenance services for the Software.
+
+However, the Licensor is entitled to offer this type of services. The
+terms and conditions of such technical assistance, and/or such
+maintenance, shall be set forth in a separate instrument. Only the
+Licensor offering said maintenance and/or technical assistance services
+shall incur liability therefor.
+
+7.2 Similarly, any Licensor is entitled to offer to its licensees, under
+its sole responsibility, a warranty, that shall only be binding upon
+itself, for the redistribution of the Software and/or the Modified
+Software, under terms and conditions that it is free to decide. Said
+warranty, and the financial terms and conditions of its application,
+shall be subject of a separate instrument executed between the Licensor
+and the Licensee.
+
+
+    Article 8 - LIABILITY
+
+8.1 Subject to the provisions of Article 8.2, the Licensee shall be
+entitled to claim compensation for any direct loss it may have suffered
+from the Software as a result of a fault on the part of the relevant
+Licensor, subject to providing evidence thereof.
+
+8.2 The Licensor's liability is limited to the commitments made under
+this Agreement and shall not be incurred as a result of in particular:
+(i) loss due the Licensee's total or partial failure to fulfill its
+obligations, (ii) direct or consequential loss that is suffered by the
+Licensee due to the use or performance of the Software, and (iii) more
+generally, any consequential loss. In particular the Parties expressly
+agree that any or all pecuniary or business loss (i.e. loss of data,
+loss of profits, operating loss, loss of customers or orders,
+opportunity cost, any disturbance to business activities) or any or all
+legal proceedings instituted against the Licensee by a third party,
+shall constitute consequential loss and shall not provide entitlement to
+any or all compensation from the Licensor.
+
+
+    Article 9 - WARRANTY
+
+9.1 The Licensee acknowledges that the scientific and technical
+state-of-the-art when the Software was distributed did not enable all
+possible uses to be tested and verified, nor for the presence of
+possible defects to be detected. In this respect, the Licensee's
+attention has been drawn to the risks associated with loading, using,
+modifying and/or developing and reproducing the Software which are
+reserved for experienced users.
+
+The Licensee shall be responsible for verifying, by any or all means,
+the suitability of the product for its requirements, its good working
+order, and for ensuring that it shall not cause damage to either persons
+or properties.
+
+9.2 The Licensor hereby represents, in good faith, that it is entitled
+to grant all the rights over the Software (including in particular the
+rights set forth in Article 5).
+
+9.3 The Licensee acknowledges that the Software is supplied "as is" by
+the Licensor without any other express or tacit warranty, other than
+that provided for in Article 9.2 and, in particular, without any warranty 
+as to its commercial value, its secured, safe, innovative or relevant
+nature.
+
+Specifically, the Licensor does not warrant that the Software is free
+from any error, that it will operate without interruption, that it will
+be compatible with the Licensee's own equipment and software
+configuration, nor that it will meet the Licensee's requirements.
+
+9.4 The Licensor does not either expressly or tacitly warrant that the
+Software does not infringe any third party intellectual property right
+relating to a patent, software or any other property right. Therefore,
+the Licensor disclaims any and all liability towards the Licensee
+arising out of any or all proceedings for infringement that may be
+instituted in respect of the use, modification and redistribution of the
+Software. Nevertheless, should such proceedings be instituted against
+the Licensee, the Licensor shall provide it with technical and legal
+assistance for its defense. Such technical and legal assistance shall be
+decided on a case-by-case basis between the relevant Licensor and the
+Licensee pursuant to a memorandum of understanding. The Licensor
+disclaims any and all liability as regards the Licensee's use of the
+name of the Software. No warranty is given as regards the existence of
+prior rights over the name of the Software or as regards the existence
+of a trademark.
+
+
+    Article 10 - TERMINATION
+
+10.1 In the event of a breach by the Licensee of its obligations
+hereunder, the Licensor may automatically terminate this Agreement
+thirty (30) days after notice has been sent to the Licensee and has
+remained ineffective.
+
+10.2 A Licensee whose Agreement is terminated shall no longer be
+authorized to use, modify or distribute the Software. However, any
+licenses that it may have granted prior to termination of the Agreement
+shall remain valid subject to their having been granted in compliance
+with the terms and conditions hereof.
+
+
+    Article 11 - MISCELLANEOUS
+
+
+      11.1 EXCUSABLE EVENTS
+
+Neither Party shall be liable for any or all delay, or failure to
+perform the Agreement, that may be attributable to an event of force
+majeure, an act of God or an outside cause, such as defective
+functioning or interruptions of the electricity or telecommunications
+networks, network paralysis following a virus attack, intervention by
+government authorities, natural disasters, water damage, earthquakes,
+fire, explosions, strikes and labor unrest, war, etc.
+
+11.2 Any failure by either Party, on one or more occasions, to invoke
+one or more of the provisions hereof, shall under no circumstances be
+interpreted as being a waiver by the interested Party of its right to
+invoke said provision(s) subsequently.
+
+11.3 The Agreement cancels and replaces any or all previous agreements,
+whether written or oral, between the Parties and having the same
+purpose, and constitutes the entirety of the agreement between said
+Parties concerning said purpose. No supplement or modification to the
+terms and conditions hereof shall be effective as between the Parties
+unless it is made in writing and signed by their duly authorized
+representatives.
+
+11.4 In the event that one or more of the provisions hereof were to
+conflict with a current or future applicable act or legislative text,
+said act or legislative text shall prevail, and the Parties shall make
+the necessary amendments so as to comply with said act or legislative
+text. All other provisions shall remain effective. Similarly, invalidity
+of a provision of the Agreement, for any reason whatsoever, shall not
+cause the Agreement as a whole to be invalid.
+
+
+      11.5 LANGUAGE
+
+The Agreement is drafted in both French and English and both versions
+are deemed authentic.
+
+
+    Article 12 - NEW VERSIONS OF THE AGREEMENT
+
+12.1 Any person is authorized to duplicate and distribute copies of this
+Agreement.
+
+12.2 So as to ensure coherence, the wording of this Agreement is
+protected and may only be modified by the authors of the License, who
+reserve the right to periodically publish updates or new versions of the
+Agreement, each with a separate number. These subsequent versions may
+address new issues encountered by Free Software.
+
+12.3 Any Software distributed under a given version of the Agreement may
+only be subsequently distributed under the same version of the Agreement
+or a subsequent version, subject to the provisions of Article 5.3.4.
+
+
+    Article 13 - GOVERNING LAW AND JURISDICTION
+
+13.1 The Agreement is governed by French law. The Parties agree to
+endeavor to seek an amicable solution to any disagreements or disputes
+that may arise during the performance of the Agreement.
+
+13.2 Failing an amicable solution within two (2) months as from their
+occurrence, and unless emergency proceedings are necessary, the
+disagreements or disputes shall be referred to the Paris Courts having
+jurisdiction, by the more diligent Party.
+
+
+Version 2.0 dated 2006-09-05.
Index: branches/1/Changes
===================================================================
--- branches/1/Changes	(revision 155)
+++ branches/1/Changes	(revision 155)
@@ -0,0 +1,15 @@
+This file documents the revision history for Perl extension Vote.
+
+1.00  2009-04-03 16:00
+        - add '-- ' marker in mail to avoid login/pass in mail on reply
+        - fix some typo
+
+0.20  2009-03-30 17:30:00
+        - lowercase email to detect uniqness
+        - kill whitespace caracters before and after login
+
+0.10  2009-03-28 23:00:00
+        - initial pre - release
+
+0.01  2009-03-05 11:13:11
+        - initial revision, generated by Catalyst
Index: branches/1/LICENCES
===================================================================
--- branches/1/LICENCES	(revision 155)
+++ branches/1/LICENCES	(revision 155)
@@ -0,0 +1,11 @@
+Epoll is Free software, you can redistribute it and/or modify it under
+the terms of either:
+
+CeCILL (see CeCILL.txt)
+
+Perl License, so either:
+  - the GNU General Public License as published by the Free Software Foundation;
+    either version 1, or (at your option) any later version,
+  - the "Artistic License".
+
+
Index: branches/1/MANIFEST
===================================================================
--- branches/1/MANIFEST	(revision 155)
+++ branches/1/MANIFEST	(revision 155)
@@ -0,0 +1,90 @@
+Authors
+Changes
+control/md5sum
+epoll.spec.in
+inc/Module/AutoInstall.pm
+inc/Module/Install.pm
+inc/Module/Install/AutoInstall.pm
+inc/Module/Install/Base.pm
+inc/Module/Install/Can.pm
+inc/Module/Install/Catalyst.pm
+inc/Module/Install/Fetch.pm
+inc/Module/Install/Include.pm
+inc/Module/Install/Makefile.pm
+inc/Module/Install/Metadata.pm
+inc/Module/Install/Scripts.pm
+inc/Module/Install/Win32.pm
+inc/Module/Install/WriteAll.pm
+lib/Vote.pm
+lib/Vote/Controller/Admin.pm
+lib/Vote/Controller/Ballot.pm
+lib/Vote/Controller/Newpoll.pm
+lib/Vote/Controller/Root.pm
+lib/Vote/Controller/Vote.pm
+lib/Vote/Model/Vote.pm
+lib/Vote/Template/Plugin/Mail.pm
+lib/Vote/View/TT.pm
+Makefile.PL
+MANIFEST			This list of files
+META.yml
+README
+root/favicon.ico
+root/static/images/blue-h.png
+root/static/images/blue-v.png
+root/static/images/btn_120x50_built.png
+root/static/images/btn_120x50_built_shadow.png
+root/static/images/btn_120x50_powered.png
+root/static/images/btn_120x50_powered_shadow.png
+root/static/images/btn_88x31_built.png
+root/static/images/btn_88x31_built_shadow.png
+root/static/images/btn_88x31_powered.png
+root/static/images/btn_88x31_powered_shadow.png
+root/static/images/catalyst_logo.png
+root/static/images/green-h.png
+root/static/images/green-v.png
+root/static/images/red-h.png
+root/static/images/red-v.png
+root/static/images/yellow-h.png
+root/static/images/yellow-v.png
+root/static/js/CalendarPopup.js
+root/static/vote.css
+root/templates/admin/default.tt
+root/templates/admin/denied.tt
+root/templates/admin/index.tt
+root/templates/admin/login.tt
+root/templates/ballot/closed.tt
+root/templates/ballot/default.tt
+root/templates/ballot/done.tt
+root/templates/ballot/index.tt
+root/templates/ballot/login.tt
+root/templates/ballot/signed.tt
+root/templates/default.tt
+root/templates/includes/footer.tt
+root/templates/includes/header.tt
+root/templates/includes/poll.tt
+root/templates/includes/poll_results.tt
+root/templates/includes/poll_running.tt
+root/templates/includes/signing_list.tt
+root/templates/newpoll/default.tt
+root/templates/newpoll/index.tt
+root/templates/newpoll/norequest.tt
+root/templates/newpoll/request.tt
+root/templates/vote/default.tt
+script/vote_cgi.pl
+script/vote_create.pl
+script/vote_fastcgi.pl
+script/vote_server.pl
+script/vote_test.pl
+sql/postgres.dump
+t/01app.t
+t/02pod.t
+t/03podcoverage.t
+t/controller_Admin.t
+t/controller_Ballot.t
+t/controller_Newpoll.t
+t/controller_Vote.t
+t/model_Vote.t
+t/view_TT.t
+epoll.yml.example
+LICENCES
+CeCILL.txt
Index: branches/1/Makefile.PL
===================================================================
--- branches/1/Makefile.PL	(revision 155)
+++ branches/1/Makefile.PL	(revision 155)
@@ -0,0 +1,58 @@
+use inc::Module::Install;
+
+name 'Vote';
+all_from 'lib/Vote.pm';
+
+requires 'Catalyst' => '5.7015';
+requires 'Catalyst::Plugin::ConfigLoader';
+requires 'Catalyst::Plugin::Static::Simple';
+requires 'Catalyst::Action::RenderView';
+requires 'YAML'; # This should reflect the config file format you've chosen
+                 # See Catalyst::Plugin::ConfigLoader for supported formats
+catalyst;
+
+install_script glob('script/*.pl');
+auto_install;
+WriteAll;
+
+package MY;
+
+sub distdir {
+    my $dist = shift->SUPER::distdir(@_);
+    $dist =~ s/^(create_distdir :.*)/$1 md5sum/m;
+    $dist
+}
+
+sub dist_core {
+    my $dist = shift->SUPER::dist_core(@_);
+    $dist =~ s/^(\S.* :.*)/$1 md5sum/mg;
+    $dist
+}
+
+sub postamble {
+    return "
+
+md5sum:
+\tcat MANIFEST | sed 's/\\t.*//' | grep -v control/md5sum | xargs md5sum > control/md5sum
+
+checkmd5:
+\tmd5sum -c control/md5sum
+
+.PHONY: md5sum checkmd5
+
+ChangeLog:
+\tsvn log > ChangeLog
+
+epoll.spec: epoll.spec.in Makefile
+\tsed -e 's/\@VERSION@/\$(VERSION)/' < \$< > \$@
+
+rpm: \$(DISTVNAME).tar.gz epoll.spec
+\tmkdir ./noarch || :
+\trpmbuild -ba --clean\\
+\t --define \"_sourcedir `pwd`\" \\
+\t --define \"_specdir `pwd`\" \\
+\t --define \"_srcrpmdir `pwd`\" \\
+\t --define \"_rpmdir `pwd`\" \\
+\t epoll.spec
+"
+}
Index: branches/1/README
===================================================================
--- branches/1/README	(revision 155)
+++ branches/1/README	(revision 155)
@@ -0,0 +1,51 @@
+* Requirements *
+
+- perl
+- Catalyst (http://www.catalystframework.org/)
+- postgresql (http://www.postgresql.org/)
+- a smtp server
+
+* Installation from source code *
+
+perl Makefile.PL
+make install
+
+On the database serveur:
+
+- You may want to create a specific user:
+  From postgres as administrator:
+    CREATE ROLE 'rolename' WITH LOGIN PASSWORD 'rolepassword';
+  From command line:
+    createuser 'rolename' --password
+
+- Create a database owned by this role:
+  From postgres as administrator:
+    CREATE DATABASE 'basename' OWNER TO 'rolename';
+  From command line:
+    createdb basename -O rolename
+
+- restore the based schema with sql/postgres.dump AS rolename
+  psql -u rolename basename < sql/postgres.dump
+
+(Refer to postgresql documentation for more options)
+
+Copy the vote.yml.example to /etc/vote.yml then edit it:
+- change the db settings (and probably the smtp one)
+
+- configure apache to use either the cgi or fast_cgi program or run
+  it as a standalone server.
+
+Enjoy !
+
+* WARNING *
+
+This application is provide as is, without any warranty.
+
+This file is just a quick installation guide to install the
+application does it best to respect basic rules of elections
+but do not provide information about security YOU should provide
+around to avoid fraud.
+
+* Contact *
+
+Olivier Thauvin <olivier dot thauvin at latmos dot ipsl dot fr>
Index: branches/1/epoll.spec.in
===================================================================
--- branches/1/epoll.spec.in	(revision 155)
+++ branches/1/epoll.spec.in	(revision 155)
@@ -0,0 +1,54 @@
+%define realname   Vote
+%define version    @VERSION@
+%define release    %mkrel 1
+
+Name:       Epoll
+Version:    %{version}
+Release:    %{release}
+License:    GPL or Artistic
+Group:      Development/Perl
+Summary:    A web voting application
+Source:     %{realname}-%{version}.tar.gz
+Url:        http://search.cpan.org/dist/%{realname}
+BuildRoot:  %{_tmppath}/%{name}-%{version}-%{release}-buildroot
+BuildArch:  noarch
+
+%description
+A web voting application
+
+%prep
+%setup -q -n %{realname}-%{version} 
+
+%build
+%{__perl} Makefile.PL INSTALLDIRS=vendor
+%make
+
+%if 0
+%check
+make test
+%endif
+make checkmd5
+
+%install
+rm -rf %buildroot
+%makeinstall_std
+
+mkdir -p %buildroot/etc
+install -m 700 epoll.yml.example %buildroot%_sysconfdir/epoll.yml
+mkdir -p %buildroot%_var/www/cgi-bin
+install -m 755 script/vote_cgi.pl %buildroot%_var/www/cgi-bin/vote_cgi.pl
+
+%clean
+rm -rf %buildroot
+
+%files
+%defattr(-,root,root)
+%doc Changes README
+%doc sql/postgres.dump
+%attr(0640,root,apache) %config(noreplace) %_sysconfdir/epoll.yml
+%_bindir/*
+%_var/www/cgi-bin/vote_cgi.pl
+%{_mandir}/man3/*
+%{_mandir}/man1/*
+%perl_vendorlib/*
+
Index: branches/1/epoll.yml.example
===================================================================
--- branches/1/epoll.yml.example	(revision 155)
+++ branches/1/epoll.yml.example	(revision 155)
@@ -0,0 +1,7 @@
+---
+name: Vote
+-- db: connection, see libpq documentation
+-- dbname=BASENAME;host=SERVER;user=USER;password=PASS
+db: dbname=vote
+-- The smtp serveur to use, default is localhost
+smtp:
Index: branches/1/lib/Vote.pm
===================================================================
--- branches/1/lib/Vote.pm	(revision 155)
+++ branches/1/lib/Vote.pm	(revision 155)
@@ -0,0 +1,89 @@
+package Vote;
+
+use strict;
+use warnings;
+use Config::YAML;
+
+use Catalyst::Runtime '5.70';
+
+# Set flags and add plugins for the application
+#
+#         -Debug: activates the debug mode for very useful log messages
+#   ConfigLoader: will load the configuration from a YAML file in the
+#                 application's home directory
+# Static::Simple: will serve static files from the application's root 
+#                 directory
+
+use Catalyst qw/
+    ConfigLoader
+    Static::Simple
+    Session
+    Session::Store::DBI
+    Session::State::Cookie
+    /;
+
+our $VERSION = '1.00';
+
+# Configure the application. 
+#
+# Note that settings in Vote.yml (or other external
+# configuration file that you set up manually) take precedence
+# over this when using ConfigLoader. Thus configuration
+# details given here can function as a default configuration,
+# with a external configuration file acting as an override for
+# local deployment.
+
+__PACKAGE__->config( name => 'Vote' );
+
+# Config file, in tree, else should be in /etc
+
+__PACKAGE__->config(
+    'Plugin::ConfigLoader' => {
+        file => -f __PACKAGE__->path_to('epoll.yml')
+            ? __PACKAGE__->path_to('epoll.yml')
+            : '/etc/epoll.yml',
+    }
+);
+
+__PACKAGE__->config->{session} = {
+    expires   => 1800,
+    dbi_table => 'sessions',
+    dbi_dsn => 'noo',
+};
+
+
+# Start the application
+__PACKAGE__->setup;
+
+# This is after because db config is in config file
+__PACKAGE__->config->{session}{dbi_dsn} = 'dbi:Pg:' . __PACKAGE__->config->{db};
+
+
+=head1 NAME
+
+Vote - Catalyst based application
+
+=head1 SYNOPSIS
+
+    script/vote_server.pl
+
+=head1 DESCRIPTION
+
+[enter your description here]
+
+=head1 SEE ALSO
+
+L<Vote::Controller::Root>, L<Catalyst>
+
+=head1 AUTHOR
+
+Thauvin Olivier
+
+=head1 LICENSE
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself or CeCILL.
+
+=cut
+
+1;
Index: branches/1/lib/Vote/Controller/Admin.pm
===================================================================
--- branches/1/lib/Vote/Controller/Admin.pm	(revision 155)
+++ branches/1/lib/Vote/Controller/Admin.pm	(revision 155)
@@ -0,0 +1,140 @@
+package Vote::Controller::Admin;
+
+use strict;
+use warnings;
+use base 'Catalyst::Controller';
+
+=head1 NAME
+
+Vote::Controller::Admin - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index 
+
+=cut
+
+sub begin : Private {
+    my ( $self, $c ) = @_;
+    $c->model('Vote')->db->rollback;
+}
+
+sub index : Private {
+    my ( $self, $c ) = @_;
+
+    $c->res->redirect($c->uri_for('/'));
+}
+
+sub default : Private {
+    my ( $self, $c, undef, $id ) = @_;
+    $c->stash->{voteid} = $id;
+    my $vote = $c->model('Vote');
+
+    $vote->vote_info($id) or do {
+        $c->res->redirect($c->uri_for('/'));
+        return;
+    };
+
+    my $password = $c->session->{vpassword} || $c->req->param('vpassword');
+
+    if (!$c->model('Vote')->auth_poll($id, $password)) {
+        $c->stash->{page}{title} = $vote->vote_info($id)->{label} . ': Login d\'administration';
+        $c->session->{vpassword} = undef;
+        $c->stash->{template} = 'admin/login.tt';
+        return;
+    }
+
+    $c->session->{vpassword} = $password;
+
+    $c->stash->{page}{title} = $c->model('Vote')->vote_info($id)->{label} . ': Administration';
+
+    for ($vote->vote_status($id) || '') {
+    /^BEFORE$/ and do {
+        if ($c->req->param('addch')) {
+            $vote->vote_add_choice($id, $c->req->param('addch'))
+                and $vote->db->commit;
+        } elsif ($c->req->param('delch')) {
+            $vote->delete_choice($c->req->param('delch'))
+                and $vote->db->commit;
+        } elsif ($c->req->param('label')) {
+            if ($c->req->param('dstart')) {
+                $c->req->param('start',
+                    $c->req->param('dstart') . ' ' . ($c->req->param('hstart') || '')
+                );
+            }
+            if ($c->req->param('dend')) {
+                $c->req->param('end',
+                    $c->req->param('dend') . ' ' . ($c->req->param('hend') || '')
+                );
+            }
+            $vote->vote_param(
+                $id,
+                map { $_ => ($c->req->param($_) || undef) }
+                qw(label description start end choice_count free_choice)
+            ) and $vote->db->commit;
+        }
+    };
+
+    /^(BEFORE|RUNNING)$/ and do {
+        if (my ($upload) = $c->req->upload('votinglist')) {
+            $vote->voting_from_file(
+                $id,
+                $upload->fh,
+                $c->req->param('delete'),
+            ) and $vote->db->commit;
+        } elsif($c->req->param('delvoting')) {
+            $vote->delete_voting($c->req->param('delvoting'))
+                and $vote->db->commit;
+        } elsif ($c->req->param('mail')) {
+            $vote->addupd_voting($id, $c->req->param('mail'), $c->req->param('id'))
+                and $vote->db->commit;
+        } elsif($c->req->param('mailpasswd')) {
+            $vote->mail_passwd_ifnul($id, {
+                voteurl => $c->uri_for('/ballot', $id),
+            });
+        }
+    };
+
+    /^AFTER$/ and do {
+        if ($c->req->param('mapfrom') && $c->req->param('mapto')) {
+            $vote->vote_map_value(
+                $id,
+                $c->req->param('mapfrom'),
+                $c->req->param('mapto'),
+            );
+        }
+        foreach my $bid ($vote->list_vote_ballot_needvalid($id)) {
+            if (!$c->req->param($bid)) {
+                next;
+            } elsif($c->req->param($bid) eq 'invalid') {
+                $vote->mark_ballot_invalid($bid, 1);
+                $vote->db->commit;
+            } elsif($c->req->param($bid) eq 'valid') {
+                $vote->mark_ballot_invalid($bid, 0);
+                $vote->db->commit;
+            }
+        }
+    };
+    }
+
+}
+
+=head1 AUTHOR
+
+Thauvin Olivier
+
+=head1 LICENSE
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself or CeCILL.
+
+=cut
+
+1;
Index: branches/1/lib/Vote/Controller/Ballot.pm
===================================================================
--- branches/1/lib/Vote/Controller/Ballot.pm	(revision 155)
+++ branches/1/lib/Vote/Controller/Ballot.pm	(revision 155)
@@ -0,0 +1,139 @@
+package Vote::Controller::Ballot;
+
+use strict;
+use warnings;
+use base 'Catalyst::Controller';
+
+=head1 NAME
+
+Vote::Controller::Ballot - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index 
+
+=cut
+
+sub begin : Private {
+    my ( $self, $c ) = @_;
+    $c->model('Vote')->db->rollback;
+}
+
+sub index : Private {
+    my ( $self, $c ) = @_;
+
+    $c->res->redirect($c->uri_for('/'));
+}
+
+sub default : Private {
+    my ( $self, $c, undef, $id ) = @_;
+
+    $c->stash->{voteid} = $id;
+
+    if ($c->model('Vote')->vote_status($id) ne 'RUNNING') {
+        $c->stash->{template} = 'ballot/closed.tt';
+        return;
+    }
+
+    my $mail = $c->session->{mail} || $c->req->param('mail');
+    my $password = $c->session->{password} || $c->req->param('password');
+
+    if (!$c->model('Vote')->auth_voting($id, $mail, $password)) {
+        $c->stash->{page}{title} = $c->model('Vote')->vote_info($id)->{label} . ': Login';
+        $c->delete_session('invalid user/pass');
+        $c->stash->{template} = 'ballot/login.tt';
+        if (defined($c->req->param('password'))) {
+            $c->stash->{login_failure} = 1;
+        }
+        return;
+    }
+
+    $c->session->{mail} = $mail;
+    $c->session->{password} = $password;
+
+    $c->stash->{page}{title} = $c->model('Vote')->vote_info($id)->{label} . ': Bulletin';
+
+    # login succeed, but those this user has already voted
+    if (my $date = $c->model('Vote')->voting_has_sign($id, $mail)) {
+        $c->stash->{mail} = $c->session->{mail};
+        $c->stash->{template} = 'ballot/signed.tt';
+        $c->stash->{signed_date} = $date;
+        $c->delete_session('already signed');
+        return;
+    }
+
+    my $vote = $c->model('Vote');
+    my %choices;
+    foreach ($vote->vote_choices($id)) {
+        $choices{$vote->choice_info($_)->{key}} = $vote->choice_info($_)->{label};
+    }
+    $c->stash->{choices} = { %choices };
+    $c->stash->{sbal} = { map { $_ => 1 } $c->req->param('sbal') };
+    $c->stash->{fsbal} = [ grep { $_ } map {
+        s/^\s+//;
+        s/\s+$//;
+        s/\s+/ /g;
+        lc($_)
+    } ($c->req->param('fsbal')) ];
+    $c->request->parameters->{fsbal} = $c->stash->{fsbal};
+
+    my @sbalval = grep { $_ } map { lc($choices{$_} || '') } $c->req->param('sbal');
+
+    if (scalar(@sbalval) + scalar(@{$c->stash->{fsbal} || []})
+        > $vote->vote_info($id)->{choice_count}) {
+        $c->req->parameters->{'ballot'} = '';
+        $c->stash->{vote_error} = 'Seulement ' .
+            $vote->vote_info($id)->{choice_count} . ' choix possible';
+        return;
+    }
+    {
+        my %uniq;
+        foreach(@sbalval, @{$c->stash->{fsbal} || []}) {
+            $uniq{lc($_)} ||= 0; # avoid undef
+            $uniq{lc($_)}++;
+        }
+        my @twices = grep { $uniq{$_} > 1 } (sort keys %uniq);
+        if (scalar(@twices)) {
+            $c->req->parameters->{'ballot'} = '';
+            $c->stash->{vote_error} = 'Une ou plusieurs valeurs sont en double: ' .
+                join(' ,', map { qq'"$_"' } @twices);
+            return;
+        }
+    }
+
+    if ($c->req->param('confirm')) {
+        $c->stash->{ballotid} = $vote->register_ballot(
+            $mail,
+            $id,
+            [ @sbalval ],
+            [ @{ $c->stash->{fsbal} } ],
+            $c->req->address,
+        ); # TODO trap error
+        $vote->mail_ballot_confirm($mail, $id, {
+                ballotid => $c->stash->{ballotid},
+                url => $c->uri_for('/vote', $id),
+        });
+        $c->stash->{template} = 'ballot/done.tt';
+        $c->delete_session('Vote terminÃ©');
+    }
+}
+
+=head1 AUTHOR
+
+Thauvin Olivier
+
+=head1 LICENSE
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself or CeCILL.
+
+=cut
+
+1;
Index: branches/1/lib/Vote/Controller/Newpoll.pm
===================================================================
--- branches/1/lib/Vote/Controller/Newpoll.pm	(revision 155)
+++ branches/1/lib/Vote/Controller/Newpoll.pm	(revision 155)
@@ -0,0 +1,73 @@
+package Vote::Controller::Newpoll;
+
+use strict;
+use warnings;
+use base 'Catalyst::Controller';
+
+=head1 NAME
+
+Vote::Controller::Newpoll - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index 
+
+=cut
+
+sub begin : Private {
+    my ( $self, $c ) = @_;
+    $c->model('Vote')->db->rollback;
+}
+
+sub index : Private {
+    my ( $self, $c ) = @_;
+
+    $c->stash->{page}{title} = 'CrÃ©er un nouveau vote';
+    if ($c->req->param('mail')) {
+        $c->model('Vote')->create_poll_request(
+            mail => $c->req->param('mail'),
+            url => $c->uri_for('/newpoll'),
+            label => $c->req->param('label'),
+        );
+        $c->stash->{template} = 'newpoll/request.tt';
+    }
+
+}
+
+sub default : LocalPath {
+    my ( $self, $c, undef, $id ) = @_;
+
+    $c->stash->{reqid} = $id;
+
+    if (!$c->model('Vote')->poll_request_info($id)) {
+        $c->stash->{page}{title} = "Aucune requÃªte de crÃ©ation de vote";
+        $c->stash->{template} = 'newpoll/norequest.tt';
+        return;
+    }
+
+    $c->stash->{page}{title} = 'Confirmer la crÃ©ation d\'un nouveau vote';
+    if ($c->req->param('passwd')) {
+        my $pid = $c->model('Vote')->poll_from_request($id, $c->req->param('passwd'));
+        $c->res->redirect($c->uri_for('/admin', $pid));
+    }
+}
+
+=head1 AUTHOR
+
+Thauvin Olivier
+
+=head1 LICENSE
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself or CeCILL.
+
+=cut
+
+1;
Index: branches/1/lib/Vote/Controller/Root.pm
===================================================================
--- branches/1/lib/Vote/Controller/Root.pm	(revision 155)
+++ branches/1/lib/Vote/Controller/Root.pm	(revision 155)
@@ -0,0 +1,59 @@
+package Vote::Controller::Root;
+
+use strict;
+use warnings;
+use base 'Catalyst::Controller';
+
+#
+# Sets the actions in this controller to be registered with no prefix
+# so they function identically to actions created in MyApp.pm
+#
+__PACKAGE__->config->{namespace} = '';
+
+=head1 NAME
+
+Vote::Controller::Root - Root Controller for Vote
+
+=head1 DESCRIPTION
+
+[enter your description here]
+
+=head1 METHODS
+
+=cut
+
+=head2 default
+
+=cut
+
+sub begin : Private {
+    my ( $self, $c ) = @_;
+    $c->model('Vote')->db->rollback;
+}
+
+sub default : Private {
+    my ( $self, $c ) = @_;
+
+    $c->stash->{page}{title} = 'Epoll ' . $Vote::VERSION;
+}
+
+=head2 end
+
+Attempt to render a view, if needed.
+
+=cut 
+
+sub end : ActionClass('RenderView') {}
+
+=head1 AUTHOR
+
+Thauvin Olivier
+
+=head1 LICENSE
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself or CeCILL.
+
+=cut
+
+1;
Index: branches/1/lib/Vote/Controller/Vote.pm
===================================================================
--- branches/1/lib/Vote/Controller/Vote.pm	(revision 155)
+++ branches/1/lib/Vote/Controller/Vote.pm	(revision 155)
@@ -0,0 +1,53 @@
+package Vote::Controller::Vote;
+
+use strict;
+use warnings;
+use base 'Catalyst::Controller';
+
+=head1 NAME
+
+Vote::Controller::Vote - Catalyst Controller
+
+=head1 DESCRIPTION
+
+Catalyst Controller.
+
+=head1 METHODS
+
+=cut
+
+
+=head2 index 
+
+=cut
+
+sub begin : Private {
+    my ( $self, $c ) = @_;
+    $c->model('Vote')->db->rollback;
+}
+
+sub index : Private {
+    my ( $self, $c ) = @_;
+
+    $c->redirect($c->uri_for('/'));
+}
+
+sub default : LocalPath {
+    my ( $self, $c, undef, $id ) = @_;
+
+    $c->stash->{voteid} = $id;
+    $c->stash->{page}{title} = 'Vote: ' . $c->model('Vote')->vote_info($id)->{label};
+}
+
+=head1 AUTHOR
+
+Thauvin Olivier
+
+=head1 LICENSE
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself or CeCILL.
+
+=cut
+
+1;
Index: branches/1/lib/Vote/Model/Vote.pm
===================================================================
--- branches/1/lib/Vote/Model/Vote.pm	(revision 155)
+++ branches/1/lib/Vote/Model/Vote.pm	(revision 155)
@@ -0,0 +1,971 @@
+package Vote::Model::Vote;
+
+use strict;
+use warnings;
+use base 'Catalyst::Model';
+use Vote;
+use DBI;
+use Mail::Mailer;
+
+=head1 NAME
+
+Vote::Model::Vote - Catalyst Model
+
+=head1 DESCRIPTION
+
+Catalyst Model.
+
+=cut
+
+sub new {
+    my ($class) = @_;
+    
+    bless {
+        db => _newdb(),
+    }, $class;
+}
+
+sub _newdb {
+    my $db = DBI->connect(
+        'dbi:Pg:' . Vote->config->{db},
+        undef, undef,
+        {
+            RaiseError => 0,
+            AutoCommit => 0,
+            PrintWarn => 0,
+            PrintError => 1,
+        }
+    ) or return;
+    $db->do(q{set DATESTYLE to 'DMY'});
+    return $db;
+}
+
+sub db {
+    return $_[0]->{db} && $_[0]->{db}->ping
+        ? $_[0]->{db}
+        : $_[0]->_newdb();
+}
+
+sub mail_header {
+    return(
+        'Content-Type' => 'text/plain; charset=UTF-8; format=flowed',
+        'Content-Transfer-Encoding' => '8bit',
+        'X-Epoll-version' => $Vote::VERSION,
+    );
+}
+
+sub random_string {
+    my $lenght = $_[-1] || 8;
+
+    return join('', map { ('a'..'z', 'A'..'Z', 0..9)[rand 62] } (1..$lenght));
+}
+
+sub gen_enc_passwd {
+    my ($self, $passwd) = @_;
+
+    $passwd ||= random_string(8);
+    return(crypt($passwd, '$1$' . random_string(8) . '$'));
+}
+
+sub dbtime {
+    my ($self) = @_;
+    my $sth = $self->db->prepare(
+        q{select to_char(now(), 'DD/MM/YYYY HH24:MI:SS') as d}
+    );
+
+    $sth->execute();
+    my $res = $sth->fetchrow_hashref;
+    $sth->finish;
+    $res->{d};
+}
+
+sub list_comming_vote {
+    my ($self) = @_;
+
+    my $sth = $self->db->prepare_cached(
+        q{
+        select id from poll where
+        (start > now() and "end" > now()) or
+        "end" is null or start is null
+        }
+    );
+
+    $sth->execute;
+    my @id;
+    while(my $res = $sth->fetchrow_hashref) {
+        push(@id, $res->{id});
+    }
+
+    @id
+}
+
+
+sub list_running_vote {
+    my ($self) = @_;
+
+    my $sth = $self->db->prepare_cached(
+        q{
+        select id from poll where start < now() and "end" > now()
+        }
+    );
+
+    $sth->execute;
+    my @id;
+    while(my $res = $sth->fetchrow_hashref) {
+        push(@id, $res->{id});
+    }
+
+    @id
+}
+
+sub list_closed_vote {
+    my ($self) = @_;
+
+    my $sth = $self->db->prepare_cached(
+        q{
+        select id from poll where
+        start < now() and "end" < now()
+        }
+    );
+
+    $sth->execute;
+    my @id;
+    while(my $res = $sth->fetchrow_hashref) {
+        push(@id, $res->{id});
+    }
+
+    @id
+}
+
+sub vote_param {
+    my ($self, $voteid, %attr) = @_;
+
+    keys %attr or return;
+    my @online_f = qw(label start end owner password);
+
+    if (grep { exists($attr{$_}) } @online_f) {
+        my $sth = $self->db->prepare_cached(
+            q{update poll set } .
+            join(',', map { qq("$_" = ?) } grep { exists $attr{$_} } @online_f) .
+            q{ where id = ?}
+        );
+        $sth->execute((map { $attr{$_} } grep { exists $attr{$_} } @online_f), $voteid)
+            or do {
+            $self->db->rollback;
+            return;
+        };
+    }
+
+    # vote settings in settings table
+    foreach my $var (keys %attr) {
+        grep { $var eq $_ } @online_f and next;
+        $self->vote_set_settings($voteid, $var, $attr{$var});
+    }
+    1
+}
+
+sub vote_status {
+    my ($self, $id) = @_;
+    
+    my $sth = $self->db->prepare_cached(
+        q{
+        select (start > now() or start is null) as before,
+               "end" < now() as after
+        from poll
+        where id = ?
+        }
+    );
+    $sth->execute($id);
+    my $res = $sth->fetchrow_hashref;
+    $sth->finish;
+    $res or return;
+    if ($res->{before}) {
+        return 'BEFORE';
+    } elsif ($res->{after}) {
+        return 'AFTER';
+    } else {
+        return 'RUNNING';
+    }
+}
+
+sub vote_info {
+    my ($self, $id) = @_;
+
+    my $sth = $self->db->prepare_cached(
+        q{
+        select *,
+        to_char("start", 'DD/MM/YYYY') as dstart,
+        to_char("start", 'HH24:MI:SS') as hstart,
+        to_char("end", 'DD/MM/YYYY') as dend,
+        to_char("end", 'HH24:MI:SS') as hend
+        from poll where id = ?
+        }
+    );
+
+    $sth->execute($id);
+    my $res = $sth->fetchrow_hashref;
+    $sth->finish;
+    if ($res) {
+        my $get = $self->db->prepare_cached(
+            q{select var, val from settings where poll = ?}
+        );
+        $get->execute($id);
+        while (my $set = $get->fetchrow_hashref) {
+            $res->{$set->{var}} = $set->{val};
+        }
+    }
+    $res->{free_choice} ||= 0; # avoiding undef
+    $res
+}
+
+sub vote_set_settings {
+    my ($self, $poll, $var, $val) = @_;
+
+    my $upd = $self->db->prepare_cached(
+        q{update settings set val = ? where poll = ? and var = ?}
+    );
+
+    if ($upd->execute($val, $poll, $var) == 0) {
+        my $add = $self->db->prepare_cached(
+            q{insert into settings (poll, var, val) values (?,?,?)}
+        );
+
+        $add->execute($poll, $var, $val);
+    }
+}
+
+sub vote_signing {
+    my ($self, $id) = @_;
+
+    my $sth = $self->db->prepare_cached(
+        q{
+        select *, voting.key as vkey from voting left join signing
+        on signing.key = voting.key
+        where poll = ? order by voting.mail
+        }
+    );
+    $sth->execute($id);
+    my @people;
+    while (my $res = $sth->fetchrow_hashref) {
+        push(@people, $res);
+    }
+    @people
+}
+
+sub vote_voting {
+    my ($self, $voteid) = @_;
+
+    my $sth = $self->db->prepare_cached(
+        q{
+        select key from voting
+        where poll = ? order by voting.mail
+        }
+    );
+    $sth->execute($voteid);
+    my @people;
+    while (my $res = $sth->fetchrow_hashref) {
+        push(@people, $res->{key});
+    }
+    @people
+}
+
+sub voting_info {
+    my ($self, $id) = @_;
+
+    my $sth = $self->db->prepare_cached(
+        q{
+        select *, voting.key as vkey from voting left join signing
+        on signing.key = voting.key
+        where voting.key = ?
+        }
+    );
+    $sth->execute($id);
+
+    my $res = $sth->fetchrow_hashref;
+    $sth->finish;
+    $res
+}
+
+sub vote_choices {
+    my ($self, $id) = @_;
+
+    my $sth = $self->db->prepare_cached(
+        q{
+        select key from choice where poll = ?
+        order by label
+        }
+    );
+    $sth->execute($id);
+    my @ch;
+    while (my $res = $sth->fetchrow_hashref) {
+        push(@ch, $res->{key});
+    }
+    @ch
+}
+
+sub choice_info {
+    my ($self, $chid) = @_;
+    my $sth = $self->db->prepare_cached(
+        q{select * from choice where key = ?}
+    );
+    $sth->execute($chid);
+    my $res = $sth->fetchrow_hashref;
+    $sth->finish;
+    $res
+}
+
+sub vote_add_choice {
+    my ($self, $voteid, $label) = @_;
+
+    my $sth = $self->db->prepare_cached(
+        q{insert into choice (poll, label) values (?,?)}
+    );
+
+    $sth->execute($voteid, $label) or do {
+        $self->db->rollback;
+        return;
+    };
+
+    1
+}
+
+sub modify_choice {
+    my ($self, $chid, $label) = @_;
+
+    my $sth = $self->db->prepare_cached(
+        q{update choice set label = ? where key = ?}
+    );
+    $sth->execute($label, $chid);
+}
+
+sub delete_choice {
+    my ($self, $chid) = @_;
+
+    my $sth = $self->db->prepare_cached(
+        q{delete from choice where key = ?}
+    );
+
+    $sth->execute($chid);
+}
+
+sub voting_info_id {
+    my ($self, $mail, $voteid) = @_;
+
+    my $sth = $self->db->prepare_cached(
+        q{
+        select * from voting where mail = ? and poll = ?
+        }
+    );
+    $sth->execute($mail, $voteid);
+    my $res = $sth->fetchrow_hashref();
+    $sth->finish;
+    $res
+}
+
+sub _register_signing {
+    my ($self, $mail, $voteid, $referal) = @_;
+
+    my $vinfo = $self->voting_info_id($mail, $voteid) or return;
+
+    my $sth = $self->db->prepare_cached(
+        q{
+        insert into signing (key, referal) values (?,?)
+        }
+    );
+    $sth->execute($vinfo->{key}, $referal) or do {
+        $self->db->rollback;
+        return;
+    };
+
+    1;
+}
+
+sub gen_uid {
+    unpack("H*", join("", map { chr(rand(256)) } (0..15)))
+}
+
+sub _register_ballot {
+    my ($self, $voteid, $choice, $fchoice) = @_;
+
+    my $addb = $self->db->prepare_cached(
+        q{
+        insert into ballot (id, poll, invalid) values (?,?,?)
+        }
+    );
+    my $uid = gen_uid;
+    $addb->execute($uid, $voteid, scalar(@{$fchoice || []}) ? undef : 'f') or do {
+        self->db->rollback;
+        return;
+    };
+
+    my $addbc = $self->db->prepare_cached(
+        q{
+        insert into ballot_item (id, value, fromlist) values (?,?,?)
+        }
+    );
+    foreach (@{ $choice || []}) {
+        $addbc->execute($uid, $_, 't') or do {
+            $self->db->rollback;
+            return;
+        };
+    }
+    foreach (@{ $fchoice || []}) {
+        $_ or next;
+        $addbc->execute($uid, $_, 'f') or do {
+            $self->db->rollback;
+            return;
+        };
+    }
+
+    $uid;
+}
+
+sub register_ballot {
+    my ($self, $vid, $voteid, $choice, $fchoice, $referal) = @_;
+
+    my $uid;
+    for (0..2) { # 3 try
+    # First we register voting has voted
+    $self->_register_signing($vid, $voteid, $referal) or return; # TODO error ?
+
+    # registring choices
+    $uid = $self->_register_ballot($voteid, $choice, $fchoice);
+    defined($uid) and last;
+
+    }
+    # everything went fine, saving!
+    $self->db->commit;
+
+    
+    $uid
+}
+
+sub mail_ballot_confirm {
+    my ($self, $vid, $voteid, $info) = @_;
+    my $voteinfo = $self->vote_info($voteid) or return;
+    $info->{ballotid} or return;
+    my $mailer = new Mail::Mailer 'smtp', Server => (Vote->config->{smtp} || 'localhost');
+    $ENV{MAILADDRESS} = $vid;
+    $mailer->open({
+        From => $vid, # TODO allow to configure this
+        To => $vid,
+        Subject => 'Confirmation de vote: ' . $voteinfo->{label},
+        mail_header(),
+    });
+    print $mailer <<EOF;
+
+Vous venez de participer au vote:
+
+--------
+$voteinfo->{label}
+--------
+
+Votre bulletin est idÃ©ntifiÃ© sous le numÃ©ro:
+$info->{ballotid}
+
+Les rÃ©sultats seront disponibles Ã  cet url:
+$info->{url}
+
+Cordialement.
+EOF
+    $mailer->close
+        or warn "couldn't send whole message: $!\n";
+
+}
+
+sub vote_voting_count {
+    my ($self, $id) = @_;
+
+    my $sth = $self->db->prepare_cached(
+        q{
+        select count(*) from voting
+        where poll = ?
+        }
+    );
+    $sth->execute($id);
+    my $res = $sth->fetchrow_hashref;
+    $sth->finish;
+    $res->{count}
+}
+
+sub signing_count { vote_signing_count(@_) }
+
+sub vote_signing_count {
+    my ($self, $voteid) = @_;
+
+    my $sth = $self->db->prepare_cached(
+        q{
+        select count(*) from signing join voting
+        on voting.key = signing.key where poll = ?
+        }
+    );
+
+    $sth->execute($voteid);
+    my $res = $sth->fetchrow_hashref;
+    $sth->finish;
+    $res->{count}
+}
+
+sub ballot_count { vote_ballot_count(@_) }
+
+sub vote_ballot_count {
+    my ($self, $voteid) = @_;
+
+    my $sth = $self->db->prepare_cached(
+        q{
+        select count(*) from ballot where poll = ?
+        }
+    );
+
+    $sth->execute($voteid);
+    my $res = $sth->fetchrow_hashref;
+    $sth->finish;
+    $res->{count}
+}
+
+sub ballot_count_nonull { vote_ballot_count_nonull(@_) }
+
+sub vote_ballot_count_nonull {
+    my ($self, $voteid) = @_;
+
+    my $sth = $self->db->prepare_cached(
+        q{
+        select count(*) from ballot where poll = ?
+        and id in (select id from ballot_item) and
+        (invalid = 'false' or invalid is null)
+        }
+    );
+
+    $sth->execute($voteid);
+    my $res = $sth->fetchrow_hashref;
+    $sth->finish;
+    $res->{count}
+}
+
+sub auth_voting {
+    my ($self, $poll, $mail, $password) = @_;
+    my $userinfo = $self->voting_info_id($mail, $poll) or return;
+
+    $userinfo->{passwd} or return;
+    if (crypt($password, $userinfo->{passwd} || '') eq $userinfo->{passwd}) {
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+sub auth_poll {
+    my ($self, $voteid, $passwd) = @_;
+
+    my $vinfo = $self->vote_info($voteid) or return;
+
+    $vinfo->{password} or return;
+    $passwd or return;
+    if (crypt($passwd, $vinfo->{password} || '') eq $vinfo->{password}) {
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+sub voting_has_sign {
+    my ($self, $poll, $user) = @_;
+
+    my $sth = $self->db->prepare_cached(
+        q{
+        select date from signing join voting
+        on voting.key = signing.key
+        where poll = ? and mail = ?
+        }
+    );
+
+    $sth->execute($poll, $user);
+    my $res = $sth->fetchrow_hashref;
+    $sth->finish;
+    return $res->{date}
+}
+
+# Requete de decompte des voix:
+
+sub vote_results_count {
+    my ($self, $voteid) = @_;
+
+    my $sth = $self->db->prepare(
+        q{
+        select count(ballot.id), value from ballot left join ballot_item
+        on ballot.id = ballot_item.id where ballot.poll = ? and invalid = 'false'
+        group by value
+        order by count
+        }
+    );
+    $sth->execute($voteid);
+    my @results;
+    while (my $res = $sth->fetchrow_hashref) {
+        push(@results, $res);
+    }
+    @results;
+}
+
+sub vote_results_nonull {
+    my ($self, $voteid) = @_;
+
+    my $sth = $self->db->prepare(
+        q{
+        select count(ballot.id), coalesce(corrected, value) as value
+        from ballot join ballot_item
+        on ballot.id = ballot_item.id where ballot.poll = ? and
+        (invalid = 'false' or invalid is null)
+        group by coalesce(corrected, value)
+        order by count desc
+        }
+    );
+    $sth->execute($voteid);
+    my @results;
+    while (my $res = $sth->fetchrow_hashref) {
+        push(@results, $res);
+    }
+    \@results;
+}
+
+sub list_vote_ballot {
+    my ($self, $voteid) = @_;
+
+    my $sth = $self->db->prepare_cached(
+        q{
+        select id from ballot where poll = ?
+        order by id
+        }
+    );
+    $sth->execute($voteid);
+    my @ids;
+    while (my $res = $sth->fetchrow_hashref) {
+        push(@ids, $res->{id});
+    }
+    @ids
+}
+
+sub list_vote_ballot_needvalid {
+    my ($self, $voteid) = @_;
+
+    my $sth = $self->db->prepare_cached(
+        q{
+        select id from ballot where poll = ?
+        and invalid is null order by id
+        }
+    );
+    $sth->execute($voteid);
+    my @ids;
+    while (my $res = $sth->fetchrow_hashref) {
+        push(@ids, $res->{id});
+    }
+    @ids
+}
+
+sub ballot_info {
+    my ($self, $ballotid) = @_;
+
+    my $sth = $self->db->prepare_cached(
+        q{ select * from ballot where id = ? }
+    );
+
+    $sth->execute($ballotid);
+    my $res = $sth->fetchrow_hashref;
+    $sth->finish;
+    $res
+}
+
+sub mark_ballot_invalid {
+    my ($self, $ballotid, $invalid) = @_;
+
+    my $sth = $self->db->prepare_cached(
+        q{update ballot set invalid = ? where id = ?}
+    );
+
+    $sth->execute($invalid ? 't' : 'f', $ballotid);
+}
+
+sub ballot_items {
+    my ($self, $ballotid) = @_;
+
+    my $sth = $self->db->prepare_cached(
+        q{select *, value as v from ballot_item where id = ?}
+    );
+    $sth->execute($ballotid);
+    my @ids;
+    while (my $res = $sth->fetchrow_hashref) {
+        push(@ids, $res);
+    }
+    \@ids
+}
+
+sub vote_ballot_untrusted_values {
+    my ($self, $voteid) = @_;
+
+    my $getval = $self->db->prepare_cached(
+        q{
+        select value from ballot join ballot_item
+        on ballot.id = ballot_item.id
+        where poll = ? and fromlist = false and corrected is null
+        group by value order by value
+        }
+    );
+    $getval->execute($voteid);
+    my @vals;
+    while (my $res = $getval->fetchrow_hashref) {
+        push(@vals, $res->{value});
+    }
+    @vals
+}
+
+sub vote_ballot_values {
+    my ($self, $voteid) = @_;
+
+    my $getval = $self->db->prepare_cached(
+        q{
+        select coalesce(corrected, value) as value from ballot join ballot_item
+        on ballot.id = ballot_item.id
+        where poll = ?
+        group by coalesce(corrected, value) order by coalesce(corrected, value)
+        }
+    );
+    $getval->execute($voteid);
+    my @vals;
+    while (my $res = $getval->fetchrow_hashref) {
+        push(@vals, $res->{value});
+    }
+    @vals
+}
+
+sub vote_map_value {
+    my ($self, $voteid, $from, $to) = @_;
+
+    my $sth = $self->db->prepare_cached(
+        q{
+        update ballot_item set corrected = ? where
+        id in (select id from ballot where poll = ?)
+        and (value = ? or corrected = ?)
+        }
+    );
+
+    $sth->execute($to, $voteid, $from, $from) or $self->db->rollback;
+    $self->db->commit;
+}
+
+sub addupd_voting {
+    my ($self, $voteid, $mail, $id) = @_;
+
+    $mail =~ s/\s*$//;
+    $mail =~ s/^\s*//;
+    $mail = lc($mail);
+    $id =~ s/\s*$//;
+    $id =~ s/^\s//;
+    my $upd = $self->db->prepare_cached(
+        q{
+        update voting set label = ? where mail = ? and poll = ?
+        }
+    );
+
+    if ($upd->execute($id || '', $mail, $voteid) == 0) {
+        my $add = $self->db->prepare_cached(q{
+            insert into voting (poll, label, mail) values (?,?,?)
+        });
+
+        $add->execute($voteid, $id || '', $mail);
+    }
+}
+
+sub delete_voting {
+    my ($self, $key) = @_;
+
+    $self->voting_has_sign($key) and return;
+    my $sth = $self->db->prepare_cached(
+        q{delete from voting where key = ?}
+    );
+
+    $sth->execute($key);
+}
+
+sub voting_from_file {
+    my ($self, $voteid, $fh, $delete) = @_;
+
+    if ($delete) {
+        my $sth = $self->db->prepare(q{delete from voting where poll = ?});
+        $sth->execute($voteid);
+    }
+
+    while (my $line = <$fh>) {
+        chomp($line);
+        my ($mail, $name) = split(';', $line);
+        $mail or do {
+            $self->db->rollback;
+            return;
+        };
+        $self->addupd_voting($voteid, $mail, $name || '');
+    }
+    1;
+}
+
+sub mail_passwd_ifnul {
+    my ($self, $voteid, $mailinfo) = @_;
+
+    my $list_voting = $self->db->prepare_cached(
+        q{select key from voting where poll = ? and passwd is null or passwd = ''}
+    );
+
+    $list_voting->execute($voteid);
+    while (my $res = $list_voting->fetchrow_hashref) {
+        $self->mail_voting_passwd($res->{key}, $mailinfo);
+    }
+}
+
+sub mail_voting_passwd {
+    my ($self, $id, $mailinfo) = @_;
+    
+    my $vinfo = $self->voting_info($id) or return;
+    my $voteinfo = $self->vote_info($vinfo->{poll});
+    $voteinfo->{description} ||= "";
+
+    my $passwd = random_string(8);
+    my $encpasswd = $self->gen_enc_passwd($passwd);
+
+    my $upd_voting = $self->db->prepare_cached(
+        q{update voting set passwd = ? where key = ?}
+    );
+
+    $upd_voting->execute($encpasswd, $id);
+
+    my $date = $voteinfo->{dstart} && $voteinfo->{dend}
+        ? sprintf("\n" . 'Vous pourrez voter entre le %s %s et le %s %s' . "\n",
+            $voteinfo->{dstart}, $voteinfo->{hstart}, $voteinfo->{dend}, $voteinfo->{hend})
+        : '';
+
+    # TODO complete this properly:
+    my $mailer = new Mail::Mailer 'smtp', Server => (Vote->config->{smtp} || 'localhost');
+    $ENV{MAILADDRESS} = $voteinfo->{owner};
+    $mailer->open({
+        From => $voteinfo->{owner},
+        To => $vinfo->{mail},
+        Subject => 'Invitation a voter: ' . $voteinfo->{label},
+        'X-Epoll-poll' => $id,
+        mail_header(),
+    });
+    print $mailer <<EOF;
+Vous Ãªtes conviÃ© Ã  participer a ce vote:
+
+--------
+$voteinfo->{label}
+--------
+$voteinfo->{description}
+--------
+
+Ã  l'adresse:
+
+$mailinfo->{voteurl}
+$date
+
+-- 
+Votre identifiant est: $vinfo->{mail}
+Votre mot de passe est: $passwd
+
+Conservez prÃ©cieusement ces identifiants, il ne vous seront pas retransmits.
+
+Cordialement.
+EOF
+    $mailer->close or warn "couldn't send whole message: $!\n";
+
+    $self->db->commit;
+}
+
+sub poll_request_info {
+    my ($self, $rid) = @_;
+
+    my $sth = $self->db->prepare_cached(
+        q{select * from poll_request where id = ?}
+    );
+
+    $sth->execute($rid);
+    my $res = $sth->fetchrow_hashref;
+    $sth->finish;
+    $res
+}
+
+sub poll_from_request {
+    my ($self, $rid, $passwd) = @_;
+    my $rinfo = $self->poll_request_info($rid) or return;
+
+    my $encpasswd = $self->gen_enc_passwd($passwd);
+
+    my $getpollid = $self->db->prepare_cached(
+        q{select nextval('poll_id_seq')}
+    );
+    $getpollid->execute();
+    my $newpollid = $getpollid->fetchrow_hashref->{nextval};
+    
+    my $newpoll = $self->db->prepare_cached(
+        q{insert into poll (id, label, owner, password) values (?,?,?,?)}
+    );
+
+    $newpoll->execute($newpollid, $rinfo->{label}, $rinfo->{mail}, $encpasswd);
+    # set some default
+    $self->vote_param($newpollid,
+        free_choice => 0,
+        choice_count => 1,
+    );     
+
+    my $delreq = $self->db->prepare_cached(
+        q{delete from poll_request where id = ?}
+    );
+
+    $delreq->execute($rid);
+    $self->db->commit;
+
+    $newpollid
+}
+
+sub create_poll_request {
+    my ($self, %info) = @_;
+
+    $info{mail} or return;
+    my $addreq = $self->db->prepare_cached(
+        q{insert into poll_request (id, label, mail) values (?,?,?)}
+    );
+
+    my $reqid = gen_uid;
+
+    $addreq->execute($reqid, $info{label}, $info{mail});
+    my $mailer = new Mail::Mailer 'smtp', Server => (Vote->config->{smtp} || 'localhost');
+    $ENV{MAILADDRESS} = undef;
+    $mailer->open({
+        From => 'Voting system <nomail@nomail.com>', # TODO allow to configure this
+        To => $info{mail},
+        Subject => 'Votre nouveau vote',
+        mail_header(),
+    });
+    print $mailer <<EOF;
+
+Vous avez demandez la crÃ©ation d'un nouveau vote:
+$info{label}
+
+Pour valider votre demande, veuiller allez visitez la page:
+$info{url}/$reqid
+
+A bientÃŽt
+EOF
+    $mailer->close
+        or warn "couldn't send whole message: $!\n";
+    $self->db->commit;
+    1;
+}
+
+=head1 AUTHOR
+
+Thauvin Olivier
+
+=head1 LICENSE
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself or CeCILL.
+
+=cut
+
+1;
Index: branches/1/lib/Vote/Template/Plugin/Mail.pm
===================================================================
--- branches/1/lib/Vote/Template/Plugin/Mail.pm	(revision 155)
+++ branches/1/lib/Vote/Template/Plugin/Mail.pm	(revision 155)
@@ -0,0 +1,50 @@
+# $Id$
+
+package Vote::Template::Plugin::Mail;
+
+=head1 NAME
+
+Vote::Template::Plugin::Mail - Template Filter for email
+
+=head1 DESCRIPTION
+
+This filter replace '.' by 'dot' and '@' by 'at' to make email
+harder to find by spam bot.
+
+=cut
+
+use strict;
+use warnings;
+
+use base qw( Template::Plugin::Filter );
+
+sub init {
+    my $self = shift;
+    my $name = $self->{ _ARGS }->[0] || 'mail';
+    $self->install_filter($name);
+    return $self;
+}
+
+sub filter {
+    my ($self, $text) = @_;
+
+    $text ||= '';
+
+    $text =~ s/\@/ at /g;
+    $text =~ s/\./ dot /g;
+
+    $text
+}
+
+=head1 AUTHORS
+
+Olivier Thauvin
+
+=head1 LICENSE
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself or CeCILL.
+
+=cut
+
+1;
Index: branches/1/lib/Vote/View/TT.pm
===================================================================
--- branches/1/lib/Vote/View/TT.pm	(revision 155)
+++ branches/1/lib/Vote/View/TT.pm	(revision 155)
@@ -0,0 +1,46 @@
+package Vote::View::TT;
+
+use strict;
+use base 'Catalyst::View::TT';
+
+__PACKAGE__->config(
+    TEMPLATE_EXTENSION => '.tt',
+    INCLUDE_PATH => Vote->path_to( 'root', 'templates' ),
+    PRE_PROCESS => 'includes/header.tt',
+    POST_PROCESS => 'includes/footer.tt',
+    PLUGIN_BASE => 'Vote::Template::Plugin',
+);
+
+=head1 NAME
+
+Vote::View::TT - TT View for Vote
+
+=head1 DESCRIPTION
+
+TT View for Vote. 
+
+=cut
+
+sub process {
+    my ($self, $c) = @_;
+
+    $c->stash->{Vote}{VERSION} = $Vote::VERSION;
+    Catalyst::View::TT::process($self, $c);
+}
+
+=head1 SEE ALSO
+
+L<Vote>
+
+=head1 AUTHOR
+
+Thauvin Olivier
+
+=head1 LICENSE
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself or CeCILL.
+
+=cut
+
+1;
Index: branches/1/root/static/js/CalendarPopup.js
===================================================================
--- branches/1/root/static/js/CalendarPopup.js	(revision 155)
+++ branches/1/root/static/js/CalendarPopup.js	(revision 155)
@@ -0,0 +1,1465 @@
+// ===================================================================
+// Author: Matt Kruse <matt@mattkruse.com>
+// WWW: http://www.mattkruse.com/
+//
+// NOTICE: You may use this code for any purpose, commercial or
+// private, without any further permission from the author. You may
+// remove this notice from your final code if you wish, however it is
+// appreciated by the author if at least my web site address is kept.
+//
+// You may *NOT* re-distribute this code in any way except through its
+// use. That means, you can include it in your product, or your web
+// site, or any other form where the code is actually being used. You
+// may not put the plain javascript up on your site for download or
+// include it in your javascript libraries for download. 
+// If you wish to share this code with others, please just point them
+// to the URL instead.
+// Please DO NOT link directly to my .js files from your site. Copy
+// the files to your server and use them there. Thank you.
+// ===================================================================
+
+
+/* SOURCE FILE: AnchorPosition.js */
+
+/* 
+AnchorPosition.js
+Author: Matt Kruse
+Last modified: 10/11/02
+
+DESCRIPTION: These functions find the position of an <A> tag in a document,
+so other elements can be positioned relative to it.
+
+COMPATABILITY: Netscape 4.x,6.x,Mozilla, IE 5.x,6.x on Windows. Some small
+positioning errors - usually with Window positioning - occur on the 
+Macintosh platform.
+
+FUNCTIONS:
+getAnchorPosition(anchorname)
+  Returns an Object() having .x and .y properties of the pixel coordinates
+  of the upper-left corner of the anchor. Position is relative to the PAGE.
+
+getAnchorWindowPosition(anchorname)
+  Returns an Object() having .x and .y properties of the pixel coordinates
+  of the upper-left corner of the anchor, relative to the WHOLE SCREEN.
+
+NOTES:
+
+1) For popping up separate browser windows, use getAnchorWindowPosition. 
+   Otherwise, use getAnchorPosition
+
+2) Your anchor tag MUST contain both NAME and ID attributes which are the 
+   same. For example:
+   <A NAME="test" ID="test"> </A>
+
+3) There must be at least a space between <A> </A> for IE5.5 to see the 
+   anchor tag correctly. Do not do <A></A> with no space.
+*/ 
+
+// getAnchorPosition(anchorname)
+//   This function returns an object having .x and .y properties which are the coordinates
+//   of the named anchor, relative to the page.
+function getAnchorPosition(anchorname) {
+	// This function will return an Object with x and y properties
+	var useWindow=false;
+	var coordinates=new Object();
+	var x=0,y=0;
+	// Browser capability sniffing
+	var use_gebi=false, use_css=false, use_layers=false;
+	if (document.getElementById) { use_gebi=true; }
+	else if (document.all) { use_css=true; }
+	else if (document.layers) { use_layers=true; }
+	// Logic to find position
+ 	if (use_gebi && document.all) {
+		x=AnchorPosition_getPageOffsetLeft(document.all[anchorname]);
+		y=AnchorPosition_getPageOffsetTop(document.all[anchorname]);
+		}
+	else if (use_gebi) {
+		var o=document.getElementById(anchorname);
+		x=AnchorPosition_getPageOffsetLeft(o);
+		y=AnchorPosition_getPageOffsetTop(o);
+		}
+ 	else if (use_css) {
+		x=AnchorPosition_getPageOffsetLeft(document.all[anchorname]);
+		y=AnchorPosition_getPageOffsetTop(document.all[anchorname]);
+		}
+	else if (use_layers) {
+		var found=0;
+		for (var i=0; i<document.anchors.length; i++) {
+			if (document.anchors[i].name==anchorname) { found=1; break; }
+			}
+		if (found==0) {
+			coordinates.x=0; coordinates.y=0; return coordinates;
+			}
+		x=document.anchors[i].x;
+		y=document.anchors[i].y;
+		}
+	else {
+		coordinates.x=0; coordinates.y=0; return coordinates;
+		}
+	coordinates.x=x;
+	coordinates.y=y;
+	return coordinates;
+	}
+
+// getAnchorWindowPosition(anchorname)
+//   This function returns an object having .x and .y properties which are the coordinates
+//   of the named anchor, relative to the window
+function getAnchorWindowPosition(anchorname) {
+	var coordinates=getAnchorPosition(anchorname);
+	var x=0;
+	var y=0;
+	if (document.getElementById) {
+		if (isNaN(window.screenX)) {
+			x=coordinates.x-document.body.scrollLeft+window.screenLeft;
+			y=coordinates.y-document.body.scrollTop+window.screenTop;
+			}
+		else {
+			x=coordinates.x+window.screenX+(window.outerWidth-window.innerWidth)-window.pageXOffset;
+			y=coordinates.y+window.screenY+(window.outerHeight-24-window.innerHeight)-window.pageYOffset;
+			}
+		}
+	else if (document.all) {
+		x=coordinates.x-document.body.scrollLeft+window.screenLeft;
+		y=coordinates.y-document.body.scrollTop+window.screenTop;
+		}
+	else if (document.layers) {
+		x=coordinates.x+window.screenX+(window.outerWidth-window.innerWidth)-window.pageXOffset;
+		y=coordinates.y+window.screenY+(window.outerHeight-24-window.innerHeight)-window.pageYOffset;
+		}
+	coordinates.x=x;
+	coordinates.y=y;
+	return coordinates;
+	}
+
+// Functions for IE to get position of an object
+function AnchorPosition_getPageOffsetLeft (el) {
+	var ol=el.offsetLeft;
+	while ((el=el.offsetParent) != null) { ol += el.offsetLeft; }
+	return ol;
+	}
+function AnchorPosition_getWindowOffsetLeft (el) {
+	return AnchorPosition_getPageOffsetLeft(el)-document.body.scrollLeft;
+	}	
+function AnchorPosition_getPageOffsetTop (el) {
+	var ot=el.offsetTop;
+	while((el=el.offsetParent) != null) { ot += el.offsetTop; }
+	return ot;
+	}
+function AnchorPosition_getWindowOffsetTop (el) {
+	return AnchorPosition_getPageOffsetTop(el)-document.body.scrollTop;
+	}
+
+/* SOURCE FILE: date.js */
+
+// HISTORY
+// ------------------------------------------------------------------
+// May 17, 2003: Fixed bug in parseDate() for dates <1970
+// March 11, 2003: Added parseDate() function
+// March 11, 2003: Added "NNN" formatting option. Doesn't match up
+//                 perfectly with SimpleDateFormat formats, but 
+//                 backwards-compatability was required.
+
+// ------------------------------------------------------------------
+// These functions use the same 'format' strings as the 
+// java.text.SimpleDateFormat class, with minor exceptions.
+// The format string consists of the following abbreviations:
+// 
+// Field        | Full Form          | Short Form
+// -------------+--------------------+-----------------------
+// Year         | yyyy (4 digits)    | yy (2 digits), y (2 or 4 digits)
+// Month        | MMM (name or abbr.)| MM (2 digits), M (1 or 2 digits)
+//              | NNN (abbr.)        |
+// Day of Month | dd (2 digits)      | d (1 or 2 digits)
+// Day of Week  | EE (name)          | E (abbr)
+// Hour (1-12)  | hh (2 digits)      | h (1 or 2 digits)
+// Hour (0-23)  | HH (2 digits)      | H (1 or 2 digits)
+// Hour (0-11)  | KK (2 digits)      | K (1 or 2 digits)
+// Hour (1-24)  | kk (2 digits)      | k (1 or 2 digits)
+// Minute       | mm (2 digits)      | m (1 or 2 digits)
+// Second       | ss (2 digits)      | s (1 or 2 digits)
+// AM/PM        | a                  |
+//
+// NOTE THE DIFFERENCE BETWEEN MM and mm! Month=MM, not mm!
+// Examples:
+//  "MMM d, y" matches: January 01, 2000
+//                      Dec 1, 1900
+//                      Nov 20, 00
+//  "M/d/yy"   matches: 01/20/00
+//                      9/2/00
+//  "MMM dd, yyyy hh:mm:ssa" matches: "January 01, 2000 12:30:45AM"
+// ------------------------------------------------------------------
+
+var MONTH_NAMES=new Array('January','February','March','April','May','June','July','August','September','October','November','December','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
+var DAY_NAMES=new Array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sun','Mon','Tue','Wed','Thu','Fri','Sat');
+function LZ(x) {return(x<0||x>9?"":"0")+x}
+
+// ------------------------------------------------------------------
+// isDate ( date_string, format_string )
+// Returns true if date string matches format of format string and
+// is a valid date. Else returns false.
+// It is recommended that you trim whitespace around the value before
+// passing it to this function, as whitespace is NOT ignored!
+// ------------------------------------------------------------------
+function isDate(val,format) {
+	var date=getDateFromFormat(val,format);
+	if (date==0) { return false; }
+	return true;
+	}
+
+// -------------------------------------------------------------------
+// compareDates(date1,date1format,date2,date2format)
+//   Compare two date strings to see which is greater.
+//   Returns:
+//   1 if date1 is greater than date2
+//   0 if date2 is greater than date1 of if they are the same
+//  -1 if either of the dates is in an invalid format
+// -------------------------------------------------------------------
+function compareDates(date1,dateformat1,date2,dateformat2) {
+	var d1=getDateFromFormat(date1,dateformat1);
+	var d2=getDateFromFormat(date2,dateformat2);
+	if (d1==0 || d2==0) {
+		return -1;
+		}
+	else if (d1 > d2) {
+		return 1;
+		}
+	return 0;
+	}
+
+// ------------------------------------------------------------------
+// formatDate (date_object, format)
+// Returns a date in the output format specified.
+// The format string uses the same abbreviations as in getDateFromFormat()
+// ------------------------------------------------------------------
+function formatDate(date,format) {
+	format=format+"";
+	var result="";
+	var i_format=0;
+	var c="";
+	var token="";
+	var y=date.getYear()+"";
+	var M=date.getMonth()+1;
+	var d=date.getDate();
+	var E=date.getDay();
+	var H=date.getHours();
+	var m=date.getMinutes();
+	var s=date.getSeconds();
+	var yyyy,yy,MMM,MM,dd,hh,h,mm,ss,ampm,HH,H,KK,K,kk,k;
+	// Convert real date parts into formatted versions
+	var value=new Object();
+	if (y.length < 4) {y=""+(y-0+1900);}
+	value["y"]=""+y;
+	value["yyyy"]=y;
+	value["yy"]=y.substring(2,4);
+	value["M"]=M;
+	value["MM"]=LZ(M);
+	value["MMM"]=MONTH_NAMES[M-1];
+	value["NNN"]=MONTH_NAMES[M+11];
+	value["d"]=d;
+	value["dd"]=LZ(d);
+	value["E"]=DAY_NAMES[E+7];
+	value["EE"]=DAY_NAMES[E];
+	value["H"]=H;
+	value["HH"]=LZ(H);
+	if (H==0){value["h"]=12;}
+	else if (H>12){value["h"]=H-12;}
+	else {value["h"]=H;}
+	value["hh"]=LZ(value["h"]);
+	if (H>11){value["K"]=H-12;} else {value["K"]=H;}
+	value["k"]=H+1;
+	value["KK"]=LZ(value["K"]);
+	value["kk"]=LZ(value["k"]);
+	if (H > 11) { value["a"]="PM"; }
+	else { value["a"]="AM"; }
+	value["m"]=m;
+	value["mm"]=LZ(m);
+	value["s"]=s;
+	value["ss"]=LZ(s);
+	while (i_format < format.length) {
+		c=format.charAt(i_format);
+		token="";
+		while ((format.charAt(i_format)==c) && (i_format < format.length)) {
+			token += format.charAt(i_format++);
+			}
+		if (value[token] != null) { result=result + value[token]; }
+		else { result=result + token; }
+		}
+	return result;
+	}
+	
+// ------------------------------------------------------------------
+// Utility functions for parsing in getDateFromFormat()
+// ------------------------------------------------------------------
+function _isInteger(val) {
+	var digits="1234567890";
+	for (var i=0; i < val.length; i++) {
+		if (digits.indexOf(val.charAt(i))==-1) { return false; }
+		}
+	return true;
+	}
+function _getInt(str,i,minlength,maxlength) {
+	for (var x=maxlength; x>=minlength; x--) {
+		var token=str.substring(i,i+x);
+		if (token.length < minlength) { return null; }
+		if (_isInteger(token)) { return token; }
+		}
+	return null;
+	}
+	
+// ------------------------------------------------------------------
+// getDateFromFormat( date_string , format_string )
+//
+// This function takes a date string and a format string. It matches
+// If the date string matches the format string, it returns the 
+// getTime() of the date. If it does not match, it returns 0.
+// ------------------------------------------------------------------
+function getDateFromFormat(val,format) {
+	val=val+"";
+	format=format+"";
+	var i_val=0;
+	var i_format=0;
+	var c="";
+	var token="";
+	var token2="";
+	var x,y;
+	var now=new Date();
+	var year=now.getYear();
+	var month=now.getMonth()+1;
+	var date=1;
+	var hh=now.getHours();
+	var mm=now.getMinutes();
+	var ss=now.getSeconds();
+	var ampm="";
+	
+	while (i_format < format.length) {
+		// Get next token from format string
+		c=format.charAt(i_format);
+		token="";
+		while ((format.charAt(i_format)==c) && (i_format < format.length)) {
+			token += format.charAt(i_format++);
+			}
+		// Extract contents of value based on format token
+		if (token=="yyyy" || token=="yy" || token=="y") {
+			if (token=="yyyy") { x=4;y=4; }
+			if (token=="yy")   { x=2;y=2; }
+			if (token=="y")    { x=2;y=4; }
+			year=_getInt(val,i_val,x,y);
+			if (year==null) { return 0; }
+			i_val += year.length;
+			if (year.length==2) {
+				if (year > 70) { year=1900+(year-0); }
+				else { year=2000+(year-0); }
+				}
+			}
+		else if (token=="MMM"||token=="NNN"){
+			month=0;
+			for (var i=0; i<MONTH_NAMES.length; i++) {
+				var month_name=MONTH_NAMES[i];
+				if (val.substring(i_val,i_val+month_name.length).toLowerCase()==month_name.toLowerCase()) {
+					if (token=="MMM"||(token=="NNN"&&i>11)) {
+						month=i+1;
+						if (month>12) { month -= 12; }
+						i_val += month_name.length;
+						break;
+						}
+					}
+				}
+			if ((month < 1)||(month>12)){return 0;}
+			}
+		else if (token=="EE"||token=="E"){
+			for (var i=0; i<DAY_NAMES.length; i++) {
+				var day_name=DAY_NAMES[i];
+				if (val.substring(i_val,i_val+day_name.length).toLowerCase()==day_name.toLowerCase()) {
+					i_val += day_name.length;
+					break;
+					}
+				}
+			}
+		else if (token=="MM"||token=="M") {
+			month=_getInt(val,i_val,token.length,2);
+			if(month==null||(month<1)||(month>12)){return 0;}
+			i_val+=month.length;}
+		else if (token=="dd"||token=="d") {
+			date=_getInt(val,i_val,token.length,2);
+			if(date==null||(date<1)||(date>31)){return 0;}
+			i_val+=date.length;}
+		else if (token=="hh"||token=="h") {
+			hh=_getInt(val,i_val,token.length,2);
+			if(hh==null||(hh<1)||(hh>12)){return 0;}
+			i_val+=hh.length;}
+		else if (token=="HH"||token=="H") {
+			hh=_getInt(val,i_val,token.length,2);
+			if(hh==null||(hh<0)||(hh>23)){return 0;}
+			i_val+=hh.length;}
+		else if (token=="KK"||token=="K") {
+			hh=_getInt(val,i_val,token.length,2);
+			if(hh==null||(hh<0)||(hh>11)){return 0;}
+			i_val+=hh.length;}
+		else if (token=="kk"||token=="k") {
+			hh=_getInt(val,i_val,token.length,2);
+			if(hh==null||(hh<1)||(hh>24)){return 0;}
+			i_val+=hh.length;hh--;}
+		else if (token=="mm"||token=="m") {
+			mm=_getInt(val,i_val,token.length,2);
+			if(mm==null||(mm<0)||(mm>59)){return 0;}
+			i_val+=mm.length;}
+		else if (token=="ss"||token=="s") {
+			ss=_getInt(val,i_val,token.length,2);
+			if(ss==null||(ss<0)||(ss>59)){return 0;}
+			i_val+=ss.length;}
+		else if (token=="a") {
+			if (val.substring(i_val,i_val+2).toLowerCase()=="am") {ampm="AM";}
+			else if (val.substring(i_val,i_val+2).toLowerCase()=="pm") {ampm="PM";}
+			else {return 0;}
+			i_val+=2;}
+		else {
+			if (val.substring(i_val,i_val+token.length)!=token) {return 0;}
+			else {i_val+=token.length;}
+			}
+		}
+	// If there are any trailing characters left in the value, it doesn't match
+	if (i_val != val.length) { return 0; }
+	// Is date valid for month?
+	if (month==2) {
+		// Check for leap year
+		if ( ( (year%4==0)&&(year%100 != 0) ) || (year%400==0) ) { // leap year
+			if (date > 29){ return 0; }
+			}
+		else { if (date > 28) { return 0; } }
+		}
+	if ((month==4)||(month==6)||(month==9)||(month==11)) {
+		if (date > 30) { return 0; }
+		}
+	// Correct hours value
+	if (hh<12 && ampm=="PM") { hh=hh-0+12; }
+	else if (hh>11 && ampm=="AM") { hh-=12; }
+	var newdate=new Date(year,month-1,date,hh,mm,ss);
+	return newdate.getTime();
+	}
+
+// ------------------------------------------------------------------
+// parseDate( date_string [, prefer_euro_format] )
+//
+// This function takes a date string and tries to match it to a
+// number of possible date formats to get the value. It will try to
+// match against the following international formats, in this order:
+// y-M-d   MMM d, y   MMM d,y   y-MMM-d   d-MMM-y  MMM d
+// M/d/y   M-d-y      M.d.y     MMM-d     M/d      M-d
+// d/M/y   d-M-y      d.M.y     d-MMM     d/M      d-M
+// A second argument may be passed to instruct the method to search
+// for formats like d/M/y (european format) before M/d/y (American).
+// Returns a Date object or null if no patterns match.
+// ------------------------------------------------------------------
+function parseDate(val) {
+	var preferEuro=(arguments.length==2)?arguments[1]:false;
+	generalFormats=new Array('y-M-d','MMM d, y','MMM d,y','y-MMM-d','d-MMM-y','MMM d');
+	monthFirst=new Array('M/d/y','M-d-y','M.d.y','MMM-d','M/d','M-d');
+	dateFirst =new Array('d/M/y','d-M-y','d.M.y','d-MMM','d/M','d-M');
+	var checkList=new Array('generalFormats',preferEuro?'dateFirst':'monthFirst',preferEuro?'monthFirst':'dateFirst');
+	var d=null;
+	for (var i=0; i<checkList.length; i++) {
+		var l=window[checkList[i]];
+		for (var j=0; j<l.length; j++) {
+			d=getDateFromFormat(val,l[j]);
+			if (d!=0) { return new Date(d); }
+			}
+		}
+	return null;
+	}
+
+/* SOURCE FILE: PopupWindow.js */
+
+/* 
+PopupWindow.js
+Author: Matt Kruse
+Last modified: 02/16/04
+
+DESCRIPTION: This object allows you to easily and quickly popup a window
+in a certain place. The window can either be a DIV or a separate browser
+window.
+
+COMPATABILITY: Works with Netscape 4.x, 6.x, IE 5.x on Windows. Some small
+positioning errors - usually with Window positioning - occur on the 
+Macintosh platform. Due to bugs in Netscape 4.x, populating the popup 
+window with <STYLE> tags may cause errors.
+
+USAGE:
+// Create an object for a WINDOW popup
+var win = new PopupWindow(); 
+
+// Create an object for a DIV window using the DIV named 'mydiv'
+var win = new PopupWindow('mydiv'); 
+
+// Set the window to automatically hide itself when the user clicks 
+// anywhere else on the page except the popup
+win.autoHide(); 
+
+// Show the window relative to the anchor name passed in
+win.showPopup(anchorname);
+
+// Hide the popup
+win.hidePopup();
+
+// Set the size of the popup window (only applies to WINDOW popups
+win.setSize(width,height);
+
+// Populate the contents of the popup window that will be shown. If you 
+// change the contents while it is displayed, you will need to refresh()
+win.populate(string);
+
+// set the URL of the window, rather than populating its contents
+// manually
+win.setUrl("http://www.site.com/");
+
+// Refresh the contents of the popup
+win.refresh();
+
+// Specify how many pixels to the right of the anchor the popup will appear
+win.offsetX = 50;
+
+// Specify how many pixels below the anchor the popup will appear
+win.offsetY = 100;
+
+NOTES:
+1) Requires the functions in AnchorPosition.js
+
+2) Your anchor tag MUST contain both NAME and ID attributes which are the 
+   same. For example:
+   <A NAME="test" ID="test"> </A>
+
+3) There must be at least a space between <A> </A> for IE5.5 to see the 
+   anchor tag correctly. Do not do <A></A> with no space.
+
+4) When a PopupWindow object is created, a handler for 'onmouseup' is
+   attached to any event handler you may have already defined. Do NOT define
+   an event handler for 'onmouseup' after you define a PopupWindow object or
+   the autoHide() will not work correctly.
+*/ 
+
+// Set the position of the popup window based on the anchor
+function PopupWindow_getXYPosition(anchorname) {
+	var coordinates;
+	if (this.type == "WINDOW") {
+		coordinates = getAnchorWindowPosition(anchorname);
+		}
+	else {
+		coordinates = getAnchorPosition(anchorname);
+		}
+	this.x = coordinates.x;
+	this.y = coordinates.y;
+	}
+// Set width/height of DIV/popup window
+function PopupWindow_setSize(width,height) {
+	this.width = width;
+	this.height = height;
+	}
+// Fill the window with contents
+function PopupWindow_populate(contents) {
+	this.contents = contents;
+	this.populated = false;
+	}
+// Set the URL to go to
+function PopupWindow_setUrl(url) {
+	this.url = url;
+	}
+// Set the window popup properties
+function PopupWindow_setWindowProperties(props) {
+	this.windowProperties = props;
+	}
+// Refresh the displayed contents of the popup
+function PopupWindow_refresh() {
+	if (this.divName != null) {
+		// refresh the DIV object
+		if (this.use_gebi) {
+			document.getElementById(this.divName).innerHTML = this.contents;
+			}
+		else if (this.use_css) { 
+			document.all[this.divName].innerHTML = this.contents;
+			}
+		else if (this.use_layers) { 
+			var d = document.layers[this.divName]; 
+			d.document.open();
+			d.document.writeln(this.contents);
+			d.document.close();
+			}
+		}
+	else {
+		if (this.popupWindow != null && !this.popupWindow.closed) {
+			if (this.url!="") {
+				this.popupWindow.location.href=this.url;
+				}
+			else {
+				this.popupWindow.document.open();
+				this.popupWindow.document.writeln(this.contents);
+				this.popupWindow.document.close();
+			}
+			this.popupWindow.focus();
+			}
+		}
+	}
+// Position and show the popup, relative to an anchor object
+function PopupWindow_showPopup(anchorname) {
+	this.getXYPosition(anchorname);
+	this.x += this.offsetX;
+	this.y += this.offsetY;
+	if (!this.populated && (this.contents != "")) {
+		this.populated = true;
+		this.refresh();
+		}
+	if (this.divName != null) {
+		// Show the DIV object
+		if (this.use_gebi) {
+			document.getElementById(this.divName).style.left = this.x + "px";
+			document.getElementById(this.divName).style.top = this.y + "px";
+			document.getElementById(this.divName).style.visibility = "visible";
+			}
+		else if (this.use_css) {
+			document.all[this.divName].style.left = this.x;
+			document.all[this.divName].style.top = this.y;
+			document.all[this.divName].style.visibility = "visible";
+			}
+		else if (this.use_layers) {
+			document.layers[this.divName].left = this.x;
+			document.layers[this.divName].top = this.y;
+			document.layers[this.divName].visibility = "visible";
+			}
+		}
+	else {
+		if (this.popupWindow == null || this.popupWindow.closed) {
+			// If the popup window will go off-screen, move it so it doesn't
+			if (this.x<0) { this.x=0; }
+			if (this.y<0) { this.y=0; }
+			if (screen && screen.availHeight) {
+				if ((this.y + this.height) > screen.availHeight) {
+					this.y = screen.availHeight - this.height;
+					}
+				}
+			if (screen && screen.availWidth) {
+				if ((this.x + this.width) > screen.availWidth) {
+					this.x = screen.availWidth - this.width;
+					}
+				}
+			var avoidAboutBlank = window.opera || ( document.layers && !navigator.mimeTypes['*'] ) || navigator.vendor == 'KDE' || ( document.childNodes && !document.all && !navigator.taintEnabled );
+			this.popupWindow = window.open(avoidAboutBlank?"":"about:blank","window_"+anchorname,this.windowProperties+",width="+this.width+",height="+this.height+",screenX="+this.x+",left="+this.x+",screenY="+this.y+",top="+this.y+"");
+			}
+		this.refresh();
+		}
+	}
+// Hide the popup
+function PopupWindow_hidePopup() {
+	if (this.divName != null) {
+		if (this.use_gebi) {
+			document.getElementById(this.divName).style.visibility = "hidden";
+			}
+		else if (this.use_css) {
+			document.all[this.divName].style.visibility = "hidden";
+			}
+		else if (this.use_layers) {
+			document.layers[this.divName].visibility = "hidden";
+			}
+		}
+	else {
+		if (this.popupWindow && !this.popupWindow.closed) {
+			this.popupWindow.close();
+			this.popupWindow = null;
+			}
+		}
+	}
+// Pass an event and return whether or not it was the popup DIV that was clicked
+function PopupWindow_isClicked(e) {
+	if (this.divName != null) {
+		if (this.use_layers) {
+			var clickX = e.pageX;
+			var clickY = e.pageY;
+			var t = document.layers[this.divName];
+			if ((clickX > t.left) && (clickX < t.left+t.clip.width) && (clickY > t.top) && (clickY < t.top+t.clip.height)) {
+				return true;
+				}
+			else { return false; }
+			}
+		else if (document.all) { // Need to hard-code this to trap IE for error-handling
+			var t = window.event.srcElement;
+			while (t.parentElement != null) {
+				if (t.id==this.divName) {
+					return true;
+					}
+				t = t.parentElement;
+				}
+			return false;
+			}
+		else if (this.use_gebi && e) {
+			var t = e.originalTarget;
+			while (t.parentNode != null) {
+				if (t.id==this.divName) {
+					return true;
+					}
+				t = t.parentNode;
+				}
+			return false;
+			}
+		return false;
+		}
+	return false;
+	}
+
+// Check an onMouseDown event to see if we should hide
+function PopupWindow_hideIfNotClicked(e) {
+	if (this.autoHideEnabled && !this.isClicked(e)) {
+		this.hidePopup();
+		}
+	}
+// Call this to make the DIV disable automatically when mouse is clicked outside it
+function PopupWindow_autoHide() {
+	this.autoHideEnabled = true;
+	}
+// This global function checks all PopupWindow objects onmouseup to see if they should be hidden
+function PopupWindow_hidePopupWindows(e) {
+	for (var i=0; i<popupWindowObjects.length; i++) {
+		if (popupWindowObjects[i] != null) {
+			var p = popupWindowObjects[i];
+			p.hideIfNotClicked(e);
+			}
+		}
+	}
+// Run this immediately to attach the event listener
+function PopupWindow_attachListener() {
+	if (document.layers) {
+		document.captureEvents(Event.MOUSEUP);
+		}
+	window.popupWindowOldEventListener = document.onmouseup;
+	if (window.popupWindowOldEventListener != null) {
+		document.onmouseup = new Function("window.popupWindowOldEventListener(); PopupWindow_hidePopupWindows();");
+		}
+	else {
+		document.onmouseup = PopupWindow_hidePopupWindows;
+		}
+	}
+// CONSTRUCTOR for the PopupWindow object
+// Pass it a DIV name to use a DHTML popup, otherwise will default to window popup
+function PopupWindow() {
+	if (!window.popupWindowIndex) { window.popupWindowIndex = 0; }
+	if (!window.popupWindowObjects) { window.popupWindowObjects = new Array(); }
+	if (!window.listenerAttached) {
+		window.listenerAttached = true;
+		PopupWindow_attachListener();
+		}
+	this.index = popupWindowIndex++;
+	popupWindowObjects[this.index] = this;
+	this.divName = null;
+	this.popupWindow = null;
+	this.width=0;
+	this.height=0;
+	this.populated = false;
+	this.visible = false;
+	this.autoHideEnabled = false;
+	
+	this.contents = "";
+	this.url="";
+	this.windowProperties="toolbar=no,location=no,status=no,menubar=no,scrollbars=auto,resizable,alwaysRaised,dependent,titlebar=no";
+	if (arguments.length>0) {
+		this.type="DIV";
+		this.divName = arguments[0];
+		}
+	else {
+		this.type="WINDOW";
+		}
+	this.use_gebi = false;
+	this.use_css = false;
+	this.use_layers = false;
+	if (document.getElementById) { this.use_gebi = true; }
+	else if (document.all) { this.use_css = true; }
+	else if (document.layers) { this.use_layers = true; }
+	else { this.type = "WINDOW"; }
+	this.offsetX = 0;
+	this.offsetY = 0;
+	// Method mappings
+	this.getXYPosition = PopupWindow_getXYPosition;
+	this.populate = PopupWindow_populate;
+	this.setUrl = PopupWindow_setUrl;
+	this.setWindowProperties = PopupWindow_setWindowProperties;
+	this.refresh = PopupWindow_refresh;
+	this.showPopup = PopupWindow_showPopup;
+	this.hidePopup = PopupWindow_hidePopup;
+	this.setSize = PopupWindow_setSize;
+	this.isClicked = PopupWindow_isClicked;
+	this.autoHide = PopupWindow_autoHide;
+	this.hideIfNotClicked = PopupWindow_hideIfNotClicked;
+	}
+
+/* SOURCE FILE: CalendarPopup.js */
+
+// HISTORY
+// ------------------------------------------------------------------
+// Feb 7, 2005: Fixed a CSS styles to use px unit
+// March 29, 2004: Added check in select() method for the form field
+//      being disabled. If it is, just return and don't do anything.
+// March 24, 2004: Fixed bug - when month name and abbreviations were
+//      changed, date format still used original values.
+// January 26, 2004: Added support for drop-down month and year
+//      navigation (Thanks to Chris Reid for the idea)
+// September 22, 2003: Fixed a minor problem in YEAR calendar with
+//      CSS prefix.
+// August 19, 2003: Renamed the function to get styles, and made it
+//      work correctly without an object reference
+// August 18, 2003: Changed showYearNavigation and 
+//      showYearNavigationInput to optionally take an argument of
+//      true or false
+// July 31, 2003: Added text input option for year navigation.
+//      Added a per-calendar CSS prefix option to optionally use 
+//      different styles for different calendars.
+// July 29, 2003: Fixed bug causing the Today link to be clickable 
+//      even though today falls in a disabled date range.
+//      Changed formatting to use pure CSS, allowing greater control
+//      over look-and-feel options.
+// June 11, 2003: Fixed bug causing the Today link to be unselectable
+//      under certain cases when some days of week are disabled
+// March 14, 2003: Added ability to disable individual dates or date
+//      ranges, display as light gray and strike-through
+// March 14, 2003: Removed dependency on graypixel.gif and instead 
+///     use table border coloring
+// March 12, 2003: Modified showCalendar() function to allow optional
+//      start-date parameter
+// March 11, 2003: Modified select() function to allow optional
+//      start-date parameter
+/* 
+DESCRIPTION: This object implements a popup calendar to allow the user to
+select a date, month, quarter, or year.
+
+COMPATABILITY: Works with Netscape 4.x, 6.x, IE 5.x on Windows. Some small
+positioning errors - usually with Window positioning - occur on the 
+Macintosh platform.
+The calendar can be modified to work for any location in the world by 
+changing which weekday is displayed as the first column, changing the month
+names, and changing the column headers for each day.
+
+USAGE:
+// Create a new CalendarPopup object of type WINDOW
+var cal = new CalendarPopup(); 
+
+// Create a new CalendarPopup object of type DIV using the DIV named 'mydiv'
+var cal = new CalendarPopup('mydiv'); 
+
+// Easy method to link the popup calendar with an input box. 
+cal.select(inputObject, anchorname, dateFormat);
+// Same method, but passing a default date other than the field's current value
+cal.select(inputObject, anchorname, dateFormat, '01/02/2000');
+// This is an example call to the popup calendar from a link to populate an 
+// input box. Note that to use this, date.js must also be included!!
+<A HREF="#" onClick="cal.select(document.forms[0].date,'anchorname','MM/dd/yyyy'); return false;">Select</A>
+
+// Set the type of date select to be used. By default it is 'date'.
+cal.setDisplayType(type);
+
+// When a date, month, quarter, or year is clicked, a function is called and
+// passed the details. You must write this function, and tell the calendar
+// popup what the function name is.
+// Function to be called for 'date' select receives y, m, d
+cal.setReturnFunction(functionname);
+// Function to be called for 'month' select receives y, m
+cal.setReturnMonthFunction(functionname);
+// Function to be called for 'quarter' select receives y, q
+cal.setReturnQuarterFunction(functionname);
+// Function to be called for 'year' select receives y
+cal.setReturnYearFunction(functionname);
+
+// Show the calendar relative to a given anchor
+cal.showCalendar(anchorname);
+
+// Hide the calendar. The calendar is set to autoHide automatically
+cal.hideCalendar();
+
+// Set the month names to be used. Default are English month names
+cal.setMonthNames("January","February","March",...);
+
+// Set the month abbreviations to be used. Default are English month abbreviations
+cal.setMonthAbbreviations("Jan","Feb","Mar",...);
+
+// Show navigation for changing by the year, not just one month at a time
+cal.showYearNavigation();
+
+// Show month and year dropdowns, for quicker selection of month of dates
+cal.showNavigationDropdowns();
+
+// Set the text to be used above each day column. The days start with 
+// sunday regardless of the value of WeekStartDay
+cal.setDayHeaders("S","M","T",...);
+
+// Set the day for the first column in the calendar grid. By default this
+// is Sunday (0) but it may be changed to fit the conventions of other
+// countries.
+cal.setWeekStartDay(1); // week is Monday - Sunday
+
+// Set the weekdays which should be disabled in the 'date' select popup. You can
+// then allow someone to only select week end dates, or Tuedays, for example
+cal.setDisabledWeekDays(0,1); // To disable selecting the 1st or 2nd days of the week
+
+// Selectively disable individual days or date ranges. Disabled days will not
+// be clickable, and show as strike-through text on current browsers.
+// Date format is any format recognized by parseDate() in date.js
+// Pass a single date to disable:
+cal.addDisabledDates("2003-01-01");
+// Pass null as the first parameter to mean "anything up to and including" the
+// passed date:
+cal.addDisabledDates(null, "01/02/03");
+// Pass null as the second parameter to mean "including the passed date and
+// anything after it:
+cal.addDisabledDates("Jan 01, 2003", null);
+// Pass two dates to disable all dates inbetween and including the two
+cal.addDisabledDates("January 01, 2003", "Dec 31, 2003");
+
+// When the 'year' select is displayed, set the number of years back from the 
+// current year to start listing years. Default is 2.
+// This is also used for year drop-down, to decide how many years +/- to display
+cal.setYearSelectStartOffset(2);
+
+// Text for the word "Today" appearing on the calendar
+cal.setTodayText("Today");
+
+// The calendar uses CSS classes for formatting. If you want your calendar to
+// have unique styles, you can set the prefix that will be added to all the
+// classes in the output.
+// For example, normal output may have this:
+//     <SPAN CLASS="cpTodayTextDisabled">Today<SPAN>
+// But if you set the prefix like this:
+cal.setCssPrefix("Test");
+// The output will then look like:
+//     <SPAN CLASS="TestcpTodayTextDisabled">Today<SPAN>
+// And you can define that style somewhere in your page.
+
+// When using Year navigation, you can make the year be an input box, so
+// the user can manually change it and jump to any year
+cal.showYearNavigationInput();
+
+// Set the calendar offset to be different than the default. By default it
+// will appear just below and to the right of the anchorname. So if you have
+// a text box where the date will go and and anchor immediately after the
+// text box, the calendar will display immediately under the text box.
+cal.offsetX = 20;
+cal.offsetY = 20;
+
+NOTES:
+1) Requires the functions in AnchorPosition.js and PopupWindow.js
+
+2) Your anchor tag MUST contain both NAME and ID attributes which are the 
+   same. For example:
+   <A NAME="test" ID="test"> </A>
+
+3) There must be at least a space between <A> </A> for IE5.5 to see the 
+   anchor tag correctly. Do not do <A></A> with no space.
+
+4) When a CalendarPopup object is created, a handler for 'onmouseup' is
+   attached to any event handler you may have already defined. Do NOT define
+   an event handler for 'onmouseup' after you define a CalendarPopup object 
+   or the autoHide() will not work correctly.
+   
+5) The calendar popup display uses style sheets to make it look nice.
+
+*/ 
+
+// Quick fix for FF3
+function CP_stop(e) { if (e && e.stopPropagation) { e.stopPropagation(); } }
+
+// CONSTRUCTOR for the CalendarPopup Object
+function CalendarPopup() {
+	var c;
+	if (arguments.length>0) {
+		c = new PopupWindow(arguments[0]);
+		}
+	else {
+		c = new PopupWindow();
+		c.setSize(150,175);
+		}
+	c.offsetX = -152;
+	c.offsetY = 25;
+	c.autoHide();
+	// Calendar-specific properties
+	c.monthNames = new Array("January","February","March","April","May","June","July","August","September","October","November","December");
+	c.monthAbbreviations = new Array("Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec");
+	c.dayHeaders = new Array("S","M","T","W","T","F","S");
+	c.returnFunction = "CP_tmpReturnFunction";
+	c.returnMonthFunction = "CP_tmpReturnMonthFunction";
+	c.returnQuarterFunction = "CP_tmpReturnQuarterFunction";
+	c.returnYearFunction = "CP_tmpReturnYearFunction";
+	c.weekStartDay = 0;
+	c.isShowYearNavigation = false;
+	c.displayType = "date";
+	c.disabledWeekDays = new Object();
+	c.disabledDatesExpression = "";
+	c.yearSelectStartOffset = 2;
+	c.currentDate = null;
+	c.todayText="Today";
+	c.cssPrefix="";
+	c.isShowNavigationDropdowns=false;
+	c.isShowYearNavigationInput=false;
+	window.CP_calendarObject = null;
+	window.CP_targetInput = null;
+	window.CP_dateFormat = "MM/dd/yyyy";
+	// Method mappings
+	c.copyMonthNamesToWindow = CP_copyMonthNamesToWindow;
+	c.setReturnFunction = CP_setReturnFunction;
+	c.setReturnMonthFunction = CP_setReturnMonthFunction;
+	c.setReturnQuarterFunction = CP_setReturnQuarterFunction;
+	c.setReturnYearFunction = CP_setReturnYearFunction;
+	c.setMonthNames = CP_setMonthNames;
+	c.setMonthAbbreviations = CP_setMonthAbbreviations;
+	c.setDayHeaders = CP_setDayHeaders;
+	c.setWeekStartDay = CP_setWeekStartDay;
+	c.setDisplayType = CP_setDisplayType;
+	c.setDisabledWeekDays = CP_setDisabledWeekDays;
+	c.addDisabledDates = CP_addDisabledDates;
+	c.setYearSelectStartOffset = CP_setYearSelectStartOffset;
+	c.setTodayText = CP_setTodayText;
+	c.showYearNavigation = CP_showYearNavigation;
+	c.showCalendar = CP_showCalendar;
+	c.hideCalendar = CP_hideCalendar;
+	c.getStyles = getCalendarStyles;
+	c.refreshCalendar = CP_refreshCalendar;
+	c.getCalendar = CP_getCalendar;
+	c.select = CP_select;
+	c.setCssPrefix = CP_setCssPrefix;
+	c.showNavigationDropdowns = CP_showNavigationDropdowns;
+	c.showYearNavigationInput = CP_showYearNavigationInput;
+	c.copyMonthNamesToWindow();
+	// Return the object
+	return c;
+	}
+function CP_copyMonthNamesToWindow() {
+	// Copy these values over to the date.js 
+	if (typeof(window.MONTH_NAMES)!="undefined" && window.MONTH_NAMES!=null) {
+		window.MONTH_NAMES = new Array();
+		for (var i=0; i<this.monthNames.length; i++) {
+			window.MONTH_NAMES[window.MONTH_NAMES.length] = this.monthNames[i];
+		}
+		for (var i=0; i<this.monthAbbreviations.length; i++) {
+			window.MONTH_NAMES[window.MONTH_NAMES.length] = this.monthAbbreviations[i];
+		}
+	}
+}
+// Temporary default functions to be called when items clicked, so no error is thrown
+function CP_tmpReturnFunction(y,m,d) { 
+	if (window.CP_targetInput!=null) {
+		var dt = new Date(y,m-1,d,0,0,0);
+		if (window.CP_calendarObject!=null) { window.CP_calendarObject.copyMonthNamesToWindow(); }
+		window.CP_targetInput.value = formatDate(dt,window.CP_dateFormat);
+		}
+	else {
+		alert('Use setReturnFunction() to define which function will get the clicked results!'); 
+		}
+	}
+function CP_tmpReturnMonthFunction(y,m) { 
+	alert('Use setReturnMonthFunction() to define which function will get the clicked results!\nYou clicked: year='+y+' , month='+m); 
+	}
+function CP_tmpReturnQuarterFunction(y,q) { 
+	alert('Use setReturnQuarterFunction() to define which function will get the clicked results!\nYou clicked: year='+y+' , quarter='+q); 
+	}
+function CP_tmpReturnYearFunction(y) { 
+	alert('Use setReturnYearFunction() to define which function will get the clicked results!\nYou clicked: year='+y); 
+	}
+
+// Set the name of the functions to call to get the clicked item
+function CP_setReturnFunction(name) { this.returnFunction = name; }
+function CP_setReturnMonthFunction(name) { this.returnMonthFunction = name; }
+function CP_setReturnQuarterFunction(name) { this.returnQuarterFunction = name; }
+function CP_setReturnYearFunction(name) { this.returnYearFunction = name; }
+
+// Over-ride the built-in month names
+function CP_setMonthNames() {
+	for (var i=0; i<arguments.length; i++) { this.monthNames[i] = arguments[i]; }
+	this.copyMonthNamesToWindow();
+	}
+
+// Over-ride the built-in month abbreviations
+function CP_setMonthAbbreviations() {
+	for (var i=0; i<arguments.length; i++) { this.monthAbbreviations[i] = arguments[i]; }
+	this.copyMonthNamesToWindow();
+	}
+
+// Over-ride the built-in column headers for each day
+function CP_setDayHeaders() {
+	for (var i=0; i<arguments.length; i++) { this.dayHeaders[i] = arguments[i]; }
+	}
+
+// Set the day of the week (0-7) that the calendar display starts on
+// This is for countries other than the US whose calendar displays start on Monday(1), for example
+function CP_setWeekStartDay(day) { this.weekStartDay = day; }
+
+// Show next/last year navigation links
+function CP_showYearNavigation() { this.isShowYearNavigation = (arguments.length>0)?arguments[0]:true; }
+
+// Which type of calendar to display
+function CP_setDisplayType(type) {
+	if (type!="date"&&type!="week-end"&&type!="month"&&type!="quarter"&&type!="year") { alert("Invalid display type! Must be one of: date,week-end,month,quarter,year"); return false; }
+	this.displayType=type;
+	}
+
+// How many years back to start by default for year display
+function CP_setYearSelectStartOffset(num) { this.yearSelectStartOffset=num; }
+
+// Set which weekdays should not be clickable
+function CP_setDisabledWeekDays() {
+	this.disabledWeekDays = new Object();
+	for (var i=0; i<arguments.length; i++) { this.disabledWeekDays[arguments[i]] = true; }
+	}
+	
+// Disable individual dates or ranges
+// Builds an internal logical test which is run via eval() for efficiency
+function CP_addDisabledDates(start, end) {
+	if (arguments.length==1) { end=start; }
+	if (start==null && end==null) { return; }
+	if (this.disabledDatesExpression!="") { this.disabledDatesExpression+= "||"; }
+	if (start!=null) { start = parseDate(start); start=""+start.getFullYear()+LZ(start.getMonth()+1)+LZ(start.getDate());}
+	if (end!=null) { end=parseDate(end); end=""+end.getFullYear()+LZ(end.getMonth()+1)+LZ(end.getDate());}
+	if (start==null) { this.disabledDatesExpression+="(ds<="+end+")"; }
+	else if (end  ==null) { this.disabledDatesExpression+="(ds>="+start+")"; }
+	else { this.disabledDatesExpression+="(ds>="+start+"&&ds<="+end+")"; }
+	}
+	
+// Set the text to use for the "Today" link
+function CP_setTodayText(text) {
+	this.todayText = text;
+	}
+
+// Set the prefix to be added to all CSS classes when writing output
+function CP_setCssPrefix(val) { 
+	this.cssPrefix = val; 
+	}
+
+// Show the navigation as an dropdowns that can be manually changed
+function CP_showNavigationDropdowns() { this.isShowNavigationDropdowns = (arguments.length>0)?arguments[0]:true; }
+
+// Show the year navigation as an input box that can be manually changed
+function CP_showYearNavigationInput() { this.isShowYearNavigationInput = (arguments.length>0)?arguments[0]:true; }
+
+// Hide a calendar object
+function CP_hideCalendar() {
+	if (arguments.length > 0) { window.popupWindowObjects[arguments[0]].hidePopup(); }
+	else { this.hidePopup(); }
+	}
+
+// Refresh the contents of the calendar display
+function CP_refreshCalendar(index) {
+	var calObject = window.popupWindowObjects[index];
+	if (arguments.length>1) { 
+		calObject.populate(calObject.getCalendar(arguments[1],arguments[2],arguments[3],arguments[4],arguments[5]));
+		}
+	else {
+		calObject.populate(calObject.getCalendar());
+		}
+	calObject.refresh();
+	}
+
+// Populate the calendar and display it
+function CP_showCalendar(anchorname) {
+	if (arguments.length>1) {
+		if (arguments[1]==null||arguments[1]=="") {
+			this.currentDate=new Date();
+			}
+		else {
+			this.currentDate=new Date(parseDate(arguments[1]));
+			}
+		}
+	this.populate(this.getCalendar());
+	this.showPopup(anchorname);
+	}
+
+// Simple method to interface popup calendar with a text-entry box
+function CP_select(inputobj, linkname, format) {
+	var selectedDate=(arguments.length>3)?arguments[3]:null;
+	if (!window.getDateFromFormat) {
+		alert("calendar.select: To use this method you must also include 'date.js' for date formatting");
+		return;
+		}
+	if (this.displayType!="date"&&this.displayType!="week-end") {
+		alert("calendar.select: This function can only be used with displayType 'date' or 'week-end'");
+		return;
+		}
+	if (inputobj.type!="text" && inputobj.type!="hidden" && inputobj.type!="textarea") { 
+		alert("calendar.select: Input object passed is not a valid form input object"); 
+		window.CP_targetInput=null;
+		return;
+		}
+	if (inputobj.disabled) { return; } // Can't use calendar input on disabled form input!
+	window.CP_targetInput = inputobj;
+	window.CP_calendarObject = this;
+	this.currentDate=null;
+	var time=0;
+	if (selectedDate!=null) {
+		time = getDateFromFormat(selectedDate,format)
+		}
+	else if (inputobj.value!="") {
+		time = getDateFromFormat(inputobj.value,format);
+		}
+	if (selectedDate!=null || inputobj.value!="") {
+		if (time==0) { this.currentDate=null; }
+		else { this.currentDate=new Date(time); }
+		}
+	window.CP_dateFormat = format;
+	this.showCalendar(linkname);
+	}
+	
+// Get style block needed to display the calendar correctly
+function getCalendarStyles() {
+	var result = "";
+	var p = "";
+	if (this!=null && typeof(this.cssPrefix)!="undefined" && this.cssPrefix!=null && this.cssPrefix!="") { p=this.cssPrefix; }
+	result += "<STYLE>\n";
+	result += "."+p+"cpYearNavigation,."+p+"cpMonthNavigation { background-color:#C0C0C0; text-align:center; vertical-align:center; text-decoration:none; color:#000000; font-weight:bold; }\n";
+	result += "."+p+"cpDayColumnHeader, ."+p+"cpYearNavigation,."+p+"cpMonthNavigation,."+p+"cpCurrentMonthDate,."+p+"cpCurrentMonthDateDisabled,."+p+"cpOtherMonthDate,."+p+"cpOtherMonthDateDisabled,."+p+"cpCurrentDate,."+p+"cpCurrentDateDisabled,."+p+"cpTodayText,."+p+"cpTodayTextDisabled,."+p+"cpText { font-family:arial; font-size:8pt; }\n";
+	result += "TD."+p+"cpDayColumnHeader { text-align:right; border:solid thin #C0C0C0;border-width:0px 0px 1px 0px; }\n";
+	result += "."+p+"cpCurrentMonthDate, ."+p+"cpOtherMonthDate, ."+p+"cpCurrentDate  { text-align:right; text-decoration:none; }\n";
+	result += "."+p+"cpCurrentMonthDateDisabled, ."+p+"cpOtherMonthDateDisabled, ."+p+"cpCurrentDateDisabled { color:#D0D0D0; text-align:right; text-decoration:line-through; }\n";
+	result += "."+p+"cpCurrentMonthDate, .cpCurrentDate { color:#000000; }\n";
+	result += "."+p+"cpOtherMonthDate { color:#808080; }\n";
+	result += "TD."+p+"cpCurrentDate { color:white; background-color: #C0C0C0; border-width:1px; border:solid thin #800000; }\n";
+	result += "TD."+p+"cpCurrentDateDisabled { border-width:1px; border:solid thin #FFAAAA; }\n";
+	result += "TD."+p+"cpTodayText, TD."+p+"cpTodayTextDisabled { border:solid thin #C0C0C0; border-width:1px 0px 0px 0px;}\n";
+	result += "A."+p+"cpTodayText, SPAN."+p+"cpTodayTextDisabled { height:20px; }\n";
+	result += "A."+p+"cpTodayText { color:black; }\n";
+	result += "."+p+"cpTodayTextDisabled { color:#D0D0D0; }\n";
+	result += "."+p+"cpBorder { border:solid thin #808080; }\n";
+	result += "</STYLE>\n";
+	return result;
+	}
+
+// Return a string containing all the calendar code to be displayed
+function CP_getCalendar() {
+	var now = new Date();
+	// Reference to window
+	if (this.type == "WINDOW") { var windowref = "window.opener."; }
+	else { var windowref = ""; }
+	var result = "";
+	// If POPUP, write entire HTML document
+	if (this.type == "WINDOW") {
+		result += "<HTML><HEAD><TITLE>Calendar</TITLE>"+this.getStyles()+"</HEAD><BODY MARGINWIDTH=0 MARGINHEIGHT=0 TOPMARGIN=0 RIGHTMARGIN=0 LEFTMARGIN=0>\n";
+		result += '<CENTER><TABLE WIDTH=100% BORDER=0 BORDERWIDTH=0 CELLSPACING=0 CELLPADDING=0>\n';
+		}
+	else {
+		result += '<TABLE CLASS="'+this.cssPrefix+'cpBorder" WIDTH=144 BORDER=1 BORDERWIDTH=1 CELLSPACING=0 CELLPADDING=1>\n';
+		result += '<TR><TD ALIGN=CENTER>\n';
+		result += '<CENTER>\n';
+		}
+	// Code for DATE display (default)
+	// -------------------------------
+	if (this.displayType=="date" || this.displayType=="week-end") {
+		if (this.currentDate==null) { this.currentDate = now; }
+		if (arguments.length > 0) { var month = arguments[0]; }
+			else { var month = this.currentDate.getMonth()+1; }
+		if (arguments.length > 1 && arguments[1]>0 && arguments[1]-0==arguments[1]) { var year = arguments[1]; }
+			else { var year = this.currentDate.getFullYear(); }
+		var daysinmonth= new Array(0,31,28,31,30,31,30,31,31,30,31,30,31);
+		if ( ( (year%4 == 0)&&(year%100 != 0) ) || (year%400 == 0) ) {
+			daysinmonth[2] = 29;
+			}
+		var current_month = new Date(year,month-1,1);
+		var display_year = year;
+		var display_month = month;
+		var display_date = 1;
+		var weekday= current_month.getDay();
+		var offset = 0;
+		
+		offset = (weekday >= this.weekStartDay) ? weekday-this.weekStartDay : 7-this.weekStartDay+weekday ;
+		if (offset > 0) {
+			display_month--;
+			if (display_month < 1) { display_month = 12; display_year--; }
+			display_date = daysinmonth[display_month]-offset+1;
+			}
+		var next_month = month+1;
+		var next_month_year = year;
+		if (next_month > 12) { next_month=1; next_month_year++; }
+		var last_month = month-1;
+		var last_month_year = year;
+		if (last_month < 1) { last_month=12; last_month_year--; }
+		var date_class;
+		if (this.type!="WINDOW") {
+			result += "<TABLE WIDTH=144 BORDER=0 BORDERWIDTH=0 CELLSPACING=0 CELLPADDING=0>";
+			}
+		result += '<TR>\n';
+		var refresh = windowref+'CP_refreshCalendar';
+		var refreshLink = 'javascript:' + refresh;
+		if (this.isShowNavigationDropdowns) {
+			result += '<TD CLASS="'+this.cssPrefix+'cpMonthNavigation" WIDTH="78" COLSPAN="3"><select CLASS="'+this.cssPrefix+'cpMonthNavigation" name="cpMonth" onmouseup="CP_stop(event)" onChange="'+refresh+'('+this.index+',this.options[this.selectedIndex].value-0,'+(year-0)+');">';
+			for( var monthCounter=1; monthCounter<=12; monthCounter++ ) {
+				var selected = (monthCounter==month) ? 'SELECTED' : '';
+				result += '<option value="'+monthCounter+'" '+selected+'>'+this.monthNames[monthCounter-1]+'</option>';
+				}
+			result += '</select></TD>';
+			result += '<TD CLASS="'+this.cssPrefix+'cpMonthNavigation" WIDTH="10">&nbsp;</TD>';
+
+			result += '<TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="56" COLSPAN="3"><select CLASS="'+this.cssPrefix+'cpYearNavigation" name="cpYear" onmouseup="CP_stop(event)" onChange="'+refresh+'('+this.index+','+month+',this.options[this.selectedIndex].value-0);">';
+			for( var yearCounter=year-this.yearSelectStartOffset; yearCounter<=year+this.yearSelectStartOffset; yearCounter++ ) {
+				var selected = (yearCounter==year) ? 'SELECTED' : '';
+				result += '<option value="'+yearCounter+'" '+selected+'>'+yearCounter+'</option>';
+				}
+			result += '</select></TD>';
+			}
+		else {
+			if (this.isShowYearNavigation) {
+				result += '<TD CLASS="'+this.cssPrefix+'cpMonthNavigation" WIDTH="10"><A CLASS="'+this.cssPrefix+'cpMonthNavigation" HREF="'+refreshLink+'('+this.index+','+last_month+','+last_month_year+');">&lt;</A></TD>';
+				result += '<TD CLASS="'+this.cssPrefix+'cpMonthNavigation" WIDTH="58"><SPAN CLASS="'+this.cssPrefix+'cpMonthNavigation">'+this.monthNames[month-1]+'</SPAN></TD>';
+				result += '<TD CLASS="'+this.cssPrefix+'cpMonthNavigation" WIDTH="10"><A CLASS="'+this.cssPrefix+'cpMonthNavigation" HREF="'+refreshLink+'('+this.index+','+next_month+','+next_month_year+');">&gt;</A></TD>';
+				result += '<TD CLASS="'+this.cssPrefix+'cpMonthNavigation" WIDTH="10">&nbsp;</TD>';
+
+				result += '<TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="10"><A CLASS="'+this.cssPrefix+'cpYearNavigation" HREF="'+refreshLink+'('+this.index+','+month+','+(year-1)+');">&lt;</A></TD>';
+				if (this.isShowYearNavigationInput) {
+					result += '<TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="36"><INPUT NAME="cpYear" CLASS="'+this.cssPrefix+'cpYearNavigation" SIZE="4" MAXLENGTH="4" VALUE="'+year+'" onBlur="'+refresh+'('+this.index+','+month+',this.value-0);"></TD>';
+					}
+				else {
+					result += '<TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="36"><SPAN CLASS="'+this.cssPrefix+'cpYearNavigation">'+year+'</SPAN></TD>';
+					}
+				result += '<TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="10"><A CLASS="'+this.cssPrefix+'cpYearNavigation" HREF="'+refreshLink+'('+this.index+','+month+','+(year+1)+');">&gt;</A></TD>';
+				}
+			else {
+				result += '<TD CLASS="'+this.cssPrefix+'cpMonthNavigation" WIDTH="22"><A CLASS="'+this.cssPrefix+'cpMonthNavigation" HREF="'+refreshLink+'('+this.index+','+last_month+','+last_month_year+');">&lt;&lt;</A></TD>\n';
+				result += '<TD CLASS="'+this.cssPrefix+'cpMonthNavigation" WIDTH="100"><SPAN CLASS="'+this.cssPrefix+'cpMonthNavigation">'+this.monthNames[month-1]+' '+year+'</SPAN></TD>\n';
+				result += '<TD CLASS="'+this.cssPrefix+'cpMonthNavigation" WIDTH="22"><A CLASS="'+this.cssPrefix+'cpMonthNavigation" HREF="'+refreshLink+'('+this.index+','+next_month+','+next_month_year+');">&gt;&gt;</A></TD>\n';
+				}
+			}
+		result += '</TR></TABLE>\n';
+		result += '<TABLE WIDTH=120 BORDER=0 CELLSPACING=0 CELLPADDING=1 ALIGN=CENTER>\n';
+		result += '<TR>\n';
+		for (var j=0; j<7; j++) {
+
+			result += '<TD CLASS="'+this.cssPrefix+'cpDayColumnHeader" WIDTH="14%"><SPAN CLASS="'+this.cssPrefix+'cpDayColumnHeader">'+this.dayHeaders[(this.weekStartDay+j)%7]+'</TD>\n';
+			}
+		result += '</TR>\n';
+		for (var row=1; row<=6; row++) {
+			result += '<TR>\n';
+			for (var col=1; col<=7; col++) {
+				var disabled=false;
+				if (this.disabledDatesExpression!="") {
+					var ds=""+display_year+LZ(display_month)+LZ(display_date);
+					eval("disabled=("+this.disabledDatesExpression+")");
+					}
+				var dateClass = "";
+				if ((display_month == this.currentDate.getMonth()+1) && (display_date==this.currentDate.getDate()) && (display_year==this.currentDate.getFullYear())) {
+					dateClass = "cpCurrentDate";
+					}
+				else if (display_month == month) {
+					dateClass = "cpCurrentMonthDate";
+					}
+				else {
+					dateClass = "cpOtherMonthDate";
+					}
+				if (disabled || this.disabledWeekDays[col-1]) {
+					result += '	<TD CLASS="'+this.cssPrefix+dateClass+'"><SPAN CLASS="'+this.cssPrefix+dateClass+'Disabled">'+display_date+'</SPAN></TD>\n';
+					}
+				else {
+					var selected_date = display_date;
+					var selected_month = display_month;
+					var selected_year = display_year;
+					if (this.displayType=="week-end") {
+						var d = new Date(selected_year,selected_month-1,selected_date,0,0,0,0);
+						d.setDate(d.getDate() + (7-col));
+						selected_year = d.getYear();
+						if (selected_year < 1000) { selected_year += 1900; }
+						selected_month = d.getMonth()+1;
+						selected_date = d.getDate();
+						}
+					result += '	<TD CLASS="'+this.cssPrefix+dateClass+'"><A HREF="javascript:'+windowref+this.returnFunction+'('+selected_year+','+selected_month+','+selected_date+');'+windowref+'CP_hideCalendar(\''+this.index+'\');" CLASS="'+this.cssPrefix+dateClass+'">'+display_date+'</A></TD>\n';
+					}
+				display_date++;
+				if (display_date > daysinmonth[display_month]) {
+					display_date=1;
+					display_month++;
+					}
+				if (display_month > 12) {
+					display_month=1;
+					display_year++;
+					}
+				}
+			result += '</TR>';
+			}
+		var current_weekday = now.getDay() - this.weekStartDay;
+		if (current_weekday < 0) {
+			current_weekday += 7;
+			}
+		result += '<TR>\n';
+		result += '	<TD COLSPAN=7 ALIGN=CENTER CLASS="'+this.cssPrefix+'cpTodayText">\n';
+		if (this.disabledDatesExpression!="") {
+			var ds=""+now.getFullYear()+LZ(now.getMonth()+1)+LZ(now.getDate());
+			eval("disabled=("+this.disabledDatesExpression+")");
+			}
+		if (disabled || this.disabledWeekDays[current_weekday+1]) {
+			result += '		<SPAN CLASS="'+this.cssPrefix+'cpTodayTextDisabled">'+this.todayText+'</SPAN>\n';
+			}
+		else {
+			result += '		<A CLASS="'+this.cssPrefix+'cpTodayText" HREF="javascript:'+windowref+this.returnFunction+'(\''+now.getFullYear()+'\',\''+(now.getMonth()+1)+'\',\''+now.getDate()+'\');'+windowref+'CP_hideCalendar(\''+this.index+'\');">'+this.todayText+'</A>\n';
+			}
+		result += '		<BR>\n';
+		result += '	</TD></TR></TABLE></CENTER></TD></TR></TABLE>\n';
+	}
+
+	// Code common for MONTH, QUARTER, YEAR
+	// ------------------------------------
+	if (this.displayType=="month" || this.displayType=="quarter" || this.displayType=="year") {
+		if (arguments.length > 0) { var year = arguments[0]; }
+		else { 
+			if (this.displayType=="year") {	var year = now.getFullYear()-this.yearSelectStartOffset; }
+			else { var year = now.getFullYear(); }
+			}
+		if (this.displayType!="year" && this.isShowYearNavigation) {
+			result += "<TABLE WIDTH=144 BORDER=0 BORDERWIDTH=0 CELLSPACING=0 CELLPADDING=0>";
+			result += '<TR>\n';
+			result += '	<TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="22"><A CLASS="'+this.cssPrefix+'cpYearNavigation" HREF="javascript:'+windowref+'CP_refreshCalendar('+this.index+','+(year-1)+');">&lt;&lt;</A></TD>\n';
+			result += '	<TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="100">'+year+'</TD>\n';
+			result += '	<TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="22"><A CLASS="'+this.cssPrefix+'cpYearNavigation" HREF="javascript:'+windowref+'CP_refreshCalendar('+this.index+','+(year+1)+');">&gt;&gt;</A></TD>\n';
+			result += '</TR></TABLE>\n';
+			}
+		}
+		
+	// Code for MONTH display 
+	// ----------------------
+	if (this.displayType=="month") {
+		// If POPUP, write entire HTML document
+		result += '<TABLE WIDTH=120 BORDER=0 CELLSPACING=1 CELLPADDING=0 ALIGN=CENTER>\n';
+		for (var i=0; i<4; i++) {
+			result += '<TR>';
+			for (var j=0; j<3; j++) {
+				var monthindex = ((i*3)+j);
+				result += '<TD WIDTH=33% ALIGN=CENTER><A CLASS="'+this.cssPrefix+'cpText" HREF="javascript:'+windowref+this.returnMonthFunction+'('+year+','+(monthindex+1)+');'+windowref+'CP_hideCalendar(\''+this.index+'\');" CLASS="'+date_class+'">'+this.monthAbbreviations[monthindex]+'</A></TD>';
+				}
+			result += '</TR>';
+			}
+		result += '</TABLE></CENTER></TD></TR></TABLE>\n';
+		}
+	
+	// Code for QUARTER display
+	// ------------------------
+	if (this.displayType=="quarter") {
+		result += '<BR><TABLE WIDTH=120 BORDER=1 CELLSPACING=0 CELLPADDING=0 ALIGN=CENTER>\n';
+		for (var i=0; i<2; i++) {
+			result += '<TR>';
+			for (var j=0; j<2; j++) {
+				var quarter = ((i*2)+j+1);
+				result += '<TD WIDTH=50% ALIGN=CENTER><BR><A CLASS="'+this.cssPrefix+'cpText" HREF="javascript:'+windowref+this.returnQuarterFunction+'('+year+','+quarter+');'+windowref+'CP_hideCalendar(\''+this.index+'\');" CLASS="'+date_class+'">Q'+quarter+'</A><BR><BR></TD>';
+				}
+			result += '</TR>';
+			}
+		result += '</TABLE></CENTER></TD></TR></TABLE>\n';
+		}
+
+	// Code for YEAR display
+	// ---------------------
+	if (this.displayType=="year") {
+		var yearColumnSize = 4;
+		result += "<TABLE WIDTH=144 BORDER=0 BORDERWIDTH=0 CELLSPACING=0 CELLPADDING=0>";
+		result += '<TR>\n';
+		result += '	<TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="50%"><A CLASS="'+this.cssPrefix+'cpYearNavigation" HREF="javascript:'+windowref+'CP_refreshCalendar('+this.index+','+(year-(yearColumnSize*2))+');">&lt;&lt;</A></TD>\n';
+		result += '	<TD CLASS="'+this.cssPrefix+'cpYearNavigation" WIDTH="50%"><A CLASS="'+this.cssPrefix+'cpYearNavigation" HREF="javascript:'+windowref+'CP_refreshCalendar('+this.index+','+(year+(yearColumnSize*2))+');">&gt;&gt;</A></TD>\n';
+		result += '</TR></TABLE>\n';
+		result += '<TABLE WIDTH=120 BORDER=0 CELLSPACING=1 CELLPADDING=0 ALIGN=CENTER>\n';
+		for (var i=0; i<yearColumnSize; i++) {
+			for (var j=0; j<2; j++) {
+				var currentyear = year+(j*yearColumnSize)+i;
+				result += '<TD WIDTH=50% ALIGN=CENTER><A CLASS="'+this.cssPrefix+'cpText" HREF="javascript:'+windowref+this.returnYearFunction+'('+currentyear+');'+windowref+'CP_hideCalendar(\''+this.index+'\');" CLASS="'+date_class+'">'+currentyear+'</A></TD>';
+				}
+			result += '</TR>';
+			}
+		result += '</TABLE></CENTER></TD></TR></TABLE>\n';
+		}
+	// Common
+	if (this.type == "WINDOW") {
+		result += "</BODY></HTML>\n";
+		}
+	return result;
+	}
Index: branches/1/root/static/vote.css
===================================================================
--- branches/1/root/static/vote.css	(revision 155)
+++ branches/1/root/static/vote.css	(revision 155)
@@ -0,0 +1,173 @@
+img {
+    border: 0;
+    vertical-align: middle;
+}
+
+td {
+    padding: 4px;
+}
+
+tr {
+    vertical-align: top;
+    padding: 4px;
+}
+
+h1 {
+	border-bottom:1px solid #E3AB2C;
+	color:#000000;
+	margin-left:50px;
+	text-align:left;
+	width:500px;
+	font-size:18px;
+}
+
+a {
+	color:#3344FF;
+	font-weight:bold;
+}
+
+ul {
+    margin: 0px;
+}
+
+/* The CLASS used in epoll */
+
+.alert {
+    color: red;
+}
+
+.warning {
+    background-color: red;
+}
+
+div.page_box {
+    border-style: outset;
+    border-width: 5px;
+    margin-left: 20%;
+    margin-right: 20%;
+    margin-top: 15px;
+    margin-bottom: 15px;
+    padding: 5px;
+}
+
+div.login_box {
+    border-style: outset;
+    border-width: 5px;
+    margin-left: 20%;
+    margin-right: 20%;
+    margin-top: 15px;
+    margin-bottom: 15px;
+    padding: 5px;
+    /* background-color: red; */
+}
+
+div.box {
+    border-style: outset;
+    border-width: 5px;
+    padding: 1em;
+}
+
+.majabs {
+    background-color: #FF9933;
+}
+
+.selected {
+    background-color: #FF9999;
+}
+
+.notselected {
+}
+
+/* The ID used in epoll */
+
+#now {
+    font-size: 0.8em;
+    padding: 0.4em;
+    top: 1em;
+    right: 2em;
+    position: absolute;
+}
+
+#header {
+    background-color:#CCFF99;
+    padding-bottom: 1em;
+    margin-bottom: 1em;
+}
+
+#title {
+    font-size: 1.5em;
+}
+
+#menu {
+    font-size: 0.9em;
+}
+
+#footer {
+    padding-top: 1em;
+    margin-top: 1em;
+    text-align: right;
+}
+
+#poll_summary {
+    border-style: outset;
+    border-width: 5px;
+    padding: 1em;
+}
+
+#poll_label {}
+
+#voting_list {}
+
+#poll_running {
+    border-style: outset;
+    border-width: 5px;
+    padding: 1em;
+}
+
+#vote_done {}
+
+#ballot {
+    border-style: inset;
+    border-width: 10px;
+    margin-left: 30%;
+    margin-right: 30%;
+    margin-top: 20px;
+    margin-bottom: 20px;
+    padding: 10px;
+    background-color: Silver;
+}
+
+#admin_login {}
+
+#global_poll {
+    margin-left:30px;
+}
+
+#open_poll {
+    color:#E3AB2C;
+}
+
+#comming_poll {
+    color:#E3AB2C;
+}
+
+#closed_poll {
+    color:#E3AB2C;
+}
+
+#ballot_list {}
+
+#results {}
+
+#map_ballot {}
+
+#ballot_to_valid {}
+
+#poll_request {
+	margin-left: auto;
+	margin-right: auto;
+	width: 600px; 
+	text-align: left;
+}
+
+#poll_confirm {}
Index: branches/1/root/templates/admin/default.tt
===================================================================
--- branches/1/root/templates/admin/default.tt	(revision 155)
+++ branches/1/root/templates/admin/default.tt	(revision 155)
@@ -0,0 +1,182 @@
+<DIV ID="testdiv1" STYLE="position:absolute;visibility:hidden;background-color:white;layer-background-color:white;"></DIV>
+
+[% vote = c.model('Vote') %]
+
+[% IF vote.vote_status(voteid) == 'BEFORE' %]
+<table border="1">
+<tr><th>Vote</th><th>PossibilitÃ© de vote</th></tr>
+<tr>
+<td valign="TOP">
+<form action="[% c.uri_for(voteid) %]" method="POST">
+
+[% thisvote = vote.vote_info(voteid) %]
+
+Label: <input type="text" name="label" value="[% c.req.param('label') || thisvote.label | html %]">
+<br>
+Description:<br>
+<textarea name="description" cols="60" rows="4">[% c.req.param('description') || thisvote.description | html %]</textarea>
+<br>
+<br>
+<!-- http://www.mattkruse.com/javascript/calendarpopup/ -->
+<i>Les dates sont au format JJ/MM/AAAA HH:MM:SS</i><br>
+
+<SCRIPT LANGUAGE="JavaScript" ID="js13">
+var cal13 = new CalendarPopup();
+</SCRIPT>
+
+Debut du vote:<br>
+Date: <input type="text" name="dstart" value="[% c.req.param('dstart') || thisvote.dstart | html %]">
+<A HREF="#" onClick="cal13.select(document.forms[0].dstart,'anchor13','dd/MM/yyyy'); return false;" TITLE="cal13.select(document.forms[0].dstart,'anchor13','dd/MM/yyyy'); return false;" NAME="anchor13" ID="anchor13">select</A>
+Heure: <input type="text" name="hstart" value="[% c.req.param('hstart') || thisvote.hstart | html %]">
+<br>
+Fin du vote:<br>
+Date: <input type="text" name="dend" value="[% c.req.param('dend') || thisvote.dend | html %]">
+<A HREF="#" onClick="cal13.select(document.forms[0].dend,'anchor14','dd/MM/yyyy',(document.forms[0].dend.value=='')?document.forms[0].dstart.value:null); return false;" TITLE="cal13.select(document.forms[0].dend,'anchor14','dd/MM/yyyy',(document.forms[0].dend.value=='')?document.forms[0].dstart.value:null); return false;" NAME="anchor14" ID="anchor14">select</A>
+Heure: <input type="text" name="hend" value="[% c.req.param('hend') || thisvote.hend | html %]">
+<br>
+<br>
+Nombre de choix possible:
+<input type="text" name="choice_count" value="[% c.req.param('choice_count') || thisvote.choice_count | html %]">
+<br>
+Nombre de choix libres:
+<input type="text" name="free_choice" value="[% c.req.param('free_choice') || thisvote.free_choice || 0 | html %]">
+<br>
+<input type="submit" value="Enregistrer">
+</form>
+</td>
+<td valign="TOP">
+[% IF ! vote.vote_choices(voteid).size %]
+<p class="alert">Aucun choix configurÃ©</p>
+[% ELSE %]
+[% FOREACH choice = vote.vote_choices(voteid) %]
+<form action="[% c.uri_for(voteid) %]" method="POST">
+[% loop.count %] - [% vote.choice_info(choice).label | html %]
+<input type="hidden" name="delch" value="[% vote.choice_info(choice).key %]">
+<input type="submit" name="del" value="Effacer">
+</form>
+<br>
+[% END %]
+[% END %]
+<form action="[% c.uri_for(voteid) %]" method="POST">
+Ajouter un choix:<br>
+<input type="text" name="addch">
+<input type="submit" value="Ajouter">
+</form>
+</td>
+</tr>
+</table>
+[% ELSE %]
+[% INCLUDE 'includes/poll.tt' %]
+[% END %]
+
+[% IF vote.vote_status(voteid) != 'AFTER' %]
+<table border="1">
+<tr><th colspan=2>Liste des votants</th></tr>
+<tr>
+<td valign="TOP">
+<p>Ajouter un votant</p>
+<form action="[% c.uri_for(voteid) %]" method="POST">
+Nom: <input type="text" name="id"><br>
+Mail: <input type="text" name="mail"><br>
+<input type="submit" value="Ajouter">
+</form>
+<hr>
+<p>Soumettre la liste via un fichier csv (le Nom est optionnel)</p>
+<p>Format: <i>Mail@domain;Nom</i></p>
+<form method="POST" ENCTYPE="multipart/form-data" action="[% c.uri_for(voteid) %]">
+<input type="file" name="votinglist"><br>
+[% IF vote.vote_status(voteid) == 'BEFORE' %]
+<input type="checkbox" name="delete">Effacer la liste des votants<br>
+[% END %]
+<input type="submit" value="Ajouter">
+</form>
+<hr>
+<p>Envoyer les mots de passes au nouveaux votants</p>
+<form action="[% c.uri_for(voteid) %]" method="POST">
+<input type="submit" name="mailpasswd" value="Envoyer les mot de passe">
+</form>
+</td>
+<td valign="TOP">
+[% IF NOT vote.vote_voting_count(voteid) %]
+<p class="alert">Aucun votant inscrit</p>
+[% ELSE %]
+[% signing = vote.vote_voting(voteid) %]
+[% FOREACH thisvoting = signing %]
+[% voting = vote.voting_info(thisvoting) %]
+[% IF NOT voting.date %]
+<form action="[% c.uri_for(voteid) %]" method="POST">
+<input type="hidden" name="delvoting" value="[% voting.vkey %]">
+[% END %]
+[% voting.label | html %] [% voting.mail | html %]
+[% IF voting.passwd %]<i>(pass envoyÃ©)</i>[% END %]
+[% IF NOT voting.date %]
+<input type="submit" name="delete" value="delete"><br>
+</form>
+[% ELSE %]
+(<i>a votÃ©</i>)
+[% END %]
+[% END %]
+[% END %]
+</td>
+</tr>
+</table>
+
+[% ELSE %]
+
+[% # INCLUDE 'includes/poll.tt' %]
+
+[% FOREACH untrusted = vote.vote_ballot_untrusted_values(voteid) %]
+[% IF loop.first %]
+<div id="map_ballot">
+<p>Remapper les valeurs des bulletins</p>
+<table border="1">
+<tr><th>Valeur saisie</th><th>Valeur Ã  considÃ©rer</th></tr>
+[% END %]
+<tr>
+<td>[% untrusted | html %]</td>
+<td>
+    <form action="[% c.uri_for('/admin', voteid) %]" method="POST">
+    <input type="hidden" name="mapfrom" value="[% untrusted | html %]">
+    <select name="mapto">
+    [% FOREACH v = vote.vote_ballot_values(voteid) %]
+        [% NEXT IF v == untrusted %]
+        <option>[% v | html %]</options>
+    [% END %]
+    </select>
+    <input type="submit">
+    </form>
+</td>
+</tr>
+[% IF loop.last %]
+</table>
+</div>
+[% END %]
+[% END %]
+
+[% FOREACH bid = vote.list_vote_ballot_needvalid(voteid) %]
+    [% IF loop.first %]
+    <div id="ballot_to_valid">
+    <p>Bulletins Ã  valider:</p>
+    <form action="[% c.uri_for(voteid) %]" method="POST">
+    <table border="1">
+    [% END %]
+<tr>
+<td>[% bid %]</td>
+<td>
+[% FOREACH item = vote.ballot_items(bid) %]
+[% loop.count %]- [% item.v %]<br>
+[% END %]
+</td>
+<td>
+<input type="radio" name="[% bid %]" value="valid">est valide<br>
+<input type="radio" name="[% bid %]" value="invalid">n'est pas valide<br>
+</td>
+</tr>
+    [% IF loop.last %]
+    </table>
+    <input type="submit">
+    </form>
+    </div>
+    [% END %]
+[% END %]
+[% END %]
Index: branches/1/root/templates/admin/denied.tt
===================================================================
--- branches/1/root/templates/admin/denied.tt	(revision 155)
+++ branches/1/root/templates/admin/denied.tt	(revision 155)
@@ -0,0 +1,5 @@
+<!-- $Id$ -->
+
+<div class="alert">
+<p>Impossible de modifier un vote entamÃ© ou fini</p>
+</div>
Index: branches/1/root/templates/admin/index.tt
===================================================================
--- branches/1/root/templates/admin/index.tt	(revision 155)
+++ branches/1/root/templates/admin/index.tt	(revision 155)
@@ -0,0 +1,6 @@
+[% vote = c.model('Vote') %]
+
+[% FOREACH voteid = vote.list_comming_vote %]
+<a href="[% c.uri_for('/admin', voteid) %]">[% vote.vote_info(voteid).label | html %]</a>
+<br>
+[% END %]
Index: branches/1/root/templates/admin/login.tt
===================================================================
--- branches/1/root/templates/admin/login.tt	(revision 155)
+++ branches/1/root/templates/admin/login.tt	(revision 155)
@@ -0,0 +1,10 @@
+<!-- $Id$ -->
+[% vote = c.model('Vote') %]
+
+<div id="admin_login" class="login_box">
+<form action="[% c.uri_for('/admin', voteid) %]" method="POST">
+<p>Acc&egrave;s &agrave; l'interface d'administration du vote <b><i>[% vote.vote_info(voteid).label | html %]</i></b></p>
+<p>Veuillez saisir le mot de passe : <input type="password" name="vpassword">
+<input type="submit"></p>
+</form>
+</div>
Index: branches/1/root/templates/ballot/closed.tt
===================================================================
--- branches/1/root/templates/ballot/closed.tt	(revision 155)
+++ branches/1/root/templates/ballot/closed.tt	(revision 155)
@@ -0,0 +1,12 @@
+[% INCLUDE 'includes/poll.tt' %]
+
+[% vote = c.model('Vote') %]
+
+<div id="ballot">
+[% IF vote.vote_status(voteid) == 'BEFORE' %]
+Ce Vote n'est pas encore ouvert
+[% ELSIF vote.vote_status(voteid) == 'AFTER' %]
+Ce vote est terminÃ©, les rÃ©sultats sont disponibles
+<a href="[% c.uri_for('/vote', voteid) %]">ici</a>.
+[% END %]
+</div>
Index: branches/1/root/templates/ballot/default.tt
===================================================================
--- branches/1/root/templates/ballot/default.tt	(revision 155)
+++ branches/1/root/templates/ballot/default.tt	(revision 155)
@@ -0,0 +1,51 @@
+[% vote = c.model('Vote') %]
+
+[% thisvote = vote.vote_info(voteid) %]
+
+<div id="ballot" class="ballot">
+<form action="[% c.uri_for(voteid) %]" method="POST">
+<p><b>Vote: [% thisvote.label | html %]</b></p>
+<p>[% thisvote.description | html %]</p>
+<p>[% thisvote.choice_count %] choix possibles:</p>
+<hr>
+[% IF c.req.param('ballot') %]
+<p>Confirmez votre vote:</p>
+[% FOREACH ch = c.req.param('sbal') %]
+[% choices.$ch | html %]
+<input type="hidden" name="sbal" value="[% ch %]">
+<br>
+[% END %]
+[% FOREACH ch = c.req.param('fsbal') %]
+[% ch | html %]<br>
+<input type="hidden" name="fsbal" value="[% ch %]">
+[% END %]
+[% IF ! c.req.param('fsbal').size && ! c.req.param('sbal').size %]
+<i>Vote blanc</i><br>
+[% END %]
+<hr>
+<p class="warning">AprÃšs avoir valider, il vous sera impossible de modifier votre vote.</p>
+<p><input type="submit" name="confirm" value="Valider dÃ©finitivement">
+<input type="submit" name="Modify" value="Modifier mon vote"></p>
+</form>
+
+[% ELSE %]
+
+[% IF vote_error %]<p class="alert">[% vote_error %]</p>[% END %]
+
+[% FOREACH choice = vote.vote_choices(voteid) %]
+[% key = vote.choice_info(choice).key %]
+<input type="checkbox" name="sbal" value="[% key %]"[% " checked" IF sbal.$key %]>
+[% vote.choice_info(choice).label | html %]<br>
+[% END %]
+
+[% count = 0 %]
+[% WHILE count < thisvote.free_choice %]
+<input type="text" name="fsbal" value="[% fsbal.$count %]"><br>
+[% count = count + 1 %]
+[% END %]
+<hr>
+<input type="submit" name="ballot" value="Voter &gt;&gt;">
+<p>Votre vote ne sera pris en compte qu'aprÃšs confirmation</p>
+[% END %]
+</form>
+</div>
Index: branches/1/root/templates/ballot/done.tt
===================================================================
--- branches/1/root/templates/ballot/done.tt	(revision 155)
+++ branches/1/root/templates/ballot/done.tt	(revision 155)
@@ -0,0 +1,10 @@
+[% INCLUDE 'includes/poll.tt' %]
+
+<div id="ballot" class="page_box">
+<p>Vous venez de voter, le numÃ©ro de votre bulletin est le <b>[% ballotid %]</b>.</p>
+
+<p>Notez le si vous dÃ©sirez reconnaÃ®tre votre vote au dÃ©pouillement.</p> 
+
+<p>Les rÃ©sultats seront disponibles
+<a href="[% c.uri_for('/vote', voteid) %]">Ici</a>.</p>
+</div>
Index: branches/1/root/templates/ballot/index.tt
===================================================================
--- branches/1/root/templates/ballot/index.tt	(revision 155)
+++ branches/1/root/templates/ballot/index.tt	(revision 155)
@@ -0,0 +1,14 @@
+[% vote = c.model('Vote') %]
+
+[% voteids = vote.list_running_vote %]
+
+[% IF voteids.size %]
+
+[% FOREACH voteid = voteids %]
+<a href="[% c.uri_for('/ballot', voteid) %]">[% vote.vote_info(voteid).label | html %]</a>
+<br>
+[% END %]
+
+[% ELSE %]
+Pas de vote en cours
+[% END %]
Index: branches/1/root/templates/ballot/login.tt
===================================================================
--- branches/1/root/templates/ballot/login.tt	(revision 155)
+++ branches/1/root/templates/ballot/login.tt	(revision 155)
@@ -0,0 +1,21 @@
+[% INCLUDE 'includes/poll.tt' %]
+
+<div id="login" class="login_box">
+[% IF login_failure %]
+<p class="alert">Mauvais login et/ou mot de passe. Etes-vous sur les listes Ã©lectorales ?</p>
+[% END %]
+<p>Veuillez vous authentifier pour voter:</p>
+<form action="[% c.uri_for(voteid) %]" method="POST">
+<table border="0" align="center">
+<tr>
+<td>Email:</td>
+<td><input type="text" name="mail" value="[% c.req.param('mail') %]"></td>
+</tr>
+<tr>
+<td>Mot de passe:</td>
+<td><input type="password" name="password" value=""></td>
+</tr>
+<tr><td colspan="2" align="center"><input type="submit"></td></tr>
+</table>
+</form>
+</div>
Index: branches/1/root/templates/ballot/signed.tt
===================================================================
--- branches/1/root/templates/ballot/signed.tt	(revision 155)
+++ branches/1/root/templates/ballot/signed.tt	(revision 155)
@@ -0,0 +1,7 @@
+[% INCLUDE 'includes/poll.tt' %]
+
+<div id="vote_done">
+<p>[% mail | html %] a d&eacute;j&agrave; vot&eacute; le: [% signed_date | html %]</p>
+<p>Les rÃ©sultats du vote seront disponible <a href="[% c.uri_for('/vote', voteid) %]">ici</a>.</p>
+</div>
+
Index: branches/1/root/templates/default.tt
===================================================================
--- branches/1/root/templates/default.tt	(revision 155)
+++ branches/1/root/templates/default.tt	(revision 155)
@@ -0,0 +1,59 @@
+
+[% vote = c.model('Vote') %]
+
+
+[% FOREACH id = vote.list_running_vote %]
+[% IF loop.first %]
+<div id="global_poll">
+<div id="open_poll">
+<h1>Vote en cours</h1>
+<table>
+<tbody>
+[% END %]
+<tr>
+<td><lu><li><a href="[% c.uri_for( '/vote', id ) %]">[% vote.vote_info(id).label | html %]</a></lu></td></td>
+<td>&gt;<a href="[% c.uri_for( '/ballot', id ) %]"> Voter</a></td>
+</tr>
+[% IF loop.last %]
+</tbody>
+</table>
+</div>
+[% END %]
+[% END %]
+
+[% FOREACH id = vote.list_comming_vote %]
+[% IF loop.first %]
+<div id="comming_poll">
+<h1>Vote Ã  venir</h1>
+<table>
+<tbody>
+[% END %]
+<tr>
+<td><lu><li><a href="[% c.uri_for( 'vote', id ) %]">[% vote.vote_info(id).label | html %]</a></li></lu></td>
+<td>&gt;<a href="[% c.uri_for( '/admin', id ) %]"> Administrer</a></td>
+</tr>
+[% IF loop.last %]
+</tbody>
+</table>
+</div>
+[% END %]
+[% END %]
+
+
+[% FOREACH id = vote.list_closed_vote %]
+[% IF loop.first %]
+<div id="closed_poll">
+<h1>Vote Ferm&eacute;</h1>
+<table>
+<tbody>
+[% END %]
+<tr>
+<td><lu><li><a href="[% c.uri_for( 'vote', id ) %]">[% vote.vote_info(id).label | html %]</a></lu></td></td>
+</tr>
+[% IF loop.last %]
+</tbody>
+</table>
+</div>
+</div>
+[% END %]
+[% END %]
Index: branches/1/root/templates/includes/footer.tt
===================================================================
--- branches/1/root/templates/includes/footer.tt	(revision 155)
+++ branches/1/root/templates/includes/footer.tt	(revision 155)
@@ -0,0 +1,6 @@
+<div id="footer">
+<a href="http://forge.ipsl.jussieu.fr/epoll"> Epoll [% Vote.VERSION | html %]</a>
+<a href="http://www.catalystframework.org/"><img src="[% c.uri_for('/static', 'images', 'btn_88x31_powered_shadow.png') %]"></a>
+</div>
+</body>
+</html>
Index: branches/1/root/templates/includes/header.tt
===================================================================
--- branches/1/root/templates/includes/header.tt	(revision 155)
+++ branches/1/root/templates/includes/header.tt	(revision 155)
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+<title>[% page.title %]</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<link rel="stylesheet" href="[% c.uri_for('/static', 'vote.css') %]" type="text/css">
+<link rel="icon" href="[%- c.uri_for('/static', 'images', 'favicon.ico') -%]" type="image/x-icon">
+
+<script language="JavaScript" src="[% c.uri_for('/static', 'js', 'CalendarPopup.js') %]"></SCRIPT>
+
+</head>
+<body>
+<div id="header">
+    <p id="title"><b>Epoll:</b> systÃšme de vote en ligne</p>
+    <p id="now" style="float: right">Page gÃ©nÃ©rÃ©e le [% c.model('Vote').dbtime | html %]</p>
+    <p id="menu">
+    <a href="[% c.uri_for('/') %]">Liste des votes</a> ::
+    <a href="[% c.uri_for('/newpoll') %]">CrÃ©er un nouveau vote</a>
+    [% IF voteid %]
+    :: <a href="[% c.uri_for('/vote', voteid) %]">
+        Votre vote: [% c.model('Vote').vote_info(voteid).label | truncate(40) | html %]
+    </a>
+    [% END %]
+    </p>
+</div>
Index: branches/1/root/templates/includes/poll.tt
===================================================================
--- branches/1/root/templates/includes/poll.tt	(revision 155)
+++ branches/1/root/templates/includes/poll.tt	(revision 155)
@@ -0,0 +1,33 @@
+[% vote = c.model('Vote') %]
+[% thisvote = vote.vote_info(voteid) %]
+
+<div id="poll_summary" class="box" style="clear: both;">
+<div style="float: left; width: 70%;">
+<p id="poll_label">[% thisvote.label | html %]</p>
+<p>[% thisvote.description | html | html_line_break %]</p>
+[% IF thisvote.dstart && thisvote.dend %]
+<p>Scrutin ouvert du <b>[% thisvote.dstart _ ' ' _ thisvote.hstart | html %]</b> au
+<b>[% thisvote.dend _ ' ' _ thisvote.hend | html %]</b></p>
+[% END %]
+Ont votÃ©: [% vote.vote_signing_count(voteid) %] / [% vote.vote_voting_count(voteid) %]<br>
+Bulletins: [% vote.vote_ballot_count(voteid) %]
+[% IF vote.vote_signing_count(voteid) != vote.vote_ballot_count(voteid) %]
+<p class="alert">Le nombre de bulletins et le nombre de personnes ayant votÃ© divergent,
+suspicion de tricherie</p>
+[% END %]
+</div>
+[% FOREACH item = vote.vote_choices(voteid) %]
+[% IF loop.first %]
+<div style="float: right; width: 20%; top: 0px;" class="box">
+<p>Bulletin:</p>
+<ol style="padding-left:15px; margin:5px;">
+[% END %]
+<li>[% vote.choice_info(item).label | html %]</li>
+[% IF loop.last %]
+</ol>
+</div>
+[% END %]
+[% END %]
+<div style="clear: both;"></div>
+</div>
+
Index: branches/1/root/templates/includes/poll_results.tt
===================================================================
--- branches/1/root/templates/includes/poll_results.tt	(revision 155)
+++ branches/1/root/templates/includes/poll_results.tt	(revision 155)
@@ -0,0 +1,110 @@
+[% vote = c.model('Vote') %]
+
+<div id="results" class="box">
+[% IF vote.list_vote_ballot_needvalid(voteid).size %]
+<p>Resultats provisoires:</p>
+<p class="alert">Le gestionnaire doit vÃ©rifier la validitÃ© de certains bulletins</p>
+[% ELSE %]
+<p>Resultats:</p>
+[% END %]
+<p>Nombre de choix Ã  retenir: [% vote.vote_info(voteid).choice_count %]</p>
+<p>Participations: [% vote.vote_signing_count(voteid) %] / [% vote.vote_voting_count(voteid) %]
+[% IF vote.vote_voting_count(voteid) %]
+([% vote.vote_signing_count(voteid) * 100 / vote.vote_voting_count(voteid) | format('%.2f') %])
+[% END %]</p>
+<p>Nombre de vote(s) exprimÃ©(s): [% vote.vote_ballot_count_nonull(voteid) %]
+[% IF vote.vote_ballot_count(voteid) %]
+([% vote.vote_ballot_count_nonull(voteid) * 100 / vote.vote_ballot_count(voteid) | format('%.2f') %]%)
+[% END %]
+</p>
+
+[% IF vote.vote_ballot_count_nonull(voteid) %]
+[% FOREACH res = vote.vote_results_nonull(voteid) %]
+[% IF loop.first %]
+<p>Score:</p>
+<table border="1"><tr>
+<th>LÃ©gende:</th>
+<td class="majabs">MajoritÃ© Absolue</td>
+<td class="selected">Elus</td>
+<td class="notselected">Non Ã©lu</td>
+</tr></table><br>
+<table border="1">
+<tr><th>Score</th><th>Ligne NÂ°</th><th>choix</th><th>Nb voix</th><th>%</th><th></th></tr>
+[% END %]
+
+[% IF res.count != loop.prev.count && loop.count > 0 %]
+    [% # If equality, we keep same color, even we are over resullts %]
+[% mycount = loop.count %]
+[% IF loop.count <= vote.vote_info(voteid).choice_count %]
+[% IF res.count >= vote.ballot_count_nonull(voteid) / 2 + 1 %]
+[% class = 'majabs' %]
+[% ELSE %]
+[% class = 'selected' %]
+[% END %]
+[% ELSE %]
+[% class = 'notselected' %]
+[% END %]
+[% END %]
+<tr class="[% class %]">
+<td>[% mycount %]</td>
+<td>[% loop.count %]</td>
+<td>[% res.value | html %]</td>
+<td>[% res.count %]</td>
+<td>[% res.count * 100 / vote.ballot_count_nonull(voteid) | format('%.2f') %]</td>
+<td><img src="[% c.uri_for('/static', 'images', 'green-v.png') %]" 
+height="10px" width="[% res.count * 400 / vote.ballot_count_nonull(voteid) | format('%d') %]px"></td>
+</tr>
+
+[% IF loop.last %]
+</table>
+[% END %]
+[% END %]
+[% ELSE %]
+<p class="alert">Aucun suffrage exprimÃ©.</p>
+[% END %]
+</div>
+
+[% FOREACH id = vote.list_vote_ballot(voteid) %]
+[% IF loop.first %]
+<div id="ballot_list" class="box">
+<p>Liste des bulletins:</p>
+<table border="1">
+<tr><th>numÃ©ro</th><th>Id</th>
+<th>contenu (=> corrigÃ© en)
+[% IF vote.vote_info(voteid).free_choice %]
+<br>(*: hors liste)
+[% END %]
+</th><th>remarque</th></tr>
+[% END %]
+<tr>
+<td>[% loop.count %]</td>
+<td>[% id | html %]</td>
+<td>
+[% items = vote.ballot_items(id) %]
+[% IF items.size %]
+[% FOREACH item = items %]
+[% "<ul>\n" IF loop.first %]
+<li>[% item.v | html %]
+[% ' *' IF NOT item.fromlist %]
+[% IF item.corrected %] (=&gt; [% item.corrected %])[% END %]</li>
+[% "</ul>\n" IF loop.last %]
+[% END %]
+[% ELSE %]
+<i>Vote blanc</i>
+[% END %]
+</td>
+<td>
+[% invalid = '#' _ vote.ballot_info(id).invalid %]
+[% IF invalid == '#1' %]
+<span class="alert">InvalidÃ©</span>
+[% ELSIF invalid  == '#' %]
+<i>doit Ãªtre validÃ©</i>
+[% END %]
+</td>
+</tr>
+[% IF loop.last %]
+</table>
+</div>
+[% END %]
+[% END %]
+
Index: branches/1/root/templates/includes/poll_running.tt
===================================================================
--- branches/1/root/templates/includes/poll_running.tt	(revision 155)
+++ branches/1/root/templates/includes/poll_running.tt	(revision 155)
@@ -0,0 +1,4 @@
+<div id="poll_running">
+<p>Le vote est actuellement en cours</p>
+<p>Vous pouvez: <a href="[% c.uri_for('/ballot', voteid) %]">Voter ici</a></li></p>
+</div>
Index: branches/1/root/templates/includes/signing_list.tt
===================================================================
--- branches/1/root/templates/includes/signing_list.tt	(revision 155)
+++ branches/1/root/templates/includes/signing_list.tt	(revision 155)
@@ -0,0 +1,25 @@
+[% USE Mail %]
+
+[% vote = c.model('Vote') %]
+
+[% FOREACH id = vote.vote_voting(voteid) %]
+[% voting = vote.voting_info(id) %]
+[% IF loop.first %]
+<div id="voting_list" class="box">
+<p>Liste des votants:</p>
+<table border="1">
+<tr><th>NumÃ©ro</th><th>Electeur</th><th>Emargement</th></tr>
+[% END %]
+<tr>
+<td>[% loop.count %]</td>
+<td>
+    [% voting.label | html %]
+    [% "&lt;" IF voting.label %][% voting.mail | mail | html %][% "&gt;" IF voting.label %]
+</td>
+<td>[% "A votÃ©" IF voting.date %]</td>
+</tr>
+[% IF loop.last %]
+</table>
+</div>
+[% END %]
+[% END %]
Index: branches/1/root/templates/newpoll/default.tt
===================================================================
--- branches/1/root/templates/newpoll/default.tt	(revision 155)
+++ branches/1/root/templates/newpoll/default.tt	(revision 155)
@@ -0,0 +1,8 @@
+<div id="poll_confirm" class="page_box">
+<form action="[% c.uri_for(reqid) %]" method="POST">
+[% c.model('Vote').poll_request_info(reqid).label | html %]<br>
+<p>Saisissez un mot de passe pour administrer votre vote</p>
+<p><input type="password" name="passwd"></p>
+<p><input type="submit"></p>
+</form>
+</div>
Index: branches/1/root/templates/newpoll/index.tt
===================================================================
--- branches/1/root/templates/newpoll/index.tt	(revision 155)
+++ branches/1/root/templates/newpoll/index.tt	(revision 155)
@@ -0,0 +1,18 @@
+<div id="poll_request" class="page_box">
+<p>CrÃ©er un nouveau vote:</p>
+
+<form action="[% c.uri_for() %]" method="POST">
+<center>
+<table border="0">
+<tbody align="center">
+<tr><th>votre mail:</th><td><input type="text" name="mail" tabindex="1" size="45" maxlength="150"></td></tr>
+<tr><th>nom du vote:</th><td><input type="text" name="label" tabindex="2" size="45" maxlength="150"></td></tr>
+<!-- Cf http://forge.ipsl.jussieu.fr/epoll/ticket/1
+<tr><th>mot de passe:</th><td><input type="password" name="vpasswd"></td></tr>
+-->
+<tr><td></td><td><input type="submit" tabindex="3"></td></tr>
+</tbody>
+</table>
+</center>
+</form>
+</div>
Index: branches/1/root/templates/newpoll/norequest.tt
===================================================================
--- branches/1/root/templates/newpoll/norequest.tt	(revision 155)
+++ branches/1/root/templates/newpoll/norequest.tt	(revision 155)
@@ -0,0 +1,5 @@
+<!-- $Id$ -->
+
+<div id="poll_request" class="box">
+<p class="alert">Je n'ai pas cette requÃªte de crÃ©ation de vote.</p>
+</div>
Index: branches/1/root/templates/newpoll/request.tt
===================================================================
--- branches/1/root/templates/newpoll/request.tt	(revision 155)
+++ branches/1/root/templates/newpoll/request.tt	(revision 155)
@@ -0,0 +1,6 @@
+<div id="poll_request" class="box">
+<p>Un mail indiquant la procÃ©dure de confirmation de crÃ©ation du vote vient
+d'Ãªtre envoyÃ© Ã  l'adresse [% c.req.param('mail') | html %].</p>
+
+<p>Merci</p>
+</div>
Index: branches/1/root/templates/vote/default.tt
===================================================================
--- branches/1/root/templates/vote/default.tt	(revision 155)
+++ branches/1/root/templates/vote/default.tt	(revision 155)
@@ -0,0 +1,20 @@
+<div style="text-align: right;"><a href="[% c.uri_for( '/admin', voteid ) %]">Administrer</a></div>
+
+[% vote = c.model('Vote') %]
+
+[% INCLUDE 'includes/poll.tt' %]
+
+[% SWITCH vote.vote_status(voteid) %]
+[% CASE 'RUNNING' %]
+[% INCLUDE 'includes/poll_running.tt' %]
+[% CASE 'BEFORE' %]
+<div class="page_box">
+<p>Ce scrutin n'est pas encore ouvert.</p>
+</div>
+[% INCLUDE 'includes/signing_list.tt' %]
+[% CASE 'AFTER' %]
+[% INCLUDE 'includes/poll_results.tt' %]
+[% INCLUDE 'includes/signing_list.tt' %]
+[% END %]
+
+
Index: branches/1/sandbox/encryption.pl
===================================================================
--- branches/1/sandbox/encryption.pl	(revision 155)
+++ branches/1/sandbox/encryption.pl	(revision 155)
@@ -0,0 +1,44 @@
+# A test for encryption (#27: https://forge.ipsl.jussieu.fr/epoll/ticket/27)
+
+use Crypt::RSA; # crypt/decrypt
+use XML::Simple; # dump data
+use MIME::Base64; # to make it ascii
+
+my $rsa = new Crypt::RSA ES => 'PKCS1v15'; 
+
+    my ($public, $private) = 
+        $rsa->keygen ( 
+            Identity  => 'Vote ID 12',
+            Size      => 768,  
+            Password  => undef, 
+            Verbosity => 0,
+            KF=>'SSH',
+        ) or die $rsa->errstr();
+print $public->serialize;
+print "\n";
+print encode_base64($private->serialize);
+print "\n";
+my $xs = XML::Simple->new(ForceArray => 1, RootName => 'ballot');
+print my $xml = $xs->XMLout({ id => '8daad55a7f94a4fb79bda7ab007953dc', sbal => [ 'coco', 'kikikikikikikikikikikikiki' ], fsbal => [] });
+print "##########\n";
+
+my $encryptedballot = $rsa->encrypt ( 
+            Message    => $xml,
+            Key        => $public,
+            Armour     => 1,
+        ) || die $rsa->errstr();
+
+# reading
+print $encryptedballot . "\n";
+
+my $ballot;
+foreach (0..1000) {
+$ballot = $rsa->decrypt(
+        Cyphertext => $encryptedballot,
+            Key        => $private,
+            Armour     => 1,
+        ) || die $rsa->errstr();
+}
+
+print $ballot
+
Index: branches/1/script/vote_cgi.pl
===================================================================
--- branches/1/script/vote_cgi.pl	(revision 155)
+++ branches/1/script/vote_cgi.pl	(revision 155)
@@ -0,0 +1,37 @@
+#!/usr/bin/perl -w
+
+BEGIN { $ENV{CATALYST_ENGINE} ||= 'CGI' }
+
+use strict;
+use warnings;
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use Vote;
+
+Vote->run;
+
+1;
+
+=head1 NAME
+
+vote_cgi.pl - Catalyst CGI
+
+=head1 SYNOPSIS
+
+See L<Catalyst::Manual>
+
+=head1 DESCRIPTION
+
+Run a Catalyst application as a cgi script.
+
+=head1 AUTHOR
+
+Sebastian Riedel, C<sri@oook.de>
+
+=head1 COPYRIGHT
+
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
Index: branches/1/script/vote_create.pl
===================================================================
--- branches/1/script/vote_create.pl	(revision 155)
+++ branches/1/script/vote_create.pl	(revision 155)
@@ -0,0 +1,74 @@
+#!/usr/bin/perl -w
+
+use strict;
+use warnings;
+use Getopt::Long;
+use Pod::Usage;
+use Catalyst::Helper;
+
+my $force = 0;
+my $mech  = 0;
+my $help  = 0;
+
+GetOptions(
+    'nonew|force'    => \$force,
+    'mech|mechanize' => \$mech,
+    'help|?'         => \$help
+ );
+
+pod2usage(1) if ( $help || !$ARGV[0] );
+
+my $helper = Catalyst::Helper->new( { '.newfiles' => !$force, mech => $mech } );
+
+pod2usage(1) unless $helper->mk_component( 'Vote', @ARGV );
+
+1;
+
+=head1 NAME
+
+vote_create.pl - Create a new Catalyst Component
+
+=head1 SYNOPSIS
+
+vote_create.pl [options] model|view|controller name [helper] [options]
+
+ Options:
+   -force        don't create a .new file where a file to be created exists
+   -mechanize    use Test::WWW::Mechanize::Catalyst for tests if available
+   -help         display this help and exits
+
+ Examples:
+   vote_create.pl controller My::Controller
+   vote_create.pl -mechanize controller My::Controller
+   vote_create.pl view My::View
+   vote_create.pl view MyView TT
+   vote_create.pl view TT TT
+   vote_create.pl model My::Model
+   vote_create.pl model SomeDB DBIC::Schema MyApp::Schema create=dynamic\
+   dbi:SQLite:/tmp/my.db
+   vote_create.pl model AnotherDB DBIC::Schema MyApp::Schema create=static\
+   dbi:Pg:dbname=foo root 4321
+
+ See also:
+   perldoc Catalyst::Manual
+   perldoc Catalyst::Manual::Intro
+
+=head1 DESCRIPTION
+
+Create a new Catalyst Component.
+
+Existing component files are not overwritten.  If any of the component files
+to be created already exist the file will be written with a '.new' suffix.
+This behavior can be suppressed with the C<-force> option.
+
+=head1 AUTHOR
+
+Sebastian Riedel, C<sri@oook.de>
+Maintained by the Catalyst Core Team.
+
+=head1 COPYRIGHT
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
Index: branches/1/script/vote_fastcgi.pl
===================================================================
--- branches/1/script/vote_fastcgi.pl	(revision 155)
+++ branches/1/script/vote_fastcgi.pl	(revision 155)
@@ -0,0 +1,80 @@
+#!/usr/bin/perl -w
+
+BEGIN { $ENV{CATALYST_ENGINE} ||= 'FastCGI' }
+
+use strict;
+use warnings;
+use Getopt::Long;
+use Pod::Usage;
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use Vote;
+
+my $help = 0;
+my ( $listen, $nproc, $pidfile, $manager, $detach, $keep_stderr );
+ 
+GetOptions(
+    'help|?'      => \$help,
+    'listen|l=s'  => \$listen,
+    'nproc|n=i'   => \$nproc,
+    'pidfile|p=s' => \$pidfile,
+    'manager|M=s' => \$manager,
+    'daemon|d'    => \$detach,
+    'keeperr|e'   => \$keep_stderr,
+);
+
+pod2usage(1) if $help;
+
+Vote->run( 
+    $listen, 
+    {   nproc   => $nproc,
+        pidfile => $pidfile, 
+        manager => $manager,
+        detach  => $detach,
+	keep_stderr => $keep_stderr,
+    }
+);
+
+1;
+
+=head1 NAME
+
+vote_fastcgi.pl - Catalyst FastCGI
+
+=head1 SYNOPSIS
+
+vote_fastcgi.pl [options]
+ 
+ Options:
+   -? -help      display this help and exits
+   -l -listen    Socket path to listen on
+                 (defaults to standard input)
+                 can be HOST:PORT, :PORT or a
+                 filesystem path
+   -n -nproc     specify number of processes to keep
+                 to serve requests (defaults to 1,
+                 requires -listen)
+   -p -pidfile   specify filename for pid file
+                 (requires -listen)
+   -d -daemon    daemonize (requires -listen)
+   -M -manager   specify alternate process manager
+                 (FCGI::ProcManager sub-class)
+                 or empty string to disable
+   -e -keeperr   send error messages to STDOUT, not
+                 to the webserver
+
+=head1 DESCRIPTION
+
+Run a Catalyst application as fastcgi.
+
+=head1 AUTHOR
+
+Sebastian Riedel, C<sri@oook.de>
+Maintained by the Catalyst Core Team.
+
+=head1 COPYRIGHT
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
Index: branches/1/script/vote_server.pl
===================================================================
--- branches/1/script/vote_server.pl	(revision 155)
+++ branches/1/script/vote_server.pl	(revision 155)
@@ -0,0 +1,111 @@
+#!/usr/bin/perl -w
+
+BEGIN { 
+    $ENV{CATALYST_ENGINE} ||= 'HTTP';
+    $ENV{CATALYST_SCRIPT_GEN} = 30;
+    require Catalyst::Engine::HTTP;
+}  
+
+use strict;
+use warnings;
+use Getopt::Long;
+use Pod::Usage;
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+
+my $debug             = 0;
+my $fork              = 0;
+my $help              = 0;
+my $host              = undef;
+my $port              = 3000;
+my $keepalive         = 0;
+my $restart           = 0;
+my $restart_delay     = 1;
+my $restart_regex     = '\.yml$|\.yaml$|\.pm$';
+my $restart_directory = undef;
+
+my @argv = @ARGV;
+
+GetOptions(
+    'debug|d'             => \$debug,
+    'fork'                => \$fork,
+    'help|?'              => \$help,
+    'host=s'              => \$host,
+    'port=s'              => \$port,
+    'keepalive|k'         => \$keepalive,
+    'restart|r'           => \$restart,
+    'restartdelay|rd=s'   => \$restart_delay,
+    'restartregex|rr=s'   => \$restart_regex,
+    'restartdirectory=s'  => \$restart_directory,
+);
+
+pod2usage(1) if $help;
+
+if ( $restart ) {
+    $ENV{CATALYST_ENGINE} = 'HTTP::Restarter';
+}
+if ( $debug ) {
+    $ENV{CATALYST_DEBUG} = 1;
+}
+
+# This is require instead of use so that the above environment
+# variables can be set at runtime.
+require Vote;
+
+Vote->run( $port, $host, {
+    argv              => \@argv,
+    'fork'            => $fork,
+    keepalive         => $keepalive,
+    restart           => $restart,
+    restart_delay     => $restart_delay,
+    restart_regex     => qr/$restart_regex/,
+    restart_directory => $restart_directory,
+} );
+
+1;
+
+=head1 NAME
+
+vote_server.pl - Catalyst Testserver
+
+=head1 SYNOPSIS
+
+vote_server.pl [options]
+
+ Options:
+   -d -debug          force debug mode
+   -f -fork           handle each request in a new process
+                      (defaults to false)
+   -? -help           display this help and exits
+      -host           host (defaults to all)
+   -p -port           port (defaults to 3000)
+   -k -keepalive      enable keep-alive connections
+   -r -restart        restart when files get modified
+                      (defaults to false)
+   -rd -restartdelay  delay between file checks
+   -rr -restartregex  regex match files that trigger
+                      a restart when modified
+                      (defaults to '\.yml$|\.yaml$|\.pm$')
+   -restartdirectory  the directory to search for
+                      modified files
+                      (defaults to '../')
+
+ See also:
+   perldoc Catalyst::Manual
+   perldoc Catalyst::Manual::Intro
+
+=head1 DESCRIPTION
+
+Run a Catalyst Testserver for this application.
+
+=head1 AUTHOR
+
+Sebastian Riedel, C<sri@oook.de>
+Maintained by the Catalyst Core Team.
+
+=head1 COPYRIGHT
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
Index: branches/1/script/vote_test.pl
===================================================================
--- branches/1/script/vote_test.pl	(revision 155)
+++ branches/1/script/vote_test.pl	(revision 155)
@@ -0,0 +1,54 @@
+#!/usr/bin/perl -w
+
+use strict;
+use warnings;
+use Getopt::Long;
+use Pod::Usage;
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use Catalyst::Test 'Vote';
+
+my $help = 0;
+
+GetOptions( 'help|?' => \$help );
+
+pod2usage(1) if ( $help || !$ARGV[0] );
+
+print request($ARGV[0])->content . "\n";
+
+1;
+
+=head1 NAME
+
+vote_test.pl - Catalyst Test
+
+=head1 SYNOPSIS
+
+vote_test.pl [options] uri
+
+ Options:
+   -help    display this help and exits
+
+ Examples:
+   vote_test.pl http://localhost/some_action
+   vote_test.pl /some_action
+
+ See also:
+   perldoc Catalyst::Manual
+   perldoc Catalyst::Manual::Intro
+
+=head1 DESCRIPTION
+
+Run a Catalyst action from the command line.
+
+=head1 AUTHOR
+
+Sebastian Riedel, C<sri@oook.de>
+Maintained by the Catalyst Core Team.
+
+=head1 COPYRIGHT
+
+This library is free software, you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
Index: branches/1/t/01app.t
===================================================================
--- branches/1/t/01app.t	(revision 155)
+++ branches/1/t/01app.t	(revision 155)
@@ -0,0 +1,7 @@
+use strict;
+use warnings;
+use Test::More tests => 2;
+
+BEGIN { use_ok 'Catalyst::Test', 'Vote' }
+
+ok( request('/')->is_success, 'Request should succeed' );
Index: branches/1/t/02pod.t
===================================================================
--- branches/1/t/02pod.t	(revision 155)
+++ branches/1/t/02pod.t	(revision 155)
@@ -0,0 +1,9 @@
+use strict;
+use warnings;
+use Test::More;
+
+eval "use Test::Pod 1.14";
+plan skip_all => 'Test::Pod 1.14 required' if $@;
+plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD};
+
+all_pod_files_ok();
Index: branches/1/t/03podcoverage.t
===================================================================
--- branches/1/t/03podcoverage.t	(revision 155)
+++ branches/1/t/03podcoverage.t	(revision 155)
@@ -0,0 +1,9 @@
+use strict;
+use warnings;
+use Test::More;
+
+eval "use Test::Pod::Coverage 1.04";
+plan skip_all => 'Test::Pod::Coverage 1.04 required' if $@;
+plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD};
+
+all_pod_coverage_ok();
Index: branches/1/t/controller_Admin.t
===================================================================
--- branches/1/t/controller_Admin.t	(revision 155)
+++ branches/1/t/controller_Admin.t	(revision 155)
@@ -0,0 +1,10 @@
+use strict;
+use warnings;
+use Test::More tests => 3;
+
+BEGIN { use_ok 'Catalyst::Test', 'Vote' }
+BEGIN { use_ok 'Vote::Controller::Admin' }
+
+ok( request('/admin')->is_success, 'Request should succeed' );
+
+
Index: branches/1/t/controller_Ballot.t
===================================================================
--- branches/1/t/controller_Ballot.t	(revision 155)
+++ branches/1/t/controller_Ballot.t	(revision 155)
@@ -0,0 +1,10 @@
+use strict;
+use warnings;
+use Test::More tests => 3;
+
+BEGIN { use_ok 'Catalyst::Test', 'Vote' }
+BEGIN { use_ok 'Vote::Controller::Ballot' }
+
+ok( request('/ballot')->is_success, 'Request should succeed' );
+
+
Index: branches/1/t/controller_Newpoll.t
===================================================================
--- branches/1/t/controller_Newpoll.t	(revision 155)
+++ branches/1/t/controller_Newpoll.t	(revision 155)
@@ -0,0 +1,10 @@
+use strict;
+use warnings;
+use Test::More tests => 3;
+
+BEGIN { use_ok 'Catalyst::Test', 'Vote' }
+BEGIN { use_ok 'Vote::Controller::Newpoll' }
+
+ok( request('/newpoll')->is_success, 'Request should succeed' );
+
+
Index: branches/1/t/controller_Vote.t
===================================================================
--- branches/1/t/controller_Vote.t	(revision 155)
+++ branches/1/t/controller_Vote.t	(revision 155)
@@ -0,0 +1,10 @@
+use strict;
+use warnings;
+use Test::More tests => 3;
+
+BEGIN { use_ok 'Catalyst::Test', 'Vote' }
+BEGIN { use_ok 'Vote::Controller::Vote' }
+
+ok( request('/vote')->is_success, 'Request should succeed' );
+
+
Index: branches/1/t/model_Vote.t
===================================================================
--- branches/1/t/model_Vote.t	(revision 155)
+++ branches/1/t/model_Vote.t	(revision 155)
@@ -0,0 +1,6 @@
+use strict;
+use warnings;
+use Test::More tests => 1;
+
+BEGIN { use_ok 'Vote::Model::Vote' }
+
Index: branches/1/t/view_TT.t
===================================================================
--- branches/1/t/view_TT.t	(revision 155)
+++ branches/1/t/view_TT.t	(revision 155)
@@ -0,0 +1,6 @@
+use strict;
+use warnings;
+use Test::More tests => 1;
+
+BEGIN { use_ok 'Vote::View::TT' }
+
