├── venv ├── seafformsite │ ├── seafform │ │ ├── __init__.py │ │ ├── migrations │ │ │ ├── __init__.py │ │ │ ├── __pycache__ │ │ │ │ ├── __init__.cpython-34.pyc │ │ │ │ └── 0001_initial.cpython-34.pyc │ │ │ ├── 0008_form_public.py │ │ │ ├── 0003_form_repoid.py │ │ │ ├── 0005_form_reponame.py │ │ │ ├── 0006_form_description.py │ │ │ ├── 0007_auto_20150920_1410.py │ │ │ ├── 0004_form_creation_datetime.py │ │ │ ├── 0002_auto_20150913_2152.py │ │ │ └── 0001_initial.py │ │ ├── locale │ │ │ └── fr │ │ │ │ └── LC_MESSAGES │ │ │ │ ├── django.mo │ │ │ │ └── django.po │ │ ├── static │ │ │ ├── jqueryfiletree │ │ │ │ ├── images │ │ │ │ │ ├── db.png │ │ │ │ │ ├── code.png │ │ │ │ │ ├── css.png │ │ │ │ │ ├── doc.png │ │ │ │ │ ├── file.png │ │ │ │ │ ├── film.png │ │ │ │ │ ├── flash.png │ │ │ │ │ ├── html.png │ │ │ │ │ ├── java.png │ │ │ │ │ ├── linux.png │ │ │ │ │ ├── music.png │ │ │ │ │ ├── pdf.png │ │ │ │ │ ├── php.png │ │ │ │ │ ├── ppt.png │ │ │ │ │ ├── psd.png │ │ │ │ │ ├── ruby.png │ │ │ │ │ ├── txt.png │ │ │ │ │ ├── xls.png │ │ │ │ │ ├── zip.png │ │ │ │ │ ├── picture.png │ │ │ │ │ ├── script.png │ │ │ │ │ ├── spinner.gif │ │ │ │ │ ├── directory.png │ │ │ │ │ ├── application.png │ │ │ │ │ └── folder_open.png │ │ │ │ ├── jqueryFileTree.js │ │ │ │ └── jqueryFileTree.css │ │ │ ├── seafform │ │ │ │ └── images │ │ │ │ │ ├── telephone-man-32px.png │ │ │ │ │ ├── telephone-man-150px.png │ │ │ │ │ ├── telephone-man-300px.png │ │ │ │ │ ├── telephone-woman-150px.png │ │ │ │ │ └── telephone-woman-300px.png │ │ │ └── bootstrap │ │ │ │ ├── fonts │ │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ │ └── glyphicons-halflings-regular.woff2 │ │ │ │ ├── js │ │ │ │ └── bootstrap.min.js │ │ │ │ ├── config.json │ │ │ │ └── css │ │ │ │ └── bootstrap-theme.min.css │ │ ├── templates │ │ │ ├── seafform │ │ │ │ ├── lsdir.html │ │ │ │ ├── rowedit.html │ │ │ │ ├── thanks.html │ │ │ │ ├── form_as_form.html │ │ │ │ ├── new.html │ │ │ │ ├── private.html │ │ │ │ ├── index.html │ │ │ │ └── form_as_table.html │ │ │ ├── 404.html │ │ │ ├── 500.html │ │ │ ├── bootstrapform │ │ │ │ └── field.html │ │ │ └── base.html │ │ ├── tests.py │ │ ├── templatetags │ │ │ └── utils.py │ │ ├── urls.py │ │ ├── admin.py │ │ ├── models.py │ │ ├── forms.py │ │ ├── seafform.py │ │ ├── seafile.py │ │ └── views.py │ ├── seafformsite │ │ ├── __init__.py │ │ ├── wsgi.py │ │ ├── urls.py │ │ └── settings.py.sample │ └── manage.py └── seafform.sh.sample ├── doc ├── private-screenshot.png ├── seafform-supervisor.conf └── seafform-nginx ├── .gitignore ├── forms-library ├── examples_en │ ├── hiring-table.ods │ ├── next-meeting.ods │ ├── camping-hollidays.ods │ └── training_course_satisfaction.ods ├── templates │ ├── form_template_en.ods │ └── modèle-formulaire_fr.ods └── exemples_fr │ ├── prochaine-réunion.ods │ ├── vacances-camping.ods │ ├── recrutement-formateurs.ods │ └── satisfaction-suite-à-la-formation.ods ├── CHANGELOG.md ├── INSTALL.md ├── README.md └── TODO.md /venv/seafformsite/seafform/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /venv/seafformsite/seafformsite/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /doc/private-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/doc/private-screenshot.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/__pycache__/ 2 | *~ 3 | *lock* 4 | *.sqlite3 5 | **/proto/ 6 | settings.py 7 | **/bin 8 | **/lib 9 | -------------------------------------------------------------------------------- /forms-library/examples_en/hiring-table.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/forms-library/examples_en/hiring-table.ods -------------------------------------------------------------------------------- /forms-library/examples_en/next-meeting.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/forms-library/examples_en/next-meeting.ods -------------------------------------------------------------------------------- /forms-library/templates/form_template_en.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/forms-library/templates/form_template_en.ods -------------------------------------------------------------------------------- /forms-library/examples_en/camping-hollidays.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/forms-library/examples_en/camping-hollidays.ods -------------------------------------------------------------------------------- /forms-library/exemples_fr/prochaine-réunion.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/forms-library/exemples_fr/prochaine-réunion.ods -------------------------------------------------------------------------------- /forms-library/exemples_fr/vacances-camping.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/forms-library/exemples_fr/vacances-camping.ods -------------------------------------------------------------------------------- /forms-library/templates/modèle-formulaire_fr.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/forms-library/templates/modèle-formulaire_fr.ods -------------------------------------------------------------------------------- /forms-library/exemples_fr/recrutement-formateurs.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/forms-library/exemples_fr/recrutement-formateurs.ods -------------------------------------------------------------------------------- /forms-library/examples_en/training_course_satisfaction.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/forms-library/examples_en/training_course_satisfaction.ods -------------------------------------------------------------------------------- /venv/seafformsite/seafform/locale/fr/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/locale/fr/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/jqueryfiletree/images/db.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/jqueryfiletree/images/db.png -------------------------------------------------------------------------------- /forms-library/exemples_fr/satisfaction-suite-à-la-formation.ods: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/forms-library/exemples_fr/satisfaction-suite-à-la-formation.ods -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/jqueryfiletree/images/code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/jqueryfiletree/images/code.png -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/jqueryfiletree/images/css.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/jqueryfiletree/images/css.png -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/jqueryfiletree/images/doc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/jqueryfiletree/images/doc.png -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/jqueryfiletree/images/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/jqueryfiletree/images/file.png -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/jqueryfiletree/images/film.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/jqueryfiletree/images/film.png -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/jqueryfiletree/images/flash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/jqueryfiletree/images/flash.png -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/jqueryfiletree/images/html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/jqueryfiletree/images/html.png -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/jqueryfiletree/images/java.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/jqueryfiletree/images/java.png -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/jqueryfiletree/images/linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/jqueryfiletree/images/linux.png -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/jqueryfiletree/images/music.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/jqueryfiletree/images/music.png -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/jqueryfiletree/images/pdf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/jqueryfiletree/images/pdf.png -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/jqueryfiletree/images/php.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/jqueryfiletree/images/php.png -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/jqueryfiletree/images/ppt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/jqueryfiletree/images/ppt.png -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/jqueryfiletree/images/psd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/jqueryfiletree/images/psd.png -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/jqueryfiletree/images/ruby.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/jqueryfiletree/images/ruby.png -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/jqueryfiletree/images/txt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/jqueryfiletree/images/txt.png -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/jqueryfiletree/images/xls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/jqueryfiletree/images/xls.png -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/jqueryfiletree/images/zip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/jqueryfiletree/images/zip.png -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | * v0.2 2 | - allow to ignore TLS certs check 3 | - allow forms to be public (with or without authentication) 4 | 5 | * v0.1 - 26 sept 2015 6 | - initial release 7 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/jqueryfiletree/images/picture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/jqueryfiletree/images/picture.png -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/jqueryfiletree/images/script.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/jqueryfiletree/images/script.png -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/jqueryfiletree/images/spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/jqueryfiletree/images/spinner.gif -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/jqueryfiletree/images/directory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/jqueryfiletree/images/directory.png -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/jqueryfiletree/images/application.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/jqueryfiletree/images/application.png -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/jqueryfiletree/images/folder_open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/jqueryfiletree/images/folder_open.png -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/seafform/images/telephone-man-32px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/seafform/images/telephone-man-32px.png -------------------------------------------------------------------------------- /venv/seafformsite/seafform/migrations/__pycache__/__init__.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/migrations/__pycache__/__init__.cpython-34.pyc -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/seafform/images/telephone-man-150px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/seafform/images/telephone-man-150px.png -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/seafform/images/telephone-man-300px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/seafform/images/telephone-man-300px.png -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/seafform/images/telephone-woman-150px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/seafform/images/telephone-woman-150px.png -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/seafform/images/telephone-woman-300px.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/seafform/images/telephone-woman-300px.png -------------------------------------------------------------------------------- /venv/seafformsite/seafform/migrations/__pycache__/0001_initial.cpython-34.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/migrations/__pycache__/0001_initial.cpython-34.pyc -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/bootstrap/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/bootstrap/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/bootstrap/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/bootstrap/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/bootstrap/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/bootstrap/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/bootstrap/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/florencebiree/seafform/HEAD/venv/seafformsite/seafform/static/bootstrap/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /doc/seafform-supervisor.conf: -------------------------------------------------------------------------------- 1 | [program:forms_example.org] 2 | command=/var/webapps/seafform/venv/seafform.sh 3 | directory=/var/webapps/seafform/venv/ 4 | user=seafform 5 | group=seafform 6 | autostart=true 7 | autorestart=true 8 | redirect_stderr=True 9 | -------------------------------------------------------------------------------- /venv/seafformsite/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "seafformsite.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | # Seafile forms 2 | 3 | seafform repository has moved to framagit.org! 4 | 5 | Here is the new project homepage: 6 | https://framagit.org/flobiree/seafform 7 | 8 | And the new git repository url: 9 | https://framagit.org/flobiree/seafform.git 10 | 11 | Please follow the project to his new home! 12 | Thank you 13 | Flo. 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Seafile forms (old home page) 2 | 3 | seafform repository has moved to framagit.org! 4 | 5 | Here is the new project homepage: 6 | https://framagit.org/flobiree/seafform 7 | 8 | And the new git repository url: 9 | https://framagit.org/flobiree/seafform.git 10 | 11 | Please follow the project to his new home! 12 | 13 | Thank you 14 | 15 | Flo. 16 | -------------------------------------------------------------------------------- /venv/seafformsite/seafformsite/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for seafformsite project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "seafformsite.settings") 12 | 13 | from django.core.wsgi import get_wsgi_application 14 | application = get_wsgi_application() 15 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/migrations/0008_form_public.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('seafform', '0007_auto_20150920_1410'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='form', 16 | name='public', 17 | field=models.BooleanField(default=False), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/templates/seafform/lsdir.html: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/migrations/0003_form_repoid.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('seafform', '0002_auto_20150913_2152'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='form', 16 | name='repoid', 17 | field=models.CharField(default='', max_length=40), 18 | preserve_default=False, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/migrations/0005_form_reponame.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('seafform', '0004_form_creation_datetime'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='form', 16 | name='reponame', 17 | field=models.CharField(default='', max_length=256), 18 | preserve_default=False, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/migrations/0006_form_description.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('seafform', '0005_form_reponame'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='form', 16 | name='description', 17 | field=models.CharField(max_length=1000, default='Some random description'), 18 | preserve_default=False, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/migrations/0007_auto_20150920_1410.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('seafform', '0006_form_description'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AlterField( 15 | model_name='form', 16 | name='formid', 17 | field=models.SlugField(serialize=False, max_length=40, primary_key=True), 18 | preserve_default=True, 19 | ), 20 | ] 21 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/migrations/0004_form_creation_datetime.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | from django.utils.timezone import utc 6 | import datetime 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('seafform', '0003_form_repoid'), 13 | ] 14 | 15 | operations = [ 16 | migrations.AddField( 17 | model_name='form', 18 | name='creation_datetime', 19 | field=models.DateTimeField(auto_now_add=True, default=datetime.datetime(2015, 9, 15, 8, 49, 18, 634121, tzinfo=utc)), 20 | preserve_default=False, 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/migrations/0002_auto_20150913_2152.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | from django.conf import settings 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('seafform', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.AddField( 16 | model_name='form', 17 | name='title', 18 | field=models.CharField(default='NoTitle', max_length=100), 19 | preserve_default=False, 20 | ), 21 | migrations.AlterField( 22 | model_name='form', 23 | name='owner', 24 | field=models.ForeignKey(to=settings.AUTH_USER_MODEL), 25 | preserve_default=True, 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/templates/404.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load i18n %} 3 | 4 | {% block title %}{% trans "Seafile forms" %} – {% trans "Form not found" %}{% endblock %} 5 | 6 | {% block body %} 7 | 8 |
9 | 10 |
11 | 12 | 19 | 20 |

{% trans "Seafile forms" %}

21 | 22 |

23 | {% blocktrans %} 24 | The form you try to open is no more reachable. 25 | May be its owner put it off-line? 26 | {% endblocktrans %} 27 |

28 | 29 |
30 | 31 |
32 | 33 | {% endblock %} 34 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/templates/seafform/rowedit.html: -------------------------------------------------------------------------------- 1 | {% load bootstrap %} 2 | {% load i18n %} 3 | 4 | {% for field in djform %} 5 | {% if field.is_hidden %} 6 | {{ field }} 7 | {% if field.field.isstatic %} 8 | 9 |

{{ field.value|default:"" }}

10 | 11 | {% endif %} 12 | {% else %} 13 | 14 | {{ field|bootstrap }} 15 | 16 | {% endif %} 17 | {% endfor %} 18 | 19 | 20 |
21 | 24 | 28 |
29 | 30 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/templates/500.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load i18n %} 3 | 4 | {% block title %}{% trans "Seafile forms" %} – {% trans "Server fault" %}{% endblock %} 5 | 6 | {% block body %} 7 | 8 |
9 | 10 |
11 | 12 | 19 | 20 |

{% trans "Seafile forms" %}

21 | 22 |

23 | {% blocktrans %} 24 | An error append server-side. May be a form is not well-built? 25 | {% endblocktrans %} 26 |

27 |

28 | {% blocktrans %} 29 | You can contact the form owner to tell her you had a problem. 30 | {% endblocktrans %} 31 |

32 |
33 | 34 |
35 | 36 | {% endblock %} 37 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/templates/seafform/thanks.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load i18n %} 3 | 4 | {% block title %}{{ seafform.title }} – {% trans "Thanks!" %}{% endblock %} 5 | 6 | {% block body %} 7 | 8 |
9 | 10 |
11 | 12 | 19 | 20 |

{{ seafform.title }}

21 | 22 | {{ seafform.description|linebreaks }} 23 |
24 | 25 |
26 | 27 |
28 |

29 | 30 | {% trans "Thank you for taking time to answer this form!" %} 31 |

