This commit is contained in:
Oskar Hahn 2011-07-31 10:46:29 +02:00
commit 3db6a12024
213 changed files with 17416 additions and 0 deletions

8
.hgignore Normal file
View File

@ -0,0 +1,8 @@
^.venv/
^openslides/.venv/
\.pyc$
^openslides/settings\.py$
^database\.db$
~$
\.DS_Store$
^docs/_build

7
AUTHORS Normal file
View File

@ -0,0 +1,7 @@
Authors of OpenSlides in alphabetical order:
Emanuel Schütze <emanuel@intevation.de>
Oskar Hahn <mail@oshahn.de>
Thanks:
Norman Jäckel, Leipzig

124
INSTALL Normal file
View File

@ -0,0 +1,124 @@
Installation Instructions for OpenSlides
========================================
2011-07-29
Installation on Windows (32/64bit)
----------------------------------
1. Install Python 2.7
- 32bit:
a) Download and run 32bit MSI installer (python-2.7.2.msi)
from http://www.python.org/download/
b) Add python dirs to PATH (via Control Panel > System > Advanced):
C:\Python27;C:\Python27\Scripts
- 64bit:
a) Download and run 64bit MSI installer (python-2.7.2.amd64.msi)
from http://www.python.org/download/
b) Add python dirs to PATH (via Control Panel > System > Advanced):
C:\Python27;C:\Python27\Scripts
2. Install Setuptools 0.6c11
- 32bit:
Download and run 32bit binary installer
(setuptools-0.6c11.win32-py2.7.exe)
from http://pypi.python.org/pypi/setuptools
- 64bit:
a) Download 64bit version (ez_setup.py)
from http://pypi.python.org/pypi/setuptools
b) Open cmd and run to install setuptools:
python ez_setup.py
3. Install django packages
Open cmd and run:
easy_install django django-model-utils
4. Install packages for building PDF (reportlab, PIL)
- 32bit:
Run on cmd:
easy_install reportlab pil
- 64bit:
a) Download and run Reportlab 64bit binary installer
(reportlab-2.5-win-amd64-py2.7.exe) from
http://www.reportlab.com/ftp/
b) Download and run PIL 64bit binary installer
(PIL-1.1.7.win-amd64-py2.7.exe) from
http://www.lfd.uci.edu/~gohlke/pythonlibs/
5. Get OpenSlides source code (e.g. via mercurial checkout)
hg clone ssh://hg@openslides.org/openslides)
6. Copy default.settings.py to settings.py
(in directory 'openslides')
7. Install OpenSlides database:
python manage.py syncdb
8. Run OpenSlides server:
python manage.py runserver
9. Open OpenSlides in your Browser:
http://127.0.0.1:8000/
Installation on Linux and MacOSX
--------------------------------
Make sure that you have python and virtualenv installed on your
system.
1. Setup virtualenv:
cd openslides
virtualenv .venv
. .venv/bin/activate
2. Install 'pip' if not available:
easy_install pip
3. Install django packages:
pip install django django-model-utils
4. Install packages for building PDF (reportlab, PIL):
pip install reportlab pil
5. Get OpenSlides source code (e.g. via mercurial checkout)
hg clone ssh://hg@openslides.org/openslides
6. Copy default.settings.py to settings.py
(in directory 'openslides')
7. Install OpenSlides database (and follow on screen instruction):
python manage.py syncdb
8. Run OpenSlides server:
python manage.py runserver
9. Open OpenSlides in your Browser:
http://127.0.0.1:8000/
--
If you need help ask on OpenSlides users mailinglist.
See www.openslides.org for more information.

339
LICENSE Normal file
View File

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

13
THANKS Normal file
View File

@ -0,0 +1,13 @@
THANKS file for OpenSlides
OpenSlides uses parts of the following projects:
* Oxygen-Icons
<http://www.oxygen-icons.org/>
* jQuery
<http://www.jquery.com>
* Drupal (tabledrag function)
<http://www.drupal.org/>

143
docs/Makefile Normal file
View File

@ -0,0 +1,143 @@
# Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = _build
LANGUAGES = en de
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees/$$lang $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) -c . -A language=$$lang -A languages='$(LANGUAGES)'
#ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees/$$lang $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) -c . -A language=$$lang -D language=$$lang -A target=$(TARGET) -A languages='$(LANGUAGES)'
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
help:
@echo "Please use \`make <target>' where <target> is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
@echo " gettext to make PO message catalogs"
clean:
-rm -rf $(BUILDDIR)/*
html:
@for lang in $(LANGUAGES);\
do \
mkdir -p $(BUILDDIR)/html/$$lang $(BUILDDIR)/doctrees/$$lang; \
echo "$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $$lang $(BUILDDIR)/html/$$lang";\
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $$lang $(BUILDDIR)/html/$$lang;\
done
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/OpenSlides.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/OpenSlides.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/OpenSlides"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/OpenSlides"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
make -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."

13
docs/README Normal file
View File

@ -0,0 +1,13 @@
The OpenSlides website based on Sphinx <http://sphinx.pocoo.org/>.
You have to install Sphinx before you can build the website.
To build the website into the ./_build directory use:
make html
To clean up your build directory use:
make clean
--
http://www.openslides.org

460
docs/_static/basic.css vendored Normal file
View File

@ -0,0 +1,460 @@
/*
* basic.css
* ~~~~~~~~~
*
* Sphinx stylesheet -- basic theme.
*
* :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
* :license: BSD, see LICENSE for details.
*
*/
/* -- relbar ---------------------------------------------------------------- */
div.related {
width: 100%;
font-size: 90%;
}
div.related h3 {
display: none;
}
div.related ul {
margin: 0;
padding: 0 0 0 10px;
list-style: none;
}
div.related li {
display: inline;
}
div.related li.right {
float: right;
margin-right: 5px;
}
/* -- search page ----------------------------------------------------------- */
ul.search {
margin: 10px 0 0 20px;
padding: 0;
}
ul.search li {
padding: 5px 0 5px 20px;
background-image: url(file.png);
background-repeat: no-repeat;
background-position: 0 7px;
}
ul.search li a {
font-weight: bold;
}
ul.search li div.context {
color: #888;
margin: 2px 0 0 30px;
text-align: left;
}
ul.keywordmatches li.goodmatch a {
font-weight: bold;
}
/* -- general index --------------------------------------------------------- */
table.indextable {
width: 100%;
}
table.indextable td {
text-align: left;
vertical-align: top;
}
table.indextable dl, table.indextable dd {
margin-top: 0;
margin-bottom: 0;
}
table.indextable tr.pcap {
height: 10px;
}
table.indextable tr.cap {
margin-top: 10px;
background-color: #f2f2f2;
}
img.toggler {
margin-right: 3px;
margin-top: 3px;
cursor: pointer;
}
div.modindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
div.genindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
/* -- general body styles --------------------------------------------------- */
a.headerlink {
visibility: hidden;
}
h1:hover > a.headerlink,
h2:hover > a.headerlink,
h3:hover > a.headerlink,
h4:hover > a.headerlink,
h5:hover > a.headerlink,
h6:hover > a.headerlink,
dt:hover > a.headerlink {
visibility: hidden;
}
div.body p.caption {
text-align: inherit;
}
div.body td {
text-align: left;
}
.field-list ul {
padding-left: 1em;
}
.first {
margin-top: 0 !important;
}
p.rubric {
margin-top: 30px;
font-weight: bold;
}
img.align-left, .figure.align-left, object.align-left {
clear: left;
float: left;
margin-right: 1em;
}
img.align-right, .figure.align-right, object.align-right {
clear: right;
float: right;
margin-left: 1em;
}
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left;
}
.align-center {
clear: both;
text-align: center;
}
.align-right {
text-align: right;
}
/* -- sidebars -------------------------------------------------------------- */
div.sidebar {
margin: 0 0 0.5em 1em;
border: 1px solid #ddb;
padding: 7px 7px 0 7px;
background-color: #ffe;
width: 40%;
float: right;
}
p.sidebar-title {
font-weight: bold;
}
/* -- topics ---------------------------------------------------------------- */
div.topic {
border: 1px solid #ccc;
padding: 7px 7px 0 7px;
margin: 10px 0 10px 0;
}
p.topic-title {
font-size: 1.1em;
font-weight: bold;
margin-top: 10px;
}
/* -- admonitions ----------------------------------------------------------- */
div.admonition {
margin-top: 10px;
margin-bottom: 10px;
padding: 7px;
}
div.admonition dt {
font-weight: bold;
}
div.admonition dl {
margin-bottom: 0;
}
p.admonition-title {
margin: 0px 10px 5px 0px;
font-weight: bold;
}
div.body p.centered {
text-align: center;
margin-top: 25px;
}
/* -- tables ---------------------------------------------------------------- */
table.docutils {
border: 0;
border-collapse: collapse;
}
table.docutils td, table.docutils th {
padding: 1px 8px 1px 5px;
border-top: 0;
border-left: 0;
border-right: 0;
border-bottom: 1px solid #aaa;
}
table.field-list td, table.field-list th {
border: 0 !important;
}
table.footnote td, table.footnote th {
border: 0 !important;
}
th {
text-align: left;
padding-right: 5px;
}
table.citation {
border-left: solid 1px gray;
margin-left: 1px;
}
table.citation td {
border-bottom: none;
}
/* -- other body styles ----------------------------------------------------- */
ol.arabic {
list-style: decimal;
}
ol.loweralpha {
list-style: lower-alpha;
}
ol.upperalpha {
list-style: upper-alpha;
}
ol.lowerroman {
list-style: lower-roman;
}
ol.upperroman {
list-style: upper-roman;
}
dl {
margin-bottom: 15px;
}
dd p {
margin-top: 0px;
}
dd ul, dd table {
margin-bottom: 10px;
}
dd {
margin-top: 3px;
margin-bottom: 10px;
margin-left: 30px;
}
dt:target, .highlighted {
background-color: #fbe54e;
}
dl.glossary dt {
font-weight: bold;
font-size: 1.1em;
}
.field-list ul {
margin: 0;
padding-left: 1em;
}
.field-list p {
margin: 0;
}
.refcount {
color: #060;
}
.optional {
font-size: 1.3em;
}
.versionmodified {
font-style: italic;
}
.system-message {
background-color: #fda;
padding: 5px;
border: 3px solid red;
}
.footnote:target {
background-color: #ffa;
}
.line-block {
display: block;
margin-top: 1em;
margin-bottom: 1em;
}
.line-block .line-block {
margin-top: 0;
margin-bottom: 0;
margin-left: 1.5em;
}
.guilabel, .menuselection {
font-family: sans-serif;
}
.accelerator {
text-decoration: underline;
}
.classifier {
font-style: oblique;
}
/* -- code displays --------------------------------------------------------- */
pre {
overflow: auto;
overflow-y: hidden; /* fixes display issues on Chrome browsers */
}
td.linenos pre {
padding: 5px 0px;
border: 0;
background-color: transparent;
color: #aaa;
}
table.highlighttable {
margin-left: 0.5em;
}
table.highlighttable td {
padding: 0 0.5em 0 0.5em;
}
tt.descname {
background-color: transparent;
font-weight: bold;
font-size: 1.2em;
}
tt.descclassname {
background-color: transparent;
}
tt.xref, a tt {
background-color: transparent;
font-weight: bold;
}
h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
background-color: transparent;
}
.viewcode-link {
float: right;
}
.viewcode-back {
float: right;
font-family: sans-serif;
}
div.viewcode-block:target {
margin: -1px -10px;
padding: 0 10px;
}
/* -- math display ---------------------------------------------------------- */
img.math {
vertical-align: middle;
}
div.body div.math p {
text-align: center;
}
span.eqno {
float: right;
}
/* -- printout stylesheet --------------------------------------------------- */
@media print {
div.document,
div.documentwrapper,
div.bodywrapper {
margin: 0 !important;
width: 100%;
}
div.sphinxsidebar,
div.related,
div.footer,
#top-link {
display: none;
}
}

424
docs/_static/default.css vendored Normal file
View File

