Compare commits

...

1213 Commits
pw ... main

Author SHA1 Message Date
Frédéric Péters 283927d8e9 pfwb: remove videos navigation entry (#90056)
gitea/docbow/pipeline/head This commit looks good Details
2024-04-26 08:39:10 +02:00
Emmanuel Cazenave ee9f9c3396 sendmail: grag recipient from environ (#88821)
gitea/docbow/pipeline/head This commit looks good Details
Alse grab sender from environ (only in pfwb sendmail).
2024-03-28 14:49:48 +01:00
Emmanuel Cazenave 42e5b05fb4 setup: compute pep440 compliant dirty version number (#81731)
gitea/docbow/pipeline/head This commit looks good Details
2024-03-28 14:06:29 +01:00
Emmanuel Cazenave 6357511ad2 uwsgi: set DJANGO_SETTINGS_MODULE in environment (#87539)
gitea/docbow/pipeline/head This commit looks good Details
2024-02-27 17:55:56 +01:00
Emmanuel Cazenave 8ba4e8a8f4 misc: force lasso messages to be considered as debug level (#74014)
gitea/docbow/pipeline/head This commit looks good Details
2024-02-27 16:20:37 +01:00
Emmanuel Cazenave 77cc736a18 misc: move bs4 dependency so setup.py (#87483)
gitea/docbow/pipeline/head This commit looks good Details
2024-02-27 15:40:35 +01:00
Benjamin Dauvergne dcb24b4d19 admin: prevent too big join by get_search_results (#80932)
gitea/docbow/pipeline/head This commit looks good Details
2024-02-27 14:05:40 +01:00
Benjamin Dauvergne 28a3800d4d tests: add non regression test on search filter SQL query (#80932) 2024-02-27 14:05:40 +01:00
Emmanuel Cazenave 2c380cca2f misc: include djdt urls (#87524)
gitea/docbow/pipeline/head This commit looks good Details
2024-02-27 12:52:36 +01:00
Emmanuel Cazenave 186e993371 misc: support sqlalchemy < 2 (#87473)
gitea/docbow/pipeline/head This commit looks good Details
2024-02-26 15:56:48 +01:00
Emmanuel Cazenave de2aa7299c misc: delete tools directory (#87464)
gitea/docbow/pipeline/head This commit looks good Details
2024-02-26 14:58:50 +01:00
Emmanuel Cazenave 9d0672f2be setup: do not include gunicorn (#87460)
gitea/docbow/pipeline/head This commit looks good Details
2024-02-26 14:18:22 +01:00
Emmanuel Cazenave 3a67533c54 debian: delete init (#87462)
gitea/docbow/pipeline/head This commit looks good Details
2024-02-26 12:10:02 +01:00
Benjamin Dauvergne e4362f551b ci: add pre-commit changes to .git-blame-ignore-revs·(#86370)
gitea/docbow/pipeline/head This commit looks good Details
2024-02-01 11:26:26 +01:00
Benjamin Dauvergne 98dccb4964 ci: fix remaining ruff warnings (#86370)
gitea/docbow/pipeline/head This commit looks good Details
2024-01-31 19:05:16 +01:00
Benjamin Dauvergne 229479409e ci: apply pre-commit hooks (#86370) 2024-01-31 18:49:23 +01:00
Benjamin Dauvergne f98e0560da ci: update tox.ini & precommit hooks & compatibility py311 (#86370) 2024-01-31 18:49:10 +01:00
Emmanuel Cazenave f8934ee59c tox: don't test against django 2.2 (#84191)
gitea/docbow/pipeline/head There was a failure building this commit Details
2023-12-04 14:48:19 +01:00
Emmanuel Cazenave 024ab3a215 misc: mark a string for translation (#82756)
gitea/docbow/pipeline/head This commit looks good Details
2023-12-01 17:28:39 +01:00
Thomas NOËL 888054a5fe debian: add back memory-report to uwsgi default configuration (#80451)
gitea/docbow/pipeline/head This commit looks good Details
2023-11-13 11:33:20 +01:00
Thomas NOËL f4e981f3fe debian: add uwsgi/docbow SyslogIdentifier in service (#82977)
gitea/docbow/pipeline/head This commit looks good Details
2023-10-31 13:19:11 +01:00
Valentin Deniaud 7511e7c531 misc: update git-blame-ignore-revs to ignore quote changes (#79788)
gitea/docbow/pipeline/head This commit looks good Details
2023-08-16 11:52:29 +02:00
Valentin Deniaud 2489ed708e misc: apply double-quote-string-fixer (#79788) 2023-08-16 11:52:29 +02:00
Valentin Deniaud 02efc0cd33 misc: add pre commit hook to force single quotes (#79788) 2023-08-16 11:52:29 +02:00
Thomas NOËL 9c6f4e27ad debian: remove memory-report from uwsgi default configuration (#79890)
gitea/docbow/pipeline/head This commit looks good Details
2023-07-20 17:59:14 +02:00
Frédéric Péters 86b49c5ce8 ci: build deb package for bookworm (#78968)
gitea/docbow/pipeline/head This commit looks good Details
2023-06-23 17:29:12 +02:00
Frédéric Péters 7e10f32655 ci: stop building buster packages (#78451)
gitea/docbow/pipeline/head This commit looks good Details
2023-06-13 15:21:36 +02:00
Frédéric Péters fd3a3009db misc: allow bigger list names (#78439)
gitea/docbow/pipeline/head There was a failure building this commit Details
2023-06-13 13:55:04 +02:00
Emmanuel Cazenave 7de24a45fb add django-tables2 static files (#44600)
gitea/docbow/pipeline/head This commit looks good Details
2023-03-21 11:52:10 +01:00
Frédéric Péters 103be3de3c settings: disable models.W042 check (#74833)
gitea/docbow/pipeline/head This commit looks good Details
2023-02-24 11:46:02 +01:00
Agate d10e96fe72 Prepare Jenkinsfile for Gitea migration (#74572)
gitea/docbow/pipeline/head This commit looks good Details
2023-02-20 15:04:03 +01:00
Emmanuel Cazenave 77a7bc7ab9 debian: remove django-picklefield constraint (#69143)
gitea/docbow/pipeline/head Build started... Details
2022-09-15 16:27:04 +02:00
Frédéric Péters 0b0c3f0a4a debian: remove obsolete standard error output config from systemd unit (#65101) 2022-08-02 10:02:23 +02:00
Emmanuel Cazenave 8103ec73f1 django 3.2: update dependencies and test against django 3.2
gitea/docbow/pipeline/head Build started... Details
2022-06-13 22:17:07 +02:00
Emmanuel Cazenave ebb39515f3 django 3.2: import StringIO from io (#64430) 2022-06-13 21:21:43 +02:00
Emmanuel Cazenave 8481eade60 django 3.2: use standard functools.wrap capabilities (#64430) 2022-06-13 21:21:43 +02:00
Emmanuel Cazenave 67217e48b7 django 3.2: get rid of print_function (#64430) 2022-06-13 21:21:43 +02:00
Emmanuel Cazenave 3241234ca6 django 3.2: get rid of six.print_ (#64430) 2022-06-13 21:21:43 +02:00
Emmanuel Cazenave dc9e7a0d42 django 3.2: get rid of six.text_type (#64430) 2022-06-13 21:21:43 +02:00
Emmanuel Cazenave 112c55fac4 django 3.2: get rid of six.PY3 (#64430) 2022-06-13 21:21:43 +02:00
Emmanuel Cazenave 3f335c9bee django 3.2: import url related functions from urllib (#64430) 2022-06-13 21:20:32 +02:00
Emmanuel Cazenave 8fdd44a639 django 3.2: stop using @python_2_unicode_compatible (#64430) 2022-06-09 17:57:46 +02:00
Pierre Ducroquet 7209ea81a1 sql: optimize the hand written queries (#49580)
gitea/docbow/pipeline/head Build started... Details
2022-05-23 17:42:49 +02:00
Emmanuel Cazenave f2dd0da209 pfwb: properly truncate mail subject (#64737)
gitea/docbow/pipeline/head Build started... Details
2022-05-03 15:40:04 +02:00
Frédéric Péters 38bd3f102a trivial: bump black version to 22.3.0 2022-04-03 16:52:06 +02:00
Frédéric Péters e339d91b74 misc: apply black 22.1.0 2022-03-01 19:39:36 +01:00
Frédéric Péters 35e1b919ac trivial: bump black version to 22.1.0 (#62312) 2022-03-01 19:39:36 +01:00
Nicolas Roche d954071784 settings: close local settings file (#61978) 2022-02-25 14:51:46 +01:00
Emmanuel Cazenave 0e4aa8e784 sync-tabellio: add debug output (#61005) 2022-01-24 15:14:35 +01:00
Emmanuel Cazenave 964a28b2dd jenkins: show execution context in coverage reports (#60446) 2022-01-11 16:24:17 +01:00
Frédéric Péters 690d2ff874 build: update setup.py to require at least django 2.2 2021-12-19 16:38:58 +01:00
Frédéric Péters f2f9abcfdb debian: remove obsolete dh-systemd build-dep 2021-12-12 12:04:10 +01:00
Frédéric Péters 7673fac9a8 jenkins: build packages for buster & bullseye 2021-12-12 11:39:44 +01:00
Frédéric Péters 48e437ff6d debian: use debhelper compat level 12 2021-12-12 11:39:31 +01:00
Frédéric Péters ea718ccbf1 build: bump black version 2021-11-22 22:14:54 +01:00
Emmanuel Cazenave 0eb7acf9a5 sync-tabellio: do not crash if a user to desactivate has multiple accounts (#57393) 2021-09-29 13:03:14 +02:00
Emmanuel Cazenave aa2b3dcaf6 tox: stop testing against django 1.11 (#57155) 2021-09-21 15:38:44 +02:00
Emmanuel Cazenave 3e876668a0 mellon: handle new Issuer model (#57136)
See https://dev.entrouvert.org/issues/56819.
2021-09-21 15:32:39 +02:00
Emmanuel Cazenave 8573ac08da admin: add is_staff and is_superuser in user edit view (#55452)
Also clean up user list view.
2021-07-07 14:59:33 +02:00
Frédéric Péters 0ebab963bb views: open help image files as bytes (#55102) 2021-07-07 14:17:11 +02:00
Emmanuel Cazenave 5925bb7af4 admin: expand map() result (#54673) 2021-07-07 14:15:42 +02:00
Frédéric Péters 9500209553 tox: limit psycopg2 to < 2.9 (#54925) 2021-06-17 08:50:03 +02:00
Frédéric Péters 8527fd09f6 debian: enable uwsgi memory reports (#54610) 2021-06-07 13:33:43 +02:00
Emmanuel Cazenave 02e4e35784 misc: disable admin action name system check (#53469) 2021-04-27 18:46:00 +02:00
Emmanuel Cazenave 137d9ad66f packaging: set django version upper bound to 2.2.x (#53478) 2021-04-27 17:42:34 +02:00
Emmanuel Cazenave 7e2737e61f misc: remove django-debug-toolbar (#49507) 2021-04-27 16:12:46 +02:00
Emmanuel Cazenave 24e98fdf31 jenkins: restrict sqlalchemy version (#52083) 2021-03-16 10:37:37 +01:00
Emmanuel Cazenave 3fb2971f1e extra senders: limit number by file type (#51403) 2021-02-24 14:27:29 +01:00
Emmanuel Cazenave dc5509e16e run black (#51394) 2021-02-24 11:54:53 +01:00
Emmanuel Cazenave 4e1862d3df tox: add black (via pre-commit) to tests (#51394) 2021-02-24 11:53:21 +01:00
Emmanuel Cazenave 820fe90fe6 update translation 2021-02-02 14:49:56 +01:00
Emmanuel Cazenave 216e53061a js: listen on the label (#49185) 2021-02-02 14:49:27 +01:00
Emmanuel Cazenave 37ad37270d add a verbose_name to extra_senders (#49185) 2021-02-02 14:48:04 +01:00
Emmanuel Cazenave 98733518e4 update translations 2021-02-02 11:43:54 +01:00
Emmanuel Cazenave 01f2e2c657 include foldable.js (#50750) 2021-02-01 18:03:10 +01:00
Emmanuel Cazenave 211371a7c1 template: conditionnaly display extra_senders (#50737) 2021-02-01 17:31:14 +01:00
Emmanuel Cazenave a950176a47 add extra senders capability (#49185) 2021-01-29 18:13:53 +01:00
Emmanuel Cazenave baebc42524 misc: reformat using black (#49290) 2021-01-29 18:13:53 +01:00
Emmanuel Cazenave 1e425f5f29 misc: move Meta and Media at the beginning of their outer class (#49290) 2021-01-29 18:13:52 +01:00
Emmanuel Cazenave d84145ca2c change existing migration (#49240) 2021-01-29 18:13:52 +01:00
Emmanuel Cazenave 87a5e8ac4c tox: run tests against django 2.2 (#49189) 2021-01-29 18:13:52 +01:00
Emmanuel Cazenave ca3d999afc use mark_safe instead of allow_tags (#49189) 2021-01-29 18:13:52 +01:00
Emmanuel Cazenave 26db4f6600 tests: use localized datetimes (#49189) 2021-01-29 18:13:52 +01:00
Emmanuel Cazenave e495a58638 ellipsize at model level to avoid escape clusterfuck (#49189) 2021-01-29 18:13:52 +01:00
Emmanuel Cazenave ca7a948c2b user 'set' method instead of direct assignment on many to many relationship (#49189) 2021-01-29 18:13:52 +01:00
Emmanuel Cazenave f068578330 user raw string for regexp pattern (#49189) 2021-01-29 18:13:52 +01:00
Emmanuel Cazenave f1d07f7e05 use base_manager_name instead of use_for_related_fields (#49189) 2021-01-29 18:13:52 +01:00
Emmanuel Cazenave 27a8bdb7fb widgets.render must accept a renderer argument (#49189) 2021-01-29 18:13:52 +01:00
Emmanuel Cazenave 24ee0ceb68 user is_anonymous and is_authenticated as properties (#49189) 2021-01-29 18:13:52 +01:00
Emmanuel Cazenave 9aa59cef6c accept a 'request' argument and the authentication backend (#49189) 2021-01-29 18:13:52 +01:00
Emmanuel Cazenave bb5b695f75 use contrib.auth class based views (#49189) 2021-01-29 18:13:52 +01:00
Emmanuel Cazenave d18989835b avoid deprecated invocation of 'include' (#49189) 2021-01-29 18:13:52 +01:00
Emmanuel Cazenave 0e4db71461 make the middleware compatible with MIDDLEWARE setting (#49189) 2021-01-29 18:13:52 +01:00
Emmanuel Cazenave 72109fd8dd import url functions from django.urls (#49189) 2021-01-29 18:13:52 +01:00
Emmanuel Cazenave 568d4c93d9 declare on_delete behaviour on ForeignKey and OneToOne fields (#49189) 2021-01-29 18:13:52 +01:00
Emmanuel Cazenave 40d75a7395 tests: use assert statement instead of assertEquals and assertNotEquals (#49189) 2021-01-29 18:13:52 +01:00
Emmanuel Cazenave 522f7e588a make manage.py executable (#48822) 2021-01-29 18:13:52 +01:00
Frédéric Péters b43c2556b2 build: update to use origin/main 2020-12-26 15:21:16 +01:00
Emmanuel Cazenave 5145db8747 restore raw sql queries (#48320) 2020-11-06 02:01:53 +01:00
Emmanuel Cazenave 7f0cc69a96 test: set LANGUAGE_CODE (#48320) 2020-11-06 01:54:28 +01:00
Emmanuel Cazenave 168166ea57 use restore icon in outbox trash view (#48151) 2020-10-30 13:48:34 +01:00
Emmanuel Cazenave 71fa0d992c move trash link (#47989) 2020-10-23 12:19:06 +02:00
Emmanuel Cazenave abfe4928e5 debian: disable write exception in uwsgi.ini (#47951) 2020-10-23 11:06:17 +02:00
Emmanuel Cazenave 1e8895dfdd packaging: add svg static files (#47971) 2020-10-22 17:05:12 +02:00
Christophe Siraut a0cc9a779a templates: remove whitespace in email subject (#47725) 2020-10-15 12:14:30 +02:00
Emmanuel Cazenave 703eb06ce7 translation update 2020-10-11 20:48:09 +02:00
Emmanuel Cazenave ebbec1584e use custom html to display page numbers (#47468) 2020-10-11 18:47:26 +02:00
Emmanuel Cazenave 522049a9a0 create inbox/outbox trash (#45372) 2020-10-08 12:33:32 +02:00
Emmanuel Cazenave 50619c3f8a avoid raw SQL queries (#44909) 2020-10-08 12:33:32 +02:00
Frédéric Péters 1f2564b839 tox: limit mock version for compatibility with python 3.5 2020-10-06 09:27:05 +02:00
Emmanuel Cazenave 32f23618a9 sendmail: let the email library do the decoding (#46820) 2020-10-01 12:21:27 +02:00
Emmanuel Cazenave a190bc6afc tox: remove python 2 builds (#47227) 2020-10-01 11:46:58 +02:00
Emmanuel Cazenave c332ebea7d use fromtimestamp from where it belongs (#46751) 2020-09-21 12:40:11 +02:00
Emmanuel Cazenave 2b4bad5151 stop grabbing CUSTOMIZATION from environnment (#34085) 2020-09-17 11:49:34 +02:00
Christophe Siraut 5fee328e3c views: do not encode csv/ods strings (#46654) 2020-09-15 19:01:32 +02:00
Emmanuel Cazenave c0a69c7e8c pw: try different encodings in sendmail (#46623) 2020-09-14 18:18:33 +02:00
Christophe Siraut f34d5850b1 d/control: add python3-django-mellon to recommends (#46267) 2020-09-03 09:24:17 +02:00
Emmanuel Cazenave ec37ca9909 debian: use docbow-manage script in cron (#46271) 2020-09-02 10:59:27 +02:00
Emmanuel Cazenave 12eab2a608 misc: compat with python3-magic (#46253) 2020-09-01 15:15:22 +02:00
Emmanuel Cazenave a2793266db debian: switch to python 3, uwsgi and native systemd configuration (#44410) 2020-09-01 11:57:18 +02:00
Emmanuel Cazenave 0364ad0ee5 tox: pin setuptools 2020-08-31 17:15:59 +02:00
Emmanuel Cazenave f5b6b11fc0 tox: pin SQLAlchemy-Utils (#44866) 2020-07-06 15:42:40 +02:00
Emmanuel Cazenave 4bf659da18 py3: update except syntax (#44415) 2020-06-29 14:03:22 +02:00
Emmanuel Cazenave a382734360 exclude inactive MailingList from recipients (#44085) 2020-06-17 10:46:07 +02:00
Emmanuel Cazenave a275a2eace unicodecsv: use StringIO as a class (#44147) 2020-06-17 10:43:01 +02:00
Emmanuel Cazenave 4c357fa5d4 limit django-picklefield to < 2.0.0 (#43922) 2020-06-17 10:11:49 +02:00
Emmanuel Cazenave fdeca6eec0 tests: actively close db connection (#43575) 2020-06-08 13:52:49 +02:00
Emmanuel Cazenave 88f548eabe limit django-picklefield to < 1.0.0 (#43744) 2020-06-08 13:51:07 +02:00
Emmanuel Cazenave eef45365ac remove settings safeguard (#35125) 2020-06-03 16:24:32 +02:00
Emmanuel Cazenave 51bd0086ae run tests in python 3 (#40572) 2020-06-03 15:07:27 +02:00
Emmanuel Cazenave 406bea83ca handle binary/text when using file (#40572) 2020-06-03 15:07:14 +02:00
Emmanuel Cazenave 79f8e67f78 expand some iterator (#40572) 2020-06-03 15:05:50 +02:00
Emmanuel Cazenave 3226f8b2bb grab urlopen from six (#40572) 2020-06-03 15:05:46 +02:00
Emmanuel Cazenave 805c7d825e tests: read/write email in binary mode (#40572) 2020-06-03 15:05:42 +02:00
Emmanuel Cazenave 155aa68995 tests: use force_text() on bytes before string iterpolation (#40572) 2020-06-03 15:05:38 +02:00
Emmanuel Cazenave da11a1298f tests: write file in binary mode (#40572) 2020-06-03 15:05:29 +02:00
Emmanuel Cazenave 0942b6477f tests: use explicit integer division (#40572) 2020-06-03 15:05:25 +02:00
Emmanuel Cazenave 421da3bcaa expand map() result (#40572) 2020-06-03 15:05:21 +02:00
Emmanuel Cazenave 4b8723b695 remove str.decode() calls (#40572) 2020-06-03 15:05:17 +02:00
Emmanuel Cazenave 6a3c23fb0f use resp.text instead of resp.content (#40572) 2020-06-03 15:05:12 +02:00
Emmanuel Cazenave 2836f24119 use items() instead of iteritems() (#40572) 2020-06-03 15:05:08 +02:00
Emmanuel Cazenave 898dfe95d2 remove usage of 'unicode' (#40572) 2020-06-03 15:04:52 +02:00
Emmanuel Cazenave 672fe73dce grab StringIO from six (#40572) 2020-06-03 15:01:33 +02:00
Emmanuel Cazenave 7e1c6d061a update except syntax (#40572) 2020-06-03 15:01:27 +02:00
Emmanuel Cazenave af75993e9e import _thread as thread (#40572) 2020-06-03 15:01:21 +02:00
Emmanuel Cazenave 67cce5d3b2 eventually grab BeautifulSoup from bs4 (#40572) 2020-06-03 15:01:15 +02:00
Emmanuel Cazenave 58d6c346cc grab urlparse from six (#40572) 2020-06-03 15:01:10 +02:00
Emmanuel Cazenave 2a791ce1cd use __str__() instead of __unicode__() (#40572) 2020-06-03 15:01:00 +02:00
Emmanuel Cazenave bd46309742 use absolute imports (#40572) 2020-06-03 15:00:53 +02:00
Emmanuel Cazenave 8d44c284fc use AppConfig to register signals (#40572) 2020-06-03 15:00:47 +02:00
Emmanuel Cazenave 39a961d1f9 use open() to open file (#40572) 2020-06-03 15:00:41 +02:00
Emmanuel Cazenave 04579e0784 use print as a function (#40572) 2020-06-03 15:00:35 +02:00
Emmanuel Cazenave 3a986d9669 jenkins: build default eobuilder target (#42463) 2020-06-03 12:41:23 +02:00
Emmanuel Cazenave 505d23bffb remove rfc3161 timestamping (#42352) 2020-05-04 13:59:26 +02:00
Emmanuel Cazenave 779138c4fd stop django 1.8 support (#42340) 2020-05-04 13:55:18 +02:00
Lauréline Guérin 13d7219738
settings: fix DEBUG (#41579) 2020-04-24 10:45:02 +02:00
Emmanuel Cazenave f0e771cf54 tests: mock urllib2 (#33751) 2020-04-13 18:58:42 +02:00
Emmanuel Cazenave de2560126a pfwb: use tabellio identifier during SSO (#40827) 2020-04-02 12:37:17 +02:00
Emmanuel Cazenave d7e5e44a9c sync-tabellio: use/store tabellio identifer (#40795) 2020-03-31 18:58:26 +02:00
Emmanuel Cazenave 61f4f355c5 sync-tabellio: use sqlalchemy instead of raw SQL (#40795) 2020-03-31 18:58:26 +02:00
Emmanuel Cazenave 7f204d5844 sync-tabellio: delete dead code (#40795) 2020-03-31 18:58:26 +02:00
Emmanuel Cazenave 4d6c5bbc5b sync-tabellio: use sqlalchemy to test it (#40795) 2020-03-31 18:58:26 +02:00
Emmanuel Cazenave 9fcb121ef4 add external_identifer on DocbowProfile (#40798) 2020-03-31 18:57:05 +02:00
Emmanuel Cazenave 36b66ec8fe add missing migration (#40798) 2020-03-31 18:57:05 +02:00
Emmanuel Cazenave 01c1832477 update exisiting migration (#40798) 2020-03-31 18:57:05 +02:00
Emmanuel Cazenave 3d99026ac2 make mellon optional (#41202) 2020-03-31 18:39:18 +02:00
Emmanuel Cazenave fdcb025534 tox: pass BRANCH_NAME in environ (#41010) 2020-03-26 08:38:50 +01:00
Emmanuel Cazenave fca079ed2c settings: rely on AppDirectoriesFinder to find static files (#41004) 2020-03-26 08:38:11 +01:00
Emmanuel Cazenave ef61f0d72a sync-tabellio: do not rely on environment (#41008) 2020-03-26 08:37:20 +01:00
Emmanuel Cazenave 4b0d93750b get rid of statsd (#34953) 2020-03-25 15:29:11 +01:00
Emmanuel Cazenave ea7ed2186e custom backport of CVE-2019-19844 (#40956)
https://www.djangoproject.com/weblog/2019/dec/18/security-releases/
https://security-tracker.debian.org/tracker/CVE-2019-19844
f4cff43bf9

Shoud be reverted when support of django 1:1.11.20-1~bpo9+1 is not needed any more.
The revert should maintain the usage of '_unicode_ci_compare' in
'PasswordResetFormWithLogging.clean_idenfiers'.
2020-03-24 12:50:43 +01:00
Emmanuel Cazenave ab8f6e659c test email recipient on password reset (#40956) 2020-03-24 12:49:37 +01:00
Emmanuel Cazenave 47a4edd998 debian: allow django > 1.9 (#40915) 2020-03-23 15:21:04 +01:00
Christophe Siraut 4cf439d2d9 debian: move postgresql to suggests (#40907) 2020-03-23 14:35:57 +01:00
Emmanuel Cazenave 252db71e91 jenkins: build deb package for stretch (#40389) 2020-03-23 12:28:47 +01:00
Emmanuel Cazenave 566865de6f misc: remove python-raven dependency (#40390) 2020-03-05 17:12:06 +01:00
Benjamin Dauvergne 50682cb2f2 misc: remove use of python-entrouvert (#40385) 2020-03-05 16:28:51 +01:00
Frédéric Péters 8a0dde0d25 pfwb: adjust navigation to align with portal site
(#40206)40206)40206)40206)
2020-02-26 14:01:06 +01:00
Frédéric Péters 3257dab00d pfwb: add link to videos site (#40206) 2020-02-26 13:52:08 +01:00
Emmanuel Cazenave 5bd0918b85 limit django-watson version (#40093) 2020-02-21 19:32:26 +01:00
Benjamin Dauvergne d04cf5987b misc: update merge-junit-results.py 2019-12-07 11:08:32 +01:00
Emmanuel Cazenave 444b61b7fb jenkins: use ci@entrouvert.org for notifications 2019-10-02 13:51:24 +02:00
Emmanuel Cazenave b0ab589654 create forward-docs module (#36302) 2019-09-25 12:01:21 +02:00
Emmanuel Cazenave 868d6c8a85 move some test helpers in a dedicated module (#36302) 2019-09-24 15:17:32 +02:00
Emmanuel Cazenave 2938cf5e30 include new locale directory in distribution 2019-09-23 15:27:47 +02:00
Emmanuel Cazenave 6e85db4aa5 fix message format in transaltion 2019-09-23 14:58:54 +02:00
Emmanuel Cazenave d0495769a8 update transaltions 2019-09-23 14:23:43 +02:00
Emmanuel Cazenave e9e6d438fc define verbose names for PloneFileType (#36232) 2019-09-20 11:09:31 +02:00
Emmanuel Cazenave 543b69470f merge multiple po files into one (#36232)
Merge each po file from docbow, humantime and pfwb into locale/fr/LC_MESSAGES/django.po
2019-09-19 19:44:49 +02:00
Emmanuel Cazenave 2aded15069 clean BASE_DIR setting (#36232) 2019-09-19 19:39:30 +02:00
Emmanuel Cazenave 0d8c46c208 log zip file download (#36249) 2019-09-19 10:46:46 +02:00
Emmanuel Cazenave e299fcff0a rename zip file (#36249) 2019-09-19 10:35:50 +02:00
Emmanuel Cazenave d35f5e1878 download zip archive of attached files (#36184) 2019-09-18 17:45:28 +02:00
Emmanuel Cazenave 6c89eed154 keep is_staff flag on superuser without group (#35095) 2019-09-18 11:46:11 +02:00
Emmanuel Cazenave 115b1b491b workaround 1.11 prefetch_related crash (#35674) 2019-09-16 18:41:03 +02:00
Emmanuel Cazenave 932d8f5e8d explicit redirection after document forwarding (#35674) 2019-09-16 18:40:57 +02:00
Emmanuel Cazenave 521499d074 use django-webtest client to check redirect (#35674) 2019-09-16 18:40:00 +02:00
Emmanuel Cazenave 1e86ec0c63 run tests with django 1.11 (#35674) 2019-09-16 18:34:15 +02:00
Emmanuel Cazenave 5f11056f8d use argparse in management commands (#35976) 2019-09-16 18:31:08 +02:00
Emmanuel Cazenave 5198912cc1 delegate: bypass django's session invalidation (#36160) 2019-09-16 18:17:46 +02:00
Emmanuel Cazenave 03ed3eea94 django 1.11: display actions buttons in mailbox (#36013) 2019-09-16 17:56:14 +02:00
Emmanuel Cazenave c77d487350 make FilteredSelectMultiple django 1.11 compliant (#35616)
- relax render signature
- disable html5 validation
2019-09-16 16:52:28 +02:00
Emmanuel Cazenave 86b6caf424 make file upload widgets django 1.11 compliant (#35616) 2019-09-16 16:52:28 +02:00
Emmanuel Cazenave bce10f49f6 delete dead code (#35616) 2019-09-16 16:52:28 +02:00
Emmanuel Cazenave f07bcfca78 notify: do not send mail if no recipients (#36027) 2019-09-11 19:31:59 +02:00
Emmanuel Cazenave d2e6eae288 pfwb: find users by email in sendmail command (#35965) 2019-09-10 15:35:47 +02:00
Emmanuel Cazenave 25e4bffb55 initialize ModelChoiceField with a queryset (#35743) 2019-09-06 11:47:58 +02:00
Emmanuel Cazenave c5ce2dcb90 remove custom 404 handling on favicon.icon (#35676) 2019-09-06 11:46:16 +02:00
Frédéric Péters 871bef9745 sendmail: add option to collect emails in a mbox to help debugging (#35808) 2019-09-04 15:40:58 +02:00
Frédéric Péters 4462db6bd8 sendmail: fix spelling of exit_code parameter name (#35807) 2019-09-04 11:31:36 +02:00
Frédéric Péters 46b9312182 sendmail: handle missing subject (#35803) 2019-09-04 10:37:19 +02:00
Emmanuel Cazenave c747d2ba8c settings: add back LOGOUT_URL (#35609) 2019-08-28 12:31:29 +02:00
Emmanuel Cazenave b1abeb828b pw: declare AppConfig object (#35609) 2019-08-28 12:31:04 +02:00
Emmanuel Cazenave 71882d91dd pfwb: register signals after application is marked as ready (#35609) 2019-08-28 12:29:44 +02:00
Emmanuel Cazenave df27412f59 remove django south configuration (#35609) 2019-08-28 12:29:14 +02:00
Emmanuel Cazenave 64c09b60e1 use django.forms.utils module (#35609) 2019-08-28 12:28:55 +02:00
Emmanuel Cazenave c02fd06bd1 use stdlib import_module (#35609) 2019-08-28 12:28:37 +02:00
Emmanuel Cazenave f59cc5930f mark redirect view as non permanent (#35609) 2019-08-28 12:27:27 +02:00
Emmanuel Cazenave 44114e6a52 get rid of django-autocomplete-light (#34952) 2019-08-28 12:20:53 +02:00
Emmanuel Cazenave b76a6bc61a get rid of django-cripsy dependency (#34933) 2019-08-28 12:16:46 +02:00
Emmanuel Cazenave cace429ddd use django native forms instead of django-cripsy (#34933) 2019-08-28 12:16:46 +02:00
Emmanuel Cazenave d8ff7fcf17 redirect unreferenced view (#34933)
And empty to templates that needs to exist but the rendered
response is never displayed to the user,
ony the context is used to create another response
(hackish subview implementation)
2019-08-28 12:16:46 +02:00
Emmanuel Cazenave cd1d9183dd jenkins: add support for hotfix releases 2019-08-28 12:08:54 +02:00
Christophe Siraut 21138adaf3 debian: add dh-python to build-depends (#33919) 2019-08-12 10:53:44 +02:00
Christophe Siraut 6aa08af7fe debian: remove dbconfig (#34084) 2019-08-12 10:52:29 +02:00
Christophe Siraut 72321ffdb1 pw: add updated banner (#35335) 2019-08-09 11:44:05 +02:00
Emmanuel Cazenave 6bb6cd686b support non naive datetime when timestamping documents (#35122) 2019-07-30 14:48:18 +02:00
Emmanuel Cazenave ffac503514 pw: update a template and some css (#35124) 2019-07-30 14:48:18 +02:00
Frédéric Péters 9c5a984318 pfwb: ignore noreply@pfwb.be for real 2019-07-22 12:18:13 +02:00
Emmanuel Cazenave 6a23d64555 road to 1.11: add compatiblity with django-journal 2.0.0 (#34755) 2019-07-15 17:30:11 +02:00
Emmanuel Cazenave 706b1ac182 delete unused DeletedMailBox (#34767) 2019-07-15 17:24:11 +02:00
Emmanuel Cazenave af32677238 road to 1.11 : delete custom json serializer (#34770) 2019-07-15 17:23:16 +02:00
Emmanuel Cazenave 96b0f8a939 stop using environment variables (#34305)
Except the CUSTOMIZATION variable.

Setting variables on a specific deployment is now possible in
/etc/docbow/settings.py and /etc/docbow/settings.d/*.py files.
These settings are the last being loaded and therefore take
precedence over docbow_projects.settings.

docbow_project/pw/setttings.py and docbow_project/pfwb/setttings.py are kept
as legacy documentation but are not loaded any more.
2019-07-15 15:59:45 +02:00
Emmanuel Cazenave 1fbb00c7af tests: use postgres (#34500) 2019-07-15 15:31:32 +02:00
Emmanuel Cazenave a13e8eabeb road to 1.11: use the TEMPLATES variable in settings (#34620) 2019-07-15 15:17:11 +02:00
Emmanuel Cazenave d8ce22be2b road to 1.11: use _meta.get_field method (#34612) 2019-07-15 15:13:08 +02:00
Emmanuel Cazenave 25eec07578 road to 1.11: don't load 'url' template tag (#34609) 2019-07-15 15:11:32 +02:00
Emmanuel Cazenave 4bc47bdf8e road to 1.11: get rid of django 1.8 deprecations in url declarations (#34606) 2019-07-15 15:09:44 +02:00
Frédéric Péters f539658201 pfwb: allow users to be both deputy and ministre (#34553) 2019-07-09 16:03:21 +02:00
Frédéric Péters 5d8d5f78aa pfwb: ignore noreply@pfwb.be emails (#34143) 2019-07-09 15:08:29 +02:00
Emmanuel Cazenave c7d3c3e8ed delete unused function (#334474) 2019-07-04 09:32:43 +02:00
Emmanuel Cazenave a4f9f987b2 hide private documents from guest delegate (#34515) 2019-07-04 09:29:02 +02:00
Emmanuel Cazenave a8b4c2453a test current behaviour on document access (#34515) 2019-07-04 09:29:02 +02:00
Emmanuel Cazenave 3485e21da7 revert private document fix (#34515)
Revert "hide private documents from guest delegate (#34231)"

This reverts commit a1f6cf01a2.
2019-07-04 09:29:02 +02:00
Frédéric Péters fd6bba5a4e pfwb: update tabellio synchronisation to match on email or names (#34477) 2019-07-02 18:27:02 +02:00
Emmanuel Cazenave d041bf8868 add some tests (#34362) 2019-07-02 15:31:24 +02:00
Emmanuel Cazenave 3de6a05244 move tests in tests directory (#34362)
And minor test adaptations to make it work (mainly get rid of relative imports).
2019-07-02 15:31:24 +02:00
Emmanuel Cazenave 633e997713 delete debian squeeze packaging (#34391) 2019-07-02 15:27:03 +02:00
Emmanuel Cazenave d9c81d5dce get rid of django-south dependency (#34364) 2019-07-02 15:26:21 +02:00
Emmanuel Cazenave 4d81bf927c delete south migrations (#34364) 2019-07-02 15:26:21 +02:00
Frédéric Péters be18b3d4af pfwb: add new TABELLIO_DBPORT settings (#34304) 2019-06-27 21:21:26 +02:00
Emmanuel Cazenave d26ea8a0e3 delete unused logger (#34363) 2019-06-27 11:59:27 +02:00
Emmanuel Cazenave 180ed300cf idp: return 403 when no credentials to admin (#33786) 2019-06-27 11:55:02 +02:00
Benjamin Dauvergne a1f6cf01a2 hide private documents from guest delegate (#34231) 2019-06-24 10:13:11 +02:00
Benjamin Dauvergne 748d90d7ec tests: test private flag with guest delegate (#34231) 2019-06-24 10:12:20 +02:00
Frédéric Péters 8fcf8de82c style: limit width using pixels, not ex (#33827) 2019-06-18 12:06:41 +02:00
Christophe Siraut 5702540027 doc: remove password change instructions from pfwb (#34086) 2019-06-18 10:49:31 +02:00
Frédéric Péters 6dca93db79 only serve help files from subdirectories (#34088) 2019-06-18 07:17:42 +02:00
Frédéric Péters 4c02888734 misc: don't include sms explanation text when they are disabled (#34087) 2019-06-17 22:00:13 +02:00
Frédéric Péters 04537f967b setup: do not include hash in version number (#33789?) 2019-06-17 21:46:17 +02:00
Frédéric Péters 36f6a6aa1b misc: restore compatibility with older yelp-xsl (#33789) 2019-06-17 21:18:43 +02:00
Frédéric Péters 5fccc051cd debian: force /bin/sh when running collectstatic/migrate (#34083) 2019-06-17 21:16:25 +02:00
Frédéric Péters 8af840be5e debian: use appropriate name for init script metadata (#34083) 2019-06-17 21:16:21 +02:00
Frédéric Péters 3953118d02 debian: add missing yelp-xsl dependency 2019-06-17 21:07:16 +02:00
Frédéric Péters 34dd759840 build: distribute documentation Makefile 2019-06-17 20:58:15 +02:00
Christophe Siraut a7c44c421a build and distribute documentation, use HELP_DIR variable (#33789) 2019-06-17 20:34:31 +02:00
Emmanuel Cazenave 37ed11e925 pfwb: distribute svg files (#34082) 2019-06-17 18:49:51 +02:00
Emmanuel Cazenave 0b9face5fe idp: give created user a role (#34023) 2019-06-17 14:31:37 +02:00
Frédéric Péters f77666a3d8 pfwb: add new navigation bar (#33827) 2019-06-14 12:05:26 +02:00
Emmanuel Cazenave ea156ae5dc idp: show mellon models in admin (#33800) 2019-06-14 09:36:13 +02:00
Emmanuel Cazenave e84a04edeb set some admin titles (#33793) 2019-06-14 09:23:45 +02:00
Emmanuel Cazenave 1ff5dcca1f get rid of django-grappelli (#33793) 2019-06-14 09:22:44 +02:00
Emmanuel Cazenave 66c15575f4 idp: add some tests in a mellon environment (#33621) 2019-06-14 09:05:32 +02:00
Emmanuel Cazenave 8017b31be1 idp: create custom backend (#33621) 2019-06-14 09:05:23 +02:00
Emmanuel Cazenave 2765f7dfeb idp: create and delete remote delegate user (#33621) 2019-06-14 09:05:08 +02:00
Emmanuel Cazenave b93abfe854 make delegation view atomic (#33621) 2019-06-14 09:04:18 +02:00
Emmanuel Cazenave c501565645 start django-mellon SSO compliance (#33714) 2019-06-10 11:54:16 +02:00
Emmanuel Cazenave dd5691000e create a jenkins pipeline (#21074) 2019-06-10 11:32:18 +02:00
Emmanuel Cazenave 4cf2cffb9c jenkins: use an available pdf file (#33751)
The old one does not exit any more.
2019-06-06 14:13:32 +02:00
Christophe Siraut ea0fbed91f silence sucessful cron task (#31508) 2019-03-18 16:56:34 +01:00
Christophe Siraut cebd2bfe0c tests optimizations (#24489) 2019-01-11 20:57:16 +01:00
Emmanuel Cazenave ed869ded4b use migrate command (#29534) 2019-01-07 19:20:31 +01:00
Emmanuel Cazenave 4971816ecb support multiple hyphen in delegate username (#29538) 2019-01-07 19:15:06 +01:00
Christophe Siraut 880d339084 packaging: dpkg-divert when upgrading (#29439) 2019-01-07 14:15:53 +01:00
Benjamin Dauvergne 1dced77289 views: use proper exception for model not found (fixes #24814) 2018-06-27 19:31:51 +02:00
Christophe Siraut 8b503acc19 debian: do not re-add diversion on postrm upgrade (#24490) 2018-06-13 11:40:16 +02:00
Christophe Siraut 08b2f9eeb6 debian: do not fail on preinst upgrade (#24490) 2018-06-13 11:40:10 +02:00
Christophe Siraut 683ba6da0b debian: remove diversion on purge 2018-06-12 19:43:34 +02:00
Christophe Siraut 439e6e50fe debian: do not reexecute dpkg-divert on upgrades (#24490) 2018-06-12 19:43:26 +02:00
Emmanuel Cazenave 9982a9e71a handle django-watson >= 1.2.0 (#24464) 2018-06-12 18:23:00 +02:00
Emmanuel Cazenave b45849158d adapt DelegationAuthBackend to django 1.8 (#24444) 2018-06-11 20:54:45 +02:00
Emmanuel Cazenave 840e5e5dd0 make admin notification view django 1.8 compliant (#24436) 2018-06-11 17:13:55 +02:00
Christophe Siraut 4c8dd0217f enable package compilation on jessie (#24427) 2018-06-11 13:25:07 +02:00
Emmanuel Cazenave bb8a064c3e make password reset compliant with django 1.8 (#24351) 2018-06-11 11:55:50 +02:00
Emmanuel Cazenave a3b0e0f478 drop django 1.7 support (#24379) 2018-06-08 18:40:14 +02:00
Christophe Siraut 8083ed76cd debian: provide updated /etc/magic (closes: #24346) 2018-06-07 12:03:03 +02:00
Emmanuel Cazenave ac94c578ec Revert "backport forms from django 1.8" (#24185)
This reverts commit 73b914b1ce.
2018-05-31 15:25:06 +02:00
Emmanuel Cazenave e5af7e63ee Revert "Fix PasswordResetFormWithLogging not sending emails and adapt urls and templates of django.contrib.auth for changes introduced in django 1.6 (#23342)" (#24185)
This reverts commit 51930688d8.
2018-05-31 15:24:42 +02:00
Emmanuel Cazenave 74efa01038 Revert "test password reset (#23342)" (#24185)
This reverts commit 7dcad3877f.
2018-05-31 15:24:22 +02:00
Emmanuel Cazenave 7dcad3877f test password reset (#23342)
csiraut: change field name from identifier to email

Signed-off-by: Christophe Siraut <csiraut@entrouvert.com>
2018-04-25 15:28:59 +02:00
Christophe Siraut 51930688d8 Fix PasswordResetFormWithLogging not sending emails and adapt urls and templates of django.contrib.auth for changes introduced in django 1.6 (#23342) 2018-04-25 15:28:59 +02:00
Christophe Siraut 73b914b1ce backport forms from django 1.8 2018-04-25 15:28:59 +02:00
Emmanuel Cazenave 8c40187526 absorb humantime from python-entrouvert (#23368) 2018-04-24 11:14:46 +02:00
Emmanuel Cazenave ceb98ed6d0 increase buffer size to guess MIME type (#23283)
and use a setting to control it
2018-04-19 15:23:30 +02:00
Emmanuel Cazenave 2468dd6adf change django-autocomplete-light version requirement (#23166)
depends on django-autocomplete-light<2.3.4 to satisfy django 1.8
2018-04-13 11:26:51 +02:00
Emmanuel Cazenave 5248f0430c use content_type arg for django >= 1.7 compat (#23140) 2018-04-13 11:23:39 +02:00
Emmanuel Cazenave a8726bee69 django 1.8 compatiblity
Plus additionnal changes :
 - use argparse instead of optparse in cli (recommended starting django 1.8)
 - add some tests on CLI
 - update README
2018-01-11 11:17:11 +01:00
Emmanuel Cazenave 6dd7c17c09 virtenv-Pip install working
- pin some dependencies
  - update README
  - Use of DEBUG now permits to not set SECRET_KEY (which was
    obviously the code intention but was not working)
2018-01-08 15:23:21 +01:00
Frédéric Péters 3915237844 don't include inactive users in maillinglist members (#18017) 2017-12-08 11:07:37 +01:00
Benjamin Dauvergne ce71d62d84 pfwb: desactivate dead commissions (#14910) 2017-02-08 16:03:06 +01:00
Benjamin Dauvergne e2a4dfae7b do not remove users from deputy list automatically 2017-02-08 14:57:31 +01:00
Benjamin Dauvergne d4e72f90d6 handle removal of deputies, ministres and comppol lists (fixes #14910) 2017-02-08 14:39:14 +01:00
Benjamin Dauvergne 33dc4ebdfb do not use setdefault() to set the customized Django settings module (fixes #14921) 2017-02-08 14:25:54 +01:00
Benjamin Dauvergne 87b53522d8 views: limit available filetypes to active ones (fixes #????) 2016-11-08 17:53:08 +01:00
Benjamin Dauvergne d1e98a3853 add requests as dependency as it's the transport we use with raven 2016-03-15 00:52:37 +01:00
Benjamin Dauvergne 10d205749f settings: extract SENTRY_DSN from environment 2016-03-15 00:51:52 +01:00
Benjamin Dauvergne 45acf3aefb install authentic2 handlers only if it is available 2016-03-14 19:37:36 +01:00
Benjamin Dauvergne 319e3c767c adapt to change in HttpResposne from Django 1.7 2016-03-14 19:28:40 +01:00
Benjamin Dauvergne 86f5bde338 adapt to custom user model in authentic 2016-03-14 19:28:27 +01:00
Benjamin Dauvergne 6f3fda4a61 add Django 1.7 migrations 2016-03-14 16:30:29 +01:00
Benjamin Dauvergne b55d11c8f8 debian: raise compatibility to only python >=2.7 2016-03-14 15:58:16 +01:00
Benjamin Dauvergne b70f38917b jenkins.sh: install python-six and tox 2016-03-14 15:46:27 +01:00
Benjamin Dauvergne 5816a6a7df Django 1.7 compatibility 2016-03-14 14:45:41 +01:00
Benjamin Dauvergne 7223c01042 settings: install raven 2016-03-14 12:34:08 +01:00
Benjamin Dauvergne 2ca78c1792 jenkins.sh: use m2crypto from underlying platform 2016-03-11 11:43:52 +01:00
Benjamin Dauvergne 79bce957d2 pw: unconditionnaly update passwords
To prevent newer password from authentic to be ovewritten, we should implement the same signals in
authentic so that the synchronization on passwords is bidirectionnal.
2015-10-30 22:37:57 +01:00
Benjamin Dauvergne 07ba049c62 be more resilient against double creation of SeenDocument models
.get_or_create() is imperfect since default isolation level is read-commited which does not prevent
phantom read. Or get_or_create() contract is possible only if phantom reads are prevented (as it
first verify that no object exists, before creating a new one, but the possibility of phantom reads
prevent this condition to be still true when the transaction is commited).
2015-10-30 22:00:16 +01:00
Benjamin Dauvergne 09191039f6 pw: restore hanging arrows 2015-09-09 14:32:12 +02:00
Benjamin Dauvergne 70aa0895d5 pw: change coloscheme for grey, remove hanging arrows 2015-09-09 13:47:09 +02:00
Benjamin Dauvergne 8cbe684101 pfwb: integrate css customization for iPad (fixes #6041) 2015-09-01 10:51:03 +02:00
Benjamin Dauvergne 9dd02484f7 Supprimer la déclaration dependency_links ajoute le changement de version south dans requirements.txt 2015-05-20 14:44:32 +02:00
Benjamin Dauvergne 5dbd835cbb Nécessite south>=1.0 pour utiliser les migrations dans south_migrations 2015-05-20 14:15:51 +02:00
Benjamin Dauvergne 57c4fd98a2 Filtre les destinataires d'un message seulement pour un message reçu
fixes #7310
2015-05-20 14:00:49 +02:00
Benjamin Dauvergne b706d60384 Limit accepted version of Django in setup_requires 2015-04-21 17:43:52 +02:00
Benjamin Dauvergne 278e707a79 add logrotate configuration for gunicorn logs 2015-04-21 17:37:03 +02:00
Benjamin Dauvergne b658a9cf54 Add 'debian-squeeze/' from commit '4aaa958c26673e1666cc5cbc13dc1aa32974d2f1'
git-subtree-dir: debian-squeeze
git-subtree-mainline: 63ef125ac6
git-subtree-split: 4aaa958c26
2015-04-21 17:26:27 +02:00
Benjamin Dauvergne 63ef125ac6 Add 'debian-wheezy/' from commit '3efb0fbc5fdb9c037fe19686ee9c1c1e8db139a9'
git-subtree-dir: debian-wheezy
git-subtree-mainline: df89f21c11
git-subtree-split: 3efb0fbc5f
2015-04-21 17:26:02 +02:00
Benjamin Dauvergne df89f21c11 Use fontawesome to show tooltip opening symbol, close other tooltip before showing a new one 2015-03-12 17:51:17 +01:00
Benjamin Dauvergne ec1f6eb6c8 On click on pictogram, display the tooltip 2015-03-12 17:23:31 +01:00
Benjamin Dauvergne 1f329df40b Fix reference do document's comment (fix previous commit) (refs #6575) 2015-03-12 14:26:51 +01:00
Benjamin Dauvergne 3a4c60376a Show document's comment as tooltip over documents table's rows (fixes #6575) 2015-03-12 14:15:01 +01:00
Benjamin Dauvergne 20c9a8d37f Generate version from git tags only 2015-03-12 11:44:10 +01:00
Benjamin Dauvergne 10a34f398a bump release to 1.2.9 2015-01-20 10:24:29 +01:00
Benjamin Dauvergne 19858064f4 Do not read the full file when trying to guess the mime type 2015-01-20 10:23:53 +01:00
Benjamin Dauvergne 2c5fd7f553 bump release to 1.2.8 2014-12-08 22:35:43 +01:00
Benjamin Dauvergne 2e13f4ce10 Fix french grammar in password_reset_email.html 2014-12-08 22:32:40 +01:00
Benjamin Dauvergne b0dbe5f51b Try installing pylint inside the virtualenv (to eliminate import errors) 2014-11-13 16:20:45 +01:00
Benjamin Dauvergne 43afd3dc5e bump release to 1.2.7 2014-11-13 15:59:07 +01:00
Frédéric Péters b0e7c758dc pfwb: create lists for the various commissions (#5862) 2014-11-13 15:58:49 +01:00
Benjamin Dauvergne 6b2488b1f4 bumpt release to 1.2.6 2014-11-10 10:34:16 +01:00
Benjamin Dauvergne 588452922d Returns only active user in non_guest_user()
Also remove explicit exclusion of inactive users, replace most direct
access to User by use of non_guest_user().

refs #5905
2014-11-07 15:48:09 +01:00
Benjamin Dauvergne 470fa794c5 version 1.2.5 2014-10-28 11:05:54 +01:00
Frédéric Péters 606910993b homepage: set docbow-user cookie to expire after 30 days 2014-10-24 14:20:49 +02:00
Frédéric Péters c6bce5d8a6 sendfile: get filename from URL if available (#5738)
Content-Disposition is considered an unsafe header and may not be retrieved
during xmlHttpRequest.
2014-10-21 15:55:02 +02:00
Frédéric Péters c6508c6373 homepage: set a docbow-user cookie on top domain
This will allow other applications running on the same domaine to present
docbow options to docbow users only.
2014-10-21 14:15:20 +02:00
Frédéric Péters cfcd4353c0 sendfile: automatically download and attach file pointer by url (#5738) 2014-10-21 14:15:20 +02:00
Benjamin Dauvergne 550ebafc4c Integrate django-statsd-mozilla (fixes #4859)
Just add

	STATSD_CLIENT='django_statsd.clients.normal'

to local_settings.py to test it.
2014-10-16 16:02:52 +02:00
Benjamin Dauvergne 235b18c7fa Surcharge la variable MEDIA_ROOT dans les tests sur les notifications aux délégués 2014-10-02 10:56:28 +02:00
Benjamin Dauvergne bc8ca5dd2b version 1.2.4 2014-10-02 10:54:50 +02:00
Benjamin Dauvergne 9566bf8b9b Fini de filtrer les délégués non actif (refs #5521) 2014-10-02 10:53:39 +02:00
Benjamin Dauvergne 35cb0908a6 version 1.2.3 2014-10-02 10:25:56 +02:00
Benjamin Dauvergne 46b336e9eb Do not notify deactivated user having a delegation (fixes #5521)
Contains tests.
2014-10-02 10:19:18 +02:00
Benjamin Dauvergne b6adf8ab53 Do not allow to select a sender which is deactivated (refs #5521) 2014-10-02 10:19:18 +02:00
Benjamin Dauvergne 0294e904a4 Synchronize with authentic even when SAML is not activated in PW 2014-09-24 09:44:47 +02:00
Benjamin Dauvergne 9b20cf48c5 Remove unused import in pw/models 2014-09-24 09:44:47 +02:00
Benjamin Dauvergne 594212d6c4 bump release to 1.2.2 2014-09-16 14:54:26 +02:00
Benjamin Dauvergne 254ed9a0be Add SESSION_COOKIE_AGE to supported configuration environment variable 2014-09-16 14:53:37 +02:00
Benjamin Dauvergne b13baed498 bump release to 1.2.1 2014-09-08 10:22:34 +02:00
Benjamin Dauvergne 941815d07f Utilise execfile pour charger le fichier de configuration Python 2014-09-05 16:12:07 +02:00
Benjamin Dauvergne fc3971a9df Ne pas notifier les délégués quand un document est privé (fixes #5401) 2014-09-05 14:47:40 +02:00
Benjamin Dauvergne a5cae61edd Ajoute un test de non-regression concernant le comportement des notifications quand le flag privé est mis (refs #5401) 2014-09-05 14:46:47 +02:00
Benjamin Dauvergne 760b5de87b bump release to 1.2.0 2014-09-03 09:44:12 +02:00
Benjamin Dauvergne d904ce99fb Ignore guest users when looking up user in sendmail command
Also allow recipient users to have more than one user with the same
mail.
2014-09-03 09:42:52 +02:00
Benjamin Dauvergne 8da5aeee84 Do not check MultipleObjectsReturned of a get by username as it cannot happen
Usernames are unique by database constraint.
2014-09-03 09:42:32 +02:00
Benjamin Dauvergne 96c94467cd Execute /etc/docbow/settings.py in settings.py context if it exists
refs #4859
2014-08-28 16:12:44 +02:00
Benjamin Dauvergne 192ccc5305 bump release to 1.1.11 2014-06-30 10:53:06 +02:00
Benjamin Dauvergne b3a8767c58 forms: fix EmailForm.clean() do not expect email and email2 to be present
fixes #5071
2014-06-30 10:29:24 +02:00
Benjamin Dauvergne 1e170ac4ac settings: fix typo 2014-06-30 10:29:23 +02:00
Benjamin Dauvergne 08c241a328 bumpt release to 1.1.10 2014-06-19 11:13:24 +02:00
Benjamin Dauvergne e58d7b6619 pfwb/settings: do not block loading of settings if PARLEMENTAIRES_MAILING_ID of MINISTRES_MAILING_ID is missing 2014-06-19 11:12:38 +02:00
Benjamin Dauvergne 85028d68f6 bump release to 1.1.9 2014-06-19 11:00:13 +02:00
Benjamin Dauvergne 0e1a73ef9d pfwb/settings: extract settings relative to tabellio from the environment 2014-06-19 11:00:13 +02:00
Benjamin Dauvergne 4155ff92ea settings: extract the SECURE_PROXY_SSL_HEADER setting from the environment 2014-06-19 11:00:13 +02:00
Benjamin Dauvergne 24e6da4ed6 sms_carrier_ovh: convert international phone numbers with + prefix to 00 prefix 2014-06-19 11:00:13 +02:00
Frédéric Péters 9279401b31 sync-tabellio: also import persons that do not have email addresses (#4986) 2014-06-19 10:05:56 +02:00
Benjamin Dauvergne 4aaa958c26 docbow.cron.d: only run the cronjob if the daemon is running
The idea is that on fail-over nodes the cronjob is stopped when the
service is stopped.
2014-06-17 10:49:48 +02:00
Benjamin Dauvergne 3076701970 postrm: do not remove /etc/docbow/secret 2014-06-17 10:49:44 +02:00
Benjamin Dauvergne 3efb0fbc5f docbow.cron.d: only run the cronjob if the daemon is running
The idea is that on fail-over nodes the cronjob is stopped when the
service is stopped.
2014-06-17 10:49:18 +02:00
Benjamin Dauvergne 5b8d9bc5e6 postrm: do not remove /etc/docbow/secret 2014-06-17 10:46:59 +02:00
Benjamin Dauvergne 5514e99940 forms: do not check old_email (bis) 2014-06-16 17:22:23 +02:00
Benjamin Dauvergne 0d9d86c5c2 forms: do not check old_email 2014-06-16 17:07:28 +02:00
Benjamin Dauvergne 1441087c04 bump release to 1.1.6 2014-06-16 16:02:27 +02:00
Benjamin Dauvergne e7a8eb9ac6 forms: repeat current email address in old_email field and disable it
refs #4950
2014-06-16 16:01:36 +02:00
Benjamin Dauvergne 030fc74bee bump release to 1.1.5 2014-06-16 15:41:27 +02:00
Benjamin Dauvergne e505d4b30d pfwb/archive2: use Django timezone aware now() function 2014-06-16 15:16:55 +02:00
Benjamin Dauvergne 1a71a5959d pfwb/archive2: optimize for lot of documents and log of log lines
also improve reporting
2014-06-16 14:49:36 +02:00
Benjamin Dauvergne 71ced0f322 pfwb/archive2: do not load all log into memory, dump by blocks 2014-06-16 11:35:31 +02:00
Benjamin Dauvergne 593ce346e8 bump release to 1.1.4 2014-06-16 10:12:15 +02:00
Benjamin Dauvergne f19ccafd8e control: add python-importlib to dependencies, it's needed by Django 2014-06-16 10:05:54 +02:00
Benjamin Dauvergne 5a4b5ff885 forms: remove initial value on EmailForm.email field
refs #4950
2014-06-16 10:02:10 +02:00
Benjamin Dauvergne 59c035fe08 bump release to 1.1.3 2014-06-16 09:35:48 +02:00
Benjamin Dauvergne fd8e4ec615 pfwb/archive2: close attached file content after reading it
refs #4962
2014-06-16 09:24:59 +02:00
Benjamin Dauvergne 98ec070731 forms,views,autocomplete_light_registry: limit autocomplete to documents in the current mailbox 2014-06-14 16:26:47 +02:00
Benjamin Dauvergne b4fe46102a bump release to 1.1.2 2014-06-12 13:52:23 +02:00
Benjamin Dauvergne 27b423526c docbow: change button caption from Close to Cancel in notification prefererences dialog
refs #4817
2014-06-12 13:46:46 +02:00
Benjamin Dauvergne 7d79d11d3b bump release to 1.1.0 2014-06-12 13:29:43 +02:00
Benjamin Dauvergne 2550bc96b9 settings: add EMAIL_HOST to environment settings 2014-06-12 13:20:55 +02:00
Benjamin Dauvergne b2e3c6538b pw: add missing image 2014-06-12 12:40:16 +02:00
Benjamin Dauvergne 6a67d964b9 pw: change listing odd line background color to light grey, and use black circle instead of orange star for signaling new documnents 2014-06-12 12:23:28 +02:00
Benjamin Dauvergne a03ed70401 pfwb/sendmail: lower level of messages when refusing a mail to warning 2014-06-02 15:02:58 +02:00
Benjamin Dauvergne 4c2e705872 pw/models: signals receivers must take **kwargs as argument 2014-06-02 11:09:18 +02:00
Benjamin Dauvergne d377b12cd3 update french translations
fixes #4815
2014-05-30 18:03:43 +02:00
Benjamin Dauvergne 65f858a92c add view behaviour to handle mass deletion of message from mailboxes
refs #4815
2014-05-30 18:03:43 +02:00
Benjamin Dauvergne 3a38db85e9 add checkboxes to select messages and delete them
refs #4815
2014-05-30 18:03:43 +02:00
Benjamin Dauvergne ca63cc658b jenkins.sh: do not fix pyOpenSSL version 2014-05-30 14:24:43 +02:00
Benjamin Dauvergne 3ef4e18b71 pw/models: user are managed inside docbow, not on authentic 2014-05-30 11:50:34 +02:00
Benjamin Dauvergne ddd5de8e77 pw/models: only synchronize guest accounts using signal handlers 2014-05-29 01:10:08 +02:00
Benjamin Dauvergne df71670947 pw/models: add signal handler to delete dead delegate accounts 2014-05-29 01:07:38 +02:00
Benjamin Dauvergne 71123b4931 admin: fix name of lists column in user admin listing 2014-05-29 01:04:46 +02:00
Benjamin Dauvergne 6ed0fef76c app_settings: add setting DEFAULT_ACCEPT_NOTIFICATIONS_FOR_GUEST
Default is True, for PW it's False. It defines the default value for the
accept_notifications field of newly created guest accounts.
2014-05-28 14:32:50 +02:00
Benjamin Dauvergne b8dbd8246f urls: give a name to homepage 2014-05-27 23:21:02 +02:00
Benjamin Dauvergne ccfbbed074 templates: password reset page must return to the homepage not a login page
As login page can be SAML or classical login/password it's easier to
target the homepage.
2014-05-27 22:25:03 +02:00
Benjamin Dauvergne b8e6a71800 pw/settings: only activate SAML settings if USE_SAML is in the environment 2014-05-27 22:24:11 +02:00
Benjamin Dauvergne c36007099a pw/settings: disable delegation to existing users 2014-05-27 22:23:55 +02:00
Benjamin Dauvergne 0feb346c38 forms: make delegation to existing users optional 2014-05-27 22:20:55 +02:00
Benjamin Dauvergne f53d1667d0 models: fix typo 2014-05-27 14:59:34 +02:00
Frédéric Péters 3941942a1d help: simplify handling of two sets of pages 2014-05-26 14:36:08 +02:00
Benjamin Dauvergne 569d123e25 help: add Makefile 2014-05-26 11:56:39 +02:00
Benjamin Dauvergne c0f63fad35 Merge remote-tracking branch 'origin/pfwb' 2014-05-26 10:36:53 +02:00
Pierre Cros 8018f0910a Modifications to have one only doc for pw and pfwb 2014-05-26 10:33:29 +02:00
Benjamin Dauvergne b7044a6581 Merge remote-tracking branch 'origin/pfwb' 2014-05-26 09:38:37 +02:00
Pierre Cros 404c288d8f Logo removed from captures 2014-05-23 07:51:09 +02:00
Benjamin Dauvergne f311feb6cb update french translation
refs #4817
2014-05-22 18:25:35 +02:00
Benjamin Dauvergne 0944ad35ef sms: send notification without the stop header, but send registration code with it 2014-05-22 18:01:37 +02:00
Benjamin Dauvergne b231073b20 remove clear sms button from the profile form
refs #4817
2014-05-22 17:57:18 +02:00
Benjamin Dauvergne aa4358e287 forms,templates: change links to buttons
fixes #4817
2014-05-22 17:30:34 +02:00
Benjamin Dauvergne 82a574eb10 js: adapt checkall.js and the jQuery upload widget to jQuery 1.11 2014-05-22 17:04:52 +02:00
Benjamin Dauvergne d8bb9f5d44 templates: come back to home page after logout 2014-05-22 17:01:55 +02:00
Benjamin Dauvergne a25fde517d pw: fix background color of header bars on the send file page 2014-05-22 15:23:46 +02:00
Benjamin Dauvergne 82e147a387 update french translation 2014-05-21 23:51:47 +02:00
Benjamin Dauvergne 5f4289c783 control: add dependency upon python-django-autocomplete-light 2014-05-21 23:17:29 +02:00
Benjamin Dauvergne f4b0d579f5 use django-autocomplete-light to provide type ahead in the search bar
fixes #4816
2014-05-21 21:53:40 +02:00
Benjamin Dauvergne 61be32815c upgrade to jquery-1.11.1 2014-05-21 21:53:08 +02:00
Benjamin Dauvergne 85f7fa0b28 models: waton search entries content field cannot be null
refs #4816
2014-05-21 12:08:56 +02:00
Benjamin Dauvergne 78002c029c models: use a custom search adapter to limit length of the title field
refs #4816
2014-05-21 12:06:36 +02:00
Benjamin Dauvergne 54589691c4 views: do not raise 404 on inexisting object delete, just redirect to origin 2014-05-21 10:53:57 +02:00
Benjamin Dauvergne 11bf47ab23 pfwb/tests: Document.date must be a datetime not a date 2014-05-19 19:23:55 +02:00
Benjamin Dauvergne 580ba94b48 jenkins.sh: upgrade pyOpenSSL version used 2014-05-19 18:55:53 +02:00
Benjamin Dauvergne 515a417ed8 control: add python-django-watson to dependencies 2014-05-19 18:44:01 +02:00
Benjamin Dauvergne eecc113d2d setup.py,requirements: add django-watson to dependencies
refs #4816
2014-05-19 18:42:40 +02:00
Benjamin Dauvergne 86470b1246 forms,views,templates: add text search field to the filter form
refs #4816
2014-05-19 18:35:02 +02:00
Benjamin Dauvergne f64ba9779b install django-watson for full text search indexing
refs #4816
2014-05-19 18:34:54 +02:00
Benjamin Dauvergne eb0aa02b15 update french translation 2014-05-16 18:02:18 +02:00
Benjamin Dauvergne 65f7355a1c widgets: fix import of SubWidget on Django < 1.6 2014-05-16 17:56:00 +02:00
Benjamin Dauvergne f4f83b4021 control: add dependency on python-psycopg2 2014-05-16 16:49:32 +02:00
Benjamin Dauvergne 2a14883573 control: add dbconfig-common and postgresql as pre-depends 2014-05-16 16:47:45 +02:00
Benjamin Dauvergne d1c97e7881 notification: in SMSNotifier.__init__ call parent implementation 2014-05-15 17:01:13 +02:00
Benjamin Dauvergne af7491d091 notification: check notification preference when notifying users about documents
fixes #4817
2014-05-15 16:50:10 +02:00
Benjamin Dauvergne 5602366ec8 tests: add test case to validate that notification preferences are honored
refs #4817
2014-05-15 16:50:10 +02:00
Benjamin Dauvergne b56e2ea7ae profile_views: add new profile view NotificationPreferenceView
implement modal dialog using CSS and hash tag URL.

refs #4817
2014-05-15 16:50:10 +02:00
Benjamin Dauvergne 45c33510b7 forms: add NotificationPreferencesForm
refs #4817
2014-05-15 16:50:09 +02:00
Benjamin Dauvergne acc46840f8 widgets: add CheckboxMultipleSelect widget
refs #4817
2014-05-15 16:50:09 +02:00
Benjamin Dauvergne 8f6974a22f forms: remove dead import
refs #4817
2014-05-15 16:50:09 +02:00
Benjamin Dauvergne 471fa6cf10 models: add model NotificationPreference to store user's notification preferences
refs #4817
2014-05-15 16:50:09 +02:00
Benjamin Dauvergne d6d01d133a notification: add description and key field
refs #4817
2014-05-15 16:50:09 +02:00
Benjamin Dauvergne a7f9d60003 tests: make the number of users, documents and filetypes a parameter of the test case 2014-05-15 16:50:09 +02:00
Benjamin Dauvergne 9a8910a1fe notification: add default implementation of BaseNotifier.process() 2014-05-15 16:50:09 +02:00
Benjamin Dauvergne 818a739263 css: enlarge input fields in profile forms 2014-05-14 12:34:51 +02:00
Benjamin Dauvergne 37d8adf11b set autocomplete to off on second email field 2014-05-14 12:34:19 +02:00
Benjamin Dauvergne e7dd8a6a8c update french translation
fixes #4814
2014-05-14 12:12:14 +02:00
Benjamin Dauvergne d8cdcf3c75 profile_views: add EmailView to change user email
refs #4814
2014-05-14 12:12:11 +02:00
Benjamin Dauvergne 4069703e62 cbv: change cbv hierarchy and create a default is_post_target implementation
refs #4814
2014-05-14 12:12:08 +02:00
Benjamin Dauvergne 148e214084 forms: add new form to edit user email
refs #4814
2014-05-14 12:12:02 +02:00
Benjamin Dauvergne 853a7e3a65 settings,app_settings,pfwb/settings: add DOCBOW_EDIT_EMAIL setting
refs #4814
2014-05-14 12:11:45 +02:00
Benjamin Dauvergne dac9798323 pfwb/sync-tabellio: do not initialize the personal email 2014-05-14 11:20:31 +02:00
Benjamin Dauvergne 8510393a23 pfwb: remove dead imports 2014-05-14 11:19:43 +02:00
Frédéric Péters fc372ca5d8 pfwb: only set email address when persons are created (fixes #3824) 2014-05-14 11:17:10 +02:00
Benjamin Dauvergne 60bbfa3c97 settings,pw/settings: add a PLATFORM setting and use it to customize settings for the test platform 2014-05-13 16:53:43 +02:00
Benjamin Dauvergne 880af44bd3 pw/settings: add EMAIL_SUBJECT_PREFIX setting 2014-05-13 16:52:47 +02:00
Benjamin Dauvergne e7501b8e0d pw/settings: add BASE_URL setting 2014-05-13 16:48:04 +02:00
Benjamin Dauvergne e722481b3d pw/sendmail: make a document private if any email addresse local part contains the suffix -private 2014-05-13 15:34:52 +02:00
Benjamin Dauvergne b629d2a78f pw/sendmail: allow to set the private flag using a prefix on the subject 2014-05-13 14:58:08 +02:00
Benjamin Dauvergne 8cc5e197a1 pw/settings: make the journal a copy of the default one for test purpose 2014-05-13 12:25:27 +02:00
Benjamin Dauvergne 4d9de8975e pw/templates: override password reset templates to target the SAML login page 2014-05-13 12:10:01 +02:00
Benjamin Dauvergne e29aa90aed pw/sendmail: add subject property on the Command class 2014-05-13 08:54:03 +02:00
Benjamin Dauvergne 7a634aa18c pw/sendmail: first lookup by email then by username 2014-05-12 17:55:24 +02:00
Benjamin Dauvergne 3d91b3230b admin: add private to DocumentAdmin.list_filter not filter_horizontal 2014-05-12 14:11:26 +02:00
Benjamin Dauvergne 4a50c60041 admin: integrate private flag to document admin page 2014-05-12 12:44:18 +02:00
Benjamin Dauvergne 141d6119d3 signals: log only change to the default database
Logging all modifications breaks synchronization with authentic, as the
User object is from another database than the one where the Journal
object is stored.
2014-05-12 11:43:28 +02:00
Benjamin Dauvergne c78d17346a pw/models: update password of authentic's users conditionnaly
The password is updated only if:
- the user is new in authentic,
- or the password has changed in Docbow.
2014-05-12 11:26:03 +02:00
Benjamin Dauvergne c5657269a7 pw: only copy username, first name, last name and email field when synchronizing users with authentic 2014-05-12 10:25:44 +02:00
Benjamin Dauvergne ac4bd9a928 pw/models: do not clobber the user.id of the authentic user instance 2014-05-09 17:06:52 +02:00
Benjamin Dauvergne 9c085f2ebb pw/models: fix user_pre_save handler 2014-05-09 16:49:48 +02:00
Benjamin Dauvergne 860274ffff docbow.cron.d: add missing username to run the cron job as 2014-05-09 16:33:32 +02:00
Benjamin Dauvergne 049000c98f pw/settings: add settings for accessing authentic db 2014-05-09 16:25:04 +02:00
Benjamin Dauvergne 3418cfbdee jenkins.sh: report an error if any of the test script fails 2014-05-09 16:03:05 +02:00
Benjamin Dauvergne 36b20aed70 tests: modify test as inbox_by_document is not used anymore 2014-05-09 15:50:08 +02:00
Benjamin Dauvergne 714b83aa35 jenkins.sh: delete sqlite database after testing syncdb / migrate 2014-05-09 15:42:43 +02:00
Benjamin Dauvergne 324ccbea46 tests: when testing notifications take into account app_settings.PERSONAL_EMAIL 2014-05-09 15:41:44 +02:00
Benjamin Dauvergne 9c2edf1fa7 tests: when doing authentication add an override_setting to be sure the default ModelBackend is active 2014-05-09 15:37:13 +02:00
Benjamin Dauvergne cae4b6a9cd tests: augment message in test of private flag 2014-05-09 15:13:04 +02:00
Benjamin Dauvergne 006045ecde tests: check users[0] and [1] have username user-0 and user-1 2014-05-09 15:04:03 +02:00
Benjamin Dauvergne 51b34c91bf jekins.sh: give explicit list of applications to test 2014-05-09 14:51:15 +02:00
Benjamin Dauvergne 5aecb304b0 jekins.sh,docbow-ctl: set SECRET_KEY as part of the jenkins script 2014-05-09 14:42:23 +02:00
Benjamin Dauvergne b15de59302 jekins.sh: initialize db for each customization 2014-05-09 14:39:37 +02:00
Benjamin Dauvergne ff8336b0dc tests: allocate a temporary MEDIA_ROOT for tests 2014-05-09 14:37:58 +02:00
Benjamin Dauvergne 513bc1a01d jenkins.sh: fix problem with SSL certificates and SNI 2014-05-09 14:34:24 +02:00
Benjamin Dauvergne 8881bb3624 requirements.txt: fix reference for python-entrouvert 2014-05-09 14:30:13 +02:00
Benjamin Dauvergne 9235d1892e requirements.txt: add python-entrouvert 2014-05-09 14:01:26 +02:00
Benjamin Dauvergne 3e54883944 requirements: add django-mellon to requirements, do not commmit to a specific version of rfc3161 2014-05-09 14:00:12 +02:00
Benjamin Dauvergne e9684f7734 pw: rewrite pw sendmail command using pfwb version
- add unit tests too
2014-05-09 13:57:55 +02:00
Benjamin Dauvergne 08b22b7947 pfwb/sendmail: add recipients and filenames in error logs 2014-05-09 13:57:52 +02:00
Benjamin Dauvergne c1f7f2eb11 pw/models: add a pre_save signal on User to synchronize users with authentic
To activate this feature, a database named 'authentic' must be configure
in the settings.
2014-05-09 13:57:52 +02:00
Benjamin Dauvergne 9e79cc6946 templates/registration: fix template as the banner as been moved into base.html 2014-05-07 16:59:43 +02:00
Benjamin Dauvergne 25a97bfddb docbow.cron.d: add cronjob to send notifications 2014-05-06 14:35:15 +02:00
Benjamin Dauvergne 7257c32e4f sql: use placeholder to pass boolean parameters as the syntax differ between backends 2014-05-06 10:51:10 +02:00
Benjamin Dauvergne 52dffde69f settings: simplify LOGGING setting 2014-05-06 10:46:26 +02:00
Benjamin Dauvergne 4810d8314c wsgi: user the CUSTOMIZATION environment variable 2014-05-06 09:19:34 +02:00
Benjamin Dauvergne d16d0c4d39 jenkins.sh: run tests 2014-05-06 08:47:14 +02:00
Benjamin Dauvergne 37ec050710 bump release to 1.0.1 2014-05-06 08:44:24 +02:00
Benjamin Dauvergne b6b20dd358 fixtures: add natural keys 2014-05-05 17:27:25 +02:00
Benjamin Dauvergne 0fa2aa79a5 pw/fixtures: add natural keys 2014-05-05 17:15:25 +02:00
Benjamin Dauvergne 01051f5dbc settings: install a deserializer for JSON which understands natural keys 2014-05-05 17:15:00 +02:00
Benjamin Dauvergne ad9a98923b pw/fixtures: move fixtures from the generic docbow application
Those fixtures were made for the PW
2014-05-05 17:02:55 +02:00
Benjamin Dauvergne 2072cf666a init: collect static files when reloading 2014-05-05 16:38:42 +02:00
Benjamin Dauvergne 9fc5365eb2 settings: change directory for overriding static files 2014-05-05 16:35:29 +02:00
Benjamin Dauvergne 50ad05842a settings: change the STATIC_ROOT directory 2014-05-05 16:33:39 +02:00
Benjamin Dauvergne 1a72e0e75b init: call collectstatic in start 2014-05-05 16:33:00 +02:00
Benjamin Dauvergne 3679533942 init: fix syntax errors 2014-05-05 16:32:01 +02:00
Benjamin Dauvergne e5a8134be0 remove all references to tinymce 2014-05-05 16:22:11 +02:00
Benjamin Dauvergne 93e4d16d9e control: add dependency on python-magic 2014-05-05 16:19:01 +02:00
Benjamin Dauvergne 4abab27257 clean dependencies 2014-05-05 16:18:40 +02:00
Benjamin Dauvergne 5c68751b01 remove LDAP settings 2014-05-05 16:18:29 +02:00
Benjamin Dauvergne ef45621e49 control: fix name of Debian package for django-tables2 2014-05-05 16:09:17 +02:00
Benjamin Dauvergne 3efcf3fe67 MANIFEST.in: distribute help pages, text and js templates, pfwb's README 2014-05-05 15:44:21 +02:00
Benjamin Dauvergne 6de087cb7e fix distibution issues 2014-05-05 15:41:49 +02:00
Benjamin Dauvergne 3e54cafee9 MANIFEST.in: distribute locale files 2014-05-05 15:40:54 +02:00
Benjamin Dauvergne abd2060d77 MANIFEST.in: distribute timestamping authorities certificates 2014-05-05 15:37:05 +02:00
Benjamin Dauvergne 6b0e38b833 MANIFEST.in: distribute collation database 2014-05-05 15:36:23 +02:00
Benjamin Dauvergne ec32e1b9c7 setup.py: clean dependencies 2014-05-05 15:33:53 +02:00
Benjamin Dauvergne 2f8d9447e1 distribute MANIFEST.in 2014-05-05 15:32:27 +02:00
Benjamin Dauvergne 1939bf2f7e fix distibution issues 2014-05-05 15:29:48 +02:00
Benjamin Dauvergne 67bbc975e8 rename db.conf to db.template 2014-05-05 15:18:43 +02:00
Benjamin Dauvergne 2c16601c07 fix typo in debian/install 2014-05-05 15:14:42 +02:00
Benjamin Dauvergne 827759889c set version to 1.0.0 2014-05-05 15:13:23 +02:00
Benjamin Dauvergne 0a1971c0c8 move Makefile to please eobuilder 2014-05-05 15:04:36 +02:00
Benjamin Dauvergne 7c542eb630 rename Makefile to please eobuilder 2014-05-05 15:04:00 +02:00
Benjamin Dauvergne 5347a3a678 first commit 2014-05-05 15:02:33 +02:00
Benjamin Dauvergne ad86877df7 relax dependency on Django 2014-05-05 14:30:24 +02:00
Benjamin Dauvergne ebdf067b69 update french translation 2014-05-05 14:30:02 +02:00
Benjamin Dauvergne a3f35ab536 forms: [private-flag] plug the private flag in the send file form 2014-05-05 13:31:42 +02:00
Benjamin Dauvergne d4529976f1 settings: use TEMPLATE_DIRS to overload templates from docbow then the customization application 2014-05-05 13:31:42 +02:00
Benjamin Dauvergne 165044a6d4 templates: overload crispy_form template for fields
The normal template does not format checkboxes like other fields.
2014-05-05 13:31:42 +02:00
Benjamin Dauvergne 2ad68f6cc4 css: [private-flag] adapt style to checkboxes 2014-05-05 13:31:42 +02:00
Benjamin Dauvergne d4d8426023 settings: [private-flag] add default for DOCBOW_PRIVATE_DOCUMENTS 2014-05-05 13:31:42 +02:00
Benjamin Dauvergne 753637541c settings: set level to DEBUG for all logger when DEBUG is on 2014-05-05 13:31:42 +02:00
Benjamin Dauvergne e50a7be2d2 sql: hide deleted document in listings
regression appeared when raw SQL were introduced
2014-05-05 13:31:42 +02:00
Benjamin Dauvergne 2a97da9d32 test: in DelegatesTestCase keep the delegate user around 2014-05-05 13:31:42 +02:00
Benjamin Dauvergne 80be740f90 settings: fix logging configuration on docbow_project domain 2014-05-05 13:31:42 +02:00
Benjamin Dauvergne b5d6e81095 timestamp: pass the provider as first argument to encode_timestamp 2014-05-05 13:31:42 +02:00
Benjamin Dauvergne 72b80cb264 timestamp: copy configuration to prevent tampering with the global copy 2014-05-05 13:31:42 +02:00
Benjamin Dauvergne 8657f594b4 tests: [private-flag] add tests for the private flag feature 2014-05-05 13:31:42 +02:00
Benjamin Dauvergne d34dbd0097 views: [private-flag] do not allow delegates to access private documents 2014-05-05 13:31:42 +02:00
Benjamin Dauvergne 7a2d333133 sql: [private-flag] only show private documents to real recipients 2014-05-05 13:31:42 +02:00
Benjamin Dauvergne 2c70b9dced models: [private-flag] add new field Document.private
private documents are cannot be accessed by delegates
2014-05-05 13:31:42 +02:00
Benjamin Dauvergne 2112287b8f app_settings: [private-flag] add setting ALLOW_PRIVATE_FLAG to activate the private-flag feature 2014-05-05 13:31:42 +02:00
Benjamin Dauvergne f60a238188 pw: report production settings 2014-05-02 19:50:31 +02:00
Benjamin Dauvergne fd2ad5e83a urls: load customization urls 2014-05-02 18:09:11 +02:00
Benjamin Dauvergne 0da3818952 docbow-ctl: set DJANGO_SETTINGS_MODULE based on the CUSTOMIZATION environment variable, default to pfwb 2014-05-02 18:08:02 +02:00
Benjamin Dauvergne 811642d359 settings: add console logger for debugging only after loading local_settings.py 2014-05-02 17:56:01 +02:00
Benjamin Dauvergne e69805a7c9 context_processors: add logout_url_processor to expose settings.LOGOUT_URL to templates 2014-05-02 17:51:36 +02:00
Benjamin Dauvergne 52865ecd3c templates: use the login page style for base.html and add base_raw.html to extends base_user.html upon 2014-05-02 17:48:38 +02:00
Benjamin Dauvergne 4c8d9a7f1c templates,pw,pfwb: add a local stylesheet to the default template 2014-04-28 09:19:44 +02:00
Benjamin Dauvergne 5e63af952a settings: add new setting CUSTOMIZATION to select pw or pfwb customizations 2014-04-28 09:19:44 +02:00
Benjamin Dauvergne 30dc34be47 notification: disable notification to personal_email or by SMS when the corresponding profile field are disabled 2014-04-26 11:36:57 +02:00
Benjamin Dauvergne 7dbc0faa76 README.rst: add new application settings 2014-04-26 11:34:34 +02:00
Benjamin Dauvergne 39252a1191 templatetags: add application setting to remove the mailing lists view link from the left menu 2014-04-26 11:32:03 +02:00
Benjamin Dauvergne 52a42d6435 style,templates: add a footer block to hide some padding space under long content
It also allow us to display our copyright and distribution license.
2014-04-26 11:27:16 +02:00
Benjamin Dauvergne 534bb7edbb templates: hide the profile notification form when no field is shown 2014-04-26 11:26:45 +02:00
Benjamin Dauvergne 8b499d6f06 forms,profile_views: allow to hide the mobile phone field in the notification profile view 2014-04-26 11:25:30 +02:00
Benjamin Dauvergne 937901a3ea admin: show DocbowProfile model using an InlineAdmin in the user admin 2014-04-26 11:10:28 +02:00
Benjamin Dauvergne 770a248ef3 form: hide the personal_email field if DOCBOW_PERSONAL_EMAIL is False 2014-04-26 11:10:05 +02:00
Benjamin Dauvergne 34d1922482 app_settings: ease definition of application settings using a default dictionnary 2014-04-26 11:09:38 +02:00
Benjamin Dauvergne 0bae8f7e8f Use LC_ALL to set the UTF-8 codeset in the init.d script file 2014-04-26 10:49:51 +02:00
Benjamin Dauvergne bf051beb68 raise requirement to Django 1.6 2014-04-26 10:48:30 +02:00
Benjamin Dauvergne 4e591e5eac settings: remove TransactionMiddleware as it's deprecated in Django 1.6 2014-04-26 10:47:53 +02:00
Benjamin Dauvergne 379c5e04f4 sql: adapt raw sql queries to work with sqlite3 too
sqlite3 does not support tuple in query placeholders, you must flatten
the tuple in the query parameters and the template first.
2014-04-26 10:46:25 +02:00
Benjamin Dauvergne f0712b94ea tables: fix ordering of the sender column in the inbox table 2014-04-26 10:45:54 +02:00
Benjamin Dauvergne e69e202949 tables: fix display of the sender 2014-04-26 10:39:37 +02:00
Benjamin Dauvergne f4ad7ec846 timestamp: use importlib to make timestamp module more modular
Timestamping objects are now builts using a parametrizable class,
actual providers all use the rfc3161.RemoteTimestamper class.
2014-04-25 18:43:07 +02:00
Benjamin Dauvergne 0dd2fa2c51 timestamp: make the provider a parameter of the timestamp function 2014-04-24 15:31:07 +02:00
Benjamin Dauvergne c58200921f locale: update french translations 2014-01-31 16:44:55 +01:00
Benjamin Dauvergne 18d33e5e3b Merge branch 'stable' into pfwb 2014-01-31 16:38:11 +01:00
Benjamin Dauvergne e412463db3 views,tables: fix model used for CSV and ODS views 2014-01-31 16:36:43 +01:00
Benjamin Dauvergne f4e870b295 Merge branch 'stable' into pfwb 2014-01-31 16:12:44 +01:00
Benjamin Dauvergne aea3fceaf9 docbow.init: augment default number of workers 2014-01-31 16:12:42 +01:00
Benjamin Dauvergne 278d8803f5 docbow.init: augment default number of workers 2014-01-31 16:12:09 +01:00
Benjamin Dauvergne c6e6f54597 Merge branch 'pfwb' into stable 2014-01-31 16:09:30 +01:00
Benjamin Dauvergne 32e07a0025 locale: replace Idem by - when the official is the same as the real sender 2014-01-31 16:08:33 +01:00
Benjamin Dauvergne 58140c6d61 sql: do not mix mailbox from the inbox and the outbox 2014-01-31 16:08:11 +01:00
Benjamin Dauvergne e989838624 tests: test content of mail notifications 2014-01-31 16:00:36 +01:00
Benjamin Dauvergne c4c84e4b8a README.rst: describe settings DOCBOW_BASE_URL, DOCBOW_MAILBOX_PER_PAGE, DOCBOW_TRUNCATE_FILENAME and DOCBOW_TIMESTAMP_PROVIDER 2014-01-31 10:25:05 +01:00
Benjamin Dauvergne 78010b285d README.rst: describe DOCBOW_MAX_FILE_SIZE setting 2014-01-31 10:20:33 +01:00
Benjamin Dauvergne 3c8a22cc0b upload_views: add server side control of upload size 2014-01-31 00:33:23 +01:00
Benjamin Dauvergne 6737ca6e16 fileupload: translate error messages, allow to limit max file size client side 2014-01-31 00:33:19 +01:00
Benjamin Dauvergne 638bdd8261 settings: add new Django 1.6 setting ATOMIC_REQUESTS to replace the TransactionMiddleware 2014-01-31 00:07:24 +01:00
Benjamin Dauvergne d17040d901 pfwb/tests: add test on the command archive2 2014-01-30 22:41:57 +01:00
Benjamin Dauvergne 4089057600 pfwb/sendmail: rename commit_on_success as atomic 2014-01-30 22:00:18 +01:00
Benjamin Dauvergne 357abea8ec bump required version of django-journal 2014-01-30 21:49:44 +01:00
Benjamin Dauvergne b0aca451f9 pfwb/tests: use a temporary directory for MEDIA_ROOT 2014-01-30 21:49:07 +01:00
Benjamin Dauvergne e26b043fb4 pfwb/sendmail: use Django 1.6 atomic() decorator if available revert on commit_on_success() otherwise 2014-01-30 21:47:59 +01:00
Benjamin Dauvergne b32e66ed14 update minimum version for django-journal 2014-01-30 17:20:59 +01:00
Benjamin Dauvergne df72fbf74c pfwb/tests: output log to stderr 2014-01-30 16:38:07 +01:00
Benjamin Dauvergne e3b6cff550 pfwb/tests: set GED_DIRECTORY to None when testing mail interface 2014-01-30 16:37:53 +01:00
Benjamin Dauvergne c13e55c285 pfwb/tests: add test for the attached file mode of the mail interface 2014-01-30 16:24:08 +01:00
Benjamin Dauvergne 0a395c8219 add script used to update platforms 2014-01-30 15:54:05 +01:00
Benjamin Dauvergne b3eb40097d add sample page to display benchmark results 2014-01-30 15:53:29 +01:00
Benjamin Dauvergne 888bb922bf pfwb/tests: add test on the push to ged interface 2014-01-30 15:20:20 +01:00
Benjamin Dauvergne c745708f6d tests: remove dead statements 2014-01-30 15:20:20 +01:00
Benjamin Dauvergne 8cad213460 pfwb: fix potential unicode decode error 2014-01-30 15:20:20 +01:00
Benjamin Dauvergne f132bbef97 settings: fix default GED_DIRECTORY 2014-01-30 15:20:20 +01:00
Benjamin Dauvergne a4b67b5bf3 pfwb: change app_settings implementation 2014-01-30 15:20:20 +01:00
Benjamin Dauvergne d2d22cf381 models: prepare for Django 1.6, add default value for all boolean fields 2014-01-30 15:20:20 +01:00
Benjamin Dauvergne a496fa78c8 Revert partially commit 558ca2b14a 2014-01-30 15:20:20 +01:00
Benjamin Dauvergne 68b765efec models: use 'inbox-message' url endpoint in Document.url()
fixes #4249
2014-01-30 15:20:20 +01:00
Benjamin Dauvergne d3bc263d78 improve the 404 template 2014-01-30 15:20:19 +01:00
Benjamin Dauvergne 2be10c597d tools/docbow.init: use gunicorn to do the chuid not start-stop-daemon (allow to bind on the port 80) 2014-01-30 15:20:19 +01:00
Benjamin Dauvergne e260776938 views: when a document is not found report if the document doest not exist of if the user does not have access to it
fixes #4225
2014-01-30 15:20:19 +01:00
Benjamin Dauvergne 42bea2b8f9 Revert partially commit 558ca2b14a 2014-01-24 16:27:21 +01:00
Benjamin Dauvergne da0f330ba4 models: use 'inbox-message' url endpoint in Document.url()
fixes #4249
2014-01-24 16:26:58 +01:00
Benjamin Dauvergne 300ee2d528 improve the 404 template 2014-01-24 16:26:58 +01:00
Benjamin Dauvergne 2d38ebc2df tools/docbow.init: use gunicorn to do the chuid not start-stop-daemon (allow to bind on the port 80) 2014-01-24 16:26:58 +01:00
Benjamin Dauvergne 0c5d03a57c views: when a document is not found report if the document doest not exist of if the user does not have access to it
fixes #4225
2014-01-24 16:26:57 +01:00
Benjamin Dauvergne 72e4273978 Revert partially commit 558ca2b14a 2014-01-23 19:23:40 +01:00
Benjamin Dauvergne 558ca2b14a models: use 'inbox-message' url endpoint in Document.url()
fixes #4249
2014-01-23 16:43:29 +01:00
Benjamin Dauvergne 1f07d22811 models: remove the Mailbox.deleted field 2014-01-21 21:35:31 +01:00
Benjamin Dauvergne ebd64254a9 models: remove the Mailbox.seen field 2014-01-21 21:30:46 +01:00
Benjamin Dauvergne 48f32ea4db setup.py: adapt to change in Django compilemessages 2014-01-21 15:32:04 +01:00
Benjamin Dauvergne 390dbe4eee use tarball instead of git to retrieve python-entrouvert 2014-01-21 15:06:30 +01:00
Benjamin Dauvergne 8c0c774235 use pytz 2014-01-21 14:54:47 +01:00
Benjamin Dauvergne 9ead810522 sql: add d.date to GROUP BY to comply with Postgresql 8.4
Postgresql 9.1 is able to infer that d.date depends on d.id
2014-01-16 09:22:27 +01:00
Benjamin Dauvergne b0e7b88e69 views: make get_related_users always return a QuerySet 2014-01-16 09:19:16 +01:00
Benjamin Dauvergne 1afa257ee9 forms,fields: on new document form order sender list box content 2014-01-16 09:12:02 +01:00
Benjamin Dauvergne 2b430a74f9 improve documentation 2014-01-16 09:09:32 +01:00
Benjamin Dauvergne d7bd50f822 views,templatetags: use new optimized SQL queries 2014-01-16 09:09:07 +01:00
Benjamin Dauvergne 0ac341ef45 views: do not store User objects in the session to comply with Django 1.6 new default JSON session serializer 2014-01-16 09:08:38 +01:00
Benjamin Dauvergne 143399e47f sql: new module for storing optimized SQL queries 2014-01-16 09:02:43 +01:00
Benjamin Dauvergne 5197eb7aef views: order delegators by theyr last_name, first_name then username
fixes #4221
2014-01-15 14:27:10 +01:00
Benjamin Dauvergne a2cde1066e models,tables: use full name for the sender column
fixes #4222
2014-01-15 14:23:13 +01:00
Benjamin Dauvergne 788a3fe536 forms: order sender in the sender list box
fixes #4221
2014-01-15 14:20:35 +01:00
Benjamin Dauvergne 2483aaf31e views,tables: restore the seen column on inbox view 2014-01-15 12:59:41 +01:00
Benjamin Dauvergne d0b197ac7b static: highlight rows when hovering on mailbox table
fixes #4062
2014-01-14 14:57:05 +01:00
Benjamin Dauvergne 15b4c93fe1 views: fix misuse of get_or_create() 2014-01-13 16:10:33 +01:00
Benjamin Dauvergne ffed6751ff pfwb: in archive2 command use natural keys when dumping attached files
It preserves attached file kind name instead of the numeric id.
2014-01-06 17:01:11 +01:00
Benjamin Dauvergne 6283a9ddc3 pfwb: translate README file into french, add section on archiving 2014-01-06 16:41:30 +01:00
Benjamin Dauvergne 6d93475c28 pfwb: move archive2 command into the pfwb application 2014-01-06 16:41:25 +01:00
Benjamin Dauvergne ceafa8d5ef pfwb: add more docstrings and comments 2014-01-06 15:53:42 +01:00
Benjamin Dauvergne 226337c7fb pfwb/tests: remove test on undecodable as I don't know how to make it pass 2013-12-20 01:37:51 +01:00
Benjamin Dauvergne 496ac48bed pfwb: in sendmail log repr(subject) as it could be non ascii 2013-12-20 01:35:49 +01:00
Benjamin Dauvergne d3648dd079 pfwb/sendmail: improve report when failing to retrieve the document from the tabellio server 2013-12-20 01:35:24 +01:00
Benjamin Dauvergne b8d7293d31 pfwb: add more tests 2013-12-20 01:34:47 +01:00
Benjamin Dauvergne 472c8fb004 settings: do not overwirte the DEBUG_TOOLBAR_CONFIG setting 2013-12-18 15:48:14 +01:00
Benjamin Dauvergne 9c7f844da5 tests: ignore ordering when comparing user list 2013-12-18 15:12:19 +01:00
Benjamin Dauvergne 6cc62362f9 pfwb/migrations: add dependency on first docbow migration 2013-12-18 15:11:30 +01:00
Benjamin Dauvergne 5caacd78ed django.conf.urls.defaults is deprecated 2013-12-18 10:01:07 +01:00
Benjamin Dauvergne 3c1d4072a8 benchmark: output result to JSON 2013-12-18 09:33:55 +01:00
Benjamin Dauvergne f588355118 templatetags: update unseen documents counting in the menu template tag 2013-12-17 10:32:43 +01:00
Benjamin Dauvergne d0f3b6c031 views: create seen document objects when downloading an attached file 2013-12-17 10:32:43 +01:00
Benjamin Dauvergne f830fe2e0a models: use prefetched mailboxes in Document.related_users() 2013-12-17 10:32:43 +01:00
Benjamin Dauvergne b103476067 models: add SeenDocument model 2013-12-17 10:32:43 +01:00
Benjamin Dauvergne b88eec536b models: add index on Mailbox.outbox field 2013-12-17 10:06:13 +01:00
Benjamin Dauvergne 2180131b29 templatetags: use get_documents() to compute count of visible documents 2013-12-17 09:52:44 +01:00
Benjamin Dauvergne 1f3cce5b86 views: factorize code to compute visible documents into function get_documents() 2013-12-17 09:52:07 +01:00
Benjamin Dauvergne 12eb4f5447 settings: activate cached template loader when DEBUG is False
It shaves 10ms from the mean view rendering time.

refs #4124
2013-12-16 17:23:19 +01:00
Benjamin Dauvergne 5178015576 benchmark: measure rendering time for main views and all users
Selected views are inbox, outbox and send_file.

Current measures indicate a rendering mean time of around 50 ms for all
views.

fixes #4124
2013-12-16 16:28:13 +01:00
Benjamin Dauvergne 0375da771f migrations: fix typo in migration 35 2013-12-16 15:17:10 +01:00
Benjamin Dauvergne 3bc598a9d4 tables: fix ordering directives to remove the Mailbox level 2013-12-16 15:17:10 +01:00
Benjamin Dauvergne 04585b13b7 settings: only activate sentry logger if RAVEN_CONFIG_DSN is configured, use the mail_admins handler otherwise 2013-12-16 15:17:10 +01:00
Benjamin Dauvergne c29f8a3a71 forms,pfwb: use truncate_filename when attaching files to new documents 2013-12-16 15:17:10 +01:00
Benjamin Dauvergne 53973b239a utils: add a truncate_filename function parameterized by a setting 2013-12-16 15:17:08 +01:00
Benjamin Dauvergne 7177f46f61 adopt new app_settings pattern 2013-12-16 15:16:09 +01:00
Benjamin Dauvergne 06871eb300 templates: fix click handler on mailbox tables to work under iOS webkit (bis)
rename live() to delegate()
2013-12-11 18:15:36 +01:00
Benjamin Dauvergne 1f53dbb22e templates: fix click handler on mailbox tables to work under iOS webkit
Click events do not bubble to the body element, it breaks the jQuery
live method.

fixes #4073
2013-12-11 18:09:20 +01:00
Benjamin Dauvergne a8eda560dd static: enlarge line height and with of inbox recipients column 2013-12-06 11:28:27 +01:00
Benjamin Dauvergne 1f4636c4ad template: load project stylesheet at the end, to permit overloading 2013-12-06 10:10:24 +01:00
Benjamin Dauvergne 78194be045 tables: restore recipients column 2013-12-04 17:18:38 +01:00
Benjamin Dauvergne ab17f5eaae css: set cursor to pointer over mailbox tables
fixes #4062
2013-12-04 15:46:28 +01:00
Benjamin Dauvergne 5119c0d616 templates: in message.html use username template tag to format user names
That fixes empty username when users have no first name and last name.

fixes #4061
2013-12-04 12:22:51 +01:00
Benjamin Dauvergne 6cab115064 views: return documents only one time 2013-12-03 17:55:46 +01:00
Benjamin Dauvergne 2b3e3b449c in the message view show recipient only if the user has a delegation over it or if it's him
refs #3941
2013-11-29 18:19:24 +01:00
Benjamin Dauvergne 087bda41ea views: fix multiple object returned exception in get_document() 2013-11-29 18:17:18 +01:00
Benjamin Dauvergne a0ba27a129 clean unused imports 2013-11-29 10:24:47 +01:00
Benjamin Dauvergne a2e2887bf2 views,models: simplify code computing transitive closure of MailingList models 2013-11-29 10:23:41 +01:00
Benjamin Dauvergne a87ab42812 views: remove all uses of LoggerAdapter 2013-11-29 10:22:48 +01:00
Benjamin Dauvergne 4704690c5a tests: inbox_by_document changed its behaviour since inbox-message now use documents ids 2013-11-26 11:09:14 +01:00
Benjamin Dauvergne e786499a1e views: inbox_by_document must redirect to inbox-message not inbox
fix bug introduced in 75ea414334
2013-11-26 11:01:27 +01:00
Benjamin Dauvergne 6dde756176 style.css: remove position: absolute on download-buttons
fixes #3813
2013-11-26 10:57:13 +01:00
Benjamin Dauvergne 8c9ee047b2 templates: clear floats before displaying the forwarding forms
fixes #3813
2013-11-26 10:54:57 +01:00
Benjamin Dauvergne 6eaf66c098 views: in su() clear the related_users cache when changing user 2013-11-26 10:45:00 +01:00
Benjamin Dauvergne 57e629d339 migrations: in migration 35 use foreignkey id instead of objects 2013-11-26 10:40:58 +01:00
Benjamin Dauvergne cf615c7fac migrations: fix migration 35 2013-11-26 10:40:13 +01:00
Benjamin Dauvergne b9e73fb26e style.css: adjust date column width 2013-11-26 10:38:21 +01:00
Benjamin Dauvergne 75ea414334 use documents instead of mailboxes in views 2013-11-26 10:38:21 +01:00
Benjamin Dauvergne af28eb63f3 models: fix bug in username() when first_name and last_name are not both defined 2013-11-26 10:38:21 +01:00
Benjamin Dauvergne 9a94816476 raise requirement on python-entrouvert 2013-11-26 10:38:20 +01:00
Benjamin Dauvergne bdc61cc169 views: add a get_related_users function to get and cache user linked to the current session 2013-11-26 10:38:20 +01:00
Benjamin Dauvergne 9cd7d79131 add deletion of DeletedDocument objects to the undelete-all command 2013-11-26 10:38:20 +01:00
Benjamin Dauvergne 5c979fb5a7 migrations: provision new model DeletedDocument from Mailbox.deleted field 2013-11-26 10:38:20 +01:00
Benjamin Dauvergne 3133e0ab8e models: new model DeletedDocument 2013-11-26 10:38:20 +01:00
Benjamin Dauvergne 05b9aab7e0 tables: remove the official_recipient table column from the InboxTable
also update translations

refs #3941
2013-11-26 10:38:01 +01:00
Benjamin Dauvergne b6f537c7db jenkins.sh: do not ignore errors 2013-11-20 08:50:51 +01:00
Benjamin Dauvergne 0e10b59fd6 remove debuging statement 2013-11-15 12:29:08 +01:00
Benjamin Dauvergne c884fb8dc8 docbow: username should return user.username when first_name and last_name are both empty
fix bug introduced in f25be11
2013-11-15 12:02:55 +01:00
Benjamin Dauvergne f25be1102f views: fix typo 2013-11-15 11:24:27 +01:00
Benjamin Dauvergne b5928dc7cd pfwb/sendmail: add support for recipients like liste-listes-des-parlementaires@ to send documents to mailing lists
fixes #3959
2013-11-15 11:05:23 +01:00
Benjamin Dauvergne 12fd8cedb4 pfwb/sendmail: clean unused imports 2013-11-15 10:43:50 +01:00
Benjamin Dauvergne 3f16165a42 tables: recipients column cannot be ordered
fixes #3971
2013-11-15 10:41:50 +01:00
Benjamin Dauvergne a3b8fa44ea view: factorize common code in inbox_by_document, outbox_by_document
fixes #3947
2013-11-15 10:36:53 +01:00
Benjamin Dauvergne 5b9105af21 docbow.init: preserve environment when using sudo 2013-11-06 12:47:29 +01:00
Benjamin Dauvergne 667fe6159e settings: add sentry support 2013-11-06 11:59:18 +01:00
Benjamin Dauvergne 6b281270d0 docbow: add missing fedict certificate 2013-11-04 10:28:54 +01:00
Benjamin Dauvergne aa8ceb207b timestamp: add new timestamp provider fedict 2013-10-31 11:52:02 +01:00
Benjamin Dauvergne 060d7cc175 models: urse urlparse.urljoin to build document urls 2013-10-29 15:51:52 +01:00
Benjamin Dauvergne 96d482f48c tests: add test around delegation, first with the *_by_document improvement for multiple delegates
refs #3886
2013-10-29 11:47:52 +01:00
Benjamin Dauvergne 33539b7e4e tests: build a base class to common setUp() operations 2013-10-29 11:47:52 +01:00
Benjamin Dauvergne 2be8998ab1 views: in the *_by_document view, redirect to the first owned mailbox if multiple one are returned or the first no owned mailbox is returned
fixes #3886
2013-10-29 11:47:52 +01:00
Frédéric Péters 60ca929d79 style: update "new message" icon with a darker version (#3899) 2013-10-28 14:35:09 +01:00
Benjamin Dauvergne 5da9a6a7ec pfwb/sendmail: truncate filename coming from expedition at 230 characters
fixes #3870
2013-10-24 11:04:13 +02:00
Benjamin Dauvergne 705a01268f settings: import SECRET_KEY from environment 2013-10-22 12:10:17 +02:00
Benjamin Dauvergne 420d644a2a docbow-ctl: set a SECRET_KEY when launching tests 2013-10-22 11:59:35 +02:00
Benjamin Dauvergne 8a5ac231c2 docbow-ctl: do not always activate the debug log, it breaks the sendmail command 2013-10-22 11:40:08 +02:00
Benjamin Dauvergne e6dcdeb9e9 requirements.txt: rfc3161 release 0.1.8 is bugged, bump requirement to 0.1.9 2013-10-22 11:34:57 +02:00
Benjamin Dauvergne 861b8bca14 start a new application for the pw 2013-10-22 11:33:28 +02:00
Benjamin Dauvergne 3c327e8369 requirements.txt: bump needed version of python-rfc3161 to 0.1.8 2013-10-18 10:58:13 +02:00
Benjamin Dauvergne 1c38b5ff45 signals: do not log modification to model done by code and not an user 2013-10-18 10:57:07 +02:00
Benjamin Dauvergne e68a56f6f2 settings: set an explicit logger to disable the console logger automatically installed by Django 2013-10-18 10:56:37 +02:00
Benjamin Dauvergne d9854f9b96 signals: do not log non user-initiated modifications to models 2013-10-18 10:34:40 +02:00
Benjamin Dauvergne 4ebbcb794e docbow: add a new timestamping provider 2013-10-17 23:02:24 +02:00
Benjamin Dauvergne 658e6bad31 models: fix ordering of User models
fixes #3712
2013-10-17 15:35:22 +02:00
Benjamin Dauvergne 8b13a688cd docbow: log at warning level when unable to timestamp 2013-10-16 18:54:20 +02:00
Benjamin Dauvergne 8725c9b08b views: restrict view /mailing-lists/ to authenticated users 2013-10-15 18:15:59 +02:00
Benjamin Dauvergne 2c45b32d6c pfwb/templates: fix grammatical error 2013-10-15 18:12:05 +02:00
Benjamin Dauvergne 8d38aae523 css: add word-break: break-all on filename row in upload widget 2013-10-15 17:58:52 +02:00
Benjamin Dauvergne e817a0b182 css: set word-break: break-all on the filename column of mailbox tables 2013-10-15 17:51:38 +02:00
Benjamin Dauvergne 331983cc30 models: enlarge the AttachedFile.name field to 300 characters 2013-10-15 17:51:35 +02:00
Benjamin Dauvergne d377b524e2 forms: improve email handling in PasswordResetForm
1. send mail to personal email if it was the email used for the request
2. or if there is no official email,
3. if no email is found skip the user
2013-10-15 17:01:50 +02:00
Benjamin Dauvergne dc8bad4ae9 pfwb: overload template for password reset emails 2013-10-15 15:53:00 +02:00
Benjamin Dauvergne 75354b6a2f templates: fix typo in password_reset_email.html template 2013-10-11 15:02:18 +02:00
Benjamin Dauvergne 2dee46260a auth_backend: complete the DummyUser.save() method 2013-10-11 11:19:14 +02:00
Benjamin Dauvergne 35776f1860 auth_backend: adapt to Django 1.5 change in the login() method, define DummyUser.pk 2013-10-11 11:15:37 +02:00
Benjamin Dauvergne 92f4ddc3b1 templates: update url template tag usage for django 1.5 2013-10-10 17:01:29 +02:00
Benjamin Dauvergne 9046eb762d models: enlarge AttachedFile.content field 2013-10-10 15:10:57 +02:00
Benjamin Dauvergne a44c5e97d4 pfwb/tests: add test on the sendmail command customzed for pfwb 2013-10-08 15:52:25 +02:00
Benjamin Dauvergne 3e1d7f6567 migrations: fix migration 0012 for naive datetime 2013-10-08 15:50:32 +02:00
Benjamin Dauvergne d0db52b5fc filter-widget/SelectFilter2.js: replace js urls by onlick attributes
IE trigger onbeforeunload when cliking on a link with a javascript:
scheme in the URL; it breaks askdirtyform.js.
2013-10-08 13:53:00 +02:00
Benjamin Dauvergne 6b14e1bea4 docbow: use time from trusted timestamps, if available, to timestamp the documents
This commit absolutely requires python-rfc3161 0.1.7, in consequence
requirements.txt was updated.
2013-10-08 12:31:13 +02:00
Benjamin Dauvergne 52ee7f35d9 filter-widget/SelectBox.js: work around missing method Array.indexOf in IE8 2013-10-08 12:30:24 +02:00
Benjamin Dauvergne 4981807201 askdirtyform.js: fix bug in IE which does not like window.onbeforeunload to be set to undefined 2013-10-08 12:29:44 +02:00
Benjamin Dauvergne 3f29d11417 docbow/tests: fix use of assertQuerysetEqual in test_document_accessors 2013-10-07 01:19:14 +02:00
Benjamin Dauvergne f7d7ff5ef3 templates,tables: install and configure refresh.js, fixes #3704 2013-10-03 17:17:50 +02:00
Benjamin Dauvergne 0e9570e41d docbow: add more tests on models methods 2013-10-03 16:47:08 +02:00
Benjamin Dauvergne e0ca0638ce models: fix Document.delivered_to 2013-10-03 16:47:08 +02:00
Frédéric Péters d464db3b47 update and sync django required version number to allow all of 1.5.* 2013-10-02 23:22:36 +02:00
Frédéric Péters b01bf14743 update requirements to match django version from setup.py 2013-10-02 23:19:58 +02:00
Benjamin Dauvergne 0c49c9264a pfwb/sendmail: if name does not end with .pdf add it, fixes #3688 2013-09-30 10:08:48 +02:00
Frédéric Péters a661adf0e3 allow longer filenames 2013-09-27 10:02:02 +02:00
Benjamin Dauvergne 22515225ee models: in Document.post() only notify active users, fixes #3650 2013-09-23 16:50:09 +02:00
Frédéric Péters 6cedb59f4b only disable persons that were deputies or ministers (#3649) 2013-09-23 16:46:04 +02:00
Benjamin Dauvergne ebaa4514fb tools: make postfix_filter.sh use directly the init script 2013-09-20 11:14:01 +02:00
Benjamin Dauvergne dbbb34253f profile_views: do not forget to save the password in PasswordChangeView, fixes #3643 2013-09-20 11:13:10 +02:00
Benjamin Dauvergne 2873db4bb4 docbow.init: fix typo 2013-09-20 10:06:29 +02:00
Benjamin Dauvergne 9bb9a8f3bf docbow.init: make it usable by the docbow user 2013-09-20 10:04:33 +02:00
Benjamin Dauvergne 0b1fc6132f docbow.init: use "$@" not just $@ to pass arguments 2013-09-20 10:02:35 +02:00
Benjamin Dauvergne da22854c55 pfwb: only use the username of the email address in the sendmail command 2013-09-20 09:59:21 +02:00
Pierre Cros d81b822ca9 doc mise à jour avec modifs Gaëtan 2013-09-18 17:56:44 +02:00
Pierre Cros fa39600d98 ajout page notification à la doc 2013-09-18 17:56:44 +02:00
Pierre Cros 4eed01bb2d modifs doc demandées par Gaëtan 2013-09-18 17:56:44 +02:00
Benjamin Dauvergne 040702687d locale: change fr translation for the notification reception checkbox 2013-09-18 14:35:29 +02:00
Benjamin Dauvergne 9325a57b36 add missing migration for field accept_notifications 2013-09-18 11:18:02 +02:00
Benjamin Dauvergne f68d46a94a views: adapt su() view to work with guest delegates 2013-09-17 21:48:56 +02:00
Benjamin Dauvergne 6ded756009 provile_views: hide delegation section for guest delegates, fixes #3634 2013-09-17 21:40:21 +02:00
Benjamin Dauvergne c26751db6b models: do not aggregate delegate recursively when computing the set of users to notify about a document 2013-09-17 21:27:14 +02:00
Benjamin Dauvergne b72fd7cd0a models add field DocbowProfile.accept_notifications to allow user to block notifications, fixes #3635 2013-09-17 21:23:45 +02:00
Benjamin Dauvergne a208f055ed models: augment NotificationManager.notify() to also notify delegates recursively, fixes #3635 2013-09-17 21:07:36 +02:00
Benjamin Dauvergne c37a4e933a views: the profile view must be accessed as the delegate and not as the delegatee, fixes #3634 2013-09-17 20:53:57 +02:00
Benjamin Dauvergne 454a2b1ae3 views: use delegation list to compute recipient limitations, fixes #3633 2013-09-17 20:53:20 +02:00
Benjamin Dauvergne 2de2128b0c views: add recursive list members when computing list limitations 2013-09-17 17:52:20 +02:00
Benjamin Dauvergne 55422f752d views: only show the forwarding form if user limitations allows it to send the document filetype, fixes #3625 2013-09-16 19:16:24 +02:00
Benjamin Dauvergne aeba55812b forms: send mobile phone verification code synchronously 2013-09-14 02:16:40 +02:00
Benjamin Dauvergne a9e6f99de5 template: add query string to export URLs only if there is currently one 2013-09-13 18:54:17 +02:00
Benjamin Dauvergne 76337ddad6 docbow: add new ODS export view for inbox and outbox
This commit includes code copied from w.c.s.

fixes #3621
2013-09-13 18:44:56 +02:00
Benjamin Dauvergne f659d02f99 utils: only take the most used extension for each mime-type 2013-09-13 11:56:22 +02:00
Benjamin Dauvergne 90ab28a292 forms: when creating delegation for existing users, emit errors, fixes #3570
- if the email is one of an existing simple delegation refuse to create
	 it,
 - if the email belong to existing normal users, list them and tell user
	 that it must use the combo box listing existing users,
 - if the email belong to the current user, tell him he cannot delegate
	 to himself.
2013-09-12 18:47:51 +02:00
Benjamin Dauvergne b8d207d037 views: remove repeating users in user recipients form fields when sending limitations are enforced, fixes #3572 2013-09-12 11:28:55 +02:00
Benjamin Dauvergne 739baa6388 forms: use threadind package for sending SMS in background thread, log exceptions 2013-09-12 10:35:32 +02:00
Benjamin Dauvergne 359f099e53 sms_carrier_ovh: restore import urllib 2013-09-11 18:51:34 +02:00
Benjamin Dauvergne 60730b8306 locale: fix typo
fixes #3597
2013-09-11 17:36:47 +02:00
Frédéric Péters 77b0cc9789 sync-from-tabellio: alter types to always start with a capital (#3592) 2013-09-11 17:11:55 +02:00
Benjamin Dauvergne 3fe8f31aa9 utils: read full file when trying do detect mime-type 2013-09-11 16:46:46 +02:00
Benjamin Dauvergne 628a1d01ec utils: read at least 10000 bytes from file for detecting content type 2013-09-11 16:44:49 +02:00
Benjamin Dauvergne abee690dcc sms_carrier_ovh: use urllib2 which correctly handle https proxies 2013-09-11 16:04:24 +02:00
Benjamin Dauvergne a997886429 docbow: add minimum number of files limitation to FileTypeAttachedFileKind model
fixes #3588
2013-09-11 13:29:05 +02:00
Benjamin Dauvergne c725f50c81 views: pass the delegate user as user to the send file form when a guest delegate is sending
fixes #3590
2013-09-11 12:59:11 +02:00
Benjamin Dauvergne 2389b49ca9 tables,locale: explicitely declare the string "self" for translation and restore its translation
ref #3590
2013-09-11 12:29:48 +02:00
Benjamin Dauvergne 78ac477e6e models,views: compute limitations by unioning with limitations from delegators
Only if the delegate had no limitation before the delegation, then no
limitation is applied to him.

fixes #3587
2013-09-11 12:22:28 +02:00
Benjamin Dauvergne 0bc9c7e93c models: change regexp to authorize dots in mime-types
refs #3589
2013-09-11 12:22:28 +02:00
Frédéric Péters b01a1e0539 add a real description for init script 2013-09-11 10:23:02 +02:00
Benjamin Dauvergne 7cf9c2dcdf views: when computing user list after limitations, ignore inactive users 2013-09-10 19:37:41 +02:00
Benjamin Dauvergne ac2323b857 forms: do not clobber the user_qs keyword argument
fixes #3572
2013-09-10 19:37:41 +02:00
Frédéric Péters c365f1a6aa sync-tabellio: sync document types (#3544) 2013-09-10 16:55:06 +02:00
Frédéric Péters 696054321b move sync-tabellio to the pfwb application 2013-09-10 09:46:14 +02:00
Frédéric Péters 4d7c688459 doc: document tabellio synchronisation settings 2013-09-10 09:45:20 +02:00
Frédéric Péters bfae035913 doc: fix underlining length 2013-09-10 09:42:25 +02:00
Frédéric Péters 03a7b1415f sync-tabellio: add a bunch of comments 2013-09-10 09:41:00 +02:00
Frédéric Péters 89a8a7e9d7 sync-tabellio: comment translation 2013-09-10 09:41:00 +02:00
Benjamin Dauvergne cecd8c30d3 pfwb: overload docbow command sendmail()
This command handle specificities about the handling of incoming emails
at the PFWB. It uses six new special application settings.

The loading order of the pfwb application was changed as you can only
overload a command from a previously loaded application.

ref #3542
2013-09-09 22:50:23 +02:00
Benjamin Dauvergne ff43bcdf8b models: use app_settings.BASE_URL instead of setting.DOCBOW_BASE_URL 2013-09-09 18:00:53 +02:00
Benjamin Dauvergne a61dc44c24 docbow: fix typo error in import path in the sendmail command 2013-09-09 18:00:42 +02:00
Benjamin Dauvergne 9f9df45b43 settings: remove duplicate entry 2013-09-09 16:24:24 +02:00
Frédéric Péters c6bd9f7735 sync-tabellio: create and sync mailing lists for political groups (#3545) 2013-09-09 13:16:39 +02:00
Frédéric Péters ddadfa65a7 settings: remove duplicated DOCBOW_BASE_URL 2013-09-09 13:06:56 +02:00
Benjamin Dauvergne 8ec434ed24 settings: define default BASE_URL for the PFWB 2013-09-09 11:22:26 +02:00
Benjamin Dauvergne 1bc81386d6 docbow/app_settings: introduce a BASE_URL setting, use it in sendmail 2013-09-09 11:21:55 +02:00
Benjamin Dauvergne e35f26d4b4 pfwb/models: add new model TabellioDocType 2013-09-09 11:17:32 +02:00
Benjamin Dauvergne b7d2b0cace pfwb: initialize migrations 2013-09-09 11:15:15 +02:00
Benjamin Dauvergne 73d5fe9526 rename application plone to pfwb
It will hold all adaptation to pfwb context
2013-09-09 10:41:41 +02:00
Benjamin Dauvergne c6a5c34d70 tables: add a recipients column to outbox table (CSV and HTML) 2013-09-06 09:28:28 +02:00
Pierre Cros 46551e97e0 Suppression du développement de l'acronyme DocBow sur la page d'accueil 2013-09-05 17:48:23 +02:00
Frédéric Péters 600363b798 create accounts as first.lastname, not the other way 2013-09-05 14:49:20 +02:00
Frédéric Péters 3871abce8f add management command to sync users with tabellio 2013-09-05 14:41:11 +02:00
Benjamin Dauvergne 7d4b313b54 commands/add_user,commands/add-list: fix wrong access name for command flags, add flags for activating deactivating users, and for setting the is_superuser field
fixes #3534
2013-09-04 15:56:37 +02:00
Benjamin Dauvergne 21b99f1a1b commands/list-users: add columns for the is_active and is_superuser flags 2013-09-04 15:56:03 +02:00
Benjamin Dauvergne f3cdb2c626 views: when filtering by date the end date is inclusive, fixes #3442 2013-09-04 12:42:18 +02:00
Benjamin Dauvergne 5880747208 utils: add method to convert date to aware datetime set at 00:00 2013-09-04 12:41:56 +02:00
Benjamin Dauvergne 92ad7b13a4 models,views: take the union of sending limitations applying to an user, not the first one found
It also handles recursive group membership.

fixes #3465
2013-09-03 16:59:54 +02:00
Benjamin Dauvergne f564f43f40 setup.py,requirements.txt: change name of disitribution for python-entrouvert 2013-09-03 16:35:47 +02:00
Benjamin Dauvergne f14f8a6458 docbow.init: add more environment variable when calling management commands 2013-09-03 15:10:44 +02:00
Benjamin Dauvergne 3d9fe34dd7 docbow.init: the -- must precede the command but be after the environment variables declarations 2013-09-02 12:05:09 +02:00
Benjamin Dauvergne 20415669b7 docbow.init: conserve some environment variable when calling sudo 2013-09-02 12:02:55 +02:00
Benjamin Dauvergne c5601cc34d docbow.init: use sudo instead of su 2013-09-02 11:59:16 +02:00
Benjamin Dauvergne 0716b72706 admin,actions: add mass activation/deactivation actions for users and mailing lists, fixes #3494 2013-09-02 11:55:16 +02:00
Benjamin Dauvergne d3f0b4a2b0 admin: restore mass deletion action for users and mailing lists, fixes #3494 2013-08-30 17:24:56 +02:00
Benjamin Dauvergne a1c77dc498 models: set minimum cardinality to 0 2013-08-30 15:25:07 +02:00
Benjamin Dauvergne 9ed7b9b2fa forms: pass a dummy attached_file_kind in case no busness rule exists 2013-08-30 15:20:15 +02:00
Benjamin Dauvergne 305da35b29 locale: escape quotes in messages 2013-08-29 14:14:24 +02:00
Pierre Cros f8f387b181 mise à jour pour le PFWB 2013-08-29 14:09:46 +02:00
Frédéric Péters c03e741420 style: rearrange a bit the profile page 2013-08-29 11:29:01 +02:00
Frédéric Péters 8846eac890 style: a little bit of the profile page 2013-08-29 11:20:56 +02:00
Benjamin Dauvergne 8b876cbb37 static: add missing yelp-code.png 2013-08-29 10:56:33 +02:00
Benjamin Dauvergne fe7de5db9f css: change paths to yelp icons 2013-08-29 10:55:38 +02:00
Benjamin Dauvergne eee5b9b72a static: add missing yelp icons 2013-08-29 10:49:32 +02:00
Frédéric Péters e7f475789b help: force image into their width 2013-08-29 10:48:04 +02:00
Benjamin Dauvergne bfc7d99824 help: add missing figure 2013-08-29 10:26:16 +02:00
Pierre Cros 03c9ffc631 Captures PFWB en remplacement des captures PW 2013-08-29 10:03:27 +02:00
Benjamin Dauvergne 59de435809 locale: change translation of the cardinality field 2013-08-29 09:50:24 +02:00
Benjamin Dauvergne 9b83090986 admin: improve display of horizontal selectors 2013-08-27 22:09:49 +02:00
Benjamin Dauvergne 8b2cd6d5e4 replace matching of file extensions by use of libmagic to identify file types
fixes #3189
2013-08-27 17:20:03 +02:00
Benjamin Dauvergne aeaf5fe5c8 plone: fix style issue 2013-08-27 11:00:20 +02:00
Benjamin Dauvergne 9efde9e0da plone: add the attached file kind to the metadata content 2013-08-27 11:00:08 +02:00
Benjamin Dauvergne d2bded1c7e plone: add the document id to the metadata content 2013-08-27 10:59:47 +02:00
Benjamin Dauvergne cfba863744 plone: prefix metadata and data file name with the document id 2013-08-27 10:59:35 +02:00
Benjamin Dauvergne 890aa5d9e7 load-users-csv: add an --activate flag
It forces activation of users matching the CSV file. It targets only
existing users. New users are always active.
2013-08-27 10:55:20 +02:00
Benjamin Dauvergne 621187dcec remove debugging statement 2013-08-26 16:05:28 +02:00
Benjamin Dauvergne 3b42520975 fixes validation errors on delegate creation, fixes #3470 2013-08-26 11:17:10 +02:00
Benjamin Dauvergne 7ff47aa277 remove legacy mailbox rendering code 2013-08-26 11:17:10 +02:00
Frédéric Péters 22833f80f7 style: update header logo (#3459) 2013-08-20 11:29:34 +02:00
Frédéric Péters f400ff480a style: rebalance column widths (#3460) 2013-08-16 15:24:30 +02:00
Frédéric Péters c7d65e23c2 style: give proper input type to search filter, remove 'empty' icon (#3466) 2013-08-16 11:05:34 +02:00
Frédéric Péters c10937fc9e style: increase a bit table font size (#3464) 2013-08-16 10:32:35 +02:00
Frédéric Péters 68a5c231c7 style: give the whole width to tables 2013-08-16 10:30:22 +02:00
Frédéric Péters baa091b969 style: remove "Welcome" text (#3460) 2013-08-16 10:21:34 +02:00
Frédéric Péters 940578c5fa change default table length to have 20 items (#3464) 2013-08-16 10:12:59 +02:00
Frédéric Péters 3e5e1cff34 change 'Profil' menu entry label (#3461) 2013-08-16 10:10:36 +02:00
Benjamin Dauvergne 861465c597 restore limitations on filetypes, fixes #3439
As filetype are now chosen on a separate page, the filetype limitation
code was broken. This commit implements limitations on the selection
page and enforce the validation on the sending page, redirecting the
user to the selection page if it tries to use a forbidden filetype.
2013-08-15 15:14:26 +02:00
Benjamin Dauvergne 295f0f779c make run.sh cwd blind 2013-08-15 15:14:26 +02:00
Benjamin Dauvergne c41d5ab558 remove hard dependency on python-ldap 2013-08-15 15:14:26 +02:00
Frédéric Péters 380072acc8 style: do not use default true/false icons for sent column (#3438) 2013-08-13 16:43:27 +02:00
Frédéric Péters 92a450a4ec ~fix sorting on sender and official recipients (#3443) 2013-08-13 15:57:18 +02:00
Pierre Cros a65ad60505 Documentation : PW logo removed from the captures 2013-08-13 15:27:53 +02:00
Benjamin Dauvergne b70cedacc0 forms: for content type without a file kind, authorize all filenames (fixes #3411) 2013-08-02 16:35:03 +02:00
Benjamin Dauvergne 75b1014ce2 settings: set logging level for south to INFO 2013-07-01 11:07:41 +02:00
Benjamin Dauvergne 9690cdd637 settings: remove deprecated setting ADMIN_MEDIA_PREFIX 2013-07-01 11:03:58 +02:00
Benjamin Dauvergne 346c60e0c5 migrations: set no_dry_run=True on pure data migration 0020 2013-07-01 10:44:00 +02:00
Benjamin Dauvergne 49aa868590 migrations: fix changed reference to PickledObjectField in an old migration 2013-07-01 10:33:36 +02:00
Benjamin Dauvergne 330969213f jenkins.sh: add a run script for the continuous integration server 2013-07-01 10:27:24 +02:00
Benjamin Dauvergne d72b561f07 requirements.txt: use http for retrieving python-entrouvert, it's the only protocol available on pfwb-preprod 2013-07-01 10:19:45 +02:00
Benjamin Dauvergne 2edf3c89f5 settings: fix typo 2013-07-01 10:17:23 +02:00
Benjamin Dauvergne a7b16920fa settings: remove DJANGO_ prefix for environment variables 2013-07-01 09:51:08 +02:00
Benjamin Dauvergne 50602c1d51 profile_views: add a FullProfileView which multiplex all other profile views
A bit ugly, but it keeps old views as is if we ever need them again.

refs #2922
2013-06-30 02:04:38 +02:00
Benjamin Dauvergne fc50b910c2 profile_views: add prefix to all fields and submit buttons
refs #2922
2013-06-28 16:53:19 +02:00
Benjamin Dauvergne 639687e06f profile_views: reimplement password change view as a class based view
refs #2922
2013-06-28 16:52:50 +02:00
Benjamin Dauvergne 55ec056a4c profile_views: reimplement delegation management as class based view
refs #2922
2013-06-28 16:52:43 +02:00
Benjamin Dauvergne 88e8858f05 views: rewrite profile view as a class based view
refs #2922
2013-06-28 16:51:51 +02:00
Benjamin Dauvergne 1ddc317700 forms: remove debugging statement 2013-06-28 14:23:44 +02:00
Benjamin Dauvergne 9c8877ee94 forms: remove dead import 2013-06-28 14:23:44 +02:00
Benjamin Dauvergne 5e218ec9bb settings: set neutral title for admin site 2013-06-28 14:22:52 +02:00
Benjamin Dauvergne 44397c8236 templates: restore link to root page from admin site title 2013-06-28 14:22:52 +02:00
Benjamin Dauvergne aa010d1a39 update fr translations 2013-06-27 13:31:31 +02:00
Benjamin Dauvergne 6c3ffe7a9e forms: fix typo 2013-06-27 13:29:57 +02:00
Benjamin Dauvergne 285bed77f9 forms: show allowed file patterns in the jquery file upload widget button
refs #2920
2013-06-27 13:26:57 +02:00
Benjamin Dauvergne 014b18a342 migrate from uni_form to crispy_forms as the former is deprecated 2013-06-27 13:12:10 +02:00
Benjamin Dauvergne 8aaabccf0a templates: move in application docbow last remaining templates fomr docbow_project/templates 2013-06-27 12:38:21 +02:00
Benjamin Dauvergne dfa68032ea gitignore: add default dev db and virtualenv 2013-06-27 12:35:13 +02:00
Benjamin Dauvergne 8311965d25 forms: when file type kind are used, add checks on the file name using wildcards
refs #2920
2013-06-27 12:34:10 +02:00
Benjamin Dauvergne 1941fef916 forms: remove dead imports 2013-06-27 12:06:55 +02:00
Benjamin Dauvergne 9c54aba1dd docbow.init: add a manage command 2013-06-27 11:51:52 +02:00
Benjamin Dauvergne dedf729d7e docbow.init: redirect to new setting module 2013-06-27 11:51:39 +02:00
Benjamin Dauvergne 645b88001b settings: report ADMIN setting content in assertion checks errors 2013-06-27 11:51:16 +02:00
Benjamin Dauvergne 4b49c028a6 settings: fix setting of loggers in debug mode 2013-06-27 11:50:43 +02:00
Benjamin Dauvergne 2e803db25e settings: fix parsing of the DJANGO_ADMINS environment variable 2013-06-27 11:50:05 +02:00
Benjamin Dauvergne 187d923706 plone: add french translation to plone application 2013-06-27 02:16:54 +02:00
Benjamin Dauvergne 170020a21c split upload form in two, group uploaded files by kind
refs #2920
2013-06-27 02:15:17 +02:00
Benjamin Dauvergne b421a5d71d admin: in Document listing, show attached files ordered by kind
refs #2920
2013-06-27 02:14:01 +02:00
Benjamin Dauvergne f2f820e499 admin: add management of file types attached files kinds using an inline forms
refs #2920
2013-06-27 02:13:38 +02:00
Benjamin Dauvergne b71e6a83ff models: add field AttachedFile.kind
refs #2920
2013-06-27 02:13:35 +02:00
Benjamin Dauvergne a8aae06688 models: add model FileTypeAttachedFileKind
refs #2920
2013-06-27 02:12:30 +02:00
Benjamin Dauvergne 3ef5369a34 models: remove auto_now=True flag from Mailbox.date field 2013-06-27 00:08:12 +02:00
Benjamin Dauvergne bb2573f48c Makefile: dump metadata using natural keys 2013-06-27 00:07:20 +02:00
Benjamin Dauvergne ccdb7f52f9 move locale files into the docbow application 2013-06-27 00:07:20 +02:00
Benjamin Dauvergne 3d14ef3c37 templatetags: move DOCBOW_MENU setting into the app_settings module 2013-06-27 00:07:20 +02:00
Benjamin Dauvergne 718c7615e0 allow sorting listing through column headers, filtering by date range and exporting as CSV
this commit adds django-tables2 as a dependency

fixes #2923
2013-06-27 00:07:20 +02:00
Benjamin Dauvergne c9081ed4bf urls: move upload views into their own urls file 2013-06-27 00:07:19 +02:00
Benjamin Dauvergne 0304e15ce0 models: fix datetime handling to be timezone aware 2013-06-27 00:07:12 +02:00
Benjamin Dauvergne 68fd4ba23c run.sh: remove call to deactivate shell function 2013-06-26 08:44:58 +02:00
Benjamin Dauvergne e36e51ab13 move auth_urls into the docbow application 2013-06-25 13:53:41 +02:00
Benjamin Dauvergne 809ae1b2ea move auth_backend module into the docbow application 2013-06-25 13:53:41 +02:00
Benjamin Dauvergne b41f3b3ebd remove unused module docbow_project.utils 2013-06-25 13:53:41 +02:00
Benjamin Dauvergne 5345916cce move module email_utils into the docbow application 2013-06-25 13:53:41 +02:00
Benjamin Dauvergne 5e8eb090d5 move module sms_carrier_ovh in the docbow application 2013-06-25 13:53:40 +02:00
Benjamin Dauvergne b1bf9a0d27 remove old settings modules 2013-06-25 13:53:40 +02:00
Benjamin Dauvergne 67fe778612 move actions module into the docbow application 2013-06-25 13:53:40 +02:00
Benjamin Dauvergne ac1f905736 remove unused module docbow_project.prod 2013-06-25 13:53:40 +02:00
Benjamin Dauvergne b40475f20b move unicodecsv module into docbow application 2013-06-25 13:53:40 +02:00
Benjamin Dauvergne 984bff1375 remove the views module 2013-06-25 13:53:40 +02:00
Benjamin Dauvergne ec5d5d40e0 views: move all views into the docbow application 2013-06-25 13:53:40 +02:00
Benjamin Dauvergne 5c36e7f88a remove the forms module at the project level 2013-06-25 13:53:40 +02:00
Benjamin Dauvergne 7898fcd7ff forms: move all form into the docbow application 2013-06-25 13:53:40 +02:00
Benjamin Dauvergne 2ee8242f8c clean unused imports 2013-06-25 13:53:39 +02:00
Benjamin Dauvergne 56c7871f9b local_settings.py.example: comment out email backend settings 2013-06-25 13:53:39 +02:00
Benjamin Dauvergne d3cad38418 README: add a bootstrap section for developpers 2013-06-25 13:53:39 +02:00
Benjamin Dauvergne d9d0f043b5 fixtures: remove logline related permissions 2013-06-25 13:53:39 +02:00
Benjamin Dauvergne b23e0cbdc7 remove dead filter module 2013-06-25 13:53:39 +02:00
Benjamin Dauvergne c90b7be746 settings: set a default SECRET_KEY in debug mode 2013-06-25 13:53:39 +02:00
Benjamin Dauvergne cafd11b1fa simplify settings, remove log application, add development scripts 2013-06-25 13:53:39 +02:00
Benjamin Dauvergne 34ca15ec9b settings: to comply with Django 1.5 set ALLOWED_HOSTS to * 2013-06-24 15:10:41 +02:00
Benjamin Dauvergne c277285882 settings: do not send broken links emails 2013-06-24 15:10:41 +02:00
Benjamin Dauvergne e10550b11e plone: new app implementing a docbow plugin for exporting posted documents to plone GED portal 2013-03-01 14:56:28 +01:00
Benjamin Dauvergne c2e7973448 commands: remove incompatibilities with python < 2.7 in format string syntax 2013-02-20 17:38:18 +01:00
Benjamin Dauvergne 7ecf2c52ac limit usable mailing list to active ones everywhere, allow editing the is_active field in the admin 2013-02-20 17:07:07 +01:00
Benjamin Dauvergne 1047594809 models: add new field MailingList.is_active 2013-02-20 16:59:57 +01:00
Benjamin Dauvergne 2ec3842279 models: allows FileType.is_active to be False 2013-02-20 16:57:40 +01:00
Benjamin Dauvergne 483f699ab2 update fr translations 2013-02-20 15:43:47 +01:00
Benjamin Dauvergne 4db7b8b60a update fr translations 2013-02-20 15:42:45 +01:00
Benjamin Dauvergne f7a9650ffa add link to document on reply rows of listing 2013-02-20 15:38:52 +01:00
Benjamin Dauvergne 3fbc95b4c7 views: show replies to received documents 2013-02-20 15:38:52 +01:00
Benjamin Dauvergne 5aa1df2e02 templatetags: add new filter to show document real sender name 2013-02-20 15:38:52 +01:00
Benjamin Dauvergne 006450652f views/forms: add a reply-to button 2013-02-20 15:38:52 +01:00
Benjamin Dauvergne 04d5b8e684 views/forms: do not show user which already have a delegation in existing users listing 2013-02-20 14:16:23 +01:00
Benjamin Dauvergne 01828bf0e6 views/forms: exclude current user from existing user listing in the delegation form 2013-02-20 14:14:16 +01:00
Benjamin Dauvergne d418312d10 update fr translations 2013-02-20 14:04:05 +01:00
Benjamin Dauvergne b5433c0103 forms/views: adapt forwardingform for delegate accounts 2013-02-20 13:59:35 +01:00
Benjamin Dauvergne 6ca73dbb60 views/forms: handle default sender for documents in the FileForm 2013-02-20 11:38:34 +01:00
Benjamin Dauvergne 33bf565bdc templatetags: do not show delegation view to guest accounts 2013-02-20 11:38:06 +01:00
Benjamin Dauvergne 95df253632 models: add is_guest function 2013-02-20 11:31:31 +01:00
Benjamin Dauvergne e9d81a3356 add columns to show real sender, official sender and official recipient
refs #2437
2013-02-20 11:12:54 +01:00
Benjamin Dauvergne e299beeca4 js: in the send_file form keep ordering of recipients when moving them around 2013-02-19 18:47:13 +01:00
Benjamin Dauvergne f720731a45 js: do not block form submitting 2013-02-19 18:14:18 +01:00
Benjamin Dauvergne a19ef59eb5 fix spelling 2013-02-19 18:12:07 +01:00
Benjamin Dauvergne 2a787c4cb1 profile: add a "clear sms alert" button 2013-02-19 18:11:25 +01:00
Benjamin Dauvergne 5333153ca7 templates: in mailing-liss.html show username when member does not have a first name and a last name 2013-02-19 17:52:16 +01:00
Benjamin Dauvergne e90440ff11 forms: remove extra Media declaration 2013-02-19 17:49:11 +01:00
Benjamin Dauvergne c917d53791 add missing js file from last commit 2013-02-19 17:31:47 +01:00
Benjamin Dauvergne 7bd6e4f0f6 js: show confirmation dialog when leaving a completed form without saving, fixes #2347 2013-02-19 17:31:21 +01:00
Benjamin Dauvergne e2f788ed67 templates: in the new delegation message, do not try to follow relation as the user can already have been deleted 2013-02-19 15:45:37 +01:00
Benjamin Dauvergne e79d99d48b replace plateforme by plate-forme everywhere 2013-02-19 15:40:33 +01:00
Benjamin Dauvergne 64c4b3fea1 templates: complete mail subject with origin indication 2013-02-19 15:39:48 +01:00
Benjamin Dauvergne 1bd8160c60 templates: use short organization name in sms template if possible 2013-02-19 15:33:38 +01:00
Benjamin Dauvergne 142be78892 locale: fix wrong variable name in translated message 2013-02-19 15:26:40 +01:00
Benjamin Dauvergne f4c05dd8d6 templates: add missing endautoescape tag 2013-02-19 10:17:03 +01:00
Benjamin Dauvergne 4f14353a20 add an example for local_settings.py 2013-02-19 10:13:46 +01:00
Benjamin Dauvergne 147ba89c8f admin: improve user listing
- add flag for guest accounts
 - add list of delegations received
2013-02-11 13:18:56 +01:00
Benjamin Dauvergne 01cbe9d7db forms: queryset shown in the user field of the delegation is stale, fix #2494 2013-02-11 13:16:35 +01:00
Benjamin Dauvergne d947d8e88a templates: improve templates notification of delegation deletion, refs #2493 2013-02-11 13:00:03 +01:00
Benjamin Dauvergne 9efab4e707 static: add missing js files, refresh.js 2013-02-11 12:59:21 +01:00
Benjamin Dauvergne f72ded2eea views: fix message for new guest delegates, refs #2424 2013-02-07 17:19:03 +01:00
Benjamin Dauvergne d4738c5805 views: do not show empty lists on the mailing lists page 2008-02-03 00:07:23 +01:00
Benjamin Dauvergne 22f75e68b1 style: collapse border of delegation table 2013-02-05 14:17:08 +01:00
Benjamin Dauvergne 86428d1961 views: do not let browsers cache page for logged in users, fixes #2421 2013-02-05 13:54:14 +01:00
Benjamin Dauvergne fe1ee463f0 change module name for decorators 2013-02-05 13:54:01 +01:00
Benjamin Dauvergne db36448b1f settings: use a less real default from email 2013-01-31 23:20:10 +01:00
Benjamin Dauvergne 704788d513 Makefile: exclude south when creating a dump of the database 2013-01-31 23:19:23 +01:00
Benjamin Dauvergne 8cad7fa0ec README: add a section on template customization 2013-01-31 23:17:24 +01:00
Benjamin Dauvergne 38d6d77398 settings: add /var/lib/docbow/templates/ as a place to overload default templates 2013-01-31 23:13:55 +01:00
Benjamin Dauvergne 6359b7a70e add /var/lib/docbow/extra_static/ as a place to hold extra static files 2013-01-31 23:13:14 +01:00
Benjamin Dauvergne 6729d6de99 README: add a settings section 2013-01-31 23:13:11 +01:00
Benjamin Dauvergne d5c9a368d9 commands: add new commands to administrate list and users from the command line 2013-01-31 23:05:00 +01:00
Benjamin Dauvergne 0dbe93f296 models/template: improve all email notifications templates
settings was already passed to notification templates, use it to get to
the ORGANIZATION variable.

New file to hold the signature of the platform were added.
2013-01-31 22:58:48 +01:00
Benjamin Dauvergne db1ec038dd templates: improve new delegation templates, fixes #2424 2013-01-31 22:36:14 +01:00
Benjamin Dauvergne b63bc95fff remove grappelli templates 2013-01-31 16:58:06 +01:00
Benjamin Dauvergne 21ce921301 models/templates: add an organization variable to the new-document notifications templates, fixes # 2013-01-31 16:56:50 +01:00
Benjamin Dauvergne 9bf7e704ca models: do not fail on timestamping errors, just log them 2013-01-31 15:34:51 +01:00
Benjamin Dauvergne 42d6d62bbc timestamp: convert all internal exceptions to TimestampingError 2013-01-31 15:31:51 +01:00
Benjamin Dauvergne 562f0d6ac3 change profile menu french translation, fixes #2439 2013-01-31 15:31:05 +01:00
Benjamin Dauvergne 8cca8e5fd9 forms: in FileForm, fix error when sender field is removed 2013-01-30 21:01:18 +01:00
Benjamin Dauvergne d2ad9b7052 forms: update translation of the Enter SMS code message, fixes #2414
Also move the initialization of the crispy form layout for the profile
form as it is modified by the clean_mobile_phone() method.
2013-01-30 14:13:28 +01:00
Benjamin Dauvergne b61847abe4 forms: in ProfileForm give a placement to the sms code field, fixes #2415 2013-01-30 14:02:26 +01:00
Benjamin Dauvergne 24228a4d56 models/forms: validate formatting of the mobile phone, fixes #2413 2013-01-30 14:01:38 +01:00
Benjamin Dauvergne ce31f38d7d views: when creating a delegate from an existing email extract notification emails from the user not the form, fixes #2417 2013-01-30 01:00:43 +01:00
Benjamin Dauvergne 5ff132e192 remove debugging statements 2013-01-28 21:51:50 +01:00
Benjamin Dauvergne 4a3e43d589 models: replace Delegation.guest_delegate by an attribute on DocbowProfile named is_guest 2013-01-28 01:49:50 +01:00
Benjamin Dauvergne 737d726205 views: remove all user of transaction.commit_on_success
We use the Transaction middleware now.
2013-01-23 19:31:39 +01:00
Benjamin Dauvergne ed2eb7e7d3 forms,models: add guest_users() and non_guest_users() methods
use them when needing to list guest and non guest users.
2013-01-23 19:31:14 +01:00
Benjamin Dauvergne 01c1e2bf88 views: remove debugging statement 2013-01-23 18:02:43 +01:00
Benjamin Dauvergne a3efef8dbd admin: using horizontal selector widget for mailing list members of mailing lists 2013-01-18 16:20:14 +01:00
Benjamin Dauvergne cdbda4981d templates: fix title in admin template 2013-01-18 15:09:31 +01:00
Benjamin Dauvergne 4b0da894e3 templates: add template for adding link to homepage from admin
fixes #2386
2013-01-18 15:07:21 +01:00
Benjamin Dauvergne a5441e09e2 models: record to which list user belong when posting 2013-01-18 14:50:17 +01:00
Benjamin Dauvergne f9ab1bcf6c views: override the user argument in request.record, when logging delegate actions 2013-01-18 14:24:17 +01:00
Benjamin Dauvergne 40fa0c568f views: prevent testing for empty sender to raise DoesNotExist exception 2013-01-18 13:51:35 +01:00
Benjamin Dauvergne e495a62ff0 commands: add a sendfile command 2013-01-18 10:09:04 +01:00
Benjamin Dauvergne c6fe18e331 templates: show last connection even for non guest accounts (potential security problem) 2013-01-18 10:09:04 +01:00
Benjamin Dauvergne dddb29c1a0 views: enable forwarding for everybody 2013-01-17 19:05:50 +01:00
Benjamin Dauvergne 88efae4503 admin: supress all delete actions in action menus 2013-01-17 18:40:17 +01:00
Benjamin Dauvergne 315871b8f2 models: fix DocumentForwarded.__unicode__ 2013-01-17 18:19:39 +01:00
Benjamin Dauvergne 7e1077f4fb templatetags: allows URL when configuring the main menu 2013-01-17 18:19:18 +01:00
Benjamin Dauvergne fd76665b42 css: augment scaling of file upload button to cover the complete upload button
fixes #2370
fixes #2049
2013-01-17 17:55:10 +01:00
Benjamin Dauvergne ddf7c14be2 forms: fix traceback in FileForm 2013-01-17 17:54:14 +01:00
Benjamin Dauvergne 0085518029 views: add logic to send document as another person 2013-01-17 15:57:54 +01:00
Benjamin Dauvergne b455829b95 update .gitignore 2013-01-17 14:32:27 +01:00
Benjamin Dauvergne ed5df6fc37 undelete-all: delete DeletedMailbox objects too 2013-01-17 14:31:59 +01:00
Benjamin Dauvergne 9d80245b7f views: provide deletion to message shown for a delegation 2013-01-17 14:03:19 +01:00
Benjamin Dauvergne 18c022785d add target to makefile to dump db metadatas
This commit also add a rewrite of dumpdata as it does not support
circular dependencies upon models, which we need to handle lists of
lists.
2013-01-17 12:16:59 +01:00
Benjamin Dauvergne 4d4c5d3c7f update Makefile 2013-01-17 11:20:40 +01:00
Benjamin Dauvergne 2ef5c92743 models/views: limit choices of filetype to active ones (fixes #2343) 2013-01-16 00:08:26 +01:00
Benjamin Dauvergne 4648422925 locale: update translations 2013-01-16 00:01:01 +01:00
Benjamin Dauvergne 2e898ead73 admin: show FileType.is_active in list display 2013-01-15 23:59:27 +01:00
Benjamin Dauvergne 1aeb1744d6 models: add an is_active field to FileType 2013-01-15 23:58:44 +01:00
Benjamin Dauvergne 8b549d9eed migrations: add missing migration in last commit 2013-01-15 23:56:40 +01:00
Benjamin Dauvergne 781009babc models: add a model to hold deleted mailbox entries for delegates 2013-01-15 23:54:52 +01:00
Benjamin Dauvergne 36e68a41af views: display delegators messages 2013-01-15 23:52:03 +01:00
Benjamin Dauvergne 35c03666f3 remove unused import 2013-01-15 22:59:18 +01:00
Benjamin Dauvergne fba0e5d8fd admin do not show guest delegate users in user selection fields 2013-01-15 17:34:41 +01:00
Benjamin Dauvergne 5508fb30b1 admin/forms: do not show guest delegate accounts in user select fields 2013-01-15 16:56:39 +01:00
Benjamin Dauvergne f670a3bb65 models: add __unicode__ to all models 2013-01-15 16:56:35 +01:00
Benjamin Dauvergne 2efdc8a08f admin: improve display of delegation objects 2013-01-15 16:51:08 +01:00
Benjamin Dauvergne 69e8eab0de migrations: fix migrations with respect to new implementation of PickledObjectField 2013-01-15 15:30:56 +01:00
Benjamin Dauvergne 9a0c068ffb views: fix get_file_form_kwargs if there is more than one list 2013-01-15 14:39:13 +01:00
Benjamin Dauvergne a2d005b18b remove local implementation of PickledObjectField use the one from pypi 2013-01-15 14:22:18 +01:00
Benjamin Dauvergne a5fa41d06b notification: do not choke on missing emails 2013-01-15 12:19:04 +01:00
Benjamin Dauvergne 9bdbea556c css: fix arrow cursor over upload button (fixes #2340) 2013-01-15 12:19:04 +01:00
Benjamin Dauvergne 47d44fbf6d settings: remove dobow_project.log 2013-01-15 12:18:37 +01:00
Benjamin Dauvergne 3c5716714d Revert "views: temp fix to help pages"
This reverts commit 987f09bcdf.
2013-01-15 08:47:09 +01:00
Benjamin Dauvergne 5510b3c6e4 admin: restore DocbowGroup proxy model admin 2013-01-14 22:42:19 +01:00
Benjamin Dauvergne 987f09bcdf views: temp fix to help pages 2013-01-14 17:25:50 +01:00
Benjamin Dauvergne 26bf7c17d0 css: style delegated messages 2013-01-14 17:19:39 +01:00
Benjamin Dauvergne 8921cbdcbd forms: fix call to sms carrier send 2013-01-14 17:11:18 +01:00
Benjamin Dauvergne 4ce927c23e admin: fix format string error 2013-01-14 16:55:25 +01:00
Benjamin Dauvergne bc3f6e16a6 views: optimize listing 2013-01-14 16:53:10 +01:00
Benjamin Dauvergne dc1761b187 views: add missing tag to record call 2013-01-14 16:40:45 +01:00
Benjamin Dauvergne 1fcc819b0e views: show received delegations 2013-01-14 16:37:04 +01:00
Benjamin Dauvergne 3cf106bd94 merge 2013-01-14 16:32:10 +01:00
Benjamin Dauvergne 001b95f75a notification: fix typo 2013-01-14 16:24:59 +01:00
Benjamin Dauvergne 16d637056c forms: support personal email in password reminder form 2013-01-14 16:00:16 +01:00
Benjamin Dauvergne 9006189b2a views: allow delegate to see message from the delegator 2013-01-14 15:27:41 +01:00
Benjamin Dauvergne 89ce0239ae move manage.py at the root 2013-01-14 15:27:41 +01:00
Benjamin Dauvergne 081aff6d60 templates: reformat listing.html 2013-01-14 15:27:36 +01:00
Frédéric Péters 4a7abd4e63 help: integrate mallard documentation 2013-01-14 10:29:29 +01:00
Frédéric Péters 404b4957f1 removed unused imports 2013-01-06 16:26:05 +01:00
Frédéric Péters e4e6eaeee9 style fixes 2013-01-06 16:25:55 +01:00
Frédéric Péters 8c5fa1861d indentation fixes 2013-01-06 16:25:43 +01:00
Benjamin Dauvergne 56ca249c42 locale: update translation (fixes #1845) 2013-01-03 15:54:18 +01:00
Benjamin Dauvergne 20f8b751bc views: clean ldap users before giving it to the notification system 2013-01-03 15:16:09 +01:00
Benjamin Dauvergne fed4229da0 views: remove debugging statement 2013-01-03 15:11:55 +01:00
Benjamin Dauvergne 052b3b801a views: when showing delegate last connection use last_login if journals is empty
Also:
 - prevent showing last connection for non guest account delegates,
 - show at most last 5 connections
2013-01-03 13:09:02 +01:00
Benjamin Dauvergne de3746db02 views: implemented delegation to existing users 2012-12-21 02:41:13 +01:00
Benjamin Dauvergne 1d9c871a67 models: changed max_lenth of field Notification.kind 2012-12-21 02:40:46 +01:00
Benjamin Dauvergne 1031f45e0f views: improved display of delegates last connection 2012-12-21 00:34:17 +01:00
Benjamin Dauvergne 9a23f85d0e templates: in mailing-lists.html change header of lists index 2012-12-21 00:21:12 +01:00
Benjamin Dauvergne 27cff16031 views: added mailing_lists view 2012-12-21 00:17:39 +01:00
Benjamin Dauvergne b12d85be18 signals: improved formatting of journalisation of model modifications 2012-12-20 18:45:41 +01:00
Benjamin Dauvergne fd55a9a8b7 signals: fixed typo 2012-12-20 18:43:56 +01:00
Benjamin Dauvergne 90aa7c5abf signals: improved journalisation of model modifications 2012-12-20 18:42:55 +01:00
Benjamin Dauvergne 9eae5c1ab8 add new management command notify 2012-12-20 18:37:40 +01:00
Benjamin Dauvergne 37ceae83de views: show last connection in delegation view 2012-12-20 18:33:07 +01:00
Benjamin Dauvergne 253bf21615 signals: removed signals linked to DocbowUser and DocbowGroup 2012-12-20 18:33:07 +01:00
Benjamin Dauvergne 676feab2d2 models: improved a docstring 2012-12-20 18:33:07 +01:00
Benjamin Dauvergne 9b185b5d42 admin: improved User column presentation in JournalAdmin listing 2012-12-20 18:33:07 +01:00
Benjamin Dauvergne 628da83b25 admin: restored User and Group instead of DocbowUser and DocbowGroup in admin 2012-12-20 18:33:07 +01:00
Benjamin Dauvergne 904d84433c signals: fixed journalisation of login and logout to report delegates login 2012-12-20 18:33:07 +01:00
Benjamin Dauvergne 441145fa04 notification: use Notification.ctx when processing notifications
It is used for templating, and also to find recipients adresses and
reply-to headers.
2012-12-20 18:33:07 +01:00
Benjamin Dauvergne e073d2fa7a notifications: fixed journalisation 2012-12-20 18:33:07 +01:00
Benjamin Dauvergne 7ed824eae6 notification: fixed wrong path for DocbowProfile.DoesNotExist 2012-12-20 18:33:07 +01:00
Benjamin Dauvergne bdf10c9d2c models: added Notification.__unicode__ 2012-12-20 18:33:07 +01:00
Benjamin Dauvergne 54399353bc models: prevented creation of delegations with same user and delegate 2012-12-20 18:33:07 +01:00
Benjamin Dauvergne 4c77f503e0 admin: set Notification.ctx as read-only 2012-12-20 18:33:07 +01:00
Benjamin Dauvergne daf28ec36d models: added Notification.ctx field to hold additional template context 2012-12-20 18:33:06 +01:00
Benjamin Dauvergne f35160b092 templates: renamed delegation notification templates 2012-12-20 18:33:06 +01:00
Benjamin Dauvergne 00b18928d0 removed obsolete notification module 2012-12-20 18:33:06 +01:00
Benjamin Dauvergne 33c7abea6e models_fields: added a PickledObjectField 2012-12-20 18:33:06 +01:00
Benjamin Dauvergne b0b37f13fa auth_backends: prevented non guest delegates to login as a delegate 2012-12-20 18:18:20 +01:00
Benjamin Dauvergne d726db2c26 locale: update fr translations 2012-12-20 10:58:22 +01:00
Benjamin Dauvergne 90a20ea5c0 signals: modified logging, use django_journal instead of logging 2012-12-19 22:03:13 +01:00
Benjamin Dauvergne b42f99dbd5 models: fixed python 2.6 compatibility 2012-12-19 17:34:04 +01:00
Benjamin Dauvergne 8a63c68330 tests: removed debugging statements 2012-12-19 17:33:50 +01:00
Benjamin Dauvergne f747ddca33 update requirements 2012-12-19 17:14:19 +01:00
Benjamin Dauvergne 82e8d81f3c management: new command archive2 to archive documents and journal lines before a certain day
fixes #1851
2012-12-16 11:02:41 +01:00
Benjamin Dauvergne 99c4539fda models: add a personal email field to the profile
Notifications will also be sent to this address.

fixes #1845
2012-12-16 10:36:35 +01:00
Benjamin Dauvergne a2a5757f40 models: documnt DocbowProfil, Notification and NotificationManager 2012-12-16 10:15:58 +01:00
Benjamin Dauvergne 0f7ac5ce4b admin: hide is_staff field in user admin
fixes #1971
2012-12-16 10:11:40 +01:00
Benjamin Dauvergne c80fb5ce9f models: automatically set and unset is_staff if an user is in some group
fixes #1971
2012-12-16 10:11:22 +01:00
Benjamin Dauvergne c20ba71c19 locale: update fr translations 2012-12-16 09:58:18 +01:00
Benjamin Dauvergne 89f1185b6f templates: show if a message was forwarded automatically or manually 2012-12-16 09:57:24 +01:00
Benjamin Dauvergne a1c9432910 views: add a singular form of the summary message following a document sending 2012-12-16 09:54:31 +01:00
Benjamin Dauvergne d27705fa2f models: added logging to journal of automatic forwarding rules execution 2012-12-16 09:53:07 +01:00
Benjamin Dauvergne be8dddf83f models: improved method AutomaticForwarding.__unicode__
It is more precise now.
2012-12-16 09:52:04 +01:00
Benjamin Dauvergne b7c9eadb1a models: moved timestamping before delivery for logic sake 2012-12-16 09:51:02 +01:00
Benjamin Dauvergne 9455805aa0 models: fixed typo 2012-12-14 15:46:47 +01:00
Benjamin Dauvergne 74ec1928cc settings: use the transaction middleware 2012-12-14 15:45:54 +01:00
Benjamin Dauvergne f92bdba5fb models: model manager is not accessible from an instance 2012-12-14 15:45:42 +01:00
Benjamin Dauvergne f4b9e778a4 sms_carrier_ovh: lower credit limit before alerting administrators 2012-12-14 15:42:17 +01:00
Benjamin Dauvergne 26f886db44 sms_carrier_ovh: "to" is a list 2012-12-14 15:41:55 +01:00
Benjamin Dauvergne 77e56fbf6b sms_carrier_ovh: log sucessfull sms sending 2012-12-14 15:41:35 +01:00
Benjamin Dauvergne cea04d264d forms: use the new sms notification classes for sending sms 2012-12-14 15:40:30 +01:00
Benjamin Dauvergne bed0c5eb15 notification: improve logging of errors for email notifications 2012-12-14 15:39:58 +01:00
Benjamin Dauvergne c41ecae2e3 notification: do not fail at the first exception in SMSNotifier.finish 2012-12-14 15:39:29 +01:00
Benjamin Dauvergne 306e1723df notification: add a method to retrieve the current SMS carrier 2012-12-14 15:39:02 +01:00
Benjamin Dauvergne 92db598562 notification: remove newline from subject template 2012-12-14 15:38:27 +01:00
Benjamin Dauvergne b65a8b9f48 models: add method Document.url for procuding URL for documents in templates 2012-12-14 15:38:00 +01:00
Benjamin Dauvergne e69a75848c models: improve access to Document model by prefetching all related fields 2012-12-14 15:37:38 +01:00
Benjamin Dauvergne 718617550d admin: completed Notification.retry action by launching process of notifications in a thread 2012-12-14 15:36:40 +01:00
Benjamin Dauvergne 63724b3c25 settings: removed log db handlers 2012-12-14 15:36:01 +01:00
Benjamin Dauvergne 847a70f55c templates: rename sms and email notification templates 2012-12-14 15:34:24 +01:00
Benjamin Dauvergne 998056f065 git: allow committing compiled translation files 2012-12-14 11:57:50 +01:00
Benjamin Dauvergne 76caf4f326 locale: update fr translations 2012-12-14 11:57:50 +01:00
Benjamin Dauvergne 2acf76d1b6 views: use .format for easier translation 2012-12-14 11:57:50 +01:00
Benjamin Dauvergne 8434b9c913 models: added field Notification.create_dt 2012-12-14 11:57:50 +01:00
Benjamin Dauvergne 086332173f admin: registerd Notification model 2012-12-14 11:57:50 +01:00
Benjamin Dauvergne 57a2cfb1cd sms: removed the module 2012-12-14 11:57:50 +01:00
Benjamin Dauvergne 5fb3b70e9e git: hide media and mail directories 2012-12-14 11:57:50 +01:00
Benjamin Dauvergne 6d8c7ff99c css: fixed wrong url 2012-12-14 11:57:50 +01:00
Benjamin Dauvergne 28271ec431 views: removed notification, posting and forwarding custom logic
The new implementations of the post and forwarding views use methods
from the models.

Notification is handled through the notification framework.
2012-12-14 11:57:50 +01:00
Benjamin Dauvergne 0face19259 views: reworked method to list users able to forward and to verify is a user can forward 2012-12-14 11:57:50 +01:00
Benjamin Dauvergne 48fc720783 sms_carrier_ovh: added module implementing the OVH HTTP2SMS as a SMS carrrier for the notification framework 2012-12-14 11:57:50 +01:00
Benjamin Dauvergne f16253a551 notification: added new module for handling asynchronous notifications 2012-12-14 11:57:50 +01:00
Benjamin Dauvergne 2dde59ec9d models: simplified Document.__unicode__() 2012-12-14 11:57:50 +01:00
Benjamin Dauvergne ac7a96f606 forms: hidden the _timestamp field in the FileForm 2012-12-14 11:57:50 +01:00
Benjamin Dauvergne 81488745cf timestamp: raised an exception when an error is returned by the timestamping API 2012-12-14 11:57:50 +01:00
Benjamin Dauvergne 54394590a6 settings: removed unused configuration variables 2012-12-14 11:57:49 +01:00
Benjamin Dauvergne d0174e750a models: moves journalisation, timestamping, automatic forwarding and notification in the Document.post method
It completely replaces most of the implementation of views.send_file.
2012-12-14 11:57:49 +01:00
Benjamin Dauvergne 69badce6de models: added AutomaticForwarding.try_forwarding
It replaces the implementation from views.
2012-12-14 11:57:49 +01:00
Benjamin Dauvergne 83ae83e20c models: added Document.forward method
It replaces the implementation in views
2012-12-14 11:57:49 +01:00
Benjamin Dauvergne edfd13d5aa models: use the optional "to" parameter in Document.timestamp_blob 2012-12-14 11:57:49 +01:00
Benjamin Dauvergne 53d2df944d models: use django_journal 2012-12-14 11:57:49 +01:00
Benjamin Dauvergne 5b7bd9ed0b models: added "to" parameter to Document.timestamp_blob 2012-12-14 11:57:49 +01:00
Benjamin Dauvergne aebcca4ea1 models: added a "timestamp" method to Document
It returns the saved timestamp from the _timestamp field of the document
if the field is not empty, otherwise it requests a new timestamp.
2012-12-14 11:57:49 +01:00
Benjamin Dauvergne 8907e2a157 models: added method delivered_to which returns the real recipients of a document
It's computed using the Mailbox models.

Recipients computed from the to_list or to_user field of the document can
change if the list members are modified or the is_active flag of a user
is changed.
2012-12-14 11:57:49 +01:00
Benjamin Dauvergne eea0a92adb models: added forward argument to the "post" method of Document 2012-12-14 11:57:49 +01:00
Benjamin Dauvergne 79c8279be0 models: rewritten Document.to to resolve recursive groups
As groups can now contain other groups, you must use .recursive_members()
to obtain the list of members of a group.

fixes #1843
2012-12-14 11:57:49 +01:00
Benjamin Dauvergne 3c9aaa3afb models: removed auto_now flag from the documentforwarded.date field
The auto_now_add flag is kept.
2012-12-14 11:57:49 +01:00
Benjamin Dauvergne 0e314451f0 models: added new permission can_forward to the document model
It targets users authorized to forward documents; it replaces the explicit
list of user and groups actually in the code.
2012-12-14 11:57:49 +01:00
Benjamin Dauvergne e6cd0a8a8a models: added _timestamp field to Document 2012-12-14 11:57:49 +01:00
Benjamin Dauvergne 39baef7ec3 models: add a notification model 2012-12-14 11:57:49 +01:00
Benjamin Dauvergne 015ecd3173 templates: fixed url of the trashcan icon 2012-12-14 11:57:49 +01:00
Benjamin Dauvergne 1a881e6356 templates: use {% autoescape off %} for all text templates (mail and sms) 2012-12-14 11:57:49 +01:00
Benjamin Dauvergne dadc549b7d forms: fix cleaning of the mobile phone 2012-12-14 11:57:48 +01:00
Benjamin Dauvergne c073fb0ab2 models: mobile_phone is optional 2012-12-14 11:57:48 +01:00
Benjamin Dauvergne bb39f94865 views: removed unused import 2012-12-14 11:57:48 +01:00
Benjamin Dauvergne d76ba16399 view: records modification to profiles 2012-12-14 11:57:48 +01:00
Benjamin Dauvergne a0ad4deee3 views: added "su" view 2012-12-14 11:57:48 +01:00
Benjamin Dauvergne 3efaa1c8cc settings: use django_journal 2012-12-14 11:57:48 +01:00
Benjamin Dauvergne e35d7d702c remove journal 2012-12-14 11:57:48 +01:00
Benjamin Dauvergne a5dc6c46aa templates: remove useless script tag 2012-12-14 11:57:48 +01:00
Benjamin Dauvergne 75b675eb8c static: fixed path of image files 2012-12-14 11:57:48 +01:00
Benjamin Dauvergne 0d4a350f69 models: changed __unicode__ method of AttachedFile 2012-12-14 11:57:48 +01:00
Benjamin Dauvergne bd4790f078 views: used journal to log success and errors in send_sms_notification() 2012-12-14 11:57:48 +01:00
Benjamin Dauvergne 44c798728f views: used journal to log successful deletion of a delegation 2012-12-14 11:57:48 +01:00
Benjamin Dauvergne 44907ab59d views: if we fail to notify the new delegate we delete the new user and the delegation 2012-12-14 11:57:48 +01:00
Benjamin Dauvergne 3b9d42fe5d views: removed debugging statement 2012-12-14 11:57:48 +01:00
Benjamin Dauvergne 488ab95d96 views: use journal to log success in delete() 2012-12-14 11:57:48 +01:00
Benjamin Dauvergne 1575b60d71 views: use journal to log success and errors in contact() 2012-12-14 11:57:48 +01:00
Benjamin Dauvergne ec9e1b0a37 views: use journal to log sucess in forward_document() 2012-12-14 11:57:48 +01:00
Benjamin Dauvergne 5e73653b64 views: reworked message_attached_file()
- use get_object_or_404 to retrieve the attached file
- use the journal for logging a successful download
2012-12-14 11:57:48 +01:00
Benjamin Dauvergne d192edafa4 views: add the N_ localization marker 2012-12-14 11:57:47 +01:00
Benjamin Dauvergne de7298aa35 settings: prevent db initialization errors in tests by reordering loading times of auth and contenttypes applications (auth depends upon contentypes) 2012-12-14 11:57:47 +01:00
Benjamin Dauvergne 295276e22a add a new log journal application 2012-12-14 11:57:47 +01:00
Benjamin Dauvergne 188446536b models: completed MailingList mode to allow list of lists
A list can now inherit members from others lists. Two methods,
recursive_members() and recursive_members_with_origin(), compute the
closure of this transitive relation. The first one simply return the set
of members closed under the inheritance relationship between mailing
lists. The second one returns the set of members but also for each one
the set of list it is member of; it is done using at most two traversal
of the reachable lists. Cyclic relationships between lists is handled.

ticket #1843
2012-12-06 23:47:32 +01:00
Benjamin Dauvergne e229097b3d restored the trashcan icon for the SelectFilter2 widget 2012-12-03 15:10:47 +01:00
Benjamin Dauvergne 3e9d5c474e restored the progress bar of jquery-file-upload 2012-12-03 15:10:12 +01:00
Benjamin Dauvergne d91295c30e removed unused script element in upload.html template 2012-12-03 14:58:27 +01:00
Benjamin Dauvergne 310d0aefc1 restored jquery-ui image ressources 2012-12-03 14:49:21 +01:00
Benjamin Dauvergne 8992ee693c Merge branch 'master' into pfwb 2012-12-03 14:28:26 +01:00
Benjamin Dauvergne ec1e25fb80 improved mobile_phone cleaning method
now the code is valid only for one day lon
2012-12-03 14:27:24 +01:00
Benjamin Dauvergne 291ae670ea imported order_field_choices from the fields module instead of duplicating it 2012-12-03 14:26:43 +01:00
Benjamin Dauvergne 28dd84f6a0 added translation of a message from the profile view 2012-12-03 14:25:34 +01:00
Benjamin Dauvergne 5cbfd0429f added use of django-uni-form in the profile view 2012-12-03 14:25:14 +01:00
Benjamin Dauvergne dc17e11a7f improved formatting in forms 2012-12-03 14:24:20 +01:00
Benjamin Dauvergne b18d88758b admin: removed unused queryset() overloading in MailboxAdmin 2012-12-03 14:23:58 +01:00
Benjamin Dauvergne 4a1842f3e9 improved docstring in models, forms, fields, middleware, logger_adapter and admin 2012-12-03 14:23:22 +01:00
Benjamin Dauvergne f8b218d14d removed unused imports 2012-12-03 14:20:54 +01:00
Benjamin Dauvergne 6d6bc950e4 manage.py made executable 2012-12-03 14:19:37 +01:00
docbow e7fa092581 static: remove useless static files, improve their filing 2012-12-03 14:15:47 +01:00
Benjamin Dauvergne 409f405a4d filter-widget: fix wrong url for right arrow button icon 2012-12-03 14:15:47 +01:00
Frédéric Péters 3bf291bb4f style: fix foreground colour of name/logout elements 2012-11-30 13:34:34 +01:00
Frédéric Péters ed65ef358c initial custom styling for the pfwb 2012-11-28 14:00:42 +01:00
Benjamin Dauvergne 41993e788b help: fix convertion to table of contents headings (bis) 2012-11-12 15:45:53 +00:00
Benjamin Dauvergne f513c04173 help: fix convertion to table of contents headings 2012-11-12 16:35:42 +01:00
Benjamin Dauvergne d5bd17b453 add collectstatic to the Makefile 2012-11-12 13:52:31 +00:00
Benjamin Dauvergne c40fe10d15 implement sms notification for newly received message 2012-11-12 00:14:40 +00:00
Benjamin Dauvergne 5184e6e1d2 add DocbowProfile model to holde the mobile phone number of users, and views to set it 2012-11-11 23:29:26 +00:00
Benjamin Dauvergne 208c960fab add an ovh sms send module 2012-11-11 23:28:58 +00:00
Benjamin Dauvergne 89a9b56b1e look for local_settings.py in /etc/docbow 2012-11-09 11:39:10 +00:00
Benjamin Dauvergne c5f52ae240 add gunicorn to requirements 2012-11-09 11:23:42 +00:00
Benjamin Dauvergne 9468b9da5d simplify Makefile 2012-11-09 11:23:22 +00:00
1180 changed files with 23951 additions and 89503 deletions

5
.coveragerc Normal file
View File

@ -0,0 +1,5 @@
[run]
dynamic_context = test_function
[html]
show_contexts = True

6
.git-blame-ignore-revs Normal file
View File

@ -0,0 +1,6 @@
# misc: apply double-quote-string-fixer (#79788)
2489ed708e466f94fcddaeb46a196be9d8963414
# ci: fix remaining ruff warnings (#86370)
98dccb4964b4bb1dcc33ebbd78461648abdd93a4
# ci: apply pre-commit hooks (#86370)
229479409e3534c388c4a6083c476ea9d606d7a8

7
.gitignore vendored
View File

@ -1,2 +1,7 @@
local_settings.py
archive/
*.pyc
*.mo
mbox/
media/
/docbow.db
/docbow-venv

44
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,44 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: double-quote-string-fixer
- repo: https://github.com/asottile/pyupgrade
rev: v3.3.1
hooks:
- id: pyupgrade
args: ['--keep-percent-format', '--py39-plus']
- repo: https://github.com/adamchainz/django-upgrade
rev: 1.13.0
hooks:
- id: django-upgrade
args: ['--target-version', '3.2']
- repo: https://github.com/psf/black
rev: 23.1.0
hooks:
- id: black
args: ['--target-version', 'py39', '--skip-string-normalization', '--line-length', '110']
- repo: https://github.com/PyCQA/isort
rev: 5.12.0
hooks:
- id: isort
args: ['--profile', 'black', '--line-length', '110']
- repo: https://github.com/rtts/djhtml
rev: '3.0.5'
hooks:
- id: djhtml
args: ['--tabwidth', '2']
- repo: https://git.entrouvert.org/pre-commit-debian.git
rev: v0.3
hooks:
- id: pre-commit-debian
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.1.15
hooks:
# Run the linter.
- id: ruff
exclude: 'debian/.*|benchmark'
args: [ --fix ]

53
Jenkinsfile vendored Normal file
View File

@ -0,0 +1,53 @@
@Library('eo-jenkins-lib@main') import eo.Utils
pipeline {
agent any
options { disableConcurrentBuilds() }
stages {
stage('Unit Tests') {
steps {
sh 'tox -rv'
}
post {
always {
script {
utils = new Utils()
utils.publish_coverage('coverage.xml')
utils.publish_coverage_native('index.html')
}
mergeJunitResults()
}
}
}
stage('Packaging') {
steps {
script {
env.SHORT_JOB_NAME=sh(
returnStdout: true,
// given JOB_NAME=gitea/project/PR-46, returns project
// given JOB_NAME=project/main, returns project
script: '''
echo "${JOB_NAME}" | sed "s/gitea\\///" | awk -F/ '{print $1}'
'''
).trim()
if (env.GIT_BRANCH == 'main' || env.GIT_BRANCH == 'origin/main') {
sh "sudo -H -u eobuilder /usr/local/bin/eobuilder -d bullseye,bookworm ${SHORT_JOB_NAME}"
} else if (env.GIT_BRANCH.startsWith('hotfix/')) {
sh "sudo -H -u eobuilder /usr/local/bin/eobuilder -d bullseye,bookworm --branch ${env.GIT_BRANCH} --hotfix ${SHORT_JOB_NAME}"
}
}
}
}
}
post {
always {
script {
utils = new Utils()
utils.mail_notify(currentBuild, env, 'ci+jenkins-docbow@entrouvert.org')
}
}
success {
cleanWs()
}
}
}

View File

@ -1,12 +1,21 @@
recursive-include tools *
recursive-include docbow_project/locale *.po
recursive-include docbow_project/templates *.html
recursive-include help Makefile *.png *.page
recursive-include docbow_project/locale *.po *.mo
recursive-include docbow_project/docbow/fixtures *.json
recursive-include docbow_project/docbow/static *.css *.gif *.html *.jpg *.js *.php *.png *.txt *.xml
recursive-include docbow_project/docbow/templates *.html
recursive-include docbow_project/docbow/static *.css *.gif *.html *.jpg *.js *.png *.svg
recursive-include docbow_project/docbow/templates *.html *.txt *.js
recursive-include docbow_project/pw/static *.css *.png
recursive-include docbow_project/pw/templates *.html
recursive-include docbow_project/pw/locale *.po *.mo
recursive-include docbow_project/pw/fixtures *.json
recursive-include docbow_project/pfwb/static *.css *.png *.svg
recursive-include docbow_project/pfwb/templates *.html
recursive-include docbow_project/pfwb/locale *.po *.mo
include docbow_project/docbow/allkeys.txt
include docbow_project/docbow/*.crt
include docbow_project/pfwb/README.txt
include COPYING
include Makefile
include dev-req.txt
include README.rst
include MANIFEST.in
prune media
prune static

View File

@ -1,17 +0,0 @@
HOST=$(shell uname -n)
update:
cd source/ && git pull
cd source/docbow_project && python manage.py collectstatic
cd source/docbow_project && python manage.py compilemessages
cd source/docbow_project && python manage.py syncdb --migrate
pip install -r source/dev-req.txt
/etc/init.d/courrier.parlement-wallon.be reload
ifeq ($(HOST),docbow1.entrouvert.com)
rsync -avz source/ docbow@docbow2-internal:/home/docbow/source/
rsync -avz env/ docbow@docbow2-internal:/home/docbow/env/
else
rsync -avz source/ docbow@docbow1-internal:/home/docbow/source/
rsync -avz env/ docbow@docbow1-internal:/home/docbow/env/
endif

View File

@ -11,6 +11,19 @@ docbow. The user running the application need access to this database. On most
Unix system it means creating a postgresql user with the same name as the Unix
user it the database server runs on the same host.
Installation for developpers
----------------------------
- First run:
./start.sh
- and next times:
./run.sh
- To update dependencies in the virtualenv environment rerun ./start.sh
Installation
------------
@ -26,8 +39,8 @@ Installation
python-ldap, for them you will some developement package installed, on
Debian, it means gcc, python-dev, libssl-dev, libldap-dev, libsasl2-dev,
libpq-dev)::
pip install -r requirements.txt
./getlasso.sh
pip install -e .
- Create user and database (you can replace $USER by www-data for a WSGI
installation)::
@ -35,29 +48,35 @@ Installation
sudo -u postgres createuser $USER
sudo -u postgres createdb -O $USER docbow
- Fill in your local settings
Rename locale_settings.py.example to local_settings.py, and fill it
with appropriate database informations (standard Django settings.py file like)
Also put DEBUG to True or set the SECRET_KEY
- Create database schema and load initial data::
( cd docbow_project; python manage.py syncdb --migrate )
( cd docbow_project; python manage.py loaddata content filetype groups )
docbow-ctl syncdb
docbow-ctl loaddata content filetype groups
- Compile UI strings translations::
( cd docbow_project; python manage.py compilemessages )
docbow-ctl makemessages --all
docbow-ctl compilemessages
- Load user list (you need to get a user list as a CSV files, see format later)::
( cd docbow_project; python manage.py load-users-csv users.csv )
( cd docbow_project; docbow-ctl load-users-csv users.csv )
Upgrading
---------
When you upgrade you must execute this from the root directory:
( cd docbow_project; python manage.py syncdb --migrate )
( cd docbow_project; docbow-ctl syncdb --migrate )
if you application run as another user (www-data for example if using WSGI)::
( cd docbow_project; sudo -u www-data python manage.py syncdb --migrate )
( cd docbow_project; sudo -u www-data docbow-ctl syncdb --migrate )
Installation on Debian with Apache2/mod_wsgi
--------------------------------------------
@ -70,7 +89,7 @@ Now you can follow the generic installation described before.
Collect all static content::
(cd docbow_project; python manage.py collectstatic)
(cd docbow_project; docbow-ctl collectstatic)
After that you must install apache2 and the mod_wsgi module. On Debian do::
@ -116,7 +135,7 @@ Install gunicorn::
Collect all static content::
(cd docbow_project; python manage.py collectstatic)
(cd docbow_project; docbow-ctl collectstatic)
After that you must install apache2 and the mod_wsgi module. On Debian do::
@ -149,3 +168,200 @@ docbow postgresql database)::
You must set a charset to use as file are created with name recevied from user
agents which can contain any unicode character. The locale "C.UTF-8" works in
this case.
Configuring main menu
---------------------
To configure the main menu you can setup the DOCBOW_MENU variable in your
`local_settings.py` file. The default setting is: ::
DOCBOW_MENU = [
('send-file', gettext_noop('send-file_menu')),
('inbox', gettext_noop('inbox_menu')),
('outbox', gettext_noop('outbox_menu')),
('docbow_admin:index', gettext_noop('admin_menu')),
('profile', gettext_noop('profile_menu')),
('auth_password_change', gettext_noop('password_change_menu')),
('delegate', gettext_noop('delegate_menu')),
('mailing-lists', gettext_noop('mailing-lists')),
('help', gettext_noop('help_menu')),
('contact', gettext_noop('contact_menu')),
]
The first element of each pair must be a django view name of an URL, the second
one is potentially localized, for example to add a link to google, add this line::
('http://google.com/', u'Google'),
Settings
--------
All settings must be donne in the file ``/etc/docbow/local_settings.py``. Available settings are:
* ``DOCBOW_ORGANIZATION``: an unicode string giving a description of the
organization providing the platform. It's used as a signature in mail and
sms notifications.
* ``DOCBOW_BASE_URL``: the base URL of the application. It's used for building
URL in notifications, emails or SMS.
* ``DOCBOW_MENU``: description of the left column menu; see previous section
for a description and the default value.
* ``DOCBOW_MAILBOX_PER_PAGE``: the number of message to show on listing pages.
Default is 20.
* ``RAVEN_CONFIG_DSN``: the URL of the sentry project for gathering exceptions.
* ``DOCBOW_MAX_FILE_SIZE``: the maximum file size for attached files, as
bytes. Default is 10 Mo.
* ``DOCBOW_TRUNCATE_FILENAME``: the maximum length for filenames. Default is
80 unicode characters (codepoints).
* ``DOCBOW_PERSONAL_EMAIL``: allow user to have a personal email,
notifications will be sent to their institutional email and personal email.
* ``DOCBOW_MOBILE_PHONE``: allow user to set a mobile phone number
to receive notification by SMS.
* ``GROUP_LISTING``: whether to show the link to the list of
mailing lists in the left menu.
* ``DOCBOW_PRIVATE_DOCUMENTS``: add a private checkbox to the sending form,
when checked the document is only display to direct recipients and not to
delegates,
Customizing templates
---------------------
You can override any template by copying it from
``docbow_project/docbow/templates/`` to ``/var/lib/docbow/templates/`` and then
modifying it. If you want to provide static ressources you can put them in
``/var/lib/docbow/extra_static/`` and update the static files cache with: ::
./docbow-ctl collectstatic
Sending a file from the command line
------------------------------------
The ``sendfile`` command is used to send a file from the command line. Basic
usage is::
./docbow-ctl sendfile --sender bdauvergne --to-user pcros --to-list liste1 \
--description "New question" file1 file2
The sender and filetype parameters are mandatory. There must be at least one
recipient, list or user, and one file attached to the sending.
Listing users from the command line
-----------------------------------
The ``list-users`` command is used to list the user registerd in docbow. The
default output format is an ASCII table. You can ask for CSV output using the
``--csv`` parameter.
Example output::
| Id | Username | First name | Last name | Email | Mobile phone | Personal mail | Lists | Groups |
| 502 | test3 | Thomas | Noël | bob@gmail.com | | | Liste test2 | |
| 503 | bdauvergne-1 | benjamin | dauvergne | test@gmail.com | | | | |
| 501 | test2 | Kévin | Gaspard | test2@gmail.com | | | Liste test1 | |
| 504 | 503 | | | toto@example.com | | | | |
| 444 | test | Jérôme | Schneider | john.doe@gmail.com | | | Liste test1 | |
| 98 | bdauvergne | Benjamin | Dauvergne | coin@entrouvert.com | +33630XXX893 | joe@example.com | | Administrateurs |
The command supports a minimal query language, for example to get all users in
the group ``Administrator`` ::
./docbow-ctl list-users groups__name=Administrator
Listing lists from the command line
-----------------------------------
The ``list-lists`` command provide exactly the same service as ``list-users``
but for mailing lists.
Example output::
| Id | Name | Members | List Members |
| 54 | Liste test1 | | test,test2 |
| 55 | Liste test2 | Liste test1 | test3 |
| 56 | List test1 | | |
Adding users from the command line
----------------------------------
The ``add-user`` create a new user or modify an existing user. The username can
only be set at creation.::
Usage: ./docbow-ctl add-user [options] <username>
Create a new user or update an existing user
List and groups can be referred by name or by id.
Options:
-v VERBOSITY, --verbosity=VERBOSITY
Verbosity level; 0=minimal output, 1=normal output,
2=verbose output, 3=very verbose output
--settings=SETTINGS The Python path to a settings module, e.g.
"myproject.settings.main". If this isn't provided, the
DJANGO_SETTINGS_MODULE environment variable will be
used.
--pythonpath=PYTHONPATH
A directory to add to the Python path, e.g.
"/home/djangoprojects/myproject".
--traceback Print traceback on exception
--first-name=FIRST_NAME
set first name
--last-name=LAST_NAME
set last name
--email=EMAIL set email
--mobile-phone=MOBILE_PHONE
set mobile phone used for SMS notifications
--personal-email=PERSONAL_EMAIL
set personal email
--add-list=ADD_LIST add user to list
--add-group=ADD_GROUP
add user to group
--remove-list=REMOVE_LIST
remove user from list
--remove-group=REMOVE_GROUP
remove user from group
--version show program's version number and exit
-h, --help show this help message and exit
Adding lists from the command line
----------------------------------
The ``add-list`` command allows to create new lists and to update their
members, lists or users::
usage: docbow-ctl add-list [-h] [--version] [-v {0,1,2,3}]
[--settings SETTINGS] [--pythonpath PYTHONPATH]
[--traceback] [--no-color] [--add-list ADD_LIST]
[--remove-list REMOVE_LIST] [--add-user ADD_USER]
[--remove-user REMOVE_USER]
ml_name
Create or update a list
positional arguments:
ml_name Name of the mailing list
optional arguments:
-h, --help show this help message and exit
--version show program's version number and exit
-v {0,1,2,3}, --verbosity {0,1,2,3}
Verbosity level; 0=minimal output, 1=normal output,
2=verbose output, 3=very verbose output
--settings SETTINGS The Python path to a settings module, e.g.
"myproject.settings.main". If this isn't provided, the
DJANGO_SETTINGS_MODULE environment variable will be
used.
--pythonpath PYTHONPATH
A directory to add to the Python path, e.g.
"/home/djangoprojects/myproject".
--traceback Raise on CommandError exceptions
--no-color Don't colorize the command output.
--add-list ADD_LIST add a list as a sublist
--remove-list REMOVE_LIST
remove list as a sublist
--add-user ADD_USER add a user member
--remove-user REMOVE_USER
remove a user member

59
benchmark Executable file
View File

@ -0,0 +1,59 @@
#!/usr/bin/env python
import json
import os.path
import random
import time
import numpy
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'docbow_project.settings')
from django.contrib.auth.models import User
from django.test.client import RequestFactory
from docbow_project.docbow.models import FileType
from docbow_project.docbow.views import inbox_view, outbox_view, send_file
def stat(seq):
return {
'min': min(seq),
'max': max(seq),
'mean': numpy.mean(seq),
'median': numpy.median(seq),
'std': numpy.std(seq),
}
rf = RequestFactory()
paths = [('/inbox/', inbox_view, {}), ('/outbox/', outbox_view, {})]
filetypes = list(FileType.objects.all())
random.shuffle(filetypes)
for file_type in filetypes[:10]:
paths.append(('/inbox/%s/' % file_type.id, send_file, {'file_type_id': file_type.id}))
data = []
for path, view, kwargs in paths:
get_request = rf.get(path)
get_request.session = {}
print('Testing view', path, 'for all users:')
seq = []
for user in User.objects.all():
get_request.user = user
now = time.time()
response = view(get_request, **kwargs)
if hasattr(response, 'render'):
response.render()
str(response)
duration = time.time() - now
seq.append(duration * 1000)
data.append(stat(seq))
data[-1]['path'] = path
with file('vix.js', 'w') as f:
f.write(
'''var vix =
'''
)
f.write(json.dumps(data, indent=4))
f.write(';')

5
debian/changelog vendored Normal file
View File

@ -0,0 +1,5 @@
docbow (0.1.44.g543c8de-1) unstable; urgency=low
* Initial release
-- Benjamin Dauvergne <bdauvergne@entrouvert.com> Fri, 5 May 2014 14:31:55 +0200

41
debian/conf/docbow.nginx vendored Normal file
View File

@ -0,0 +1,41 @@
server {
listen 443;
server_name citoyen.example.fr;
ssl on;
ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem;
ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key;
access_log /var/log/nginx/citoyen.example.fr-access.log combined;
error_log /var/log/nginx/citoyen.example.fr-error.log;
location /static {
alias /var/lib/portail-citoyen/static;
}
location / {
proxy_pass http://unix:/var/run/portail-citoyen/portail-citoyen.sock;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-SSL on;
proxy_set_header X-Forwarded-Protocol ssl;
proxy_set_header X-Forwarded-Proto https;
}
}
server {
listen 80;
server_name citoyen.example.fr;
access_log /var/log/nginx/citoyen.example.fr-access.log combined;
error_log /var/log/nginx/citoyen.example.fr-error.log;
location /static {
alias /var/lib/portail-citoyen/static;
}
location / {
proxy_pass http://unix:/var/run/portail-citoyen/portail-citoyen.sock;
proxy_set_header Host $http_host;
}
}

44
debian/conf/magic vendored Normal file
View File

@ -0,0 +1,44 @@
# https://raw.githubusercontent.com/file/file/master/magic/Magdir/msooxml
#------------------------------------------------------------------------------
# $File: msooxml,v 1.8 2018/05/24 18:11:17 christos Exp $
# msooxml: file(1) magic for Microsoft Office XML
# From: Ralf Brown <ralf.brown@gmail.com>
# .docx, .pptx, and .xlsx are XML plus other files inside a ZIP
# archive. The first member file is normally "[Content_Types].xml".
# but some libreoffice generated files put this later. Perhaps skip
# the "[Content_Types].xml" test?
# Since MSOOXML doesn't have anything like the uncompressed "mimetype"
# file of ePub or OpenDocument, we'll have to scan for a filename
# which can distinguish between the three types
0 name msooxml
>0 string word/ Microsoft Word 2007+
!:mime application/vnd.openxmlformats-officedocument.wordprocessingml.document
>0 string ppt/ Microsoft PowerPoint 2007+
!:mime application/vnd.openxmlformats-officedocument.presentationml.presentation
>0 string xl/ Microsoft Excel 2007+
!:mime application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
# start by checking for ZIP local file header signature
0 string PK\003\004
!:strength +10
# make sure the first file is correct
>0x1E use msooxml
>0x1E regex \\[Content_Types\\]\\.xml|_rels/\\.rels
# skip to the second local file header
# since some documents include a 520-byte extra field following the file
# header, we need to scan for the next header
>>(18.l+49) search/2000 PK\003\004
# now skip to the *third* local file header; again, we need to scan due to a
# 520-byte extra field following the file header
>>>&26 search/1000 PK\003\004
# and check the subdirectory name to determine which type of OOXML
# file we have. Correct the mimetype with the registered ones:
# http://technet.microsoft.com/en-us/library/cc179224.aspx
>>>>&26 use msooxml
>>>>&26 default x
# OpenOffice/Libreoffice orders ZIP entry differently, so check the 4th file
>>>>>&26 search/1000 PK\003\004
>>>>>>&26 use msooxml
>>>>>>&26 default x Microsoft OOXML

32
debian/control vendored Normal file
View File

@ -0,0 +1,32 @@
Source: docbow
Maintainer: Benjamin Dauvergne <bdauvergne@entrouvert.com>
Section: python
Priority: optional
Build-Depends: debhelper-compat (= 12),
dh-python,
openssl,
python3-all,
python3-django (>= 1:1.11),
python3-setuptools,
yelp-tools,
yelp-xsl,
Standards-Version: 3.9.6
Homepage: https://dev.entrouvert.org/projects/docbow
Package: docbow
Architecture: all
Suggests: postgresql,
Depends: python3-django (>= 1:1.11),
python3-django-journal (>= 2.0.0),
python3-django-picklefield,
python3-django-tables2,
python3-django-watson (>= 1.2.0),
python3-magic,
python3-psycopg2,
python3-requests,
uwsgi,
uwsgi-plugin-python3,
${misc:Depends},
${python3:Depends},
Recommends: python3-django-mellon,
Description: Document Box Wallone

16
debian/debian_config.py vendored Normal file
View File

@ -0,0 +1,16 @@
# This file is sourced by "execfile" from docbow_project.settings
import glob
import os
PROJECT_NAME = 'docbow'
ETC_DIR = os.path.join('/etc', PROJECT_NAME)
ETC_SETTINGS_PY = os.path.join(ETC_DIR, 'settings.py')
if os.path.exists(ETC_SETTINGS_PY):
exec(open(ETC_SETTINGS_PY).read())
for filename in sorted(glob.glob(os.path.join(ETC_DIR, 'settings.d', '*.py'))):
exec(open(filename).read())

10
debian/dirs vendored Normal file
View File

@ -0,0 +1,10 @@
etc/docbow
etc/nginx/sites-available
usr/lib/docbow
usr/share/docbow/templates
var/lib/docbow/collectstatic
var/lib/docbow/media
var/lib/docbow/static
var/lib/docbow/templates
var/log/docbow
var/run/docbow

25
debian/docbow-manage vendored Normal file
View File

@ -0,0 +1,25 @@
#!/bin/sh
NAME=docbow
MANAGE=/usr/lib/$NAME/manage.py
# load Debian default configuration
export DOCBOW_SETTINGS_FILE=/usr/lib/$NAME/debian_config.py
# check user
if test x$1 = x"--forceuser"
then
shift
elif test $(id -un) != "$NAME"
then
echo "error: must use $0 with user ${NAME}"
exit 1
fi
if test $# -eq 0
then
python3 ${MANAGE} help
exit 1
fi
python3 ${MANAGE} "$@"

2
debian/docbow.cron.d vendored Normal file
View File

@ -0,0 +1,2 @@
*/1 * * * * docbow /bin/systemctl status docbow > /dev/null 2>&1 && /usr/bin/docbow-manage notify > /dev/null
*/10 * * * * docbow /usr/bin/docbow-manage empty-trash > /dev/null

26
debian/docbow.service vendored Normal file
View File

@ -0,0 +1,26 @@
[Unit]
Description=Docbow
After=network.target postgresql.service
Wants=postgresql.service
[Service]
SyslogIdentifier=uwsgi/%p
Environment=DOCBOW_SETTINGS_FILE=/usr/lib/%p/debian_config.py
Environment=LANG=fr_FR.UTF-8
Environment=LC_ALL=fr_FR.UTF-8
User=%p
Group=%p
ExecStartPre=/usr/bin/docbow-manage migrate --noinput --verbosity 1
ExecStartPre=/usr/bin/docbow-manage collectstatic --noinput
ExecStart=/usr/bin/uwsgi --ini /etc/%p/uwsgi.ini
ExecReload=/bin/kill -HUP $MAINPID
KillSignal=SIGQUIT
TimeoutStartSec=0
PrivateTmp=true
Restart=on-failure
RuntimeDirectory=docbow
Type=notify
NotifyAccess=all
[Install]
WantedBy=multi-user.target

3
debian/docs vendored Normal file
View File

@ -0,0 +1,3 @@
README.rst
help/fr/build-pfwb
help/fr/build-pw

5
debian/install vendored Normal file
View File

@ -0,0 +1,5 @@
debian/conf/docbow.nginx /etc/nginx/sites-available/
debian/conf/magic /usr/share/docbow
debian/debian_config.py /usr/lib/docbow
debian/docbow-manage /usr/bin
debian/uwsgi.ini /etc/docbow

1
debian/links vendored Normal file
View File

@ -0,0 +1 @@
/usr/share/docbow/magic /etc/magic

14
debian/logrotate vendored Normal file
View File

@ -0,0 +1,14 @@
/var/log/docbow/*.log {
daily
missingok
rotate 365
compress
delaycompress
notifempty
create 640 root adm
sharedscripts
postrotate
[ ! -f /var/run/docbow/docbow.pid ] || kill -HUP `cat /var/run/docbow/docbow.pid`
endscript
}

69
debian/postinst vendored Normal file
View File

@ -0,0 +1,69 @@
#!/bin/sh
#
# Postinst script for docbow
#
set -e
NAME=docbow
USER=$NAME
GROUP=$NAME
HOME=/var/lib/$NAME
case "$1" in
configure)
if ! getent group $GROUP > /dev/null 2>&1; then
echo -n "Adding group $GROUP.." >&2
addgroup --quiet --system $GROUP
echo "..done" >&2
fi
if ! getent passwd $USER > /dev/null 2>&1; then
echo -n "Adding user $USER.." >&2
adduser --quiet --system --gecos "$NAME daemon" \
--ingroup $GROUP \
--no-create-home --home $HOME \
$USER
echo "..done" >&2
fi
if [ ! -f "/etc/$NAME/secret" ]; then
echo -n "Generating Django secret.." >&2
echo "export SECRET_KEY='`</dev/urandom tr -dc [:alnum:]-_\!\%\^:\; | head -c70`'" > /etc/$NAME/secret
chmod 0640 /etc/$NAME/secret
chown root:$GROUP /etc/$NAME/secret
echo "..done" >&2
fi
if [ ! -f "/etc/$NAME/db" ]; then
cat > /etc/$NAME/db <<EOC
export DATABASE_ENGINE='django.db.backends.postgresql_psycopg2'
export DATABASE_NAME='docbow'
export DATABASE_USER='docbow'
export DATABASE_PASSWORD='SOME_PASSWORD'
export DATABASE_HOST='localhost'
EOC
fi
chown $USER:$GROUP /var/lib/docbow \
/var/lib/docbow/static \
/var/lib/docbow/collectstatic \
/var/lib/docbow/templates \
/var/lib/docbow/media \
/var/run/docbow \
/var/log/docbow
;;
reconfigure|abort-upgrade|abort-remove|abort-deconfigure)
;;
*)
echo "postinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

24
debian/postrm vendored Normal file
View File

@ -0,0 +1,24 @@
#!/bin/sh
# postrm script for docbow
#
# see: dh_installdeb(1)
set -e
NAME=docbow
USER=$NAME
GROUP=$NAME
case "$1" in purge)
deluser --quiet --system $NAME > /dev/null || true
rm -rf /var/lib/$NAME/collectstatic
;;
esac
case "$1" in remove|abort-install|purge)
dpkg-divert --remove --package docbow --rename /etc/magic
;;
esac
exit 0

38
debian/preinst vendored Normal file
View File

@ -0,0 +1,38 @@
#!/bin/sh
# preinst script for docbow
#
# see: dh_installdeb(1)
set -e
# summary of how this script can be called:
# * <new-preinst> `install'
# * <new-preinst> `install' <old-version>
# * <new-preinst> `upgrade' <old-version>
# * <old-preinst> `abort-upgrade' <new-version>
# for details, see https://www.debian.org/doc/debian-policy/ or
# the debian-policy package
case "$1" in
install|upgrade)
dpkg-divert --add --package docbow --rename \
--divert /etc/magic.libmagic \
/etc/magic
;;
abort-upgrade)
;;
*)
echo "preinst called with unknown argument \`$1'" >&2
exit 1
;;
esac
# dh_installdeb will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#
exit 0

4
debian/py3dist-overrides vendored Normal file
View File

@ -0,0 +1,4 @@
django_watson python3-django-watson
django_journal python3-django-journal
python_magic python3-magic
typing

16
debian/rules vendored Executable file
View File

@ -0,0 +1,16 @@
#!/usr/bin/make -f
# -*- makefile -*-
export PYBUILD_NAME=docbow
export PYBUILD_DISABLE=test
%:
dh $@ --with python3 --buildsystem=pybuild
override_dh_install:
dh_install
mv $(CURDIR)/debian/docbow/usr/bin/manage.py $(CURDIR)/debian/docbow/usr/lib/docbow/manage.py
override_dh_auto_build:
$(MAKE) -C help/fr
dh_auto_build

1
debian/source/format vendored Normal file
View File

@ -0,0 +1 @@
3.0 (quilt)

47
debian/uwsgi.ini vendored Normal file
View File

@ -0,0 +1,47 @@
[uwsgi]
strict = true
auto-procname = true
procname-prefix-spaced = docbow
plugin = python3
single-interpreter = true
module = docbow_project.wsgi:application
need-app = true
vacuum = true
http-socket = /run/docbow/docbow.sock
chmod-socket = 666
vacuum = true
master = true
enable-threads = true
harakiri = 120
plugin = cheaper_busyness
cheaper-algo = busyness
processes = 500
cheaper = 5
cheaper-initial = 10
cheaper-overload = 5
cheaper-step = 10
cheaper-busyness-multiplier = 30
cheaper-busyness-min = 20
cheaper-busyness-max = 70
cheaper-busyness-backlog-alert = 16
cheaper-busyness-backlog-step = 2
max-requests = 500
max-worker-lifetime = 7200
buffer-size = 32768
py-tracebacker = /run/docbow/py-tracebacker.sock.
stats = /run/docbow/stats.sock
memory-report = true
ignore-sigpipe = true
disable-write-exception = true
if-file = /etc/docbow/uwsgi-local.ini
include = /etc/docbow/uwsgi-local.ini
endif =

11
docbow-ctl Executable file
View File

@ -0,0 +1,11 @@
#!/usr/bin/env python
import os
import sys
if __name__ == '__main__':
from django.core.management import execute_from_command_line
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'docbow_project.settings')
execute_from_command_line(sys.argv)

View File

@ -1,29 +0,0 @@
import csv
from django.core.exceptions import PermissionDenied
from django.http import HttpResponse
from django.utils.translation import ugettext as _
def export_as_csv(modeladmin, request, queryset):
"""
Generic csv export admin action.
"""
if not request.user.is_staff:
raise PermissionDenied
opts = modeladmin.model._meta
response = HttpResponse(mimetype='text/csv')
response['Content-Disposition'] = 'attachment; filename=%s.csv' % unicode(opts).replace('.', '_')
writer = csv.writer(response)
field_names = [field.name for field in opts.fields]
m2m_field_names = [m2m_field.name for m2m_field in opts.many_to_many]
# Write a first row with header information
writer.writerow(field_names+m2m_field_names)
# Write data rows
for obj in queryset:
values = [ unicode(getattr(obj, field)) for field in field_names]
for m2m_field in m2m_field_names:
value = getattr(obj, m2m_field)
value = u','.join(map(unicode, value.all()))
values.append(unicode(value))
writer.writerow(map(lambda x: unicode.encode(x, 'utf8'), values))
return response
export_as_csv.short_description = _("Export selected objects as csv file")

View File

@ -1,45 +0,0 @@
from django.contrib.auth.models import User
class DummyUser(object):
def __init__(self, user, delegate):
self.user = user
self.delegate = delegate
self.id = '%s,%s' % (user.id, delegate.id)
@property
def is_active(self):
return self.user.is_active
def save(self):
self.delegate.last_login = self.last_login
self.delegate.save()
def __str__(self):
return str(self.user)
class DelegationAuthBackend:
supports_object_permissions = False
supports_anonymous_user = False
def authenticate(self, username=None, password=None):
try:
if '-' in username:
prefix, suffix = username.rsplit('-', 1)
delegate = User.objects.get(username=username)
user = User.objects.get(username=prefix)
if delegate.check_password(password):
user.delegate = username
return DummyUser(user, delegate)
except User.DoesNotExist:
pass
return None
def get_user(self, id_pair):
user_id, delegate_id = id_pair.rsplit(',', 1)
try:
user = User.objects.get(pk=user_id)
delegate = User.objects.get(pk=delegate_id)
user.delegate = delegate
return user
except User.DoesNotExist:
return None

View File

@ -1,39 +0,0 @@
from django.conf.urls.defaults import patterns, url
from django.contrib.auth import views as auth_views
from docbow.decorator import as_delegate
import views
import forms
urlpatterns = patterns('',
url(r'^login/$',
auth_views.login,
{'template_name': 'registration/login.html'},
name='auth_login'),
url(r'^logout/$',
auth_views.logout,
{'template_name': 'registration/logout.html'},
name='auth_logout'),
url(r'^password/change/$',
as_delegate(auth_views.password_change),
kwargs={'password_change_form':
forms.PasswordChangeFormWithLogging},
name='auth_password_change'),
url(r'^password/change/done/$',
auth_views.password_change_done,
name='auth_password_change_done'),
url(r'^password/reset/$',
auth_views.password_reset,
name='auth_password_reset',
kwargs={'post_reset_redirect': '/accounts/password/reset/done/',
'password_reset_form': forms.PasswordResetFormWithLogging }),
url(r'^password/reset/confirm/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$',
auth_views.password_reset_confirm,
name='auth_password_reset_confirm'),
url(r'^password/reset/complete/$',
auth_views.password_reset_complete,
name='auth_password_reset_complete'),
url(r'^password/reset/done/$',
views.password_reset_done,
name='auth_password_reset_done'),
)

View File

@ -0,0 +1,48 @@
import csv
from django.core.exceptions import PermissionDenied
from django.http import HttpResponse
from django.utils.encoding import force_str
from django.utils.translation import gettext as _
def export_as_csv(modeladmin, request, queryset):
"""
Generic csv export admin action.
"""
if not request.user.is_staff:
raise PermissionDenied
opts = modeladmin.model._meta
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename=%s.csv' % force_str(opts).replace('.', '_')
writer = csv.writer(response)
field_names = [field.name for field in opts.fields]
m2m_field_names = [m2m_field.name for m2m_field in opts.many_to_many]
# Write a first row with header information
writer.writerow(field_names + m2m_field_names)
# Write data rows
for obj in queryset:
values = [force_str(getattr(obj, field)) for field in field_names]
for m2m_field in m2m_field_names:
value = getattr(obj, m2m_field)
value = ','.join(map(force_str, value.all()))
values.append(force_str(value))
writer.writerow(map(lambda x: force_str(x), values))
return response
export_as_csv.short_description = _('Export selected objects as csv file')
def activate_selected(modeladmin, request, queryset):
queryset.update(is_active=True)
activate_selected.short_description = _('Activate selected objects')
def deactivate_selected(modeladmin, request, queryset):
queryset.update(is_active=False)
deactivate_selected.short_description = _('De-activate selected objects')

View File

@ -1,139 +1,330 @@
import django.contrib.admin as admin
from django.utils.translation import ugettext as _
from django.contrib.auth import admin as auth_admin
from django.conf.urls.defaults import patterns
import functools
import operator
import django.contrib.admin as admin
from django.conf import settings
from django.contrib.auth import admin as auth_admin
from django.contrib.auth import models as auth_models
from django.core.exceptions import PermissionDenied
from django.db.models import Q
from django.urls import NoReverseMatch, re_path, reverse
from django.utils.safestring import mark_safe
from django.utils.translation import gettext as _
try:
import thread
except ImportError:
import _thread as thread
import django_journal.admin
from docbow_project.docbow import actions, auth_views, forms, models, notification, views
TITLE = "Plate-forme sécurisée d'échange de documents"
class GetSearchResultsMixin:
def get_search_results(self, request, queryset, search_term):
search_fields = self.get_search_fields(request)
orm_lookups = [Q(**{f'{field}__icontains': search_term}) for field in search_fields]
return queryset.filter(functools.reduce(operator.or_, orm_lookups)), False
import models
import forms
import views
import signals
from docbow_project.log import models as log_models, admin as log_admin
import docbow_project.actions as actions
class DocbowAdminSite(admin.AdminSite):
pass
site_title = TITLE
site_header = TITLE
index_title = TITLE
site = DocbowAdminSite('docbow_admin')
site.disable_action('delete_selected')
class DocumentAdmin(admin.ModelAdmin):
list_display = [ 'date', 'sender', 'recipients', 'filetype', 'filename_links', 'comment' ]
list_filter = [ 'sender', 'to_user', 'to_list', 'filetype' ]
fields = [ 'date', 'sender', 'recipients', 'filetype', 'filename_links', 'comment']
readonly_fields = [ 'sender', 'filetype', 'date', 'recipients', 'filename_links', 'comment' ]
filter_horizontal = [ 'to_user', 'to_list' ]
list_display = ['date', 'sender', 'recipients', 'filetype', 'filename_links', 'comment', 'private']
list_filter = ['sender', 'to_user', 'to_list', 'filetype', 'private']
fields = ['date', 'sender', 'recipients', 'filetype', 'filename_links', 'comment', 'private']
readonly_fields = fields
filter_horizontal = ['to_user', 'to_list']
date_hierarchy = 'date'
class SendingLimitationAdmin(admin.ModelAdmin):
list_display = [ 'mailing_list', 'filetypes_list', 'lists_list' ]
filter_horizontal = [ 'filetypes', 'lists' ]
actions = [ actions.export_as_csv ]
list_display = ['mailing_list', 'filetypes_list', 'lists_list']
filter_horizontal = ['filetypes', 'lists']
actions = [actions.export_as_csv, 'delete_selected']
def lists_list(self, obj):
'''Display method for the field lists'''
return ', '.join(obj.lists.values_list('name', flat=True))
lists_list.short_description = _('Limitation des destinataires')
def filetypes_list(self, obj):
'''Display method for the field filetypes'''
return ', '.join(obj.filetypes.values_list('name', flat=True))
filetypes_list.short_description = _('Limitation des types de fichier')
class MailingListAdmin(admin.ModelAdmin):
list_display = [ 'name' ]
search_fields = [ 'name', 'members__username', 'members__first_name',
'members__last_name']
class MailingListAdmin(GetSearchResultsMixin, admin.ModelAdmin):
list_display = ['name', 'is_active']
list_filter = ['is_active']
search_fields = ['name', 'members__username', 'members__first_name', 'members__last_name']
ordering = ['name']
form = forms.MailingListForm
actions = [ actions.export_as_csv ]
actions = [actions.export_as_csv]
def get_actions(self, request):
"""Show delete actions only if user has delete rights
Show activation actions only if user has rights to change mailing lists
"""
a = super().get_actions(request)
if request.user.has_perm('docbow.delete_mailinglist'):
a['delete_selected'] = self.get_action('delete_selected')
if request.user.has_perm('docbow.change_mailinglist'):
a['activate_selected'] = self.get_action(actions.activate_selected)
a['deactivate_selected'] = self.get_action(actions.deactivate_selected)
return a
class Media:
js = ('docbow/js/SelectBox-count.js',)
class AttachedFileAdmin(admin.ModelAdmin):
def get_model_perms(self, request):
return {}
def get_urls(self):
urls = super(AttachedFileAdmin, self).get_urls()
attached_file_urls = patterns('',
(r'^(.+)/download/$', self.download)
)
urls = super().get_urls()
attached_file_urls = [re_path(r'^(.+)/download/$', self.download)]
return attached_file_urls + urls
def download(self, request, object_id):
'''Downlod view for attached files'''
attached_file = models.AttachedFile.objects.get(pk=object_id)
return views.upload(request, attached_file)
class DocbowProfileInlineAdmin(admin.StackedInline):
model = models.DocbowProfile
extra = 0
class DocbowUserAdmin(auth_admin.UserAdmin):
fieldsets = (
(None, {'fields': ('username', 'password')}),
(_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}),
(_('Permissions'), {'fields': ('is_active', 'is_staff')}),
(_('Permissions'), {'fields': ('is_active', 'is_staff', 'is_superuser')}),
(_('Important dates'), {'fields': ('last_login', 'date_joined')}),
(_('Groups'), {'fields': ('groups',)}),
)
readonly_fields = [ 'last_login', 'date_joined' ]
exclude = [ 'user_permissions' ]
actions = [ actions.export_as_csv ]
readonly_fields = ['last_login', 'date_joined']
exclude = ['user_permissions']
actions = [actions.export_as_csv]
list_display = (
'username',
'email',
'first_name',
'last_name',
'delegations',
'get_lists',
'get_groups',
'is_active',
'is_staff',
'is_superuser',
)
inlines = [DocbowProfileInlineAdmin]
if 'mellon' in settings.INSTALLED_APPS:
class UserSAMLIdentifierInlineAdmin(admin.StackedInline):
import mellon
model = mellon.models.UserSAMLIdentifier
extra = 0
inlines += [UserSAMLIdentifierInlineAdmin]
def get_groups(self, user):
return ', '.join(group.name for group in user.groups.all())
get_groups.short_description = _('groups')
def get_lists(self, user):
return ', '.join(_list.name for _list in user.mailing_lists.all())
get_lists.short_description = _('mailing lists')
def get_actions(self, request):
a = super().get_actions(request)
if request.user.has_perm('auth.delete_docbowuser'):
a['delete_selected'] = self.get_action('delete_selected')
if request.user.has_perm('auth.change_docbowuser'):
a['activate_selected'] = self.get_action(actions.activate_selected)
a['deactivate_selected'] = self.get_action(actions.deactivate_selected)
return a
def guest_account(self, instance):
try:
return instance.docbowprofile.is_guest
except models.DocbowProfile.DoesNotExist:
return False
guest_account.boolean = True
guest_account.short_description = _('Guest account')
def delegations(self, instance):
from_users = auth_models.User.objects.filter(delegations_to__to=instance)
return models.list_to_csv(from_users, models.username)
delegations.short_description = _('Delegations by')
class DocbowGroupAdmin(auth_admin.GroupAdmin):
exclude = [ 'permissions' ]
exclude = ['permissions']
class MailboxAdmin(admin.ModelAdmin):
list_display = [ 'owner', 'document', 'date', 'seen' ]
list_filter = [ 'owner', 'outbox' ]
def queryset(self, request):
qs = super(MailboxAdmin, self).queryset(request)
return qs
list_display = ['owner', 'document', 'date']
list_filter = ['owner', 'outbox']
def lookup_allowed(self, *args, **kwargs):
'''Allow complex filters'''
return True
class InboxAdmin(MailboxAdmin):
list_display = [ 'date', 'owner', 'document', 'seen', 'deleted' ]
fields = [ 'date', 'owner', 'document', 'seen', 'deleted' ]
readonly_fields = [ 'date', 'owner', 'document', 'seen' ]
list_display = ['date', 'owner', 'document']
fields = ['date', 'owner', 'document']
readonly_fields = ['date', 'owner', 'document']
def queryset(self, request):
qs = super(InboxAdmin, self).queryset(request)
'''Only show input mailboxes'''
qs = super().queryset(request)
qs = qs.filter(outbox=False)
return qs
class OutboxAdmin(MailboxAdmin):
list_display = [ 'date', 'owner', 'document' ]
list_display = ['date', 'owner', 'document']
fields = list_display
readonly_fields = list_display
def queryset(self, request):
qs = super(OutboxAdmin, self).queryset(request)
'''Only show output mailboxes'''
qs = super().queryset(request)
qs = qs.filter(outbox=True)
return qs
class ContentAdmin(admin.ModelAdmin):
verbose_name = _('Predefined content description')
actions = [ actions.export_as_csv ]
actions = [actions.export_as_csv, 'delete_selected']
class AutomaticForwardingAdmin(admin.ModelAdmin):
filter_horizontal = [ 'filetypes', 'originaly_to_user', 'forward_to_user',
'forward_to_list' ]
filter_horizontal = ['filetypes', 'originaly_to_user', 'forward_to_user', 'forward_to_list']
form = forms.AutomaticForwardingForm
actions = [ actions.export_as_csv ]
actions = [actions.export_as_csv, 'delete_selected']
def formfield_for_manytomany(self, db_field, request, **kwargs):
if db_field.name in ('originaly_to_user', 'forward_to_user'):
kwargs['queryset'] = models.non_guest_users()
return super().formfield_for_foreignkey(db_field, request, **kwargs)
class FileTypeAttachedFileKindAdmin(admin.TabularInline):
model = models.FileTypeAttachedFileKind
sortable_field_name = 'position'
extra = 0
class FileTypeAdmin(admin.ModelAdmin):
actions = [ actions.export_as_csv ]
list_display = ['name', 'is_active']
if settings.EXTRA_SENDERS:
list_display.append('extra_senders')
fields = list_display
actions = [actions.export_as_csv]
inlines = [FileTypeAttachedFileKindAdmin]
class NotificationAdmin(GetSearchResultsMixin, admin.ModelAdmin):
search_fields = [
'user__username',
'user__first_name',
'user__last_name',
'user__docbowprofile__mobile_phone',
]
list_display = ['create_dt', '_document', 'user', 'kind', 'done', 'failure']
readonly_fields = ['ctx']
date_hierarchy = 'create_dt'
list_filter = ['user', 'kind', 'done']
actions = ['retry', 'delete_selected']
def retry(self, request, queryset):
queryset.update(done=False, failure=None)
thread.start_new_thread(notification.process_notifications, ())
retry.short_description = _('Clear failure and done field, resubmitting ' 'the notifications.')
def object_link(self, obj):
if obj is not None:
url = '{}:{}_{}_change'.format(
self.admin_site.name, obj.__class__._meta.app_label, obj.__class__._meta.model_name
)
try:
url = reverse(url, args=(obj.id,))
return f'<a href="{url}" class="external-link">{obj}</a>'
except NoReverseMatch:
pass
return ''
def _document(self, notification):
return mark_safe(self.object_link(notification.document))
_document.short_description = _('Document')
class JournalAdmin(django_journal.admin.JournalAdmin):
def user(self, entry):
'''Search and return any associated objectdata whose tag is "user"'''
user, delegate = '', ''
for objectdata in entry.objectdata_set.all():
if objectdata.tag.name == 'user':
user = self.object_filter_link(objectdata) + self.object_link(objectdata)
if objectdata.tag.name == 'delegate':
delegate = self.object_filter_link(objectdata) + self.object_link(objectdata)
if user and delegate:
return mark_safe(delegate + _(' as ') + user)
elif user:
return mark_safe(user)
return mark_safe(_('None'))
user.short_description = _('User')
class DelegationAdmin(admin.ModelAdmin):
list_display = ['id', 'by', 'to']
# Docbow Admin Site
site.register(models.DocbowUser, DocbowUserAdmin)
site.register(auth_models.User, DocbowUserAdmin)
site.register(models.DocbowGroup, DocbowGroupAdmin)
site.register(models.FileType, FileTypeAdmin)
site.register(models.Content, ContentAdmin)
site.register(models.Document, DocumentAdmin)
site.register(models.MailingList, MailingListAdmin)
site.register(models.Delegation)
site.register(models.Delegation, DelegationAdmin)
site.register(models.Inbox, InboxAdmin)
site.register(models.Outbox, OutboxAdmin)
site.register(log_models.LogLine, log_admin.LogLineAdmin)
site.register(models.AttachedFile, AttachedFileAdmin)
site.register(models.SendingLimitation, SendingLimitationAdmin)
site.register(models.AutomaticForwarding, AutomaticForwardingAdmin)
site.register(models.Notification, NotificationAdmin)
_('Django_Journal')
_('Auth')
_('Docbow')
site.register(django_journal.admin.Journal, JournalAdmin)
# Superadmin Admin Site
admin.site.register(models.FileType)
@ -141,5 +332,16 @@ admin.site.register(models.Content, ContentAdmin)
admin.site.register(models.Document)
admin.site.register(models.AttachedFile)
admin.site.register(models.MailingList, MailingListAdmin)
admin.site.register(models.Delegation)
admin.site.register(models.Delegation, DelegationAdmin)
admin.site.register(models.Mailbox, MailboxAdmin)
admin.site.register(models.Notification, NotificationAdmin)
def login(request, *args, **kwargs):
if request.user.is_authenticated and not (request.user.is_active and request.user.is_staff):
raise PermissionDenied()
return auth_views.login(request, *args, **kwargs)
site.logout = auth_views.logout
site.login = login

View File

@ -0,0 +1,75 @@
import sys
class AppSettings:
__DEFAULTS = {
'PERSONAL_EMAIL': True,
'MOBILE_PHONE': True,
'GROUP_LISTING': True,
'PRIVATE_DOCUMENTS': False,
'EDIT_EMAIL': False,
'DELEGATE_TO_EXISTING_USER': True,
'DEFAULT_ACCEPT_NOTIFICATIONS_FOR_GUEST': True,
}
def __init__(self, prefix):
self.__prefix = prefix
@property
def settings(self):
if not hasattr(self, '_settings'):
from django.conf import settings
self._settings = settings
return self._settings
@property
def DOCBOW_MAILBOX_PER_PAGE(self):
return getattr(self.settings, 'DOCBOW_MAILBOX_PER_PAGE', 20)
@property
def DOCBOW_MENU(self):
from django.utils.translation import gettext_noop
return getattr(
self.settings,
'DOCBOW_MENU',
[
('send-file-selector', gettext_noop('send-file_menu')),
('inbox', gettext_noop('inbox_menu')),
('outbox', gettext_noop('outbox_menu')),
('docbow_admin:index', gettext_noop('admin_menu')),
('profile', gettext_noop('profile_menu')),
('mailing-lists', gettext_noop('mailing-lists')),
('help', gettext_noop('help_menu')),
('contact', gettext_noop('contact_menu')),
],
)
@property
def BASE_URL(self):
return getattr(self.settings, 'DOCBOW_BASE_URL', 'http://localhost:8000')
@property
def TRUNCATE_FILENAME(self):
return getattr(self.settings, 'DOCBOW_TRUNCATE_FILENAME', 80)
@property
def MAX_FILE_SIZE(self):
return getattr(self.settings, 'DOCBOW_MAX_FILE_SIZE', 10 * 1024 * 1024)
@property
def MIME_BUFFER_SIZE(self):
return getattr(self.settings, 'DOCBOW_MIME_BUFFER_SIZE', 300000)
def __getattr__(self, name):
from django.conf import settings
if name not in self.__DEFAULTS:
raise AttributeError
return getattr(settings, self.__prefix + name, self.__DEFAULTS[name])
app_settings = AppSettings(prefix='DOCBOW_')
app_settings.__name__ = __name__
sys.modules[__name__] = app_settings

View File

@ -0,0 +1,8 @@
import django.apps
class AppConfig(django.apps.AppConfig):
name = 'docbow_project.docbow'
def ready(self):
from . import signals # noqa: F401

View File

@ -0,0 +1,52 @@
from django.contrib.auth.models import User
def set_auth_hash_getter(user, delegate):
if hasattr(user, 'get_session_auth_hash') and hasattr(delegate, 'get_session_auth_hash'):
user.get_session_auth_hash = delegate.get_session_auth_hash
class DelegationAuthBackend:
supports_object_permissions = False
supports_anonymous_user = False
def authenticate(self, request, username=None, password=None):
try:
if '-' in username:
prefix, suffix = username.rsplit('-', 1)
delegate = User.objects.get(username=username, docbowprofile__is_guest=True)
if delegate.check_password(password):
return delegate
except User.DoesNotExist:
pass
return None
def get_user(self, user_id):
try:
delegate = User.objects.get(pk=user_id, docbowprofile__is_guest=True)
user = User.objects.get(username=delegate.username.rsplit('-', 1)[0])
user.delegate = delegate
set_auth_hash_getter(user, delegate)
return user
except User.DoesNotExist:
return None
try:
import mellon.backends
class DocbowMellonAuthBackend(mellon.backends.SAMLBackend):
def get_user(self, user_id):
try:
delegate = User.objects.get(pk=user_id, docbowprofile__is_guest=True)
if delegate.delegations_by.count() != 1:
return None
user = delegate.delegations_by.first().by
user.delegate = delegate
set_auth_hash_getter(user, delegate)
return user
except User.DoesNotExist:
return super().get_user(user_id)
except ImportError:
pass

View File

@ -0,0 +1,39 @@
from django.contrib.auth import views as auth_views
from django.urls import path, re_path, reverse_lazy
from docbow_project.docbow import auth_views as docbow_auth_views
from docbow_project.docbow import forms, views
urlpatterns = [
path('login/', docbow_auth_views.login, {'template_name': 'registration/login.html'}, name='auth_login'),
path(
'logout/',
auth_views.LogoutView.as_view(template_name='registration/logout.html'),
name='auth_logout',
),
path('password/change/', views.password_change, name='auth_password_change'),
path(
'password/change/done/',
auth_views.PasswordChangeDoneView.as_view(),
name='auth_password_change_done',
),
path(
'password/reset/',
auth_views.PasswordResetView.as_view(
success_url=reverse_lazy('auth_password_reset_done'),
form_class=forms.PasswordResetFormWithLogging,
),
name='auth_password_reset',
),
re_path(
r'^password/reset/confirm/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>.+)/$',
auth_views.PasswordResetConfirmView.as_view(),
name='auth_password_reset_confirm',
),
path(
'password/reset/complete/',
auth_views.PasswordResetCompleteView.as_view(),
name='password_reset_complete',
),
path('password/reset/done/', views.password_reset_done, name='auth_password_reset_done'),
]

View File

@ -0,0 +1,29 @@
import urllib.parse
from django.conf import settings
from django.contrib.auth import views as auth_views
from django.http import HttpResponseRedirect
from django.shortcuts import resolve_url
if 'mellon' in settings.INSTALLED_APPS:
from mellon.utils import get_idps
else:
def get_idps():
return []
def login(request, *args, **kwargs):
if any(get_idps()):
if 'next' not in request.GET:
return HttpResponseRedirect(resolve_url('mellon_login'))
return HttpResponseRedirect(
resolve_url('mellon_login') + '?next=' + urllib.parse.quote(request.GET.get('next'))
)
return auth_views.LoginView.as_view(*args, **kwargs)(request)
def logout(request, *args, **kwargs):
if any(get_idps()):
return HttpResponseRedirect(resolve_url('mellon_logout'))
return auth_views.LogoutView.as_view(*args, **kwargs)(request)

View File

@ -0,0 +1,27 @@
class FormWithRequestMixin:
def get_form_kwargs(self, **kwargs):
kwargs = super().get_form_kwargs(**kwargs)
kwargs['request'] = self.request
return kwargs
class FormWithPrefixMixin:
# deprecated after Django 1.6
prefix = None
def get_form_kwargs(self, **kwargs):
kwargs = super().get_form_kwargs(**kwargs)
kwargs['prefix'] = self.prefix
return kwargs
class FormWithPostTarget(FormWithPrefixMixin):
def get_form_kwargs(self, **kwargs):
kwargs = super().get_form_kwargs(**kwargs)
if not self.is_post_target():
kwargs.pop('data', None)
kwargs.pop('files', None)
return kwargs
def is_post_target(self):
return self.prefix + '-validate' in self.request.POST

View File

@ -0,0 +1,11 @@
from django.conf import settings
from django.shortcuts import resolve_url
def settings_url_processor(request):
logout_url = settings.LOGOUT_URL
logout_url = resolve_url(logout_url)
return {
'logout_url': logout_url,
'portal_base_url': getattr(settings, 'PORTAL_BASE_URL', None),
}

View File

@ -1,37 +0,0 @@
try:
from functools import wraps
except ImportError:
from django.utils.functional import wraps # Python 2.4 fallback.
from django.shortcuts import redirect
from django.contrib import messages
from django.utils.translation import ugettext as _
from django.utils.decorators import available_attrs
def no_delegate(view_func):
'''
Forbid delegated account to use this view.
'''
@wraps(view_func, assigned=available_attrs(view_func))
def f(request, *args, **kwargs):
if hasattr(request.user, 'delegate'):
messages.warning(request, _('Your delegation does not allow you to do this action'))
return redirect('inbox')
return view_func(request, *args, **kwargs)
return f
def as_delegate(view_func):
'''
Replace the effective user by the real user of the delegate for the
given view.
'''
@wraps(view_func, assigned=available_attrs(view_func))
def f(request, *args, **kwargs):
if hasattr(request.user, 'delegate'):
old_user = request.user
request.user = request.user.delegate
out = view_func(request, *args, **kwargs)
request.user = old_user
return out
else:
return view_func(request, *args, **kwargs)
return f

View File

@ -0,0 +1,57 @@
from functools import wraps
from django.contrib import messages
from django.shortcuts import redirect
from django.utils.cache import patch_cache_control
from django.utils.translation import gettext as _
from django.views.decorators.cache import never_cache as old_never_cache
def no_delegate(view_func):
"""
Forbid delegated account to use this view.
"""
@wraps(view_func)
def f(request, *args, **kwargs):
if hasattr(request.user, 'delegate'):
messages.warning(request, _('Your delegation does not allow you to do this action'))
return redirect('inbox')
return view_func(request, *args, **kwargs)
return f
def as_delegate(view_func):
"""
Replace the effective user by the real user of the delegate for the
given view.
"""
@wraps(view_func)
def f(request, *args, **kwargs):
if hasattr(request.user, 'delegate'):
old_user = request.user
request.user = request.user.delegate
out = view_func(request, *args, **kwargs)
request.user = old_user
return out
else:
return view_func(request, *args, **kwargs)
return f
def never_cache(view_func):
'''Block client caching in all browsers.'''
view_func = old_never_cache(view_func)
@wraps(view_func)
def f(request, *args, **kwargs):
result = view_func(request, *args, **kwargs)
patch_cache_control(result, no_cache=True)
patch_cache_control(result, no_store=True)
patch_cache_control(result, must_revalidate=True)
return result
return f

Binary file not shown.

View File

@ -1,17 +1,21 @@
import re
from email.header import decode_header, make_header
from django.utils.encoding import force_str
# check spaces between encoded_words (and strip them)
sre = re.compile(r'\?=[ \t]+=\?')
# re pat for MIME encoded_word (without trailing spaces)
mre = re.compile(r'=\?[^?]*?\?[bq]\?[^? \t]*?\?=', re.I)
def decode_mime(m):
# substitute matching encoded_word with unicode equiv.
# substitute matching encoded_word with force_text equiv.
h = decode_header(m.group(0))
u = unicode(make_header(h))
u = force_str(make_header(h))
return u
def u2u_decode(s):
# utility function for (final) decoding of mime header
# note: resulting string is in one line (no \n within)
@ -20,4 +24,3 @@ def u2u_decode(s):
s = sre.sub('?==?', s)
u = mre.sub(decode_mime, s)
return u

Binary file not shown.

View File

@ -1,42 +1,57 @@
from django.contrib.auth.models import User
from django.forms import ValidationError, MultipleChoiceField
from django.utils.translation import ugettext as _
from django.forms import MultipleChoiceField, ValidationError
from django.utils.translation import gettext as _
from docbow_project.docbow import pyuca
from docbow_project.docbow.models import MailingList, username
from docbow_project.docbow.widgets import FilteredSelectMultiple, ForcedValueWidget
import pyuca
from models import username, MailingList
from widgets import ForcedValueWidget, FilteredSelectMultiple
def order_choices(choices):
return sorted(list(choices),
key=lambda x: pyuca.collator.sort_key(x[1]))
'''Sort choices using Unicode collations'''
return sorted(list(choices), key=lambda x: pyuca.collator.sort_key(x[1]))
def order_field_choices(field):
field.choices = order_choices(field.choices)
'''Order choices of this field'''
choices = list(field.choices)
field.choices = [choice for choice in choices if choice[1].startswith('---')] + order_choices(
[choice for choice in field.choices if not choice[1].startswith('---')]
)
print(field.choices)
class Func2Iter:
'''Transform a generator producing function into an iterator'''
class Func2Iter(object):
def __init__(self, func):
self.func = func
def __iter__(self):
return self.func().__iter__()
class RecipientField(MultipleChoiceField):
'''Field allowing selection among user or list for recipients'''
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
self.user_qs = kwargs.pop('user_qs', User.objects.filter())
self.user_qs = self.user_qs.filter(is_active=True, delegations_by__isnull=True)
self.list_qs = kwargs.pop('list_qs', MailingList.objects.all())
kwargs.setdefault('choices', Func2Iter(self.get_recipients_choices))
super(RecipientField, self).__init__(*args, **kwargs)
self.list_qs = kwargs.pop('list_qs', MailingList.objects.active())
super().__init__(*args, **kwargs)
self._choices = self.widget.choices = Func2Iter(self.get_recipients_choices)
def reset_choices(self):
'''Reset the list of choices'''
self._set_choices(Func2Iter(self.get_recipients_choices))
def get_recipients_choices(self):
'''
Create a unique list of recipients from the list of users and the
list of mailing-lists.
'''
"""
Create a unique list of recipients from the list of users and the
list of mailing-lists.
"""
users = self.user_qs
if self.user:
users = users.exclude(pk=self.user.pk)
@ -53,20 +68,18 @@ class RecipientField(MultipleChoiceField):
list_choices.append((i, mailing_list.name))
list_choices = order_choices(list_choices)
if list_choices and user_choices:
choices = list_choices+[('', '---')]+user_choices
choices = list_choices + [('', '---')] + user_choices
else:
choices = list_choices+user_choices
choices = list_choices + user_choices
if len(choices) == 1:
self.widget = ForcedValueWidget(value=[choices[0][0]],
display_value=choices[0][1])
self.widget = ForcedValueWidget(value=[choices[0][0]], display_value=choices[0][1])
else:
self.widget = FilteredSelectMultiple(_('Recipients'), False)
return choices
def clean(self, value):
value = super(RecipientField, self).clean(value)
'''Validate that the value is not empty.'''
value = super().clean(value)
if not value:
raise ValidationError(_(u'you must set at least one user recipient or one list recipient...'))
raise ValidationError(_('you must set at least one user recipient or one list recipient...'))
return value

View File

@ -1,20 +0,0 @@
[
{ "pk": null, "model": "docbow.Content", "fields": { "description": "Envoi en Commission" } },
{ "pk": null, "model": "docbow.Content", "fields": { "description": "Envoi de document(s)" } },
{ "pk": null, "model": "docbow.Content", "fields": { "description": "Texte(s) adopté(s) en séance plénière" } },
{ "pk": null, "model": "docbow.Content", "fields": { "description": "Transmission des IQO" } },
{ "pk": null, "model": "docbow.Content", "fields": { "description": "Décision de la Conférence des présidents" } },
{ "pk": null, "model": "docbow.Content", "fields": { "description": "Liste" } },
{ "pk": null, "model": "docbow.Content", "fields": { "description": "Objet et motivation - questions d'actualité" } },
{ "pk": null, "model": "docbow.Content", "fields": { "description": "Addendum" } },
{ "pk": null, "model": "docbow.Content", "fields": { "description": "Corrigendum" } },
{ "pk": null, "model": "docbow.Content", "fields": { "description": "Erratum" } },
{ "pk": null, "model": "docbow.Content", "fields": { "description": "Avis" } },
{ "pk": null, "model": "docbow.Content", "fields": { "description": "Budget" } },
{ "pk": null, "model": "docbow.Content", "fields": { "description": "Ajustement" } },
{ "pk": null, "model": "docbow.Content", "fields": { "description": "Préfiguration" } },
{ "pk": null, "model": "docbow.Content", "fields": { "description": "Budget de fonctionnement" } },
{ "pk": null, "model": "docbow.Content", "fields": { "description": "Cahier d'observations de la Cour des comptes" } },
{ "pk": null, "model": "docbow.Content", "fields": { "description": "Règlement définitif" } },
{ "pk": null, "model": "docbow.Content", "fields": { "description": "Décret-programme" } }
]

View File

@ -1,28 +0,0 @@
[
{ "pk": null, "model": "docbow.filetype", "fields": {"name": "BT"}},
{ "pk": null, "model": "docbow.filetype", "fields": {"name": "Budget"}},
{ "pk": null, "model": "docbow.filetype", "fields": {"name": "Circulaire"}},
{ "pk": null, "model": "docbow.filetype", "fields": {"name": "CRA"}},
{ "pk": null, "model": "docbow.filetype", "fields": {"name": "CRI/CRIC"}},
{ "pk": null, "model": "docbow.filetype", "fields": {"name": "Correction CRI/CRIC"}},
{ "pk": null, "model": "docbow.filetype", "fields": {"name": "Décret-Projet"}},
{ "pk": null, "model": "docbow.filetype", "fields": {"name": "Décret-Proposition"}},
{ "pk": null, "model": "docbow.filetype", "fields": {"name": "IQO"}},
{ "pk": null, "model": "docbow.filetype", "fields": {"name": "ODJC"}},
{ "pk": null, "model": "docbow.filetype", "fields": {"name": "ODJS"}},
{ "pk": null, "model": "docbow.filetype", "fields": {"name": "QA"}},
{ "pk": null, "model": "docbow.filetype", "fields": {"name": "QE-question"}},
{ "pk": null, "model": "docbow.filetype", "fields": {"name": "QE-réponse"}},
{ "pk": null, "model": "docbow.filetype", "fields": {"name": "Résolution"}},
{ "pk": null, "model": "docbow.filetype", "fields": {"name": "Notification d'une décision du Gouvernement"}},
{ "pk": null, "model": "docbow.filetype", "fields": {"name": "ODJ du Gouvernement"}},
{ "pk": null, "model": "docbow.filetype", "fields": {"name": "Accord"}},
{ "pk": null, "model": "docbow.filetype", "fields": {"name": "Arrêt CC"}},
{ "pk": null, "model": "docbow.filetype", "fields": {"name": "Motion"}},
{ "pk": null, "model": "docbow.filetype", "fields": {"name": "Rapport hors projet/proposition"}},
{ "pk": null, "model": "docbow.filetype", "fields": {"name": "Documents européens"}},
{ "pk": null, "model": "docbow.filetype", "fields": {"name": "Documents préparatoires"}},
{ "pk": null, "model": "docbow.filetype", "fields": {"name": "Arrêtés ministériels"}},
{ "pk": null, "model": "docbow.filetype", "fields": {"name": "Divers"}}
]

View File

@ -66,7 +66,7 @@
]
},
"model": "auth.group",
"pk": null
"pk": ["Administrateurs des listes"]
},
{
"fields": {
@ -182,11 +182,6 @@
"docbow",
"sendinglimitation"
],
[
"change_logline",
"auth",
"logline"
],
[
"add_sendinglimitation",
"docbow",
@ -205,7 +200,7 @@
]
},
"model": "auth.group",
"pk": null
"pk": ["Administrateurs"]
},
{
"fields": {
@ -213,6 +208,6 @@
"permissions": []
},
"model": "auth.group",
"pk": null
"pk": ["Contact \u00ab Administrateur du syst\u00e8me \u00bb"]
}
]

View File

@ -1,194 +1,717 @@
import collections
import datetime
import hashlib
import hmac
import logging
import os.path
import unicodedata
import urllib.parse
from django.forms import ModelForm, Form, \
ModelMultipleChoiceField, Textarea, EmailField, \
CharField
from django import forms
from django.contrib.auth.models import User
from django.utils.translation import ugettext as _
from django.conf import settings
from django.contrib.admin.widgets import FilteredSelectMultiple as AdminFilteredSelectMultiple
from django.forms import ValidationError
from django.contrib.auth.forms import PasswordChangeForm, PasswordResetForm
from django.contrib.auth.models import User
from django.contrib.auth.tokens import default_token_generator
from django.contrib.sites.shortcuts import get_current_site
from django.db.models.query import Q
from django.forms import CharField, EmailField, Form, ModelChoiceField, ModelForm, Textarea, ValidationError
from django.utils.encoding import force_bytes, force_str
from django.utils.http import urlsafe_base64_encode
from django.utils.translation import gettext as _
from django_journal import journal as django_journal
from models import Document, username, MailingList, FileType, Content, \
AttachedFile, AutomaticForwarding
from uni_form.helpers import FormHelper, Submit, Layout, ButtonHolder
import pyuca
from widgets import TextInpuWithPredefinedValues, JqueryFileUploadInput
from fields import RecipientField
from docbow_project.docbow import app_settings, fields, models, notification, pyuca, widgets
from docbow_project.docbow.fields import RecipientField
from docbow_project.docbow.middleware import get_extra
from docbow_project.docbow.models import (
AttachedFile,
AutomaticForwarding,
Content,
DocbowProfile,
Document,
FileTypeAttachedFileKind,
MailingList,
is_guest,
non_guest_users,
username,
)
from docbow_project.docbow.utils import a2_wscall, mime_types_to_extensions, truncate_filename
from docbow_project.docbow.validators import phone_normalize, validate_fr_be_phone
from docbow_project.docbow.widgets import (
FilteredSelectMultiple,
JqueryFileUploadInput,
TextInpuWithPredefinedValues,
)
class UserChoiceField(ModelMultipleChoiceField):
def label_from_instance(self, obj):
return username(obj)
logger = logging.getLogger(__name__)
def order_choices(choices):
return sorted(list(choices),
key=lambda x: pyuca.collator.sort_key(x[1]))
def order_field_choices(field):
field.choices = order_choices(field.choices)
class RecipientForm:
"""
Base form mixin for forms containing a RecipienField, i.e. all
forms for sending documents.
"""
class RecipientForm(object):
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
user_qs = kwargs.pop('user_qs', User.objects.filter())
user_qs = user_qs.filter(is_active=True, delegations_by__isnull=True)
list_qs = kwargs.pop('list_qs', MailingList.objects.all())
super(RecipientForm, self).__init__(*args, **kwargs)
self.user = user
user_qs = kwargs.pop('user_qs', None)
if user_qs is None:
user_qs = non_guest_users()
list_qs = kwargs.pop('list_qs', MailingList.objects.active())
super().__init__(*args, **kwargs)
self.fields['recipients'].user = user
self.fields['recipients'].user_qs = user_qs
self.fields['recipients'].list_qs = list_qs
self.fields['recipients'].reset_choices()
class ForwardingForm(RecipientForm, Form):
recipients = RecipientField(label=_('Recipients'))
layout = Layout(
'recipients',
ButtonHolder(
Submit('forward', _('forward the document'))),
)
helper = FormHelper()
helper.form_method = 'POST'
helper.add_layout(layout)
helper.form_action = ''
class ForwardingForm(RecipientForm, Form):
'''Form for forwarding documents'''
recipients = RecipientField(label=_('Recipients'), required=True)
sender = ModelChoiceField(label=_('Sender'), queryset=User.objects.all())
class Media:
js = ('js/askdirtyform.js',)
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
assert self.user
self.default_sender = self.user
if is_guest(self.default_sender):
self.default_sender = self.user.delegations_by.all()[0].by
delegations = User.objects.none()
else:
self.default_sender = self.user
delegations = (
non_guest_users()
.filter(Q(id=self.user.id) | Q(delegations_to__to=self.user))
.order_by('last_name', 'first_name', 'username')
.distinct()
)
super().__init__(*args, **kwargs)
if len(delegations) > 1:
self.fields['sender'].queryset = delegations
self.fields['sender'].label_from_instance = lambda y: username(y)
else:
del self.fields['sender']
def clean(self):
cleaned_data = super().clean()
if not cleaned_data.get('sender'):
cleaned_data['sender'] = self.default_sender
return cleaned_data
def max_filename_length():
field = AttachedFile._meta.get_field_by_name('content')[0]
"""Compute the maximum filename length from the possible maximum length of
the AttachedFile model."""
field = AttachedFile._meta.get_field('content')
prefix = field.generate_filename(None, '')
max_length = field.max_length
return max_length - len(prefix)
class FileForm(RecipientForm, ModelForm):
'''Form for creating a new mailing'''
user = None
recipients = RecipientField(label=_('Recipients'))
layout = Layout(
'filetype',
'content',
'recipients',
'comment',
ButtonHolder(
Submit('send', _('send a file'))),
)
content = forms.Field(label=_('Attached files'),
widget=JqueryFileUploadInput(max_filename_length=max_filename_length()))
helper = FormHelper()
helper.form_method = 'POST'
helper.add_layout(layout)
helper.form_action = ''
def __init__(self, *args, **kwargs):
self.filetype_qs = kwargs.pop('filetype_qs', FileType.objects.all())
super(FileForm, self).__init__(*args, **kwargs)
old_widget = self.fields['comment'].widget
self.fields['comment'].widget = TextInpuWithPredefinedValues(attrs=old_widget.attrs,
choices=self.get_predefined_comments())
self.fields['filetype'].empty_label = '---'
self.fields['filetype'].queryset = self.filetype_qs
order_field_choices(self.fields['filetype'])
def get_predefined_comments(self):
choices = [ (content.description,)*2 for content in Content.objects.all() ]
choices.insert(0, ('---','---'))
return choices
class Meta:
model = Document
exclude = ('sender','date', 'to_user', 'to_list')
exclude = ('filetype', 'date', 'to_user', 'to_list', '_timestamp', 'real_sender', 'reply_to')
widgets = {'extra_senders': FilteredSelectMultiple(_('Extra Senders'), False)}
class Media:
css = { 'all': ('css/send-file.css', 'docbow/css/send_file_form.css'
)}
css = {'all': ('docbow/css/send-file.css', 'docbow/css/send_file_form.css')}
js = ('js/askdirtyform.js', 'js/url-preload.js', 'js/foldable.js')
def __init__(self, *args, **kwargs):
'''Initialize the form.'''
self.file_type = kwargs.pop('file_type')
self.attached_file_kinds = self.file_type.filetypeattachedfilekind_set.all()
self.reply_to = kwargs.pop('reply_to', None)
self.default_sender = kwargs.pop('default_sender', None)
self.delegations = kwargs.pop('delegations', [])
initial = kwargs.setdefault('initial', {})
if self.reply_to:
doc = self.reply_to
initial['sender'] = kwargs.get('user', None)
initial['recipients'] = ['user-%s' % doc.sender.id]
if doc.extra_senders.exists():
initial['recipients'] += ['user-%s' % sender.pk for sender in doc.extra_senders.all()]
initial['comment'] = 'Re: ' + doc.comment
super().__init__(*args, **kwargs)
self.content_fields = []
if self.attached_file_kinds:
insert_index = 2
for attached_file_kind in self.attached_file_kinds:
key = 'content-%s' % attached_file_kind.id
self.content_fields.append((key, attached_file_kind))
label = attached_file_kind.name
mime_types = attached_file_kind.get_mime_types()
widget = JqueryFileUploadInput(
max_filename_length=max_filename_length(),
extensions=mime_types_to_extensions(mime_types),
attached_file_kind=attached_file_kind,
)
self.fields[key] = forms.Field(label=label, widget=widget)
insert_index += 1
else:
attached_file_kind = FileTypeAttachedFileKind(mime_types='*/*')
self.content_fields = [('content', attached_file_kind)]
widget = JqueryFileUploadInput(
max_filename_length=max_filename_length(), attached_file_kind=attached_file_kind
)
self.fields['content'] = forms.Field(label=_('Attached files'), widget=widget)
old_widget = self.fields['comment'].widget
self.fields['comment'].widget = TextInpuWithPredefinedValues(
attrs=old_widget.attrs, choices=self.get_predefined_comments()
)
if len(self.delegations) > 1:
self.fields['sender'].queryset = self.delegations
self.fields['sender'].label_from_instance = lambda y: username(y)
fields.order_field_choices(self.fields['sender'])
else:
del self.fields['sender']
self.fields['private'].widget.attrs['class'] = 'checkboxinput'
if not app_settings.PRIVATE_DOCUMENTS:
del self.fields['private']
if self.reply_to or not settings.EXTRA_SENDERS or not self.file_type.extra_senders:
del self.fields['extra_senders']
else:
self.fields['extra_senders'].required = False
extra_senders_qs = User.objects.filter(is_active=True).exclude(docbowprofile__is_guest=True)
if self.user:
extra_senders_qs = extra_senders_qs.exclude(pk=self.user.pk)
extra_senders = [(user.pk, username(user)) for user in extra_senders_qs]
extra_senders = sorted(list(extra_senders), key=lambda x: pyuca.collator.sort_key(x[1]))
self.fields['extra_senders'].choices = extra_senders
def template_content_fields(self):
return [self[name] for name, _ in self.content_fields]
def get_predefined_comments(self):
"""Return a list of predefined comments, structured as choice list for
a form field.
"""
choices = [(content.description,) * 2 for content in Content.objects.all()]
choices.insert(0, ('---', '---'))
return choices
def clean(self):
cleaned_data = super(FileForm, self).clean()
upload_id, files = self.cleaned_data.get('content', (None, []))
if not files:
self._errors['content'] = self.error_class([_(u'You must attach at least one file.')])
'''Validate that there is at least one file attached to this mailing.'''
cleaned_data = super().clean()
for field, attached_file_kind in self.content_fields:
upload_id, upload_files = self.cleaned_data.get(field, (None, []))
max_files = attached_file_kind.cardinality
min_files = attached_file_kind.minimum
errors = []
if max_files and len(upload_files) > max_files:
errors.append(_('You must attach at most %d file(s).') % max_files)
if min_files and len(upload_files) < min_files:
errors.append(_('You must attach at least %d file(s).') % min_files)
for upload_file in upload_files:
if not attached_file_kind.match_file(upload_file):
mime_types = attached_file_kind.get_mime_types()
file_name = os.path.basename(upload_file.name)
msg = _('The file name "{file_name}" does not match the patterns "{extensions}".').format(
file_name=file_name, extensions=mime_types_to_extensions(mime_types)
)
errors.append(msg)
if errors:
self._errors[field] = self.error_class(errors)
if 'extra_senders' in cleaned_data:
if len(cleaned_data['extra_senders']) > self.file_type.extra_senders:
self._errors['extra_senders'] = self.error_class(
[_('No more than %s additional senders allowed') % self.file_type.extra_senders]
)
return cleaned_data
def save(self, commit=False):
self.instance.filetype = self.file_type
if not self.instance.sender_id:
assert self.default_sender
self.instance.sender = self.default_sender
if self.reply_to:
self.instance.reply_to = self.reply_to
if self.user != self.instance.sender:
self.instance.real_sender = username(self.user)
return super().save(commit=commit)
def save_attachments(self):
"""Create a new AttachedFile object for each uploaded file; and attach
them to the newly created Document object."""
instance = self.instance
upload_id, uploaded_files = self.cleaned_data.get('content', (None, []))
for uploaded_file in uploaded_files:
uploaded_file.name = os.path.basename(uploaded_file.name)
AttachedFile(document=instance,
name=os.path.basename(uploaded_file.name),
content=uploaded_file).save()
for field, attached_file_kind in self.content_fields:
upload_id, uploaded_files = self.cleaned_data.get(field, (None, []))
for uploaded_file in uploaded_files:
uploaded_file.name = os.path.basename(uploaded_file.name)
AttachedFile(
document=instance,
kind=attached_file_kind if attached_file_kind.id else None,
name=truncate_filename(uploaded_file.name),
content=uploaded_file,
).save()
class ContactForm(Form):
layout = Layout(
'subject',
'message',
ButtonHolder(
Submit('send', _('send your message'))),
)
helper = FormHelper()
helper.form_method = 'POST'
helper.add_layout(layout)
helper.form_action = ''
'''Form to contact administrators of the platform for logged in users.'''
subject = forms.CharField(max_length=100, label=_('Subject'),
required=True)
message = forms.CharField(widget=Textarea(attrs={'rows': 25, 'cols': 80}),
label=_('Message'), required=True)
subject = forms.CharField(max_length=100, label=_('Subject'), required=True)
message = forms.CharField(
widget=Textarea(attrs={'rows': 25, 'cols': 80}), label=_('Message'), required=True
)
class Media:
js = ('js/askdirtyform.js',)
class AnonymousContactForm(ContactForm):
layout = Layout(
'name',
'email',
'phone_number',
'subject',
'message',
ButtonHolder(
Submit('send', _('send your message'))),
)
helper = FormHelper()
helper.form_method = 'POST'
helper.add_layout(layout)
helper.form_action = ''
'''Form to contact administrators of the platform for anonymous users.'''
name = forms.CharField(max_length=100, label=_('Name'), required=True)
email = forms.EmailField(max_length=100, label=_('Email'), required=False)
phone_number = forms.CharField(max_length=100, label=_('Phone number'),
required=False)
phone_number = forms.CharField(max_length=100, label=_('Phone number'), required=False)
class MailingListForm(ModelForm):
def __init__(self, *args, **kwargs):
ModelForm.__init__(self, *args, **kwargs)
self.fields['members'].queryset = User.objects.all().order_by('username')
self.fields['members'].label_from_instance = lambda y: username(y)
'''Admin form to edit MailingList objects'''
class Meta:
model = MailingList
fields = ('name', 'is_active', 'members', 'mailing_list_members')
widgets = {
'members': AdminFilteredSelectMultiple(_('Persons'), False),
'members': AdminFilteredSelectMultiple(_('Persons'), False),
}
def __init__(self, *args, **kwargs):
'''Orders members by their username, use their username to display them.'''
ModelForm.__init__(self, *args, **kwargs)
self.fields['members'].queryset = non_guest_users().order_by('username')
self.fields['members'].label_from_instance = lambda y: username(y)
class UserChoiceField(ModelChoiceField):
def label_from_instance(self, user):
if user.first_name or user.last_name:
return user.first_name + ' ' + user.last_name
return user.username
class DelegationForm(Form):
layout = Layout(
'first_name', 'last_name', 'email',
ButtonHolder(
Submit('create', _('Create a new delegation'))),
)
helper = FormHelper()
helper.form_method = 'POST'
helper.add_layout(layout)
helper.form_action = ''
'''Form to manager delegations of users'''
first_name = CharField(label=_('Firstname'))
last_name = CharField(label=_('Lastname'))
email = EmailField(label=_('Email'))
first_name = CharField(label=_('Firstname'), max_length=30, required=False)
last_name = CharField(label=_('Lastname'), max_length=30, required=False)
email = EmailField(label=_('Email'), required=False)
existing_user = UserChoiceField(label=_('User'), required=False, queryset=User.objects.all())
class AutomaticForwardingForm(ModelForm):
class Meta:
model = AutomaticForwarding
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user', None)
self.delegatees = kwargs.pop('delegatees', [])
self._request = kwargs.pop('request')
super().__init__(*args, **kwargs)
qs = non_guest_users()
if self.user:
qs = qs.exclude(id=self.user.id)
if self.delegatees:
qs = qs.exclude(id__in=[u.id for u in self.delegatees])
self.fields['existing_user'].queryset = qs.order_by('first_name', 'last_name')
if not app_settings.DELEGATE_TO_EXISTING_USER:
del self.fields['existing_user']
def clean(self):
cleaned_data = super(AutomaticForwardingForm, self).clean()
cleaned_data = super().clean()
ok1 = bool(cleaned_data.get('first_name'))
ok2 = bool(cleaned_data.get('last_name'))
ok3 = bool(cleaned_data.get('email'))
new = ok1 and ok2 and ok3
ok4 = bool(cleaned_data.get('existing_user'))
if not ((ok1 or ok2 or ok3) ^ ok4):
raise ValidationError(
_(
'You must choose between creating a new '
'user or delegating to an existing one; the two are mutually '
'exclusive.'
)
)
if not new and (ok1 or ok2 or ok3):
raise ValidationError(
_('To create a new user you must give a first name, a last name and a valid email')
)
if new:
email = cleaned_data.get('email')
if email == self.user.email:
raise ValidationError(_('Email is yours, you cannot delegate to yourself'))
if any(delegate.email == email for delegate in self.delegatees):
raise ValidationError(_('A delegation with the same email already exists'))
qs = non_guest_users().filter(email=email)
if qs.exists():
list_of_names = ', '.join([user.get_full_name() for user in qs])
self.data = {}
self.is_bound = False
raise ValidationError(
_('This email belong to existing user(s) {0}, look in the list of existing users').format(
list_of_names
)
)
if 'mellon' in app_settings.settings.INSTALLED_APPS:
# Create user
url = urllib.parse.urljoin(app_settings.settings.AUTHENTIC_URL, 'api/users/')
json = {
'first_name': cleaned_data['first_name'],
'last_name': cleaned_data['last_name'],
'email': email,
'send_registration_email': True,
'send_registration_email_next_url': self._request.build_absolute_uri('/'),
}
err, json_data, err_desc = a2_wscall(url, 'post', json)
if err:
raise ValidationError(err_desc)
cleaned_data['name_id'] = json_data['uuid']
# Give created user a role
role_uuid = getattr(app_settings.settings, 'AUTHENTIC_ROLE', None)
if role_uuid:
url = urllib.parse.urljoin(
app_settings.settings.AUTHENTIC_URL,
'api/roles/%s/members/%s/' % (role_uuid, json_data['uuid']),
)
err, json_data, err_desc = a2_wscall(url, 'post')
if err:
raise ValidationError(err_desc)
return cleaned_data
class AutomaticForwardingForm(ModelForm):
'''Admin form for editing AutomaticForwarding objects'''
class Meta:
model = AutomaticForwarding
fields = '__all__'
def clean(self):
'''Validate that the forwarding rule contains at least one recipient.'''
cleaned_data = super().clean()
if not cleaned_data.get('forward_to_user') and not cleaned_data.get('forward_to_list'):
raise ValidationError(_('A forwarding rule must have at least one recipient, person or list.'))
return cleaned_data
class ProfileForm(ModelForm):
"""User form for editing personal informations like email and mobile
phone.
"""
class Meta:
model = DocbowProfile
fields = ()
if app_settings.MOBILE_PHONE:
fields += ('accept_notifications', 'mobile_phone')
if app_settings.PERSONAL_EMAIL:
fields += ('personal_email',)
def __init__(self, request, *args, **kwargs):
"""Initialize the form object.
Define a custom help text.
"""
self.request = request
ModelForm.__init__(self, *args, **kwargs)
if app_settings.MOBILE_PHONE:
self.fields['mobile_phone'].help_text = _(
'Use international phone number '
'format, i.e +[country code][number]. A challenge SMS will be sent to you to validate it.'
)
def clean_mobile_phone(self):
"""Validate the mobile phone number by sending a HMAC signature as a code by SMS
to the phone number.
The HMAC code is valid for one day.
"""
if self.cleaned_data.get('mobile_phone'):
mobile_phone = phone_normalize(self.cleaned_data['mobile_phone'])
validate_fr_be_phone(mobile_phone)
self.cleaned_data['mobile_phone'] = mobile_phone
if not self.instance or self.instance.mobile_phone != mobile_phone:
date = datetime.date.today()
code = hmac.new(
force_bytes(settings.SECRET_KEY), force_str(date) + mobile_phone, hashlib.sha1
).hexdigest()
code = '%06d' % (int(code, 16) % 1000000)
key = '%s-code' % self.prefix if self.prefix else 'code'
if self.data.get(key, '').strip() != code:
def send_sms(mobile_phone, code):
try:
sms_carrier = notification.SMSNotifier.get_carrier()
sms_carrier.send_sms((mobile_phone,), 'code is ' + code, no_stop=False)
return True
except Exception:
logger.exception(
'unable to send SMS verification code %r to %r', code, mobile_phone
)
self.request.record(
'error',
'unable to send SMS verification code {code} to {mobile_phone}',
mobile_phone=mobile_phone,
code=code,
)
return False
if not send_sms(mobile_phone, code):
raise ValidationError(_('Unable to send you the SMS code, try again.'))
self.fields['code'] = CharField(label='Code')
raise ValidationError(_('Enter code received by SMS'))
return self.cleaned_data.get('mobile_phone')
def save(self, commit=True):
'''Attach current user to the newly created profile object.'''
instance = ModelForm.save(self, commit=False)
instance.user = self.request.user
if commit:
instance.save()
return instance
def _unicode_ci_compare(s1, s2):
"""
Perform case-insensitive comparison of two identifiers, using the
recommended algorithm from Unicode Technical Report 36, section
2.11.2(B)(2).
"""
normalized1 = unicodedata.normalize('NFKC', s1)
normalized2 = unicodedata.normalize('NFKC', s2)
return normalized1.casefold() == normalized2.casefold()
class PasswordResetFormWithLogging(PasswordResetForm):
email = forms.EmailField(widget=forms.HiddenInput(), required=False)
identifier = forms.CharField(label=_('E-mail or identifier'), max_length=75)
def clean_email(self):
return None
def clean_identifier(self):
"""
Validates that an active user exists with the given email address.
"""
identifier = self.cleaned_data['identifier']
self.users_cache = User.objects.filter(
Q(email__iexact=identifier)
| Q(username=identifier)
| Q(docbowprofile__personal_email=identifier),
is_active=True,
).distinct()
for user in self.users_cache:
try:
if not user.email or _unicode_ci_compare(user.docbowprofile.personal_email, identifier):
user.email = user.docbowprofile.personal_email
except DocbowProfile.DoesNotExist:
pass
self.users_cache = [user for user in self.users_cache if user.email]
if not len(self.users_cache):
raise forms.ValidationError(
_(
"That e-mail address or identifier doesn't have an associated user account. Are you sure you've registered?"
)
)
return identifier
def get_users(self, *args):
return self.users_cache
def save(
self,
domain_override=None,
subject_template_name='registration/password_reset_subject.txt',
email_template_name='registration/password_reset_email.html',
use_https=False,
token_generator=default_token_generator,
from_email=None,
request=None,
html_email_template_name=None,
extra_email_context=None,
):
"""
Generates a one-use only link for resetting password and sends to the
user.
"""
email = self.cleaned_data['email']
for user in self.get_users(email):
if not domain_override:
current_site = get_current_site(request)
site_name = current_site.name
domain = current_site.domain
else:
site_name = domain = domain_override
user_email = getattr(user, 'email')
context = {
'email': user_email,
'domain': domain,
'site_name': site_name,
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
'user': user,
'token': token_generator.make_token(user),
'protocol': 'https' if use_https else 'http',
}
if extra_email_context is not None:
context.update(extra_email_context)
self.send_mail(
subject_template_name,
email_template_name,
context,
from_email,
user_email,
html_email_template_name=html_email_template_name,
)
for user in self.users_cache:
django_journal.record(
'password-reset',
'password reset link sent to {email}',
user=user,
email=user.email,
ip=get_extra()['ip'],
)
class PasswordChangeFormWithLogging(PasswordChangeForm):
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
django_journal.record('password-change', 'changed its email', user=self.user, ip=get_extra()['ip'])
class FilterForm(forms.Form):
not_before = forms.DateField(label=_('From'), required=False, localize=True)
not_after = forms.DateField(label=_('To'), required=False, localize=True)
search_terms = forms.CharField(label=_('Search terms'), required=False)
class Media:
js = (
'jquery-ui/js/jquery-ui-1.12.1-autocomplete-datepicker.min.js',
'docbow/js/filter-form.js',
)
css = {
'all': ('jquery-ui/css/jquery-ui-1.12.1.css',),
}
def __init__(self, *args, **kwargs):
request = kwargs.pop('request')
outbox = kwargs.pop('outbox', False)
super().__init__(*args, **kwargs)
self.fields['search_terms'].widget.attrs['data-boxtype'] = 'outbox' if outbox else 'inbox'
for field in ('sort', 'page'):
if field in request.GET:
self.fields[field] = forms.CharField(initial=request.GET.get(field), widget=forms.HiddenInput)
def clean(self):
cleaned_data = super().clean()
if (
cleaned_data.get('not_before')
and cleaned_data.get('not_after')
and cleaned_data['not_before'] > cleaned_data['not_after']
):
raise ValidationError(_('From must be inferior or equal to To'))
return cleaned_data
class EmailForm(ModelForm):
old_email = forms.EmailField(
label=_('Old email'), required=False, widget=forms.TextInput(attrs={'disabled': 'on'})
)
password = forms.CharField(label=_('Password'), widget=forms.PasswordInput())
email = forms.EmailField(label=_('New email'), required=True, initial='')
email2 = forms.EmailField(label=_('New email (repeated)'), required=True, widget=forms.TextInput())
class Meta:
model = User
fields = ('email',)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.initial['email'] = ''
self.initial['old_email'] = self.instance.email
def clean_password(self):
password = self.cleaned_data['password']
if not self.instance.check_password(password):
raise ValidationError(_('password incorrect'))
return password
def clean(self):
cleaned_data = super().clean()
email = cleaned_data.get('email')
email2 = cleaned_data.get('email2')
if email and email2 and email != email2:
self._errors['email'] = self.error_class([_('emails are not equal')])
return cleaned_data
class NotificationPreferencesForm(Form):
class Media:
js = ('docbow/js/checkall.js',)
def __init__(self, request, *args, **kwargs):
self.user = request.user
self.notifiers = notification.get_notifiers()
self.filetypes = models.FileType.objects.all()
self.choices = []
self.initials = {}
self.kinds = []
for notifier in self.notifiers:
self.choices.append((notifier.key, notifier.description))
self.kinds.append(notifier.key)
for filetype in self.filetypes:
self.initials[filetype.id] = set(self.kinds)
for np in models.NotificationPreference.objects.filter(user=self.user):
if not np.value:
self.initials[np.filetype_id].remove(np.kind)
super().__init__(*args, **kwargs)
for filetype in self.filetypes:
key = 'filetype-%s' % filetype.id
self.fields[key] = forms.MultipleChoiceField(
label=force_str(filetype),
choices=self.choices,
initial=self.initials[filetype.id],
widget=widgets.CheckboxMultipleSelect,
required=False,
)
def save(self):
cleaned_data = self.cleaned_data
adds = collections.defaultdict(lambda: [])
removes = collections.defaultdict(lambda: [])
for key in cleaned_data:
filetype_id = int(key.split('-')[1])
new = set(cleaned_data[key])
old = self.initials[filetype_id]
remove = old - new
for kind in remove:
removes[kind].append(filetype_id)
for kind in new - old:
adds[kind].append(filetype_id)
for kind in adds:
models.NotificationPreference.objects.filter(
user=self.user, kind=kind, filetype__in=adds[kind]
).delete()
for kind in removes:
for filetype_id in removes[kind]:
filetype = models.FileType.objects.get(id=filetype_id)
np, created = models.NotificationPreference.objects.get_or_create(
user=self.user, kind=kind, filetype=filetype, value=False
)

View File

@ -0,0 +1,8 @@
import logging
class ForceDebugFilter(logging.Filter):
def filter(self, record):
record.levelno = logging.DEBUG
record.levelname = 'DEBUG'
return super().filter(record)

View File

@ -1,12 +0,0 @@
import logging
class DjangoLoggerAdapter(logging.LoggerAdapter):
def __init__(self, logger, request):
user = request.user
if hasattr(user, 'delegate'):
user = '%s/%s' % (user.delegate, user)
extra = { 'ip': request.META.get('REMOTE_ADDR', '-'), 'user': user }
logging.LoggerAdapter.__init__(self, logger, extra)
def get_logger(request, domain='docbow.views'):
return DjangoLoggerAdapter(logging.getLogger(domain), request)

View File

@ -0,0 +1,57 @@
import locale
from django.contrib.auth.models import User
from django.core.management.base import BaseCommand
from django.db import transaction
from django.utils.encoding import force_str
from docbow_project.docbow.models import MailingList
def get_object(model, ref):
'''Try to get a model by id or by name'''
if ref.isdigit():
return model.objects.get(id=int(ref))
else:
return model.objects.get(name=ref)
class Command(BaseCommand):
help = '''Create or update a list'''
def add_arguments(self, parser):
parser.add_argument('ml_name', type=str, help='Name of the mailing list')
parser.add_argument(
'--add-list',
action='append',
help='add a list as a sublist',
default=[],
)
parser.add_argument(
'--remove-list',
action='append',
help='remove list as a sublist',
default=[],
)
parser.add_argument('--add-user', action='append', help='add a user member', default=[])
parser.add_argument('--remove-user', action='append', help='remove a user member', default=[])
@transaction.atomic
def handle(self, **options):
locale.setlocale(locale.LC_ALL, '')
locale_encoding = locale.nl_langinfo(locale.CODESET)
mailing_list, created = MailingList.objects.get_or_create(
name=force_str(options['ml_name'], locale_encoding)
)
for name in options['add_list']:
ml = get_object(MailingList, name)
mailing_list.mailing_list_members.add(ml)
for name in options['remove_list']:
ml = get_object(MailingList, name)
mailing_list.mailing_list_members.remove(ml)
for g in options['add_user']:
g = get_object(User, g)
mailing_list.members.add(g)
for g in options['remove_user']:
g = get_object(User, g)
mailing_list.members.remove(g)

View File

@ -0,0 +1,79 @@
from optparse import make_option
from django.contrib.auth.models import Group, User
from django.core.management.base import BaseCommand, CommandError
from django.db import transaction
from django.utils.encoding import force_str
from docbow_project.docbow.models import DocbowProfile, MailingList
def get_object(model, ref):
'''Try to get a model by id or by name'''
if ref.isdigit():
return model.objects.get(id=int(ref))
else:
return model.objects.get(name=ref)
class Command(BaseCommand):
args = '<username>'
help = '''Create a new user or update an existing user
List and groups can be referred by name or by id.
'''
option_list = BaseCommand.option_list + (
make_option('--first-name', help='set first name'),
make_option('--last-name', help='set last name'),
make_option('--email', help='set email'),
make_option('--mobile-phone', help='set mobile phone used for SMS notifications'),
make_option('--personal-email', help='set personal email'),
make_option('--add-list', help='add user to list', action='append', default=[]),
make_option('--add-group', help='add user to group', action='append', default=[]),
make_option('--remove-list', help='remove user from list', action='append', default=[]),
make_option('--remove-group', help='remove user from group', action='append', default=[]),
make_option(
'--activate', action='store_true', help='activate the user (default at creation)', default=None
),
make_option('--deactivate', dest='activate', action='store_false', help='deactivate the user'),
make_option('--superuser', action='store_true', help='set the superuser flag', default=None),
make_option(
'--no-superuser', dest='superuser', action='store_false', help='unset the superuser flag'
),
)
@transaction.atomic
def handle(self, *args, **options):
if len(args) != 1:
raise CommandError('username argument is mandatory')
user, created = User.objects.get_or_create(username=args[0])
if options['first_name']:
user.first_name = force_str(options['first_name'], 'utf-8')
if options['last_name']:
user.last_name = force_str(options['last_name'], 'utf-8')
if options['email']:
user.email = force_str(options['email'], 'utf-8')
if options['activate'] is not None:
user.is_active = options['activate']
if options['superuser'] is not None:
user.is_superuser = options['superuser']
for name in options['add_list']:
ml = get_object(MailingList, name)
ml.members.add(user)
for name in options['remove_list']:
ml = get_object(MailingList, name)
ml.members.remove(user)
for g in options['add_group']:
g = get_object(Group, g)
user.groups.add(g)
for g in options['remove_group']:
g = get_object(Group, g)
user.groups.remove(g)
profile, created = DocbowProfile.objects.get_or_create(user=user)
if options['mobile_phone']:
profile.mobile_phone = force_str(options['mobile_phone'], 'utf-8')
if options['personal_email']:
profile.personal_email = force_str(options['personal_email'], 'utf-8')
user.save()
profile.save()

View File

@ -1,17 +1,18 @@
import shutil
import csv
import datetime as dt
import os
import os.path
import datetime as dt
import shutil
import time
from django.core.management.base import BaseCommand, CommandError
import django.contrib.auth.models as auth_models
from django.db import transaction
from django.conf import settings
from django.core.management.base import BaseCommand, CommandError
from django.db import transaction
from ... import models
from ... import timestamp
from ....log import models as log_models
from ... import models
class Command(BaseCommand):
args = '<directory>'
@ -23,12 +24,7 @@ class Command(BaseCommand):
def save_users(self, path):
with open(os.path.join(path, 'users.csv'), 'w') as f:
headers = ['username',
'prenom',
'nom',
'email',
'profil',
'groupe']
headers = ['username', 'prenom', 'nom', 'email', 'profil', 'groupe']
csv_handle = csv.DictWriter(f, headers)
users = auth_models.User.objects.filter(is_superuser=False)
csv_handle.writerow(dict(zip(csv_handle.fieldnames, csv_handle.fieldnames)))
@ -41,24 +37,20 @@ class Command(BaseCommand):
'nom': user.last_name,
'email': user.email,
'profil': ','.join([ml.name for ml in user.mailing_lists.all()]),
'groupe': ','.join([group.name for group in user.groups.all()])}
'groupe': ','.join([group.name for group in user.groups.all()]),
}
self.dict_to_utf8(d)
csv_handle.writerow(d)
users.delete()
def save_logs(self, path):
with open(os.path.join(path, 'log.csv'), 'w') as f:
headers = ['timestamp',
'name',
'levelname',
'ip',
'user',
'message']
headers = ['timestamp', 'name', 'levelname', 'ip', 'user', 'message']
csv_handle = csv.DictWriter(f, headers)
csv_handle.writerow(dict(zip(csv_handle.fieldnames, csv_handle.fieldnames)))
logs = log_models.LogLine.objects.all()
for log in logs:
d = dict([(header, getattr(log, header)) for header in headers])
d = {header: getattr(log, header) for header in headers}
d['timestamp'] = d['timestamp'].isoformat()
self.dict_to_utf8(d)
csv_handle.writerow(d)
@ -66,19 +58,17 @@ class Command(BaseCommand):
def timestamp_logs(self, path):
with open(os.path.join(path, 'log-timestamp.der'), 'w') as f:
with open(os.path.join(path, 'log.csv')) as log_handle:
tst, message = timestamp.timestamp(log_handle.read())
if not tst:
raise CommandError('Failure to compute the timestamp: %s' % message)
with open(os.path.join(path, 'log.csv')):
tst = str(time.time())
f.write(tst)
def remove_profiles(self):
models.MailingList.objects.all().delete()
def removal_error(self, function, path, excinfo):
print 'unable to delete %s: %s' % (path, excinfo[1])
print('unable to delete %s: %s' % (path, excinfo[1]))
@transaction.commit_on_success
@transaction.atomic
def handle(self, *args, **options):
directory = args[0]
path = os.path.join(directory, dt.datetime.now().isoformat())
@ -89,14 +79,14 @@ class Command(BaseCommand):
self.save_logs(path)
self.timestamp_logs(path)
file_dir = os.path.join(settings.MEDIA_ROOT, 'files/')
print 'Removing %s...' % file_dir
print('Removing %s...' % file_dir)
shutil.rmtree(file_dir, onerror=self.removal_error)
upload_dir = os.path.join(settings.MEDIA_ROOT, 'upload/')
print 'Removing %s...' % upload_dir
print('Removing %s...' % upload_dir)
shutil.rmtree(upload_dir, onerror=self.removal_error)
print 'Creating %s...' % file_dir
print('Creating %s...' % file_dir)
os.mkdir(file_dir)
print 'Creating %s...' % upload_dir
print('Creating %s...' % upload_dir)
os.mkdir(upload_dir)
except:
shutil.rmtree(path)

View File

@ -1,11 +1,14 @@
from django.core.management.base import BaseCommand
from ... import models
class Command(BaseCommand):
'''Undelete all documents'''
help = 'Undelete all documents'
def handle(self, *args, **options):
count = models.Document.objects.count()
models.Document.objects.all().delete()
print "Definitively deleted %d documents." % count
print('Definitively deleted %d documents.' % count)

View File

@ -0,0 +1,240 @@
from optparse import make_option
from django.core import serializers
from django.core.exceptions import ImproperlyConfigured
from django.core.management.base import BaseCommand, CommandError
from django.db import DEFAULT_DB_ALIAS, router
from django.utils.datastructures import SortedDict
class Command(BaseCommand):
option_list = BaseCommand.option_list + (
make_option(
'--format',
default='json',
dest='format',
help='Specifies the output serialization format for fixtures.',
),
make_option(
'--indent',
default=None,
dest='indent',
type='int',
help='Specifies the indent level to use when pretty-printing output',
),
make_option(
'--database',
action='store',
dest='database',
default=DEFAULT_DB_ALIAS,
help='Nominates a specific database to dump '
'fixtures from. Defaults to the "default" database.',
),
make_option(
'-e',
'--exclude',
dest='exclude',
action='append',
default=[],
help='An appname or appname.ModelName to exclude (use multiple --exclude to exclude multiple apps/models).',
),
make_option(
'-n',
'--natural',
action='store_true',
dest='use_natural_keys',
default=False,
help='Use natural keys if they are available.',
),
make_option(
'-a',
'--all',
action='store_true',
dest='use_base_manager',
default=False,
help="Use Django's base manager to dump all models stored in the database, including those that would otherwise be filtered or modified by a custom manager.",
),
)
help = (
'Output the contents of the database as a fixture of the given '
"format (using each model's default manager unless --all is "
'specified).'
)
args = '[appname appname.ModelName ...]'
def handle(self, *app_labels, **options):
from django.db.models import get_app, get_apps, get_model, get_models
format = options.get('format')
indent = options.get('indent')
using = options.get('database')
excludes = options.get('exclude')
show_traceback = options.get('traceback')
use_natural_keys = options.get('use_natural_keys')
use_base_manager = options.get('use_base_manager')
excluded_apps = set()
excluded_models = set()
for exclude in excludes:
if '.' in exclude:
app_label, model_name = exclude.split('.', 1)
model_obj = get_model(app_label, model_name)
if not model_obj:
raise CommandError('Unknown model in excludes: %s' % exclude)
excluded_models.add(model_obj)
else:
try:
app_obj = get_app(exclude)
excluded_apps.add(app_obj)
except ImproperlyConfigured:
raise CommandError('Unknown app in excludes: %s' % exclude)
if len(app_labels) == 0:
app_list = SortedDict(
(app, [model for model in get_models(app) if model not in excluded_models])
for app in get_apps()
if app not in excluded_apps
)
else:
app_list = SortedDict()
for label in app_labels:
try:
app_label, model_label = label.split('.')
try:
app = get_app(app_label)
except ImproperlyConfigured:
raise CommandError('Unknown application: %s' % app_label)
if app in excluded_apps:
continue
model = get_model(app_label, model_label)
if model is None:
raise CommandError('Unknown model: %s.%s' % (app_label, model_label))
if model in excluded_models:
continue
if app in app_list.keys():
if app_list[app] and model not in app_list[app]:
app_list[app].append(model)
else:
app_list[app] = [model]
except ValueError:
# This is just an app - no model qualifier
app_label = label
try:
app = get_app(app_label)
except ImproperlyConfigured:
raise CommandError('Unknown application: %s' % app_label)
if app in excluded_apps:
continue
app_list[app] = None
# Check that the serialization format exists; this is a shortcut to
# avoid collating all the objects and _then_ failing.
if format not in serializers.get_public_serializer_formats():
raise CommandError('Unknown serialization format: %s' % format)
try:
serializers.get_serializer(format)
except KeyError:
raise CommandError('Unknown serialization format: %s' % format)
# Now collate the objects to be serialized.
objects = []
for model in sort_dependencies(app_list.items()):
if model in excluded_models:
continue
if not model._meta.proxy and router.allow_syncdb(using, model):
if use_base_manager:
objects.extend(model._base_manager.using(using).all())
else:
objects.extend(model._default_manager.using(using).all())
try:
return serializers.serialize(
format, objects, indent=indent, use_natural_foreign_keys=use_natural_keys
)
except Exception as e:
if show_traceback:
raise
raise CommandError('Unable to serialize database: %s' % e)
def sort_dependencies(app_list):
"""Sort a list of app,modellist pairs into a single list of models.
The single list of models is sorted so that any model with a natural key
is serialized before a normal model, and any model with a natural key
dependency has it's dependencies serialized first.
"""
from django.db.models import get_model, get_models
# Process the list of models, and get the list of dependencies
model_dependencies = []
models = set()
for app, model_list in app_list:
if model_list is None:
model_list = get_models(app)
for model in model_list:
models.add(model)
# Add any explicitly defined dependencies
if hasattr(model, 'natural_key'):
deps = getattr(model.natural_key, 'dependencies', [])
if deps:
deps = [get_model(*d.split('.')) for d in deps]
else:
deps = []
# Now add a dependency for any FK or M2M relation with
# a model that defines a natural key
for field in model._meta.fields:
if hasattr(field.rel, 'to'):
rel_model = field.rel.to
if hasattr(rel_model, 'natural_key'):
deps.append(rel_model)
for field in model._meta.many_to_many:
rel_model = field.rel.to
if hasattr(rel_model, 'natural_key'):
deps.append(rel_model)
# Remove circular dependencies
deps = filter(lambda x: x != model, deps)
model_dependencies.append((model, deps))
model_dependencies.reverse()
# Now sort the models to ensure that dependencies are met. This
# is done by repeatedly iterating over the input list of models.
# If all the dependencies of a given model are in the final list,
# that model is promoted to the end of the final list. This process
# continues until the input list is empty, or we do a full iteration
# over the input models without promoting a model to the final list.
# If we do a full iteration without a promotion, that means there are
# circular dependencies in the list.
model_list = []
while model_dependencies:
skipped = []
changed = False
while model_dependencies:
model, deps = model_dependencies.pop()
# If all of the models in the dependency list are either already
# on the final model list, or not on the original serialization list,
# then we've found another model with all it's dependencies satisfied.
found = True
for candidate in ((d not in models or d in model_list) for d in deps):
if not candidate:
found = False
if found:
model_list.append(model)
changed = True
else:
skipped.append((model, deps))
if not changed:
raise CommandError(
"Can't resolve dependencies for %s in serialized app list."
% ', '.join(
'%s.%s' % (model._meta.app_label, model._meta.object_name)
for model, deps in sorted(skipped, key=lambda obj: obj[0].__name__)
)
)
model_dependencies = skipped
return model_list

View File

@ -1,10 +1,9 @@
import csv
from django.core.management.base import BaseCommand, CommandError
import django.contrib.auth.models as auth_models
from django.core.management.base import BaseCommand, CommandError
from django.db import transaction
from ... import models
class Command(BaseCommand):
args = '<directory>'
@ -16,13 +15,7 @@ class Command(BaseCommand):
def save_users(self, path):
with open(path, 'w') as f:
headers = ['username',
'password',
'prenom',
'nom',
'email',
'profil',
'groupe']
headers = ['username', 'password', 'prenom', 'nom', 'email', 'profil', 'groupe']
csv_handle = csv.DictWriter(f, headers)
users = auth_models.User.objects.filter(is_superuser=False, delegations_by__isnull=True)
csv_handle.writerow(dict(zip(csv_handle.fieldnames, csv_handle.fieldnames)))
@ -36,11 +29,12 @@ class Command(BaseCommand):
'email': user.email,
'password': user.password,
'profil': ','.join([ml.name for ml in user.mailing_lists.all()]),
'groupe': ','.join([group.name for group in user.groups.all()])}
'groupe': ','.join([group.name for group in user.groups.all()]),
}
self.dict_to_utf8(d)
csv_handle.writerow(d)
@transaction.commit_on_success
@transaction.atomic
def handle(self, *args, **options):
path = args[0]
self.save_users(path)

View File

@ -0,0 +1,21 @@
from datetime import timedelta
from django.conf import settings
from django.core.management.base import BaseCommand
from django.db import transaction
from django.utils.timezone import now
from docbow_project.docbow.models import DeletedDocument
class Command(BaseCommand):
help = 'Empty trash'
@transaction.atomic
def handle(self, *args, **kwargs):
target_date = now() - timedelta(days=settings.TRASH_DURATION)
for deleted_doc in DeletedDocument.objects.filter(soft_delete=True).filter(
soft_delete_date__lte=target_date
):
deleted_doc.soft_delete = False
deleted_doc.save()

View File

@ -0,0 +1,59 @@
import argparse
from datetime import datetime
from django.contrib.auth.models import User
from django.core.management.base import BaseCommand
from django.db import transaction
from docbow_project.docbow.models import Document, Inbox
from docbow_project.docbow.utils import date_to_aware_datetime
def valid_date(s):
try:
return date_to_aware_datetime(datetime.strptime(s, '%Y-%m-%d'))
except ValueError:
msg = f"Not a valid date: '{s}'."
raise argparse.ArgumentTypeError(msg)
class Command(BaseCommand):
help = 'Forward documents received by a user to another'
def add_arguments(self, parser):
parser.add_argument('from_user', type=int)
parser.add_argument('to_user', type=int)
parser.add_argument(
'-s', '--startdate', required=False, type=valid_date, help='The start date - format YYYY-MM-DD'
)
parser.add_argument(
'-e', '--enddate', required=False, type=valid_date, help='The end date - format YYYY-MM-DD'
)
@transaction.atomic
def handle(self, from_user, to_user, *args, **kwargs):
verbose = kwargs.get('verbosity') > 1
from_user = User.objects.get(pk=from_user)
to_user = User.objects.get(pk=to_user)
from_user_inboxes = Inbox.objects.filter(owner=from_user, outbox=False)
to_user_inboxes = Inbox.objects.filter(owner=to_user, outbox=False)
docs = (
Document.objects.exclude(deleteddocument__user=from_user)
.filter(mailboxes__in=from_user_inboxes)
.exclude(mailboxes__in=to_user_inboxes)
.distinct()
)
startdate, enddate = kwargs.get('startdate'), kwargs.get('enddate')
if startdate:
docs = docs.filter(date__gte=startdate)
if enddate:
docs = docs.filter(date__lt=enddate)
if verbose:
print('Prepare forwarding', docs.count(), 'from', from_user, 'to', to_user)
for doc in docs:
doc.forward(doc.sender, [], [to_user])
if verbose:
print('Forward ', doc)

View File

@ -0,0 +1,52 @@
import locale
import sys
from optparse import make_option
from django.core.management.base import BaseCommand
from django.db import transaction
from django.utils.encoding import force_str
from ...models import MailingList
from ...unicodecsv import UnicodeWriter
def print_table(table):
col_width = [max(len(x) for x in col) for col in zip(*table)]
for line in table:
line = '| ' + ' | '.join('{0:>{1}}'.format(x, col_width[i]) for i, x in enumerate(line)) + ' |'
print(line)
class Command(BaseCommand):
args = ''
help = '''List mailing lists'''
option_list = BaseCommand.option_list + (make_option('--csv', action='store_true'),)
@transaction.atomic
def handle(self, *args, **options):
locale.setlocale(locale.LC_ALL, '')
locale_encoding = locale.nl_langinfo(locale.CODESET)
mailing_lists = MailingList.objects.prefetch_related('members', 'mailing_list_members')
for arg in args:
key, value = arg.split('=')
mailing_lists = mailing_lists.filter(**{key: value})
tables = [('Id', 'Name', 'Members', 'List Members')]
for mailing_list in mailing_lists:
tables.append(
map(
force_str,
(
mailing_list.id,
mailing_list.name,
','.join(m.name for m in mailing_list.mailing_list_members.all()),
','.join(g.username for g in mailing_list.members.all()),
),
)
)
if options['csv']:
writer = UnicodeWriter(sys.stdout, encoding=locale_encoding)
for row in tables:
writer.writerow(row)
else:
print_table(tables)

View File

@ -0,0 +1,79 @@
import locale
import sys
from optparse import make_option
from django.contrib.auth.models import User
from django.core.management.base import BaseCommand
from django.db import transaction
from django.utils.encoding import force_str
from ...models import DocbowProfile
from ...unicodecsv import UnicodeWriter
def print_table(table):
col_width = [max(len(x) for x in col) for col in zip(*table)]
for line in table:
line = '| ' + ' | '.join('{0:>{1}}'.format(x, col_width[i]) for i, x in enumerate(line)) + ' |'
print(line)
class Command(BaseCommand):
args = ''
help = '''List users'''
option_list = BaseCommand.option_list + (make_option('--csv', action='store_true'),)
@transaction.atomic
def handle(self, *args, **options):
locale.setlocale(locale.LC_ALL, '')
users = User.objects.prefetch_related('docbowprofile', 'groups', 'mailing_lists')
for arg in args:
key, value = arg.split('=')
users = users.filter(**{key: value})
tables = [
(
'Id',
'Username',
'First name',
'Last name',
'Email',
'Mobile phone',
'Personal mail',
'Lists',
'Groups',
'Active',
'Superuser',
)
]
for user in users:
try:
mobile_phone = user.docbowprofile.mobile_phone
personal_email = user.docbowprofile.personal_email
except DocbowProfile.DoesNotExist:
mobile_phone = ''
personal_email = ''
tables.append(
map(
force_str,
(
user.id,
user.username,
user.first_name,
user.last_name,
user.email,
mobile_phone,
personal_email,
','.join(m.name for m in user.mailing_lists.all()),
','.join(g.name for g in user.groups.all()),
user.is_active,
user.is_superuser,
),
)
)
if options['csv']:
writer = UnicodeWriter(sys.stdout, encoding=locale.nl_langinfo(locale.CODESET))
for row in tables:
writer.writerow(row)
else:
print_table(tables)

View File

@ -1,59 +1,68 @@
import csv
import functools
import os.path
from optparse import make_option
import unicodedata
import random
import sys
import unicodedata
from optparse import make_option
from django.core.management.base import BaseCommand, CommandError
from django.contrib.auth import models as auth_models
from django.core.management.base import BaseCommand, CommandError
from django.utils.encoding import force_str
from ... import models
def strip_accents(s):
return ''.join((c for c in unicodedata.normalize('NFD', s)
if unicodedata.category(c) != 'Mn'))
return ''.join(c for c in unicodedata.normalize('NFD', s) if unicodedata.category(c) != 'Mn')
def keep_letters(s):
return ''.join([c for c in s if c.isalpha()])
def unicode_csv_reader(utf8_csv_data, dialect=csv.excel, **kwargs):
# csv.py doesn't do Unicode; encode temporarily as UTF-8:
csv_reader = csv.reader(utf8_csv_data,
dialect=dialect, **kwargs)
csv_reader = csv.reader(utf8_csv_data, dialect=dialect, **kwargs)
for row in csv_reader:
# decode UTF-8 back to Unicode, cell by cell:
yield [unicode(cell, 'utf-8') for cell in row]
yield [force_str(cell, 'utf-8') for cell in row]
def csv_to_list(s):
return filter(None, map(unicode.strip, s.split(u',')))
return filter(None, map(str.strip, s.split(',')))
# Utilise seulement des majuscules et des chiffres, sauf i,l et 1, O et 0
__pwd_alphabet = 'ABCDEFGHJKMNPQRSTUVWXYZ23456789'
def create_password(pwd_length=8):
password = ''.join([random.choice(__pwd_alphabet)
for x in range(pwd_length)])
password = ''.join([random.choice(__pwd_alphabet) for x in range(pwd_length)])
return password
class Command(BaseCommand):
args = '[--profile default_profile1,default_profile2] [--group defaut_group1,default_group2] [--password default_password] [--generate-password] file.csv'
help = 'Load a CSV file containg user definitions'
option_list = BaseCommand.option_list + (
make_option("--profile", action='append', default=[]),
make_option("--group", action='append', default=[]),
make_option("--password", action='store'),
make_option("--generate-password", action='store_true', dest='generate_password'),
)
make_option('--profile', action='append', default=[]),
make_option('--group', action='append', default=[]),
make_option('--password', action='store'),
make_option('--activate', action='store_true'),
make_option('--generate-password', action='store_true', dest='generate_password'),
)
headers = { 'nom': 'last_name',
'prenom': 'first_name',
'email': 'email',
'profil': None,
'username': None,
'groupe': None,
'password': None }
headers = {
'nom': 'last_name',
'prenom': 'first_name',
'email': 'email',
'profil': None,
'username': None,
'groupe': None,
'password': None,
}
def synthesis(self, data, **options):
if not data.get('username'):
@ -61,38 +70,38 @@ class Command(BaseCommand):
raise CommandError('Username or nom/prenom must be given')
prenom = keep_letters(strip_accents(data['prenom'])).lower()
nom = keep_letters(strip_accents(data['nom'])).lower()
username = "%s.%s" % (prenom, nom)
username = '%s.%s' % (prenom, nom)
data['username'] = username
if 'profil' not in data:
default_profiles = csv_to_list(','.join(map(unicode, options.get('profile',[]))))
default_profiles = csv_to_list(','.join(map(force_str, options.get('profile', []))))
data['profil'] = default_profiles
else:
data['profil'] = csv_to_list(data['profil'])
if 'groupe' not in data:
default_groups = csv_to_list(','.join(map(unicode, options.get('group',[]))))
default_groups = csv_to_list(','.join(map(force_str, options.get('group', []))))
data['groupe'] = default_groups
else:
data['groupe'] = csv_to_list(data['groupe'])
if not data.get('password'):
if options.get('password'):
data['password'] = unicode(options['password'], 'utf8')
data['password'] = force_str(options['password'], 'utf8')
elif options.get('generate_password', False):
data['password'] = unicode(create_password())
data['password'] = force_str(create_password())
def handle(self, *args, **options):
if len(args) == 0:
raise CommandError("missing filename")
raise CommandError('missing filename')
if not os.path.exists(args[0]):
raise CommandError("%s not found" % args[0])
tuples = unicode_csv_reader(file(args[0]), dialect='excel')
raise CommandError('%s not found' % args[0])
tuples = unicode_csv_reader(open(args[0]), dialect='excel')
first = tuples.next()
allowed_headers = set(self.headers.keys())
if not set(first) <= allowed_headers:
msg = "Bad headers %s, only those are permitted: %s" % (first, allowed_headers)
msg = 'Bad headers %s, only those are permitted: %s' % (first, allowed_headers)
raise CommandError(msg)
all_users = []
@ -100,7 +109,7 @@ class Command(BaseCommand):
d = dict(zip(first, line))
self.synthesis(d, **options)
all_users.append(d)
all_profiles = set(reduce(list.__add__, [x['profil'] for x in all_users]))
all_profiles = set(functools.reduce(list.__add__, [x['profil'] for x in all_users]))
profiles = dict()
count_created, count_modified = 0, 0
for profile in filter(None, all_profiles):
@ -122,20 +131,25 @@ class Command(BaseCommand):
user_profiles = map(profiles.get, user['profil'])
user_instance.mailing_lists = user_profiles
if user['groupe']:
user_groups = map(lambda x: auth_models.Group.objects.get_or_create(name=x)[0],
user['groupe'])
user_groups = map(
lambda x: auth_models.Group.objects.get_or_create(name=x)[0], user['groupe']
)
user_instance.groups = user_groups
user_instance.is_staff = reduce(bool.__or__, ['Administrateur' in x for x in user['groupe']])
user_instance.is_staff = functools.reduce(
bool.__or__, ['Administrateur' in x for x in user['groupe']]
)
if user.get('password'):
password = user.get('password')
if password.startswith('sha1$'):
user_instance.password = password
else:
user_instance.set_password(user['password'])
if options.get('activate'):
user_instance.is_active = True
user_instance.save()
header = ['username', 'prenom', 'nom', 'email', 'password']
csv_writer = csv.DictWriter(sys.stdout, header)
csv_writer.writerow(dict(zip(header,header)))
csv_writer.writerow(dict(zip(header, header)))
for user in all_users:
d = {}
for key in header:

View File

@ -0,0 +1,13 @@
from django.core.management.base import BaseCommand
from django.db import transaction
from ... import notification
class Command(BaseCommand):
args = '<directory>'
help = 'Send notifications'
@transaction.atomic
def handle(self, *args, **options):
notification.process_notifications()

View File

@ -0,0 +1,71 @@
import locale
import os.path
from django.contrib.auth.models import User
from django.core.files import File
from django.core.management.base import BaseCommand, CommandError
from django.db import transaction
from django.utils.encoding import force_str
from docbow_project.docbow.models import AttachedFile, Document, FileType, MailingList
def get_object(model, ref, name='name'):
'''Try to get a model by id or by name'''
if ref.isdigit():
return model.objects.get(id=int(ref))
else:
return model.objects.get(**{name: ref})
class Command(BaseCommand):
help = '''Send a document'''
def add_arguments(self, parser):
parser.add_argument('file_tosend', nargs='+', type=str, help='File to send')
parser.add_argument('--sender')
parser.add_argument('--to-list', action='append')
parser.add_argument('--to-user', action='append')
parser.add_argument('--filetype')
parser.add_argument('--description')
@transaction.atomic
def handle(self, **options):
locale.setlocale(locale.LC_ALL, '')
locale_encoding = locale.nl_langinfo(locale.CODESET)
if 'sender' not in options:
raise CommandError('missing --sender parameter')
try:
sender = get_object(User, force_str(options['sender'], locale_encoding), 'username')
except User.DoesNotExist:
raise CommandError('user %r does not exist' % options['sender'])
to_lists = []
for name in options.get('to_list') or []:
ml = get_object(MailingList, force_str(name, locale_encoding))
to_lists.append(ml)
to_users = []
for username in options.get('to_user') or []:
user = get_object(User, force_str(username, locale_encoding), 'username')
to_users.append(user)
if 'filetype' not in options:
raise CommandError('missing --filetype parameter')
try:
filetype = get_object(FileType, force_str(options['filetype'], locale_encoding))
except FileType.DoesNotExist:
raise CommandError('filetype %r does not exist' % options['filetype'])
if not to_users and not to_lists:
print(to_users, to_lists, options)
raise CommandError(
'you must specify at least one list or user ' 'recipient using --to-list and --to-user.'
)
document = Document(sender=sender, filetype=filetype)
document.save()
document.to_user.set(to_users)
document.to_list.set(to_lists)
for arg in options['file_tosend']:
if not os.path.isfile(arg):
raise CommandError('file %r does not exist')
AttachedFile.objects.create(
name=force_str(arg, locale_encoding), content=File(open(arg)), document=document
)
document.post()

View File

@ -1,169 +0,0 @@
from optparse import make_option
import sys
import email
import email.errors
import email.utils
import email.header
import logging
import re
from django.core.management.base import BaseCommand, CommandError
import django.contrib.auth.models as auth_models
from django.core.files.base import ContentFile
from django.db import transaction
from django.core.exceptions import MultipleObjectsReturned
from ... import models, views, timestamp
from docbow_project.email_utils import u2u_decode
logger = logging.getLogger('docbow.mail_interface')
class Command(BaseCommand):
args = ''
help = '''Convert a mail to a document send.
In case of failure the following return value is returned:
- 1 failure to parse the message on stdin
- 2 the mail is missing a subject
- 3 the subject could not be decoded
- 4 obligatory attributes or contents were missing
- 5 notifications mails could not be sent
- 6 missing Message-ID
'''
option_list = BaseCommand.option_list + (
make_option("--sender"),
make_option("--base-url", default="http://localhost:8000"),
make_option("--ip", default=''),
make_option("--file"))
def handle(self, *args, **options):
mail_sender = None
self.logger = logging.LoggerAdapter(logging.getLogger('docbow'), {
'ip': options['ip'], 'user': 'Anonymous' })
if options.get('sender'):
try:
mail_sender = auth_models.User.objects.get(username=options['sender'])
except auth_models.User.DoesNotExist:
raise CommandError('Not found')
try:
if options.get('file'):
mail = email.message_from_file(file(options['file']))
else:
mail = email.message_from_file(sys.stdin)
except email.errors.MessageParseError, e:
self.error('7.7.1 Error parsing message', exite_code=1)
try:
self.handle_mail(mail, mail_sender, args, **options)
except Exception, e:
self.logger.exception('Unknown exception')
self.error('7.7.1 Uknown error when handling the mail', exit_code=5)
def error(self, msg, exit_code=None, *args):
if args:
print >>sys.stderr, msg % args
else:
print >>sys.stderr, msg
if hasattr(self, 'message_id'):
msg = ('smtp interface: message %s refused, ' % self.message_id) + msg
else:
msg = 'smtp interface: message refused, ' + msg
self.logger.error(msg, *args)
if exit_code:
sys.exit(exit_code)
def decode_filename(self, filename):
'''See if the filename contains encoded-word work around bugs in FileMakerPro'''
m = re.match(r'=\?(.*)\?(.*)\?(.*)\?=', filename)
if m:
result = []
for content, encoding in email.header.decode_header(filename):
result.append(unicode(content, encoding or 'ascii'))
return ''.join(result)
else:
return filename
def handle_mail(self, mail, mail_sender, mail_recipients, **options):
content_errors = []
attachments = []
recipients = []
description = u''
mail_from = email.utils.parseaddr(mail.get('From'))[1]
tos = mail.get_all('to', [])
ccs = mail.get_all('cc', [])
resent_tos = mail.get_all('resent-to', [])
resent_ccs = mail.get_all('resent-cc', [])
all_recipients = mail_recipients or [b for a,b in email.utils.getaddresses(tos + ccs + resent_tos +
resent_ccs)]
self.message_id = mail.get('Message-ID', None)
if not self.message_id:
self.error('7.7.1 Mail is missing a Message-ID', exit_code=6)
subject = mail.get('Subject', None)
if not subject:
self.error('7.7.1 Mail is missing a subject', exit_code=2)
try:
subject = u2u_decode(subject)
except Exception, e:
self.error('7.7.1 The subject cannot be decoded', exit_code=3)
try:
filetype = models.FileType.objects.get(name=subject)
except models.FileType.DoesNotExist:
content_errors.append('The subject "%s" does not match any known file type' % subject)
for part in mail.walk():
filename = part.get_filename(None)
if part.get_content_type() == 'text/plain' and \
('Content-Disposition' not in part or 'inline' in part['Content-Disposition']):
charset = part.get_content_charset('us-ascii')
description = unicode(part.get_payload(decode=True), charset)
if filename:
attachments.append((self.decode_filename(filename),
part.get_payload(decode=True)))
for email_address in all_recipients:
try:
user = auth_models.User.objects.get(email=email_address, delegations_by__isnull=True)
recipients.append(user)
except auth_models.User.DoesNotExist:
msg = 'Recipient %r is not an user of the platform' \
% email_address
content_errors.append(msg)
except MultipleObjectsReturned:
msg = 'Recipient %r has more than 1 user in the platform' \
% email_address
content_errors.append(msg)
if not len(attachments):
content_errors.append('You must sent at least one attached file')
if not len(all_recipients):
content_errors.append('You must have at least one recipient in your message.')
try:
sender = mail_sender or auth_models.User.objects.get(email=mail_from)
except auth_models.User.DoesNotExist:
content_errors.append('Unable to find an unique sender for the mail %s' % mail.get('From'))
if content_errors:
msg = [ '7.7.1 The email sent contains many errors:' ]
for error in content_errors:
msg.append(' - %s' % error)
self.error('\n'.join(msg), exit_code=4)
else:
with transaction.commit_on_success():
document = models.Document(sender=sender,
comment=description, filetype=filetype)
document.save()
document.to_user = recipients
for filename, payload in attachments:
content = ContentFile(payload)
attached_file = models.AttachedFile(document=document,
name=filename)
attached_file.content.save(filename, content, save=False)
attached_file.save()
document.post()
if not views.send_mail_notifications(document, options['base_url'], logger=self.logger):
transaction.rollback()
self.error('7.7.1 Document send failed because '
'some notifications could not be sent, administrators '
'have been informed.', code=5)
else:
blob = document.timestamp_blob()
tst = timestamp.timestamp_json(blob)
self.logger.info('smtp interface: message %s accepted, sent %s, timestamp %s' % (self.message_id, document, tst))

View File

@ -1,11 +0,0 @@
from django.core.management.base import BaseCommand
from ... import models
class Command(BaseCommand):
'''Undelete all documents'''
help = 'Undelete all documents'
def handle(self, *args, **options):
count = models.Mailbox.objects.filter(deleted=True).count()
models.Mailbox.objects.update(deleted=False)
print "Undeleted %d documents." % count

View File

@ -1,6 +1,8 @@
import threading
import logging
import sys
import threading
from django.utils.deprecation import MiddlewareMixin
__ALL__ = ('KeepUserAroundMiddleware', 'get_extra')
@ -9,16 +11,22 @@ NO_IP = NO_USER
logger = logging.getLogger('docbow')
def get_extra():
k = KeepUserAroundMiddleware
return { 'ip': k.get_global_ip(), 'user': k.get_global_user() }
class KeepUserAroundMiddleware(object):
'''
Store the request.user and the REMOTE_ADDR in a global thread local
variable, so that logging calls inside signals handler can know about
them.
'''
def get_extra():
"""Extract current remote ip and current user from the thread local storage
from the KeepUserAroundMiddleware middleware class.
"""
k = KeepUserAroundMiddleware
return {'ip': k.get_global_ip(), 'user': k.get_global_user()}
class KeepUserAroundMiddleware(MiddlewareMixin):
"""
Store the request.user and the REMOTE_ADDR in a global thread local
variable, so that logging calls inside signals handler can know about
them.
"""
__middleware_ctx = threading.local()
__middleware_ctx.user = NO_USER
__middleware_ctx.ip = NO_USER
@ -34,14 +42,16 @@ class KeepUserAroundMiddleware(object):
return response
def process_exception(self, request, exception):
logger.error('Internal Server Error: %s' % request.path,
logger.error(
'Internal Server Error: %s' % request.path,
exc_info=sys.exc_info,
extra={
'status_code': 500,
'request': request,
'ip': self.get_global_ip(),
'user': self.get_global_user(),
})
},
)
self.__middleware_ctx.user = NO_USER
self.__middleware_ctx.ip = NO_IP
return None

View File

@ -1,239 +1,638 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
import django.core.validators
import django.utils.timezone
import picklefield.fields
from django.conf import settings
from django.db import migrations, models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'FileType'
db.create_table('docbow_filetype', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=128)),
))
db.send_create_signal('docbow', ['FileType'])
# Adding model 'Content'
db.create_table('docbow_content', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('description', self.gf('django.db.models.fields.CharField')(unique=True, max_length=128)),
))
db.send_create_signal('docbow', ['Content'])
# Adding model 'Document'
db.create_table('docbow_document', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('sender', self.gf('django.db.models.fields.related.ForeignKey')(related_name='documents_sent', to=orm['auth.User'])),
('date', self.gf('django.db.models.fields.DateTimeField')(default=datetime.datetime.now)),
('filetype', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['docbow.FileType'])),
('comment', self.gf('django.db.models.fields.TextField')(blank=True)),
))
db.send_create_signal('docbow', ['Document'])
# Adding M2M table for field to_user on 'Document'
db.create_table('docbow_document_to_user', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('document', models.ForeignKey(orm['docbow.document'], null=False)),
('user', models.ForeignKey(orm['auth.user'], null=False))
))
db.create_unique('docbow_document_to_user', ['document_id', 'user_id'])
# Adding M2M table for field to_list on 'Document'
db.create_table('docbow_document_to_list', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('document', models.ForeignKey(orm['docbow.document'], null=False)),
('mailinglist', models.ForeignKey(orm['docbow.mailinglist'], null=False))
))
db.create_unique('docbow_document_to_list', ['document_id', 'mailinglist_id'])
# Adding model 'AttachedFile'
db.create_table('docbow_attachedfile', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('name', self.gf('django.db.models.fields.CharField')(max_length=128)),
('content', self.gf('django.db.models.fields.files.FileField')(max_length=100)),
('document', self.gf('django.db.models.fields.related.ForeignKey')(related_name='attached_files', to=orm['docbow.Document'])),
))
db.send_create_signal('docbow', ['AttachedFile'])
# Adding model 'MailingList'
db.create_table('docbow_mailinglist', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('name', self.gf('django.db.models.fields.CharField')(max_length=128)),
))
db.send_create_signal('docbow', ['MailingList'])
# Adding M2M table for field members on 'MailingList'
db.create_table('docbow_mailinglist_members', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('mailinglist', models.ForeignKey(orm['docbow.mailinglist'], null=False)),
('user', models.ForeignKey(orm['auth.user'], null=False))
))
db.create_unique('docbow_mailinglist_members', ['mailinglist_id', 'user_id'])
# Adding model 'Mailbox'
db.create_table('docbow_mailbox', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('owner', self.gf('django.db.models.fields.related.ForeignKey')(related_name='documents', to=orm['auth.User'])),
('document', self.gf('django.db.models.fields.related.ForeignKey')(related_name='mailboxes', to=orm['docbow.Document'])),
('seen', self.gf('django.db.models.fields.BooleanField')(default=False)),
('deleted', self.gf('django.db.models.fields.BooleanField')(default=False)),
('outbox', self.gf('django.db.models.fields.BooleanField')(default=False)),
('date', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, auto_now_add=True, blank=True)),
))
db.send_create_signal('docbow', ['Mailbox'])
# Adding model 'SendingLimitation'
db.create_table('docbow_sendinglimitation', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('mailing_list', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['docbow.MailingList'], unique=True)),
))
db.send_create_signal('docbow', ['SendingLimitation'])
# Adding M2M table for field filetypes on 'SendingLimitation'
db.create_table('docbow_sendinglimitation_filetypes', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('sendinglimitation', models.ForeignKey(orm['docbow.sendinglimitation'], null=False)),
('filetype', models.ForeignKey(orm['docbow.filetype'], null=False))
))
db.create_unique('docbow_sendinglimitation_filetypes', ['sendinglimitation_id', 'filetype_id'])
# Adding M2M table for field lists on 'SendingLimitation'
db.create_table('docbow_sendinglimitation_lists', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('sendinglimitation', models.ForeignKey(orm['docbow.sendinglimitation'], null=False)),
('mailinglist', models.ForeignKey(orm['docbow.mailinglist'], null=False))
))
db.create_unique('docbow_sendinglimitation_lists', ['sendinglimitation_id', 'mailinglist_id'])
import docbow_project.docbow.models
def backwards(self, orm):
# Deleting model 'FileType'
db.delete_table('docbow_filetype')
class Migration(migrations.Migration):
dependencies = [
('auth', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
# Deleting model 'Content'
db.delete_table('docbow_content')
# Deleting model 'Document'
db.delete_table('docbow_document')
# Removing M2M table for field to_user on 'Document'
db.delete_table('docbow_document_to_user')
# Removing M2M table for field to_list on 'Document'
db.delete_table('docbow_document_to_list')
# Deleting model 'AttachedFile'
db.delete_table('docbow_attachedfile')
# Deleting model 'MailingList'
db.delete_table('docbow_mailinglist')
# Removing M2M table for field members on 'MailingList'
db.delete_table('docbow_mailinglist_members')
# Deleting model 'Mailbox'
db.delete_table('docbow_mailbox')
# Deleting model 'SendingLimitation'
db.delete_table('docbow_sendinglimitation')
# Removing M2M table for field filetypes on 'SendingLimitation'
db.delete_table('docbow_sendinglimitation_filetypes')
# Removing M2M table for field lists on 'SendingLimitation'
db.delete_table('docbow_sendinglimitation_lists')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'docbow.attachedfile': {
'Meta': {'object_name': 'AttachedFile'},
'content': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'attached_files'", 'to': "orm['docbow.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
},
'docbow.content': {
'Meta': {'ordering': "['description']", 'object_name': 'Content'},
'description': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'docbow.document': {
'Meta': {'ordering': "['-date']", 'object_name': 'Document'},
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'filetype': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['docbow.FileType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'sender': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'documents_sent'", 'to': "orm['auth.User']"}),
'to_list': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['docbow.MailingList']", 'null': 'True', 'blank': 'True'}),
'to_user': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'directly_received_documents'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"})
},
'docbow.filetype': {
'Meta': {'ordering': "['name']", 'object_name': 'FileType'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'})
},
'docbow.mailbox': {
'Meta': {'ordering': "['-date']", 'object_name': 'Mailbox'},
'date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'auto_now_add': 'True', 'blank': 'True'}),
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'mailboxes'", 'to': "orm['docbow.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'outbox': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'documents'", 'to': "orm['auth.User']"}),
'seen': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
},
'docbow.mailinglist': {
'Meta': {'ordering': "['name']", 'object_name': 'MailingList'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'members': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'mailing_lists'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
},
'docbow.sendinglimitation': {
'Meta': {'object_name': 'SendingLimitation'},
'filetypes': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'filetype_limitation'", 'blank': 'True', 'to': "orm['docbow.FileType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'lists': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'lists_limitation'", 'symmetrical': 'False', 'to': "orm['docbow.MailingList']"}),
'mailing_list': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['docbow.MailingList']", 'unique': 'True'})
}
}
complete_apps = ['docbow']
operations = [
migrations.CreateModel(
name='AttachedFile',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('name', models.CharField(max_length=300, verbose_name='Name')),
(
'content',
models.FileField(
upload_to=docbow_project.docbow.models.generate_filename,
max_length=300,
verbose_name='File',
),
),
],
options={},
bases=(models.Model,),
),
migrations.CreateModel(
name='AutomaticForwarding',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
],
options={
'verbose_name': 'Automatic forwarding rule',
'verbose_name_plural': 'Automatic forwarding rules',
},
bases=(models.Model,),
),
migrations.CreateModel(
name='Content',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('description', models.CharField(unique=True, max_length=128)),
],
options={
'ordering': ['description'],
'verbose_name': 'Content',
'verbose_name_plural': 'Contents',
},
bases=(models.Model,),
),
migrations.CreateModel(
name='Delegation',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
(
'by',
models.ForeignKey(
related_name='delegations_to',
verbose_name='From',
to=settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
),
),
(
'to',
models.ForeignKey(
related_name='delegations_by',
verbose_name='To',
to=settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
),
),
],
options={
'ordering': ['by'],
'db_table': 'auth_delegation',
'verbose_name': 'Account delegation',
'verbose_name_plural': 'Account delegations',
},
bases=(models.Model,),
),
migrations.CreateModel(
name='DeletedDocument',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
],
options={
'ordering': ('-document',),
'verbose_name': 'deleted document',
'verbose_name_plural': 'deleted documents',
},
bases=(models.Model,),
),
migrations.CreateModel(
name='DeletedMailbox',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('delegate', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
],
options={},
bases=(models.Model,),
),
migrations.CreateModel(
name='DocbowProfile',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('is_guest', models.BooleanField(default=False, verbose_name='Guest user')),
(
'mobile_phone',
models.CharField(
blank=True,
max_length=32,
verbose_name='Mobile phone',
validators=[django.core.validators.RegexValidator('^\\+\\d+$')],
),
),
(
'personal_email',
models.EmailField(
help_text='if you provide a personal email address, notifications of new documents will also be sent to this address.',
max_length=75,
verbose_name='personal email address',
blank=True,
),
),
(
'accept_notifications',
models.BooleanField(
default=True,
help_text='If unchecked you will not received notifications anymore, by email or SMS.',
verbose_name='Accept to be notified',
),
),
('user', models.OneToOneField(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
],
options={},
bases=(models.Model,),
),
migrations.CreateModel(
name='Document',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('real_sender', models.CharField(max_length=64, verbose_name='Real sender', blank=True)),
(
'date',
models.DateTimeField(default=django.utils.timezone.now, verbose_name="Date d'envoi"),
),
('comment', models.TextField(verbose_name='Comments', blank=True)),
('_timestamp', models.TextField(blank=True)),
(
'private',
models.BooleanField(
default=False, help_text='delegates cannot see this document', verbose_name='Private'
),
),
],
options={
'base_manager_name': 'objects',
'ordering': ['-date'],
'verbose_name': 'Document',
'verbose_name_plural': 'Documents',
'permissions': (('FORWARD_DOCUMENT', 'Can forward documents'),),
},
bases=(models.Model,),
),
migrations.CreateModel(
name='DocumentForwarded',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('date', models.DateTimeField(auto_now_add=True)),
('automatic', models.BooleanField(default=False)),
(
'from_document',
models.ForeignKey(
related_name='document_forwarded_to', to='docbow.Document', on_delete=models.CASCADE
),
),
(
'to_document',
models.ForeignKey(
related_name='document_forwarded_from', to='docbow.Document', on_delete=models.CASCADE
),
),
],
options={},
bases=(models.Model,),
),
migrations.CreateModel(
name='FileType',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('name', models.CharField(unique=True, max_length=128)),
('is_active', models.BooleanField(default=True, verbose_name='is active')),
],
options={
'ordering': ['name'],
'verbose_name': 'File type',
'verbose_name_plural': 'File types',
},
bases=(docbow_project.docbow.models.NameNaturalKey, models.Model),
),
migrations.CreateModel(
name='FileTypeAttachedFileKind',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('name', models.CharField(max_length=128, verbose_name='name')),
(
'mime_types',
models.TextField(
help_text='mime types separated by spaces, wildcards are allowed',
verbose_name='mime types',
blank=True,
),
),
(
'cardinality',
models.PositiveSmallIntegerField(
default=0,
help_text='zero is a special value setting no limitation',
verbose_name='cardinality',
),
),
(
'minimum',
models.PositiveSmallIntegerField(default=0, verbose_name='minimum number of files'),
),
('position', models.PositiveSmallIntegerField(verbose_name='position')),
(
'file_type',
models.ForeignKey(
verbose_name='document type', to='docbow.FileType', on_delete=models.CASCADE
),
),
],
options={
'ordering': ('file_type', 'position', 'name'),
'verbose_name': 'file type attached file kind',
'verbose_name_plural': 'file type attached file kinds',
},
bases=(models.Model,),
),
migrations.CreateModel(
name='Mailbox',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('outbox', models.BooleanField(default=False, db_index=True, verbose_name='Outbox message')),
('date', models.DateTimeField(auto_now_add=True)),
(
'document',
models.ForeignKey(
related_name='mailboxes',
verbose_name='Document',
to='docbow.Document',
on_delete=models.CASCADE,
),
),
(
'owner',
models.ForeignKey(
related_name='documents',
verbose_name='Mailbox owner',
to=settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
),
),
],
options={
'ordering': ['-date'],
'verbose_name': 'Mailbox',
'verbose_name_plural': 'Mailboxes',
},
bases=(models.Model,),
),
migrations.CreateModel(
name='MailingList',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('name', models.CharField(max_length=400, verbose_name='Name')),
('is_active', models.BooleanField(default=True, verbose_name='is active')),
(
'mailing_list_members',
models.ManyToManyField(
related_name='members_lists',
verbose_name='Mailing lists members',
to='docbow.MailingList',
blank=True,
),
),
(
'members',
models.ManyToManyField(
related_name='mailing_lists',
verbose_name='Members',
to=settings.AUTH_USER_MODEL,
blank=True,
),
),
],
options={
'ordering': ['name'],
'verbose_name': 'Mailing list',
'verbose_name_plural': 'Mailing lists',
},
bases=(docbow_project.docbow.models.NameNaturalKey, models.Model),
),
migrations.CreateModel(
name='Notification',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('create_dt', models.DateTimeField(auto_now_add=True)),
('kind', models.CharField(default='new-document', max_length=32)),
('done', models.BooleanField(default=False)),
('failure', models.TextField(null=True, blank=True)),
('ctx', picklefield.fields.PickledObjectField(null=True, editable=False, blank=True)),
(
'document',
models.ForeignKey(blank=True, to='docbow.Document', null=True, on_delete=models.CASCADE),
),
(
'user',
models.ForeignKey(
blank=True, to=settings.AUTH_USER_MODEL, null=True, on_delete=models.CASCADE
),
),
],
options={
'ordering': ('-id',),
},
bases=(models.Model,),
),
migrations.CreateModel(
name='NotificationPreference',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('kind', models.CharField(max_length=8, verbose_name='kind')),
('value', models.BooleanField(default=True, verbose_name='value')),
(
'filetype',
models.ForeignKey(
verbose_name='file type', to='docbow.FileType', on_delete=models.CASCADE
),
),
(
'user',
models.ForeignKey(
verbose_name='user', to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE
),
),
],
options={
'ordering': ('user__last_name', 'user__first_name', 'kind'),
'verbose_name': 'notification preference',
'verbose_name_plural': 'notification preferences',
},
bases=(models.Model,),
),
migrations.CreateModel(
name='SeenDocument',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('document', models.ForeignKey(to='docbow.Document', on_delete=models.CASCADE)),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
],
options={
'ordering': ('-document',),
'verbose_name': 'seen document',
'verbose_name_plural': 'seen documents',
},
bases=(models.Model,),
),
migrations.CreateModel(
name='SendingLimitation',
fields=[
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
(
'filetypes',
models.ManyToManyField(
related_name='filetype_limitation',
verbose_name='Limitation des types de fichier',
to='docbow.FileType',
blank=True,
),
),
(
'lists',
models.ManyToManyField(
related_name='lists_limitation',
verbose_name='Limitation des destinataires',
to='docbow.MailingList',
),
),
(
'mailing_list',
models.OneToOneField(
verbose_name='Mailing list', to='docbow.MailingList', on_delete=models.CASCADE
),
),
],
options={
'verbose_name': 'Limitation par liste de destinataires',
'verbose_name_plural': 'Limitation par liste de destinataires',
},
bases=(models.Model,),
),
migrations.AlterUniqueTogether(
name='filetypeattachedfilekind',
unique_together={('name', 'file_type')},
),
migrations.AddField(
model_name='document',
name='filetype',
field=models.ForeignKey(
verbose_name='Document type', to='docbow.FileType', on_delete=models.CASCADE
),
preserve_default=True,
),
migrations.AddField(
model_name='document',
name='reply_to',
field=models.ForeignKey(
related_name='replies',
verbose_name='Reply to',
blank=True,
to='docbow.Document',
null=True,
on_delete=models.CASCADE,
),
preserve_default=True,
),
migrations.AddField(
model_name='document',
name='sender',
field=models.ForeignKey(
related_name='documents_sent',
verbose_name='Sender',
to=settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
),
preserve_default=True,
),
migrations.AddField(
model_name='document',
name='to_list',
field=models.ManyToManyField(
to='docbow.MailingList', verbose_name='Groups to send to', blank=True
),
),
migrations.AddField(
model_name='document',
name='to_user',
field=models.ManyToManyField(
related_name='directly_received_documents',
verbose_name='Users to send to',
to=settings.AUTH_USER_MODEL,
blank=True,
),
),
migrations.AddField(
model_name='deletedmailbox',
name='mailbox',
field=models.ForeignKey(to='docbow.Mailbox', on_delete=models.CASCADE),
preserve_default=True,
),
migrations.AddField(
model_name='deleteddocument',
name='document',
field=models.ForeignKey(to='docbow.Document', on_delete=models.CASCADE),
preserve_default=True,
),
migrations.AddField(
model_name='deleteddocument',
name='user',
field=models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE),
preserve_default=True,
),
migrations.AlterUniqueTogether(
name='delegation',
unique_together={('by', 'to')},
),
migrations.AddField(
model_name='automaticforwarding',
name='filetypes',
field=models.ManyToManyField(
related_name='forwarding_rules', verbose_name='filetype', to='docbow.FileType'
),
preserve_default=True,
),
migrations.AddField(
model_name='automaticforwarding',
name='forward_to_list',
field=models.ManyToManyField(
related_name='as_recipient_forwarding_rules',
verbose_name='Groups to forward to',
to='docbow.MailingList',
blank=True,
),
),
migrations.AddField(
model_name='automaticforwarding',
name='forward_to_user',
field=models.ManyToManyField(
related_name='as_recipient_forwarding_rules',
verbose_name='Users to forward to',
to=settings.AUTH_USER_MODEL,
blank=True,
),
),
migrations.AddField(
model_name='automaticforwarding',
name='originaly_to_user',
field=models.ManyToManyField(
related_name='as_original_recipient_forwarding_rules',
to=settings.AUTH_USER_MODEL,
blank=True,
help_text='At least one recipient must match for the rule to apply.',
verbose_name='Original recipients',
),
),
migrations.AddField(
model_name='attachedfile',
name='document',
field=models.ForeignKey(
related_name='attached_files',
verbose_name='Attached to',
to='docbow.Document',
on_delete=models.CASCADE,
),
preserve_default=True,
),
migrations.AddField(
model_name='attachedfile',
name='kind',
field=models.ForeignKey(
verbose_name='attached file kind',
blank=True,
to='docbow.FileTypeAttachedFileKind',
null=True,
on_delete=models.CASCADE,
),
preserve_default=True,
),
migrations.CreateModel(
name='DocbowGroup',
fields=[],
options={
'verbose_name': 'Docbow admin group',
'proxy': True,
},
bases=('auth.group',),
),
migrations.CreateModel(
name='DocbowUser',
fields=[],
options={
'verbose_name': 'Docbow admin user',
'proxy': True,
},
bases=('auth.user',),
),
migrations.CreateModel(
name='Inbox',
fields=[],
options={
'verbose_name': 'Inbox',
'proxy': True,
'verbose_name_plural': 'Inboxes',
},
bases=('docbow.mailbox',),
),
migrations.CreateModel(
name='Outbox',
fields=[],
options={
'verbose_name': 'Outbox',
'proxy': True,
'verbose_name_plural': 'Outboxes',
},
bases=('docbow.mailbox',),
),
]

View File

@ -0,0 +1,13 @@
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('docbow', '0001_initial'),
]
operations = [
migrations.DeleteModel(
name='DeletedMailbox',
),
]

View File

@ -1,123 +0,0 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'DocumentForwarded'
db.create_table('docbow_documentforwarded', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('from_document', self.gf('django.db.models.fields.related.ForeignKey')(related_name='document_forwarded_to', to=orm['docbow.Document'])),
('to_document', self.gf('django.db.models.fields.related.ForeignKey')(related_name='document_forwarded_from', to=orm['docbow.Document'])),
('date', self.gf('django.db.models.fields.DateTimeField')(auto_now=True, auto_now_add=True, blank=True)),
))
db.send_create_signal('docbow', ['DocumentForwarded'])
def backwards(self, orm):
# Deleting model 'DocumentForwarded'
db.delete_table('docbow_documentforwarded')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'docbow.attachedfile': {
'Meta': {'object_name': 'AttachedFile'},
'content': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'attached_files'", 'to': "orm['docbow.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
},
'docbow.content': {
'Meta': {'ordering': "['description']", 'object_name': 'Content'},
'description': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'docbow.document': {
'Meta': {'ordering': "['-date']", 'object_name': 'Document'},
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'filetype': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['docbow.FileType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'sender': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'documents_sent'", 'to': "orm['auth.User']"}),
'to_list': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['docbow.MailingList']", 'null': 'True', 'blank': 'True'}),
'to_user': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'directly_received_documents'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"})
},
'docbow.documentforwarded': {
'Meta': {'object_name': 'DocumentForwarded'},
'date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'auto_now_add': 'True', 'blank': 'True'}),
'from_document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'document_forwarded_to'", 'to': "orm['docbow.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'to_document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'document_forwarded_from'", 'to': "orm['docbow.Document']"})
},
'docbow.filetype': {
'Meta': {'ordering': "['name']", 'object_name': 'FileType'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'})
},
'docbow.mailbox': {
'Meta': {'ordering': "['-date']", 'object_name': 'Mailbox'},
'date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'auto_now_add': 'True', 'blank': 'True'}),
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'mailboxes'", 'to': "orm['docbow.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'outbox': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'documents'", 'to': "orm['auth.User']"}),
'seen': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
},
'docbow.mailinglist': {
'Meta': {'ordering': "['name']", 'object_name': 'MailingList'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'members': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'mailing_lists'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
},
'docbow.sendinglimitation': {
'Meta': {'object_name': 'SendingLimitation'},
'filetypes': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'filetype_limitation'", 'blank': 'True', 'to': "orm['docbow.FileType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'lists': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'lists_limitation'", 'symmetrical': 'False', 'to': "orm['docbow.MailingList']"}),
'mailing_list': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['docbow.MailingList']", 'unique': 'True'})
}
}
complete_apps = ['docbow']

View File

@ -0,0 +1,20 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('docbow', '0002_auto_20190711_1812'),
]
operations = [
migrations.AlterField(
model_name='docbowprofile',
name='personal_email',
field=models.EmailField(
help_text='if you provide a personal email address, notifications of new documents will also be sent to this address.',
max_length=254,
verbose_name='personal email address',
blank=True,
),
),
]

View File

@ -1,172 +0,0 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'AutomaticForwarding'
db.create_table('docbow_automaticforwarding', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
))
db.send_create_signal('docbow', ['AutomaticForwarding'])
# Adding M2M table for field from_list on 'AutomaticForwarding'
db.create_table('docbow_automaticforwarding_from_list', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('automaticforwarding', models.ForeignKey(orm['docbow.automaticforwarding'], null=False)),
('mailinglist', models.ForeignKey(orm['docbow.mailinglist'], null=False))
))
db.create_unique('docbow_automaticforwarding_from_list', ['automaticforwarding_id', 'mailinglist_id'])
# Adding M2M table for field filetypes on 'AutomaticForwarding'
db.create_table('docbow_automaticforwarding_filetypes', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('automaticforwarding', models.ForeignKey(orm['docbow.automaticforwarding'], null=False)),
('filetype', models.ForeignKey(orm['docbow.filetype'], null=False))
))
db.create_unique('docbow_automaticforwarding_filetypes', ['automaticforwarding_id', 'filetype_id'])
# Adding M2M table for field forward_to_user on 'AutomaticForwarding'
db.create_table('docbow_automaticforwarding_forward_to_user', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('automaticforwarding', models.ForeignKey(orm['docbow.automaticforwarding'], null=False)),
('user', models.ForeignKey(orm['auth.user'], null=False))
))
db.create_unique('docbow_automaticforwarding_forward_to_user', ['automaticforwarding_id', 'user_id'])
# Adding M2M table for field forward_to_list on 'AutomaticForwarding'
db.create_table('docbow_automaticforwarding_forward_to_list', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('automaticforwarding', models.ForeignKey(orm['docbow.automaticforwarding'], null=False)),
('mailinglist', models.ForeignKey(orm['docbow.mailinglist'], null=False))
))
db.create_unique('docbow_automaticforwarding_forward_to_list', ['automaticforwarding_id', 'mailinglist_id'])
def backwards(self, orm):
# Deleting model 'AutomaticForwarding'
db.delete_table('docbow_automaticforwarding')
# Removing M2M table for field from_list on 'AutomaticForwarding'
db.delete_table('docbow_automaticforwarding_from_list')
# Removing M2M table for field filetypes on 'AutomaticForwarding'
db.delete_table('docbow_automaticforwarding_filetypes')
# Removing M2M table for field forward_to_user on 'AutomaticForwarding'
db.delete_table('docbow_automaticforwarding_forward_to_user')
# Removing M2M table for field forward_to_list on 'AutomaticForwarding'
db.delete_table('docbow_automaticforwarding_forward_to_list')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'docbow.attachedfile': {
'Meta': {'object_name': 'AttachedFile'},
'content': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'attached_files'", 'to': "orm['docbow.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
},
'docbow.automaticforwarding': {
'Meta': {'object_name': 'AutomaticForwarding'},
'filetypes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'forwarding_rules'", 'symmetrical': 'False', 'to': "orm['docbow.FileType']"}),
'forward_to_list': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'as_recipient_forwarding_rules'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['docbow.MailingList']"}),
'forward_to_user': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'as_recipient_forwarding_rules'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}),
'from_list': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'forwarding_rules'", 'symmetrical': 'False', 'to': "orm['docbow.MailingList']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'docbow.content': {
'Meta': {'ordering': "['description']", 'object_name': 'Content'},
'description': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'docbow.document': {
'Meta': {'ordering': "['-date']", 'object_name': 'Document'},
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'filetype': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['docbow.FileType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'sender': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'documents_sent'", 'to': "orm['auth.User']"}),
'to_list': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['docbow.MailingList']", 'null': 'True', 'blank': 'True'}),
'to_user': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'directly_received_documents'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"})
},
'docbow.documentforwarded': {
'Meta': {'object_name': 'DocumentForwarded'},
'date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'auto_now_add': 'True', 'blank': 'True'}),
'from_document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'document_forwarded_to'", 'to': "orm['docbow.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'to_document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'document_forwarded_from'", 'to': "orm['docbow.Document']"})
},
'docbow.filetype': {
'Meta': {'ordering': "['name']", 'object_name': 'FileType'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'})
},
'docbow.mailbox': {
'Meta': {'ordering': "['-date']", 'object_name': 'Mailbox'},
'date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'auto_now_add': 'True', 'blank': 'True'}),
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'mailboxes'", 'to': "orm['docbow.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'outbox': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'documents'", 'to': "orm['auth.User']"}),
'seen': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
},
'docbow.mailinglist': {
'Meta': {'ordering': "['name']", 'object_name': 'MailingList'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'members': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'mailing_lists'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
},
'docbow.sendinglimitation': {
'Meta': {'object_name': 'SendingLimitation'},
'filetypes': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'filetype_limitation'", 'blank': 'True', 'to': "orm['docbow.FileType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'lists': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'lists_limitation'", 'symmetrical': 'False', 'to': "orm['docbow.MailingList']"}),
'mailing_list': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['docbow.MailingList']", 'unique': 'True'})
}
}
complete_apps = ['docbow']

View File

@ -1,129 +0,0 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Removing M2M table for field from_list on 'AutomaticForwarding'
db.delete_table('docbow_automaticforwarding_from_list')
def backwards(self, orm):
# Adding M2M table for field from_list on 'AutomaticForwarding'
db.create_table('docbow_automaticforwarding_from_list', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('automaticforwarding', models.ForeignKey(orm['docbow.automaticforwarding'], null=False)),
('mailinglist', models.ForeignKey(orm['docbow.mailinglist'], null=False))
))
db.create_unique('docbow_automaticforwarding_from_list', ['automaticforwarding_id', 'mailinglist_id'])
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'docbow.attachedfile': {
'Meta': {'object_name': 'AttachedFile'},
'content': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'attached_files'", 'to': "orm['docbow.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
},
'docbow.automaticforwarding': {
'Meta': {'object_name': 'AutomaticForwarding'},
'filetypes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'forwarding_rules'", 'symmetrical': 'False', 'to': "orm['docbow.FileType']"}),
'forward_to_list': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'as_recipient_forwarding_rules'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['docbow.MailingList']"}),
'forward_to_user': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'as_recipient_forwarding_rules'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'docbow.content': {
'Meta': {'ordering': "['description']", 'object_name': 'Content'},
'description': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'docbow.document': {
'Meta': {'ordering': "['-date']", 'object_name': 'Document'},
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'filetype': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['docbow.FileType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'sender': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'documents_sent'", 'to': "orm['auth.User']"}),
'to_list': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['docbow.MailingList']", 'null': 'True', 'blank': 'True'}),
'to_user': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'directly_received_documents'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"})
},
'docbow.documentforwarded': {
'Meta': {'object_name': 'DocumentForwarded'},
'date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'auto_now_add': 'True', 'blank': 'True'}),
'from_document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'document_forwarded_to'", 'to': "orm['docbow.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'to_document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'document_forwarded_from'", 'to': "orm['docbow.Document']"})
},
'docbow.filetype': {
'Meta': {'ordering': "['name']", 'object_name': 'FileType'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'})
},
'docbow.mailbox': {
'Meta': {'ordering': "['-date']", 'object_name': 'Mailbox'},
'date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'auto_now_add': 'True', 'blank': 'True'}),
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'mailboxes'", 'to': "orm['docbow.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'outbox': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'documents'", 'to': "orm['auth.User']"}),
'seen': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
},
'docbow.mailinglist': {
'Meta': {'ordering': "['name']", 'object_name': 'MailingList'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'members': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'mailing_lists'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
},
'docbow.sendinglimitation': {
'Meta': {'object_name': 'SendingLimitation'},
'filetypes': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'filetype_limitation'", 'blank': 'True', 'to': "orm['docbow.FileType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'lists': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'lists_limitation'", 'symmetrical': 'False', 'to': "orm['docbow.MailingList']"}),
'mailing_list': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['docbow.MailingList']", 'unique': 'True'})
}
}
complete_apps = ['docbow']

View File

@ -0,0 +1,15 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('docbow', '0003_auto_20200319_1129'),
]
operations = [
migrations.AddField(
model_name='docbowprofile',
name='external_id',
field=models.CharField(max_length=256, verbose_name='External identifer', blank=True),
),
]

View File

@ -1,125 +0,0 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'AutomaticForwarding.automatic'
db.add_column('docbow_automaticforwarding', 'automatic', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False)
def backwards(self, orm):
# Deleting field 'AutomaticForwarding.automatic'
db.delete_column('docbow_automaticforwarding', 'automatic')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'docbow.attachedfile': {
'Meta': {'object_name': 'AttachedFile'},
'content': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'attached_files'", 'to': "orm['docbow.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
},
'docbow.automaticforwarding': {
'Meta': {'object_name': 'AutomaticForwarding'},
'automatic': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'filetypes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'forwarding_rules'", 'symmetrical': 'False', 'to': "orm['docbow.FileType']"}),
'forward_to_list': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'as_recipient_forwarding_rules'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['docbow.MailingList']"}),
'forward_to_user': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'as_recipient_forwarding_rules'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'docbow.content': {
'Meta': {'ordering': "['description']", 'object_name': 'Content'},
'description': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'docbow.document': {
'Meta': {'ordering': "['-date']", 'object_name': 'Document'},
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'filetype': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['docbow.FileType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'sender': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'documents_sent'", 'to': "orm['auth.User']"}),
'to_list': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['docbow.MailingList']", 'null': 'True', 'blank': 'True'}),
'to_user': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'directly_received_documents'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"})
},
'docbow.documentforwarded': {
'Meta': {'object_name': 'DocumentForwarded'},
'date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'auto_now_add': 'True', 'blank': 'True'}),
'from_document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'document_forwarded_to'", 'to': "orm['docbow.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'to_document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'document_forwarded_from'", 'to': "orm['docbow.Document']"})
},
'docbow.filetype': {
'Meta': {'ordering': "['name']", 'object_name': 'FileType'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'})
},
'docbow.mailbox': {
'Meta': {'ordering': "['-date']", 'object_name': 'Mailbox'},
'date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'auto_now_add': 'True', 'blank': 'True'}),
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'mailboxes'", 'to': "orm['docbow.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'outbox': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'documents'", 'to': "orm['auth.User']"}),
'seen': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
},
'docbow.mailinglist': {
'Meta': {'ordering': "['name']", 'object_name': 'MailingList'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'members': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'mailing_lists'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
},
'docbow.sendinglimitation': {
'Meta': {'object_name': 'SendingLimitation'},
'filetypes': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'filetype_limitation'", 'blank': 'True', 'to': "orm['docbow.FileType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'lists': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'lists_limitation'", 'symmetrical': 'False', 'to': "orm['docbow.MailingList']"}),
'mailing_list': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['docbow.MailingList']", 'unique': 'True'})
}
}
complete_apps = ['docbow']

View File

@ -0,0 +1,22 @@
# Generated by Django 1.11.29 on 2020-07-21 12:30
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('docbow', '0004_external_identifier'),
]
operations = [
migrations.AddField(
model_name='deleteddocument',
name='soft_delete',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='deleteddocument',
name='soft_delete_date',
field=models.DateTimeField(null=True),
),
]

View File

@ -1,131 +0,0 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding field 'DocumentForwarded.automatic'
db.add_column('docbow_documentforwarded', 'automatic', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False)
# Deleting field 'AutomaticForwarding.automatic'
db.delete_column('docbow_automaticforwarding', 'automatic')
def backwards(self, orm):
# Deleting field 'DocumentForwarded.automatic'
db.delete_column('docbow_documentforwarded', 'automatic')
# Adding field 'AutomaticForwarding.automatic'
db.add_column('docbow_automaticforwarding', 'automatic', self.gf('django.db.models.fields.BooleanField')(default=False), keep_default=False)
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'docbow.attachedfile': {
'Meta': {'object_name': 'AttachedFile'},
'content': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'attached_files'", 'to': "orm['docbow.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
},
'docbow.automaticforwarding': {
'Meta': {'object_name': 'AutomaticForwarding'},
'filetypes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'forwarding_rules'", 'symmetrical': 'False', 'to': "orm['docbow.FileType']"}),
'forward_to_list': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'as_recipient_forwarding_rules'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['docbow.MailingList']"}),
'forward_to_user': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'as_recipient_forwarding_rules'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'docbow.content': {
'Meta': {'ordering': "['description']", 'object_name': 'Content'},
'description': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'docbow.document': {
'Meta': {'ordering': "['-date']", 'object_name': 'Document'},
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'filetype': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['docbow.FileType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'sender': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'documents_sent'", 'to': "orm['auth.User']"}),
'to_list': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['docbow.MailingList']", 'null': 'True', 'blank': 'True'}),
'to_user': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'directly_received_documents'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"})
},
'docbow.documentforwarded': {
'Meta': {'object_name': 'DocumentForwarded'},
'automatic': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'auto_now_add': 'True', 'blank': 'True'}),
'from_document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'document_forwarded_to'", 'to': "orm['docbow.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'to_document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'document_forwarded_from'", 'to': "orm['docbow.Document']"})
},
'docbow.filetype': {
'Meta': {'ordering': "['name']", 'object_name': 'FileType'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'})
},
'docbow.mailbox': {
'Meta': {'ordering': "['-date']", 'object_name': 'Mailbox'},
'date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'auto_now_add': 'True', 'blank': 'True'}),
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'mailboxes'", 'to': "orm['docbow.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'outbox': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'documents'", 'to': "orm['auth.User']"}),
'seen': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
},
'docbow.mailinglist': {
'Meta': {'ordering': "['name']", 'object_name': 'MailingList'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'members': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'mailing_lists'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
},
'docbow.sendinglimitation': {
'Meta': {'object_name': 'SendingLimitation'},
'filetypes': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'filetype_limitation'", 'blank': 'True', 'to': "orm['docbow.FileType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'lists': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'lists_limitation'", 'symmetrical': 'False', 'to': "orm['docbow.MailingList']"}),
'mailing_list': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['docbow.MailingList']", 'unique': 'True'})
}
}
complete_apps = ['docbow']

View File

@ -0,0 +1,19 @@
# Generated by Django 1.11.29 on 2020-12-10 14:33
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('docbow', '0005_soft_delete'),
]
operations = [
migrations.AddField(
model_name='document',
name='extra_senders',
field=models.ManyToManyField(to=settings.AUTH_USER_MODEL, verbose_name='Extra senders'),
),
]

View File

@ -1,131 +0,0 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding M2M table for field originaly_to_user on 'AutomaticForwarding'
db.create_table('docbow_automaticforwarding_originaly_to_user', (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('automaticforwarding', models.ForeignKey(orm['docbow.automaticforwarding'], null=False)),
('user', models.ForeignKey(orm['auth.user'], null=False))
))
db.create_unique('docbow_automaticforwarding_originaly_to_user', ['automaticforwarding_id', 'user_id'])
def backwards(self, orm):
# Removing M2M table for field originaly_to_user on 'AutomaticForwarding'
db.delete_table('docbow_automaticforwarding_originaly_to_user')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'docbow.attachedfile': {
'Meta': {'object_name': 'AttachedFile'},
'content': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'attached_files'", 'to': "orm['docbow.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
},
'docbow.automaticforwarding': {
'Meta': {'object_name': 'AutomaticForwarding'},
'filetypes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'forwarding_rules'", 'symmetrical': 'False', 'to': "orm['docbow.FileType']"}),
'forward_to_list': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'as_recipient_forwarding_rules'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['docbow.MailingList']"}),
'forward_to_user': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'as_recipient_forwarding_rules'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'originaly_to_user': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'as_original_recipient_forwarding_rules'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"})
},
'docbow.content': {
'Meta': {'ordering': "['description']", 'object_name': 'Content'},
'description': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'docbow.document': {
'Meta': {'ordering': "['-date']", 'object_name': 'Document'},
'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'filetype': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['docbow.FileType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'sender': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'documents_sent'", 'to': "orm['auth.User']"}),
'to_list': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['docbow.MailingList']", 'null': 'True', 'blank': 'True'}),
'to_user': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'directly_received_documents'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"})
},
'docbow.documentforwarded': {
'Meta': {'object_name': 'DocumentForwarded'},
'automatic': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'auto_now_add': 'True', 'blank': 'True'}),
'from_document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'document_forwarded_to'", 'to': "orm['docbow.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'to_document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'document_forwarded_from'", 'to': "orm['docbow.Document']"})
},
'docbow.filetype': {
'Meta': {'ordering': "['name']", 'object_name': 'FileType'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '128'})
},
'docbow.mailbox': {
'Meta': {'ordering': "['-date']", 'object_name': 'Mailbox'},
'date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'auto_now_add': 'True', 'blank': 'True'}),
'deleted': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'document': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'mailboxes'", 'to': "orm['docbow.Document']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'outbox': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'documents'", 'to': "orm['auth.User']"}),
'seen': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
},
'docbow.mailinglist': {
'Meta': {'ordering': "['name']", 'object_name': 'MailingList'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'members': ('django.db.models.fields.related.ManyToManyField', [], {'blank': 'True', 'related_name': "'mailing_lists'", 'null': 'True', 'symmetrical': 'False', 'to': "orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '128'})
},
'docbow.sendinglimitation': {
'Meta': {'object_name': 'SendingLimitation'},
'filetypes': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'filetype_limitation'", 'blank': 'True', 'to': "orm['docbow.FileType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'lists': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'lists_limitation'", 'symmetrical': 'False', 'to': "orm['docbow.MailingList']"}),
'mailing_list': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['docbow.MailingList']", 'unique': 'True'})
}
}
complete_apps = ['docbow']

View File

@ -0,0 +1,17 @@
# Generated by Django 1.11.29 on 2021-02-24 12:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('docbow', '0006_extra_senders'),
]
operations = [
migrations.AddField(
model_name='filetype',
name='extra_senders',
field=models.PositiveIntegerField(default=0, verbose_name='Extra senders'),
),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 3.2.16 on 2023-06-13 11:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('docbow', '0007_filetype_extra_senders'),
]
operations = [
migrations.AlterField(
model_name='mailinglist',
name='name',
field=models.CharField(max_length=1000, verbose_name='Name'),
),
]

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,206 @@
import importlib
import logging
from django.conf import settings
from django.core.mail import EmailMultiAlternatives
from django.template.loader import TemplateDoesNotExist, render_to_string
from django.utils.encoding import force_str
from django.utils.translation import gettext_lazy as _
from django_journal import journal as django_journal
from docbow_project.docbow import app_settings, models
logger = logging.getLogger(__name__)
class BaseNotifier:
def __init__(self):
# accumulate preferences of users first
self.filter = set()
for np in models.NotificationPreference.objects.filter(kind=self.key, value=False):
self.filter.add((np.user.id, np.filetype.id))
def skip(self, notification):
'''Verify if notification should be skipped'''
if notification.document and notification.user:
if (notification.user.id, notification.document.filetype.id) in self.filter:
return True
return False
def generate_part(self, template_path, notification):
template_path = template_path.format(kind=notification.kind)
try:
ctx = {
'notification': notification,
'settings': settings,
}
if isinstance(notification.ctx, dict):
ctx.update(notification.ctx)
return render_to_string(template_path, ctx)
except TemplateDoesNotExist:
return None
def process(sef, notification):
pass
def finish(self):
pass
class MailNotifier(BaseNotifier):
description = _('Email')
key = 'email'
subject_template = 'docbow/email-notification_{kind}_subject.txt'
body_template = 'docbow/email-notification_{kind}_body.txt'
html_body_template = 'docbow/email-notification_{kind}_body.html'
def process(self, notification):
emails = []
if notification.user:
user = notification.user
if user.email:
emails.append(user.email)
if app_settings.PERSONAL_EMAIL:
try:
personal_email = user.docbowprofile.personal_email
if personal_email:
emails.append(personal_email)
except models.DocbowProfile.DoesNotExist:
pass
if not emails and notification.ctx and 'to' in notification.ctx:
emails.extend(notification.ctx['to'])
emails = [email for email in emails if email]
if not emails:
return
headers = {}
if notification.ctx and 'reply_to' in notification.ctx:
headers['Reply-To'] = notification.ctx['reply_to']
to = set(emails)
subject = self.generate_part(self.subject_template, notification)
subject = subject.replace('\n', '').replace('\r', '')
body = self.generate_part(self.body_template, notification)
html_body = self.generate_part(self.html_body_template, notification)
mail = EmailMultiAlternatives(to=list(to), subject=subject, body=body, headers=headers)
if html_body:
mail.attach_alternative(html_body, 'text/html')
mail.send(fail_silently=False)
django_journal.record(
'mail-notify',
'mail notification {notification} ' 'sent for document {document} to {to} of user {recipient}',
notification=notification,
recipient=notification.user,
document=notification.document,
to=','.join(to),
)
class SMSNotifier(BaseNotifier):
"""Variable in sms notification:
- document
- settings
"""
description = _('SMS')
key = 'sms'
body_template = 'docbow/sms-notification_{kind}_body.txt'
def __init__(self):
super().__init__()
self.mobile_phones = dict()
def process(self, notification):
if not app_settings.MOBILE_PHONE:
return
try:
profile = models.DocbowProfile.objects.get(user=notification.user)
except models.DocbowProfile.DoesNotExist:
return
if not profile.mobile_phone:
return
self.mobile_phones.setdefault((notification.document, notification.kind), []).append(
(notification.user, profile.mobile_phone)
)
@classmethod
def get_carrier(cls):
return resolve_class(settings.DOCBOW_SMS_CARRIER_CLASS)()
def finish(self):
if not self.mobile_phones:
return
exc = None
for key, value in self.mobile_phones.items():
try:
document, kind = key
notification = models.Notification(document=document, kind=kind)
body = self.generate_part(self.body_template, notification)
sms_carrier = self.get_carrier()
for user, phone_number in value:
django_journal.record(
'sms-notify',
'sms notification for document {document} to user '
'{recipient} with phone number {phone_number}',
document=document,
recipient=user,
phone_number=phone_number,
)
sms_carrier.send_sms([v[1] for v in value], body)
except Exception as e:
exc = e
if exc:
raise exc
def resolve_class(class_path):
module, cls_name = class_path.rsplit('.', 1)
module = importlib.import_module(module)
return getattr(module, cls_name)
def get_notifiers():
notification_classes = getattr(
settings, 'DOCBOW_NOTIFIERS', ['docbow_project.docbow.notification.MailNotifier']
)
return [resolve_class(class_path)() for class_path in notification_classes]
def process_notifications():
notifiers = get_notifiers()
for notification in models.Notification.objects.order_by('id').select_for_update().filter(done=False):
for notifier in notifiers:
failures = []
if not notifier.skip(notification):
try:
notifier.process(notification)
except Exception as e:
failures.append(
'Exception %r when handling with notifier %r' % (force_str(e), notifier.__class__)
)
logger.exception(
'Exception when handling notification %r with notifier %r', notification, notifier
)
django_journal.error_record(
'error',
'notification {notification} failed for ' 'notifier {notifier}',
notification=notification,
notifier=notifier.__class__,
)
notification.done = True
if len(failures) > 0:
notification.failure = ','.join(failures)
notification.save()
for notifier in notifiers:
try:
notifier.finish()
except Exception as e:
logger.exception(
'Exception when finishing handling ' 'notification %r with notifier %r',
notification,
notifier,
)
django_journal.error_record(
'error',
'unable to finish sending ' 'notification with notifier {notifier}, error: {error}',
notifier=notifier.__class__,
error=force_str(e),
)

View File

@ -0,0 +1,130 @@
#
# w.c.s. - web application for online forms
# Copyright (C) 2005-2013 Entr'ouvert
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
import zipfile
try:
import elementtree.ElementTree as ET
except ImportError:
try:
import xml.etree.ElementTree as ET
except ImportError:
ET = None
from django.utils.encoding import force_str
OFFICE_NS = 'urn:oasis:names:tc:opendocument:xmlns:office:1.0'
TABLE_NS = 'urn:oasis:names:tc:opendocument:xmlns:table:1.0'
TEXT_NS = 'urn:oasis:names:tc:opendocument:xmlns:text:1.0'
XLINK_NS = 'http://www.w3.org/1999/xlink'
class Workbook:
def __init__(self, encoding='utf-8'):
self.sheets = []
self.encoding = encoding
def add_sheet(self, name):
sheet = WorkSheet(self, name)
self.sheets.append(sheet)
return sheet
def get_node(self):
root = ET.Element('{%s}document-content' % OFFICE_NS)
ET.SubElement(root, '{%s}scripts' % OFFICE_NS)
ET.SubElement(root, '{%s}font-face-decls' % OFFICE_NS)
body = ET.SubElement(root, '{%s}body' % OFFICE_NS)
spreadsheet = ET.SubElement(body, '{%s}spreadsheet' % OFFICE_NS)
for sheet in self.sheets:
spreadsheet.append(sheet.get_node())
return root
def get_data(self):
return ET.tostring(self.get_node(), 'utf-8')
def save(self, output):
z = zipfile.ZipFile(output, 'w')
z.writestr('content.xml', self.get_data())
z.writestr('mimetype', 'application/vnd.oasis.opendocument.spreadsheet')
z.writestr(
'META-INF/manifest.xml',
'''<?xml version="1.0" encoding="UTF-8"?>
<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0">
<manifest:file-entry manifest:full-path="/" manifest:media-type="application/vnd.oasis.opendocument.spreadsheet"/>
<manifest:file-entry manifest:full-path="styles.xml" manifest:media-type="text/xml"/>
<manifest:file-entry manifest:full-path="content.xml" manifest:media-type="text/xml"/>
<manifest:file-entry manifest:full-path="META-INF/manifest.xml" manifest:media-type="text/xml"/>
<manifest:file-entry manifest:full-path="mimetype" manifest:media-type="text/plain"/>
</manifest:manifest>''',
)
z.writestr(
'styles.xml',
'''<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<office:document-styles xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0">
</office:document-styles>''',
)
z.close()
class WorkSheet:
def __init__(self, workbook, name):
self.cells = {}
self.name = name
self.workbook = workbook
def write(self, row, column, value, hint=None):
if row not in self.cells:
self.cells[row] = {}
self.cells[row][column] = WorkCell(self, value, hint=hint)
def get_node(self):
root = ET.Element('{%s}table' % TABLE_NS)
root.attrib['{%s}name' % TABLE_NS] = self.name
ET.SubElement(root, '{%s}table-column' % TABLE_NS)
for i in range(0, max(self.cells.keys()) + 1):
row = ET.SubElement(root, '{%s}table-row' % TABLE_NS)
for j in range(0, max(self.cells.get(i).keys()) + 1):
cell = self.cells.get(i, {}).get(j, None)
if not cell:
ET.SubElement(row, '{%s}table-cell' % TABLE_NS)
else:
row.append(cell.get_node())
return root
class WorkCell:
def __init__(self, worksheet, value, hint=None):
if not isinstance(value, str):
value = force_str(value, 'utf-8')
self.value = value
self.worksheet = worksheet
self.hint = hint
def get_node(self):
root = ET.Element('{%s}table-cell' % TABLE_NS)
root.attrib['{%s}value-type' % OFFICE_NS] = 'string'
p = ET.SubElement(root, '{%s}p' % TEXT_NS)
if self.hint == 'uri':
base_filename = self.value.split('/')[-1]
if base_filename:
a = ET.SubElement(p, '{%s}a' % TEXT_NS)
a.attrib['{%s}href' % XLINK_NS] = self.value
a.text = base_filename
return root
p.text = self.value
return root

View File

@ -0,0 +1,340 @@
import urllib.parse
from django.contrib import messages
from django.contrib.auth.models import User
from django.core.exceptions import ImproperlyConfigured
from django.db.transaction import atomic
from django.http import HttpResponseRedirect
from django.utils.translation import gettext as _
from django.views.generic.base import TemplateResponseMixin, View
from django.views.generic.edit import FormView, UpdateView
from django_journal.models import Journal
from docbow_project.docbow import app_settings, cbv, forms, models, utils
class ProfileView(cbv.FormWithRequestMixin, cbv.FormWithPostTarget, UpdateView):
form_class = forms.ProfileForm
template_name = 'docbow/profile.html'
prefix = 'profile'
success_url = '#profile'
def get_object(self):
try:
return models.DocbowProfile.objects.get(user=self.request.user)
except models.DocbowProfile.DoesNotExist:
return None
def is_post_target(self):
return 'profile-validate' in self.request.POST
def form_valid(self, form):
self.request.record('update-profile', 'modified its profile', **form.cleaned_data)
return super().form_valid(form)
class DelegateView(cbv.FormWithPostTarget, FormView):
form_class = forms.DelegationForm
template_name = 'docbow/delegate.html'
success_url = '#delegate'
prefix = 'delegate'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def add_journal_to_delegations(self, delegations):
delegations__to = [delegation.to for delegation in delegations]
journals = (
Journal.objects.for_objects(delegations__to)
.filter(tag__name='login')
.order_by('-time')[: len(delegations__to) * 5]
)
journal_by_users = dict()
for journal in journals:
for objectdata in journal.objectdata_set.all():
if objectdata.tag.name == 'delegate':
journal_by_users.setdefault(objectdata.object_id, []).append(journal.time)
for delegation in delegations:
delegation.journals = journal_by_users.get(delegation.to.id, [])[:5]
@property
def delegations(self):
qs = models.Delegation.objects.all()
qs = qs.filter(by=self.request.user)
qs = qs.select_related()
qs = qs.prefetch_related('to__docbowprofile')
self.add_journal_to_delegations(qs)
return qs
def get_form_kwargs(self, **kwargs):
kwargs = super().get_form_kwargs(**kwargs)
kwargs['user'] = self.request.user
kwargs['delegatees'] = [delegation.to for delegation in self.delegations]
kwargs['request'] = self.request
return kwargs
def delete(self, delegation):
request = self.request
delegation.delete()
delegate_user = delegation.to
request.record(
'delete-delegation',
'deleted delegation ' '{delegation} to user {delegated}',
delegation=delegation.id,
delegated=delegate_user,
)
user = request.user
# notify delegate
ctx = {
'user': utils.clean_ldap_user(user),
'delegate': delegate_user,
'to': [delegate_user.email],
'reply_to': user.email,
}
models.Notification.objects.create(kind='delete-delegation', ctx=ctx)
messages.info(request, _('Delegation %s supressed') % delegate_user)
messages.info(request, _('%s has been notified.') % delegate_user.email)
request.record(
'notify', 'notified {email} of ' 'the deletion of its delegate user', email=delegate_user.email
)
# delete guest accounts
if delegation.guest_delegate:
if 'mellon' in app_settings.settings.INSTALLED_APPS and delegate_user.saml_identifiers.count():
err, json_data, err_desc = utils.a2_wscall(
urllib.parse.urljoin(
app_settings.settings.AUTHENTIC_URL,
'api/users/%s' % delegate_user.saml_identifiers.first().name_id,
),
'delete',
)
delegate_user.delete()
request.record(
'delete-delegate',
'deleted delegate ' 'user {username}, {first_name} {last_name}, ' '({email})',
username=delegate_user.username,
first_name=delegate_user.first_name,
last_name=delegate_user.last_name,
email=delegate_user.email,
)
return HttpResponseRedirect(self.success_url)
def is_post_target(self):
if 'delegate-create' in self.request.POST:
return True
for delegation in self.delegations:
username = delegation.to.username
if f'delegate-delete-{username}.x' in self.request.POST:
return True
return False
@atomic
def post(self, request, *args, **kwargs):
if 'delegate-create' in request.POST:
return super().post(request, *args, **kwargs)
for delegation in self.delegations:
username = delegation.to.username
if f'delegate-delete-{username}.x' in self.request.POST:
return self.delete(delegation)
return self.get(request, *args, **kwargs)
def form_valid(self, form):
request = self.request
is_guest = not form.cleaned_data.get('existing_user')
ctx = {
'user': utils.clean_ldap_user(request.user),
'reply_to': request.user.email,
'is_guest': is_guest,
}
if is_guest:
delegate_username = utils.get_free_delegation_number(request.user)
delegate_user = User(
username=delegate_username,
first_name=form.cleaned_data.get('first_name', ''),
last_name=form.cleaned_data.get('last_name', ''),
email=form.cleaned_data.get('email', ''),
)
delegate_user.set_unusable_password()
delegate_user.save()
models.DocbowProfile.objects.create(
user=delegate_user,
is_guest=True,
accept_notifications=app_settings.DEFAULT_ACCEPT_NOTIFICATIONS_FOR_GUEST,
)
delegation, created = models.Delegation.objects.get_or_create(by=request.user, to=delegate_user)
if 'mellon' in app_settings.settings.INSTALLED_APPS:
import mellon
ctx['sso'] = True
issuer = mellon.models.Issuer.objects.filter(
entity_id__startswith=app_settings.settings.AUTHENTIC_URL
).first()
if not issuer:
raise ImproperlyConfigured('Mellon issuer not found')
mellon.models.UserSAMLIdentifier.objects.create(
name_id=form.cleaned_data['name_id'],
issuer=issuer,
user=delegate_user,
)
request.record(
'create-delegate',
'created delegate with ' 'parameters {first_name} {last_name} <{email}>',
**form.cleaned_data,
)
ctx.update(
{
'password_reset_link': utils.make_password_reset_url(request, delegate_user),
'to': [form.cleaned_data['email']],
'delegate': delegate_user,
'delegation': delegation,
}
)
messages.info(request, _('New delegation to user %s created.') % delegate_user.username)
messages.info(
request, _('A notification was sent to %s about this new delegation') % delegate_user.email
)
else:
delegate_user = form.cleaned_data.get('existing_user')
delegate_username = delegate_user.username
delegation, created = models.Delegation.objects.get_or_create(by=request.user, to=delegate_user)
ctx.update(
{
'to': models.all_emails(delegate_user),
'delegate': delegate_user,
'delegation': delegation,
}
)
messages.info(request, _('New delegation to user %s created.') % delegate_user.username)
messages.info(
request,
_('A notification was sent to %s about this new delegation') % delegate_user.get_full_name(),
)
models.Notification.objects.create(kind='new-delegation', ctx=ctx)
request.record(
'create-delegation', 'created delegation to ' 'user {delegated}', delegated=delegate_user
)
return super().form_valid(form)
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['delegations'] = self.delegations
ctx['received_delegations'] = self.request.user.delegations_by.all()
ctx['delegate_to_existing_user'] = app_settings.DELEGATE_TO_EXISTING_USER
return ctx
class PasswordChangeView(cbv.FormWithPostTarget, FormView):
template_name = 'registration/password_change_form.html'
form_class = forms.PasswordChangeFormWithLogging
prefix = 'password-change'
success_url = '#password-change'
def is_post_target(self):
return 'password-change-validate' in self.request.POST
def form_valid(self, form):
messages.info(self.request, _('Password changed'))
form.save()
return super().form_valid(form)
def get_form_kwargs(self, **kwargs):
kwargs = super().get_form_kwargs(**kwargs)
kwargs['user'] = self.request.user
return kwargs
class EmailView(cbv.FormWithPostTarget, UpdateView):
form_class = forms.EmailForm
template_name = 'docbow/email.html'
prefix = 'email'
success_url = '#email'
def get_object(self):
return self.request.user
def form_valid(self, form):
messages.info(self.request, _('Email changed'))
self.request.record('update-email', 'modified its email', **form.cleaned_data)
return super().form_valid(form)
class NotificationPreferenceView(cbv.FormWithPostTarget, cbv.FormWithRequestMixin, FormView):
form_class = forms.NotificationPreferencesForm
template_name = 'docbow/notifications.html'
success_url = '#notifications'
prefix = 'notifications'
def form_valid(self, form):
form.save()
messages.info(self.request, _('Notification preferences saved'))
return super().form_valid(form)
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['mobile_phone'] = app_settings.MOBILE_PHONE
ctx['mobile_phone_paragraph'] = _(
'If You would like to receive a SMS alert each '
'time your inbox receives a document, provide your '
'mobile phone number. If you do not fill this field '
'you won\'t receive any SMS alert'
)
return ctx
class FullProfileView(TemplateResponseMixin, View):
# multiplex all profile views
template_name = 'docbow/full-profile.html'
subviews = (
('notifications_form', NotificationPreferenceView),
('delegate_form', DelegateView),
)
if 'mellon' not in app_settings.settings.INSTALLED_APPS:
subviews = subviews + (('password_change_form', PasswordChangeView),)
if app_settings.MOBILE_PHONE or app_settings.PERSONAL_EMAIL:
subviews = (('profile_form', ProfileView),) + subviews
if app_settings.EDIT_EMAIL and 'mellon' not in app_settings.settings.INSTALLED_APPS:
subviews += (('email_form', EmailView),)
def dispatch(self, request, *args, **kwargs):
if models.is_guest(request.user):
self.subviews = filter(lambda s: s[0] != 'delegate_form', self.subviews)
print(self.subviews)
return super().dispatch(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
ctx = {}
for var_name, view_class in self.subviews:
res = self.get_view(var_name, view_class)
if isinstance(res, dict):
ctx.update(res)
else:
return res
return self.render_to_response(ctx)
def get_view(self, var_name, view_class):
view = view_class()
view.request, view.args, view.kwargs = self.request, self.args, self.kwargs
if hasattr(view, 'get_object'):
view.object = view.get_object()
if self.request.method == 'POST' and view.is_post_target():
res = view.post(self.request, *self.args, **self.kwargs)
if isinstance(res, HttpResponseRedirect):
return res
else:
ctx = res.context_data
ctx[var_name] = ctx.pop('form')
return ctx
else:
form_class = view.get_form_class()
form = view.get_form(form_class)
return view.get_context_data(**{var_name: form})
def get(self, request, *args, **kwargs):
ctx = {}
for var_name, view_class in self.subviews:
ctx.update(self.get_view(var_name, view_class))
return self.render_to_response(ctx)

View File

@ -5,17 +5,17 @@
# http://jtauber.com/
# Copyright (c) 2006 James Tauber
#
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@ -50,7 +50,6 @@ import os.path
class Trie:
def __init__(self):
self.root = [None, {}]
@ -72,46 +71,43 @@ class Trie:
class Collator:
def __init__(self, filename):
self.table = Trie()
self.load(filename)
def load(self, filename):
for line in open(filename):
if line.startswith("#") or line.startswith("%"):
if line.startswith('#') or line.startswith('%'):
continue
if line.strip() == "":
if line.strip() == '':
continue
line = line[:line.find("#")] + "\n"
line = line[:line.find("%")] + "\n"
line = line[: line.find('#')] + '\n'
line = line[: line.find('%')] + '\n'
line = line.strip()
if line.startswith("@"):
if line.startswith('@'):
pass
else:
semicolon = line.find(";")
semicolon = line.find(';')
charList = line[:semicolon].strip().split()
x = line[semicolon:]
collElements = []
while True:
begin = x.find("[")
begin = x.find('[')
if begin == -1:
break
end = x[begin:].find("]")
collElement = x[begin:begin+end+1]
x = x[begin + 1:]
break
end = x[begin:].find(']')
collElement = x[begin : begin + end + 1]
x = x[begin + 1 :]
alt = collElement[1]
chars = collElement[2:-1].split(".")
chars = collElement[2:-1].split('.')
collElements.append((alt, chars))
integer_points = [int(ch, 16) for ch in charList]
self.table.add(integer_points, collElements)
def sort_key(self, string):
collation_elements = []
lookup_key = [ord(ch) for ch in string]
@ -119,19 +115,20 @@ class Collator:
value, lookup_key = self.table.find_prefix(lookup_key)
if not value:
# @@@
raise ValueError, map(hex, lookup_key)
raise ValueError(map(hex, lookup_key))
collation_elements.extend(value)
sort_key = []
for level in range(4):
if level:
sort_key.append(0) # level separator
sort_key.append(0) # level separator
for element in collation_elements:
ce_l = int(element[1][level], 16)
if ce_l:
sort_key.append(ce_l)
return tuple(sort_key)
collator = Collator(os.path.join(os.path.dirname(__file__), 'allkeys.txt'))

View File

@ -1,58 +1,103 @@
import logging
from django.contrib.auth.signals import user_logged_in, user_logged_out
from django.db.models.signals import post_save as db_post_save, m2m_changed
from django.contrib.auth import models as auth_models
from django.utils.translation import ugettext as _
from django.contrib.auth.models import User
from django.contrib.auth.signals import user_logged_in, user_logged_out
from django.db.models.signals import m2m_changed
from django.db.models.signals import post_save as db_post_save
from django.dispatch import receiver
from django.utils.translation import gettext_noop as N_
from django_journal import journal as django_journal
import models
from logger_adapter import DjangoLoggerAdapter
from middleware import get_extra
from docbow_project.docbow import middleware, models
db_logger = logging.getLogger('docbow.db')
auth_logger = logging.getLogger('docbow.auth')
def logged_in_handler(sender, request, user, **kwargs):
global auth_logger
new_logger = DjangoLoggerAdapter(auth_logger, request)
new_logger.info(_('logged in'))
extra = middleware.get_extra()
kwargs = {}
msg = '{user} logged in'
if hasattr(user, 'delegate'):
kwargs['delegate'] = user.delegate
msg = '{delegate} logged in'
if hasattr(user, 'user'):
user = user.user
django_journal.record('login', msg, user=user, ip=extra['ip'], **kwargs)
def logged_out_handler(sender, request, user, **kwargs):
global auth_logger
new_logger = DjangoLoggerAdapter(auth_logger, request)
new_logger.info(_('logged out'))
extra = middleware.get_extra()
msg = '{user} logged out'
if hasattr(user, 'delegate'):
kwargs['delegate'] = user.delegate
msg = '{delegate} logged out'
if hasattr(user, 'user'):
user = user.user
django_journal.record('logout', msg, user=user, ip=extra['ip'], **kwargs)
do_log = True
def modified_data(sender, instance, created, raw, using, **kwargs):
global db_logger
if created:
msg = _('created %(model)s %(name)s')
else:
msg = _('modified %(model)s %(name)s')
extra = get_extra()
if hasattr(extra['user'], 'is_anonymous') and extra['user'].is_anonymous():
global do_log
if using != 'default':
return
db_logger.info(msg, dict(model=unicode(sender._meta.verbose_name),
name=instance), extra=extra)
extra = middleware.get_extra()
if not do_log:
return
if hasattr(extra['user'], 'is_anonymous') and extra['user'].is_anonymous:
extra['user'] = '-'
if extra['user'] == middleware.NO_USER:
return
if created:
tag = 'create'
msg = '{user} created {model} {instance}'
else:
tag = 'modify'
msg = '{user} modified {model} {instance}'
django_journal.record(
tag, msg, user=extra['user'], ip=extra['ip'], model=sender._meta.model_name, instance=instance
)
def list_m2m_changed_handler(sender, instance, action, reverse, model, pk_set, using, **kwargs):
global db_logger
msg = None
if action == 'post_add':
msg = _('added user %(user)s to list %(list)s')
if using != 'default':
return
if action.startswith('pre'):
return
action.replace('_', '-')
extra = middleware.get_extra()
if hasattr(extra['user'], 'is_anonymous') and extra['user'].is_anonymous:
extra['user'] = '-'
if extra['user'] == middleware.NO_USER:
return
users = model.objects.filter(pk__in=pk_set or [])
if action == 'post_clear':
msg = N_('cleared mailing list {list}')
users = [None]
elif action == 'post_add':
msg = N_('added user {member} to mailing list {list}')
elif action == 'post_remove':
msg = _('removed user %(user)s from list %(list)s')
elif action == 'post_clear':
msg = _('cleared list %(user)s%(list)s')
if msg:
db_logger.info(msg,
dict(user=', '.join(map(unicode, model.objects.filter(pk__in=pk_set or []))),
list=unicode(instance)), extra=get_extra())
msg = N_('removed user {member} from mailing list {list}')
for user in users:
django_journal.record(action, msg, list=instance, member=user, ip=extra['ip'], user=extra['user'])
@receiver(m2m_changed, sender=User.groups.through)
def groups_changed(sender, instance, action, **kwargs):
'''When a user get a group, give it access to the administration.'''
global do_log
if action.startswith('post'):
try:
do_log = False
if not instance.is_superuser:
instance.is_staff = instance.groups.exists()
instance.save()
finally:
do_log = True
user_logged_in.connect(logged_in_handler)
user_logged_out.connect(logged_out_handler)
db_post_save.connect(modified_data, sender=models.MailingList)
db_post_save.connect(modified_data, sender=auth_models.User)
db_post_save.connect(modified_data, sender=auth_models.Group)
db_post_save.connect(modified_data, sender=models.DocbowUser)
db_post_save.connect(modified_data, sender=models.DocbowGroup)
m2m_changed.connect(list_m2m_changed_handler, sender=models.MailingList.members.through)

View File

@ -0,0 +1,51 @@
import json
import logging
from urllib.request import urlopen
from django.conf import settings
from django.utils.encoding import force_str
from django.utils.http import urlencode
from django_journal import journal as django_journal
logger = logging.getLogger(__name__)
class OVHSMSCarrier:
URL = 'https://www.ovh.com/cgi-bin/sms/http2sms.cgi'
SMS_CLASS = 1
def send_sms(self, to, message, sms_class=None, no_stop=True):
payload = force_str(message).encode('utf-8')
sms_class = sms_class or self.SMS_CLASS
to = ','.join([t.replace('+', '00') for t in to])
params = {
'account': settings.OVH_SMS_ACCOUNT,
'login': settings.OVH_SMS_LOGIN,
'password': settings.OVH_SMS_PASSWORD,
'from': settings.OVH_SMS_FROM,
'to': to,
'message': payload,
'contentType': 'text/json',
'class': sms_class,
}
if no_stop:
params['no_stop'] = 1
django_journal.error_record(
'ovh-sms', 'OVH SMS CARRIER: sending message {message} to {numbers}', message=message, numbers=to
)
stream = urlopen('%s?%s' % (self.URL, urlencode(params)))
result = json.loads(stream.read())
if 100 <= result['status'] < 200:
credit_alert = getattr(settings, 'OVH_SMS_CREDIT_ALERT', 100)
credit_left = result['creditLeft']
if credit_left < credit_alert:
django_journal.error_record(
'error',
'OVH SMS CARRIER: credit ' 'left({credit_left}) < credit alert limit({credit_alert})',
credit_left=credit_left,
credit_alert=credit_alert,
)
else:
django_journal.error_record(
'error', 'OVH SMS CARRIER: status "{status}"' 'message "{message}"', **result
)

View File

@ -0,0 +1,104 @@
from django.db import connection
def get_sql(sql, params):
"""
Execute an SQL query and return the associated cursor.
"""
cursor = connection.cursor()
cursor.execute(sql, params)
return cursor
def get_sql_count(sql, params):
"""
Execute a count on SUB-SELECT and return the count.
"""
cursor = get_sql('''SELECT COUNT(*) FROM (%s) AS CNT''' % sql, params)
return cursor.fetchone()[0]
def get_sql_ids(sql, params):
"""
Retrieve a list of numerical ids.
"""
cursor = get_sql(sql, params)
return (row[0] for row in cursor.fetchall())
def get_complex_join(qs, sql, params):
ids = list(get_sql_ids(sql, params))
return qs.filter(pk__in=ids)
def get_unseen_documents_count(related_users, user):
query = GET_UNSEEN_DOCUMENTS_SQL % ('(%s)' % ', '.join(['%s'] * len(related_users)))
return get_sql_count(
query,
(False,)
+ tuple(related_users.values_list('id', flat=True))
+ (user.pk, user.pk, False, user.pk, True),
)
def get_documents(qs, related_users, user, outbox):
query = GET_DOCUMENTS_SQL % ('(%s)' % ', '.join(['%s'] * len(related_users)))
qs = get_complex_join(
qs,
query,
(outbox,) + tuple(related_users.values_list('id', flat=True)) + (user.pk, False, user.pk, True),
)
qs = qs.prefetch_related('to_list', 'to_user', 'mailboxes__owner')
qs = qs.extra(select={'seen': SEEN_DOCUMENT % user.pk})
return qs
def get_trash_documents(qs, related_users, user, outbox):
query = GET_TRASH_DOCUMENTS_SQL % ('(%s)' % ', '.join(['%s'] * len(related_users)))
qs = get_complex_join(
qs,
query,
(outbox,) + tuple(related_users.values_list('id', flat=True)) + (user.pk, False, user.pk, True),
)
qs = qs.prefetch_related('to_list', 'to_user', 'mailboxes__owner')
return qs
GET_UNSEEN_DOCUMENTS_SQL = '''SELECT d.id
FROM docbow_document AS d
INNER JOIN docbow_mailbox AS mb
ON mb.outbox = %%s AND mb.document_id = d.id AND mb.owner_id IN %s
WHERE
NOT EXISTS(SELECT 1 FROM docbow_deleteddocument dd WHERE dd.document_id = d.id AND dd.user_id = %%s)
AND NOT EXISTS(SELECT 1 FROM docbow_seendocument sd WHERE sd.document_id = d.id AND sd.user_id = %%s)
AND (d.private = %%s OR (mb.owner_id = %%s AND d.private = %%s))
GROUP BY d.id, d.date
ORDER BY d.date
'''
GET_DOCUMENTS_SQL = '''SELECT d.id
FROM docbow_document AS d
INNER JOIN docbow_mailbox AS mb ON
mb.outbox = %%s AND mb.document_id = d.id AND mb.owner_id IN %s
WHERE
NOT EXISTS(SELECT 1 FROM docbow_deleteddocument dd WHERE dd.document_id = d.id AND dd.user_id = %%s)
AND (d.private = %%s OR (mb.owner_id = %%s AND d.private = %%s))
GROUP BY d.id, d.date
ORDER BY d.date'''
GET_TRASH_DOCUMENTS_SQL = '''SELECT d.id
FROM docbow_document AS d
INNER JOIN docbow_mailbox AS mb ON
mb.outbox = %%s AND mb.document_id = d.id AND mb.owner_id IN %s
LEFT JOIN docbow_deleteddocument AS dd ON
dd.document_id = d.id AND dd.user_id = %%s AND dd.soft_delete
WHERE (d.private = %%s OR (mb.owner_id = %%s AND d.private = %%s))
GROUP BY d.id, d.date
ORDER BY d.date'''
SEEN_DOCUMENT = '''SELECT COUNT(*) > 0
FROM docbow_seendocument
WHERE docbow_seendocument.document_id = docbow_document.id
AND docbow_seendocument.user_id = %s'''

View File

@ -1,100 +0,0 @@
@charset 'UTF-8';
/*
* jQuery File Upload UI Plugin CSS 5.0.6
* https://github.com/blueimp/jQuery-File-Upload
*
* Copyright 2010, Sebastian Tschan
* https://blueimp.net
*
* Licensed under the MIT license:
* http://creativecommons.org/licenses/MIT/
*/
.fileupload-buttonbar .ui-button input {
position: absolute;
top: 0;
right: 0;
margin: 0;
border: solid transparent;
border-width: 0 0 100px 200px;
opacity: 0;
filter: alpha(opacity=0);
-o-transform: translate(250px, -50px) scale(1);
-moz-transform: translate(-300px, 0) scale(4);
direction: ltr;
cursor: pointer;
}
.fileinput-button {
overflow: hidden;
}
/* Fix for IE 6: */
*html .fileinput-button {
padding: 2px 0;
}
/* Fix for IE 7: */
*+html .fileinput-button {
padding: 2px 0;
}
.fileupload-buttonbar {
padding: 0.2em 0.4em;
}
.fileupload-buttonbar .ui-button {
vertical-align: middle;
}
.fileupload-content {
padding: 0.2em 0.4em;
border-top-width: 0;
}
.fileupload-content .ui-progressbar {
width: 200px;
height: 20px;
}
.fileupload-content .ui-progressbar-value {
background: url(../images/pbar-ani.gif);
}
.fileupload-content .fileupload-progressbar {
width: 400px;
margin: 10px 0;
}
.files {
margin: 10px 0;
border-collapse: collapse;
}
.files td {
padding: 5px;
border-spacing: 5px;
}
.files img {
border: none;
}
.files .name {
padding: 0 10px;
}
.files .size {
padding: 0 10px 0 0;
text-align: right;
white-space: nowrap;
}
.ui-state-disabled .ui-state-disabled {
opacity: 1;
filter: alpha(opacity=100);
}
.ui-state-disabled input {
cursor: default;
}

View File

@ -0,0 +1,8 @@
.table-container th.asc:after {
content: '\0000a0\0025b2';
float: right;
}
.table-container th.desc:after {
content: '\0000a0\0025bc';
float: right;
}

View File

@ -0,0 +1,127 @@
table.paleblue {
border-collapse: collapse;
border-color: #CCC;
border: 1px solid #DDD;
}
table.paleblue,
table.paleblue + ul.pagination {
font: normal 11px/14px 'Lucida Grande', Verdana, Helvetica, Arial, sans-serif;
}
table.paleblue a:link,
table.paleblue a:visited,
table.paleblue + ul.pagination > li > a {
color: #5B80B2;
text-decoration: none;
font-weight: bold;
}
table.paleblue a:hover {
color: #036;
}
table.paleblue td,
table.paleblue th {
padding: 5px;
line-height: 13px;
border-bottom: 1px solid #EEE;
border-left: 1px solid #DDD;
text-align: left;
}
table.paleblue thead th:first-child,
table.paleblue thead td:first-child {
border-left: none !important;
}
table.paleblue thead th,
table.paleblue thead td {
background: #FCFCFC url(../img/header-bg.png) left bottom repeat-x;
border-bottom: 1px solid #DDD;
padding: 2px 5px;
font-size: 11px;
vertical-align: middle;
color: #666;
}
table.paleblue thead th > a:link,
table.paleblue thead th > a:visited {
color: #666;
}
table.paleblue thead th.orderable > a {
padding-right: 20px;
background: url(../img/arrow-inactive-up.png) right center no-repeat;
}
table.paleblue thead th.orderable.asc > a {
background-image: url(../img/arrow-active-up.png);
}
table.paleblue thead th.orderable.desc > a {
background-image: url(../img/arrow-active-down.png);
}
table.paleblue tr.odd {
background-color: #EDF3FE;
}
table.paleblue tr.even {
background-color: white;
}
table.paleblue + ul.pagination {
background: white url(../img/pagination-bg.gif) left 180% repeat-x;
overflow: auto;
margin: 0;
padding: 10px;
border: 1px solid #DDD;
list-style: none;
}
table.paleblue + ul.pagination > li {
float: left;
line-height: 22px;
margin-left: 10px;
}
table.paleblue + ul.pagination > li:first-child {
margin-left: 0;
}
table.paleblue + ul.pagination > li.cardinality {
float: right;
color: #8d8d8d;
}
table.paleblue > tbody > tr > td > span.true,
table.paleblue > tbody > tr > td > span.false {
background-position: top left;
background-repeat: no-repeat;
display: inline-block;
height: 10px;
overflow: hidden;
text-indent: -200px;
width: 10px;
}
table.paleblue > tbody > tr > td > .missing {
background: transparent url(../img/missing.png) right center no-repeat;
color: #717171;
padding-right: 20px;
}
table.paleblue > tbody > tr > td > .missing:hover {
color: #333;
}
table.paleblue > tbody > tr > td > span.true {
background-image: url(../img/true.gif);
}
table.paleblue > tbody > tr > td > span.false {
background-image: url(../img/false.gif);
}
div.table-container {
display: inline-block;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 B

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