32 |
33 | 34 |
35 |
36 | 37 | {% endblock %} 38 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | SeafForm TODO 2 | ============= 3 | 4 | main 5 | ---- 6 | 7 | - what if the user change its seafile password -> seafform will keep using the old one 8 | 9 | seafform module 10 | --------------- 11 | 12 | - allow to edit existing lines but not adding new ones 13 | 14 | view as table 15 | ------------- 16 | 17 | - if long table, may be fixed headers? 18 | - when a line is saved (green sucess background), viewing bug on the right 19 | - if table to wide, the scroll bar at the bottom may be hidden, and need to scrool down 20 | - when some field has a description, and others not, fields are not aligned. 21 | 22 | private space 23 | ------------- 24 | 25 | - improve text next to "choose the file" button 26 | - newly created form as an orange url on an orange background 27 | - select ods file with double-clic 28 | - if LibreOffice is integrated with Seafile, change the way to 29 | create a new form, eg Select a template -> Edit it 30 | 31 | ods parser 32 | ---------- 33 | 34 | - provide error description the form is broken 35 | - when two columns have the same name, only the first one is displayed 36 | 37 | admin space 38 | ----------- 39 | 40 | - form links are orange on an orange background 41 | - valid an ods selection using double-click 42 | - if a file is moved in Seafile, signal the problem, and allow to set an new path 43 | 44 | -------------------------------------------------------------------------------- /doc/seafform-nginx: -------------------------------------------------------------------------------- 1 | # redirect http to https. 2 | server { 3 | listen 80; ## listen for ipv4 4 | listen [::]:80; ## listen for ipv6 5 | server_name forms.example.org; 6 | return 301 https://$server_name$request_uri; #enforce https 7 | } 8 | 9 | # SeafForm 10 | server { 11 | listen 443; 12 | server_name forms.tila.im; 13 | 14 | ssl on; 15 | ssl_certificate /path/to/your/cert; 16 | ssl_certificate_key /path/to/your/cert.key; 17 | # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months) 18 | add_header Strict-Transport-Security max-age=15768000; 19 | 20 | server_name forms.example.org; 21 | 22 | location / { 23 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 24 | proxy_set_header Host $http_host; 25 | proxy_redirect off; 26 | if (!-f $request_filename) { 27 | proxy_pass http://seafform_backend; 28 | break; 29 | } 30 | } 31 | 32 | location /static { 33 | root "/var/webapps/seafform/venv/seafformsite/seafform/"; 34 | } 35 | 36 | } 37 | 38 | upstream seafform_backend { 39 | server unix:/var/webapps/seafform/venv/seafform.sock; 40 | } 41 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/tests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ############################################################################### 3 | # seafform/tests.py 4 | # 5 | # Copyright © 2015, Florian Birée 6 | # 7 | # This file is a part of seafform. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU Affero General Public License as 11 | # published by the Free Software Foundation, either version 3 of the 12 | # License, or (at your option) any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU Affero General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU Affero General Public License 20 | # along with this program. If not, see . 21 | # 22 | ############################################################################### 23 | 24 | __author__ = "Florian Birée" 25 | __version__ = "0.2" 26 | __license__ = "AGPLv3" 27 | __copyright__ = "Copyright © 2015, Florian Birée " 28 | 29 | from django.test import TestCase 30 | 31 | # Create your tests here… TODO 32 | -------------------------------------------------------------------------------- /venv/seafform.sh.sample: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ############################################################################### 3 | # seafform.sh 4 | # Gunicorn starting script 5 | # 6 | # Copyright © 2015, Florian Birée 7 | # 8 | # This file is a part of seafform. 9 | # 10 | # This program is free software: you can redistribute it and/or modify 11 | # it under the terms of the GNU Affero General Public License as 12 | # published by the Free Software Foundation, either version 3 of the 13 | # License, or (at your option) any later version. 14 | # 15 | # This program is distributed in the hope that it will be useful, 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | # GNU Affero General Public License for more details. 19 | # 20 | # You should have received a copy of the GNU Affero General Public License 21 | # along with this program. If not, see . 22 | # 23 | ############################################################################### 24 | 25 | # change those variables to suits your needs 26 | VENV_DIR=/path/to/seafform/venv 27 | ACTIVATE_PATH=$VENV_DIR/bin/activate 28 | SOCKET="unix:$VENV_DIR/seafform.sock" 29 | 30 | 31 | cd ${VENV_DIR} 32 | source ${ACTIVATE_PATH} 33 | cd seafformsite 34 | exec gunicorn --bind="$SOCKET" seafformsite.wsgi 35 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | from django.conf import settings 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Form', 17 | fields=[ 18 | ('filepath', models.CharField(max_length=256)), 19 | ('formid', models.CharField(max_length=40, serialize=False, primary_key=True)), 20 | ], 21 | options={ 22 | }, 23 | bases=(models.Model,), 24 | ), 25 | migrations.CreateModel( 26 | name='SeafileUser', 27 | fields=[ 28 | ('id', models.AutoField(auto_created=True, serialize=False, primary_key=True, verbose_name='ID')), 29 | ('seafroot', models.URLField()), 30 | ('seaftoken', models.CharField(max_length=40)), 31 | ('user', models.OneToOneField(to=settings.AUTH_USER_MODEL)), 32 | ], 33 | options={ 34 | }, 35 | bases=(models.Model,), 36 | ), 37 | migrations.AddField( 38 | model_name='form', 39 | name='owner', 40 | field=models.ForeignKey(to='seafform.SeafileUser'), 41 | preserve_default=True, 42 | ), 43 | ] 44 | -------------------------------------------------------------------------------- /venv/seafformsite/seafformsite/urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ############################################################################### 3 | # seafformsite/urls.py 4 | # 5 | # Copyright © 2017, Flo Birée 6 | # 7 | # This file is a part of seafform. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU Affero General Public License as 11 | # published by the Free Software Foundation, either version 3 of the 12 | # License, or (at your option) any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU Affero General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU Affero General Public License 20 | # along with this program. If not, see . 21 | # 22 | ############################################################################### 23 | 24 | __author__ = "Flo Birée" 25 | __version__ = "0.2" 26 | __license__ = "AGPLv3" 27 | __copyright__ = "Copyright © 2017, Flo Birée " 28 | 29 | from django.conf.urls import include, url 30 | from django.contrib import admin 31 | 32 | urlpatterns = [ 33 | url(r'^admin/', include(admin.site.urls)), 34 | url(r'^', include('seafform.urls')), 35 | ] 36 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/templatetags/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ############################################################################### 3 | # seafform/templatetags/utils.py 4 | # 5 | # Copyright © 2015, Florian Birée 6 | # 7 | # This file is a part of seafform. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, either version 3 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program. If not, see . 21 | # 22 | ############################################################################### 23 | """Seafform forms description""" 24 | 25 | __author__ = "Florian Birée" 26 | __version__ = "0.1" 27 | __license__ = "GPLv3" 28 | __copyright__ = "Copyright © 2015, Florian Birée " 29 | __revision__ = "$Revision: $" 30 | __date__ = "$Date: $" 31 | 32 | from django import template 33 | from django.forms.extras.widgets import SelectDateWidget 34 | 35 | register = template.Library() 36 | 37 | @register.filter 38 | def get_type(value): 39 | return type(value).__name__ 40 | 41 | @register.filter 42 | def is_date(field): 43 | return isinstance(field.field.widget, SelectDateWidget) 44 | 45 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/urls.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ############################################################################### 3 | # seafform/urls.py 4 | # 5 | # Copyright © 2017, Flo Birée 6 | # 7 | # This file is a part of seafform. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU Affero General Public License as 11 | # published by the Free Software Foundation, either version 3 of the 12 | # License, or (at your option) any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU Affero General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU Affero General Public License 20 | # along with this program. If not, see . 21 | # 22 | ############################################################################### 23 | """Seafform URL dispatcher""" 24 | 25 | __author__ = "Flo Birée" 26 | __version__ = "0.2" 27 | __license__ = "AGPLv3" 28 | __copyright__ = "Copyright © 2017, Flo Birée " 29 | 30 | from django.conf.urls import url 31 | 32 | from seafform import views 33 | 34 | urlpatterns = [ 35 | url(r'^$', views.index, name='index'), 36 | url(r'^private/$', views.private, name='private'), 37 | url(r'^private/logout/$', views.logout_view, name='logout'), 38 | url(r'^private/new/$', views.new, name='new'), 39 | url(r'^private/lsdir/$', views.lsdir, name='lsdir'), 40 | url(r'^form/(?P[^/]*)/$', views.formview, name='form'), 41 | url(r'^form/(?P[^/]*)/thanks/$', views.thanks, name='thanks'), 42 | url(r'^form/(?P[^/]*)/(?P\d*)/$', views.formrowedit, name='rowedit'), 43 | ] 44 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/admin.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ############################################################################### 3 | # seafform/admin.py 4 | # 5 | # Copyright © 2015, Florian Birée 6 | # 7 | # This file is a part of seafform. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU Affero General Public License as 11 | # published by the Free Software Foundation, either version 3 of the 12 | # License, or (at your option) any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU Affero General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU Affero General Public License 20 | # along with this program. If not, see . 21 | # 22 | ############################################################################### 23 | """Seafform admin site description""" 24 | 25 | __author__ = "Florian Birée" 26 | __version__ = "0.2" 27 | __license__ = "AGPLv3" 28 | __copyright__ = "Copyright © 2015, Florian Birée " 29 | 30 | from django.contrib import admin 31 | from django.contrib.auth.admin import UserAdmin 32 | from django.contrib.auth.models import User 33 | 34 | 35 | # Register your models here. 36 | from . import models 37 | 38 | # Define an inline admin descriptor for Employee model 39 | # which acts a bit like a singleton 40 | class SeafileUserInline(admin.StackedInline): 41 | model = models.SeafileUser 42 | can_delete = False 43 | verbose_name_plural = 'Seafile users' 44 | 45 | # Define a new User admin 46 | class UserAdmin(UserAdmin): 47 | inlines = (SeafileUserInline, ) 48 | 49 | # Re-register UserAdmin 50 | admin.site.unregister(User) 51 | admin.site.register(User, UserAdmin) 52 | 53 | # Register Form 54 | admin.site.register(models.Form) 55 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ############################################################################### 3 | # seafform/models.py 4 | # 5 | # Copyright © 2015, Florian Birée 6 | # 7 | # This file is a part of seafform. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU Affero General Public License as 11 | # published by the Free Software Foundation, either version 3 of the 12 | # License, or (at your option) any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU Affero General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU Affero General Public License 20 | # along with this program. If not, see . 21 | # 22 | ############################################################################### 23 | """Seafform forms description""" 24 | 25 | __author__ = "Florian Birée" 26 | __version__ = "0.2" 27 | __license__ = "AGPLv3" 28 | __copyright__ = "Copyright © 2015, Florian Birée " 29 | 30 | from django.db import models 31 | from django.contrib.auth.models import User 32 | from django.conf import settings 33 | from django.core.urlresolvers import reverse 34 | from urllib.parse import urljoin 35 | 36 | class SeafileUser(models.Model): 37 | """Profile class for user""" 38 | user = models.OneToOneField(User) 39 | seafroot = models.URLField() 40 | seaftoken = models.CharField(max_length=40) 41 | 42 | def __repr__(self): 43 | return "" % self.user.email 44 | 45 | class Form(models.Model): 46 | """Represent a Seafform""" 47 | owner = models.ForeignKey(User) 48 | filepath = models.CharField(max_length=256) 49 | repoid = models.CharField(max_length=40) 50 | reponame = models.CharField(max_length=256) 51 | formid = models.SlugField(max_length=40, primary_key=True) 52 | title = models.CharField(max_length=100) 53 | description = models.CharField(max_length=1000) 54 | creation_datetime = models.DateTimeField(auto_now_add=True) 55 | public = models.BooleanField(default=False) 56 | 57 | def __repr__(self): 58 | return "" % (self.title, self.owner.email) 59 | 60 | def get_absolute_url(self): 61 | """Return the public url of the form""" 62 | return urljoin(settings.ROOT_URL, reverse('form', args=(self.formid,))) 63 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/templates/seafform/form_as_form.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {# encoding: utf-8 #} 3 | {% load bootstrap i18n %} 4 | 5 | {% block title %}{{ seafform.title }}{% endblock %} 6 | 7 | {% block headers %} 8 | 19 | {% endblock %} 20 | 21 | {% block body %} 22 | 23 |
24 | 25 |
26 | 27 | 34 | 35 |

{{ seafform.title }}

36 | 37 | {{ seafform.description|urlize|linebreaks }} 38 | 39 |
40 | 41 | 46 | 47 |
48 | 49 |
50 | {% csrf_token %} 51 | {% if djform.non_field_errors %} 52 |
53 | × 54 | {% for non_field_error in djform.non_field_errors %} 55 | {{ non_field_error }} 56 | {% endfor %} 57 |
58 | {% endif %} 59 | {% for field in djform %} 60 | {% if field.is_hidden %} 61 |
62 | {{ field }} 63 | {% if field.field.isstatic %} 64 |

{{ field.label }}

65 | {% if field.help_text %} 66 |

67 | {{ field.help_text|urlize }} 68 |

69 | {% endif %} 70 | {% endif %} 71 |
72 | {% else %} 73 | {{ field|bootstrap }} 74 | {% endif %} 75 | {% endfor %} 76 | 78 |
79 | 80 |
81 |
82 | 83 | {% endblock %} 84 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/templates/bootstrapform/field.html: -------------------------------------------------------------------------------- 1 | {% load bootstrap %} 2 | 3 |
4 | {% if field|is_checkbox %} 5 |
6 |
7 | {% if field.auto_id %} 8 | 11 | {% endif %} 12 | {% for error in field.errors %} 13 | {{ error }} 14 | {% endfor %} 15 | 16 | {% if field.help_text %} 17 |

18 | {{ field.help_text|urlize }} 19 |

20 | {% endif %} 21 |
22 |
23 | {% elif field|is_radio %} 24 | {% if field.auto_id %} 25 | 26 | {% endif %} 27 |
28 | {% for choice in field %} 29 |
30 | 34 |
35 | {% endfor %} 36 | 37 | {% for error in field.errors %} 38 | {{ error }} 39 | {% endfor %} 40 | 41 | {% if field.help_text %} 42 |

43 | {{ field.help_text|urlize }} 44 |

45 | {% endif %} 46 |
47 | {% else %} 48 | {% if field.auto_id %} 49 | 50 | {% endif %} 51 | 52 |
53 | {{ field }} 54 | 55 | {% for error in field.errors %} 56 | {{ error }} 57 | {% endfor %} 58 | 59 | {% if field.help_text %} 60 |

61 | {{ field.help_text|urlize }} 62 |

63 | {% endif %} 64 |
65 | {% endif %} 66 |
67 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/templates/base.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | {% block title %}{% endblock %} 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 30 | 31 | {% block headers %}{% endblock %} 32 | 33 | 34 | 35 | 36 | 37 | {% block body %}{% endblock %} 38 | 39 | 40 | 41 | 42 | 43 | 45 | 46 | 47 | {% block scripts %}{% endblock %} 48 | 49 | 50 | 51 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/templates/seafform/new.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load i18n %} 3 | 4 | {% block title %}{% trans "Seafile forms" %}{% endblock %} 5 | 6 | {% block headers%} 7 | 8 | {% endblock %} 9 | 10 | {% block body %} 11 | 12 | 38 | 39 |
40 | 41 | 42 | 45 | 46 | 47 |

48 | {% blocktrans %} 49 | Choose in your Seafile the LibreOffice spreadsheet (.ods) which will be 50 | the form: 51 | {% endblocktrans %} 52 |

53 | 54 |
55 |
56 |
    57 |
  • {% trans "Conexion to Seafile..." %}
  • 58 |
59 |
60 |
61 | 62 | 76 | 77 |
78 | 79 | 80 | {% endblock %} 81 | 82 | {% block scripts %} 83 | 84 | 97 | {% endblock %} 98 | -------------------------------------------------------------------------------- /venv/seafformsite/seafformsite/settings.py.sample: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for seafformsite project. 3 | 4 | For more information on this file, see 5 | https://docs.djangoproject.com/en/1.7/topics/settings/ 6 | 7 | For the full list of settings and their values, see 8 | https://docs.djangoproject.com/en/1.7/ref/settings/ 9 | """ 10 | 11 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 12 | import os 13 | BASE_DIR = os.path.dirname(os.path.dirname(__file__)) 14 | 15 | 16 | # Quick-start development settings - unsuitable for production 17 | # See https://docs.djangoproject.com/en/1.7/howto/deployment/checklist/ 18 | 19 | # SECURITY WARNING: keep the secret key used in production secret! 20 | SECRET_KEY = 'put here a 50 chars long random key' 21 | 22 | # SECURITY WARNING: don't run with debug turned on in production! 23 | DEBUG = False 24 | 25 | ALLOWED_HOSTS = ['forms.example.org'] # Must be changed! 26 | 27 | 28 | # Application definition 29 | 30 | INSTALLED_APPS = ( 31 | 'django.contrib.admin', 32 | 'django.contrib.auth', 33 | 'django.contrib.contenttypes', 34 | 'django.contrib.sessions', 35 | 'django.contrib.messages', 36 | 'django.contrib.staticfiles', 37 | 'seafform', 38 | 'bootstrapform', 39 | ) 40 | 41 | MIDDLEWARE_CLASSES = ( 42 | 'django.contrib.sessions.middleware.SessionMiddleware', 43 | 'django.middleware.common.CommonMiddleware', 44 | 'django.middleware.csrf.CsrfViewMiddleware', 45 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 46 | 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', 47 | 'django.contrib.messages.middleware.MessageMiddleware', 48 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 49 | ) 50 | 51 | ROOT_URLCONF = 'seafformsite.urls' 52 | 53 | WSGI_APPLICATION = 'seafformsite.wsgi.application' 54 | 55 | TEMPLATES = [ 56 | { 57 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 58 | 'DIRS': [ 59 | # insert your TEMPLATE_DIRS here 60 | 'SEAFFORM_INSTALL_PATH/venv/seafformsite/seafform/templates/' 61 | ], 62 | 'APP_DIRS': True, 63 | 'OPTIONS': { 64 | 'context_processors': [ 65 | # Insert your TEMPLATE_CONTEXT_PROCESSORS here or use this 66 | # list if you haven't customized them: 67 | 'django.contrib.auth.context_processors.auth', 68 | 'django.template.context_processors.debug', 69 | 'django.template.context_processors.i18n', 70 | 'django.template.context_processors.media', 71 | 'django.template.context_processors.static', 72 | 'django.template.context_processors.tz', 73 | 'django.contrib.messages.context_processors.messages', 74 | ], 75 | }, 76 | }, 77 | ] 78 | 79 | # Database 80 | # https://docs.djangoproject.com/en/1.7/ref/settings/#databases 81 | 82 | DATABASES = { 83 | 'default': { 84 | 'ENGINE': 'django.db.backends.sqlite3', 85 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 86 | } 87 | } 88 | 89 | # Internationalization 90 | # https://docs.djangoproject.com/en/1.7/topics/i18n/ 91 | 92 | LANGUAGE_CODE = 'fr-fr' 93 | 94 | TIME_ZONE = 'UTC' 95 | 96 | USE_I18N = True 97 | 98 | USE_L10N = True 99 | 100 | USE_TZ = True 101 | 102 | 103 | # Static files (CSS, JavaScript, Images) 104 | # https://docs.djangoproject.com/en/1.7/howto/static-files/ 105 | STATIC_URL = '/static/' 106 | 107 | # HTTPS settings 108 | CSRF_COOKIE_SECURE = True 109 | SESSION_COOKIE_SECURE = True 110 | 111 | # Seafform settings 112 | SEAFILE_ROOT = 'https://seafile.example.org/' 113 | TPL_URL = 'https://seafile.example.org/d/908b45b3b6/' 114 | ROOT_URL = 'https://forms.example.org/' 115 | VERIFYCERTS = True 116 | ALLOW_PUBLIC = False 117 | PUBLIC_NEED_AUTH = True 118 | 119 | # Seafform dev setting 120 | LOCAL = False 121 | LOCAL_ROOT = '/local/root/' 122 | 123 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/jqueryfiletree/jqueryFileTree.js: -------------------------------------------------------------------------------- 1 | // jQuery File Tree Plugin 2 | // 3 | // Version 1.01 4 | // 5 | // Cory S.N. LaViska 6 | // A Beautiful Site (http://abeautifulsite.net/) 7 | // 24 March 2008 8 | // 9 | // Visit http://abeautifulsite.net/notebook.php?article=58 for more information 10 | // 11 | // Usage: $('.fileTreeDemo').fileTree( options, callback ) 12 | // 13 | // Options: root - root folder to display; default = / 14 | // script - location of the serverside AJAX file to use; default = jqueryFileTree.php 15 | // folderEvent - event to trigger expand/collapse; default = click 16 | // expandSpeed - default = 500 (ms); use -1 for no animation 17 | // collapseSpeed - default = 500 (ms); use -1 for no animation 18 | // expandEasing - easing function to use on expand (optional) 19 | // collapseEasing - easing function to use on collapse (optional) 20 | // multiFolder - whether or not to limit the browser to one subfolder at a time 21 | // loadMessage - Message to display while initial tree loads (can be HTML) 22 | // 23 | // History: 24 | // 25 | // 1.01 - updated to work with foreign characters in directory/file names (12 April 2008) 26 | // 1.00 - released (24 March 2008) 27 | // 28 | // TERMS OF USE 29 | // 30 | // This plugin is dual-licensed under the GNU General Public License and the MIT License and 31 | // is copyright 2008 A Beautiful Site, LLC. 32 | // 33 | if(jQuery) (function($){ 34 | 35 | $.extend($.fn, { 36 | fileTree: function(o, h) { 37 | // Defaults 38 | if( !o ) var o = {}; 39 | if( o.root == undefined ) o.root = '/'; 40 | if( o.script == undefined ) o.script = 'jqueryFileTree.php'; 41 | if( o.folderEvent == undefined ) o.folderEvent = 'click'; 42 | if( o.expandSpeed == undefined ) o.expandSpeed= 500; 43 | if( o.collapseSpeed == undefined ) o.collapseSpeed= 500; 44 | if( o.expandEasing == undefined ) o.expandEasing = null; 45 | if( o.collapseEasing == undefined ) o.collapseEasing = null; 46 | if( o.multiFolder == undefined ) o.multiFolder = true; 47 | if( o.loadMessage == undefined ) o.loadMessage = 'Loading...'; 48 | 49 | $(this).each( function() { 50 | 51 | function showTree(c, t) { 52 | $(c).addClass('wait'); 53 | $(".jqueryFileTree.start").remove(); 54 | $.post(o.script, { dir: t }, function(data) { 55 | $(c).find('.start').html(''); 56 | $(c).removeClass('wait').append(data); 57 | if( o.root == t ) $(c).find('UL:hidden').show(); else $(c).find('UL:hidden').slideDown({ duration: o.expandSpeed, easing: o.expandEasing }); 58 | bindTree(c); 59 | }); 60 | } 61 | 62 | function bindTree(t) { 63 | $(t).find('LI A').bind(o.folderEvent, function() { 64 | if( $(this).parent().hasClass('directory') ) { 65 | if( $(this).parent().hasClass('collapsed') ) { 66 | // Expand 67 | if( !o.multiFolder ) { 68 | $(this).parent().parent().find('UL').slideUp({ duration: o.collapseSpeed, easing: o.collapseEasing }); 69 | $(this).parent().parent().find('LI.directory').removeClass('expanded').addClass('collapsed'); 70 | } 71 | $(this).parent().find('UL').remove(); // cleanup 72 | showTree( $(this).parent(), escape($(this).attr('rel').match( /.*\// )) ); 73 | $(this).parent().removeClass('collapsed').addClass('expanded'); 74 | } else { 75 | // Collapse 76 | $(this).parent().find('UL').slideUp({ duration: o.collapseSpeed, easing: o.collapseEasing }); 77 | $(this).parent().removeClass('expanded').addClass('collapsed'); 78 | } 79 | } else { 80 | h($(this).attr('rel'), this); 81 | } 82 | return false; 83 | }); 84 | // Prevent A from triggering the # on non-click events 85 | if( o.folderEvent.toLowerCase != 'click' ) $(t).find('LI A').bind('click', function() { return false; }); 86 | } 87 | // Loading message 88 | $(this).html('
  • ' + o.loadMessage + '