@ -0,0 +1,424 @@
/*
OpenSlides CSS
@import url("basic.css");
*/
@import url(http://fonts.googleapis.com/css?family=Ubuntu);
body { font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', 'Verdana',
sans-serif; font-size: 13px; color: #000; }
a { color: #185F6D; border-bottom: 1px dotted #2BABC4; text-decoration: none; }
a:hover { color: #2794AA; border-bottom: 1px solid #2794AA; }
.box { width: 540px; margin: 20px auto; }
h1, h2, h3 { font-family: 'Ubuntu', 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
'Verdana', sans-serif; font-weight: normal; }
.header { height: 165px; }
.header h1 { margin: 0 0 30px 0; background: url(logo.png) no-repeat right;
font-size: 50px; font-weight: bold; padding-top: 50px; height: 120px; }
.header p { font-size: 15px; margin: -90px 0 0 0; }
h1 { font-size: 34px; margin: 25px 0 5px 0; }
h2 { font-size: 18px; margin: 25px 0 5px 0; }
h3 { font-size: 19px; margin: 25px 0 5px 0; }
textarea, code,
pre { font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono',
monospace!important; font-size: 15px; background: #E8EFF0; }
pre { padding: 7px 30px; margin: 15px -30px; line-height: 1.3; }
.ig { color: #888; }
p { line-height: 1.4; color:#444444;}
ul { margin: 15px 0 15px 0; padding: 0; list-style: none; line-height: 1.4; }
ul li:before { content: "\00BB \0020"; color: #888; position: absolute; margin-left: -19px; }
ol { line-height: 1.4; margin: 15px 0 15px 30px; padding: 0; }
blockquote { margin: 15px 0; font-style: italic; color: #444; }
.footer { font-size: 10px; color: #888; text-align: right; margin-top: 25px; }
.more { text-align: right; margin-top: 0; font-size: 0.9em; font-style: italic; }
.nav { text-align: left; margin: 0 0 0 0; }
table { border: 1px solid black; border-collapse: collapse;
margin: 15px 0; }
td, th { border: 1px solid black; padding: 4px 10px;
text-align: left; }
th { background: #eee; font-weight: normal; }
td input { border: none; padding: 0; }
/* latest version button */
.latestver { margin: 20px 0 0 0; float: right; font-style: italic; }
.latestver strong { font-weight: normal; }
div.document {
width: 940px;
margin: 30px auto 0 auto;
}
div.documentwrapper {
float: left;
width: 100%;
}
div.bodywrapper {
margin: 0 0 0 220px;
}
div.sphinxsidebar {
width: 220px;
}
hr {
border: 1px solid #B1B4B6;
}
div.body {
background-color: #ffffff;
color: #3E4349;
padding: 0 30px 0 30px;
}
img.floatingflask {
padding: 0 0 10px 10px;
float: right;
}
div.footer {
width: 940px;
margin: 20px auto 30px auto;
font-size: 14px;
color: #888;
text-align: right;
}
div.footer a {
color: #888;
}
div.related {
display: none;
}
div.sphinxsidebar a {
color: #444;
text-decoration: none;
border-bottom: 1px dotted #999;
}
div.sphinxsidebar a:hover {
border-bottom: 1px solid #999;
}
div.sphinxsidebar {
font-size: 13px;
line-height: 1.5;
}
div.sphinxsidebarwrapper {
padding: 18px 10px;
}
div.sphinxsidebarwrapper p.logo {
padding: 0 0 20px 0;
margin: 0;
text-align: center;
}
div.sphinxsidebar h3,
div.sphinxsidebar h4 {
font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', 'Verdana', sans-serif;
color: #444;
font-size: 24px;
font-weight: normal;
margin: 0 0 5px 0;
padding: 0;
}
div.sphinxsidebar h4 {
font-size: 20px;
}
div.sphinxsidebar h3 a {
color: #444;
}
div.sphinxsidebar p.logo a,
div.sphinxsidebar h3 a,
div.sphinxsidebar p.logo a:hover,
div.sphinxsidebar h3 a:hover {
border: none;
}
div.sphinxsidebar p {
color: #555;
margin: 10px 0;
}
div.sphinxsidebar ul {
margin: 10px 0;
padding: 0;
color: #000;
}
div.sphinxsidebar input {
border: 1px solid #ccc;
font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', 'Verdana', sans-serif;
font-size: 14px;
}
div.sphinxsidebar form.search input[name="q"] {
width: 130px;
}
/* -- body styles ----------------------------------------------------------- */
a {
color: #185F6D;
text-decoration: underline;
}
a:hover {
color: #2794AA;
text-decoration: underline;
}
div.body h1,
div.body h2,
div.body h3,
div.body h4,
div.body h5,
div.body h6 {
font-family: 'Ubuntu', 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', 'Verdana', sans-serif;
font-weight: normal;
margin: 30px 0px 10px 0px;
padding: 0;
color: black;
}
div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; }
div.body h2 { font-size: 180%; }
div.body h3 { font-size: 150%; }
div.body h4 { font-size: 130%; }
div.body h5 { font-size: 100%; }
div.body h6 { font-size: 100%; }
a.headerlink {
color: #ddd;
padding: 0 4px;
text-decoration: none;
}
a.headerlink {
visibility: hidden;
}
h1:hover > a.headerlink, h2:hover > a.headerlink, h3:hover > a.headerlink, h4:hover > a.headerlink, h5:hover > a.headerlink, h6:hover > a.headerlink, dt:hover > a.headerlink {
visibility: visible;
}
a.headerlink:hover {
color: #444;
background: #eaeaea;
}
div.body p, div.body dd, div.body li {
line-height: 1.4em;
}
div.admonition {
background: #fafafa;
margin: 20px -30px;
padding: 10px 30px;
border-top: 1px solid #ccc;
border-bottom: 1px solid #ccc;
}
div.admonition tt.xref, div.admonition a tt {
border-bottom: 1px solid #fafafa;
}
dd div.admonition {
margin-left: -60px;
padding-left: 60px;
}
div.admonition p.admonition-title {
font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', 'Verdana', sans-serif;
font-weight: normal;
font-size: 24px;
margin: 0 0 10px 0;
padding: 0;
line-height: 1;
}
div.admonition p.last {
margin-bottom: 0;
}
div.highlight {
background-color: white;
}
dt:target, .highlight {
background: #FAF3E8;
}
div.note {
background-color: #eee;
border: 1px solid #ccc;
}
div.seealso {
background-color: #ffc;
border: 1px solid #ff6;
}
div.topic {
background-color: #eee;
}
p.admonition-title {
display: inline;
}
p.admonition-title:after {
content: ":";
}
pre, tt {
font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
font-size: 0.9em;
}
img.screenshot {
}
tt.descname, tt.descclassname {
font-size: 0.95em;
}
tt.descname {
padding-right: 0.08em;
}
img.screenshot {
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils {
border: 1px solid #888;
-moz-box-shadow: 2px 2px 4px #eee;
-webkit-box-shadow: 2px 2px 4px #eee;
box-shadow: 2px 2px 4px #eee;
}
table.docutils td, table.docutils th {
border: 1px solid #888;
padding: 0.25em 0.7em;
}
table.field-list, table.footnote {
border: none;
-moz-box-shadow: none;
-webkit-box-shadow: none;
box-shadow: none;
}
table.footnote {
margin: 15px 0;
width: 100%;
border: 1px solid #eee;
background: #fdfdfd;
font-size: 0.9em;
}
table.footnote + table.footnote {
margin-top: -15px;
border-top: none;
}
table.field-list th {
padding: 0 0.8em 0 0;
}
table.field-list td {
padding: 0;
}
table.footnote td.label {
width: 0px;
padding: 0.3em 0 0.3em 0.5em;
}
table.footnote td {
padding: 0.3em 0.5em;
}
dl {
margin: 0;
padding: 0;
}
dl dd {
margin-left: 30px;
}
blockquote {
margin: 0 0 0 30px;
padding: 0;
}
ul, ol {
margin: 10px 0 10px 30px;
padding: 0;
}
pre {
background: #E8EFF0;
padding: 7px 30px;
margin: 15px -30px;
line-height: 1.3em;
}
dl pre, blockquote pre, li pre {
margin-left: -60px;
padding-left: 60px;
}
dl dl pre {
margin-left: -90px;
padding-left: 90px;
}
tt {
background-color: #E8EFF0;
color: #222;
/* padding: 1px 2px; */
}
tt.xref, a tt {
background-color: #E8EFF0;
border-bottom: 1px solid white;
}
a.reference {
text-decoration: none;
border-bottom: 1px dotted #2BABC4;
}
a.reference:hover {
border-bottom: 1px solid #2794AA;
}
a.footnote-reference {
text-decoration: none;
font-size: 0.7em;
vertical-align: top;
border-bottom: 1px dotted #004B6B;
}
a.footnote-reference:hover {
border-bottom: 1px solid #6D4100;
}
a:hover tt {
background: #EEE;
}

BIN
docs/_static/images/agenda-beamer_de.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
docs/_static/images/agenda-pdf_de.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
docs/_static/images/agenda_de.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
docs/_static/images/de.png vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 545 B

BIN
docs/_static/images/en.png vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 609 B

BIN
docs/_static/logo.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

50
docs/_templates/layout.html vendored Normal file
View File

@ -0,0 +1,50 @@
{% extends '!layout.html' %}
{%- block content %}
<div class="box">
<div class="language">
{%- for lng in languages.split(' ') %}
{%- if lng != language %}
<a href="{{ pathto('../'+lng+'/'+pagename) }}"><img src="{{ pathto('_static/images/'+lng+'.png', 1) }}" alt="{{ lng }}" title="{{ lng }}" border="0" width="18px" height="13px"/></a>
{%- endif %}
{%- endfor %}
</div>
<div class="header">
<h1><span>OpenSlides</span></h1>
<p>
{%- if language == 'de' %}
Tagesordnungs-Präsentation<br> mit Antrags- und Wahlsystem
{%- else %}
Agenda presentation<br> with application and voting system
{%- endif %}
</div>
<div class="nav">
{%- if language == 'de' %}
<a href="{{ pathto('index') }}">Home</a> |
<a href="about.html">Über</a> |
<a href="download.html">Download</a> |
<a href="http://trac.openslides.org">Entwicklung</a> |
<a href="help.html">Hilfe</a>
{%- else %}
<a href="{{ pathto('index') }}">Home</a> |
<a href="about.html">About</a> |
<a href="download.html">Download</a> |
<a href="http://trac.openslides.org">Development</a> |
<a href="help.html">Help</a>
{%- endif %}
</div>
{% block body %} {% endblock %}
<p class="footer">
&copy; 2011 | OpenSlides is licensed under GPLv2+.
</p>
</div>
{%- endblock %}
{% block relbar2 %}{% endblock %}
{% block footer %} {% endblock %}

8
docs/_templates/sidebarintro.html vendored Normal file
View File

@ -0,0 +1,8 @@
<h3>About</h3>
<p>
OpenSlides is a agenda tool with...
</p>
<h3>Demo</h3>
<p>
Try out the <a href="#">OpenSlides demo</a>!
</p>

231
docs/conf.py Normal file
View File

@ -0,0 +1,231 @@
# -*- coding: utf-8 -*-
#
# OpenSlides documentation build configuration file, created by
# sphinx-quickstart on Wed Jun 8 20:38:19 2011.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import sys, os
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#sys.path.insert(0, os.path.abspath('.'))
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = []
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'OpenSlides'
copyright = u'2011 OpenSlides '
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = '1.0'
# The full version, including alpha/beta/rc tags.
release = '1.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#languages = 'en'
#locale_dirs = 'locale/'
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build']
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# The name for this set of Sphinx documents. If None, it defaults to
# "<project> v<release> documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
#html_sidebars = {
# 'index': ['sidebarlogo.html', 'sidebarintro.html', 'searchbox.html'],
# '**': ['sidebarlogo.html', 'localtoc.html', 'relations.html',
# 'searchbox.html']
#}
html_sidebars = {
'**': ['menu_en.html'],
'de/about': ['menu_de.html'],
}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
#html_additional_pages = {'de/about': 'about_de.html'}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a <link> tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'OpenSlidesdoc'
# -- Options for LaTeX output --------------------------------------------------
# The paper size ('letter' or 'a4').
#latex_paper_size = 'letter'
# The font size ('10pt', '11pt' or '12pt').
#latex_font_size = '10pt'
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'OpenSlides.tex', u'OpenSlides Documentation',
u'OpenSlides Team', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Additional stuff for the LaTeX preamble.
#latex_preamble = ''
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'openslides', u'OpenSlides Documentation',
[u'OpenSlides Team'], 1)
]
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'http://docs.python.org/': None}

136
docs/de/about.rst Normal file
View File

@ -0,0 +1,136 @@
Über OpenSlides
===============
Was ist OpenSlides?
------------------
OpenSlides ist ein freies, webbasiertes Präsentationssystem für Tagesordnung, Anträge, Abtimmungen und Wahlen.
Über ein Webinterface lässt sich das Beamerbild mit der aktuellen Tagesordnung steuern. Änderungen an der Tagesordnung werden unmittelbar auf dem Beamer dargestellt.
Darüber hinaus können sich die Teilnehmer einer Veranstaltung mit ihrem Laptop/Smartphone an OpenSlides anmelden und so z.B. Anträge (elektronisch) einreichen, für eine Wahl kandidieren, die Tagesordnung verfolgen, Abstimmungsergebnisse nachlesen u.v.m.
OpenSlides ist konzipiert für den Einsatz auf Veranstaltungen wie
Hauptversammlungen, Delegierten-/Mitgliederversammlungen und Parteitagen.
Der entscheidene Vorteil gegenüber herkömmlichen Office-Präsentations-Systemen, wie MS PowerPoint oder OpenOffice Impress, ist die Bearbeitung der Folien über ein Redaktionssystem-System. Der Präsentationsmodus auf dem Beamer muss nicht verlassen werden.
Systemanforderungen
-------------------
- Django 1.3+
- Python 2.5+
- Webbrowser
Lizenz
------
OpenSlides ist Freie Software und steht unter der **GNU General Public License (GNU GPL)** Version 2+. Die Software darf ohne Restriktionen benutzt, verändert und (geändert) weitergegeben werden.
Eine Kopie der Lizenz liegt jedem OpenSlides-Release bei und ist auch im Quellcode-Repository nachzulesen.
Funktionen
==========
Tagesordnung
------------
- Tagesordnung verwalten
- Eintrag auswählen zur Anzeige am Beamer
- Beamer-Ansicht mit automatischer Aktualisierung bei Änderung (der Beamer läuft z.B. im Browser mit Vollbildmodus auf einem 2. Monitor)
- Eintrag während der Anzeige am Beamer im Webinterface bearbeiten
- Eintrag per Drag&Drop in der Tagesordnung sortieren (Unterpunkte möglich)
- Eintrag als erledigt markieren
- Eintrag auf der Beamer-Ansicht verstecken
- Tagesordnung als pdf erzeugen
- aktuelle Uhrzeit auf der Beameransicht
.. image:: _static/images/agenda_de.png
:width: 45%
:alt: Tagesordnungs-Ansicht
.. image:: _static/images/agenda-beamer_de.png
:width: 45%
:alt: Beamer-Ansicht
.. image:: _static/images/agenda-new-item_de.png
:width: 45%
:alt: Neuen Tagesordnungseintrag anlegen
.. image:: _static/images/agenda-pdf_de.png
:width: 45%
:alt: Tagesordnung als PDF
Anträge
-------
- Anträge anlegen, bearbeiten, löschen
- Anträge von anderen Teilnehmern unterstützen lassen
- Antragsstatus ändern und Abstimmungsergebnisse eingeben
- Abstimmungsergebenisse aus mehreren Wahlgängen darstellen
- Antrag als Tagesordnungseintrag anlegen und anzeigen
- Änderungshistorie eines Antrags anzeigen
- Antragsformular als pdf erzeugen
- Übersicht aller Anträge als pdf
.. image:: _static/images/application-overview_de.png
:width: 45%
:alt: Antragsübersicht
.. image:: _static/images/application-overview-beamer_de.png
:width: 45%
:alt: Beamer-Ansicht Antragsübersicht
.. image:: _static/images/application-view_de.png
:width: 45%
:alt: Darstellung eines Antrags mit Verwaltugsfunktion
.. image:: _static/images/application-view-beamer_de.png
:width: 45%
:alt: Beamer-Ansicht eines einzelnen Antrags
Wahlen
------
- Kandidaten aus Teilnehmerliste für eine Wahl vorschlagen (bzw. als angemeldeter Teilnehmer selbst kandidieren)
- Wahlschein als pdf generieren (mit Ankreuzfeld)
- Wahlergebenisse eingeben und darstellen
- mehrere Wahlgänge und Stichwahlen werden unterstützt
Abstimmungen
------------
- Abstimmungen verwalten (als Ergänzung zu Anträgen und Wahlen)
- Ergebnisse eingeben und darstellen (analog zu Anträgen und Wahlen)
- Abstimmungsmodus: entweder nur Ja-Stimmen oder Ja/Nein/Enthaltungs-Stimmen wählbar
- ungültige und abgegebene Stimmen können eingegeben werden
Teilnehmer
----------
- Teilnehmer anlegen und verwalten (vordefinierte Felder: *Name, Vorname, E-Mail, Geschlecht, Gruppe, Typ, Amt*)
- importieren von Teilnehmerdaten (im CSV-Format)
- Benutzergruppe frei konfigurierbar
Allgemein
---------
- Template für Beamer und Webinterface leicht per HTML und CSS anpassbar
- OpenSlides ist Freie Software (`GPL v2+ Lizenz <about.html#lizenz>`_)
- Plattformunabhängig (läuft überall dort, wo Pyhton läuft)
- vollständige deutsche und englische Übersetzung vorhanden, weitere Sprachen möglich
Ausblick -- Weiterentwicklungsideen für OpenSlides:
---------------------------------------------------
- schnelle und einfache Erstellung eines Ergebnisprotokolls (mit allen Tagesordnungseinträgen, Beschlüssen, Anträgen, Abstimmungen und Wahlergbnissen)
- grafische Darstellung der Wahl- und Abstimmungsergebnisse in Diagrammen
- Einbindung von Grafiken in Tagesordnungseinträge
- Anbindung eines elektronischen Voting-Systems
Interesse an der Weiterentwicklung von OpenSlides? Wir freuen uns über jede Mithilfe!

13
docs/de/download.rst Normal file
View File

@ -0,0 +1,13 @@
Download
========
Es ist noch kein Release von OpenSlides verfügbar.
Das Erscheinen von Version 1.0 ist für Sommer 2011 geplant.
Der aktuelle Entwicklungsstand kann im öffentlichen
Quellcode-Repository eingesehen werden.
Quellcode von OpenSlides auschecken::
hg clone http://hg.openslides.org

40
docs/de/help.rst Normal file
View File

@ -0,0 +1,40 @@
Hilfe
=====
Mailinglisten
--------------
OpenSlides hat zwei öffentliche Mailinglisten (in deutscher und englischer Sprache)
zur Koordination der Entwicklung und Diskussion von Tickets, Nutzerfragen
oder speziellen Anwendungsfällen.
Bitte abbonieren Sie eine Mailingliste bevor Sie an diese schreiben wollen.
Deutsch::
user-de@openslides.org
English::
user@openslides.org
Probleme berichten
------------------
Bitte nutzen Sie unser `Ticketsystem <http://trac.openslides.org/report/3>`_
um Problem mit OpenSlides zu berichten.
Möchten Sie zu OpenSlides beitragen?
------------------------------------
OpenSlides ist eine engagierte Freie Software-Initiative von Freiwilligen und
freut sich über Mithilfe!
Arbeiten am Quellcode, Übersetzungen oder Grafikdesign - wir freuen uns über
jede Form der Unterstützung!

12
docs/de/index.rst Normal file
View File

@ -0,0 +1,12 @@
Willkommen
==========
OpenSlides ist ein freies, webbasiertes Präsentationssystem
zur Darstellung und Steuerung von Tagesordnungen, Anträgen, Abstimmungen und Wahlen.
Erfahren Sie mehr über die :doc:`Funktionen <about>` von OpenSlides.
.. image:: _static/images/agenda_de.png
:width: 90%
:alt: Tagesordnungs-Ansicht von OpenSlides

140
docs/en/about.rst Normal file
View File

@ -0,0 +1,140 @@
About OpenSlides
================
What is OpenSlides?
------------------
OpenSlides ist ein freies, webbasiertes Präsentationssystem für Tagesordnung, Anträge, Abtimmungen und Wahlen.
Über ein Webinterface lässt sich das Beamerbild mit der aktuellen Tagesordnung steuern. Änderungen an der Tagesordnung werden unmittelbar auf dem Beamer dargestellt.
Darüber hinaus können sich die Teilnehmer einer Veranstaltung mit ihrem Laptop/Smartphone an OpenSlides anmelden und so z.B. Anträge (elektronisch) einreichen, für eine Wahl kandidieren, die Tagesordnung verfolgen, Abstimmungsergebnisse nachlesen u.v.m.
OpenSlides ist konzipiert für den Einsatz auf Veranstaltungen wie
Hauptversammlungen, Delegierten-/Mitgliederversammlungen und Parteitagen.
Der entscheidene Vorteil gegenüber herkömmlichen Office-Präsentations-Systemen, wie MS PowerPoint oder OpenOffice Impress, ist die Bearbeitung der Folien über ein Redaktionssystem-System. Der Präsentationsmodus auf dem Beamer muss nicht verlassen werden.
Systemanforderungen
-------------------
- Django 1.3+
- Python 2.5+
- Webbrowser
License
-------
OpenSlides is Free Software licensed under the GNU General Public License v2+ (GNU GPL).
The software is free to use without restrictions, may be modified and that
modifications may be distributed.
A copy of the license is included with every release of OpenSlides, but you can
also read the text of the license in the source code repository.
Funktionen
==========
Tagesordnung
------------
- Tagesordnung verwalten
- Eintrag auswählen zur Anzeige am Beamer
- Beamer-Ansicht mit automatischer Aktualisierung bei Änderung (der Beamer läuft z.B. im Browser mit Vollbildmodus auf einem 2. Monitor)
- Eintrag während der Anzeige am Beamer im Webinterface bearbeiten
- Eintrag per Drag&Drop in der Tagesordnung sortieren (Unterpunkte möglich)
- Eintrag als erledigt markieren
- Eintrag auf der Beamer-Ansicht verstecken
- Tagesordnung als pdf erzeugen
- aktuelle Uhrzeit auf der Beameransicht
.. image:: _static/images/agenda_de.png
:width: 45%
:alt: Tagesordnungs-Ansicht
.. image:: _static/images/agenda-beamer_de.png
:width: 45%
:alt: Beamer-Ansicht
.. image:: _static/images/agenda-new-item_de.png
:width: 45%
:alt: Neuen Tagesordnungseintrag anlegen
.. image:: _static/images/agenda-pdf_de.png
:width: 45%
:alt: Tagesordnung als PDF
Anträge
-------
- Anträge anlegen, bearbeiten, löschen
- Anträge von anderen Teilnehmern unterstützen lassen
- Antragsstatus ändern und Abstimmungsergebnisse eingeben
- Abstimmungsergebenisse aus mehreren Wahlgängen darstellen
- Antrag als Tagesordnungseintrag anlegen und anzeigen
- Änderungshistorie eines Antrags anzeigen
- Antragsformular als pdf erzeugen
- Übersicht aller Anträge als pdf
.. image:: _static/images/application-overview_de.png
:width: 45%
:alt: Antragsübersicht
.. image:: _static/images/application-overview-beamer_de.png
:width: 45%
:alt: Beamer-Ansicht Antragsübersicht
.. image:: _static/images/application-view_de.png
:width: 45%
:alt: Darstellung eines Antrags mit Verwaltugsfunktion
.. image:: _static/images/application-view-beamer_de.png
:width: 45%
:alt: Beamer-Ansicht eines einzelnen Antrags
Wahlen
------
- Kandidaten aus Teilnehmerliste für eine Wahl vorschlagen (bzw. als angemeldeter Teilnehmer selbst kandidieren)
- Wahlschein als pdf generieren (mit Ankreuzfeld)
- Wahlergebenisse eingeben und darstellen
- mehrere Wahlgänge und Stichwahlen werden unterstützt
Abstimmungen
------------
- Abstimmungen verwalten (als Ergänzung zu Anträgen und Wahlen)
- Ergebnisse eingeben und darstellen (analog zu Anträgen und Wahlen)
- Abstimmungsmodus: entweder nur Ja-Stimmen oder Ja/Nein/Enthaltungs-Stimmen wählbar
- ungültige und abgegebene Stimmen können eingegeben werden
Teilnehmer
----------
- Teilnehmer anlegen und verwalten (vordefinierte Felder: *Name, Vorname, E-Mail, Geschlecht, Gruppe, Typ, Amt*)
- importieren von Teilnehmerdaten (im CSV-Format)
- Benutzergruppe frei konfigurierbar
Allgemein
---------
- Template für Beamer und Webinterface leicht per HTML und CSS anpassbar
- OpenSlides ist Freie Software (`GPL v2+ Lizenz <about.html#lizenz>`_)
- Plattformunabhängig (läuft überall dort, wo Pyhton läuft)
- vollständige deutsche und englische Übersetzung vorhanden, weitere Sprachen möglich
Ausblick -- Weiterentwicklungsideen für OpenSlides:
---------------------------------------------------
- schnelle und einfache Erstellung eines Ergebnisprotokolls (mit allen Tagesordnungseinträgen, Beschlüssen, Anträgen, Abstimmungen und Wahlergbnissen)
- grafische Darstellung der Wahl- und Abstimmungsergebnisse in Diagrammen
- Einbindung von Grafiken in Tagesordnungseinträge
- Anbindung eines elektronischen Voting-Systems
Interesse an der Weiterentwicklung von OpenSlides? Wir freuen uns über jede Mithilfe!

12
docs/en/download.rst Normal file
View File

@ -0,0 +1,12 @@
Download
========
There is not yet a release of OpenSlides.
Version 1.0 is scheduled for summer 2011.
You can check the current development status in our
public sourcecode repository.
Checkout OpenSlides sourcecode::
hg clone http://hg.openslides.org

34
docs/en/help.rst Normal file
View File

@ -0,0 +1,34 @@
Help
====
Mailing lists
-------------
OpenSlides has two public mailing lists in English and German language
for coordinate the development and discussing tickets, user questions or
special use cases. Please subscribe before you are sending an email to a list.
English language::
user@openslides.org
German language::
user-de@openslides.org
Bug reports
-----------
Please use our `ticket system <http://trac.openslides.org/report/3>`_
to report bugs for OpenSlides.
Want to contribute to OpenSlides?
---------------------------------
OpenSlides is an open-source project driven by volunteers.
From code to localizations or artwork, any contribution is welcome!

12
docs/en/index.rst Normal file
View File

@ -0,0 +1,12 @@
Welcome
=======
OpenSlides is a free, webbased presentation system
for agenda, application, polls and elections.
Read more about the :doc:`features <about>` of OpenSlides.
.. image:: _static/images/agenda_de.png
:width: 90%
:alt: Agenda view of OpenSlides

170
docs/make.bat Normal file
View File

@ -0,0 +1,170 @@
@ECHO OFF
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set BUILDDIR=_build
set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
if NOT "%PAPER%" == "" (
set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
)
if "%1" == "" goto help
if "%1" == "help" (
:help
echo.Please use `make ^<target^>` where ^<target^> is one of
echo. html to make standalone HTML files
echo. dirhtml to make HTML files named index.html in directories
echo. singlehtml to make a single large HTML file
echo. pickle to make pickle files
echo. json to make JSON files
echo. htmlhelp to make HTML files and a HTML help project
echo. qthelp to make HTML files and a qthelp project
echo. devhelp to make HTML files and a Devhelp project
echo. epub to make an epub
echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
echo. text to make text files
echo. man to make manual pages
echo. changes to make an overview over all changed/added/deprecated items
echo. linkcheck to check all external links for integrity
echo. doctest to run all doctests embedded in the documentation if enabled
goto end
)
if "%1" == "clean" (
for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
del /q /s %BUILDDIR%\*
goto end
)
if "%1" == "html" (
%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/html.
goto end
)
if "%1" == "dirhtml" (
%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
goto end
)
if "%1" == "singlehtml" (
%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
goto end
)
if "%1" == "pickle" (
%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the pickle files.
goto end
)
if "%1" == "json" (
%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can process the JSON files.
goto end
)
if "%1" == "htmlhelp" (
%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run HTML Help Workshop with the ^
.hhp project file in %BUILDDIR%/htmlhelp.
goto end
)
if "%1" == "qthelp" (
%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished; now you can run "qcollectiongenerator" with the ^
.qhcp project file in %BUILDDIR%/qthelp, like this:
echo.^> qcollectiongenerator %BUILDDIR%\qthelp\OpenSlides.qhcp
echo.To view the help file:
echo.^> assistant -collectionFile %BUILDDIR%\qthelp\OpenSlides.ghc
goto end
)
if "%1" == "devhelp" (
%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
if errorlevel 1 exit /b 1
echo.
echo.Build finished.
goto end
)
if "%1" == "epub" (
%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The epub file is in %BUILDDIR%/epub.
goto end
)
if "%1" == "latex" (
%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
if errorlevel 1 exit /b 1
echo.
echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
goto end
)
if "%1" == "text" (
%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The text files are in %BUILDDIR%/text.
goto end
)
if "%1" == "man" (
%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
if errorlevel 1 exit /b 1
echo.
echo.Build finished. The manual pages are in %BUILDDIR%/man.
goto end
)
if "%1" == "changes" (
%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
if errorlevel 1 exit /b 1
echo.
echo.The overview file is in %BUILDDIR%/changes.
goto end
)
if "%1" == "linkcheck" (
%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
if errorlevel 1 exit /b 1
echo.
echo.Link check complete; look for any errors in the above output ^
or in %BUILDDIR%/linkcheck/output.txt.
goto end
)
if "%1" == "doctest" (
%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
if errorlevel 1 exit /b 1
echo.
echo.Testing of doctests in the sources finished, look at the ^
results in %BUILDDIR%/doctest/output.txt.
goto end
)
:end

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 70 KiB

294
initial_data.json Normal file
View File

@ -0,0 +1,294 @@
[
{
"pk":1,
"model":"agenda.item",
"fields":{
"weight":0,
"parent":null,
"title":"TOP 1 - Begr\u00fc\u00dfung",
"hidden":false,
"real_type":10,
"closed":false
}
},
{
"pk":2,
"model":"agenda.item",
"fields":{
"weight":0,
"parent":null,
"title":"TOP 23 - Debatten \u00fcber IT",
"hidden":false,
"real_type":10,
"closed":false
}
},
{
"pk":1,
"model":"agenda.itemtext",
"fields":{
"text":"Wir begr\u00fc\u00dfen euch herzlich."
}
},
{
"pk":2,
"model":"agenda.itemtext",
"fields":{
"text":"Hier debattieren wir \u00fcber IT-Sachen."
}
},
{
"pk":1,
"model":"participant.profile",
"fields":{
"gender":"none",
"group":"",
"user":2,
"committee":"",
"type":"observer"
}
},
{
"pk":2,
"model":"participant.profile",
"fields":{
"gender":"none",
"group":"",
"user":3,
"committee":"",
"type":"delegate"
}
},
{
"pk":3,
"model":"participant.profile",
"fields":{
"gender":"none",
"group":"",
"user":4,
"committee":"",
"type":"staff"
}
},
{
"pk":1,
"model":"auth.group",
"fields":{
"name":"Teilnehmer (unangemeldet)",
"permissions":[
28,
55,
74,
75,
73,
50
]
}
},
{
"pk":2,
"model":"auth.group",
"fields":{
"name":"Teilnehmer (angemeldet) = Beobachter, Berichterstatter, GS",
"permissions":[
28,
56,
55,
74,
75,
73,
50
]
}
},
{
"pk":3,
"model":"auth.group",
"fields":{
"name":"Delegierte",
"permissions":[
28,
56,
57,
55,
74,
75,
73,
50
]
}
},
{
"pk":4,
"model":"auth.group",
"fields":{
"name":"Tagesleitung",
"permissions":[
29,
30,
28,
56,
58,
55,
76,
74,
75,
73,
50,
66,
65
]
}
},
{
"pk":5,
"model":"auth.group",
"fields":{
"name":"Infopoint (Teilnehmerverwaltung)",
"permissions":[
30,
28,
55,
74,
75,
73,
51,
50
]
}
},
{
"pk":6,
"model":"auth.group",
"fields":{
"name":"Pr\u00e4sidium",
"permissions":[
29,
30,
28,
56,
58,
55,
76,
74,
75,
73,
50,
51,
66,
65
]
}
},
{
"pk":7,
"model":"auth.group",
"fields":{
"name":"Techniker",
"permissions":[
29,
30,
28,
58,
55,
76,
74,
75,
73,
51,
50,
66,
65,
46
]
}
},
{
"pk":1,
"model":"auth.user",
"fields":{
"username":"admin",
"first_name":"",
"last_name":"",
"is_active":true,
"is_superuser":true,
"is_staff":true,
"last_login":"2011-03-24 15:30:36",
"groups":[
],
"user_permissions":[
],
"password":"sha1$40ca1$d91a0ff8e571d6577fe5de241717ac1e1da94596",
"email":"admin@oshahn.de",
"date_joined":"2011-03-13 08:56:04"
}
},
{
"pk":2,
"model":"auth.user",
"fields":{
"username":"MusterBeobachter",
"first_name":"Muster",
"last_name":"Beobachter",
"is_active":true,
"is_superuser":false,
"is_staff":false,
"last_login":"2011-03-24 15:42:50",
"groups":[
2
],
"user_permissions":[
],
"password":"sha1$3eccf$7b61f1f1a0fd19f003b80aa653d9ba55a22d2db7",
"email":"",
"date_joined":"2011-03-24 15:42:50"
}
},
{
"pk":3,
"model":"auth.user",
"fields":{
"username":"MusterDelegierter",
"first_name":"Muster",
"last_name":"Delegierter",
"is_active":true,
"is_superuser":false,
"is_staff":false,
"last_login":"2011-03-24 15:43:57",
"groups":[
3
],
"user_permissions":[
],
"password":"sha1$99b44$ca83292683582654fa55c3ce833ed9c914e9c127",
"email":"",
"date_joined":"2011-03-24 15:43:57"
}
},
{
"pk":4,
"model":"auth.user",
"fields":{
"username":"MusterPr\u00e4sidium",
"first_name":"Muster",
"last_name":"Pr\u00e4sidium",
"is_active":true,
"is_superuser":false,
"is_staff":false,
"last_login":"2011-03-24 15:45:12",
"groups":[
6
],
"user_permissions":[
],
"password":"sha1$d4a27$d8026e4e1169f7ae60dcb8372850f7218fb2b1dd",
"email":"",
"date_joined":"2011-03-24 15:45:12"
}
}
]

29
manage.py Normal file
View File

@ -0,0 +1,29 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.manage
~~~~~~~~~~~~~~~~~
Django's execute manager.
:copyright: 2011 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from django.core.management import execute_manager
import os, site
SITE_ROOT = os.path.realpath(os.path.dirname(__file__))
site.addsitedir(os.path.join(SITE_ROOT, 'openslides'))
try:
from openslides import settings
except ImportError:
import sys
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
sys.exit(1)
if __name__ == "__main__":
execute_manager(settings)

50
openslides/__init__.py Normal file
View File

@ -0,0 +1,50 @@
import socket
import os
import sys
from os.path import realpath, join, dirname
try:
from mercurial import ui as hgui
from mercurial.localrepo import localrepository
from mercurial.node import short as shorthex
from mercurial.error import RepoError
nomercurial = False
except:
nomercurial = True
from django.template import add_to_builtins
add_to_builtins('django.templatetags.i18n')
OPENSLIDES_REVISION = 'unknown'
# Don't read ~/.hgrc, as extensions aren't available in the venvs
os.environ['HGRCPATH'] = ''
def _bootstrap():
conts = realpath(join(dirname(__file__)))
try:
ui = hgui.ui()
repository = localrepository(ui, join(conts, '..'))
#repository = localrepository(ui, conts)
ctx = repository['tip']
revision = '%(num)s:%(id)s' % {
'num': ctx.rev(), 'id': shorthex(ctx.node())
}
except TypeError:
revision = 'unknown'
except RepoError:
return 0
# This value defines the timeout for sockets in seconds. Per default python
# sockets do never timeout and as such we have blocking workers.
# Socket timeouts are set globally within the whole application.
# The value *must* be a floating point value.
socket.setdefaulttimeout(10.0)
return revision
if not nomercurial:
OPENSLIDES_REVISION = _bootstrap()
del _bootstrap

View File

View File

@ -0,0 +1,18 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.agenda.admin
~~~~~~~~~~~~~~~~~~~~~~~
Register app for admin site.
:copyright: 2011 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from django.contrib import admin
from openslides.agenda.models import Item, ItemText
admin.site.register(Item)
admin.site.register(ItemText)

55
openslides/agenda/api.py Normal file
View File

@ -0,0 +1,55 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.agenda.api
~~~~~~~~~~~~~~~~~~~~~
Useful functions for the agenda app.
:copyright: 2011 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from openslides.system.api import config_get
def get_active_item(only_id=False):
"""
Returns the active Item. If no item is active, or it can not find an Item,
it raise Item.DoesNotExist
if only_id is True, returns only the id of this item. Returns None if not Item
is active. Does not Raise Item.DoesNotExist
"""
from agenda.models import Item
id = config_get("presentation", None)
if only_id:
if id is None:
return None
return int(id)
return Item.objects.get(pk=id)
def is_summary():
"""
True, if a summery shall be displayed
"""
from agenda.models import Item
try:
get_active_item()
except Item.DoesNotExist:
return True
if config_get('summary', False):
return True
return False
def children_list(items):
"""
Return a list for items with all childitems in the right order.
"""
l = []
for item in items:
l.append(item)
if item.children:
l += children_list(item.children)
return l

View File

@ -0,0 +1,82 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.agenda.forms
~~~~~~~~~~~~~~~~~~~~~~~
Forms for the agenda app.
:copyright: 2011 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from django.forms import Form, ModelForm, IntegerField, ChoiceField, \
ModelChoiceField, HiddenInput, Select
from django.utils.translation import ugettext as _
from openslides.agenda.models import Item, ItemText, ItemApplication, ItemPoll, \
ItemAssignment
class ItemFormText(ModelForm):
error_css_class = 'error'
required_css_class = 'required'
items = Item.objects.all().filter(parent=None).order_by('weight')
parent = ModelChoiceField(queryset=items, label=_("Parent item"))
class Meta:
model = ItemText
exclude = ('closed')
class ItemFormApplication(ModelForm):
error_css_class = 'error'
required_css_class = 'required'
items = Item.objects.all().filter(parent=None).order_by('weight')
parent = ModelChoiceField(queryset=items, label=_("Parent item"))
class Meta:
model = ItemApplication
exclude = ('closed')
class ItemFormPoll(ModelForm):
error_css_class = 'error'
required_css_class = 'required'
items = Item.objects.all().filter(parent=None).order_by('weight')
parent = ModelChoiceField(queryset=items, label=_("Parent item"))
class Meta:
model = ItemPoll
exclude = ('closed')
class ItemFormAssignment(ModelForm):
error_css_class = 'error'
required_css_class = 'required'
items = Item.objects.all().filter(parent=None).order_by('weight')
parent = ModelChoiceField(queryset=items, label=_("Parent item"))
class Meta:
model = ItemAssignment
exclude = ('closed')
def genweightchoices():
l = []
for i in range(-50, 51):
l.append(('%d' % i, i))
return l
class ElementOrderForm(Form):
weight = ChoiceField(choices=genweightchoices(), \
widget=Select(attrs={'class': 'menu-weight'}),
label="")
self = IntegerField(widget=HiddenInput(attrs={'class': 'menu-mlid'}))
parent = IntegerField(widget=HiddenInput(attrs={'class': 'menu-plid'}))
MODELFORM = {
'ItemText': ItemFormText,
'ItemApplication': ItemFormApplication,
'ItemPoll': ItemFormPoll,
'ItemAssignment': ItemFormAssignment,
}

198
openslides/agenda/models.py Normal file
View File

@ -0,0 +1,198 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.agenda.models
~~~~~~~~~~~~~~~~~~~~~~~~
Models for the agenda app.
:copyright: 2011 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from django.db import models
from django.utils.translation import ugettext as _
from model_utils.models import InheritanceCastModel
from openslides.agenda.api import get_active_item
from openslides.system.api import config_set
from openslides.application.models import Application
from openslides.poll.models import Poll
from openslides.assignment.models import Assignment
class Item(InheritanceCastModel):
"""
The BasisItem.
Has all the attributes all Items need.
"""
title = models.CharField(max_length=100, verbose_name=_("Title"))
closed = models.BooleanField(default=False, verbose_name=_("Closed"))
weight = models.IntegerField(default=0, verbose_name=_("Weight"))
parent = models.ForeignKey('self', blank=True, null=True)
hidden = models.BooleanField(default=False,
verbose_name=_("Hidden (visible for agenda manager only)"))
@property
def active(self):
"""
Return True, if the the item is the active one.
"""
return True if get_active_item(only_id=True) == self.id else False
@property
def active_parent(self):
"""
Return True if the item has a activ parent
"""
if get_active_item(only_id=True) in \
[parent.id for parent in self.parents]:
return True
return False
def set_active(self, summary=False):
"""
Appoint this item as the active one.
"""
config_set("presentation", self.id)
if summary:
config_set("summary", True)
else:
config_set("summary", '')
def set_closed(self, closed=True):
"""
Changes the closed-status of the item.
"""
self.closed = closed
self.save()
@property
def parents(self):
"""
Return the parent of this item, and the parent's partent and so
furth a list.
"""
parents = []
item = self
while item.parent is not None:
parents.append(item.parent)
item = item.parent
return parents
@property
def children(self):
"""
Return a list of all childitems from the next generation. The list
is ordert by weight. The childitems are not cast, so there are only
Item-objects and not Item-type objects.
"""
return self.item_set.order_by("weight")
@property
def weight_form(self):
"""
Return the WeightForm for this item.
"""
from agenda.forms import ElementOrderForm
try:
parent = self.parent.id
except AttributeError:
parent = 0
initial = {
'weight': self.weight,
'self': self.id,
'parent': parent,
}
return ElementOrderForm(initial=initial, prefix="i%d" % self.id)
def edit_form(self, post=None):
"""
Return the EditForm for this item.
"""
try:
return self._edit_form
except AttributeError:
from agenda.forms import MODELFORM
try:
form = MODELFORM[self.type]
except KeyError:
raise NameError(_("No Form for itemtype %s") % self.type)
self._edit_form = form(post, instance=self)
return self._edit_form
@models.permalink
def get_absolute_url(self, link='view'):
"""
Return the URL to this item. By default it is the Link to its
beamer-view.
link can be:
* view
* delete
"""
if link == 'view':
return ('item_view', [str(self.id)])
if link == 'delete':
return ('item_delete', [str(self.id)])
def __unicode__(self):
return self.title
def cast(self):
try:
return self.realobject
except AttributeError:
self.realobject = super(Item, self).cast()
return self.realobject
@property
def type(self):
"""
Return the name of the class from this item
"""
try:
return self._type
except AttributeError:
self._type = self.cast().__class__.__name__
return self._type
class Meta:
permissions = (
('can_view_agenda', "Can see the agenda"),
('can_manage_agenda', "Can manage the agenda and its items"),
('can_see_beamer', "Can see the Beamer"),
)
class ItemText(Item):
"""
An Item with a TextField.
"""
text = models.TextField(null=True, blank=True, verbose_name=_("Text"))
class Meta:
pass
class ItemApplication(Item):
"""
An Item which is connected to an application.
"""
application = models.ForeignKey(Application, verbose_name=_("Application"))
class ItemAssignment(Item):
"""
An Item which is connected to an assignment.
"""
assignment = models.ForeignKey(Assignment, verbose_name=_("Election"))
class ItemPoll(Item):
"""
An Item which is connected to a poll
"""
poll = models.ForeignKey(Poll, verbose_name=_("Poll"))

View File

@ -0,0 +1,17 @@
{% extends "base.html" %}
{% load tags %}
{% block submenu %}
{% url item_overview as url_itemoverview %}
{% url item_new as url_itemnew %}
<h4 class="sectiontitle">{%trans "Agenda" %}</h4>
<ul>
<li class="{% if request.path == url_itemoverview %}selected{% endif %}"><a href="{% url item_overview %}">{%trans "All items" %}</a></li>
{% if perms.agenda.can_manage_agenda %}
<li class="{% active request '/item/new/' %}"><a href="{% url item_new 'ItemText' %}">{%trans "New item" %}</a></li>
{% endif %}
{% if perms.agenda.can_see_beamer %}
<li><a href="{% url item_beamer %}"><img src="/static/images/icons/view-presentation.png"> {%trans 'Beamer view' %}</a></li>
{% endif %}
</ul>
{% endblock %}

View File

@ -0,0 +1,35 @@
{% extends "agenda/base_agenda.html" %}
{% block title %}{{ block.super }} - {%trans "Item" %}{% endblock %}
{% block content %}
{% if item %}
<h1>{%trans "Edit item" %}</h1>
{% else %}
<h1>{%trans "New item" %}</h1>
<p>{%trans "Choose item type:" %}</p>
<p>
<a href="{% url item_new 'ItemText' %}"
{% ifequal request.path '/item/new/ItemText/' %}style='font-size:15px; font-weight:bold;'{% endifequal %}
>{%trans "Item of Text" %}</a> |
<a href="{% url item_new 'ItemApplication' %}"
{% ifequal request.path '/item/new/ItemApplication/' %}style='font-size:15px; font-weight:bold;'{% endifequal %}
>{%trans "Item of Application" %}</a> |
<a href="{% url item_new 'ItemAssignment' %}"
{% ifequal request.path '/item/new/ItemAssignment/' %}style='font-size:15px; font-weight:bold;'{% endifequal %}
>{%trans "Item of Election" %}</a>
</p>
{% endif %}
<form action="" method="post">{% csrf_token %}
{{ form.as_p }}
<button type="submit">
<span class="icon ok">{%trans 'Save' %}</span>
</button>
<a href='{% url item_overview %}'>
<button type="button">
<span class="icon cancel">{%trans 'Cancel' %}</span>
</button>
</a>
</form>
{% endblock %}

View File

@ -0,0 +1,158 @@
{% extends "agenda/base_agenda.html" %}
{% load tags %}
{% block title %}{{ block.super }} - {% trans "Agenda" %}{% endblock %}
{% block header %}
<link type="text/css" rel="stylesheet" media="all" href="/static/styles/tabledrag.css" />
{% if perms.agenda.can_manage_agenda %}
<script type="text/javascript" src="/static/javascript/jquery.js"></script>
<script type="text/javascript" src="/static/javascript/jquery.once.js"></script>
<script type="text/javascript" src="/static/javascript/jquery.cookie.js"></script>
<script type="text/javascript" src="/static/javascript/tabledrag.js"></script>
<script type="text/javascript">
<!--//--><![CDATA[//><!--
//This is Drupal Code
jQuery.extend(
Drupal.settings,
{
"tableDrag":
{ "menu-overview":
{ "menu-plid":
[ { "target": "menu-plid", "source": "menu-mlid", "relationship": "parent", "action": "match", "hidden": true, "limit": 8 } ],
"menu-weight":
[ { "target": "menu-weight", "source": "menu-weight", "relationship": "sibling", "action": "order", "hidden": true, "limit": 0 } ]
}
}
});
//--><!]]>
</script>
{% endif %}
{% endblock %}
{% block content %}
<h1>{% trans "Agenda" %}</h1>
{% if items %}
<form action="/item/" method="post">{% csrf_token %}
{% if perms.agenda.can_manage_agenda %}
<div id="changed-order-message" style="display:none" class="notification warning">
<em>{% trans "Do you want to save the changed order of agenda items?" %}<br>
<input type="submit" value="{% trans 'Yes' %}">
<input type="button" onclick="window.location.href='{% url item_overview %}';" value="{%trans 'No' %}">
</em>
</div>
{% endif %}
<table id="menu-overview" class="agendatable">
<tr>
<th style="width: 1px;">{% trans "Beamer" %}</th>
<th>{% trans "Item" %}</th>
{% if perms.agenda.can_manage_agenda %}
<th>{% trans "Item Type" %}</th>
{% endif %}
<th style="width: 1px;">{% if perms.agenda.can_manage_agenda %}{% trans "Actions" %}{% endif %}</th>
{% if perms.agenda.can_manage_agenda %}
<th class="tabledrag-hide">{% trans "Weight" %}</th>
{% endif %}
</tr>
<tr {% if overview %}class="activeline"{% endif %}>
<td {% if perms.agenda.can_manage_agenda %}
class="select" onclick="window.location.href='{% url item_activate 0 %}'"
{% endif %}>
{% if overview %}
<img class="center" src="/static/images/icons/task-accepted.png" title="{% trans 'Agenda selected' %}">
{% else %}
{% if perms.agenda.can_manage_agenda %}<img class="center" src="/static/images/icons/task-accepted-grey.png" title="{% trans 'Select agenda' %}">{% endif %}
{% endif %}
</td>
<td><i>{% trans "Agenda" %} ({{ items|length }} {% trans "items" %})</i></td>
{% if perms.agenda.can_manage_agenda %}
<td></td>
{% endif %}
<td style="width: 1px;white-space: nowrap"><a href="{% url print_agenda %}" title="{%trans 'Print reduced agenda (only first parent items)' %}"><img src="/static/images/icons/application-pdf.png"></a>
<a href="{% url print_agenda_full printAllItems=1 %}" title="{%trans 'Print full agenda (all items)' %}"><img src="/static/images/icons/pdf-annotations.png"></a></td>
</tr>
{% for item in items %}
{% if not item.hidden or item.hidden and perms.agenda.can_manage_agenda %}
<tr class="draggable {% cycle 'odd' '' %}
{% if item.active %}activeline{% else %}
{% if item.parent.active and summary %}activesummarychildline{% endif %}
{% endif %}">
<td {% if perms.agenda.can_manage_agenda %}
class="select" onclick="window.location.href='{% url item_activate item.id %}'"
{% endif %}>
{% if item.active %}
<img class="center" src="/static/images/icons/task-accepted.png" title="{% trans 'Item selected' %}"></span>
{% else %}
{% if perms.agenda.can_manage_agenda %}<img class="center" src="/static/images/icons/task-accepted-grey.png" title="{% trans 'Select item' %}"> {% endif %}
{% endif %}
</td>
<td>
{% for p in item.parents %}
<div class="indentation">&nbsp;</div>
{% endfor %}
{% if perms.agenda.can_manage_agenda %}
<div class="dragcell"></div>
{% endif %}
{{ item }}
</td>
{% if perms.agenda.can_manage_agenda %}
<td>
{% ifequal item.type 'ItemApplication' %}
<a href="{% url application_view item.cast.application.id %}">{% trans "Application" %} {{ item.cast.application.number }}</a>
{% endifequal %}
{% ifequal item.type 'ItemPoll' %}
{% if item.cast.poll.application %}
<a href="{% url poll_view item.cast.poll.id %}">{% trans "Poll of Application" %}</a>
{% endif %}
{% if item.cast.poll.assignment %}
<a href="{% url poll_view item.cast.poll.id %}">{% trans "Poll of Election" %}</a>
{% endif %}
{% endifequal %}
{% ifequal item.type 'ItemAssignment' %}
<a href="{% url assignment_view item.cast.assignment.id %}">{% trans "Election" %}</a>
{% endifequal %}
{% ifequal item.type 'ItemText' %}
Text
{% endifequal %}
</td>
{% endif %}
<td style="width: 1px;white-space: nowrap">
<a href="{{ item.get_absolute_url }}"><img src="/static/images/icons/document-preview.png" title="{% trans 'Show beamer preview' %}"></a>
{% if perms.agenda.can_manage_agenda %}
<a href="{% url item_edit item.id %}"><img src="/static/images/icons/document-edit.png" title="{% trans 'Edit item' %}"></a>
<a href="{% url item_delete item.id %}"><img src="/static/images/icons/edit-delete.png" title="{% trans 'Delete item' %}"></a>
{% if item.closed %}
<a href="{% url item_open item.id %}"><img src="/static/images/icons/user-offline.png" title="{% trans 'Click to open item' %}"></a>
{% else %}
<a href="{% url item_close item.id %}"><img src="/static/images/icons/user-online.png" title="{% trans 'Click to close item' %}"></a>
{% endif %}
{% if item.children.exists %}
<a href="{% url item_activate_summary item.id %}"><img src="/static/images/icons/view-list-tree.png" title="{% trans 'Select item overview' %}"></a>
{% endif %}
{% endif %}
</td>
{% if perms.agenda.can_manage_agenda %}
<td class="tabledrag-hide">
{% with form=item.weight_form %}
{{ form.weight }}
{{ form.self }}
{{ form.parent }}
{% endwith %}
</td>
{% endif %}
</tr>
{% endif %}
{% endfor %}
</table>
</form>
{% else %}
<i>{% trans "No items available." %}</i>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
{% load tags %}
<head>
<link type="text/css" rel="stylesheet" href="/static/styles/beamer.css">
<script type="text/javascript" src="/static/javascript/jquery.js"></script>
<script type="text/javascript" src="/static/javascript/beamer.js"></script>
<title>{% block title %} {% get_config 'event_name' %} {% endblock %}</title>
</head>
<body>
<div id="config" style="display:none;">
<div id="ajax">{{ ajax }}</div>
</div>
<div id="ajaxswitcher"></div>
<div id="header">
<div id="logo"><img src="/static/images/logo-beamer.png"></div>
<div class="event_name">{% get_config 'event_name' %}</div>
<div class="event_description">{% get_config 'event_description' %}</div>
</div>
<div id="currentTime">
{% now "H:i" %}
</div>
<div id="content">
{% block content %}
{% endblock %}
</div>
</body>
</html>

View File

@ -0,0 +1,47 @@
{% extends "beamer.html" %}
{% load tags %}
{% block title %}{{ block.super }} - {{ item.title }}{% endblock %}
{% block content %}
<div class="number">{% trans "Application" %} #{{ item.application.number }}</div>
<h1>{{ item.title }}</h1>
{% if item.application.status != "pub" and item.application.status != "per" %}
<div id="sidebar">
<div class="box">
<h4>{%trans "Status" %}:</h4>
{%trans item.application.get_status_display %}
{% with item.application.poll_set.all as poll %}
{% if poll|length > 0 %}
<h4>{% trans "Poll result" %}:</h4>
{% for p in poll %}
{% if p.has_vote %}
{% if poll|length > 1 %}
{{forloop.counter}}. {% trans "Poll" %}:<br>&nbsp;&nbsp;&nbsp;
{% endif %}
{% for option in p.get_options %}
{% trans "Yes" %}: {{ option.voteyes }} <br>&nbsp;&nbsp;&nbsp;
{% trans "No" %}: {{ option.voteno }} <br>&nbsp;&nbsp;&nbsp;
{% trans "Abstention" %}: {{ option.voteundesided }}
{% endfor %}
<br>
{% else %}
{% if poll|length == 1 %}
<i>{% trans "No poll results available." %}</i>
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endwith %}
</div>
</div>
{% endif %}
<p>
<div class="text">{{ item.application.text|linebreaks }}</div>
<br>
<div class="reason"><p><b>{% trans "Reason" %}:</b></p>
{{ item.application.reason|linebreaks }}</div>
</p>
{% endblock %}

View File

@ -0,0 +1,69 @@
{% extends "beamer.html" %}
{% block title %}{{ block.super }} - {{ item.title }}{% endblock %}
{% block content %}
{% trans "Election" %}:
<h1>{{ item.assignment }}</h1>
{% if item.assignment.status == "sea" or item.assignment.status == "vot" %}
<div id="sidebar">
<div class="box">
<h4>{% trans "Status" %}:</h4>
{% trans item.assignment.get_status_display %}
<h4>{% trans "Number of available posts" %}:</h4>
{{ item.assignment.assignment_number }}
</div>
</div>
{% endif %}
<p>
<div class="text">{{ item.assignment.description|linebreaks }}</div>
</p>
{% if item.assignment.status != "fin" %}
<h3>{% trans "Candidates" %}</h3>
<ol>
{% for profile in item.assignment.profile.all|dictsort:"user.first_name" %}
<li>{{ profile }} </li>
{% empty %}
<li style="list-style: none outside none;">
<i>{% trans "No candidates available." %}</i>
</li>
{% endfor %}
</ol>
{% endif %}
{% if item.assignment.poll_set.all.count > 0 %}
<p><br></p>
<h3>{% trans "Election results" %}</h3>
<table>
<tr>
<th>{% trans "Candidates" %}</th>
{% for poll in item.assignment.poll_set.all %}
<th><nobr>{{forloop.counter}}. {% trans "ballot" %}</nobr></th>
{% endfor %}
</tr>
{% for vote in votes %}
<tr class="{% cycle 'odd' '' %}">
{% for v in vote %}
<td>{% if v %}
{% if v|length == 3 %}
<img src="/static/images/icons/voting-yes.png" title="{% trans 'Yes' %}"> {% if v.0 %}{{ v.0 }}{% else %}&empty;{% endif %}<br>
<img src="/static/images/icons/voting-no.png" title="{% trans 'No' %}"> {% if v.1 %}{{ v.1 }}{% else %}&empty;{% endif %}<br>
<img src="/static/images/icons/voting-abstention.png" title="{% trans 'Abstention' %}"> {% if v.2 %}{{ v.2 }}{% else %}&empty;{% endif %}<br>
{% else %}
{{ v }}
{% endif %}
{% else %}&empty;{% endif %}
</td>
{% endfor %}
</tr>
{% empty %}
<tr>
<td {% if item.assignment.profile.exist %}colspan="2"{% endif %}><i>{% trans "No ballots available." %}</i></td>
</tr>
{% endfor %}
</table>
{% endif %}
<br>
{% endblock %}

View File

@ -0,0 +1,19 @@
{% extends "beamer.html" %}
{% block title %}{{ block.super }} - {{ item.title }}{% endblock %}
{% block content %}
{%trans "Poll about" %}:
<h1>{{ item.title }}</h1>
<table>
{% for option in item.poll.get_options %}
<tr>
<td>{{ option }}</td>
<td>{{ option.voteyes }}</td>
{% if item.poll.optiondecision %}
<td>{{ option.voteno }}</td>
<td>{{ option.voteundesided }}</td>
{% endif %}
</tr>
{% endfor %}
</table>
{% endblock %}

View File

@ -0,0 +1,11 @@
{% extends "beamer.html" %}
{% block title %}{{ block.super }} - {{ item.title }}{% endblock %}
{% block content %}
{% if item.text %}
<h1>{{ item.title }}</h1>
{# item.parse|safe #}
{{ item.text|linebreaks }}
{% else %}
<div class="item_fullscreen">{{ item.title }}</div>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,17 @@
{% extends "beamer.html" %}
{% block title %}{{ block.super }} -
{% if title %} {{ title }} {% else %} {%trans "Agenda" %} {% endif %}
{% endblock %}
{% block content %}
{% if title %}
<h1>{{ title }}</h1>
{% else %}
<h1>{%trans "Agenda" %}</h1>
{% endif %}
<ul class="itemlist">
{% for item in items %}
<li{% if item.closed %} class="closed"{% endif %}>{{ item }}</li>
{% endfor %}
</ul>
{% endblock %}

197
openslides/agenda/tests.py Normal file
View File

@ -0,0 +1,197 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.agenda.test
~~~~~~~~~~~~~~~~~~~~~~
Unit test for the agenda app.
:copyright: 2011 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from django.test import TestCase
from django.test.client import Client
from django.contrib.auth.models import User
from openslides.agenda.models import Item, ItemText
from openslides.agenda.api import get_active_item, is_summary, children_list
class ItemTest(TestCase):
def setUp(self):
self.item1 = ItemText.objects.create(title='item1')
self.item2 = ItemText.objects.create(title='item2')
self.item3 = ItemText.objects.create(title='item1A', parent=self.item1)
self.item4 = ItemText.objects.create(title='item1Aa', parent=self.item3)
def testActive(self):
with self.assertRaises(Item.DoesNotExist):
get_active_item()
self.assertTrue(is_summary())
self.assertFalse(self.item4.active_parent)
self.assertFalse(self.item1.active)
self.item1.set_active()
self.assertTrue(self.item1.active)
self.assertTrue(self.item4.active_parent)
self.assertEqual(get_active_item().cast(), self.item1)
self.assertNotEqual(get_active_item().cast(), self.item2)
self.assertFalse(is_summary())
self.item2.set_active(summary=True)
self.assertFalse(self.item1.active)
self.assertTrue(is_summary())
def testClosed(self):
self.assertFalse(self.item1.closed)
self.item1.set_closed()
self.assertTrue(self.item1.closed)
self.item1.set_closed(closed=False)
self.assertFalse(self.item1.closed)
def testParents(self):
self.assertEqual(self.item1.parents, [])
self.assertTrue(self.item1 in self.item3.parents)
self.assertTrue(self.item1 in self.item4.parents)
self.assertFalse(self.item2 in self.item4.parents)
def testChildren(self):
self.assertEqual(list(self.item2.children), [])
self.assertTrue(self.item3 in [item.cast() for item in self.item1.children])
self.assertFalse(self.item4 in [item.cast() for item in self.item1.children])
l = children_list([self.item1, self.item2])
self.assertEqual(str(l), "[<ItemText: item1>, <Item: item1A>, <Item: item1Aa>, <ItemText: item2>]")
def testForms(self):
for item in Item.objects.all():
initial = item.weight_form.initial
self.assertEqual(initial['self'], item.id)
if item.parent:
self.assertEqual(initial['parent'], item.parent.id)
else:
self.assertEqual(initial['parent'], 0)
self.assertEqual(initial['weight'], item.weight)
item.edit_form()
def testtype(self):
self.assertEqual(self.item1.type, 'ItemText')
class ViewTest(TestCase):
def setUp(self):
self.item1 = ItemText.objects.create(title='item1')
self.item2 = ItemText.objects.create(title='item2')
self.refreshItems()
self.admin = User.objects.create_user('testadmin', '', 'default')
self.anonym = User.objects.create_user('testanoym', '', 'default')
self.admin.is_superuser = True
self.admin.save()
def refreshItems(self):
self.item1 = Item.objects.get(pk=self.item1.id)
self.item2 = Item.objects.get(pk=self.item2.id)
@property
def adminClient(self):
c = Client()
c.login(username='testadmin', password='default')
return c
@property
def anonymClient(self):
return Client()
def testBeamer(self):
c = self.anonymClient
response = c.get('/beamer/')
self.assertEqual(response.status_code, 302)
c = self.adminClient
response = c.get('/beamer/')
self.assertEqual(response.status_code, 200)
response = c.get('/item/%d/' % self.item1.id)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context['item'], self.item1.cast())
self.assertEqual(response.templates[0].name, 'beamer/ItemText.html')
def testActivate(self):
c = self.adminClient
response = c.get('/item/%d/activate/' % self.item1.id)
self.assertEqual(response.status_code, 302)
self.assertTrue(self.item1.active)
self.assertFalse(self.item2.active)
self.assertFalse(is_summary())
response = c.get('/item/%d/activate/summary/' % self.item2.id)
self.assertEqual(response.status_code, 302)
self.assertTrue(self.item2.active)
self.assertFalse(self.item1.active)
self.assertTrue(is_summary())
response = c.get('/item/%d/activate/' % 0)
self.assertEqual(response.status_code, 302)
self.assertFalse(self.item2.active)
self.assertFalse(self.item1.active)
with self.assertRaises(Item.DoesNotExist):
get_active_item()
response = c.get('/item/%d/activate/' % 10000)
self.assertEqual(response.status_code, 302)
self.assertFalse(self.item2.active)
self.assertFalse(self.item1.active)
def testClose(self):
c = self.adminClient
response = c.get('/item/%d/close/' % self.item1.id)
self.refreshItems()
self.assertEqual(response.status_code, 302)
self.assertTrue(Item.objects.get(pk=self.item1.id).closed)
response = c.get('/item/%d/open/' % self.item1.id)
self.refreshItems()
self.assertEqual(response.status_code, 302)
self.assertFalse(self.item1.closed)
response = c.get('/item/%d/open/' % 1000)
self.refreshItems()
self.assertEqual(response.status_code, 302)
def testEdit(self):
c = self.adminClient
response = c.get('/item/%d/edit/' % self.item1.id)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context['form'].instance, self.item1.cast())
response = c.get('/item/%d/edit/' % 1000)
self.assertEqual(response.status_code, 302)
data = {'title': 'newitem1', 'text': 'item1-text', 'weight':'0'}
response = c.post('/item/%d/edit/' % self.item1.id, data)
self.assertEqual(response.status_code, 302)
self.refreshItems()
self.assertEqual(self.item1.cast().title, 'newitem1')
self.assertEqual(self.item1.cast().text, 'item1-text')
data = {'title': '', 'text': 'item1-text', 'weight': '0'}
response = c.post('/item/%d/edit/' % self.item1.id, data)
self.assertEqual(response.status_code, 200)
self.refreshItems()
self.assertEqual(self.item1.cast().title, 'newitem1')
def testNew(self):
pass

62
openslides/agenda/urls.py Normal file
View File

@ -0,0 +1,62 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.agenda.urls
~~~~~~~~~~~~~~~~~~~~~~
URL list for the agenda app.
:copyright: 2011 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from django.conf.urls.defaults import *
urlpatterns = patterns('agenda.views',
url(r'^beamer/$', 'beamer',
name='item_beamer'),
url(r'^$', 'overview'),
url(r'^item/$', 'overview',
name='item_overview'),
url(r'^item/(?P<item_id>\d+)/$', 'view',
name='item_view'),
url(r'^item/(?P<item_id>\d+)/activate/$', 'set_active',
name='item_activate'),
url(r'^item/(?P<item_id>\d+)/activate/summary/$', 'set_active',
{'summary': True},\
name='item_activate_summary'),
url(r'^item/(?P<item_id>\d+)/close/$', 'set_closed', {'closed': True},
name='item_close'),
url(r'^item/(?P<item_id>\d+)/open/$', 'set_closed', {'closed': False},
name='item_open'),
url(r'^item/(?P<item_id>\d+)/edit/$', 'edit',
name='item_edit'),
url(r'^item/new/$', 'edit',
name='item_new_default'),
url(r'^item/new/(?P<form>ItemText|ItemApplication|ItemPoll|'
r'ItemAssignment)/$', 'edit',
name='item_new'),
url(r'^item/new/(?P<form>ItemText|ItemApplication|ItemPoll|'
r'ItemAssignment)/(?P<default>\d+)/$', 'edit',
name='item_new_default'),
url(r'^item/(?P<item_id>\d+)/del/$', 'delete',
name='item_delete'),
url(r'^item/print/(?P<printAllItems>\d+)/$', 'print_agenda',
name='print_agenda_full'),
url(r'^item/print/$', 'print_agenda',
name='print_agenda'),
)

255
openslides/agenda/views.py Normal file
View File

@ -0,0 +1,255 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.agenda.views
~~~~~~~~~~~~~~~~~~~~~~~
Views for the agenda app.
:copyright: 2011 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
try:
import json
except ImportError:
import simplejson as json
from datetime import datetime
from django.http import HttpResponse
from django.shortcuts import render_to_response, redirect
from django.template import RequestContext
from django.core.urlresolvers import reverse
from django.contrib import messages
from django.utils.translation import ugettext as _
from openslides.agenda.models import Item
from openslides.agenda.api import get_active_item, is_summary, children_list
from openslides.agenda.forms import ElementOrderForm, MODELFORM
from openslides.application.models import Application
from openslides.assignment.models import Assignment
from openslides.poll.models import Poll
from openslides.system.api import config_set, config_get
from openslides.utils.template import render_block_to_string
from openslides.utils.utils import template, permission_required, \
del_confirm_form
from openslides.utils.pdf import print_agenda
from poll.models import Poll, Option
def view(request, item_id):
"""
Shows the Slide.
"""
item = Item.objects.get(id=item_id)
votes = assignment_votes(item)
return render_to_response('beamer/%s.html' % item.type,
{
'item': item.cast(),
'ajax': 'off',
'votes': votes,
},
context_instance=RequestContext(request))
@permission_required('agenda.can_see_beamer')
def beamer(request):
"""
Shows a active Slide.
"""
data = {'ajax': 'on'}
template = ''
try:
item = get_active_item()
votes = assignment_votes(item)
if is_summary():
items = item.children.filter(hidden=False)
data['items'] = items
data['title'] = item.title
template = 'beamer/overview.html'
else:
data['item'] = item.cast()
data['title'] = item.title
data['votes'] = votes
template = 'beamer/%s.html' % (item.type)
except Item.DoesNotExist:
items = Item.objects.filter(parent=None).filter(hidden=False) \
.order_by('weight')
data['items'] = items
data['title'] = _("Agenda")
template = 'beamer/overview.html'
if request.is_ajax():
content = render_block_to_string(template, 'content', data)
jsondata = {'content': content,
'title': data['title'],
'time': datetime.now().strftime('%H:%M')}
return HttpResponse(json.dumps(jsondata))
else:
return render_to_response(template,
data,
context_instance=RequestContext(request))
def assignment_votes(item):
votes = []
if item.type == "ItemAssignment":
assignment = item.cast().assignment
# list of candidates
candidates = set()
for option in Option.objects.filter(poll__assignment=assignment):
candidates.add(option.value)
# list of votes
votes = []
for candidate in candidates:
tmplist = []
tmplist.append(candidate)
for poll in assignment.poll_set.all():
if candidate in poll.options_values:
option = Option.objects.filter(poll=poll).filter(user=candidate)[0]
if poll.optiondecision:
tmplist.append([option.yes, option.no, option.undesided])
else:
tmplist.append(option.yes)
else:
tmplist.append("-")
votes.append(tmplist)
return votes
@permission_required('agenda.can_view_agenda')
@template('agenda/overview.html')
def overview(request):
"""
Shows an overview of all items.
"""
if request.method == 'POST':
for item in Item.objects.all():
form = ElementOrderForm(request.POST, prefix="i%d" % item.id)
if form.is_valid():
try:
item.parent = Item.objects.get( \
id=form.cleaned_data['parent'])
except Item.DoesNotExist:
item.parent = None
item.weight = form.cleaned_data['weight']
item.save()
items = children_list(Item.objects.filter(parent=None).order_by('weight'))
try:
overview = is_summary() and not get_active_item()
except Item.DoesNotExist:
overview = True
return {
'items': items,
'overview': overview,
'summary': is_summary()
}
@permission_required('agenda.can_manage_agenda')
def set_active(request, item_id, summary=False):
"""
Set an Item as the active one.
"""
if item_id == "0":
config_set("presentation", "0")
else:
try:
item = Item.objects.get(id=item_id)
item.set_active(summary)
except Item.DoesNotExist:
messages.error(request, _('Item ID %d does not exist.') % int(item_id))
return redirect(reverse('item_overview'))
@permission_required('agenda.can_manage_agenda')
def set_closed(request, item_id, closed=True):
"""
Close or open an Item.
"""
try:
item = Item.objects.get(id=item_id)
item.set_closed(closed)
except Item.DoesNotExist:
messages.error(request, _('Item ID %d does not exist.') % int(item_id))
return redirect(reverse('item_overview'))
@permission_required('agenda.can_manage_agenda')
@template('agenda/edit.html')
def edit(request, item_id=None, form='ItemText', default=None):
"""
Show a form to edit an existing Item, or create a new one.
"""
if item_id is not None:
try:
item = Item.objects.get(id=item_id).cast()
except Item.DoesNotExist:
messages.error(request, _('Item ID %d does not exist.') % int(item_id))
return redirect(reverse('item_overview'))
else:
item = None
if request.method == 'POST':
if item_id is None:
form = MODELFORM[form](request.POST)
else:
form = item.edit_form(request.POST)
if form.is_valid():
item = form.save()
if item_id is None:
messages.success(request, _('New item was successfully created.'))
if "application" in request.POST:
item.application.writelog(_('Agenda item created'), request.user)
else:
messages.success(request, _('Item was successfully modified.'))
if "application" in request.POST:
item.application.writelog(_('Agenda item modified'), request.user)
return redirect(reverse('item_overview'))
messages.error(request, _('Please check the form for errors.'))
else:
initial = {}
if default:
if form == "ItemAssignment":
assignment = Assignment.objects.get(pk=default)
initial = {
'assignment': assignment,
'title': assignment.name,
}
elif form == "ItemApplication":
application = Application.objects.get(pk=default)
initial = {
'application': application,
'title': application.title,
}
elif form == "ItemPoll":
poll = Poll.objects.get(pk=default)
initial = {
'poll': poll,
'title': poll.title,
}
if item_id is None:
form = MODELFORM[form](initial=initial)
else:
form = item.edit_form()
return { 'form': form,
'item': item }
@permission_required('agenda.can_manage_agenda')
def delete(request, item_id):
"""
Delete an Item.
"""
item = Item.objects.get(id=item_id).cast()
if request.method == 'POST':
item.delete()
messages.success(request, _("Item <b>%s</b> was successfully deleted.") % item)
else:
del_confirm_form(request, item)
return redirect(reverse('item_overview'))

View File

View File

@ -0,0 +1,17 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.application.admin
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Register app for admin site.
:copyright: 2011 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from django.contrib import admin
from openslides.application.models import Application, AVersion
admin.site.register(Application)
admin.site.register(AVersion)

View File

@ -0,0 +1,34 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.application.forms
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Forms for the application app.
:copyright: 2011 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from django.forms import ModelForm, Form, CharField, Textarea, TextInput
from django.utils.translation import ugettext as _
from openslides.application.models import Application
class ApplicationForm(Form):
error_css_class = 'error'
required_css_class = 'required'
title = CharField(widget=TextInput(), label=_("Title"))
text = CharField(widget=Textarea(), label=_("Text"))
reason = CharField(widget=Textarea(), required=False, label=_("Reason"))
class ApplicationManagerForm(ModelForm):
error_css_class = 'error'
required_css_class = 'required'
class Meta:
model = Application
exclude = ('number', 'status', 'permitted', 'log')

View File

@ -0,0 +1,408 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.application.models
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Models for the application app.
:copyright: 2011 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from datetime import datetime
from django.db import models
from django.db.models import Max
from django.contrib.auth.models import User
from django.utils.translation import ugettext as _
from openslides.system.api import config_get
class Application(models.Model):
STATUS = (
('pub', _('Published')),
('per', _('Permitted')),
('acc', _('Accepted')),
('rej', _('Rejected')),
('wit', _('Withdrawed')),
('adj', _('Adjourned')),
('noc', _('Not Concerned')),
('com', _('Commited a bill')),
('nop', _('Rejected (not permitted)')),
#additional actions:
# edit
# delete
# setnumber
# support
# unsupport
# createitem
# genpoll
)
submitter = models.ForeignKey(User, verbose_name=_("Submitter"))
supporter = models.ManyToManyField(User, related_name='supporter', \
null=True, blank=True, verbose_name=_("Supporters"))
number = models.PositiveSmallIntegerField(blank=True, null=True,
unique=True)
status = models.CharField(max_length=3, choices=STATUS, default='pub')
permitted = models.ForeignKey('AVersion', related_name='permitted', \
null=True, blank=True)
log = models.TextField(blank=True, null=True)
@property
def last_version(self):
"""
Return last version of the application.
"""
try:
return AVersion.objects.filter(application=self).order_by('id') \
.reverse()[0]
except IndexError:
return None
@property
def versions(self):
"""
Return a list of all versions of the application.
"""
return AVersion.objects.filter(application=self)
@property
def creation_time(self):
"""
Return the time of the creation of the application.
"""
try:
return self.versions[0].time
except IndexError:
return None
@property
def notes(self):
"""
Return some information of the application.
"""
note = []
if self.status == "pub" and not self.enough_supporters:
note.append(_("Searching for supporters."))
if self.status == "pub" and self.permitted is None:
note.append(_("Not yet permitted."))
elif self.unpermitted_changes and self.permitted:
note.append(_("Not yet permitted changes."))
return note
@property
def unpermitted_changes(self):
"""
Return True if the application has unpermitted changes.
The application has unpermitted changes, if the permitted-version
is not the lastone and the lastone is not abjected.
TODO: rename the property in unchecked__changes
"""
if (self.last_version != self.permitted
and not self.last_version.abjected):
return True
else:
return False
@property
def enough_supporters(self):
"""
Return True, if the application has enough supporters
"""
min_supporters = int(config_get('application_min_supporters'))
return self.supporter.count() >= min_supporters
def save(self, user=None):
"""
Save the Application, and create a new AVersion if necessary
"""
super(Application, self).save()
last_version = self.last_version
if last_version is not None:
if (last_version.text == self.text
and last_version.title == self.title
and last_version.reason == self.reason):
return # No changes
try:
if self.title != "":
version = AVersion(title=getattr(self, 'title', ''),
text=getattr(self, 'text', ''),
reason=getattr(self, 'reason', ''),
application=self)
version.save()
self.writelog(_("Version %s created") % version.aid, user)
is_manager = user.has_perm('application.can_manage_application')
except AttributeError:
is_manager = False
if (self.status == "pub"
and self.supporter.exists()
and not is_manager):
self.supporter.clear()
self.writelog(_("Supporters removed"), user)
def reset(self, user):
"""
Reset the application.
"""
self.status = "pub"
self.permitted = None
self.save()
self.writelog(_("Status reseted to: %s") % (self.get_status_display()), user)
def support(self, user):
"""
Add a Supporter to the list of supporters of the application.
"""
if user == self.submitter:
raise NameError('Supporter can not be the submitter of a ' \
'application.')
if self.permitted is not None:
raise NameError('This application is already permitted.')
if user not in self.supporter.all():
self.supporter.add(user)
self.writelog(_("Supporter: +%s") % (user))
def unsupport(self, user):
"""
remove a supporter from the list of supporters of the application
"""
if self.permitted is not None:
raise NameError('This application is already permitted.')
if user in self.supporter.all():
self.supporter.remove(user)
self.writelog(_("Supporter: -%s") % (user))
def set_number(self, number=None, user=None):
"""
Set a number for ths application.
"""
if self.number is not None:
raise NameError('This application has already a number.')
if number is None:
try:
number = Application.objects.aggregate(Max('number')) \
['number__max'] + 1
except TypeError:
number = 1
self.number = number
self.save()
self.writelog(_("Number set: %s") % (self.number), user)
return self.number
def permit(self, user=None):
"""
Change the status of this application to permit.
"""
self.set_status(user, "per")
aversion = self.last_version
if self.number is None:
self.set_number()
self.permitted = aversion
self.save()
self.writelog(_("Version %s permitted") % (aversion.aid), user)
return self.permitted
def notpermit(self, user=None):
"""
Change the status of this application to 'not permitted (rejected)'.
"""
self.set_status(user, "nop")
#TODO: reject last version
aversion = self.last_version
#self.permitted = aversion
if self.number is None:
self.set_number()
self.save()
self.writelog(_("Version %s not permitted") % (self.last_version.aid), user)
def set_status(self, user, status, force=False):
"""
Set the status of the application.
"""
error = True
for a, b in Application.STATUS:
if status == a:
error = False
break
if error:
raise NameError('%s is not a valid status.' % status)
if self.status == status:
raise NameError('The application status is already %s.' \
% self.status)
actions = []
actions = self.get_allowed_actions(user)
if status not in actions and not force:
raise NameError('The application status is: %s. You can not set' \
' the status to %s.' % (self.status, status))
oldstatus = self.get_status_display()
self.status = status
self.save()
self.writelog(_("Status modified")+": %s -> %s" \
% (oldstatus, self.get_status_display()), user)
def get_allowed_actions(self, user=None):
"""
Return a list of all the allowed status.
"""
actions = []
# check if user allowed to withdraw an application
if (self.status == "pub"
and self.number \
and user == self.submitter) \
or (self.status == "pub" \
and self.number \
and user.has_perm("application.can_manage_application")) \
or (self.status == "per" \
and user == self.submitter) \
or (self.status == "per" \
and user.has_perm("application.can_manage_application")):
actions.append("wit")
# Check if the user can support and unspoort the application
if self.status == "pub" \
and user != self.submitter \
and user not in self.supporter.all():
actions.append("support")
if self.status == "pub" and user in self.supporter.all():
actions.append("unsupport")
#Check if the user can edit the application
if user == self.submitter \
or user.has_perm("application.can_manage_application"):
actions.append("edit")
#Check if the user can delete the application
if self.number is None \
and self.status == "pub" \
and (self.submitter == user \
or user.has_perm("application.can_manage_application")):
actions.append("delete")
#For the rest, all actions need the manage permission
if not user.has_perm("application.can_manage_application"):
return actions
if self.status == "pub":
actions.append("nop")
actions.append("per")
if self.number == None:
actions.append("setnumber")
if self.status == "per":
actions.append("acc")
actions.append("rej")
actions.append("adj")
actions.append("noc")
actions.append("com")
actions.append("genpoll")
if self.unpermitted_changes:
actions.append("permitversion")
actions.append("rejectversion")
if self.number and not self.itemapplication_set.all():
actions.append("createitem")
return actions
def delete(self, force=False):
"""
Delete the application. It is not possible, if the application has
allready a number
"""
if self.number and not force:
raise NameError('The application has already a number. ' \
'You can not delete it.')
super(Application, self).delete()
def writelog(self, text, user=None):
if not self.log:
self.log = ""
self.log += u"%s | %s" % (datetime.now().strftime("%d.%m.%Y %H:%M:%S"), text)
if user is not None:
self.log += u" (by %s)" % (user.username)
self.log += "\n"
self.save()
def __getattr__(self, name):
"""
if name is title, text, reason or time,
Return this attribute from the newest version of the application
"""
if name in ('title', 'text', 'reason', 'time'):
try:
return self.last_version.__dict__[name]
except TypeError:
raise AttributeError(name)
except AttributeError:
raise AttributeError(name)
raise AttributeError(name)
def gen_poll(self, user=None, pollcount=None):
"""
Generates a poll object for the application
"""
from poll.models import Poll
if pollcount > 1:
description = _("%s. poll") % pollcount
else:
description = _("Poll")
poll = Poll(title=_("Vote on application #%s") % self.number, \
optiondecision=True, \
application=self,
description=description)
poll.save()
poll.add_option(self)
self.writelog(_("Poll created"), user)
return poll
@models.permalink
def get_absolute_url(self, link='view'):
if link == 'view':
return ('application_view', [str(self.id)])
if link == 'delete':
return ('application_delete', [str(self.id)])
def __unicode__(self):
try:
return self.last_version.title
except AttributeError:
return "no title jet"
class Meta:
permissions = (
('can_view_application', _("Can see applications")),
('can_insert_application', _("Can insert new applications")),
('can_support_application', _("Can support applications")),
('can_manage_application', _("Can manage applications")),
)
class AVersion(models.Model):
title = models.CharField(max_length=100)
text = models.TextField()
reason = models.TextField(null=True, blank=True)
abjected = models.BooleanField()
time = models.DateTimeField(auto_now=True)
application = models.ForeignKey(Application)
def __unicode__(self):
return "%s %s" % (self.id, self.title)
@property
def aid(self):
try:
return self._aid
except AttributeError:
self._aid = AVersion.objects \
.filter(application=self.application) \
.filter(id__lte=self.id).count()
return self._aid

View File

@ -0,0 +1,14 @@
{% extends "base.html" %}
{% load tags %}
{% block submenu %}
{% url application_overview as url_applicationoverview %}
<h4>{%trans "Applications" %}</h4>
<ul>
<li class="{% if request.path == url_applicationoverview %}selected{% endif %}"><a href="{% url application_overview %}">{%trans "All applications" %}</a></li>
{% if perms.application.can_insert_application or perms.application.can_manage_application %}
<li class="{% active request '/application/new' %}"><a href="{% url application_new %}">{%trans "New application" %}</a></li>
{% endif %}
<li><a href="{% url print_applications %}"><img src="/static/images/icons/application-pdf.png"> {%trans 'Print all applications' %}</a></li>
</ul>
{% endblock %}

View File

@ -0,0 +1,27 @@
{% extends "application/base_application.html" %}
{% block title %}{{ block.super }} - {%trans "Application" %}{% endblock %}
{% block content %}
{% if application %}
<h1>{%trans "Edit application" %}</h1>
{% else %}
<h1>{%trans "New application" %}</h1>
{% endif %}
<form action="" method="post">{% csrf_token %}
{{ form.as_p }}
{{ managerform.as_p }}
<small>* {%trans "required" %}</small>
<p>
<button type="submit">
<span class="icon ok">{%trans 'Save' %}</span>
</button>
<a href='{% url application_overview %}'>
<button type="button">
<span class="icon cancel">{%trans 'Cancel' %}</span>
</button>
</a>
</p>
</form>
{% endblock %}

View File

@ -0,0 +1,62 @@
{% extends "application/base_application.html" %}
{% block title %}{{ block.super }} - {%trans "Applications" %}{% endblock %}
{% load tags %}
{% block content %}
<h1>{%trans "Applications" %}</h1>
<p><form action="{{request.url}}" name="filter" method="get">
{%trans "Filter" %}:
{% if min_supporters > 0 %}
<input type="checkbox" name="needsup" onchange="document.forms['filter'].submit()"
{% if 'on' in request.GET.needsup %}checked{% endif %}> {%trans "Need supporters" %} &nbsp;
{% endif %}
<input type="checkbox" name="number" onchange="document.forms['filter'].submit()"
{% if 'on' in request.GET.number %}checked{% endif %}> {%trans "Without number" %} &nbsp;
<input type="checkbox" name="status" onchange="document.forms['filter'].submit()"
{% if 'on' in request.GET.status %}checked{% endif %}> {%trans "Status" %}:
<select class="default-input" name="statusvalue" onchange="{% if 'on' in request.GET.status %}document.forms['filter'].submit(){% endif %}">
<option value="pub" {% if 'pub' in request.GET.statusvalue %}selected{% endif %}>{%trans "Not yet permitted" %}</option>
<option value="per" {% if 'on' in request.GET.status and 'per' in request.GET.statusvalue %}selected{% endif %}>{%trans "Permitted" %}</option>
<option value="acc" {% if 'on' in request.GET.status and 'acc' in request.GET.statusvalue %}selected{% endif %}>{%trans "Accepted" %}</option>
<option value="rej" {% if 'on' in request.GET.status and 'rej' in request.GET.statusvalue %}selected{% endif %}>{%trans "Rejected" %}</option>
<option value="wit" {% if 'on' in request.GET.status and 'wit' in request.GET.statusvalue %}selected{% endif %}>{%trans "Withdrawed (by submitter)" %}</option>
</select>
</form>
</p>
<br>
<table>
<tr>
<th><a href="?sort=number{% if 'number' in request.GET.sort and 'reverse' not in request.GET %}&reverse{%endif%}">{%trans "Number" %}</a></th>
<th><a href="?sort=aversion__title{% if 'aversion__title' in request.GET.sort and 'reverse' not in request.GET %}&reverse{%endif%}">{%trans "Application title" %}</a></th>
{% if min_supporters > 0 %}
<th><a href="?sort=supporter{% if 'supporter' in request.GET.sort and 'reverse' not in request.GET %}&reverse{%endif%}">{%trans "Number of supporters" %}*</a></th>
{% endif %}
<th><a href="?sort=status{% if 'status' in request.GET.sort and 'reverse' not in request.GET %}&reverse{%endif%}">{%trans "Status" %}</a></th>
<th><a href="?sort=submitter{% if 'submitter' in request.GET.sort and 'reverse' not in request.GET %}&reverse{%endif%}">{%trans "Submitter" %}</a></th>
<th><a href="?sort=aversion__time{% if 'aversion__time' in request.GET.sort and 'reverse' not in request.GET %}&reverse{%endif%}">{%trans "Creation Time" %}<a></th>
</tr>
{% for application in applications %}
<tr class="{% cycle '' 'odd' %}">
<td>{% if application.number %}{{ application.number }}{% else %}-{% endif %}</td>
<td><a href="{% url application_view application.id %}">{{ application }}</a></td>
{% if min_supporters > 0 %}
<td>{{ application.supporter.count }}</td>
{% endif %}
<td>{% if application.status != "pub" %}
{{ application.get_status_display }}<br>
{% endif %}
{% for note in application.notes %}
{{ note }}
{% if not forloop.last %}<br>{%endif%}
{% endfor %}
</td>
<td>{{ application.submitter }}</td>
<td>{{ application.creation_time }}</td>
</tr>
{% empty %}
<tr>
<td colspan="6"><i>{%trans "No applications available." %}</i></td>
</tr>
{% endfor %}
</table>
{% endblock %}

View File

@ -0,0 +1,50 @@
{% extends 'application/base_application.html' %}
{% block title %}{{ block.super }} - {{ poll.title }}{% endblock %}
{% if perms.poll.can_manage_poll %}
{% block submenu %}
{{ block.super }}
<br>
<h3>{%trans "Application" %} #{{poll.application.number}}</h3>
<ul>
<li><a href="{% url print_application_poll poll.id %}"><img src="/static/images/icons/application-pdf.png"> {%trans 'Print Poll' %}</a></li>
</ul>
{% endblock %}
{% endif %}
{% block content %}
<h1>{{ poll.title }}</h1>
<h4>Title: "{{ poll.application.title }}"</h4>
{% if perms.poll.can_manage_poll %}
<form action="" method="post">{% csrf_token %}
<fieldset>
<legend>{{ poll.description }}</legend>
<p><nobr><label>{%trans "Votes in favour" %}:</label></nobr>
{{ options.0.form.yes.errors }}{{ options.0.form.yes }}
</p>
<p><nobr><label>{%trans "Votes against" %}:</label></nobr>
{{ options.0.form.no.errors }}{{ options.0.form.no }}
</p>
<p><nobr><label>{%trans "Abstentions" %}:</label></nobr>
{{ options.0.form.undesided.errors }}{{ options.0.form.undesided }}
</p>
<p><label>{%trans "Invalid" %}:</label>
{{ form.invalid.errors }}{{ form.invalid }}
</p>
<p>
<button type="submit">
<span class="icon ok">{%trans 'Apply' %}</span>
</button>
</fieldset>
<p></p>
<a href='{% url application_view poll.application.id %}'>
<button type="button">
<span class="icon previous">{%trans 'Back to application' %}</span>
</button>
</a>
</form>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,276 @@
{% extends "application/base_application.html" %}
{% block title %}{{ block.super }} - {% trans "Application" %} "{{ application.title }}"{% endblock %}
{% load tags %}
{% block content %}
<div id="sidebar">
<div class="box">
<h4 style="float:left;">{% trans "Submitter" %}:</h4>
<h4 style="text-align: right;">
{% if "delete" in actions %}
<a href="{% url application_delete application.id %}"><img src="/static/images/icons/edit-delete.png" title="{% trans 'Delete Application' %}"></a>
{% endif %}
<a href="{% url print_application application.id %}"><img src="/static/images/icons/application-pdf.png" title="{% trans 'Print Application' %}"></a>
</h4>
<span clear="all"> </span>
{{ application.submitter }}
{% if user == application.submitter %}
<img src="/static/images/icons/user-information.png" title="{% trans 'You!' %}">
{% endif %}
{% if min_supporters > 0 %}
<h4>{% trans "Supporters" %}: *</h4>
{% if application.supporter.count == 0 %}
-
{% else %}
{% for supporter in application.supporter.all %}
{{ forloop.counter }}. {{supporter }}
{% if not forloop.last %}<br>{% endif %}
{% endfor %}
{% endif %}
{% endif %}
<h4>{% trans "Status" %}:</h4>
{% if application.status != "pub" %}
{% trans application.get_status_display %}
<br>
{% endif %}
{% for note in application.notes %}
{{ note }}
{% if not forloop.last %}<br>{% endif %}
{% endfor %}
{% with application.poll_set.all as polls %}
{% if poll|length > 0 %}
<h4>{% trans "Poll result" %}:</h4>
{% for poll in polls %}
{% if poll.has_vote %}
{% if polls|length > 1 %}
{{forloop.counter}}. {% trans "Poll" %}:<br>&nbsp;&nbsp;&nbsp;
{% endif %}
{% for option in poll.options %}
{% trans "Yes" %}: {{ option.yes }} |
{% trans "No" %}: {{ option.no }} |
{% trans "Abstention" %}: {{ option.undesided }}
{% endfor %}
<br>
{% else %}
{% if polls|length == 1 %}
<i>{% trans "Not (yet) available." %}</i>
{% endif %}
{% endif %}
{% endfor %}
{% endif %}
{% endwith %}
<h4>{% trans "Creation Time" %}:</h4>
{{ application.creation_time }}
<p></p>
{% if "edit" in actions %}
<a href="{% url application_edit application.id %}">
<button><span class="icon edit">{%trans 'Edit' %}</span></button>
</a>
{% endif %}
{% if "wit" in actions and user == application.submitter %}
<p></p>
<a href='{% url application_set_status application.id 'wit' %}'>
<button><span class="icon revert">{%trans 'Withdraw' %}</span></button>
</a>
{% endif %}
{% if perms.application.can_support_application and min_supporters > 0 %}
{% if "unsupport" in actions %}
<p></p>
<a href='{% url application_unsupport application.id %}'>
<button><span class="icon remove">{% trans 'Unsupport' %}</span></button>
</a>
{% endif %}
{% if "support" in actions %}
<p></p>
<a href='{% url application_support application.id %}'>
<button><span class="icon add">{% trans 'Support' %}</span></button>
</a>
{% endif %}
{% endif %}
</div>
{% if min_supporters > 0 %}
<small>* {% trans "minimum required supporters" %}: {{ min_supporters }}</small>
{% endif %}
<br><br>
{% if perms.application.can_manage_application %}
<div class="box">
<h4><b>{% trans "Manage application" %}</b></h4>
{% if "per" in actions or "nop" in actions or "setnumber" in actions %}
<h4>{% trans "Formal validation" %}:</h4>
{% if "per" in actions %}
<a href='{% url application_permit application.id %}'><button><span class="icon ok-blue">{% trans 'Permit' %}</span></button></a>
{% endif %}
{% if "nop" in actions %}
<a href='{% url application_notpermit application.id %}'><button><span class="icon stop">{% trans 'Not permit (reject)' %}</span></button></a>
{% endif %}
{% if "setnumber" in actions %}
<a href='{% url application_set_number application.id %}'><button><span class="icon number">{% trans 'Set Number' %}</span></button></a>
{% endif %}
</p>
{% endif %}
{% if "createitem" in actions %}
<h4></h4>
<a href='{% url item_new_default 'ItemApplication' application.id %}'>
<button><span class="icon item">{%trans 'New agenda item' %}</span></button>
</a>
{% endif %}
{% if application.status != "pub" %}
<h4></h4>
{% for poll in application.poll_set.all %}
{% if poll.has_vote %}<a href="{% url application_poll_view poll.id %}">{% endif %}
{{ forloop.counter }}. {% trans "Poll" %}:
{% if poll.has_vote %}</a>{% endif %}
<a href="{% url application_poll_delete poll.id %}"><img src="/static/images/icons/edit-delete.png" title="{% trans 'Delete Poll' %}"></a>
<br>&nbsp;&nbsp;&nbsp;
{% if poll.has_vote %}
{% for option in poll.options %}
{% trans "Yes" %}: {{ option.yes }} |
{% trans "No" %}: {{ option.no }} |
{% trans "Abstention" %}: {{ option.undesided }}
{% endfor %}
{% if forloop.last %}
{% if "genpoll" in actions %}
<br>
<a href='{% url application_gen_poll application.id %}'>
<button><span class="icon poll">{%trans 'New poll' %}</span></button>
</a>
{% endif %}
{% endif %}
{% else %}
<a href="{% url application_poll_view poll.id %}">{% trans "Enter vote result!" %}</a>
{% endif %}
{% if not forloop.last %}<br>{% endif %}
{% empty %}
{% if "genpoll" in actions %}
<a href='{% url application_gen_poll application.id %}'>
<button><span class="icon poll">{%trans 'New poll' %}</span></button>
</a>
{% endif %}
{% endfor %}
{% endif %}
{% if "acc" in actions or "rej" in actions %}
<h4>{% trans "Result after vote" %}:</h4>
{% if "acc" in actions %}
<a href='{% url application_set_status application.id 'acc' %}'>
<button><span class="icon accept">{%trans 'Accepted' %}</span></button>
</a>
{% endif %}
{% if "rej" in actions %}
<a href='{% url application_set_status application.id 'rej' %}'>
<button><span class="icon reject">{%trans 'Rejected' %}</span></button>
</a>
{% endif %}
{% endif %}
{% if "adj" in actions or "noc" in actions or "com" in actions or "wit" in actions %}
<h4>{% trans 'Result after debate' %}:</h4>
{% if "adj" in actions %}
<a href='{% url application_set_status application.id 'adj' %}'><button>{% trans 'Adjourned' %}</button></a><br>
{% endif %}
{% if "noc" in actions %}
<a href='{% url application_set_status application.id 'noc' %}'><button>{% trans 'Not Concerned' %}</button></a><br>
{% endif %}
{% if "com" in actions %}
<a href='{% url application_set_status application.id 'com' %}'><button>{% trans 'Commited a bill' %}</button></a><br>
{% endif %}
{% if "wit" in actions %}
<a href='{% url application_set_status application.id 'wit' %}'><button>{% trans 'Withdrawed by Submitter' %}</button></a>
{% endif %}
{% endif %}
<p></p>
<hr>
<h4>{%trans "For Administration only:" %}</h4>
<a href='{% url application_reset application.id %}'>
<button><span class="icon undo">{%trans 'Reset' %}</span></button>
</a>
</div>
{% endif %} {# end perms.application.can_support_application #}
</div> <!-- end sidebar -->
<div>
<p>{% trans "Application" %}
{% if application.number != None %}
#{{ application.number }}
{% else %}
<i>[no number]</i>
{% endif %}</p>
<h1>{{ application.title }}</h1>
{{ application.text|linebreaks }}
<h2>{% trans "Reason" %}:</h2>
{% if application.reason %}
{{ application.reason|linebreaks }}
{% else %}
-
{% endif %}
{% if application.versions|length > 1 %}
<h2>{% trans "Revisions" %}:</h2>
<table class="table">
<tr>
<th>#</th>
<th>{% trans "Time" %}</th>
<th>{% trans "Title" %}</th>
<th>{% trans "Text" %}</th>
<th>{% trans "Reason" %}</th>
</tr>
{% for revision in application.versions %}
<tr>
<td>{{ revision.aid }} </td>
<td><i>{{ revision.time }}</i></td>
<td>
{% ifchanged %}
<b>{{ revision.title }}</b>
{% else %}
<i>[{% trans "unchanged" %}]</i>
{% endifchanged %}
</td>
<td>
{% ifchanged %}
{{ revision.text|linebreaks }}
{% else %}
<i>[{% trans "unchanged" %}]</i>
{% endifchanged %}
</td>
<td>
{% ifchanged %}
{{ revision.reason|linebreaks }}
{% else %}
<i>[{% trans "unchanged" %}]</i>
{% endifchanged %}
</td>
</tr>
{% endfor %}
</table>
{% endif %}
{% if perms.application.can_manage_application %}
<h2>{% trans "Log" %}:</h2>
{{ application.log|linebreaks }}
{% endif %}
</div>
{% endblock %}

View File

@ -0,0 +1,40 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.application.test
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Unit tests for the application app.
:copyright: 2011 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from django.test import TestCase
from django.test.client import Client
from django.contrib.auth.models import User
from openslides.application.models import Application, AVersion
class ApplicationTest(TestCase):
def setUp(self):
self.admin = User.objects.create_user('testadmin', '', 'default')
self.anonym = User.objects.create_user('testanoym', '', 'default')
self.app1 = Application(submitter=self.admin)
self.app1.save()
def refresh(self):
self.app1 = Application.objects.get(pk=self.app1.id)
def testVersion(self):
self.assertTrue(self.app1.versions.exists())
self.assertEqual(self.app1.last_version, self.app1.versions[0])
self.assertEqual(self.app1.creation_time, self.app1.last_version.time)
self.app1.title = "app1"
self.app1.save()
self.refresh()
self.assertEqual(self.app1.versions.count(), 2)
self.assertEqual(self.app1.last_version, self.app1.versions[1])

View File

@ -0,0 +1,69 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.application.urls
~~~~~~~~~~~~~~~~~~~~~~~~~~~
URL list for the application app.
:copyright: 2011 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from django.conf.urls.defaults import *
urlpatterns = patterns('application.views',
url(r'^application/$', 'overview', \
name='application_overview'),
url(r'^application/(?P<application_id>\d+)$', 'view', \
name='application_view'),
url(r'^application/new$', 'edit', \
name='application_new'),
url(r'^application/(?P<application_id>\d+)/edit$', 'edit', \
name='application_edit'),
url(r'^application/(?P<application_id>\d+)/del$', 'delete', \
name='application_delete'),
url(r'^application/(?P<application_id>\d+)/setnumber$', 'set_number', \
name='application_set_number'),
url(r'^application/(?P<application_id>\d+)/setstatus/' \
'(?P<status>[a-z]{3})$', 'set_status', \
name='application_set_status'),
url(r'^application/(?P<application_id>\d+)/permit$', 'permit', \
name='application_permit'),
url(r'^application/(?P<application_id>\d+)/notpermit$', 'notpermit', \
name='application_notpermit'),
url(r'^application/(?P<application_id>\d+)/reset$', 'reset', \
name='application_reset'),
url(r'^application/(?P<application_id>\d+)/support$', 'support', \
name='application_support'),
url(r'^application/(?P<application_id>\d+)/unsupport$', 'unsupport', \
name='application_unsupport'),
url(r'^application/(?P<application_id>\d+)/gen_poll$', 'gen_poll', \
name='application_gen_poll'),
url(r'^application/print$', 'print_application', \
name='print_applications'),
url(r'^application/(?P<application_id>\d+)/print$', 'print_application', \
name='print_application'),
url(r'^application/poll/(?P<poll_id>\d+)/print$', 'print_application_poll', name='print_application_poll'),
url(r'^application/poll/(?P<poll_id>\d+)$', 'view_poll', \
name='application_poll_view'),
url(r'^application/poll/(?P<poll_id>\d+)/del$', 'delete_poll', \
name='application_poll_delete'),
)

View File

@ -0,0 +1,350 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.application.views
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Views for the application app.
:copyright: 2011 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from django.shortcuts import redirect
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _
from openslides.application.models import Application
from openslides.application.forms import ApplicationForm, \
ApplicationManagerForm
from openslides.poll.models import Poll
from openslides.poll.forms import OptionResultForm, PollInvalidForm
from openslides.utils.utils import template, permission_required, \
render_to_forbitten, del_confirm_form
from openslides.utils.pdf import print_application, print_application_poll
from openslides.system.api import config_get
@permission_required('application.can_view_application')
@template('application/overview.html')
def overview(request):
"""
View all applications
"""
query = Application.objects
if 'number' in request.GET:
query = query.filter(number=None)
if 'status' in request.GET:
if 'statusvalue' in request.GET and 'on' in request.GET['status']:
query = query.filter(status__iexact=request.GET['statusvalue'])
try:
sort = request.GET['sort']
if sort in ['number', 'supporter', 'status', 'submitter', \
'aversion__time', 'aversion__title']:
query = query.order_by(sort)
except KeyError:
query = query.order_by("number")
if 'reverse' in request.GET:
query = query.reverse()
if 'needsup' in request.GET:
applications = []
for application in query.all():
if not application.enough_supporters:
applications.append(application)
else:
applications = query.all()
return {
'applications': applications,
'min_supporters': int(config_get('application_min_supporters')),
}
@permission_required('application.can_view_application')
@template('application/view.html')
def view(request, application_id):
"""
View one application.
"""
application = Application.objects.get(pk=application_id)
revisions = application.versions
actions = application.get_allowed_actions(user=request.user)
return {
'application': application,
'revisions': revisions,
'actions': actions,
'min_supporters': int(config_get('application_min_supporters')),
}
@login_required
@template('application/edit.html')
def edit(request, application_id=None):
"""
View a form to edit or create a application.
"""
if request.user.has_perm('application.can_manage_application'):
is_manager = True
else:
is_manager = False
if not is_manager \
and not request.user.has_perm('application.can_insert_application'):
messages.error(request, _("You have not the necessary rights to edit or insert applications."))
return redirect(reverse('application_overview'))
if application_id is not None:
application = Application.objects.get(id=application_id)
if not request.user == application.submitter and not is_manager:
messages.error(request, _("You can not edit this application. You are not the submitter."))
return redirect(reverse('application_view', args=[application.id]))
else:
application = None
if request.method == 'POST':
dataform = ApplicationForm(request.POST, prefix="data")
valid = dataform.is_valid()
if is_manager:
managerform = ApplicationManagerForm(request.POST, \
instance=application, \
prefix="manager")
valid = valid and managerform.is_valid()
else:
managerform = None
if valid:
del_supporters = True
if is_manager:
application = managerform.save()
elif application_id is None:
application = Application(submitter=request.user)
application.title = dataform.cleaned_data['title']
application.text = dataform.cleaned_data['text']
application.reason = dataform.cleaned_data['reason']
application.save(request.user)
if application_id is None:
messages.success(request, _('New application was successfully created.'))
else:
messages.success(request, _('Application was successfully modified.'))
return redirect(reverse('application_view', args=[application.id]))
else:
if application_id is None:
initial = {'text': config_get('application_preamble')}
else:
if application.status == "pub" and application.supporter.count() > 0:
if request.user.has_perm('application.can_manage_applications'):
messages.warning(request, _("Attention: Do you really want to edit this application? The supporters will not be removed automatically. Please check if the supports are valid after your changing."))
else:
messages.warning(request, _("Attention: Do you really want to edit this application? All <b>%s</b> supporters will be removed! Try to convince the supporters again.") % application.supporter.count() )
initial = {'title': application.title,
'text': application.text,
'reason': application.reason}
dataform = ApplicationForm(initial=initial, prefix="data")
if is_manager:
if application_id is None:
initial = {'submitter': str(request.user.id)}
else:
initial = {}
managerform = ApplicationManagerForm(initial=initial, \
instance=application, \
prefix="manager")
else:
managerform = None
return {
'form': dataform,
'managerform': managerform,
'application': application,
}
@template('application/view.html')
def delete(request, application_id):
"""
delete a application.
"""
application = Application.objects.get(id=application_id)
if not 'delete' in application.get_allowed_actions(user=request.user):
messages.error(request, _("You can not delete application <b>%s</b>.") % application)
else:
if request.method == 'POST':
try:
title = str(application)
application.delete()
messages.success(request, _("Application <b>%s</b> was successfully deleted.") % title)
except NameError, name:
messages.error(request, name)
else:
del_confirm_form(request, application)
return redirect(reverse('application_overview'))
@permission_required('application.can_manage_application')
@template('application/view.html')
def set_number(request, application_id):
"""
set a number for an application.
"""
try:
Application.objects.get(pk=application_id).set_number(user=request.user)
messages.success(request, _("Application number was successfully set."))
except Application.DoesNotExist:
pass
except NameError:
pass
return redirect(reverse('application_view', args=[application_id]))
@permission_required('application.can_manage_application')
@template('application/view.html')
def permit(request, application_id):
"""
permit an application.
"""
try:
Application.objects.get(pk=application_id).permit(user=request.user)
messages.success(request, _("Application was successfully permitted."))
except Application.DoesNotExist:
pass
return redirect(reverse('application_view', args=[application_id]))
@permission_required('application.can_manage_application')
@template('application/view.html')
def notpermit(request, application_id):
"""
reject (not permit) an application.
"""
try:
Application.objects.get(pk=application_id).notpermit(user=request.user)
messages.success(request, _("Application was successfully rejected."))
except Application.DoesNotExist:
pass
return redirect(reverse('application_view', args=[application_id]))
@template('application/view.html')
def set_status(request, application_id=None, status=None):
"""
set a status of an application.
"""
try:
if status is not None:
application = Application.objects.get(pk=application_id)
application.set_status(user=request.user, status=status)
messages.success(request, _("Application status was set to: <b>%s</b>.") % application.get_status_display())
except Application.DoesNotExist:
pass
return redirect(reverse('application_view', args=[application_id]))
@permission_required('application.can_manage_application')
@template('application/view.html')
def reset(request, application_id):
"""
reset an application.
"""
try:
Application.objects.get(pk=application_id).reset(user=request.user)
messages.success(request, _("Application status was reset.") )
except Application.DoesNotExist:
pass
return redirect(reverse('application_view', args=[application_id]))
@permission_required('application.can_support_application')
@template('application/view.html')
def support(request, application_id):
"""
support an application.
"""
try:
Application.objects.get(pk=application_id).support(user=request.user)
messages.success(request, _("You have support the application successfully.") )
except Application.DoesNotExist:
pass
return redirect(reverse('application_view', args=[application_id]))
@permission_required('application.can_support_application')
@template('application/view.html')
def unsupport(request, application_id):
"""
unsupport an application.
"""
try:
Application.objects.get(pk=application_id).unsupport(user=request.user)
messages.success(request, _("You have unsupport the application successfully.") )
except Application.DoesNotExist:
pass
return redirect(reverse('application_view', args=[application_id]))
@permission_required('application.can_manage_application')
@template('application/view.html')
def gen_poll(request, application_id):
"""
gen a poll for this application.
"""
try:
count = Poll.objects.filter(application=application_id).count()
Application.objects.get(pk=application_id).gen_poll(user=request.user, pollcount=count+1)
messages.success(request, _("New poll was successfully created.") )
except Application.DoesNotExist:
pass
return redirect(reverse('application_view', args=[application_id]))
@permission_required('application.can_manage_application')
def delete_poll(request, poll_id):
"""
delete a poll from this application
"""
poll = Poll.objects.get(pk=poll_id)
application = poll.application
count = application.poll_set.filter(id__lte=poll_id).count()
if request.method == 'POST':
poll.delete()
messages.success(request, _('Poll was successfully deleted.'))
else:
del_confirm_form(request, poll, name=_("the %s. poll") % count)
return redirect(reverse('application_view', args=[application.id]))
@permission_required('application.can_view_poll')
@template('application/poll_view.html')
def view_poll(request, poll_id):
"""
view a poll for this application.
"""
poll = Poll.objects.get(pk=poll_id)
options = poll.options
if request.user.has_perm('application.can_manage_applications'):
if request.method == 'POST':
form = PollInvalidForm(request.POST, prefix="poll")
if form.is_valid():
poll.voteinvalid = form.cleaned_data['invalid'] or 0
poll.save()
for option in options:
option.form = OptionResultForm(request.POST,
prefix="o%d" % option.id)
if option.form.is_valid():
option.voteyes = option.form.cleaned_data['yes']
option.voteno = option.form.cleaned_data['no'] or 0
option.voteundesided = option.form. \
cleaned_data['undesided'] or 0
option.save()
else:
form = PollInvalidForm(initial={'invalid': poll.voteinvalid}, prefix="poll")
for option in options:
option.form = OptionResultForm(initial={
'yes': option.voteyes,
'no': option.voteno,
'undesided': option.voteundesided,
}, prefix="o%d" % option.id)
return {
'poll': poll,
'form': form,
'options': options,
}

View File

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.assignment.admin
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Register app for admin site.
:copyright: 2011 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from django.contrib import admin
from assignment.models import Assignment
admin.site.register(Assignment)

View File

@ -0,0 +1,35 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.assignment.forms
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Forms for the assignment app.
:copyright: 2011 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from django.forms import ModelForm, Form, ModelChoiceField, Select
from django.utils.translation import ugettext as _
from participant.models import Profile
from assignment.models import Assignment
class AssigmentForm(ModelForm):
error_css_class = 'error'
required_css_class = 'required'
class Meta:
model = Assignment
exclude = ('status', 'profile')
class AssigmentRunForm(Form):
error_css_class = 'error'
candidate = ModelChoiceField(
widget=Select(attrs={'class': 'medium-input'}), \
queryset=Profile.objects.all().order_by('user__first_name'), \
label=_("Nominate a participant"))

View File

@ -0,0 +1,110 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.assignment.models
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Models for the assignment app.
:copyright: 2011 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from django.db import models
from django.utils.translation import ugettext as _
from participant.models import Profile
class Assignment(models.Model):
STATUS = (
('sea', _('Searching for candidates')),
('vot', _('Voting')),
('fin', _('Finished')),
)
name = models.CharField(max_length=100, verbose_name = _("Name"))
description = models.TextField(null=True, blank=True, verbose_name = _("Description"))
assignment_number = models.PositiveSmallIntegerField(verbose_name = _("Number of available posts"))
polldescription = models.CharField(max_length=50, null=True, blank=True, verbose_name = _("Short description (for ballot paper)"))
profile = models.ManyToManyField(Profile, null=True, blank=True)
status = models.CharField(max_length=1, choices=STATUS, default='sea')
def set_status(self, status):
error = True
for a, b in Assignment.STATUS:
if status == a:
error = False
break
if error:
raise NameError(_('%s is not a valid status.') % status)
if self.status == status:
raise NameError(_('The assignment status is already %s.') % self.status)
self.status = status
self.save()
def run(self, profile):
"""
run for a vote
"""
if self.is_candidate(profile):
raise NameError(_('<b>%s</b> is already a candidate.') % profile)
self.profile.add(profile)
def delrun(self, profile):
"""
stop running for a vote
"""
if self.is_candidate(profile):
self.profile.remove(profile)
else:
raise NameError(_('%s is no candidate') % profile)
def is_candidate(self, profile):
if profile in self.profile.get_query_set():
return True
else:
return False
def gen_poll(self):
from poll.models import Poll
poll = Poll()
poll.title = _("Election for %s") % self.name
# Option A: candidates <= available posts -> yes/no/abstention
if self.profile.count() <= self.assignment_number:
poll.optiondecision = True
else:
poll.optiondecision = False
# Option B: candidates == 1 -> yes/no/abstention
#if self.profile.count() == 1:
# poll.optiondecision = True
#else:
# poll.optiondecision = False
poll.assignment = self
poll.description = self.polldescription
poll.save()
for profile in self.profile.get_query_set():
poll.add_option(profile)
return poll
@models.permalink
def get_absolute_url(self, link='view'):
if link == 'view':
return ('assignment_view', [str(self.id)])
if link == 'delete':
return ('assignment_delete', [str(self.id)])
def __unicode__(self):
return self.name
class Meta:
permissions = (
('can_view_assignment', "Can see the assignments"),
('can_nominate_other', "Can nominate another person"
" for a election"),
('can_nominate_self', "Can nominate hisself for a election"),
('can_manage_assignment', "Can manage assignments"),
)

View File

@ -0,0 +1,13 @@
{% extends "base.html" %}
{% load tags %}
{% block submenu %}
{% url election_overview as url_electionoverview %}
<h4 class="sectiontitle">{%trans "Elections" %}</h4>
<ul>
<li class="{% if request.path == url_electionoverview %}selected{% endif %}"><a href="{% url assignment_overview %}">{%trans "All elections" %}</a></li>
{% if perms.assignment.can_manage_assignment %}
<li class="{% active request '/assignment/new' %}"><a href="{% url assignment_new %}">{%trans "New election" %}</a></li>
{% endif %}
</ul>
{% endblock %}

View File

@ -0,0 +1,23 @@
{% extends "assignment/base_assignment.html" %}
{% block title %}{{ block.super }} - {%trans "Election" %}{% endblock %}
{% block content %}
{% if assignment %}
<h1>{%trans "Edit election" %}</h1>
{% else %}
<h1>{%trans "New election" %}</h1>
{% endif %}
<form action="" method="post">{% csrf_token %}
{{ form.as_p }}
<button type="submit">
<span class="icon ok">{%trans 'Save' %}</span>
</button>
<a href='{% url assignment_overview %}'>
<button type="button">
<span class="icon cancel">{%trans 'Cancel' %}</span>
</button>
</a>
</form>
{% endblock %}

View File

@ -0,0 +1,44 @@
{% extends "assignment/base_assignment.html" %}
{% block title %}{{ block.super }} - {%trans "Elections" %}{% endblock %}
{% block content %}
<h1>{%trans "Elections" %}</h1>
<p><form action="{{request.url}}" name="filter" method="get">
{%trans "Filter" %}:
<select class="default-input" name="status" onchange="document.forms['filter'].submit()">
<option value="---">-- {%trans "Status" %}--</option>
<option value="sea" {% if 'sea' in request.GET.status %}selected{% endif %}>{%trans "Searching for candidates" %}</option>
<option value="vot" {% if 'vot' in request.GET.status %}selected{% endif %}>{%trans "Voting" %}</option>
<option value="fin" {% if 'fin' in request.GET.status %}selected{% endif %}>{%trans "Finished" %}</option>
</select>
</form>
</p>
<br>
<table>
<tr>
<th><a href="?sort=name{% if 'name' in request.GET.sort and 'reverse' not in request.GET %}&reverse{%endif%}">{%trans "Elections" %}</a></th>
<th>{%trans "Candidates" %}</th>
<th><a href="?sort=status{% if 'status' in request.GET.sort and 'reverse' not in request.GET %}&reverse{%endif%}">{%trans "Status" %}</a></th>
{% if perms.assignment.can_manage_assignment %}
<th>{%trans "Actions" %}</th>
{% endif %}
</tr>
{% for assignment in assignments %}
<tr class="{% cycle '' 'odd' %}">
<td><a href="{% url assignment_view assignment.id %}">{{ assignment }}</a></td>
<td>{{ assignment.profile.count }} / {{ assignment.assignment_number }}</td>
<td>{{ assignment.get_status_display }}</td>
{% if perms.assignment.can_manage_assignment %}
<td><a href="{% url assignment_edit assignment.id %}"><img src="/static/images/icons/document-edit.png" title="{%trans 'Edit assignment' %}"></a>
<a href="{% url assignment_delete assignment.id %}"><img src="/static/images/icons/edit-delete.png" title="{%trans 'Delete assignment' %}"></a>
</td>
{% endif %}
</tr>
{% empty %}
<tr>
<td colspan="4"><i>{%trans "No assignments available." %}</i></td>
</tr>
{% endfor %}
</table>
{% endblock %}

View File

@ -0,0 +1,58 @@
{% extends 'assignment/base_assignment.html' %}
{% block title %}{{ block.super }} - {%trans "Poll" %} "{{ poll.title }}"{% endblock %}
{% block content %}
<h1>{{ poll.title }}</h1>
<p>{{ ballotnumber }}. {%trans "ballot" %}: {{options.count}}
{% blocktrans count counter=options|length %}candidate{% plural %}candidates{% endblocktrans %}
</p>
<p><b>{% trans "Short description" %}:</b> {{ poll.description }}</p>
<form action="" method="post">{% csrf_token %}
<table class="table">
<tr>
<th>{%trans "Option" %}</th>
{% if poll.optiondecision %}
<th>{%trans "Yes" %}</th>
<th>{%trans "No" %}</th>
<th>{%trans "Abstention" %}</th>
{% else %}
<th>{%trans "Votes" %}</th>
{% endif %}
</tr>
{% for option in options %}
<tr>
<td>{{ option }}</td>
{% if poll.optiondecision %}
<td>{{ option.form.yes.errors }}{{ option.form.yes }}</td>
<td>{{ option.form.no.errors }}{{ option.form.no }}</td>
<td>{{ option.form.undesided.errors }}{{ option.form.undesided }}</td>
{% else %}
<td>{{ option.form.yes.errors }}{{ option.form.yes }}</td>
{% endif %}
</tr>
{% endfor %}
</table>
<label for="id_voteinvalid">{% trans "Invalid" %}:</label>
{{ form.invalid.errors }}{{ form.invalid }}
{% if perms.poll.can_manage_poll %}
<p>
<button type="submit">
<span class="icon ok">{%trans 'Apply' %}</span>
</button>
<a href='{% url assignment_view poll.assignment.id %}'>
<button type="button">
<span class="icon previous">{%trans 'Back to election' %}</span>
</button>
</a>
<a href='{% url print_assignment_poll poll.id ballotnumber poll.assignment.assignment_number %}'>
<button type="button">
<span class="icon pdf">{%trans 'Print ballot' %}</span>
</button>
</a>
</p>
{% endif %}
</form>
{% endblock %}

View File

@ -0,0 +1,185 @@
{% extends "assignment/base_assignment.html" %}
{% block title %}{{ block.super }} - {% trans "Assignment" %} "{{ assignment }}"{% endblock %}
{% block content %}
<div id="sidebar">
<div class="box">
<h4>{% trans "Status" %}:</h4>
{% trans assignment.get_status_display %}
<h4>{% trans "Number of available posts" %}:</h4>
{{ assignment.assignment_number }}
</div>
<br><br>
{% if perms.assignment.can_manage_assignment %}
<div class="box">
<h4><b>{% trans "Manage election" %}</b></h4>
<h4>{% trans "Change status" %}:</h4>
<input type="radio" name="status" onclick="window.location.href='{% url assignment_set_status assignment.id 'sea' %}';"
{% if 'sea' in assignment.status %}checked{% endif %}>{% trans 'Searching for candidates' %}<br>
<input type="radio" name="status" onclick="window.location.href='{% url assignment_set_status assignment.id 'vot' %}';"
{% if 'vot' in assignment.status %}checked{% endif %}>{% trans 'Voting' %}<br>
<input type="radio" name="status" onclick="window.location.href='{% url assignment_set_status assignment.id 'fin' %}';"
{% if 'fin' in assignment.status %}checked{% endif %}>{% trans 'Finish' %}
<h4></h4>
<a href="{% url assignment_edit assignment.id %}">
<button><span class="icon edit">{%trans 'Edit' %}</span></button>
</a>
{% if not assignment.itemassignment_set.all %}
<h4></h4>
<a href='{% url item_new_default 'ItemAssignment' assignment.id %}'>
<button type="button">
<span class="icon item">{%trans 'New agenda item' %}</span>
</button>
</a>
{% endif %}
</div>
{% endif %}
</div>
<div style="margin-right: 250px; min-width: 400px;">
<h1>{{ assignment }}</h1>
<p>{{ assignment.description }}</p>
<h3>{% trans "Candidates" %}</h3>
<ol>
{% for profile in assignment.profile.all|dictsort:"user.first_name" %}
<li>{{ profile }}
{% if perms.assignment.can_manage_assignment %}
{% if assignment.status == "sea" or assignment.status == "vot" %}
<a href="{% url assignment_delother assignment.id profile.id %}"><img src="/static/images/icons/edit-delete.png" title="{% trans 'Remove candidate' %}"></a>
{% endif %}
{% endif %}
</li>
{% empty %}
<li style="list-style: none outside none;"><i>{% trans "No candidates available." %}</i></li>
{% endfor %}
</ol>
{% if assignment.status == "sea" or perms.assignment.can_manage_assignment and assignment.status == "vot" %}
{% if perms.assignment.can_nominate_self or perms.assignment.can_nominate_other %}
<form action="" method="post">{% csrf_token %}
{% if perms.assignment.can_nominate_self %}
<p>
{% if user.profile in assignment.profile.all %}
<a href='{% url assignment_delrun assignment.id %}'>
<button type="button">
<span class="icon removeuser">{%trans 'Withdraw self candidature' %}</span>
</button>
</a>
{% else %}
{% if user.profile %}
<a href='{% url assignment_run assignment.id %}'>
<button type="button">
<span class="icon adduser">{%trans 'Self candidature' %}</span>
</button>
</a>
{% endif %}
{% endif %}
</p>
{% endif %}
{% if perms.assignment.can_nominate_other %}
{% for field in form %}
<label>{{ field.label }}:</label>
<nobr>{{ field }}
{% if perms.participant.can_view_participants and perms.participant.can_manage_participants %}
<a href="{% url user_new %}"><img src="/static/images/icons/list-add-user.png" title="{% trans 'Add new participant' %}"></a>
{% endif %}
</nobr>
{% endfor %}
<p>
<button type="submit">
<span class="icon ok">{%trans 'Apply' %}</span>
</button>
</p>
{% endif %}
</form>
{% endif %}
{% endif %}
<p><br></p>
<h3>{% trans "Election results" %}</h3>
{% if assignment.poll_set.all.count > 0 %}
<table style="width: auto;">
<tr>
<th></th>
{% with ballotnumber=assignment.poll_set.all.count %}
<th colspan="{{ ballotnumber|add:'1' }}" style="text-align: center;">
{% trans "ballot" %}</th>
{% endwith %}
</tr>
<tr>
<th>{% trans "Candidates" %}</th>
{% for poll in assignment.poll_set.all %}
<th style="vertical-align: top; white-space:nowrap;">{% if perms.assignment.can_manage_assignment %}<a href="{% url assignment_poll_view poll.id forloop.counter %}">{% endif %}
{{forloop.counter}}.
{% if perms.assignment.can_manage_assignment %}
</a>
<a href="{% url assignment_poll_delete poll.id %}"><img src="/static/images/icons/edit-delete.png" title="{% trans 'Delete Poll' %}"></a>
{% endif %}
</th>
{% endfor %}
{% if assignment.profile.count > 0 and perms.assignment.can_manage_assignment and assignment.status == "vot" %}
<th>
{% with ballotnumber=assignment.poll_set.all.count %}
<a href='{% url assignment_gen_poll assignment.id ballotnumber|add:'1' %}'>
<button type="button">
<span class="icon poll">{%trans 'New ballot' %}</span>
</button>
</a>
{% endwith %}
</th>
{% endif %}
</tr>
{% for vote in votes %}
<tr class="{% cycle 'odd' '' %}">
{% for v in vote %}
<td style="white-space:nowrap;">{% if v %}
{% if v|length == 3 %}
<img src="/static/images/icons/voting-yes.png" title="{% trans 'Yes' %}"> {% if v.0 %}{{ v.0 }}{% else %}&empty;{% endif %}<br>
<img src="/static/images/icons/voting-no.png" title="{% trans 'No' %}"> {% if v.1 %}{{ v.1 }}{% else %}&empty;{% endif %}<br>
<img src="/static/images/icons/voting-abstention.png" title="{% trans 'Abstention' %}"> {% if v.2 %}{{ v.2 }}{% else %}&empty;{% endif %}<br>
{% else %}
{{ v }}
{% endif %}
{% else %}&empty;{% endif %}
</td>
{% endfor %}
{% if assignment.profile.count > 0 and perms.assignment.can_manage_assignment and assignment.status == "vot" %}
<td></td>
{% endif %}
</tr>
{% endfor %}
</table>
{% else %}
<i>{% trans "No ballots available." %}</i>
{% if assignment.profile.count > 0 and perms.assignment.can_manage_assignment and assignment.status == "vot" %}
{% with ballotnumber=assignment.poll_set.all.count %}
<p><a href='{% url assignment_gen_poll assignment.id ballotnumber|add:'1' %}'>
<button type="button">
<span class="icon poll">{%trans 'New ballot' %}</span>
</button>
</a></p>
{% endwith %}
{% endif %}
{% endif %}
</div>
{% endblock %}

View File

@ -0,0 +1,30 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.assignment.test
~~~~~~~~~~~~~~~~~~~~~~~~~~
Unit tests for the assignment app.
:copyright: 2011 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from django.test import TestCase
#TODO: Replace these tests!
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.failUnlessEqual(1 + 1, 2)
__test__ = {"doctest": """
Another way to test that 1 + 1 is equal to 2.
>>> 1 + 1 == 2
True
"""}

View File

@ -0,0 +1,54 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.assignments.urls
~~~~~~~~~~~~~~~~~~~~~~~~~~~
URL list for the assignment app.
:copyright: 2011 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from django.conf.urls.defaults import *
urlpatterns = patterns('assignment.views',
url(r'^assignment/$', 'get_overview', \
name='assignment_overview'),
url(r'^assignment/(?P<assignment_id>\d+)$', 'view', \
name='assignment_view'),
url(r'^assignment/new$', 'edit', \
name='assignment_new'),
url(r'^assignment/(?P<assignment_id>\d+)/edit$', 'edit', \
name='assignment_edit'),
url(r'^assignment/(?P<assignment_id>\d+)/del$', 'delete', \
name='assignment_delete'),
url(r'^assignment/(?P<assignment_id>\d+)/setstatus/(?P<status>[a-z]{3})$', 'set_status', \
name='assignment_set_status'),
url(r'^assignment/(?P<assignment_id>\d+)/run$', 'run', \
name='assignment_run'),
url(r'^assignment/(?P<assignment_id>\d+)/delrun$', 'delrun', \
name='assignment_delrun'),
url(r'^assignment/(?P<assignment_id>\d+)/delother/(?P<profile_id>\d+)$', 'delother', \
name='assignment_delother'),
url(r'^assignment/poll/(?P<poll_id>\d+)/print/(?P<ballotnumber>\d+)/(?P<posts>\d+)$', 'print_assignment_poll', \
name='print_assignment_poll'),
url(r'^assignment/(?P<assignment_id>\d+)/gen_poll/(?P<ballotnumber>\d+)$', 'gen_poll', \
name='assignment_gen_poll'),
url(r'^assignment/poll/(?P<poll_id>\d+)/(?P<ballotnumber>\d+)$', 'poll_view', \
name='assignment_poll_view'),
url(r'^assignment/poll/(?P<poll_id>\d+)/del$', 'delete_poll', \
name='assignment_poll_delete'),
)

View File

@ -0,0 +1,242 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.assignment.views
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Views for the assignment app.
:copyright: 2011 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from django.shortcuts import redirect
from django.core.urlresolvers import reverse
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.utils.translation import ugettext as _
from poll.models import Poll, Option
from poll.forms import OptionResultForm, PollInvalidForm
from assignment.models import Assignment
from assignment.forms import AssigmentForm, AssigmentRunForm
from utils.utils import template, permission_required, gen_confirm_form, del_confirm_form
from utils.pdf import print_assignment_poll
from participant.models import Profile
@permission_required('assignment.can_view_assignment')
@template('assignment/overview.html')
def get_overview(request):
query = Assignment.objects
if 'status' in request.GET and '---' not in request.GET['status']:
query = query.filter(status__iexact=request.GET['status'])
try:
sort = request.GET['sort']
if sort in ['name','status']:
query = query.order_by(sort)
except KeyError:
pass
if 'reverse' in request.GET:
query = query.reverse()
assignments = query.all()
return {
'assignments': assignments,
}
@permission_required('assignment.can_view_assignment')
@template('assignment/view.html')
def view(request, assignment_id=None):
form = None
assignment = Assignment.objects.get(pk=assignment_id)
if request.method == 'POST':
if request.user.has_perm('assignment.can_nominate_other'):
form = AssigmentRunForm(request.POST)
if form.is_valid():
user = form.cleaned_data['candidate']
try:
assignment.run(user)
messages.success(request, _("Candidate <b>%s</b> was nominated successfully.") % (user))
except NameError, e:
messages.error(request, e)
else:
if request.user.has_perm('assignment.can_nominate_other'):
form = AssigmentRunForm()
# list of candidates
candidates = set()
for option in Option.objects.filter(poll__assignment=assignment):
candidates.add(option.value)
votes = []
for candidate in candidates:
tmplist = []
tmplist.append(candidate)
for poll in assignment.poll_set.all():
if candidate in poll.options_values:
option = Option.objects.filter(poll=poll).filter(user=candidate)[0]
if poll.optiondecision:
tmplist.append([option.yes, option.no, option.undesided])
else:
tmplist.append(option.yes)
else:
tmplist.append("-")
votes.append(tmplist)
return {'assignment': assignment,
'form': form,
'votes': votes}
@permission_required('assignment.can_manage_assignment')
@template('assignment/edit.html')
def edit(request, assignment_id=None):
"""
View zum editieren und neuanlegen von Wahlen
"""
if assignment_id is not None:
assignment = Assignment.objects.get(id=assignment_id)
else:
assignment = None
if request.method == 'POST':
form = AssigmentForm(request.POST, instance=assignment)
if form.is_valid():
form.save()
if assignment_id is None:
messages.success(request, _('New election was successfully created.'))
else:
messages.success(request, _('Election was successfully modified.'))
return redirect(reverse("assignment_overview"))
else:
form = AssigmentForm(instance=assignment)
return {
'form': form,
'assignment': assignment,
}
@permission_required('assignment.can_manage_assignment')
def delete(request, assignment_id):
assignment = Assignment.objects.get(pk=assignment_id)
if request.method == 'POST':
assignment.delete()
messages.success(request, _('Election <b>%s</b> was successfully deleted.') % assignment)
else:
del_confirm_form(request, assignment)
return redirect(reverse('assignment_overview'))
@permission_required('assignment.can_manage_assignment')
@template('assignment/view.html')
def set_status(request, assignment_id=None, status=None):
try:
if status is not None:
assignment = Assignment.objects.get(pk=assignment_id)
assignment.set_status(status)
messages.success(request, _('Election status was set to: <b>%s</b>.') % assignment.get_status_display())
except Assignment.DoesNotExist:
pass
return redirect(reverse('assignment_view', args=[assignment_id]))
@permission_required('assignment.can_nominate_self')
def run(request, assignment_id):
assignment = Assignment.objects.get(pk=assignment_id)
try:
assignment.run(request.user.profile)
messages.success(request, _('You have set your candidature successfully.') )
except NameError, e:
messages.error(request, e)
except Profile.DoesNotExist:
messages.error(request,
_("You can't candidate. Your user account is only for administration."))
return redirect(reverse('assignment_view', args=assignment_id))
@login_required
def delrun(request, assignment_id):
assignment = Assignment.objects.get(pk=assignment_id)
assignment.delrun(request.user.profile)
messages.success(request, _("You have withdrawn your candidature successfully.") )
return redirect(reverse('assignment_view', args=assignment_id))
@permission_required('assignment.can_manage_assignment')
def delother(request, assignment_id, profile_id):
assignment = Assignment.objects.get(pk=assignment_id)
profile = Profile.objects.get(pk=profile_id)
if request.method == 'POST':
assignment.delrun(profile)
messages.success(request, _("Candidate <b>%s</b> was withdrawn successfully.") % (profile))
else:
gen_confirm_form(request,
_("Do you really want to withdraw <b>%s</b> from the election?") \
% profile, reverse('assignment_delother', args=[assignment_id, profile_id]))
return redirect(reverse('assignment_view', args=assignment_id))
@permission_required('assignment.can_manage_assignment')
def gen_poll(request, assignment_id, ballotnumber):
try:
poll = Assignment.objects.get(pk=assignment_id).gen_poll()
messages.success(request, _("New ballot was successfully created.") )
except Assignment.DoesNotExist:
pass
return redirect(reverse('assignment_poll_view', args=[poll.id, ballotnumber]))
@permission_required('assignment.can_view_assignment')
@template('assignment/poll_view.html')
def poll_view(request, poll_id, ballotnumber=1):
poll = Poll.objects.get(pk=poll_id)
options = poll.options.order_by('user__user__first_name')
assignment = poll.assignment
if request.user.has_perm('assignment.can_manage_assignment'):
if request.method == 'POST':
form = PollInvalidForm(request.POST, prefix="poll")
if form.is_valid():
poll.voteinvalid = form.cleaned_data['invalid'] or 0
poll.save()
success = 0
for option in options:
option.form = OptionResultForm(request.POST, prefix="o%d" % option.id)
if option.form.is_valid():
option.voteyes = option.form.cleaned_data['yes']
option.voteno = option.form.cleaned_data['no'] or 0
option.voteundesided = option.form.cleaned_data['undesided'] or 0
option.save()
success = success + 1
if success == options.count():
messages.success(request, _("Votes are successfully saved.") )
else:
form = PollInvalidForm(initial={'invalid': poll.voteinvalid}, prefix="poll")
for option in options:
option.form = OptionResultForm(initial={
'yes': option.voteyes,
'no': option.voteno,
'undesided': option.voteundesided,
}, prefix="o%d" % option.id)
return {
'poll': poll,
'form': form,
'options': options,
'ballotnumber': ballotnumber,
}
@permission_required('assignment.can_manage_assignment')
def delete_poll(request, poll_id):
poll = Poll.objects.get(pk=poll_id)
assignment = poll.assignment
ballot = assignment.poll_set.filter(id__lte=poll_id).count()
if request.method == 'POST':
poll.delete()
messages.success(request, _('The %s. ballot was successfully deleted.') % ballot)
else:
del_confirm_form(request, poll, name=_("the %s. ballot") % ballot)
return redirect(reverse('assignment_view', args=[assignment.id]))

View File

@ -0,0 +1,141 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.default.settings
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Global settings file.
:copyright: 2011 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
# Django settings for openslides project.
import os
SITE_ROOT = os.path.realpath(os.path.dirname(__file__))
DEBUG = True
TEMPLATE_DEBUG = DEBUG
AUTH_PROFILE_MODULE = 'participant.Profile'
LOGIN_URL = '/login/'
LOGIN_REDIRECT_URL = '/item/'
ADMINS = (
# ('Your Name', 'your_email@domain.com'),
)
MANAGERS = ADMINS
DBPATH = os.path.join(os.path.join(SITE_ROOT, '..'), 'database.db')
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.
'NAME': DBPATH, # Or path to database file if using sqlite3.
'USER': '', # Not used with sqlite3.
'PASSWORD': '', # Not used with sqlite3.
'HOST': '', # Set to empty string for localhost. Not used with sqlite3.
'PORT': '', # Set to empty string for default. Not used with sqlite3.
}
}
# Local time zone for this installation. Choices can be found here:
# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
# although not all choices may be available on all operating systems.
# On Unix systems, a value of None will cause Django to use the same
# timezone as the operating system.
# If running in a Windows environment this must be set to the same as your
# system time zone.
TIME_ZONE = 'Europe/Berlin'
# Language code for this installation. All choices can be found here:
# http://www.i18nguy.com/unicode/language-identifiers.html
LANGUAGE_CODE = 'de'
ugettext = lambda s: s
LANGUAGES = (
('de', ugettext('German')),
('en', ugettext('English')),
)
SITE_ID = 1
# If you set this to False, Django will make some optimizations so as not
# to load the internationalization machinery.
USE_I18N = True
# If you set this to False, Django will not format dates, numbers and
# calendars according to the current locale
USE_L10N = True
# Absolute path to the directory that holds media.
# Example: "/home/media/media.lawrence.com/"
MEDIA_ROOT = ''
# URL that handles the media served from MEDIA_ROOT. Make sure to use a
# trailing slash if there is a path component (optional in other cases).
# Examples: "http://media.lawrence.com", "http://example.com/media/"
MEDIA_URL = ''
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
# trailing slash.
# Examples: "http://foo.com/media/", "/media/".
ADMIN_MEDIA_PREFIX = '/media/'
STATIC_DOC_ROOT = os.path.join(SITE_ROOT, 'static')
# Make this unique, and don't share it with anybody.
SECRET_KEY = '=(v@$58k$fcl4y8t2#q15y-9p=^45y&!$!ap$7xo6ub$akg-!5'
MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'
# List of callables that know how to import templates from various sources.
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
)
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.locale.LocaleMiddleware',
'participant.api.ChangePasswordMiddleware',
)
ROOT_URLCONF = 'openslides.urls'
TEMPLATE_DIRS = (
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
# Always use forward slashes, even on Windows.
# Don't forget to use absolute paths, not relative paths.
os.path.join(SITE_ROOT, 'templates'),
)
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.admin',
'agenda',
'system',
'participant',
'application',
'poll',
'assignment',
'utils',
)
TEMPLATE_CONTEXT_PROCESSORS = (
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'django.core.context_processors.request',
'django.core.context_processors.i18n',
'utils.utils.revision',
)

View File

@ -0,0 +1,23 @@
Steps to update translation for OpenSlides:
------------------------------------------
1. Go to project root directory:
$ cd openslides
2. Update the German po file (locale/de/LC_MESSAGES/django.po):
$ django-admin.py makemessages -l de
3. Edit the German po file: locale/de/LC_MESSAGES/django.po
(Search for "fuzzy" and empty msgstr entries.)
4. Update the German mo file (locale/de/LC_MESSAGES/django.mo):
$ django-admin.py compilemessages
5. Restart server:
$ python manage.py runserver
Additional hints for internationalization (i18n) in Django:
- http://docs.djangoproject.com/en/dev/topics/i18n/internationalization/
- http://docs.djangoproject.com/en/dev/topics/i18n/localization/

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

View File

@ -0,0 +1,16 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.participant.admin
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Register app for admin site.
:copyright: 2011 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from django.contrib import admin
from participant.models import Profile
admin.site.register(Profile)

View File

@ -0,0 +1,45 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.participant.api
~~~~~~~~~~~~~~~~~~~~~~~~~~
Useful functions for the participant app.
:copyright: 2011 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from django.contrib.auth.models import User, get_hexdigest
from django.shortcuts import redirect
from django.core.urlresolvers import reverse
from django.contrib import messages
from django.utils.translation import ugettext as _
class ChangePasswordMiddleware(object):
def process_request(self, request):
if request.user.is_authenticated() and "password_checked" not in request.session:
algo, salt, hsh = request.user.password.split('$')
bad_password = get_hexdigest(algo, salt, "%s%s" % (request.user.first_name, request.user.last_name))
if hsh == bad_password:
messages.info(request, _('You have to change your Password.'))
if request.path_info != '/user/settings' and 'static' not in request.path_info:
return redirect(reverse('user_settings'))
else:
request.session["password_checked"] = True
def gen_username(first_name, last_name):
testname = "%s%s" % (first_name, last_name)
try:
User.objects.get(username=testname)
except User.DoesNotExist:
return testname
i = 0
while True:
i += 1
testname = "%s%s%s" % (first_name, last_name, i)
try:
User.objects.get(username=testname)
except User.DoesNotExist:
return testname

View File

@ -0,0 +1,63 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.participant.forms
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Forms for the participant app.
:copyright: 2011 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from django.forms import Form, ModelForm, CharField, EmailField, FileField, FileInput, MultipleChoiceField
from django.contrib.auth.models import User, Group
from django.utils.translation import ugettext as _
from participant.models import Profile
class UserForm(ModelForm):
error_css_class = 'error'
required_css_class = 'required'
first_name = CharField(label=_("First name"))
last_name = CharField(label=_("Last name"))
class Meta:
model = User
exclude = ('username', 'password', 'is_staff', 'last_login', 'date_joined', 'user_permissions')
class UsernameForm(ModelForm):
error_css_class = 'error'
required_css_class = 'required'
class Meta:
model = User
exclude = ('first_name', 'last_name', 'email', 'is_active','is_superuser', 'groups', 'password', 'is_staff', 'last_login', 'date_joined', 'user_permissions')
class ProfileForm(ModelForm):
error_css_class = 'error'
required_css_class = 'required'
class Meta:
model = Profile
class GroupForm(ModelForm):
error_css_class = 'error'
required_css_class = 'required'
class Meta:
model = Group
class UsersettingsForm(UserForm):
error_css_class = 'error'
required_css_class = 'required'
class Meta:
model = User
fields = ('username', 'first_name', 'last_name', 'email')
class UserImportForm(Form):
error_css_class = 'error'
required_css_class = 'required'
csvfile = FileField(widget=FileInput(attrs={'size':'50'}), label=_("CSV File"))

View File

@ -0,0 +1,46 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.participant.models
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Models for the participant app.
:copyright: 2011 by the OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from django.db import models
from django.contrib.auth.models import User
from django.utils.translation import ugettext as _
class Profile(models.Model):
GENDER_CHOICES = (
('none', _('Not specified')),
('male', _('Male')),
('female', _('Female')),
)
TYPE_CHOICE = (
('delegate', _('Delegate')),
('guest', _('Guest')),
('observer', _('Observer')),
('staff', _('Staff')),
)
user = models.OneToOneField(User, unique=True, editable=False)
gender = models.CharField(max_length=50, choices=GENDER_CHOICES, default='none', verbose_name = _("Gender"))
group = models.CharField(max_length=100, null=True, blank=True, verbose_name = _("Group"))
type = models.CharField(max_length=100, choices=TYPE_CHOICE, default='delegate', verbose_name = _("Typ"))
committee = models.CharField(max_length=100, null=True, blank=True, verbose_name = _("Committee"))
def __unicode__(self):
if self.group:
return "%s (%s)" % (self.user.get_full_name(), self.group)
return "%s" % self.user.get_full_name()
class Meta:
permissions = (
('can_view_participants', "Can see the list of participants"),
('can_manage_participants', "Can manage the participant list"),
)

View File

@ -0,0 +1,19 @@
{% extends "base.html" %}
{% load tags %}
{% block submenu %}
{% url user_overview as url_useroverview %}
<h4 class="sectiontitle">{%trans "Participants" %}</h4>
<ul>
{% if perms.participant.can_view_participants %}
<li class="{% if request.path == url_useroverview %}selected{% endif %}"><a href="{% url user_overview %}">{%trans "All participants" %}</a></li>
{% endif %}
{% if perms.participant.can_manage_participants %}
<li class="{% active request '/user/new' %}"><a href="{% url user_new %}">{%trans "New participant" %}</a></li>
<li><a href="{% url user_group_overview %}">{%trans "All user groups" %}</a></li>
<li><a href="{% url user_group_new %}">{%trans "New user group" %}</a></li>
<li><a href="{% url user_print %}"><img src="/static/images/icons/application-pdf.png"> {%trans 'Print participant list' %}</a></li>
<li><a href="{% url user_import %}"> {%trans 'Import' %}</a></li>
{% endif %}
</ul>
{% endblock %}

View File

@ -0,0 +1,26 @@
{% extends "participant/base_participant.html" %}
{% block title %}{{ block.super }} - {%trans "Participant" %} {% endblock %}
{% block content %}
{% if edituser %}
<h1>{%trans "Edit participant" %}</h1>
{% else %}
<h1>{%trans "New participant" %}</h1>
{% endif %}
<form action="" method="post">{% csrf_token %}
{% if edituser %}
{{ usernameform.as_p }}
{% endif %}
{{ userform.as_p }}
{{ profileform.as_p }}
<button type="submit">
<span class="icon ok">{%trans 'Save' %}</span>
</button>
<a href='{% url user_overview %}'>
<button type="button">
<span class="icon cancel">{%trans 'Cancel' %}</span>
</button>
</a>
</form>
{% endblock %}

View File

@ -0,0 +1,22 @@
{% extends "participant/base_participant.html" %}
{% block title %}{{ block.super }} - {%trans "User Group" %} {% endblock %}
{% block content %}
{% if group %}
<h1>{%trans "Edit user group" %}</h1>
{% else %}
<h1>{%trans "New user group" %}</h1>
{% endif %}
<form action="" method="post">{% csrf_token %}
{{ form.as_p }}
<button type="submit">
<span class="icon ok">{%trans 'Save' %}</span>
</button>
<a href='{% url user_overview %}'>
<button type="button">
<span class="icon cancel">{%trans 'Cancel' %}</span>
</button>
</a>
</form>
{% endblock %}

View File

@ -0,0 +1,24 @@
{% extends "participant/base_participant.html" %}
{% block title %}{{ block.super }} - {%trans "Groups" %}{% endblock %}
{% block content %}
<h1>{%trans "User groups" %}</h1>
<table>
<tr>
<th>{%trans "User Group" %}</th>
<th>{%trans "Action" %}</th>
</tr>
{% for group in groups %}
<tr class="{% cycle '' 'odd' %}">
<td>{{ group.name }}</td>
<td><a href="{% url user_group_edit group.id %}"><img src="/static/images/icons/document-edit.png" title="{%trans 'Edit group' %}"></a>
<a href="{% url user_group_delete group.id %}"><img src="/static/images/icons/edit-delete.png" title="{%trans 'Delete group' %}"></a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="5"><i>{%trans "No participants available." %}</i></td>
</tr>
{% endfor %}
</table>
{% endblock %}

View File

@ -0,0 +1,21 @@
{% extends "participant/base_participant.html" %}
{% block title %}{{ block.super }} - {%trans "Participant Import" %} {% endblock %}
{% block content %}
<h1>Import</h1>
<p>{% trans 'Select a CSV file to import participants!' %}</p>
<p>{% trans '(Required comma separated values: <code>last_name, first_name, email, gender, group, type, committee</code>)' %} </p>
<form enctype="multipart/form-data" action="" method="post">{% csrf_token %}
{{ form.as_p }}
<button type="submit">
<span class="icon import">{%trans 'Import' %}</span>
</button>
<a href="{% url user_overview%}">
<button type="button">
<span class="icon cancel">{%trans 'Cancel' %}</span>
</button>
</a>
</form>
</p>
{% endblock %}

Some files were not shown because too many files have changed in this diff Show More