'); 89 | // Get the initial file list 90 | showTree( $(this), escape(o.root) ); 91 | }); 92 | } 93 | }); 94 | 95 | })(jQuery); 96 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/forms.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ############################################################################### 3 | # seafform/forms.py 4 | # 5 | # Copyright © 2015, Florian Birée 6 | # 7 | # This file is a part of seafform. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU Affero General Public License as 11 | # published by the Free Software Foundation, either version 3 of the 12 | # License, or (at your option) any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU Affero General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU Affero General Public License 20 | # along with this program. If not, see . 21 | # 22 | ############################################################################### 23 | """Seafform Django forms description""" 24 | 25 | __author__ = "Florian Birée" 26 | __version__ = "0.2" 27 | __license__ = "AGPLv3" 28 | __copyright__ = "Copyright © 2015, Florian Birée " 29 | 30 | from django import forms 31 | from django.forms.extras.widgets import SelectDateWidget 32 | import seafform.seafform as seafform 33 | 34 | class LoginForm(forms.Form): 35 | email = forms.EmailField() 36 | password = forms.CharField(widget=forms.PasswordInput) 37 | # autofocus to email 38 | email.widget.attrs.update({'autofocus' : 'autofocus'}) 39 | 40 | class DjForm(forms.Form): 41 | 42 | required_css_class = 'required' 43 | 44 | rowid = forms.CharField(widget=forms.HiddenInput, initial='newrow') 45 | rowid.isstatic = False 46 | 47 | def __init__(self, *args, **kwargs): 48 | """Add one more arguments: 49 | fieldlist: a seafform.Fields list 50 | """ 51 | fieldlist = kwargs.pop('fieldlist') 52 | super(DjForm, self).__init__(*args, **kwargs) 53 | 54 | firstfield = None 55 | 56 | for field in fieldlist: 57 | stdparams = { 58 | 'label': field.label, 59 | 'required': field.required, 60 | 'help_text': field.description, 61 | } 62 | djfield = None 63 | if isinstance(field, seafform.TextField): 64 | djfield = forms.CharField(**stdparams) 65 | elif isinstance(field, seafform.LongTextField): 66 | params = stdparams.copy() 67 | params.update(widget = forms.Textarea) 68 | djfield = forms.CharField(**params) 69 | elif isinstance(field, seafform.ListField): 70 | params = stdparams.copy() 71 | params.update(choices = ((c, c) for c in field.choices)) 72 | djfield = forms.ChoiceField(**params) 73 | elif isinstance(field, seafform.BooleanField): 74 | params = stdparams.copy() 75 | params.update(initial = False) 76 | djfield = forms.BooleanField(**params) 77 | elif isinstance(field, seafform.BooleanTrueField): 78 | params = stdparams.copy() 79 | params.update(initial = True) 80 | djfield = forms.BooleanField(**params) 81 | elif isinstance(field, seafform.DateField): 82 | params = stdparams.copy() 83 | params.update(widget = SelectDateWidget) 84 | djfield = forms.DateField(**params) 85 | djfield.widget.attrs.update( 86 | {'style': 'width: 32%; display: inline-block;'} 87 | ) 88 | elif isinstance(field, seafform.NumberField): 89 | djfield = forms.FloatField(**stdparams) 90 | elif isinstance(field, seafform.StaticField): 91 | params = stdparams.copy() 92 | params.update(widget=forms.HiddenInput) 93 | params['required'] = False 94 | djfield = forms.CharField(**params) 95 | djfield.isstatic = True 96 | 97 | if djfield is not None: 98 | self.fields[field.label] = djfield 99 | if not hasattr(djfield, 'isstatic'): 100 | djfield.isstatic = False 101 | 102 | if firstfield is None: 103 | firstfield = self.fields[field.label] 104 | 105 | # add autofocus to the first one 106 | firstfield.widget.attrs.update({'autofocus' : 'autofocus'}) 107 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/templates/seafform/private.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load i18n %} 3 | 4 | {% block title %}{% trans "Seafile forms" %}{% endblock %} 5 | 6 | {% block body %} 7 | 8 | 34 | 35 |
36 | 37 | 40 |
41 |

{% trans "1. create the spreadsheet with questions" %}

42 | 43 | {% trans "Forms templates" %} 45 | 46 |

47 | {% blocktrans %} 48 | You just need to download a template, to edit it, and to synchronize 49 | it in Seafile. 50 | {% endblocktrans %} 51 |

52 | 53 |

{% trans "2. create the form" %}

54 | 55 | {% trans "Create a new form" %} 57 | 58 |

59 | {% trans "Select the right spreadsheet." %} 60 |

61 | 62 |

{% trans "3. ask people to answer" %}

63 | 64 |

65 | {% blocktrans %} 66 | Send the form's public address to all affected persons! 67 | {% endblocktrans %} 68 |

69 | 70 |
71 | 72 | {% if newform %} 73 | 86 | {% endif %} 87 | 88 | {% if deleted %} 89 | 96 | {% endif %} 97 | 98 | {% if forms %} 99 | 100 | 101 | 105 | {% for form in forms %} 106 | 107 | 108 | 116 | 129 | 130 | {% endfor %} 131 |
{% trans "Title" %}{% trans "Address" %} 102 | 103 | {% trans "Action" %} 104 |
{{ form.title }} 109 | 110 | {{ form.get_absolute_url }} 112 |
113 | 114 | /{{ form.reponame }}{{ form.filepath }} 115 |
117 |
118 | {% csrf_token %} 119 | 123 | 127 |
128 |
132 | {% endif %} 133 | 134 |
    135 | 136 | 137 |
138 | 139 | 140 | {% endblock %} 141 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/jqueryfiletree/jqueryFileTree.css: -------------------------------------------------------------------------------- 1 | UL.jqueryFileTree { 2 | font-family: Verdana, sans-serif; 3 | font-size: 11px; 4 | line-height: 18px; 5 | padding: 0px; 6 | margin: 0px; 7 | } 8 | 9 | UL.jqueryFileTree LI { 10 | list-style: none; 11 | padding: 0px; 12 | padding-left: 20px; 13 | margin: 0px; 14 | white-space: nowrap; 15 | } 16 | 17 | UL.jqueryFileTree A { 18 | color: #333; 19 | text-decoration: none; 20 | display: block; 21 | padding: 0px 2px; 22 | } 23 | 24 | UL.jqueryFileTree A:hover { 25 | background: #BDF; 26 | } 27 | 28 | /* Core Styles */ 29 | .jqueryFileTree LI.directory { background: url(images/directory.png) left top no-repeat; } 30 | .jqueryFileTree LI.expanded { background: url(images/folder_open.png) left top no-repeat; } 31 | .jqueryFileTree LI.file { background: url(images/file.png) left top no-repeat; } 32 | .jqueryFileTree LI.wait { background: url(images/spinner.gif) left top no-repeat; } 33 | /* File Extensions*/ 34 | .jqueryFileTree LI.ext_3gp { background: url(images/film.png) left top no-repeat; } 35 | .jqueryFileTree LI.ext_afp { background: url(images/code.png) left top no-repeat; } 36 | .jqueryFileTree LI.ext_afpa { background: url(images/code.png) left top no-repeat; } 37 | .jqueryFileTree LI.ext_asp { background: url(images/code.png) left top no-repeat; } 38 | .jqueryFileTree LI.ext_aspx { background: url(images/code.png) left top no-repeat; } 39 | .jqueryFileTree LI.ext_avi { background: url(images/film.png) left top no-repeat; } 40 | .jqueryFileTree LI.ext_bat { background: url(images/application.png) left top no-repeat; } 41 | .jqueryFileTree LI.ext_bmp { background: url(images/picture.png) left top no-repeat; } 42 | .jqueryFileTree LI.ext_c { background: url(images/code.png) left top no-repeat; } 43 | .jqueryFileTree LI.ext_cfm { background: url(images/code.png) left top no-repeat; } 44 | .jqueryFileTree LI.ext_cgi { background: url(images/code.png) left top no-repeat; } 45 | .jqueryFileTree LI.ext_com { background: url(images/application.png) left top no-repeat; } 46 | .jqueryFileTree LI.ext_cpp { background: url(images/code.png) left top no-repeat; } 47 | .jqueryFileTree LI.ext_css { background: url(images/css.png) left top no-repeat; } 48 | .jqueryFileTree LI.ext_doc { background: url(images/doc.png) left top no-repeat; } 49 | .jqueryFileTree LI.ext_exe { background: url(images/application.png) left top no-repeat; } 50 | .jqueryFileTree LI.ext_gif { background: url(images/picture.png) left top no-repeat; } 51 | .jqueryFileTree LI.ext_fla { background: url(images/flash.png) left top no-repeat; } 52 | .jqueryFileTree LI.ext_h { background: url(images/code.png) left top no-repeat; } 53 | .jqueryFileTree LI.ext_htm { background: url(images/html.png) left top no-repeat; } 54 | .jqueryFileTree LI.ext_html { background: url(images/html.png) left top no-repeat; } 55 | .jqueryFileTree LI.ext_jar { background: url(images/java.png) left top no-repeat; } 56 | .jqueryFileTree LI.ext_jpg { background: url(images/picture.png) left top no-repeat; } 57 | .jqueryFileTree LI.ext_jpeg { background: url(images/picture.png) left top no-repeat; } 58 | .jqueryFileTree LI.ext_js { background: url(images/script.png) left top no-repeat; } 59 | .jqueryFileTree LI.ext_lasso { background: url(images/code.png) left top no-repeat; } 60 | .jqueryFileTree LI.ext_log { background: url(images/txt.png) left top no-repeat; } 61 | .jqueryFileTree LI.ext_m4p { background: url(images/music.png) left top no-repeat; } 62 | .jqueryFileTree LI.ext_mov { background: url(images/film.png) left top no-repeat; } 63 | .jqueryFileTree LI.ext_mp3 { background: url(images/music.png) left top no-repeat; } 64 | .jqueryFileTree LI.ext_mp4 { background: url(images/film.png) left top no-repeat; } 65 | .jqueryFileTree LI.ext_mpg { background: url(images/film.png) left top no-repeat; } 66 | .jqueryFileTree LI.ext_mpeg { background: url(images/film.png) left top no-repeat; } 67 | .jqueryFileTree LI.ext_ogg { background: url(images/music.png) left top no-repeat; } 68 | .jqueryFileTree LI.ext_pcx { background: url(images/picture.png) left top no-repeat; } 69 | .jqueryFileTree LI.ext_pdf { background: url(images/pdf.png) left top no-repeat; } 70 | .jqueryFileTree LI.ext_php { background: url(images/php.png) left top no-repeat; } 71 | .jqueryFileTree LI.ext_png { background: url(images/picture.png) left top no-repeat; } 72 | .jqueryFileTree LI.ext_ppt { background: url(images/ppt.png) left top no-repeat; } 73 | .jqueryFileTree LI.ext_psd { background: url(images/psd.png) left top no-repeat; } 74 | .jqueryFileTree LI.ext_pl { background: url(images/script.png) left top no-repeat; } 75 | .jqueryFileTree LI.ext_py { background: url(images/script.png) left top no-repeat; } 76 | .jqueryFileTree LI.ext_rb { background: url(images/ruby.png) left top no-repeat; } 77 | .jqueryFileTree LI.ext_rbx { background: url(images/ruby.png) left top no-repeat; } 78 | .jqueryFileTree LI.ext_rhtml { background: url(images/ruby.png) left top no-repeat; } 79 | .jqueryFileTree LI.ext_rpm { background: url(images/linux.png) left top no-repeat; } 80 | .jqueryFileTree LI.ext_ruby { background: url(images/ruby.png) left top no-repeat; } 81 | .jqueryFileTree LI.ext_sql { background: url(images/db.png) left top no-repeat; } 82 | .jqueryFileTree LI.ext_swf { background: url(images/flash.png) left top no-repeat; } 83 | .jqueryFileTree LI.ext_tif { background: url(images/picture.png) left top no-repeat; } 84 | .jqueryFileTree LI.ext_tiff { background: url(images/picture.png) left top no-repeat; } 85 | .jqueryFileTree LI.ext_txt { background: url(images/txt.png) left top no-repeat; } 86 | .jqueryFileTree LI.ext_vb { background: url(images/code.png) left top no-repeat; } 87 | .jqueryFileTree LI.ext_wav { background: url(images/music.png) left top no-repeat; } 88 | .jqueryFileTree LI.ext_wmv { background: url(images/film.png) left top no-repeat; } 89 | .jqueryFileTree LI.ext_xls { background: url(images/xls.png) left top no-repeat; } 90 | .jqueryFileTree LI.ext_xml { background: url(images/code.png) left top no-repeat; } 91 | .jqueryFileTree LI.ext_zip { background: url(images/zip.png) left top no-repeat; } -------------------------------------------------------------------------------- /venv/seafformsite/seafform/templates/seafform/index.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {% load bootstrap i18n %} 3 | 4 | {% block title %}{% trans "Seafile forms" %}{% endblock %} 5 | 6 | {% block body %} 7 | 8 | {% if authenticated %} 9 | 35 | {% endif %} 36 | 37 | 38 |
39 | 40 |
41 | 42 | 48 | 49 |

{% trans "Seafile forms" %}

50 | 51 |

{% trans "Survey? Poll? Inscriptions?" %}

52 | 53 |

{% blocktrans %} 54 | Easily create a form from a 55 | LibreOffice spreadsheet 56 | and directly get results in Seafile! 58 | {% endblocktrans %} 59 |

60 | 61 | {% if authenticated %} 62 |

63 | {% trans "Create a new form" %} 65 |

66 | {% endif %} 67 | 68 |
69 | 70 |
71 | 72 | {% if show_public %} 73 | {% if public_forms %} 74 |
75 |
76 |

77 | {% trans "Public forms" %} 78 |

79 |
80 |
81 | 82 | {% for form in public_forms %} 83 | 84 | 87 | {% if authenticated %} 88 | 91 | {% endif %} 92 | 93 | {% endfor %} 94 |
85 | {{ form.title }} 86 | 89 | {{ form.owner.email }} 90 |
95 |
96 |
97 | {% else %} 98 | 101 | {% endif %} 102 | {% endif %} 103 | 104 | {% if justlogout %} 105 | 109 | {% endif %} 110 | 111 | 112 | {% if not authenticated %} 113 |
114 |
115 |

116 | {% if allow_public and public_needauth %} 117 | {% trans "Log in to view forms or create a new one" %} 118 | {% else %} 119 | {% trans "Log in to create a form" %} 120 | {% endif %} 121 |

122 |
123 |
124 |

125 | {% blocktrans %} 126 | You must have a Seafile account on 127 | {{ seaf_root }}, and use the same 128 | credentials to log in. 129 | {% endblocktrans %} 130 |

131 | 132 | {% if autherror %} 133 | 138 | {% endif %} 139 | 140 |
141 | {% csrf_token %} 142 | {{ loginform|bootstrap }} 143 | 144 |
145 | 146 |
147 |
148 | {% endif %} 149 | 150 |
151 | 152 | {% endblock %} 153 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/templates/seafform/form_as_table.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | {# encoding: utf-8 #} 3 | {% load bootstrap utils i18n %} 4 | {% block title %}{{ seafform.title }}{% endblock %} 5 | 6 | {% block headers %} 7 | 25 | {% endblock %} 26 | 27 | {% block body %} 28 | 29 |
30 | 31 |
32 | 33 | 40 | 41 |

{{ seafform.title }}

42 | 43 | {{ seafform.description|urlize|linebreaks }} 44 | 45 |
46 | 47 |
48 |
49 | {% csrf_token %} 50 | 51 | {% if seafform.data %} {# do not display head if no results #} 52 | 53 | 54 | {% for field in seafform.fields %} 55 | 56 | {% endfor %} 57 | 61 | 62 | 63 | {% endif %} 64 | 65 | 66 | {% for row in seafform.data %} 67 | 71 | {% for value in row %} 72 | {% if first_is_static and forloop.counter0 == 0 %}{% endif %} 85 | {% endfor %} 86 | 95 | 96 | {% if seafform.edit %} 97 | 98 | {% for value in row %} 99 | {% if forloop.first %} 100 | 101 | {% else %} 102 | 103 | {% endif %} 104 | {% endfor %} 105 | 109 | 110 | {% endif %} 111 | {% endfor %} 112 | 113 | {% if seafform.data %} {# do not display computations if no results #} 114 | 115 | {% for comp in computations %} 116 | 133 | {% endfor %} 134 | 135 | 136 | {% endif %} 137 | 138 | 139 | {% for field in djform %} 140 | {% if field.is_hidden %} 141 | {{ field }} 142 | {% if field.field.isstatic %} 143 | 146 | {% endif %} 147 | {% else %} 148 | 151 | {% endif %} 152 | {% endfor %} 153 | 158 | 159 | 160 |
{{ field.label }} 58 | 59 | {% trans "Action" %} 60 |
{% else %}{% endif %} 73 | 74 | {% if value|get_type == "bool" and value == True %} 75 | 76 | {% trans "Submit" %} 77 | {% elif value|get_type == "float" %} 78 | {{ value|floatformat:"-2" }} 79 | {% elif value|get_type == "date" %} 80 | {{ value|date:"l j N Y" }} 81 | {% else %} 82 | {{ value|default:""|linebreaks }} 83 | {% endif %} 84 | {% if first_is_static and forloop.counter0 == 0 %}{% else %} 87 | {% if seafform.edit %} 88 | 93 | {% endif %} 94 |
117 | {% if comp != None %} 118 | 119 | {{ comp|floatformat:"-2" }} 120 | {% if forloop.counter0 in max_chk_column %} 121 | 122 | {% trans "(maximum)" %} 123 | {% endif %} 124 | 125 | {% endif %} 126 | {% if forloop.counter0 == 0 %} 127 |
128 | {% blocktrans with seafform.data|length as nb %} 129 | {{ nb }} answer 130 | {% endblocktrans %} 131 | {% endif %} 132 |
144 |

{{ field.value|default:"" }}

145 |
149 | {{ field|bootstrap }} 150 | 154 | 157 |
161 |
162 |
163 | 164 | 169 | 170 |
171 | 172 |
173 |
174 | 175 | {% endblock %} 176 | 177 | {% block scripts %} 178 | 209 | {% endblock %} 210 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/bootstrap/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.5 (http://getbootstrap.com) 3 | * Copyright 2011-2015 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | 7 | /*! 8 | * Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=4883ecbae98d09844da6) 9 | * Config saved to config.json and https://gist.github.com/4883ecbae98d09844da6 10 | */ 11 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(t){"use strict";var e=t.fn.jquery.split(" ")[0].split(".");if(e[0]<2&&e[1]<9||1==e[0]&&9==e[1]&&e[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(t){"use strict";function e(e){return this.each(function(){var s=t(this),n=s.data("bs.button"),o="object"==typeof e&&e;n||s.data("bs.button",n=new i(this,o)),"toggle"==e?n.toggle():e&&n.setState(e)})}var i=function(e,s){this.$element=t(e),this.options=t.extend({},i.DEFAULTS,s),this.isLoading=!1};i.VERSION="3.3.5",i.DEFAULTS={loadingText:"loading..."},i.prototype.setState=function(e){var i="disabled",s=this.$element,n=s.is("input")?"val":"html",o=s.data();e+="Text",null==o.resetText&&s.data("resetText",s[n]()),setTimeout(t.proxy(function(){s[n](null==o[e]?this.options[e]:o[e]),"loadingText"==e?(this.isLoading=!0,s.addClass(i).attr(i,i)):this.isLoading&&(this.isLoading=!1,s.removeClass(i).removeAttr(i))},this),0)},i.prototype.toggle=function(){var t=!0,e=this.$element.closest('[data-toggle="buttons"]');if(e.length){var i=this.$element.find("input");"radio"==i.prop("type")?(i.prop("checked")&&(t=!1),e.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==i.prop("type")&&(i.prop("checked")!==this.$element.hasClass("active")&&(t=!1),this.$element.toggleClass("active")),i.prop("checked",this.$element.hasClass("active")),t&&i.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var s=t.fn.button;t.fn.button=e,t.fn.button.Constructor=i,t.fn.button.noConflict=function(){return t.fn.button=s,this},t(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(i){var s=t(i.target);s.hasClass("btn")||(s=s.closest(".btn")),e.call(s,"toggle"),t(i.target).is('input[type="radio"]')||t(i.target).is('input[type="checkbox"]')||i.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(e){t(e.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(e.type))})}(jQuery),+function(t){"use strict";function e(e){return this.each(function(){var s=t(this),n=s.data("bs.affix"),o="object"==typeof e&&e;n||s.data("bs.affix",n=new i(this,o)),"string"==typeof e&&n[e]()})}var i=function(e,s){this.options=t.extend({},i.DEFAULTS,s),this.$target=t(this.options.target).on("scroll.bs.affix.data-api",t.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",t.proxy(this.checkPositionWithEventLoop,this)),this.$element=t(e),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};i.VERSION="3.3.5",i.RESET="affix affix-top affix-bottom",i.DEFAULTS={offset:0,target:window},i.prototype.getState=function(t,e,i,s){var n=this.$target.scrollTop(),o=this.$element.offset(),a=this.$target.height();if(null!=i&&"top"==this.affixed)return i>n?"top":!1;if("bottom"==this.affixed)return null!=i?n+this.unpin<=o.top?!1:"bottom":t-s>=n+a?!1:"bottom";var r=null==this.affixed,l=r?n:o.top,h=r?a:e;return null!=i&&i>=n?"top":null!=s&&l+h>=t-s?"bottom":!1},i.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(i.RESET).addClass("affix");var t=this.$target.scrollTop(),e=this.$element.offset();return this.pinnedOffset=e.top-t},i.prototype.checkPositionWithEventLoop=function(){setTimeout(t.proxy(this.checkPosition,this),1)},i.prototype.checkPosition=function(){if(this.$element.is(":visible")){var e=this.$element.height(),s=this.options.offset,n=s.top,o=s.bottom,a=Math.max(t(document).height(),t(document.body).height());"object"!=typeof s&&(o=n=s),"function"==typeof n&&(n=s.top(this.$element)),"function"==typeof o&&(o=s.bottom(this.$element));var r=this.getState(a,e,n,o);if(this.affixed!=r){null!=this.unpin&&this.$element.css("top","");var l="affix"+(r?"-"+r:""),h=t.Event(l+".bs.affix");if(this.$element.trigger(h),h.isDefaultPrevented())return;this.affixed=r,this.unpin="bottom"==r?this.getPinnedOffset():null,this.$element.removeClass(i.RESET).addClass(l).trigger(l.replace("affix","affixed")+".bs.affix")}"bottom"==r&&this.$element.offset({top:a-e-o})}};var s=t.fn.affix;t.fn.affix=e,t.fn.affix.Constructor=i,t.fn.affix.noConflict=function(){return t.fn.affix=s,this},t(window).on("load",function(){t('[data-spy="affix"]').each(function(){var i=t(this),s=i.data();s.offset=s.offset||{},null!=s.offsetBottom&&(s.offset.bottom=s.offsetBottom),null!=s.offsetTop&&(s.offset.top=s.offsetTop),e.call(i,s)})})}(jQuery),+function(t){"use strict";function e(e){var i,s=e.attr("data-target")||(i=e.attr("href"))&&i.replace(/.*(?=#[^\s]+$)/,"");return t(s)}function i(e){return this.each(function(){var i=t(this),n=i.data("bs.collapse"),o=t.extend({},s.DEFAULTS,i.data(),"object"==typeof e&&e);!n&&o.toggle&&/show|hide/.test(e)&&(o.toggle=!1),n||i.data("bs.collapse",n=new s(this,o)),"string"==typeof e&&n[e]()})}var s=function(e,i){this.$element=t(e),this.options=t.extend({},s.DEFAULTS,i),this.$trigger=t('[data-toggle="collapse"][href="#'+e.id+'"],[data-toggle="collapse"][data-target="#'+e.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};s.VERSION="3.3.5",s.TRANSITION_DURATION=350,s.DEFAULTS={toggle:!0},s.prototype.dimension=function(){var t=this.$element.hasClass("width");return t?"width":"height"},s.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var e,n=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(n&&n.length&&(e=n.data("bs.collapse"),e&&e.transitioning))){var o=t.Event("show.bs.collapse");if(this.$element.trigger(o),!o.isDefaultPrevented()){n&&n.length&&(i.call(n,"hide"),e||n.data("bs.collapse",null));var a=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[a](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var r=function(){this.$element.removeClass("collapsing").addClass("collapse in")[a](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!t.support.transition)return r.call(this);var l=t.camelCase(["scroll",a].join("-"));this.$element.one("bsTransitionEnd",t.proxy(r,this)).emulateTransitionEnd(s.TRANSITION_DURATION)[a](this.$element[0][l])}}}},s.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var e=t.Event("hide.bs.collapse");if(this.$element.trigger(e),!e.isDefaultPrevented()){var i=this.dimension();this.$element[i](this.$element[i]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var n=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return t.support.transition?void this.$element[i](0).one("bsTransitionEnd",t.proxy(n,this)).emulateTransitionEnd(s.TRANSITION_DURATION):n.call(this)}}},s.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},s.prototype.getParent=function(){return t(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(t.proxy(function(i,s){var n=t(s);this.addAriaAndCollapsedClass(e(n),n)},this)).end()},s.prototype.addAriaAndCollapsedClass=function(t,e){var i=t.hasClass("in");t.attr("aria-expanded",i),e.toggleClass("collapsed",!i).attr("aria-expanded",i)};var n=t.fn.collapse;t.fn.collapse=i,t.fn.collapse.Constructor=s,t.fn.collapse.noConflict=function(){return t.fn.collapse=n,this},t(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(s){var n=t(this);n.attr("data-target")||s.preventDefault();var o=e(n),a=o.data("bs.collapse"),r=a?"toggle":n.data();i.call(o,r)})}(jQuery),+function(t){"use strict";function e(i,s){this.$body=t(document.body),this.$scrollElement=t(t(i).is(document.body)?window:i),this.options=t.extend({},e.DEFAULTS,s),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",t.proxy(this.process,this)),this.refresh(),this.process()}function i(i){return this.each(function(){var s=t(this),n=s.data("bs.scrollspy"),o="object"==typeof i&&i;n||s.data("bs.scrollspy",n=new e(this,o)),"string"==typeof i&&n[i]()})}e.VERSION="3.3.5",e.DEFAULTS={offset:10},e.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},e.prototype.refresh=function(){var e=this,i="offset",s=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),t.isWindow(this.$scrollElement[0])||(i="position",s=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var e=t(this),n=e.data("target")||e.attr("href"),o=/^#./.test(n)&&t(n);return o&&o.length&&o.is(":visible")&&[[o[i]().top+s,n]]||null}).sort(function(t,e){return t[0]-e[0]}).each(function(){e.offsets.push(this[0]),e.targets.push(this[1])})},e.prototype.process=function(){var t,e=this.$scrollElement.scrollTop()+this.options.offset,i=this.getScrollHeight(),s=this.options.offset+i-this.$scrollElement.height(),n=this.offsets,o=this.targets,a=this.activeTarget;if(this.scrollHeight!=i&&this.refresh(),e>=s)return a!=(t=o[o.length-1])&&this.activate(t);if(a&&e=n[t]&&(void 0===n[t+1]||e, 2015 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: PACKAGE VERSION\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2015-11-11 17:21+0100\n" 11 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 12 | "Last-Translator: Florian Birée \n" 13 | "Language-Team: French\n" 14 | "Language: French\n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Plural-Forms: nplurals=2; plural=(n > 1);\n" 19 | 20 | #. Translators: field type for spreadsheet 21 | #: seafform.py:80 22 | msgid "text" 23 | msgstr "texte" 24 | 25 | #. Translators: field type for spreadsheet 26 | #: seafform.py:85 27 | msgid "longtext" 28 | msgstr "texteLong" 29 | 30 | #. Translators: field type for spreadsheet 31 | #: seafform.py:90 32 | msgid "list" 33 | msgstr "liste" 34 | 35 | #. Translators: field type for spreadsheet 36 | #: seafform.py:101 37 | msgid "check" 38 | msgstr "caseÀCocher" 39 | 40 | #. Translators: field type for spreadsheet 41 | #: seafform.py:110 42 | msgid "checked" 43 | msgstr "caseCochée" 44 | 45 | #. Translators: field type for spreadsheet 46 | #: seafform.py:119 47 | msgid "date" 48 | msgstr "date" 49 | 50 | #. Translators: field type for spreadsheet 51 | #: seafform.py:124 52 | msgid "number" 53 | msgstr "nombre" 54 | 55 | #. Translators: field type for spreadsheet 56 | #: seafform.py:129 57 | msgid "static" 58 | msgstr "statique" 59 | 60 | #. Translators: ODS view mode 61 | #: seafform.py:183 62 | msgid "table" 63 | msgstr "tableau" 64 | 65 | #: seafform.py:183 66 | msgid "form" 67 | msgstr "formulaire" 68 | 69 | #. Translators: ODS edit, yes or no 70 | #. Translators: ODS public, yes or no 71 | #: seafform.py:187 seafform.py:191 72 | msgid "yes" 73 | msgstr "oui" 74 | 75 | #: seafform.py:187 seafform.py:191 76 | msgid "no" 77 | msgstr "non" 78 | 79 | #: templates/404.html:4 templates/404.html.py:20 templates/500.html:4 80 | #: templates/500.html.py:20 templates/seafform/index.html:4 81 | #: templates/seafform/index.html.py:49 templates/seafform/new.html:4 82 | #: templates/seafform/private.html:4 83 | msgid "Seafile forms" 84 | msgstr "Formulaires Seafile" 85 | 86 | #: templates/404.html:4 87 | msgid "Form not found" 88 | msgstr "Formulaire introuvable" 89 | 90 | #: templates/404.html:23 91 | msgid "" 92 | "\n" 93 | " The form you try to open is no more reachable.\n" 94 | " May be its owner put it off-line?\n" 95 | " " 96 | msgstr "" 97 | "\n" 98 | "Le formulaire que vous essayez d'ouvrir n'est plus accessible.\n" 99 | "Peut-être que son propriétaire l'a mis hors-ligne ?" 100 | 101 | #: templates/500.html:4 102 | msgid "Server fault" 103 | msgstr "Erreur du serveur" 104 | 105 | #: templates/500.html:23 106 | msgid "" 107 | "\n" 108 | " An error append server-side. May be a form is not well-built?\n" 109 | " " 110 | msgstr "" 111 | "\n" 112 | "Le serveur a rencontré une erreur. Peut-être que le formulaire n'était pas " 113 | "bien construit ?" 114 | 115 | #: templates/500.html:28 116 | msgid "" 117 | "\n" 118 | " You can contact the form owner to tell her you had a problem.\n" 119 | " " 120 | msgstr "" 121 | "\n" 122 | "Vous pouvez contacter le propriétaire du formulaire pour lui dire que vous " 123 | "avez eu un problème." 124 | 125 | #: templates/base.html:59 126 | msgid "" 127 | "\n" 128 | " It was a form build with\n" 129 | " free\n" 130 | " softwares: LibreOffice,\n" 132 | " Seafile and \n" 133 | " SeafForm " 134 | "(give\n" 135 | " me the source code)!\n" 136 | " " 137 | msgstr "" 138 | "\n" 139 | "C'était un formulaire produit avec les logiciels libres LibreOffice, Seafile et SeafForm (donnez-moi le " 143 | "code source) !" 144 | 145 | #: templates/seafform/form_as_form.html:42 146 | #: templates/seafform/form_as_table.html:158 147 | msgid "" 148 | "\n" 149 | " : champs obligatoires.\n" 150 | " " 151 | msgstr "" 152 | "\n" 153 | " : champs obligatoires." 154 | 155 | #: templates/seafform/form_as_form.html:77 156 | #: templates/seafform/form_as_table.html:69 157 | #: templates/seafform/form_as_table.html:147 templates/seafform/new.html:72 158 | #: templates/seafform/rowedit.html:21 159 | msgid "Submit" 160 | msgstr "Valider" 161 | 162 | #: templates/seafform/form_as_table.html:52 templates/seafform/private.html:103 163 | msgid "Action" 164 | msgstr "Action" 165 | 166 | #: templates/seafform/form_as_table.html:93 167 | #: templates/seafform/form_as_table.html:174 168 | #: templates/seafform/form_as_table.html:192 169 | msgid "Loading..." 170 | msgstr "Chargement..." 171 | 172 | #: templates/seafform/form_as_table.html:115 173 | msgid "(maximum)" 174 | msgstr "(maximum)" 175 | 176 | #: templates/seafform/form_as_table.html:121 177 | #, python-format 178 | msgid "" 179 | "\n" 180 | " %(nb)s answer\n" 181 | " " 182 | msgstr "" 183 | "\n" 184 | "%(nb)s réponses\n" 185 | " " 186 | 187 | #: templates/seafform/index.html:14 templates/seafform/new.html:17 188 | #: templates/seafform/private.html:13 189 | msgid "Toggle navigation" 190 | msgstr "Basculer la navigation" 191 | 192 | #: templates/seafform/index.html:26 templates/seafform/index.html.py:77 193 | #: templates/seafform/new.html:29 templates/seafform/private.html:25 194 | msgid "Public forms" 195 | msgstr "Formulaires publics" 196 | 197 | #: templates/seafform/index.html:28 templates/seafform/new.html:31 198 | #: templates/seafform/private.html:27 199 | msgid "My forms" 200 | msgstr "Mes formulaires" 201 | 202 | #: templates/seafform/index.html:29 templates/seafform/new.html:32 203 | #: templates/seafform/private.html:28 204 | msgid "Open Seafile" 205 | msgstr "Ouvrir Seafile" 206 | 207 | #: templates/seafform/index.html:30 templates/seafform/new.html:33 208 | #: templates/seafform/private.html:29 209 | msgid "Logout" 210 | msgstr "Se déconnecter" 211 | 212 | #: templates/seafform/index.html:51 213 | msgid "Survey? Poll? Inscriptions?" 214 | msgstr "Un sondage ? Une enquête ? Des inscriptions ?" 215 | 216 | #: templates/seafform/index.html:53 217 | msgid "" 218 | "\n" 219 | " Easily create a form from a \n" 220 | " LibreOffice " 221 | "spreadsheet\n" 222 | " and directly get results in Seafile!\n" 224 | " " 225 | msgstr "" 226 | "\n" 227 | "Créez simplement un formulaire à partir d'un tableur LibreOffice, et récupérez directement les résultats " 229 | "dans Seafile !" 230 | 231 | #: templates/seafform/index.html:64 templates/seafform/new.html:43 232 | #: templates/seafform/private.html:38 templates/seafform/private.html.py:56 233 | msgid "Create a new form" 234 | msgstr "Créer un nouveau formulaire" 235 | 236 | #: templates/seafform/index.html:99 237 | msgid "No public form available" 238 | msgstr "Pas de formulaires publics disponibles" 239 | 240 | #: templates/seafform/index.html:107 241 | msgid "So long, and thanks for all the fish!" 242 | msgstr "Salut, et encore merci pour le poisson !" 243 | 244 | #: templates/seafform/index.html:117 245 | msgid "Log in to view forms or create a new one" 246 | msgstr "Se connecter pour voir les formulaires publics ou en créer un nouveau" 247 | 248 | #: templates/seafform/index.html:119 249 | msgid "Log in to create a form" 250 | msgstr "Se connecter pour créer un formulaire" 251 | 252 | #: templates/seafform/index.html:125 253 | #, python-format 254 | msgid "" 255 | "\n" 256 | " You must have a Seafile account on\n" 257 | " %(seaf_root)s, and use the " 258 | "same\n" 259 | " credentials to log in.\n" 260 | " " 261 | msgstr "" 262 | "\n" 263 | "Vous devez avoir un compte sur le Seafile " 264 | "%(seaf_root)s, et utiliser les mêmes identifiants pour vous connecter." 265 | 266 | #: templates/seafform/index.html:135 267 | msgid "Error:" 268 | msgstr "Erreur :" 269 | 270 | #: templates/seafform/index.html:136 271 | msgid "credentials not valid" 272 | msgstr "identifiants invalides" 273 | 274 | #: templates/seafform/index.html:143 275 | msgid "Login" 276 | msgstr "Se connecter" 277 | 278 | #: templates/seafform/new.html:48 279 | msgid "" 280 | "\n" 281 | " Choose in your Seafile the LibreOffice spreadsheet (.ods) which will be\n" 282 | " the form:\n" 283 | " " 284 | msgstr "" 285 | "\n" 286 | "Sélectionnez dans votre Seafile le tableur LibreOffice (.ods) qui sera à la " 287 | "base du formulaire :" 288 | 289 | #: templates/seafform/new.html:57 290 | msgid "Conexion to Seafile..." 291 | msgstr "Connexion à Seafile..." 292 | 293 | #: templates/seafform/new.html:65 templates/seafform/rowedit.html:24 294 | msgid "Cancel" 295 | msgstr "Annuler" 296 | 297 | #: templates/seafform/private.html:41 298 | msgid "1. create the spreadsheet with questions" 299 | msgstr "1. créer le tableur avec les questions" 300 | 301 | #: templates/seafform/private.html:44 302 | msgid "Forms templates" 303 | msgstr "Modèles de formulaires" 304 | 305 | #: templates/seafform/private.html:47 306 | msgid "" 307 | "\n" 308 | " You just need to download a template, to edit it, and to " 309 | "synchronize\n" 310 | " it in Seafile.\n" 311 | " " 312 | msgstr "" 313 | "\n" 314 | "Il suffit de télécharger un modèle, de le modifier, et de le synchroniser " 315 | "dans Seafile." 316 | 317 | #: templates/seafform/private.html:53 318 | msgid "2. create the form" 319 | msgstr "2. créer le formulaire" 320 | 321 | #: templates/seafform/private.html:59 322 | msgid "Select the right spreadsheet." 323 | msgstr "Indiquez quel est le tableur en question." 324 | 325 | #: templates/seafform/private.html:62 326 | msgid "3. ask people to answer" 327 | msgstr "3. invitez vos amis à répondre" 328 | 329 | #: templates/seafform/private.html:65 330 | msgid "" 331 | "\n" 332 | " Send the form's public address to all affected persons!\n" 333 | " " 334 | msgstr "" 335 | "\n" 336 | "Envoyez l'adresse publique du formulaire à toutes les personnes concernées !" 337 | 338 | #: templates/seafform/private.html:75 339 | #, python-format 340 | msgid "" 341 | "\n" 342 | " The form %(title)s was sucessfully created! Share the\n" 343 | " following address with interseted people:\n" 344 | " " 345 | msgstr "" 346 | "\n" 347 | "Le formulaire %(title)s a été créé avec succès ! Communiquez " 348 | "l'adresse suivante à vos correspondants :" 349 | 350 | #: templates/seafform/private.html:91 351 | #, python-format 352 | msgid "" 353 | "\n" 354 | " The form %(deleted)s was deleted. Its public link is " 355 | "disabled.\n" 356 | " " 357 | msgstr "" 358 | "\n" 359 | "Le formulaire %(deleted)s a bien été supprimé. Son lien public a été " 360 | "désactivé." 361 | 362 | #: templates/seafform/private.html:100 363 | msgid "Title" 364 | msgstr "Titre" 365 | 366 | #: templates/seafform/private.html:100 367 | msgid "Address" 368 | msgstr "Adresse" 369 | 370 | #: templates/seafform/private.html:124 371 | msgid "Delete" 372 | msgstr "Supprimer" 373 | 374 | #: templates/seafform/thanks.html:4 375 | msgid "Thanks!" 376 | msgstr "Merci !" 377 | 378 | #: templates/seafform/thanks.html:30 379 | msgid "Thank you for taking time to answer this form!" 380 | msgstr "Merci d'avoir pris le temps de répondre à ce questionnaire !" 381 | 382 | #~ msgid "New form" 383 | #~ msgstr "Nouveau formulaire" 384 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/seafform.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ############################################################################### 3 | # seafform/seafform.py 4 | # 5 | # Copyright © 2015, Florian Birée 6 | # 7 | # This file is a part of seafform. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU Affero General Public License as 11 | # published by the Free Software Foundation, either version 3 of the 12 | # License, or (at your option) any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU Affero General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU Affero General Public License 20 | # along with this program. If not, see . 21 | # 22 | ############################################################################### 23 | """Seafform forms description""" 24 | 25 | __author__ = "Florian Birée" 26 | __version__ = "0.2" 27 | __license__ = "AGPLv3" 28 | __copyright__ = "Copyright © 2015, Florian Birée " 29 | 30 | import os 31 | import ezodf 32 | import shutil 33 | import time 34 | import datetime 35 | from tempfile import NamedTemporaryFile 36 | if 'DJANGO_SETTINGS_MODULE' in os.environ: 37 | from django.utils.translation import ugettext_noop 38 | from django.utils.translation import ugettext as _ 39 | else: 40 | # Not in a Django environment 41 | _ = lambda x:x 42 | ugettext_noop = _ 43 | 44 | HEADERS_ROW = 4 # number of headers row in ods files 45 | #all_less_maxcount strategy delete formats 46 | # trying all_but_last, then all 47 | #ezodf.config.set_table_expand_strategy('all_but_last') 48 | 49 | class InvalidODS(Exception): 50 | """Raise when the ODS file doesn't respect the specification for Seafform 51 | """ 52 | pass 53 | 54 | class Field: 55 | """Base class for form fields""" 56 | 57 | def __init__(self, label, description=None, params=None, required=False, 58 | value=None): 59 | """Initialize a new field""" 60 | self.label = label 61 | self.description = description 62 | self.params = params 63 | self.required = required 64 | self.value = value 65 | 66 | def __repr__(self): 67 | if hasattr(self, 'ident'): 68 | ftype = self.ident 69 | else: 70 | ftype = 'Field' 71 | return '<{0}({1}){2}>'.format( 72 | ftype, 73 | self.label, 74 | ('*' if self.required else '') 75 | ) 76 | 77 | class TextField(Field): 78 | """Single-line text field""" 79 | # Translators: field type for spreadsheet 80 | ident = ugettext_noop('text') 81 | 82 | class LongTextField(Field): 83 | """Multiline text field""" 84 | # Translators: field type for spreadsheet 85 | ident = ugettext_noop('longtext') 86 | 87 | class ListField(Field): 88 | """List of choices field""" 89 | # Translators: field type for spreadsheet 90 | ident = ugettext_noop('list') 91 | 92 | def __init__(self, *args): 93 | Field.__init__(self, *args) 94 | self.choices = [ 95 | ch.strip() for ch in self.params.split(',') 96 | ] 97 | 98 | class BooleanField(Field): 99 | """Checkbox field""" 100 | # Translators: field type for spreadsheet 101 | ident = ugettext_noop('check') 102 | 103 | def __init__(self, *args): 104 | Field.__init__(self, *args) 105 | self.value = False 106 | 107 | class BooleanTrueField(Field): 108 | """Checked checkbox field""" 109 | # Translators: field type for spreadsheet 110 | ident = ugettext_noop('checked') 111 | 112 | def __init__(self, *args): 113 | Field.__init__(self, *args) 114 | self.value = True 115 | 116 | class DateField(Field): 117 | """Date field""" 118 | # Translators: field type for spreadsheet 119 | ident = ugettext_noop('date') 120 | 121 | class NumberField(Field): 122 | """Number field""" 123 | # Translators: field type for spreadsheet 124 | ident = ugettext_noop('number') 125 | 126 | class StaticField(Field): 127 | """Non-editable field""" 128 | # Translators: field type for spreadsheet 129 | ident = ugettext_noop('static') 130 | 131 | def field_of(ident): 132 | """Return the Field subclass corresponding to `ident`""" 133 | return [ 134 | cls 135 | for cls in Field.__subclasses__() 136 | if (cls.ident == ident) or (_(cls.ident) == ident) 137 | ][0] 138 | 139 | def untranslate(loc_val, raw_list, loc_list): 140 | """If loc_val in raw_list, 141 | return loc_val 142 | else: 143 | Return the raw value at the same position in raw_list than 144 | loc_val in loc_list 145 | default to the first value 146 | """ 147 | if loc_val in raw_list: 148 | return loc_val 149 | else: 150 | try: 151 | return raw_list[loc_list.index(loc_val)] 152 | except ValueError: 153 | return raw_list[0] 154 | 155 | class SeafForm: 156 | """Build and fill a form from an OpenDocumentSpreadsheet file""" 157 | _availables_f_ident = ( 158 | [cls.ident for cls in Field.__subclasses__()] 159 | + 160 | [_(cls.ident) for cls in Field.__subclasses__()] 161 | ) 162 | 163 | def __init__(self, filepath, seaf=None, repo_id=None): 164 | """Initialize a form for the file `filepath`. 165 | 166 | If seaf is a Seafile instance, repo_id must be the 167 | Seafile identifier of the repository where `filepath` is. 168 | 169 | If seaf is None, load `filepath` from the local filesystem 170 | """ 171 | # source properties 172 | self.filepath = filepath 173 | self.seaf = seaf 174 | self.repo_id = repo_id 175 | self.loaded = False 176 | 177 | # form properties 178 | self.title = None 179 | self.description = None 180 | self.fields = None 181 | self.data = None 182 | # Translators: ODS view mode 183 | self._view_as_values = (ugettext_noop('table'), ugettext_noop('form')) 184 | self._view_as_l10n = [_(v) for v in self._view_as_values] 185 | self.view_as = None # ('table' or 'form') 186 | # Translators: ODS edit, yes or no 187 | self._edit_values = (ugettext_noop('yes'), ugettext_noop('no')) 188 | self._edit_val10n = [_(v) for v in self._edit_values] 189 | self.edit = None 190 | # Translators: ODS public, yes or no 191 | self._public_values = (ugettext_noop('yes'), ugettext_noop('no')) 192 | self._public_val10n = [_(v) for v in self._public_values] 193 | self.public = None 194 | 195 | # cached items 196 | self.mtime = None 197 | self._odsfile = None 198 | self._first_empty_row = None 199 | 200 | def __repr__(self): 201 | """Representation of the form""" 202 | if self.loaded: 203 | return "".format(self.filepath, self.title) 204 | else: 205 | return "".format(self.filepath) 206 | 207 | def load(self): 208 | """Load form data from the ODS file""" 209 | odsopener = self._seaf_open if self.seaf else self._local_open 210 | 211 | seaf_f = odsopener() 212 | # save spreadsheet into a temporary file 213 | with NamedTemporaryFile(delete=False) as tmpfile: 214 | shutil.copyfileobj(seaf_f, tmpfile) 215 | tmpname = tmpfile.name 216 | seaf_f.close() 217 | 218 | # open spreadsheet document 219 | self.odsfile = ezodf.opendoc(tmpname) 220 | # delete tmp file 221 | os.unlink(tmpname) 222 | 223 | 224 | # get the Data sheet 225 | try: 226 | datash = self.odsfile.sheets['Data'] 227 | except KeyError: 228 | raise InvalidODS 229 | 230 | # get Properties 231 | self.title = datash['A7'].value 232 | self.description = datash['A9'].value 233 | self.view_as = untranslate( 234 | datash['A11'].value, 235 | self._view_as_values, 236 | self._view_as_l10n 237 | ) 238 | self.edit = ('yes' == (untranslate( 239 | datash['A13'].value, 240 | self._edit_values, 241 | self._edit_val10n 242 | ))) 243 | self.public = ('yes' == (untranslate( 244 | datash['A15'].value, 245 | self._public_values, 246 | self._public_val10n 247 | ))) 248 | 249 | # get fields 250 | self.fields = [] 251 | for colid in range(1, datash.ncols()): 252 | # get column 253 | col = datash.column(colid) 254 | # get field data 255 | fname = col[0].value 256 | fformat = col[1].value 257 | fparams = col[2].value 258 | fdesc = col[3].value 259 | # col[4:] is data 260 | 261 | # build field object (if fformat is known) 262 | if fformat and fformat.strip('*') in self._availables_f_ident: 263 | frequired = (fformat.endswith('*')) 264 | FType = field_of(fformat.rstrip('*')) 265 | self.fields.append(FType( 266 | fname, fdesc, fparams, frequired 267 | )) 268 | 269 | # get data 270 | self.data = [] 271 | first_empy_row = self._get_first_empty_row(recompute=True) 272 | for rowid in range(HEADERS_ROW, first_empy_row): 273 | row = datash.row(rowid) 274 | row_data = [] 275 | for celid in range(1, len(self.fields) + 1): 276 | val = row[celid].value 277 | if self.fields[celid-1].ident == 'date' and val: 278 | try: 279 | s_time = time.strptime(val, '%Y-%m-%d') 280 | except ValueError: 281 | pass # keep val as str 282 | else: 283 | val = datetime.date(*s_time[:3]) 284 | elif self.fields[celid-1].ident.startswith('check') and val: 285 | val = bool(val) 286 | row_data.append(val) 287 | self.data.append(row_data) 288 | 289 | # get mtime 290 | if self.seaf: 291 | s = self.seaf.stat_file(self.repo_id, self.filepath) 292 | self.mtime = float(s['mtime']) 293 | else: 294 | self.mtime = os.path.getmtime(self.filepath) 295 | 296 | self.loaded = True 297 | 298 | def _seaf_open(self): 299 | """Return an opened file-like object from Seafile""" 300 | return self.seaf.open_file(self.repo_id, self.filepath) 301 | 302 | def _local_open(self): 303 | """Return an opened file object from local filesystem""" 304 | return open(self.filepath, 'rb') 305 | 306 | def _get_first_empty_row(self, recompute=False): 307 | """Return the first empty row number 308 | if recompute, do not use the cached value 309 | """ 310 | if self._first_empty_row is not None and not recompute: 311 | return self._first_empty_row 312 | 313 | # get the Data sheet 314 | try: 315 | datash = self.odsfile.sheets['Data'] 316 | except KeyError: 317 | raise InvalidODS 318 | 319 | # find the first empty row 320 | rowid = datash.nrows() - 1 321 | bcol = datash.column(1) 322 | for celid in reversed(range(HEADERS_ROW, datash.nrows())): 323 | if not bcol[celid].value: 324 | rowid = celid 325 | else: 326 | break 327 | for colid in range(1, datash.ncols()): 328 | # check if rowid is empty for all the row 329 | # go down until empty 330 | while (rowid < datash.nrows() and datash[rowid, colid].value): 331 | rowid += 1 332 | 333 | self._first_empty_row = rowid 334 | return rowid 335 | 336 | def post(self, values, replace_row=None): 337 | """Post data from values into the ODS file 338 | 339 | values is the dict of {ident: value} 340 | optionaly replace values from the row `replace_row` 341 | 342 | WARNING: type and required verification must be done before 343 | """ 344 | # get new mtime 345 | if self.seaf: 346 | s = self.seaf.stat_file(self.repo_id, self.filepath) 347 | new_mtime = float(s['mtime']) 348 | else: 349 | new_mtime = os.path.getmtime(self.filepath) 350 | # if mtime has changed: 351 | if self.mtime != new_mtime: 352 | self.load() # reload 353 | 354 | # get the Data sheet 355 | try: 356 | datash = self.odsfile.sheets['Data'] 357 | except KeyError: 358 | raise InvalidODS 359 | 360 | 361 | # save data in a new line 362 | if replace_row is None: 363 | rowid = self._get_first_empty_row() 364 | self.data.append([]) 365 | self._first_empty_row += 1 366 | else: 367 | rowid = replace_row 368 | 369 | # fill the row with values 370 | for colid in range(1, datash.ncols()): 371 | column = datash.column(colid) 372 | fname = column[0].value 373 | if fname in values and values[fname]: 374 | value = (1 if values[fname] is True else values[fname]) 375 | try: 376 | column[rowid].set_value(value) 377 | except IndexError: 378 | # add row 379 | datash.append_rows(1) 380 | column = datash.column(colid) 381 | column[rowid].set_value(value) 382 | 383 | # convert to boolean for cached data 384 | if self.fields[colid-1].ident.startswith('check') and value: 385 | value = bool(value) 386 | try: 387 | self.data[rowid - HEADERS_ROW][colid - 1] = value 388 | except IndexError: 389 | self.data[rowid - HEADERS_ROW].append(value) 390 | else: 391 | try: 392 | self.data[rowid - HEADERS_ROW][colid - 1] = None 393 | except IndexError: 394 | self.data[rowid - HEADERS_ROW].append(None) 395 | 396 | if self.seaf: 397 | # save spreadsheet into a temporary file 398 | with NamedTemporaryFile(delete=False) as tmpfile: 399 | # ezodf realy doesn't like file-like objects… 400 | tmpname = tmpfile.name 401 | self.odsfile.saveas(tmpname) 402 | 403 | # update distant file 404 | with open(tmpname, 'rb') as fileo: 405 | fid = self.seaf.update_file(self.repo_id, self.filepath, fileo) 406 | # unlink tmp file 407 | os.unlink(tmpname) 408 | else: 409 | # save spreadsheet into the local file 410 | self.odsfile.saveas(self.filepath) 411 | 412 | def get_values_from_data(self, row_id): 413 | """Build the dict of {'name': value} for row_id from self.data""" 414 | vals = {} 415 | for i, field in enumerate(self.fields): 416 | vals[field.label] = self.data[row_id - HEADERS_ROW][i] 417 | return vals 418 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/seafile.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ############################################################################### 3 | # seafform/seafile.py 4 | # 5 | # Copyright © 2015, Florian Birée 6 | # 7 | # This file is a part of seafform. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU Affero General Public License as 11 | # published by the Free Software Foundation, either version 3 of the 12 | # License, or (at your option) any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU Affero General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU Affero General Public License 20 | # along with this program. If not, see . 21 | # 22 | ############################################################################### 23 | """Seafile API wrapper""" 24 | 25 | __author__ = "Florian Birée" 26 | __version__ = "0.2" 27 | __license__ = "AGPLv3" 28 | __copyright__ = "Copyright © 2015, Florian Birée " 29 | 30 | import os 31 | from functools import wraps 32 | from urllib.parse import urljoin, urlparse 33 | import requests 34 | from requests.exceptions import HTTPError 35 | import json 36 | 37 | # HTTP verbs 38 | GET = requests.get 39 | POST = requests.post 40 | PUT = requests.put 41 | DELETE = requests.delete 42 | 43 | # Seafile exceptions 44 | class SeafileError(Exception): 45 | """Base class for all Seafile-related exceptions""" 46 | 47 | class NotAuthenticated(SeafileError): 48 | """Raised when trying an operation that need authentication""" 49 | pass 50 | 51 | class AuthError(SeafileError): 52 | """Authentification error""" 53 | pass 54 | 55 | class APIError(SeafileError): 56 | """API error""" 57 | msg = "Seafile API error" 58 | def __init__(self, curl_cmd=None, msg=None): 59 | """APIError(curl_cmd, *args)""" 60 | self.curl_cmd = curl_cmd 61 | if msg: 62 | self.msg = msg 63 | 64 | def __add__curl(self, msg): 65 | if self.curl_cmd: 66 | return msg + '\n$ ' + self.curl_cmd 67 | else: 68 | return msg 69 | 70 | def __str__(self): 71 | return self.__add__curl(self.msg) 72 | 73 | class BadPath(APIError): 74 | code = 400 75 | msg = "400 Path is missing/bad." 76 | 77 | class Forbidden(APIError): 78 | code = 403 79 | msg = "403 Forbidden" 80 | 81 | class NotFound(APIError): 82 | code = 404 83 | msg = "404 The path does not exists." 84 | 85 | class InvalidPath(APIError): 86 | code = 440 87 | msg = "440 Invalid path or filname, or encrypted repo." 88 | 89 | class FileExists(APIError): 90 | code = 441 91 | msg = "441 File already exists." 92 | 93 | class InternalServerError(APIError): 94 | code = 500 95 | msg = "500 Internal server error (may be out of quota)." 96 | 97 | class OperationFailed(APIError): 98 | code = 520 99 | msg = "520 Operation failed." 100 | 101 | EXCEPT_CODE = { 102 | '400': BadPath, 103 | '403': Forbidden, 104 | '404': NotFound, 105 | '440': InvalidPath, 106 | '441': FileExists, 107 | '500': InternalServerError, 108 | '520': OperationFailed, 109 | } 110 | 111 | def curlify(request): 112 | """Return a curl command line corresponding to the `request`""" 113 | return ( 114 | 'curl -v ' + 115 | ('-X PUT ' if request.method == 'PUT' else '') + 116 | (('-d "%s" ' % request.body) if request.body else '') + 117 | ' '.join("-H '%s: %s'" % (k, v) for (k, v) in request.headers.items()) + 118 | ' ' + 119 | request.url 120 | ) 121 | 122 | class Seafile: 123 | """Seafile connector""" 124 | 125 | base_api = 'api2/' 126 | 127 | # internals 128 | 129 | def __init__(self, url, verifycerts=True): 130 | """New seafile connector to `url` instance 131 | 132 | set verifycerts to False to disable TLS certificate verification 133 | """ 134 | self.url = url 135 | self._api_url = urljoin(self.url, self.base_api) 136 | self.token = None 137 | self.email = None 138 | self.verify = verifycerts 139 | 140 | def _need_auth(func): 141 | """Decorator to ensure the connector is authentified before 142 | executing `func`""" 143 | @wraps(func) 144 | def wrapper(self, *args, **kwargs): 145 | if self.token is None: 146 | raise NotAuthenticated 147 | return func(self, *args, **kwargs) 148 | return wrapper 149 | 150 | def __repr__(self): 151 | """String representation of the object""" 152 | hostname = urlparse(self.url).hostname 153 | if self.token is None: 154 | return "".format({'hostname': hostname}) 155 | else: 156 | return "".format({ 157 | 'email': self.email, 158 | 'hostname': hostname 159 | }) 160 | 161 | def _api(self, verb, cmd, params=None, data=None, headers=None, files=None, 162 | token=True, raw_url=False): 163 | """Execute the API command VERB `cmd` 164 | 165 | VERB is one of GET, POST, PUT, DELETE 166 | 167 | `params` : optionals ?= params 168 | `headers` : optionals headers 169 | `data` : optionals POST or PUT data 170 | `files` : POST data content using the Content-Type 171 | multipart/form-data (RFC 2388). If a value in the data dict is 172 | an opened file object, it will be sent as a file. 173 | 174 | if `token`, add the Authorization header 175 | if `raw_url`, use `cmd` as full url instead of concatenating `cmd` 176 | to the API url. 177 | 178 | Return json-loaded data 179 | """ 180 | final_headers = {'Accept': 'application/json; indent=4; charset=utf-8'} 181 | if headers: 182 | final_headers.update(headers) 183 | if token: 184 | final_headers['Authorization'] = 'Token ' + self.token 185 | 186 | r = verb( 187 | urljoin(self._api_url, cmd) if not raw_url else cmd, 188 | params=params, 189 | headers=final_headers, 190 | data=data, 191 | files=files, 192 | verify=self.verify 193 | ) 194 | try: 195 | r.raise_for_status() 196 | except HTTPError: 197 | apierror = EXCEPT_CODE.get(r.status_code, APIError) 198 | raise apierror( 199 | curlify(r.request), 200 | r.text 201 | ) 202 | else: 203 | return r.json() 204 | 205 | def _multipart_filname_patching(self, prepped, filename): 206 | """Since Seafile doesn't handle well RFC2231 207 | wich specify to send filename*= field for utf-8 characters 208 | (which is what Requests does), we patch the request body 209 | to put just a raw utf-8 filename (like curl does) 210 | 211 | this is a dirty hack 212 | 213 | `prepped` is a prepared request 214 | `filename` is the str filename to encode in raw utf-8 215 | 216 | return the patched prepped 217 | """ 218 | starbytes = b'filename*=' 219 | # find the start of starbytes 220 | start = prepped.body.find(starbytes) 221 | if start == -1: 222 | # not here, return prepped unchanged 223 | return prepped 224 | # find the first \r\n sequence after starbytes (end of filename) 225 | end = prepped.body.find(b'\r\n', start) 226 | 227 | # patch the body 228 | prepped.body = ( 229 | prepped.body[:start] + 230 | b'filename="' + filename.encode('utf8') + b'"' + 231 | prepped.body[end:] 232 | ) 233 | # recompute the Content-Length header 234 | prepped.headers['Content-Length'] = str(len(prepped.body)) 235 | return prepped 236 | 237 | # auth methods 238 | 239 | def authenticate(self, email, password=None, token=None, validate=True): 240 | """Authenticate against the Seafile server, with `email` and either: 241 | - `password` : to authenticate with a password 242 | - `token` : to reuse a token from a previous authentication 243 | 244 | if success, the token is available at self.token 245 | else raise AuthError 246 | 247 | if not validate, the Seafile connector will not check the validy of 248 | the token (if token authentication). 249 | No seafile request will be done. If you are sure about your 250 | token validy, this will save time. 251 | """ 252 | if password is None and token is None: 253 | raise ValueError 254 | elif token is not None: # token auth 255 | if validate: 256 | # try to validate the token 257 | try: 258 | self._auth_ping(token) 259 | except SeafileError: 260 | raise AuthError 261 | else: 262 | self.token = token 263 | self.email = email 264 | else: 265 | self.token = token 266 | self.email = email 267 | else: # password auth 268 | try: 269 | resp = self._api(POST, 'auth-token/', data={ 270 | 'username': email, 271 | 'password': password 272 | }, token=False) 273 | except: 274 | raise AuthError 275 | else: 276 | if resp['token']: 277 | self.token = resp['token'] 278 | self.email = email 279 | 280 | # test methods 281 | 282 | def ping(self): 283 | """Ping the Seafile server 284 | 285 | raise SeafileError if not working 286 | """ 287 | if not self._api(GET, 'ping/', token=False) == "pong": 288 | raise SeafileError 289 | 290 | def _auth_ping(self, token): 291 | """Ping the Seafile server with the token `token` 292 | 293 | raise SeafileError if not working 294 | """ 295 | # here the token may not be validated, so we do not use _api(token=True) 296 | headers = {'Authorization': 'Token ' + token} 297 | resp = self._api(GET, 'auth/ping/', headers=headers, token=False) 298 | if resp != "pong": 299 | raise SeafileError 300 | 301 | @_need_auth 302 | def auth_ping(self): 303 | """Ping the Seafile server with the token `token` 304 | 305 | raise SeafileError if not working 306 | """ 307 | self._auth_ping(self.token) 308 | 309 | # library methods 310 | 311 | @_need_auth 312 | def list_repos(self): 313 | """List repos/librarys 314 | 315 | return a list of { 316 | "permission": "rw", 317 | "encrypted": false, 318 | "mtime": 1400054900, 319 | "owner": "user@mail.com", 320 | "id": "f158d1dd-cc19-412c-b143-2ac83f352290", 321 | "size": 0, 322 | "name": "foo", 323 | "type": "repo", 324 | "virtual": false, 325 | "desc": "new library", 326 | "root": "0000000000000000000000000000000000000000" 327 | } 328 | """ 329 | return self._api(GET, 'repos/') 330 | 331 | # directory methods 332 | 333 | @_need_auth 334 | def list_dir(self, repo_id, path="/"): 335 | """list the content of a directory from library `repo_id`/`path` 336 | 337 | return a list of { 338 | "id": "e4fe14c8cda2206bb9606907cf4fca6b30221cf9", 339 | "type": "file|dir", 340 | "name": "test", 341 | "size": 0, # only for files 342 | } 343 | 344 | Errors: 345 | 404 NotFound 346 | 440 InvalidPath (encrypted repo) 347 | 520 OperationFailed 348 | """ 349 | return self._api( 350 | GET, 351 | 'repos/{repo_id}/dir/'.format(repo_id=repo_id), 352 | {'p': path} 353 | ) 354 | 355 | # files methods 356 | 357 | @_need_auth 358 | def open_file(self, repo_id, path): 359 | """get the file `repo_id`/`path`. 360 | 361 | Return an opened requests.Response.raw file-like object 362 | 363 | Errors: 364 | 400 BadPath 365 | 404 NotFound 366 | 520 OperationFailed 367 | """ 368 | # get the file link 369 | flink = self._api( 370 | GET, 371 | 'repos/{repo_id}/file/'.format(repo_id=repo_id), 372 | {'p': path} 373 | ) 374 | resp = requests.get(flink, stream=True) 375 | resp.raw.decode_content = True 376 | return resp.raw 377 | 378 | @_need_auth 379 | def lock_file(self, repo_id, path): 380 | """lock the file `repo_id`/`path`. 381 | 382 | Not implemented server side. 383 | """ 384 | raise NotImplementedError 385 | #return (self._api_cmd( 386 | # 'repos/{repo_id}/file/'.format(repo_id=repo_id), 387 | # {'operation': 'lock', 'p': path}, 388 | # put=True 389 | #) == "success") 390 | 391 | @_need_auth 392 | def unlock_file(self, repo_id, path): 393 | """unlock the file `repo_id`/`path`. 394 | 395 | Not implemented server side. 396 | """ 397 | raise NotImplementedError 398 | #return (self._api_cmd( 399 | # 'repos/{repo_id}/file/'.format(repo_id=repo_id), 400 | # {'operation': 'unlock', 'p': path}, 401 | # put=True 402 | #) == "success") 403 | 404 | @_need_auth 405 | def upload_file(self, repo_id, parent, fileo): 406 | """upload the file object `fileo` to `repo_id`/`parent` 407 | 408 | Return the file id 409 | 410 | Errors: 411 | 400 BadPath 412 | 440 InvalidPath 413 | 441 FileExists 414 | 500 InternalServerError (out of quota) 415 | """ 416 | # get upload link 417 | up_link = self._api(GET, 418 | 'repos/{repo_id}/upload-link/'.format(repo_id=repo_id) 419 | ) 420 | 421 | filename = os.path.split(fileo.name)[1] 422 | # upload file 423 | s = requests.Session() 424 | req = requests.Request('POST', 425 | up_link, 426 | files={ 427 | 'filename': (None, filename), 428 | 'parent_dir': (None, parent), 429 | 'file': (filename, fileo, 'application/octet-stream'), 430 | }, 431 | verify=self.verify 432 | ) 433 | prepped = self._multipart_filname_patching(req.prepare(), filename) 434 | 435 | r = s.send(prepped, stream=False) 436 | try: 437 | r.raise_for_status() 438 | except HTTPError: 439 | apierror = EXCEPT_CODE.get(r.status_code, APIError) 440 | raise apierror( 441 | curlify(r.request), 442 | r.text 443 | ) 444 | else: 445 | return r.text 446 | 447 | @_need_auth 448 | def update_file(self, repo_id, filepath, fileo): 449 | """update the file `repo_id`/`filepathe` with the file object `fileo` 450 | 451 | Return the file id 452 | 453 | Errors: 454 | 400 BadPath 455 | 440 InvalidPath 456 | 500 InternalServerError (out of quota) 457 | """ 458 | # get update link 459 | up_link = self._api(GET, 460 | 'repos/{repo_id}/update-link/'.format(repo_id=repo_id) 461 | ) 462 | 463 | filename = os.path.split(fileo.name)[1] 464 | # upload file 465 | s = requests.Session() 466 | req = requests.Request('POST', 467 | up_link, 468 | files={ 469 | #'filename': (None, filename), 470 | 'target_file': (None, filepath), 471 | 'file': (filename, fileo, 'application/octet-stream'), 472 | } 473 | ) 474 | prepped = self._multipart_filname_patching(req.prepare(), filename) 475 | 476 | r = s.send(prepped, stream=False, verify=self.verify) 477 | try: 478 | r.raise_for_status() 479 | except HTTPError: 480 | apierror = EXCEPT_CODE.get(r.status_code, APIError) 481 | raise apierror( 482 | curlify(r.request), 483 | r.text 484 | ) 485 | else: 486 | return r.text 487 | 488 | 489 | @_need_auth 490 | def delete_file(self, repo_id, path): 491 | """delete the file or directory `repo_id`/`path` 492 | 493 | Errors: 494 | 400 BadPath 495 | 520 OperationFailed 496 | """ 497 | return (self._api(DELETE, 498 | 'repos/{repo_id}/file/'.format(repo_id=repo_id), 499 | params={'p': path} 500 | ) == "success") 501 | 502 | @_need_auth 503 | def stat_file(self, repo_id, path): 504 | """get data about the file `repo_id`/`path` 505 | 506 | Return {'id', 'mtime', 'type':'file', 'name', 'size'} 507 | 508 | Errors: 509 | 400 BadPath 510 | 520 OperationFailed 511 | """ 512 | return self._api(GET, 513 | 'repos/{repo_id}/file/detail/'.format(repo_id=repo_id), 514 | params={'p': path} 515 | ) 516 | 517 | 518 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/bootstrap/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "vars": { 3 | "@gray-base": "#000", 4 | "@gray-darker": "lighten(@gray-base, 13.5%)", 5 | "@gray-dark": "lighten(@gray-base, 20%)", 6 | "@gray": "lighten(@gray-base, 33.5%)", 7 | "@gray-light": "lighten(@gray-base, 46.7%)", 8 | "@gray-lighter": "lighten(@gray-base, 93.5%)", 9 | "@brand-primary": "#FD700F", 10 | "@brand-success": "#5cb85c", 11 | "@brand-info": "#FEAC74", 12 | "@brand-warning": "#f0ad4e", 13 | "@brand-danger": "#d9534f", 14 | "@body-bg": "#fff", 15 | "@text-color": "@gray-dark", 16 | "@link-color": "@brand-primary", 17 | "@link-hover-color": "darken(@link-color, 15%)", 18 | "@link-hover-decoration": "underline", 19 | "@font-family-sans-serif": "\"Helvetica Neue\", Helvetica, Arial, sans-serif", 20 | "@font-family-serif": "Georgia, \"Times New Roman\", Times, serif", 21 | "@font-family-monospace": "Menlo, Monaco, Consolas, \"Courier New\", monospace", 22 | "@font-family-base": "@font-family-sans-serif", 23 | "@font-size-base": "14px", 24 | "@font-size-large": "ceil((@font-size-base * 1.25))", 25 | "@font-size-small": "ceil((@font-size-base * 0.85))", 26 | "@font-size-h1": "floor((@font-size-base * 2.6))", 27 | "@font-size-h2": "floor((@font-size-base * 2.15))", 28 | "@font-size-h3": "ceil((@font-size-base * 1.7))", 29 | "@font-size-h4": "ceil((@font-size-base * 1.25))", 30 | "@font-size-h5": "@font-size-base", 31 | "@font-size-h6": "ceil((@font-size-base * 0.85))", 32 | "@line-height-base": "1.428571429", 33 | "@line-height-computed": "floor((@font-size-base * @line-height-base))", 34 | "@headings-font-family": "inherit", 35 | "@headings-font-weight": "500", 36 | "@headings-line-height": "1.1", 37 | "@headings-color": "inherit", 38 | "@icon-font-path": "\"../fonts/\"", 39 | "@icon-font-name": "\"glyphicons-halflings-regular\"", 40 | "@icon-font-svg-id": "\"glyphicons_halflingsregular\"", 41 | "@padding-base-vertical": "6px", 42 | "@padding-base-horizontal": "12px", 43 | "@padding-large-vertical": "10px", 44 | "@padding-large-horizontal": "16px", 45 | "@padding-small-vertical": "5px", 46 | "@padding-small-horizontal": "10px", 47 | "@padding-xs-vertical": "1px", 48 | "@padding-xs-horizontal": "5px", 49 | "@line-height-large": "1.3333333", 50 | "@line-height-small": "1.5", 51 | "@border-radius-base": "4px", 52 | "@border-radius-large": "6px", 53 | "@border-radius-small": "3px", 54 | "@component-active-color": "#fff", 55 | "@component-active-bg": "@brand-primary", 56 | "@caret-width-base": "4px", 57 | "@caret-width-large": "5px", 58 | "@table-cell-padding": "8px", 59 | "@table-condensed-cell-padding": "5px", 60 | "@table-bg": "transparent", 61 | "@table-bg-accent": "#f9f9f9", 62 | "@table-bg-hover": "#f5f5f5", 63 | "@table-bg-active": "@table-bg-hover", 64 | "@table-border-color": "#ddd", 65 | "@btn-font-weight": "normal", 66 | "@btn-default-color": "#333", 67 | "@btn-default-bg": "#fff", 68 | "@btn-default-border": "#ccc", 69 | "@btn-primary-color": "#fff", 70 | "@btn-primary-bg": "@brand-primary", 71 | "@btn-primary-border": "darken(@btn-primary-bg, 5%)", 72 | "@btn-success-color": "#fff", 73 | "@btn-success-bg": "@brand-success", 74 | "@btn-success-border": "darken(@btn-success-bg, 5%)", 75 | "@btn-info-color": "#fff", 76 | "@btn-info-bg": "@brand-info", 77 | "@btn-info-border": "darken(@btn-info-bg, 5%)", 78 | "@btn-warning-color": "#fff", 79 | "@btn-warning-bg": "@brand-warning", 80 | "@btn-warning-border": "darken(@btn-warning-bg, 5%)", 81 | "@btn-danger-color": "#fff", 82 | "@btn-danger-bg": "@brand-danger", 83 | "@btn-danger-border": "darken(@btn-danger-bg, 5%)", 84 | "@btn-link-disabled-color": "@gray-light", 85 | "@btn-border-radius-base": "@border-radius-base", 86 | "@btn-border-radius-large": "@border-radius-large", 87 | "@btn-border-radius-small": "@border-radius-small", 88 | "@input-bg": "#fff", 89 | "@input-bg-disabled": "@gray-lighter", 90 | "@input-color": "@gray", 91 | "@input-border": "#ccc", 92 | "@input-border-radius": "@border-radius-base", 93 | "@input-border-radius-large": "@border-radius-large", 94 | "@input-border-radius-small": "@border-radius-small", 95 | "@input-border-focus": "@brand-primary", 96 | "@input-color-placeholder": "#999", 97 | "@input-height-base": "(@line-height-computed + (@padding-base-vertical * 2) + 2)", 98 | "@input-height-large": "(ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2)", 99 | "@input-height-small": "(floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2)", 100 | "@form-group-margin-bottom": "15px", 101 | "@legend-color": "@gray-dark", 102 | "@legend-border-color": "@brand-primary", 103 | "@input-group-addon-bg": "@gray-lighter", 104 | "@input-group-addon-border-color": "@input-border", 105 | "@cursor-disabled": "not-allowed", 106 | "@dropdown-bg": "#fff", 107 | "@dropdown-border": "rgba(0,0,0,.15)", 108 | "@dropdown-fallback-border": "#ccc", 109 | "@dropdown-divider-bg": "#e5e5e5", 110 | "@dropdown-link-color": "@gray-dark", 111 | "@dropdown-link-hover-color": "darken(@gray-dark, 5%)", 112 | "@dropdown-link-hover-bg": "#f5f5f5", 113 | "@dropdown-link-active-color": "@component-active-color", 114 | "@dropdown-link-active-bg": "@component-active-bg", 115 | "@dropdown-link-disabled-color": "@gray-light", 116 | "@dropdown-header-color": "@gray-light", 117 | "@dropdown-caret-color": "#000", 118 | "@screen-xs": "480px", 119 | "@screen-xs-min": "@screen-xs", 120 | "@screen-phone": "@screen-xs-min", 121 | "@screen-sm": "768px", 122 | "@screen-sm-min": "@screen-sm", 123 | "@screen-tablet": "@screen-sm-min", 124 | "@screen-md": "992px", 125 | "@screen-md-min": "@screen-md", 126 | "@screen-desktop": "@screen-md-min", 127 | "@screen-lg": "1200px", 128 | "@screen-lg-min": "@screen-lg", 129 | "@screen-lg-desktop": "@screen-lg-min", 130 | "@screen-xs-max": "(@screen-sm-min - 1)", 131 | "@screen-sm-max": "(@screen-md-min - 1)", 132 | "@screen-md-max": "(@screen-lg-min - 1)", 133 | "@grid-columns": "12", 134 | "@grid-gutter-width": "30px", 135 | "@grid-float-breakpoint": "@screen-sm-min", 136 | "@grid-float-breakpoint-max": "(@grid-float-breakpoint - 1)", 137 | "@container-tablet": "(720px + @grid-gutter-width)", 138 | "@container-sm": "@container-tablet", 139 | "@container-desktop": "(940px + @grid-gutter-width)", 140 | "@container-md": "@container-desktop", 141 | "@container-large-desktop": "(1140px + @grid-gutter-width)", 142 | "@container-lg": "@container-large-desktop", 143 | "@navbar-height": "50px", 144 | "@navbar-margin-bottom": "@line-height-computed", 145 | "@navbar-border-radius": "@border-radius-base", 146 | "@navbar-padding-horizontal": "floor((@grid-gutter-width / 2))", 147 | "@navbar-padding-vertical": "((@navbar-height - @line-height-computed) / 2)", 148 | "@navbar-collapse-max-height": "340px", 149 | "@navbar-default-color": "#777", 150 | "@navbar-default-bg": "#f8f8f8", 151 | "@navbar-default-border": "darken(@navbar-default-bg, 6.5%)", 152 | "@navbar-default-link-color": "#777", 153 | "@navbar-default-link-hover-color": "#333", 154 | "@navbar-default-link-hover-bg": "transparent", 155 | "@navbar-default-link-active-color": "#555", 156 | "@navbar-default-link-active-bg": "darken(@navbar-default-bg, 6.5%)", 157 | "@navbar-default-link-disabled-color": "#ccc", 158 | "@navbar-default-link-disabled-bg": "transparent", 159 | "@navbar-default-brand-color": "@navbar-default-link-color", 160 | "@navbar-default-brand-hover-color": "darken(@navbar-default-brand-color, 10%)", 161 | "@navbar-default-brand-hover-bg": "transparent", 162 | "@navbar-default-toggle-hover-bg": "#ddd", 163 | "@navbar-default-toggle-icon-bar-bg": "#888", 164 | "@navbar-default-toggle-border-color": "#ddd", 165 | "@navbar-inverse-color": "lighten(@gray-light, 15%)", 166 | "@navbar-inverse-bg": "#222", 167 | "@navbar-inverse-border": "darken(@navbar-inverse-bg, 10%)", 168 | "@navbar-inverse-link-color": "lighten(@gray-light, 15%)", 169 | "@navbar-inverse-link-hover-color": "#fff", 170 | "@navbar-inverse-link-hover-bg": "transparent", 171 | "@navbar-inverse-link-active-color": "@navbar-inverse-link-hover-color", 172 | "@navbar-inverse-link-active-bg": "darken(@navbar-inverse-bg, 10%)", 173 | "@navbar-inverse-link-disabled-color": "#444", 174 | "@navbar-inverse-link-disabled-bg": "transparent", 175 | "@navbar-inverse-brand-color": "@navbar-inverse-link-color", 176 | "@navbar-inverse-brand-hover-color": "#fff", 177 | "@navbar-inverse-brand-hover-bg": "transparent", 178 | "@navbar-inverse-toggle-hover-bg": "#333", 179 | "@navbar-inverse-toggle-icon-bar-bg": "#fff", 180 | "@navbar-inverse-toggle-border-color": "#333", 181 | "@nav-link-padding": "10px 15px", 182 | "@nav-link-hover-bg": "@gray-lighter", 183 | "@nav-disabled-link-color": "@gray-light", 184 | "@nav-disabled-link-hover-color": "@gray-light", 185 | "@nav-tabs-border-color": "#ddd", 186 | "@nav-tabs-link-hover-border-color": "@gray-lighter", 187 | "@nav-tabs-active-link-hover-bg": "@body-bg", 188 | "@nav-tabs-active-link-hover-color": "@gray", 189 | "@nav-tabs-active-link-hover-border-color": "#ddd", 190 | "@nav-tabs-justified-link-border-color": "#ddd", 191 | "@nav-tabs-justified-active-link-border-color": "@body-bg", 192 | "@nav-pills-border-radius": "@border-radius-base", 193 | "@nav-pills-active-link-hover-bg": "@component-active-bg", 194 | "@nav-pills-active-link-hover-color": "@component-active-color", 195 | "@pagination-color": "@link-color", 196 | "@pagination-bg": "#fff", 197 | "@pagination-border": "#ddd", 198 | "@pagination-hover-color": "@link-hover-color", 199 | "@pagination-hover-bg": "@gray-lighter", 200 | "@pagination-hover-border": "#ddd", 201 | "@pagination-active-color": "#fff", 202 | "@pagination-active-bg": "@brand-primary", 203 | "@pagination-active-border": "@brand-primary", 204 | "@pagination-disabled-color": "@gray-light", 205 | "@pagination-disabled-bg": "#fff", 206 | "@pagination-disabled-border": "#ddd", 207 | "@pager-bg": "@pagination-bg", 208 | "@pager-border": "@pagination-border", 209 | "@pager-border-radius": "15px", 210 | "@pager-hover-bg": "@pagination-hover-bg", 211 | "@pager-active-bg": "@pagination-active-bg", 212 | "@pager-active-color": "@pagination-active-color", 213 | "@pager-disabled-color": "@pagination-disabled-color", 214 | "@jumbotron-padding": "30px", 215 | "@jumbotron-color": "inherit", 216 | "@jumbotron-bg": "@gray-lighter", 217 | "@jumbotron-heading-color": "inherit", 218 | "@jumbotron-font-size": "ceil((@font-size-base * 1.5))", 219 | "@jumbotron-heading-font-size": "ceil((@font-size-base * 4.5))", 220 | "@state-success-text": "#3c763d", 221 | "@state-success-bg": "#dff0d8", 222 | "@state-success-border": "darken(spin(@state-success-bg, -10), 5%)", 223 | "@state-info-text": "@text-color", 224 | "@state-info-bg": "lighten(@brand-info, 10%)", 225 | "@state-info-border": "darken(spin(@state-info-bg, -10), 7%)", 226 | "@state-warning-text": "#8a6d3b", 227 | "@state-warning-bg": "#fcf8e3", 228 | "@state-warning-border": "darken(spin(@state-warning-bg, -10), 5%)", 229 | "@state-danger-text": "#a94442", 230 | "@state-danger-bg": "#f2dede", 231 | "@state-danger-border": "darken(spin(@state-danger-bg, -10), 5%)", 232 | "@tooltip-max-width": "200px", 233 | "@tooltip-color": "#fff", 234 | "@tooltip-bg": "#000", 235 | "@tooltip-opacity": ".9", 236 | "@tooltip-arrow-width": "5px", 237 | "@tooltip-arrow-color": "@tooltip-bg", 238 | "@popover-bg": "#fff", 239 | "@popover-max-width": "276px", 240 | "@popover-border-color": "rgba(0,0,0,.2)", 241 | "@popover-fallback-border-color": "#ccc", 242 | "@popover-title-bg": "darken(@popover-bg, 3%)", 243 | "@popover-arrow-width": "10px", 244 | "@popover-arrow-color": "@popover-bg", 245 | "@popover-arrow-outer-width": "(@popover-arrow-width + 1)", 246 | "@popover-arrow-outer-color": "fadein(@popover-border-color, 5%)", 247 | "@popover-arrow-outer-fallback-color": "darken(@popover-fallback-border-color, 20%)", 248 | "@label-default-bg": "@gray-light", 249 | "@label-primary-bg": "@brand-primary", 250 | "@label-success-bg": "@brand-success", 251 | "@label-info-bg": "@brand-info", 252 | "@label-warning-bg": "@brand-warning", 253 | "@label-danger-bg": "@brand-danger", 254 | "@label-color": "#fff", 255 | "@label-link-hover-color": "#fff", 256 | "@modal-inner-padding": "15px", 257 | "@modal-title-padding": "15px", 258 | "@modal-title-line-height": "@line-height-base", 259 | "@modal-content-bg": "#fff", 260 | "@modal-content-border-color": "rgba(0,0,0,.2)", 261 | "@modal-content-fallback-border-color": "#999", 262 | "@modal-backdrop-bg": "#000", 263 | "@modal-backdrop-opacity": ".5", 264 | "@modal-header-border-color": "#e5e5e5", 265 | "@modal-footer-border-color": "@modal-header-border-color", 266 | "@modal-lg": "900px", 267 | "@modal-md": "600px", 268 | "@modal-sm": "300px", 269 | "@alert-padding": "15px", 270 | "@alert-border-radius": "@border-radius-base", 271 | "@alert-link-font-weight": "bold", 272 | "@alert-success-bg": "@state-success-bg", 273 | "@alert-success-text": "@state-success-text", 274 | "@alert-success-border": "@state-success-border", 275 | "@alert-info-bg": "@state-info-bg", 276 | "@alert-info-text": "@state-info-text", 277 | "@alert-info-border": "@state-info-border", 278 | "@alert-warning-bg": "@state-warning-bg", 279 | "@alert-warning-text": "@state-warning-text", 280 | "@alert-warning-border": "@state-warning-border", 281 | "@alert-danger-bg": "@state-danger-bg", 282 | "@alert-danger-text": "@state-danger-text", 283 | "@alert-danger-border": "@state-danger-border", 284 | "@progress-bg": "#f5f5f5", 285 | "@progress-bar-color": "#fff", 286 | "@progress-border-radius": "@border-radius-base", 287 | "@progress-bar-bg": "@brand-primary", 288 | "@progress-bar-success-bg": "@brand-success", 289 | "@progress-bar-warning-bg": "@brand-warning", 290 | "@progress-bar-danger-bg": "@brand-danger", 291 | "@progress-bar-info-bg": "@brand-info", 292 | "@list-group-bg": "#fff", 293 | "@list-group-border": "#ddd", 294 | "@list-group-border-radius": "@border-radius-base", 295 | "@list-group-hover-bg": "#f5f5f5", 296 | "@list-group-active-color": "@component-active-color", 297 | "@list-group-active-bg": "@component-active-bg", 298 | "@list-group-active-border": "@list-group-active-bg", 299 | "@list-group-active-text-color": "lighten(@list-group-active-bg, 40%)", 300 | "@list-group-disabled-color": "@gray-light", 301 | "@list-group-disabled-bg": "@gray-lighter", 302 | "@list-group-disabled-text-color": "@list-group-disabled-color", 303 | "@list-group-link-color": "#555", 304 | "@list-group-link-hover-color": "@list-group-link-color", 305 | "@list-group-link-heading-color": "#333", 306 | "@panel-bg": "#fff", 307 | "@panel-body-padding": "15px", 308 | "@panel-heading-padding": "10px 15px", 309 | "@panel-footer-padding": "@panel-heading-padding", 310 | "@panel-border-radius": "@border-radius-base", 311 | "@panel-inner-border": "#ddd", 312 | "@panel-footer-bg": "#f5f5f5", 313 | "@panel-default-text": "@gray-dark", 314 | "@panel-default-border": "#ddd", 315 | "@panel-default-heading-bg": "#f5f5f5", 316 | "@panel-primary-text": "#fff", 317 | "@panel-primary-border": "@brand-primary", 318 | "@panel-primary-heading-bg": "@brand-primary", 319 | "@panel-success-text": "@state-success-text", 320 | "@panel-success-border": "@state-success-border", 321 | "@panel-success-heading-bg": "@state-success-bg", 322 | "@panel-info-text": "@state-info-text", 323 | "@panel-info-border": "@state-info-border", 324 | "@panel-info-heading-bg": "@state-info-bg", 325 | "@panel-warning-text": "@state-warning-text", 326 | "@panel-warning-border": "@state-warning-border", 327 | "@panel-warning-heading-bg": "@state-warning-bg", 328 | "@panel-danger-text": "@state-danger-text", 329 | "@panel-danger-border": "@state-danger-border", 330 | "@panel-danger-heading-bg": "@state-danger-bg", 331 | "@thumbnail-padding": "4px", 332 | "@thumbnail-bg": "@body-bg", 333 | "@thumbnail-border": "#ddd", 334 | "@thumbnail-border-radius": "@border-radius-base", 335 | "@thumbnail-caption-color": "@text-color", 336 | "@thumbnail-caption-padding": "9px", 337 | "@well-bg": "#F2F2F2", 338 | "@well-border": "darken(@well-bg, 7%)", 339 | "@badge-color": "#fff", 340 | "@badge-link-hover-color": "#fff", 341 | "@badge-bg": "@gray-light", 342 | "@badge-active-color": "@link-color", 343 | "@badge-active-bg": "#fff", 344 | "@badge-font-weight": "bold", 345 | "@badge-line-height": "1", 346 | "@badge-border-radius": "10px", 347 | "@breadcrumb-padding-vertical": "8px", 348 | "@breadcrumb-padding-horizontal": "15px", 349 | "@breadcrumb-bg": "#f5f5f5", 350 | "@breadcrumb-color": "#ccc", 351 | "@breadcrumb-active-color": "@gray-light", 352 | "@breadcrumb-separator": "\"/\"", 353 | "@carousel-text-shadow": "0 1px 2px rgba(0,0,0,.6)", 354 | "@carousel-control-color": "#fff", 355 | "@carousel-control-width": "15%", 356 | "@carousel-control-opacity": ".5", 357 | "@carousel-control-font-size": "20px", 358 | "@carousel-indicator-active-bg": "#fff", 359 | "@carousel-indicator-border-color": "#fff", 360 | "@carousel-caption-color": "#fff", 361 | "@close-font-weight": "bold", 362 | "@close-color": "#000", 363 | "@close-text-shadow": "0 1px 0 #fff", 364 | "@code-color": "#c7254e", 365 | "@code-bg": "#f9f2f4", 366 | "@kbd-color": "#fff", 367 | "@kbd-bg": "#333", 368 | "@pre-bg": "#f5f5f5", 369 | "@pre-color": "@gray-dark", 370 | "@pre-border-color": "#ccc", 371 | "@pre-scrollable-max-height": "340px", 372 | "@component-offset-horizontal": "180px", 373 | "@text-muted": "@gray-light", 374 | "@abbr-border-color": "@gray-light", 375 | "@headings-small-color": "@gray-light", 376 | "@blockquote-small-color": "@gray-light", 377 | "@blockquote-font-size": "(@font-size-base * 1.25)", 378 | "@blockquote-border-color": "@gray-lighter", 379 | "@page-header-border-color": "@gray-lighter", 380 | "@dl-horizontal-offset": "@component-offset-horizontal", 381 | "@hr-border": "@gray-lighter" 382 | }, 383 | "css": [ 384 | "print.less", 385 | "type.less", 386 | "code.less", 387 | "grid.less", 388 | "tables.less", 389 | "forms.less", 390 | "buttons.less", 391 | "responsive-utilities.less", 392 | "glyphicons.less", 393 | "button-groups.less", 394 | "input-groups.less", 395 | "navs.less", 396 | "navbar.less", 397 | "badges.less", 398 | "jumbotron.less", 399 | "alerts.less", 400 | "panels.less", 401 | "wells.less", 402 | "component-animations.less" 403 | ], 404 | "js": [ 405 | "button.js", 406 | "affix.js", 407 | "collapse.js", 408 | "scrollspy.js", 409 | "transition.js" 410 | ], 411 | "customizerUrl": "http://getbootstrap.com/customize/?id=4883ecbae98d09844da6" 412 | } -------------------------------------------------------------------------------- /venv/seafformsite/seafform/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | ############################################################################### 3 | # seafform/views.py 4 | # 5 | # Copyright © 2015, Florian Birée 6 | # 7 | # This file is a part of seafform. 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU Affero General Public License as 11 | # published by the Free Software Foundation, either version 3 of the 12 | # License, or (at your option) any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU Affero General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU Affero General Public License 20 | # along with this program. If not, see . 21 | # 22 | ############################################################################### 23 | """Seafform views""" 24 | 25 | __author__ = "Florian Birée" 26 | __version__ = "0.2" 27 | __license__ = "AGPLv3" 28 | __copyright__ = "Copyright © 2015, Florian Birée " 29 | 30 | import os 31 | from urllib.parse import quote, unquote 32 | import itertools 33 | from django.utils.text import slugify 34 | from django.core.urlresolvers import reverse 35 | from django.contrib.auth.models import User 36 | from django.http import HttpResponseRedirect, Http404 37 | from django.shortcuts import render 38 | from django.contrib.auth.decorators import login_required 39 | from django.contrib.auth import authenticate, login, logout 40 | from django.views.decorators.csrf import csrf_exempt 41 | from django.utils import timezone 42 | from seafform.models import SeafileUser, Form 43 | from seafform.forms import LoginForm, DjForm 44 | from seafform.seafile import Seafile, AuthError, APIError 45 | from seafform.seafform import SeafForm, HEADERS_ROW 46 | from django.conf import settings 47 | 48 | def _log(request, email, password, nextview): 49 | """ Authenticate or create a new account """ 50 | seaf_root = settings.SEAFILE_ROOT 51 | user = authenticate(username=email, password=password) 52 | # if known user: 53 | if user is not None and user.is_active: 54 | login(request, user) 55 | # login, -> nexturl 56 | return HttpResponseRedirect(reverse(nextview)) 57 | elif user is not None: # not active 58 | raise AuthError 59 | else: 60 | # try to connect to seafile using credentials 61 | seaf = Seafile(seaf_root, verifycerts=settings.VERIFYCERTS) 62 | seaf.authenticate(email, password) # may raise AuthError 63 | token = seaf.token 64 | # create new user, save the token 65 | user = User.objects.create_user(email, email, password) 66 | user.save() 67 | seafuser = SeafileUser(user=user, seafroot=seaf_root, 68 | seaftoken=token) 69 | seafuser.save() 70 | # login 71 | user2 = authenticate(username=email, password=password) 72 | login(request, user2) 73 | # -> nextview 74 | return HttpResponseRedirect(reverse(nextview)) 75 | 76 | def index(request): 77 | """Main login view""" 78 | #TODO: whatif the seafile password change? 79 | # the user should with it's old password, and should be able to 80 | # enter its new password and resync 81 | justlogout = False 82 | autherror = False 83 | 84 | # if authenticated, redirect to /private and no public forms 85 | if not settings.ALLOW_PUBLIC and request.user.is_authenticated(): 86 | return HttpResponseRedirect(reverse('private')) 87 | 88 | # if this is a POST request we need to process the form data 89 | if request.method == 'POST': 90 | # create a form instance and populate it with data from the request: 91 | form = LoginForm(request.POST) 92 | # check whether it's valid: 93 | if form.is_valid(): 94 | 95 | email = form.cleaned_data['email'] 96 | password = form.cleaned_data['password'] 97 | 98 | nextstep = ( 99 | 'index' 100 | if (settings.ALLOW_PUBLIC and settings.PUBLIC_NEED_AUTH) 101 | else 'private' 102 | ) 103 | 104 | try: 105 | return _log(request, email, password, nextstep) 106 | except AuthError: 107 | autherror = True 108 | 109 | # if a GET (or any other method) we'll create a blank form 110 | else: 111 | form = LoginForm() 112 | 113 | if 'action' in request.GET: 114 | justlogout = (request.GET['action'] == 'logout') 115 | 116 | return render(request, 'seafform/index.html', { 117 | 'loginform': form, 118 | 'autherror': autherror, 119 | 'justlogout': justlogout, 120 | 'seaf_root': settings.SEAFILE_ROOT, 121 | 'allow_public': settings.ALLOW_PUBLIC, 122 | 'public_needauth': settings.PUBLIC_NEED_AUTH, 123 | 'authenticated': request.user.is_authenticated(), 124 | 'public_forms': Form.objects.filter(public=True).\ 125 | order_by('-creation_datetime'), 126 | 'show_public': ( 127 | settings.ALLOW_PUBLIC and ( 128 | request.user.is_authenticated() 129 | or 130 | not settings.PUBLIC_NEED_AUTH 131 | )), 132 | }) 133 | 134 | @login_required(login_url='index') 135 | def private(request): 136 | """Home of private pages""" 137 | newform = None 138 | delformtitle = None 139 | deleted = None 140 | if request.method == 'POST': #delete 141 | deleteid = request.POST.get('deleteid', '') 142 | try: 143 | tobedeleted = Form.objects.get(formid=deleteid, owner=request.user) 144 | except Form.DoesNotExist: 145 | # do nothing 146 | pass 147 | else: 148 | # delete, redirect plus message 149 | deleted = tobedeleted.title 150 | tobedeleted.delete() 151 | 152 | if 'newform' in request.GET: 153 | try: 154 | newform = Form.objects.get(formid=request.GET['newform'], 155 | owner=request.user) 156 | except Form.DoesNotExist: 157 | pass 158 | 159 | return render(request, 'seafform/private.html', { 160 | 'user': request.user, 161 | 'forms': None, 162 | 'tplurl': settings.TPL_URL, 163 | 'forms': Form.objects.filter(owner=request.user).\ 164 | order_by('-creation_datetime'), 165 | 'newform': newform, 166 | 'deleted': deleted, 167 | 'allow_public': settings.ALLOW_PUBLIC, 168 | }) 169 | 170 | def logout_view(request): 171 | """Just… log out""" 172 | logout(request) 173 | # Redirect to a success page. 174 | return HttpResponseRedirect(reverse('index') + '?action=logout') 175 | 176 | @login_required(login_url='index') 177 | def new(request): 178 | """Create a new form""" 179 | # if this is a POST request we need to process the form data 180 | if request.method == 'POST': 181 | path = unquote(request.POST.get('path', '')) 182 | #print('new/path=' + path) 183 | if path.endswith('.ods'): 184 | if settings.LOCAL: 185 | filepath = os.path.join(settings.LOCAL_ROOT, path.lstrip('/')) 186 | seaf = None 187 | repoid = 'LOCAL' 188 | reponame = 'LOCAL' 189 | else: 190 | # Connect to Seafile 191 | seafu = request.user.seafileuser 192 | seaf = Seafile(seafu.seafroot, verifycerts=settings.VERIFYCERTS) 193 | seaf.authenticate(request.user.email, token=seafu.seaftoken, validate=False) 194 | # retreive path info 195 | parsed_path = parse(path) 196 | filepath = parsed_path['path'] 197 | repoid = repo_id_from_name(seaf, parsed_path['repo_name']) 198 | reponame = parsed_path['repo_name'] 199 | # load the form 200 | seafform = SeafForm(filepath, seaf, repoid) 201 | seafform.load() 202 | # create the slug/formid 203 | max_length = Form._meta.get_field('formid').max_length 204 | formid = orig = slugify(seafform.title)[:max_length] 205 | 206 | for x in itertools.count(1): 207 | if not Form.objects.filter(formid=formid).exists(): 208 | break 209 | # Truncate the original slug dynamically. Minus 1 for the hyphen. 210 | formid = "%s-%d" % (orig[:max_length - len(str(x)) - 1], x) 211 | 212 | # add the new form 213 | newform = Form( 214 | owner = request.user, 215 | filepath = filepath, 216 | repoid = repoid, 217 | reponame = reponame, 218 | formid = formid, 219 | title = seafform.title, 220 | creation_datetime = timezone.now(), 221 | description = seafform.description, 222 | public = seafform.public, 223 | ) 224 | newform.save() 225 | # Redirect + message 226 | return HttpResponseRedirect(reverse('private') + '?newform=' + formid) 227 | 228 | return render(request, 'seafform/new.html', { 229 | 'user': request.user, 230 | 'allow_public': settings.ALLOW_PUBLIC, 231 | }) 232 | 233 | # utility function 234 | def parse(seafpath): 235 | """Return a seafile path under the form : 236 | { 237 | 'repo_name': 238 | 'path' 239 | } 240 | the root of all libraries has repo_id == None 241 | """ 242 | seafpath = seafpath.strip('/').split('/') 243 | if seafpath == ['']: 244 | # root 245 | return {'repo_name': None, 'repo_id': None, 'path': None} 246 | else: 247 | return { 248 | 'repo_name': seafpath[0], 249 | 'path': '/' + '/'.join(seafpath[1:]) 250 | } 251 | 252 | def repo_id_from_name(seaf, repo_name): 253 | """Return the repo_id from a repo_name""" 254 | repo_list = seaf.list_repos() 255 | for repo in repo_list: 256 | if repo['name'] == repo_name: 257 | return repo['id'] 258 | return None 259 | 260 | @csrf_exempt # the javascript lib user POST, but no data changes here 261 | @login_required(login_url='index') 262 | def lsdir(request): 263 | """Return the list of files in a Seafile directory""" 264 | if request.method == 'POST': 265 | # dirty hack 266 | path = unquote(unquote(request.POST['dir'], encoding='latin9')) 267 | if settings.LOCAL: 268 | abspath = settings.LOCAL_ROOT.rstrip('/') + path 269 | result = [ 270 | { 271 | 'name': name, 272 | 'type': ( 273 | 'dir' if os.path.isdir(os.path.join(abspath, name)) 274 | else 'file'), 275 | 'path': ( 276 | os.path.join(abspath, name)[len(settings.LOCAL_ROOT.rstrip('/')):] + 277 | ('/' if os.path.isdir(os.path.join(abspath, name)) else '') 278 | ) 279 | } for name in os.listdir(abspath) 280 | if (os.path.isdir(os.path.join(abspath, name)) 281 | or name.endswith('.ods')) 282 | ] 283 | else: 284 | # Connect to Seafile 285 | seafu = request.user.seafileuser 286 | seaf = Seafile(seafu.seafroot, verifycerts=settings.VERIFYCERTS) 287 | seaf.authenticate(request.user.email, token=seafu.seaftoken, validate=False) 288 | # list the directory 289 | parsed_path = parse(path) 290 | # root of all libraries 291 | if parsed_path['repo_name'] is None: 292 | repo_list = seaf.list_repos() 293 | result = [ 294 | { 295 | 'name': repo['name'], 296 | 'type': repo['type'], 297 | 'path': quote('/%s/' % repo['name']), 298 | } for repo in repo_list 299 | ] 300 | else: 301 | repo_name, dirpath = parsed_path['repo_name'], parsed_path['path'] 302 | repo_id = repo_id_from_name(seaf, repo_name) 303 | ls = seaf.list_dir(repo_id, dirpath) 304 | result = [ 305 | { 306 | 'name': node['name'], 307 | 'type': node['type'], 308 | 'path': quote( ( 309 | path.rstrip('/') + '/' + node['name'] + 310 | ('/' if node['type'] == 'dir' else '') ) 311 | ) 312 | } for node in ls 313 | if (node['type'] == 'dir' or node['name'].endswith('.ods')) 314 | ] 315 | return render(request, 'seafform/lsdir.html', {'result': result}) 316 | raise Http404("Bad request method") 317 | 318 | def _update_form_attr(dbform, seafform): 319 | """Update db attributes from ODS file""" 320 | needupdate = False 321 | if seafform.title != dbform.title: 322 | dbform.title = seafform.title 323 | needupdate = True 324 | if seafform.public != dbform.public: 325 | dbform.public = seafform.public 326 | needupdate = True 327 | if needupdate: 328 | dbform.save() 329 | 330 | def formview(request, formid): 331 | """Display a public form""" 332 | justaddedrow = None 333 | # get the form object 334 | try: 335 | form = Form.objects.get(formid=formid) 336 | except Form.DoesNotExist: 337 | raise Http404 338 | # get the seafform object (Seafile connexion) 339 | if settings.LOCAL: 340 | seaf = None 341 | else: 342 | seafu = form.owner.seafileuser 343 | seaf = Seafile(seafu.seafroot, verifycerts=settings.VERIFYCERTS) 344 | seaf.authenticate(form.owner.email, token=seafu.seaftoken, validate=False) 345 | seafform = SeafForm(form.filepath, seaf, form.repoid) 346 | try: 347 | seafform.load() 348 | except APIError: 349 | raise Http404 350 | 351 | _update_form_attr(form, seafform) 352 | 353 | # build the corresponding DjForm() 354 | if request.method == 'POST': 355 | results = False 356 | # results management 357 | djform = DjForm(request.POST, fieldlist=seafform.fields) 358 | if djform.is_valid(): 359 | # check if we replace a row 360 | if seafform.edit and djform.cleaned_data['rowid'] != 'newrow': 361 | replace_row = int(djform.cleaned_data['rowid']) 362 | justaddedrow = replace_row - HEADERS_ROW 363 | else: 364 | replace_row = None 365 | justaddedrow = seafform._first_empty_row - HEADERS_ROW 366 | # save data 367 | seafform.post(djform.cleaned_data, replace_row) 368 | 369 | # if valid form and form and not edit: 370 | if seafform.view_as == 'form' and not seafform.edit: 371 | # redirect to thanks 372 | return HttpResponseRedirect(reverse('thanks',args=(formid,))) 373 | elif seafform.view_as == 'form': 374 | return HttpResponseRedirect( 375 | reverse('form',args=(formid,)) + '?results' 376 | ) 377 | #results = True # redirect to table 378 | 379 | # clean fields 380 | djform = DjForm(fieldlist=seafform.fields) 381 | else: 382 | djform = DjForm(fieldlist=seafform.fields) 383 | results = ('results' in request.GET) 384 | 385 | if seafform.view_as == 'form' and not results: 386 | # form view 387 | return render(request, 'seafform/form_as_form.html', { 388 | 'seafform': seafform, 389 | 'modelform': form, 390 | 'djform': djform, 391 | }) 392 | 393 | elif seafform.view_as == 'table' or (results and seafform.edit): 394 | # hightlight first column if static field 395 | first_is_static = (seafform.fields[0].ident == 'static') 396 | # compute some results 397 | max_chk = 0 398 | computations = [] 399 | for colid, field in enumerate(seafform.fields): 400 | # if check boxe, number of checks 401 | if field.ident.startswith('check'): 402 | res = sum(row[colid] for row in seafform.data if row[colid]) 403 | max_chk = max(max_chk, res) 404 | computations.append(res) 405 | # if number, the sum 406 | elif field.ident == 'number': 407 | computations.append(sum( 408 | row[colid] for row in seafform.data if row[colid] 409 | )) 410 | else: 411 | computations.append(None) 412 | # columns with a star 413 | max_chk_column = [] 414 | for colid, field in enumerate(seafform.fields): 415 | if field.ident.startswith('check') and computations[colid] == max_chk: 416 | max_chk_column.append(colid) 417 | 418 | # table view 419 | return render(request, 'seafform/form_as_table.html', { 420 | 'seafform': seafform, 421 | 'modelform': form, 422 | 'djform': djform, 423 | 'justaddedrow': justaddedrow, 424 | 'first_is_static': first_is_static, 425 | 'computations': computations, 426 | 'max_chk_column': max_chk_column, 427 | }) 428 | 429 | raise Http404 430 | 431 | def formrowedit(request, formid, rowid): 432 | """Generate a form to edit rowid in formid""" 433 | rowid = int(rowid) + HEADERS_ROW 434 | # get the form object 435 | try: 436 | form = Form.objects.get(formid=formid) 437 | except Form.DoesNotExist: 438 | raise Http404 439 | # get the seafform object (Seafile connexion) 440 | if settings.LOCAL: 441 | seaf = None 442 | else: 443 | seafu = form.owner.seafileuser 444 | seaf = Seafile(seafu.seafroot, verifycerts=settings.VERIFYCERTS) 445 | seaf.authenticate(form.owner.email, token=seafu.seaftoken, validate=False) 446 | seafform = SeafForm(form.filepath, seaf, form.repoid) 447 | seafform.load() 448 | # create the django form for a specific row 449 | initials = seafform.get_values_from_data(rowid) 450 | initials['rowid'] = rowid 451 | djform = DjForm( 452 | initials, 453 | fieldlist=seafform.fields 454 | ) 455 | return render(request, 'seafform/rowedit.html', { 456 | 'seafform': seafform, 457 | 'djform': djform, 458 | 'modelform': form, 459 | 'rowid': rowid - HEADERS_ROW, 460 | }) 461 | 462 | def thanks(request, formid): 463 | """Display the Thanks page""" 464 | # get the form object 465 | try: 466 | form = Form.objects.get(formid=formid) 467 | except Form.DoesNotExist: 468 | raise Http404 469 | return render(request, 'seafform/thanks.html', { 470 | 'seafform': form, 471 | }) 472 | 473 | -------------------------------------------------------------------------------- /venv/seafformsite/seafform/static/bootstrap/css/bootstrap-theme.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.5 (http://getbootstrap.com) 3 | * Copyright 2011-2015 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | 7 | /*! 8 | * Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=4883ecbae98d09844da6) 9 | * Config saved to config.json and https://gist.github.com/4883ecbae98d09844da6 10 | *//*! 11 | * Bootstrap v3.3.5 (http://getbootstrap.com) 12 | * Copyright 2011-2015 Twitter, Inc. 13 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 14 | */.btn-default,.btn-primary,.btn-success,.btn-info,.btn-warning,.btn-danger{text-shadow:0 -1px 0 rgba(0,0,0,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 1px rgba(0,0,0,0.075)}.btn-default:active,.btn-primary:active,.btn-success:active,.btn-info:active,.btn-warning:active,.btn-danger:active,.btn-default.active,.btn-primary.active,.btn-success.active,.btn-info.active,.btn-warning.active,.btn-danger.active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn-default.disabled,.btn-primary.disabled,.btn-success.disabled,.btn-info.disabled,.btn-warning.disabled,.btn-danger.disabled,.btn-default[disabled],.btn-primary[disabled],.btn-success[disabled],.btn-info[disabled],.btn-warning[disabled],.btn-danger[disabled],fieldset[disabled] .btn-default,fieldset[disabled] .btn-primary,fieldset[disabled] .btn-success,fieldset[disabled] .btn-info,fieldset[disabled] .btn-warning,fieldset[disabled] .btn-danger{-webkit-box-shadow:none;box-shadow:none}.btn-default .badge,.btn-primary .badge,.btn-success .badge,.btn-info .badge,.btn-warning .badge,.btn-danger .badge{text-shadow:none}.btn:active,.btn.active{background-image:none}.btn-default{background-image:-webkit-linear-gradient(top, #fff 0, #e0e0e0 100%);background-image:-o-linear-gradient(top, #fff 0, #e0e0e0 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #fff), to(#e0e0e0));background-image:linear-gradient(to bottom, #fff 0, #e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#dbdbdb;text-shadow:0 1px 0 #fff;border-color:#ccc}.btn-default:hover,.btn-default:focus{background-color:#e0e0e0;background-position:0 -15px}.btn-default:active,.btn-default.active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top, #fd700f 0, #cd5502 100%);background-image:-o-linear-gradient(top, #fd700f 0, #cd5502 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #fd700f), to(#cd5502));background-image:linear-gradient(to bottom, #fd700f 0, #cd5502 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffd700f', endColorstr='#ffcd5502', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#c35002}.btn-primary:hover,.btn-primary:focus{background-color:#cd5502;background-position:0 -15px}.btn-primary:active,.btn-primary.active{background-color:#cd5502;border-color:#c35002}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:#cd5502;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top, #5cb85c 0, #419641 100%);background-image:-o-linear-gradient(top, #5cb85c 0, #419641 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #5cb85c), to(#419641));background-image:linear-gradient(to bottom, #5cb85c 0, #419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:hover,.btn-success:focus{background-color:#419641;background-position:0 -15px}.btn-success:active,.btn-success.active{background-color:#419641;border-color:#3e8f3e}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top, #feac74 0, #fe8837 100%);background-image:-o-linear-gradient(top, #feac74 0, #fe8837 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #feac74), to(#fe8837));background-image:linear-gradient(to bottom, #feac74 0, #fe8837 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffeac74', endColorstr='#fffe8837', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#fd822d}.btn-info:hover,.btn-info:focus{background-color:#fe8837;background-position:0 -15px}.btn-info:active,.btn-info.active{background-color:#fe8837;border-color:#fd822d}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#fe8837;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top, #f0ad4e 0, #eb9316 100%);background-image:-o-linear-gradient(top, #f0ad4e 0, #eb9316 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #f0ad4e), to(#eb9316));background-image:linear-gradient(to bottom, #f0ad4e 0, #eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:hover,.btn-warning:focus{background-color:#eb9316;background-position:0 -15px}.btn-warning:active,.btn-warning.active{background-color:#eb9316;border-color:#e38d13}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top, #d9534f 0, #c12e2a 100%);background-image:-o-linear-gradient(top, #d9534f 0, #c12e2a 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #d9534f), to(#c12e2a));background-image:linear-gradient(to bottom, #d9534f 0, #c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:hover,.btn-danger:focus{background-color:#c12e2a;background-position:0 -15px}.btn-danger:active,.btn-danger.active{background-color:#c12e2a;border-color:#b92c28}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#c12e2a;background-image:none}.thumbnail,.img-thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{background-image:-webkit-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);background-image:-o-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #f5f5f5), to(#e8e8e8));background-image:linear-gradient(to bottom, #f5f5f5 0, #e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-color:#e8e8e8}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-image:-webkit-linear-gradient(top, #fd700f 0, #f06302 100%);background-image:-o-linear-gradient(top, #fd700f 0, #f06302 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #fd700f), to(#f06302));background-image:linear-gradient(to bottom, #fd700f 0, #f06302 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffd700f', endColorstr='#fff06302', GradientType=0);background-color:#f06302}.navbar-default{background-image:-webkit-linear-gradient(top, #fff 0, #f8f8f8 100%);background-image:-o-linear-gradient(top, #fff 0, #f8f8f8 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #fff), to(#f8f8f8));background-image:linear-gradient(to bottom, #fff 0, #f8f8f8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075);box-shadow:inset 0 1px 0 rgba(255,255,255,0.15),0 1px 5px rgba(0,0,0,0.075)}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top, #dbdbdb 0, #e2e2e2 100%);background-image:-o-linear-gradient(top, #dbdbdb 0, #e2e2e2 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #dbdbdb), to(#e2e2e2));background-image:linear-gradient(to bottom, #dbdbdb 0, #e2e2e2 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,0.075);box-shadow:inset 0 3px 9px rgba(0,0,0,0.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,0.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top, #3c3c3c 0, #222 100%);background-image:-o-linear-gradient(top, #3c3c3c 0, #222 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #3c3c3c), to(#222));background-image:linear-gradient(to bottom, #3c3c3c 0, #222 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled = false);border-radius:4px}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top, #080808 0, #0f0f0f 100%);background-image:-o-linear-gradient(top, #080808 0, #0f0f0f 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #080808), to(#0f0f0f));background-image:linear-gradient(to bottom, #080808 0, #0f0f0f 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,0.25);box-shadow:inset 0 3px 9px rgba(0,0,0,0.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,0.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}@media (max-width:767px){.navbar .navbar-nav .open .dropdown-menu>.active>a,.navbar .navbar-nav .open .dropdown-menu>.active>a:hover,.navbar .navbar-nav .open .dropdown-menu>.active>a:focus{color:#fff;background-image:-webkit-linear-gradient(top, #fd700f 0, #f06302 100%);background-image:-o-linear-gradient(top, #fd700f 0, #f06302 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #fd700f), to(#f06302));background-image:linear-gradient(to bottom, #fd700f 0, #f06302 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffd700f', endColorstr='#fff06302', GradientType=0)}}.alert{text-shadow:0 1px 0 rgba(255,255,255,0.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05);box-shadow:inset 0 1px 0 rgba(255,255,255,0.25),0 1px 2px rgba(0,0,0,0.05)}.alert-success{background-image:-webkit-linear-gradient(top, #dff0d8 0, #c8e5bc 100%);background-image:-o-linear-gradient(top, #dff0d8 0, #c8e5bc 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #dff0d8), to(#c8e5bc));background-image:linear-gradient(to bottom, #dff0d8 0, #c8e5bc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top, #fecaa7 0, #feb481 100%);background-image:-o-linear-gradient(top, #fecaa7 0, #feb481 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #fecaa7), to(#feb481));background-image:linear-gradient(to bottom, #fecaa7 0, #feb481 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffecaa7', endColorstr='#fffeb481', GradientType=0);border-color:#fe9d5b}.alert-warning{background-image:-webkit-linear-gradient(top, #fcf8e3 0, #f8efc0 100%);background-image:-o-linear-gradient(top, #fcf8e3 0, #f8efc0 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #fcf8e3), to(#f8efc0));background-image:linear-gradient(to bottom, #fcf8e3 0, #f8efc0 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top, #f2dede 0, #e7c3c3 100%);background-image:-o-linear-gradient(top, #f2dede 0, #e7c3c3 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #f2dede), to(#e7c3c3));background-image:linear-gradient(to bottom, #f2dede 0, #e7c3c3 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top, #ebebeb 0, #f5f5f5 100%);background-image:-o-linear-gradient(top, #ebebeb 0, #f5f5f5 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #ebebeb), to(#f5f5f5));background-image:linear-gradient(to bottom, #ebebeb 0, #f5f5f5 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0)}.progress-bar{background-image:-webkit-linear-gradient(top, #fd700f 0, #d75902 100%);background-image:-o-linear-gradient(top, #fd700f 0, #d75902 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #fd700f), to(#d75902));background-image:linear-gradient(to bottom, #fd700f 0, #d75902 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffd700f', endColorstr='#ffd75902', GradientType=0)}.progress-bar-success{background-image:-webkit-linear-gradient(top, #5cb85c 0, #449d44 100%);background-image:-o-linear-gradient(top, #5cb85c 0, #449d44 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #5cb85c), to(#449d44));background-image:linear-gradient(to bottom, #5cb85c 0, #449d44 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0)}.progress-bar-info{background-image:-webkit-linear-gradient(top, #feac74 0, #fe8e41 100%);background-image:-o-linear-gradient(top, #feac74 0, #fe8e41 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #feac74), to(#fe8e41));background-image:linear-gradient(to bottom, #feac74 0, #fe8e41 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffeac74', endColorstr='#fffe8e41', GradientType=0)}.progress-bar-warning{background-image:-webkit-linear-gradient(top, #f0ad4e 0, #ec971f 100%);background-image:-o-linear-gradient(top, #f0ad4e 0, #ec971f 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #f0ad4e), to(#ec971f));background-image:linear-gradient(to bottom, #f0ad4e 0, #ec971f 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0)}.progress-bar-danger{background-image:-webkit-linear-gradient(top, #d9534f 0, #c9302c 100%);background-image:-o-linear-gradient(top, #d9534f 0, #c9302c 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #d9534f), to(#c9302c));background-image:linear-gradient(to bottom, #d9534f 0, #c9302c 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0)}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:-o-linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent);background-image:linear-gradient(45deg, rgba(255,255,255,0.15) 25%, transparent 25%, transparent 50%, rgba(255,255,255,0.15) 50%, rgba(255,255,255,0.15) 75%, transparent 75%, transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.075);box-shadow:0 1px 2px rgba(0,0,0,0.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #d75902;background-image:-webkit-linear-gradient(top, #fd700f 0, #e45e02 100%);background-image:-o-linear-gradient(top, #fd700f 0, #e45e02 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #fd700f), to(#e45e02));background-image:linear-gradient(to bottom, #fd700f 0, #e45e02 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffd700f', endColorstr='#ffe45e02', GradientType=0);border-color:#e45e02}.list-group-item.active .badge,.list-group-item.active:hover .badge,.list-group-item.active:focus .badge{text-shadow:none}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,0.05);box-shadow:0 1px 2px rgba(0,0,0,0.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);background-image:-o-linear-gradient(top, #f5f5f5 0, #e8e8e8 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #f5f5f5), to(#e8e8e8));background-image:linear-gradient(to bottom, #f5f5f5 0, #e8e8e8 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0)}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top, #fd700f 0, #f06302 100%);background-image:-o-linear-gradient(top, #fd700f 0, #f06302 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #fd700f), to(#f06302));background-image:linear-gradient(to bottom, #fd700f 0, #f06302 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffd700f', endColorstr='#fff06302', GradientType=0)}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top, #dff0d8 0, #d0e9c6 100%);background-image:-o-linear-gradient(top, #dff0d8 0, #d0e9c6 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #dff0d8), to(#d0e9c6));background-image:linear-gradient(to bottom, #dff0d8 0, #d0e9c6 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0)}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top, #fecaa7 0, #febb8d 100%);background-image:-o-linear-gradient(top, #fecaa7 0, #febb8d 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #fecaa7), to(#febb8d));background-image:linear-gradient(to bottom, #fecaa7 0, #febb8d 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffecaa7', endColorstr='#fffebb8d', GradientType=0)}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top, #fcf8e3 0, #faf2cc 100%);background-image:-o-linear-gradient(top, #fcf8e3 0, #faf2cc 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #fcf8e3), to(#faf2cc));background-image:linear-gradient(to bottom, #fcf8e3 0, #faf2cc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0)}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top, #f2dede 0, #ebcccc 100%);background-image:-o-linear-gradient(top, #f2dede 0, #ebcccc 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #f2dede), to(#ebcccc));background-image:linear-gradient(to bottom, #f2dede 0, #ebcccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0)}.well{background-image:-webkit-linear-gradient(top, #e5e5e5 0, #f2f2f2 100%);background-image:-o-linear-gradient(top, #e5e5e5 0, #f2f2f2 100%);background-image:-webkit-gradient(linear, left top, left bottom, color-stop(0, #e5e5e5), to(#f2f2f2));background-image:linear-gradient(to bottom, #e5e5e5 0, #f2f2f2 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe5e5e5', endColorstr='#fff2f2f2', GradientType=0);border-color:#d9d9d9;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1);box-shadow:inset 0 1px 3px rgba(0,0,0,0.05),0 1px 0 rgba(255,255,255,0.1)} --------------------------------------------------------------------------------