Compare commits

...

1707 Commits

Author SHA1 Message Date
Benjamin Dauvergne 41ab1bfa16
lingo: send the amount paid in pay_invoice() and notify() (#76572)
gitea/combo/pipeline/head This commit looks good Details
This commit factorize the creation of the JSON payload in pay_invoice()
and notify(), so that they send the same information.

The pay_invoice method is moved on the Transaction class.
2024-05-03 13:52:21 +02:00
Frédéric Péters 5cabbb9526 misc: let django-upgrade switch re_path() to path() (leftovers) (#90264)
gitea/combo/pipeline/head This commit looks good Details
2024-05-02 15:52:11 +02:00
Frédéric Péters c63bd4cfc2 misc: import re_path from django.urls (leftovers) (#90264) 2024-05-02 15:52:02 +02:00
Frédéric Péters a9ec23e960 misc: let django-upgrade switch re_path() to path() (#90229)
gitea/combo/pipeline/head This commit looks good Details
2024-04-30 20:45:41 +02:00
Frédéric Péters 86ea7e7869 misc: import re_path from django.urls (#90229) 2024-04-30 20:45:40 +02:00
Valentin Deniaud 8f48b3ff03 translation update
gitea/combo/pipeline/head This commit looks good Details
2024-04-30 11:27:52 +02:00
Yann Weber 04e1f69ff3 dataviz: add an option to print values directly on chart (#68944)
gitea/combo/pipeline/head This commit looks good Details
2024-04-30 11:09:03 +02:00
Valentin Deniaud 0df5a39b43 dataviz: remove all forms option from cell filter params (#90131)
gitea/combo/pipeline/head This commit looks good Details
2024-04-30 10:48:04 +02:00
Valentin Deniaud 11edb0c03a dataviz: use select2 without ajax in filters cell (#71885)
gitea/combo/pipeline/head This commit looks good Details
2024-04-30 10:46:21 +02:00
Valentin Deniaud 381d63467d dataviz: use select2 widget for all filters (#71885) 2024-04-30 10:46:21 +02:00
Valentin Deniaud 057f3ebd8b dataviz: refactor building of choice list (#71885) 2024-04-30 10:46:21 +02:00
Corentin Sechet 0c306eeebc misc: remove dangling files (#90133)
gitea/combo/pipeline/head This commit looks good Details
2024-04-29 17:40:16 +02:00
Valentin Deniaud 7961c5544f dataviz: add pie percent chart type (#71666)
gitea/combo/pipeline/head This commit looks good Details
2024-04-29 09:43:55 +02:00
Corentin Sechet 20ab0b42d6 ci: migrate tox to nox (#89962)
gitea/combo/pipeline/head This commit looks good Details
2024-04-26 11:50:24 +02:00
Valentin Deniaud af473d684e translation update
gitea/combo/pipeline/head This commit looks good Details
2024-04-16 10:57:17 +02:00
Valentin Deniaud 6be1d6c5fc dataviz: allow control of total display in tables (#85654)
gitea/combo/pipeline/head This commit looks good Details
2024-04-16 10:32:04 +02:00
Valentin Deniaud 5e8b58c6ca dataviz: transpose data before ods export to match html display (#85654) 2024-04-16 10:32:04 +02:00
Valentin Deniaud b20464820a tests: fix useless assertions on chart cell form fields (#85654) 2024-04-16 10:32:04 +02:00
Lauréline Guérin 8a10f3eab2 snapshots: json diff, use gadjo to collapse lines between changes (#89483)
gitea/combo/pipeline/head This commit looks good Details
2024-04-16 10:14:18 +02:00
Yann Weber d9b5247f44 wcs: check code syntax before searching for it (#89461)
gitea/combo/pipeline/head This commit looks good Details
2024-04-11 18:22:15 +02:00
Frédéric Péters 46e7c037f5 translation update
gitea/combo/pipeline/head This commit looks good Details
2024-04-05 17:49:41 +02:00
Lauréline Guérin 10055d8e54
export_import: post bundle (#89034)
gitea/combo/pipeline/head This commit looks good Details
2024-04-03 17:20:31 +02:00
Frédéric Péters 1a964cd76b translation update
gitea/combo/pipeline/head This commit looks good Details
2024-04-01 18:15:27 +02:00
Frédéric Péters a688c183e8 misc: adjust default osm attribution (#88906)
gitea/combo/pipeline/head This commit looks good Details
2024-03-31 08:45:37 +02:00
Frédéric Péters 7df1e3f997 translation update
gitea/combo/pipeline/head This commit looks good Details
2024-03-29 10:13:34 +01:00
Frédéric Péters 7b66dca2ba maps: add option to include a search button (#88131)
gitea/combo/pipeline/head This commit looks good Details
2024-03-29 08:29:05 +01:00
Lauréline Guérin d964be219e
snapshots: do not delete snapshots on user deletion (#88622)
gitea/combo/pipeline/head This commit looks good Details
2024-03-26 13:18:45 +01:00
Frédéric Péters bfdefa73cc translation update
gitea/combo/pipeline/head This commit looks good Details
2024-03-25 16:28:35 +01:00
Lauréline Guérin 820bab39b7
export_import: limit APIs to admin users (#88132)
gitea/combo/pipeline/head This commit looks good Details
2024-03-25 09:43:42 +01:00
Lauréline Guérin 50cd07545c
export_import: invalid bundle (#88132) 2024-03-25 09:43:41 +01:00
Frédéric Péters 1a4be6ec3e translation update
gitea/combo/pipeline/head This commit looks good Details
2024-03-19 16:57:54 +01:00
Lauréline Guérin 21407807ad
export_import: rebuild page positions after import (#86627)
gitea/combo/pipeline/head This commit looks good Details
2024-03-15 08:28:42 +01:00
Lauréline Guérin 741efc0e35
misc: change create_bundle logic in tests (#86627) 2024-03-15 08:28:42 +01:00
Lauréline Guérin 370beb3a84
data: remove parent field from page snapshots (#86627) 2024-03-15 08:28:42 +01:00
Lauréline Guérin b20230d34f
data: remove order field from page snapshots (#86627) 2024-03-15 08:28:42 +01:00
Lauréline Guérin 150f6e954f
applications: don't fail if wcs is not responding (#86520)
gitea/combo/pipeline/head This commit looks good Details
2024-03-14 11:47:18 +01:00
Lauréline Guérin fd0d9c6fb7
applications: don't import get_wcs_dependencies_from_template everywhere (#86520) 2024-03-14 11:29:24 +01:00
Lauréline Guérin c1b431922f
applications: lingo cells dependencies to card models (#86520) 2024-03-14 11:29:22 +01:00
Lauréline Guérin 995e3773cf
applications: weekly agenda cell dependencies to card models (#86520) 2024-03-14 11:29:01 +01:00
Lauréline Guérin ea3d41d222
applications: search cell dependencies to pages and card models (#86520) 2024-03-14 11:28:25 +01:00
Lauréline Guérin 8f6ab11272
applications: cell condition dependencies to card models (#86520) 2024-03-14 11:27:31 +01:00
Lauréline Guérin 7e43932262
applications: page dependencies to card models (#86520) 2024-03-14 11:27:31 +01:00
Lauréline Guérin 81b27151a3
applications: card cell dependencies to pages (#86520) 2024-03-14 11:27:31 +01:00
Lauréline Guérin f74e768a11
applications: card cell dependencies to card models (#86520) 2024-03-14 11:27:31 +01:00
Lauréline Guérin 057c8f49a0
manager: fix cell form rendering when disabled (#87871)
gitea/combo/pipeline/head This commit looks good Details
2024-03-08 14:00:31 +01:00
Lauréline Guérin 9f6ca2c862
translation update
gitea/combo/pipeline/head This commit looks good Details
2024-03-07 20:22:12 +01:00
Lauréline Guérin 9480ed89dc
export_import: clean old jobs (#87614)
gitea/combo/pipeline/head This commit looks good Details
2024-03-07 20:09:52 +01:00
Lauréline Guérin d128ae63be
export_import: endpoints import and declare with async job (#87614) 2024-03-07 20:09:52 +01:00
Yann Weber 61fc9aab9b update translations
gitea/combo/pipeline/head This commit looks good Details
(#86995)
2024-03-05 16:55:12 +01:00
Yann Weber c9a4a74417 Revert "Update translation"
gitea/combo/pipeline/head This commit looks good Details
This reverts commit 7815d2d00e.
2024-03-05 15:38:49 +01:00
Yann Weber 7815d2d00e Update translation
gitea/combo/pipeline/head This commit looks good Details
(#86995)
2024-03-05 15:12:46 +01:00
Yann Weber d9fd99f1dd assets: add a check to see if uploaded images are handled by PIL (#86995)
gitea/combo/pipeline/head This commit looks good Details
2024-03-05 15:03:50 +01:00
Lauréline Guérin 59c14da940
translation update
gitea/combo/pipeline/head This commit looks good Details
2024-02-29 18:07:58 +01:00
Lauréline Guérin 7913efd0ab data: export/import of page picture content (#86870)
gitea/combo/pipeline/head This commit looks good Details
2024-02-29 12:07:14 +01:00
Lauréline Guérin f05be333cb assets: don't export or clean non asset files (#86870) 2024-02-29 12:07:14 +01:00
Lauréline Guérin 1f8509f715 misc: fix typo in tests (#86870) 2024-02-29 12:07:14 +01:00
Frédéric Péters e76a113f74 misc: extend regex used for identifiers in subslugs (#87480)
gitea/combo/pipeline/head This commit looks good Details
2024-02-29 10:45:57 +01:00
Corentin Sechet 3a6ebed4db cells: fix wrong display of 'site' field of tracking code cell in backoffice (#86508)
gitea/combo/pipeline/head This commit looks good Details
2024-02-28 13:03:17 +01:00
Benjamin Dauvergne bbb12f507f lingo: pass for-payment on reading invoice for payment (#76853)
gitea/combo/pipeline/head This commit looks good Details
Only if the regie announced its support in its invoices/ endpoint.
2024-02-26 19:28:32 +01:00
Benjamin Dauvergne cb33b47a19 lingo: provision Regie.has_invoice_for_payment during Regie.get_invoices() (#76853) 2024-02-26 19:28:32 +01:00
Benjamin Dauvergne c092e19c77 lingo: add Regie.has_invoice_for_payment boolean field (#76853) 2024-02-26 19:28:32 +01:00
Benjamin Dauvergne 78c036b7a2 lingo: report exception on invoice notification failure (#87025)
gitea/combo/pipeline/head This commit looks good Details
2024-02-24 17:41:04 +01:00
Frédéric Péters 5d80833736 misc: remove hardcoded service slug in card schema lookup (#87328)
gitea/combo/pipeline/head This commit looks good Details
2024-02-23 21:17:05 +01:00
Benjamin Dauvergne 361f0a9bb1 pwa: use setting or tenant URL to build VAPID JWT sub mailto claim (#87413)
gitea/combo/pipeline/head This commit looks good Details
DEFAULT_FROM_EMAIL is usually not read and only Apple accept an http URL
instead of a mailto: URL.
2024-02-23 15:34:55 +01:00
Benjamin Dauvergne 35e6b79120 pwa: never delete subscriptions (#85458)
gitea/combo/pipeline/head This commit looks good Details
Subscriptions must be garbage collected when notifications request
receive the 410 Gone status. Deleting all subscriptions would delete
subcriptions registered from another browser and there is no way to
relate existing subscription to a particular browser.
2024-02-23 11:13:55 +01:00
Benjamin Dauvergne b2d06e0234 pwa: push existing subscription at the beginning of each session (#85458) 2024-02-23 11:13:55 +01:00
Benjamin Dauvergne a5f8140d66 pwa: add Urgency: low header (#70987)
gitea/combo/pipeline/head This commit looks good Details
2024-02-23 11:13:27 +01:00
Benjamin Dauvergne 9f25287a66 pwa: conserve VAPID headers in cache for 12 hours (#70987)
The JSON webtoken is valid for 24 hours but only kept for 23 hours, to
prevent any use after expiration.

Also factorize webpush implementation from signal handling and remove
unused legacy settings support.
2024-02-23 11:13:27 +01:00
Benjamin Dauvergne 05e615ddc9 pwa: set TTL of push notification to 30 days (#70988)
gitea/combo/pipeline/head Build queued... Details
2024-02-22 15:44:40 +01:00
Lauréline Guérin 03361bfbb9 wcs: replace newlines by spaces in title and headers (#86308)
gitea/combo/pipeline/head This commit looks good Details
2024-02-16 10:13:18 +01:00
Yann Weber 1e65e2ea49 data: add true, false & null aliases to context (#82425)
gitea/combo/pipeline/head This commit looks good Details
2024-02-15 15:47:03 +01:00
Thomas Jund c02a001384 wcs: add submit-button class on tracking code input button (#85574)
gitea/combo/pipeline/head This commit looks good Details
2024-02-15 09:43:08 +01:00
Thomas Jund 51d327d72e html: correct bad markup on link-list-cell (#86666)
gitea/combo/pipeline/head This commit looks good Details
2024-02-12 10:06:30 +01:00
Yann Weber ec57e7c060 map: add int conversion before comparing zoom levels (#86631)
gitea/combo/pipeline/head This commit looks good Details
2024-02-07 11:17:05 +01:00
Benjamin Dauvergne 7aff4544fc utils: add missing warning log on network error (#85742)
gitea/combo/pipeline/head This commit looks good Details
2024-02-01 22:47:22 +01:00
Benjamin Dauvergne 253ae3863b datavis: log detailed errors for outdated statistics (#85742) 2024-02-01 22:47:22 +01:00
Benjamin Dauvergne 8a15bacc72 dataviz: do not log failure to update statistics as errors (#85742) 2024-02-01 22:47:22 +01:00
Frédéric Péters 5fb21cd6ec assets: double check for null bytes in filename (#86356)
gitea/combo/pipeline/head This commit looks good Details
2024-02-01 17:19:35 +01:00
Paul Marillonnet e06ea594d6 menu: include parenthood in subentries hint class logic (#86069)
gitea/combo/pipeline/head This commit looks good Details
2024-01-30 14:56:02 +01:00
Frédéric Péters 17e7166841 translation update
gitea/combo/pipeline/head This commit looks good Details
2024-01-30 13:57:03 +01:00
Lauréline Guérin 59103a7db8 maps: force break-word for long url in popup (#86048)
gitea/combo/pipeline/head This commit looks good Details
2024-01-30 13:30:06 +01:00
Lauréline Guérin 32641a04a1 misc: indent combo.map.scss with tabs (#86048) 2024-01-30 13:30:06 +01:00
Lauréline Guérin 97d0fd650e manager: list pages outside applications (#85927)
gitea/combo/pipeline/head This commit looks good Details
2024-01-30 13:29:14 +01:00
Corentin Sechet dd877aad7c js: configure unit tests (#83453)
gitea/combo/pipeline/head This commit looks good Details
2024-01-29 09:34:16 +01:00
Frédéric Péters 9b491b824f translation update
gitea/combo/pipeline/head This commit looks good Details
2024-01-19 09:01:36 +01:00
Valentin Deniaud ee51907385 dataviz: include reference to cell url when getting statistics data (#85815)
gitea/combo/pipeline/head This commit looks good Details
2024-01-18 14:26:05 +01:00
Yann Weber 12a3d5b392 map: add zoom level configuration validation (#64640)
gitea/combo/pipeline/head This commit looks good Details
2024-01-16 17:58:58 +01:00
Benjamin Dauvergne d51abbf3ee wcs: pass django's request to requests_wrapper (#75916)
gitea/combo/pipeline/head This commit looks good Details
To enable the ?nocache behaviour when calling w.c.s.
2024-01-15 16:15:04 +01:00
Benjamin Dauvergne 6fcb6845ba data: implement ?nocache parameter for JSON cells (#75916)
If ?nocache is given in the page query-string and an user is
authenticated, it changes invalidate_cache to True in RequestWrapper.request().

The template placeholder.html is modified to pass the ?nocache parameter
recursively to asynchronously loaded cells if it is active on the page.
2024-01-15 16:15:04 +01:00
Yann Weber 8e4d6aa904 translation update
gitea/combo/pipeline/head This commit looks good Details
2024-01-15 15:02:51 +01:00
Yann Weber 884cf93fae export_import: add check on 'uuid' field when importing page (#84541)
gitea/combo/pipeline/head This commit looks good Details
2024-01-15 14:34:27 +01:00
Yann Weber 3878028866 data: move exceptions class from data module in exceptions.py (#84541) 2024-01-15 14:34:27 +01:00
Yann Weber 43a6bb142d commands: rename lingo-poll-backend to lingo_poll_backend (#85511)
gitea/combo/pipeline/head This commit looks good Details
2024-01-15 11:51:09 +01:00
Frédéric Péters dab4a2ae1b misc: only init card cell custom schema form once (#85623)
gitea/combo/pipeline/head This commit looks good Details
2024-01-15 11:34:43 +01:00
Valentin Deniaud 34ed582d50 wcs: set card filters form prefix regardless of fields (#85223)
gitea/combo/pipeline/head This commit looks good Details
2024-01-15 11:30:21 +01:00
Lauréline Guérin 313660c702 export_import: raise 404 if page is not found (#85191)
gitea/combo/pipeline/head This commit looks good Details
2024-01-15 08:34:30 +01:00
Frédéric Péters a2ae3a5860 misc: add a nameid property to users (#85345)
gitea/combo/pipeline/head This commit looks good Details
2024-01-12 14:28:48 +01:00
Frédéric Péters 1d22ba93b0 general: increase size of extra_css_class field (#85419)
gitea/combo/pipeline/head This commit looks good Details
2024-01-09 10:55:28 +01:00
Lauréline Guérin 7a362a0193
wcs: card cell, fix migration for list display mode (#85368)
gitea/combo/pipeline/head This commit looks good Details
2024-01-08 11:58:16 +01:00
Frédéric Péters 77f5ea9ac4 translation update
gitea/combo/pipeline/head This commit looks good Details
2023-12-22 11:50:59 +01:00
Lauréline Guérin 335670c2f1
export_import: add roles with minor=True (#85022)
gitea/combo/pipeline/head This commit looks good Details
2023-12-21 14:59:01 +01:00
Lauréline Guérin fe7771f0eb
export_import: make endpoints generic for other kinds of objects (#85022) 2023-12-21 14:51:57 +01:00
Thomas Jund b02801ebc8
wcs: add specific templates for display mode list (#79989)
gitea/combo/pipeline/head This commit looks good Details
2023-12-19 14:36:43 +01:00
Thomas Jund cd88c9e309
wcs: stock schemas for all display modes separately (#79989) 2023-12-19 14:36:43 +01:00
Thomas Jund abda013cc3
wcs: create only one instance of Card_cell_custom for each card cell (#79989) 2023-12-19 14:36:43 +01:00
Lauréline Guérin e9bc442738
wcs: add a migration for cards in with list mode (#79989) 2023-12-19 14:36:43 +01:00
Lauréline Guérin 82698fcee9
wcs: add a list mode for cards cell (#79989) 2023-12-19 14:36:43 +01:00
Lauréline Guérin b4a12a1825
wcs: rename cards.html template (#79989) 2023-12-19 14:36:42 +01:00
Lauréline Guérin d2c5c4fa17
wcs: split template (#79989) 2023-12-19 14:36:42 +01:00
Frédéric Péters 20a37687d6 misc: make card linked to page update page title (#74073)
gitea/combo/pipeline/head This commit looks good Details
2023-12-18 13:20:06 +01:00
Valentin Deniaud 3cb658b30e settings: add lingo as statistics provider (#83886)
gitea/combo/pipeline/head This commit looks good Details
2023-12-18 10:04:55 +01:00
Nicolas Roche f44ab84e46 misc: remove copyright line from footer (#84812)
gitea/combo/pipeline/head This commit looks good Details
2023-12-15 17:49:04 +01:00
Lauréline Guérin 6897b15961
translation update
gitea/combo/pipeline/head This commit looks good Details
2023-12-05 10:43:29 +01:00
Lauréline Guérin 9c2dac3060
lingo: new cell for credits (#83908)
gitea/combo/pipeline/head This commit looks good Details
2023-12-04 18:16:57 +01:00
Lauréline Guérin dd9497e614 wcs: don't populate card cell context when looking for placeholders (#83797)
gitea/combo/pipeline/head This commit looks good Details
2023-11-24 08:39:53 +01:00
Frédéric Péters b535376efe misc: declare new importlib_metadata dependency, for pygal (#83745)
gitea/combo/pipeline/head This commit looks good Details
2023-11-21 10:50:53 +01:00
Frédéric Péters e697ef2b6d translation update
gitea/combo/pipeline/head There was a failure building this commit Details
2023-11-14 10:55:23 +01:00
Valentin Deniaud 3eb771ddbd dataviz: allow exporting graph to SVG or ODS (#65947)
gitea/combo/pipeline/head This commit looks good Details
2023-11-14 10:33:12 +01:00
Valentin Deniaud 78f08c8267 dataviz: add is_table_chart method (#65947) 2023-11-14 10:32:25 +01:00
Lauréline Guérin 6ee25f340e wcs: fix loading of card cell with filtering and pagination (#82583)
gitea/combo/pipeline/head This commit looks good Details
2023-11-14 10:29:00 +01:00
Thomas NOËL 76e0a432a6 debian: add back memory-report to uwsgi default configuration (#80451)
gitea/combo/pipeline/head This commit looks good Details
2023-11-13 11:29:22 +01:00
Frédéric Péters 18eeb74ca7 translation update
gitea/combo/pipeline/head This commit looks good Details
2023-11-10 19:26:22 +01:00
Frédéric Péters 62df2add65 wcs: redirect tracking code search engine results to backoffice (#83320)
gitea/combo/pipeline/head This commit looks good Details
2023-11-10 10:52:43 +01:00
Frédéric Péters d1321676f3 misc: add support for extra variables to skeleton pages (#83003)
gitea/combo/pipeline/head This commit looks good Details
2023-11-10 08:56:45 +01:00
Lauréline Guérin 5b09a4b15a wcs: set invalid_reason_codes for some cells (#83110)
gitea/combo/pipeline/head This commit looks good Details
2023-11-10 08:35:30 +01:00
Frédéric Péters 719c810dff wcs: only propose backoffice submission engine on agent portal (#83158)
gitea/combo/pipeline/head This commit looks good Details
2023-11-06 15:08:51 +01:00
Frédéric Péters 1c631a36da misc: rename data-autocomplete to avoid conflicts (#83122)
gitea/combo/pipeline/head This commit looks good Details
2023-11-04 17:54:03 +01:00
Lauréline Guérin 8bb8e53eec
wcs: set cell as invalid if wcs_site of reference is not found (#83106)
gitea/combo/pipeline/head This commit looks good Details
2023-11-03 14:18:31 +01:00
Lauréline Guérin a5474de140
export_import: don't check dependencies of invalid cells (#83106) 2023-11-03 14:18:30 +01:00
Frédéric Péters 5a88b7efdc notifications: allow null/empty content (#83002)
gitea/combo/pipeline/head This commit looks good Details
2023-10-31 16:54:17 +01:00
Thomas NOËL bf65795e9e debian: add uwsgi/combo SyslogIdentifier in service (#82956)
gitea/combo/pipeline/head This commit looks good Details
2023-10-31 10:05:06 +01:00
Emmanuel Cazenave e38e7eadfa setup: compute pep440 compliant dirty version number (#81731)
gitea/combo/pipeline/head This commit looks good Details
2023-10-30 17:18:01 +01:00
Frédéric Péters 35fa2fef6a lingo: fix typo in error message of poll backend command (#82923)
gitea/combo/pipeline/head This commit looks good Details
2023-10-29 10:25:07 +01:00
Frédéric Péters e92a1a72a2 translation update
gitea/combo/pipeline/head This commit looks good Details
2023-10-27 16:52:38 +02:00
Lauréline Guérin e6f3a2bdda applification: check for legacy elements in bundle-check endpoint (#82492)
gitea/combo/pipeline/head This commit looks good Details
2023-10-27 16:23:59 +02:00
Frédéric Péters 32b2bb43e9 wcs: ignore cards with unknown status when filtering on status (#82908)
gitea/combo/pipeline/head This commit looks good Details
2023-10-27 16:15:07 +02:00
Lauréline Guérin 93dff30566 export_import: add uuid for role in dependencies view (#82763)
gitea/combo/pipeline/head This commit looks good Details
2023-10-27 15:48:30 +02:00
Lauréline Guérin e320c819d3 wcs - basic-rich field display in card cell (#82687)
gitea/combo/pipeline/head This commit looks good Details
2023-10-27 09:29:11 +02:00
Frédéric Péters 06ddfb6b7a search: add an engine with backoffice submission forms as hits (#81533)
gitea/combo/pipeline/head This commit looks good Details
2023-10-27 08:40:19 +02:00
Frédéric Péters 914124a66c settings: set x-frame-options to sameorigin, for PWA preview mode (#82152)
gitea/combo/pipeline/head This commit looks good Details
2023-10-27 08:40:00 +02:00
Lauréline Guérin 0f02832df5
applification: fix unlink url (#82455)
gitea/combo/pipeline/head This commit looks good Details
2023-10-17 09:28:59 +02:00
Frédéric Péters d8fcad3ed6 translation update
gitea/combo/pipeline/head This commit looks good Details
2023-10-07 08:12:29 +02:00
Lauréline Guérin 47fdcc2cd6
manager: get snapshots to compare from application version (#82082)
gitea/combo/pipeline/head This commit looks good Details
2023-10-06 14:46:17 +02:00
Lauréline Guérin a06c667356
export_import: redirect to snapshot compare view if compare in GET params (#82082) 2023-10-06 14:46:17 +02:00
Lauréline Guérin 5ce17606c4
manager: display application version in history and compare views (#82082) 2023-10-06 14:46:17 +02:00
Lauréline Guérin 96ca2ec851
export_import: bundle-check endpoint (#82080)
gitea/combo/pipeline/head Build queued... Details
2023-10-06 14:45:13 +02:00
Lauréline Guérin 77c4b473b1
export_import: set application slug/version on snapshots (#82080) 2023-10-06 14:45:13 +02:00
Lauréline Guérin 5287443cbf
export_import: bundle-unlink endpoint (#82085)
gitea/combo/pipeline/head This commit looks good Details
2023-10-06 14:18:50 +02:00
Lauréline Guérin 105cd97ac3
manager: display applications of the page in sidebar (#82081)
gitea/combo/pipeline/head Build queued... Details
2023-10-06 14:18:33 +02:00
Lauréline Guérin 3cbae76331
manager: display applications on page list, filter by app (#82081) 2023-10-06 14:18:33 +02:00
Lauréline Guérin d519781b74
export_import: create application and elements on install and declare (#81927)
gitea/combo/pipeline/head This commit looks good Details
2023-10-06 14:18:12 +02:00
Lauréline Guérin cd99705f8c
export_import: add models for Application (#81927) 2023-10-06 14:18:12 +02:00
Lauréline Guérin b916c483f8 manager: home page, move boutons in sidebar (#82027)
gitea/combo/pipeline/head This commit looks good Details
2023-10-06 14:16:01 +02:00
Frédéric Péters bf4cbfe37a translation update
gitea/combo/pipeline/head This commit looks good Details
2023-10-05 22:15:36 +02:00
Frédéric Péters 65f8efc1f0 misc: increase page slug length (#81935)
gitea/combo/pipeline/head This commit looks good Details
2023-10-05 22:06:56 +02:00
Frédéric Péters c5f1ffb36e tox: keep on testing drf 3.12 only for now (#81947)
gitea/combo/pipeline/head This commit looks good Details
2023-10-05 09:55:52 +02:00
Frédéric Péters 0198eb2a9a setup: allow djangorestframework 3.14 (#81947)
gitea/combo/pipeline/head This commit looks good Details
2023-10-03 16:51:35 +02:00
Frédéric Péters f5ff197858 api: add module with applification API (#60773)
gitea/combo/pipeline/head This commit looks good Details
2023-10-03 13:11:29 +02:00
Frédéric Péters 2d8bf3a1aa ci: keep on using pylint 2 while pylint-django is not ready (#81905)
gitea/combo/pipeline/head This commit looks good Details
2023-10-03 06:35:12 +02:00
Valentin Deniaud b7017baf57 wcs: preserve ordering of static item field values in card filters (#81289)
gitea/combo/pipeline/head This commit looks good Details
2023-09-25 11:56:14 +02:00
Lauréline Guérin 7f35da936a
wcs: refresh cached fields for form links in list of links (#81157)
gitea/combo/pipeline/head This commit looks good Details
2023-09-19 16:23:19 +02:00
Frédéric Péters 22da07f739 translation update (for djangojs) (#81345)
gitea/combo/pipeline/head This commit looks good Details
2023-09-19 14:18:45 +02:00
Frédéric Péters 8c0d0dbf43 translation update
gitea/combo/pipeline/head This commit looks good Details
2023-09-19 10:14:01 +02:00
Lauréline Guérin 98e74b6da0 manager: fix display of invalid message for little screens (#79222)
gitea/combo/pipeline/head This commit looks good Details
2023-09-19 09:10:14 +02:00
Lauréline Guérin 6dda05741f wcs: set invalid_reason_codes for draft cell (#79222) 2023-09-19 09:10:14 +02:00
Lauréline Guérin c5b835d464 manager: add a snapshot inspect view (#81031) 2023-09-19 09:09:54 +02:00
Lauréline Guérin 4f2c606cd1 manager: view snapshot manage page (#81031) 2023-09-19 09:09:54 +02:00
Lauréline Guérin ef0dac26e1 manager: don't paginate history page (#81019)
gitea/combo/pipeline/head Build queued... Details
2023-09-19 09:09:24 +02:00
Lauréline Guérin c0c93aa639 wcs: add page variables in custom_title context (#80805)
gitea/combo/pipeline/head This commit looks good Details
2023-09-19 09:08:44 +02:00
Valentin Deniaud 4f129777cf dataviz: ignore hidden chart cells when building filters (#81148)
gitea/combo/pipeline/head This commit looks good Details
2023-09-18 16:41:09 +02:00
Valentin Deniaud e3832178ee fix quote style error (#79804)
gitea/combo/pipeline/head This commit looks good Details
2023-09-18 15:40:54 +02:00
Valentin Deniaud da721b70f2 import: mention cell in error message if related page not found (#79804)
gitea/combo/pipeline/head There was a failure building this commit Details
2023-09-18 15:33:29 +02:00
Lauréline Guérin 49f81f55a1
wcs: dont wait for cache if synchronous call (#80844)
gitea/combo/pipeline/head This commit looks good Details
2023-09-05 15:26:16 +02:00
Nicolas Roche b8f86ae74c commands: add an only-assets parameter to export command (#50399)
gitea/combo/pipeline/head This commit looks good Details
2023-08-25 14:30:18 +02:00
Valentin Deniaud d015ae9330 misc: update git-blame-ignore-revs to ignore quote changes (#79788)
gitea/combo/pipeline/head This commit looks good Details
2023-08-16 10:07:28 +02:00
Valentin Deniaud 9d8876e155 misc: apply double-quote-string-fixer (#79788) 2023-08-16 10:07:28 +02:00
Valentin Deniaud 029f77cb38 misc: add pre commit hook to force single quotes (#79788) 2023-08-16 10:07:27 +02:00
Frédéric Péters 595deb68c0 translation update
gitea/combo/pipeline/head This commit looks good Details
2023-08-14 10:11:14 +02:00
Lauréline Guérin e10e5c976d manager: compare snapshot serializations (#80302)
gitea/combo/pipeline/head This commit looks good Details
2023-08-14 10:06:53 +02:00
Frédéric Péters 1caba79bc0 build: add pyquery to setup.py (#80301)
gitea/combo/pipeline/head This commit looks good Details
2023-08-11 19:11:44 +02:00
Frédéric Péters 0b680c6f9a debian: add dependency on python3-pyquery (#80301)
gitea/combo/pipeline/head This commit looks good Details
2023-08-11 13:55:40 +02:00
Frédéric Péters cf19c2873a translation update
gitea/combo/pipeline/head This commit looks good Details
2023-08-11 13:27:32 +02:00
Lauréline Guérin 6df0b36201
manager: compare snapshots (#80301)
gitea/combo/pipeline/head This commit looks good Details
2023-08-11 09:52:34 +02:00
Lauréline Guérin e87fbf829b manager: add inspect page (#80300)
gitea/combo/pipeline/head This commit looks good Details
2023-08-11 09:52:14 +02:00
Frédéric Péters fa81a01280 misc: add possibility for COMBO_CELL_TEMPLATES to provide context vars (#80260)
gitea/combo/pipeline/head This commit looks good Details
2023-08-11 00:29:13 +02:00
Frédéric Péters 65a5cca168 translation update
gitea/combo/pipeline/head This commit looks good Details
2023-08-04 12:04:58 +02:00
Lauréline Guérin 9706f93ac5
lingo: add a link to download payments certificate (#79939)
gitea/combo/pipeline/head This commit looks good Details
2023-08-04 11:50:44 +02:00
Lauréline Guérin 267af67dd2 lingo: add a PaymentsCell (#79841)
gitea/combo/pipeline/head This commit looks good Details
2023-08-04 11:50:23 +02:00
Frédéric Péters f8aae42a9f general: move locales to a combo.locales application (#80216)
gitea/combo/pipeline/head This commit looks good Details
2023-08-04 10:32:15 +02:00
Frédéric Péters 75a86cf07a general: add a timestamp to static URLs, to avoid caching issues (#80225)
gitea/combo/pipeline/head This commit looks good Details
2023-08-03 09:14:40 +02:00
Lauréline Guérin 0e0bec4263
translation update
gitea/combo/pipeline/head This commit looks good Details
2023-08-02 12:02:42 +02:00
Lauréline Guérin 2d14843b8a
wcs: add card information fields to selection (#79486)
gitea/combo/pipeline/head This commit looks good Details
2023-08-02 11:39:09 +02:00
Lauréline Guérin 55233a2c9d
wcs: card schema customization, separate card and user fields (#79988)
gitea/combo/pipeline/head This commit looks good Details
2023-08-02 11:32:30 +02:00
Thomas Jund a8e7c1a768 wcs: add lazy loading attribut on card image (#79732)
gitea/combo/pipeline/head This commit looks good Details
2023-08-02 11:31:35 +02:00
Thomas Jund aed838e4f4 wcs: use template instead script tag (#79732) 2023-08-02 11:31:35 +02:00
Thomas Jund bbdfaa3141 wcs: remove custom_cell event (#79732) 2023-08-02 11:31:35 +02:00
Thomas Jund 55cde087e8 wcs: simplify grid_cell__set_schema (#79732) 2023-08-02 11:31:35 +02:00
Thomas Jund 25cc96aa63 wcs: simplify grid_cell__edit_set_fields (#79732) 2023-08-02 11:31:35 +02:00
Thomas Jund 09e0895ae2 wcs: remove some old JS this = _self (#79732) 2023-08-02 11:31:35 +02:00
Thomas Jund aa4f6d14e7 wcs: remove grid_cell__init_form_fields (#79732) 2023-08-02 11:31:35 +02:00
Paul Marillonnet 215c5873a2 tox: add allowlist_externals for v4 compatibility (#79862)
gitea/combo/pipeline/head Build queued... Details
2023-08-02 11:31:07 +02:00
Lauréline Guérin 1b68eab919 wcs: filter care forms by card_id in page url (#80123)
gitea/combo/pipeline/head This commit looks good Details
2023-08-02 11:29:27 +02:00
Thomas NOËL 4aa324459b misc: add user|user_id_for_service template filter (#80008)
gitea/combo/pipeline/head This commit looks good Details
2023-07-31 17:24:52 +02:00
Lauréline Guérin c555efa793
wcs: card relations, look also into workflow fields (#79912)
gitea/combo/pipeline/head This commit looks good Details
2023-07-25 09:46:10 +02:00
Corentin Sechet 903c856ff9 wcs: hide page fields in card custom display form (#78629)
gitea/combo/pipeline/head This commit looks good Details
2023-07-17 17:17:39 +02:00
Lauréline Guérin f60920d678
misc: load only cells concerned by the pagination (#79501)
gitea/combo/pipeline/head This commit looks good Details
2023-07-15 14:38:32 +02:00
Lauréline Guérin e527cd42b3
wcs: delay cell loading if url was called and not yet in cache (#79501) 2023-07-15 14:38:32 +02:00
Lauréline Guérin c02de3e029
misc: load first page of paginated cards only once (#79501) 2023-07-15 14:38:32 +02:00
Lauréline Guérin 789b78e757
utils: add some methods to Request wrapper (#79501) 2023-07-15 14:38:32 +02:00
Lauréline Guérin 6691eb9ba6
wcs: never call wcs on page rendering for card cell (#79501) 2023-07-15 14:38:32 +02:00
Valentin Deniaud df0ea34d35 wcs: allow multiple card filters form (#79538)
gitea/combo/pipeline/head This commit looks good Details
2023-07-10 17:04:41 +02:00
Lauréline Guérin 397ab99d47
wcs: fix card cell pagination on a page with many card cells (#79570)
gitea/combo/pipeline/head This commit looks good Details
2023-07-10 12:14:48 +02:00
Lauréline Guérin 29bc54c9d1 wcs: add data attributes for filters on deferred cells (#79490)
gitea/combo/pipeline/head This commit looks good Details
2023-07-07 11:46:41 +02:00
Lauréline Guérin ff316ca7ca
translation update
gitea/combo/pipeline/head This commit looks good Details
2023-07-07 11:03:10 +02:00
Lauréline Guérin 2b81848571 lingo: display remaining amount in invoices cell (#79139)
gitea/combo/pipeline/head This commit looks good Details
2023-07-07 10:59:26 +02:00
Frédéric Péters 22b01c498c misc: delay load of paginated cards until their page is displayed (#79377)
gitea/combo/pipeline/head This commit looks good Details
2023-07-04 16:30:17 +02:00
Frédéric Péters ca883eaace misc: move javascript pagination code to be run before loading cells (#79377) 2023-07-04 16:30:17 +02:00
Frédéric Péters 7264911148 general: fix pagination button close tags (#79369)
gitea/combo/pipeline/head This commit looks good Details
2023-07-04 16:03:12 +02:00
Valentin Deniaud e1f85a130f dataviz: handle numbers in pie chart labels (#79339)
gitea/combo/pipeline/head This commit looks good Details
2023-07-04 11:00:34 +02:00
Valentin Deniaud 8a59cc55b6 dataviz: handle numbers in labels alphabetical sort (#79339) 2023-07-03 18:26:06 +02:00
Valentin Deniaud 03a8f3bd9b wcs: disable category cell (#72926)
gitea/combo/pipeline/head There was a failure building this commit Details
2023-07-03 10:05:36 +02:00
Frédéric Péters f974cfae76 translation update
gitea/combo/pipeline/head This commit looks good Details
2023-06-30 15:51:17 +02:00
Lauréline Guérin 59c11b79a5
wcs: add card_id in get param for forms of category links (#58850)
gitea/combo/pipeline/head This commit looks good Details
2023-06-30 11:03:42 +02:00
Lauréline Guérin 8a2ac3c080 wcs: filter user forms by card_id in page url (#58854)
gitea/combo/pipeline/head This commit looks good Details
2023-06-30 11:01:13 +02:00
Lauréline Guérin 1bbe119901 misc: fix old migrations (#58854) 2023-06-30 11:01:13 +02:00
Frédéric Péters e2247a59a1 translation update
gitea/combo/pipeline/head This commit looks good Details
2023-06-29 08:03:41 +02:00
Lauréline Guérin 4b366d9a5f
family: mark event as full (#79098)
gitea/combo/pipeline/head This commit looks good Details
2023-06-27 15:58:24 +02:00
Frédéric Péters 4d3144e721 translation update (french orthography rectifications of 1990)
gitea/combo/pipeline/head This commit looks good Details
2023-06-25 09:48:01 +02:00
Frédéric Péters 8a0a2e0ed3 ci: build deb package for bookworm (#78968)
gitea/combo/pipeline/head This commit looks good Details
2023-06-23 16:35:10 +02:00
Lauréline Guérin d67db9fb89 data: set JsonCell.log_errors to False (#78943)
gitea/combo/pipeline/head This commit looks good Details
2023-06-23 11:38:53 +02:00
Frédéric Péters c22d32cb95 translation update
gitea/combo/pipeline/head This commit looks good Details
2023-06-23 10:40:57 +02:00
Frédéric Péters 6abc867f72 a11y: add "required authentication" info to link titles (#64250)
gitea/combo/pipeline/head This commit looks good Details
2023-06-23 10:40:15 +02:00
Frédéric Péters 7bcfeaefe8 translation update
gitea/combo/pipeline/head This commit looks good Details
2023-06-23 10:18:32 +02:00
Frédéric Péters 16e652fcfd maps: add extra marker icons (#57514)
gitea/combo/pipeline/head This commit looks good Details
2023-06-23 08:07:38 +02:00
Frédéric Péters 26fa415841 assets: add clear indication an archive file is expected (#73606)
gitea/combo/pipeline/head Build queued... Details
2023-06-19 18:51:19 +02:00
Valentin Deniaud e0df321177 translation update
gitea/combo/pipeline/head This commit looks good Details
2023-06-15 14:13:19 +02:00
Valentin Deniaud 07c810a6cc wcs: allow displaying card cell filters on the same line (#78473)
gitea/combo/pipeline/head This commit looks good Details
2023-06-15 11:02:17 +02:00
Frédéric Péters 22342eee1f wcs: allow filtering cards on varnames with uppercase characters (#78328)
gitea/combo/pipeline/head This commit looks good Details
2023-06-12 14:38:57 +02:00
Lauréline Guérin 406d6186e6 wcs: add bo item fields to card cell available filters (#78136)
gitea/combo/pipeline/head This commit looks good Details
2023-06-12 09:54:26 +02:00
Frédéric Péters bcff3a2ddd wcs: do not include nonce in signed URL to wcs file (#78159)
gitea/combo/pipeline/head This commit looks good Details
2023-06-05 10:23:36 +02:00
Frédéric Péters 091bf40317 a11y: give a unique id to tracking input (#78106)
gitea/combo/pipeline/head This commit looks good Details
2023-06-01 19:41:33 +02:00
Valentin Deniaud cc8fd003cb wcs: add status to card cell available filters (#78065)
gitea/combo/pipeline/head This commit looks good Details
2023-06-01 16:01:17 +02:00
Valentin Deniaud 221af86bd9 wcs: escape option ids in card cell filters (#78004)
gitea/combo/pipeline/head This commit looks good Details
2023-05-30 16:10:53 +02:00
Thomas Jund c85578186c weekly_agenda: improve styles and readability (#77935)
gitea/combo/pipeline/head This commit looks good Details
2023-05-30 15:41:29 +02:00
Frédéric Péters a59571e272 misc: use accept-language for skeleton pages (#77973)
gitea/combo/pipeline/head This commit looks good Details
2023-05-28 11:04:06 +02:00
Frédéric Péters a752ff32f0 debian: apply new pre-commit-debian (#77727)
gitea/combo/pipeline/head This commit looks good Details
2023-05-27 21:43:10 +02:00
Frédéric Péters 63de87e7c7 ci: upgrade pre-commit-debian (#77727) 2023-05-27 21:43:10 +02:00
Frédéric Péters 759e18c70c misc: fallback skeleton to standard page template (#46412)
gitea/combo/pipeline/head This commit looks good Details
2023-05-26 17:22:42 +02:00
Frédéric Péters 95c0377933 translation update
gitea/combo/pipeline/head This commit looks good Details
2023-05-26 08:27:16 +02:00
Frédéric Péters 3d0cabd421 a11y: add aria labels to pagination buttons (#40929)
gitea/combo/pipeline/head This commit looks good Details
2023-05-26 07:39:56 +02:00
Frédéric Péters 2372ec6d6e general: remove django.contrib.admin usage (#62339) 2023-05-26 07:39:43 +02:00
Frédéric Péters 7cad831160 debian: move cron jobs to uwsgi (#74597) 2023-05-26 07:39:35 +02:00
Frédéric Péters 05ee2428dd misc: fallback to standard page template in case of missing template (#77741)
gitea/combo/pipeline/head This commit looks good Details
2023-05-26 07:39:27 +02:00
Frédéric Péters 951324bb41 pwa: do not include pwa menu entry on portal agent (#77864)
gitea/combo/pipeline/head Build queued... Details
2023-05-23 20:53:58 +02:00
Lauréline Guérin c36cef9e18
translation update
gitea/combo/pipeline/head This commit looks good Details
2023-05-22 16:23:14 +02:00
Lauréline Guérin 2aa621255d
family: fix wording for empty day (#77787)
gitea/combo/pipeline/head This commit looks good Details
2023-05-22 15:55:30 +02:00
Lauréline Guérin b2277066c1
lingo: fix migration (#77745)
gitea/combo/pipeline/head This commit looks good Details
2023-05-19 10:27:54 +02:00
Lauréline Guérin e870727e55
translation update
gitea/combo/pipeline/head This commit looks good Details
2023-05-18 10:50:06 +02:00
Lauréline Guérin 36f1e827ec
lingo: add a migration for snapshots with old invoices cells (#76948)
gitea/combo/pipeline/head This commit looks good Details
2023-05-18 10:35:39 +02:00
Lauréline Guérin 6c73e2246e
lingo: add new fields on InvoicesCell model (#76948) 2023-05-18 10:35:39 +02:00
Lauréline Guérin 212fe481ff
lingo: add cell reference in invoice urls (#76948) 2023-05-18 10:35:39 +02:00
Lauréline Guérin 2113014b1c
lingo: remove old invoices cells (#76948) 2023-05-18 10:35:39 +02:00
Lauréline Guérin f18bf88de2
data: remove old data migrations and their tests (#76948)
because with the removal off old invoice cells, the tests are not
running:

failed on teardown with "django.core.management.base.CommandError:
Database test_combo-test-wip-76948-lingo-new-invoice-cell_gw3
couldn't be flushed. Possible reasons:
  * The database isn't running or isn't configured correctly.
  * At least one of the expected database tables doesn't exist.
  * The SQL was invalid.
Hint: Look at the output of 'django-admin sqlflush'. That's the SQL this
command wasn't able to run."

E               psycopg2.errors.FeatureNotSupported: ERREUR:  ne peut pas
tronquer une table référencée dans une contrainte de clé étrangère
E               DETAIL:  La table « lingo_itemshistory_groups » référence
« auth_group ».
E               HINT:  Tronquez la table « lingo_itemshistory_groups » en
même temps, ou utilisez TRUNCATE ... CASCADE.
2023-05-18 10:35:39 +02:00
Lauréline Guérin b40ae94a6f
lingo: migration for invoices cells (#76948) 2023-05-18 10:35:39 +02:00
Lauréline Guérin 16ecc28cc7
lingo: add new invoices cell (#76948) 2023-05-18 10:35:39 +02:00
Valentin Deniaud 3a277ca561 wcs: hide required hint on card cell filters (#77711)
gitea/combo/pipeline/head This commit looks good Details
2023-05-17 15:52:48 +02:00
Valentin Deniaud 4b3809d76e translation update
gitea/combo/pipeline/head This commit looks good Details
2023-05-16 17:25:44 +02:00
Valentin Deniaud 5a4372ba2e wcs: allows filters in card cell when cards mode (#75920)
gitea/combo/pipeline/head This commit looks good Details
2023-05-16 17:24:41 +02:00
Valentin Deniaud 91f16c65f3 wcs: allow filters in card cell when table mode (#75920) 2023-05-16 17:24:41 +02:00
Valentin Deniaud 891d1543b7 data: allow static choices in select2 widgets (#75920) 2023-05-16 17:24:41 +02:00
Valentin Deniaud efb48cc299 data: always use select2 for multi select widgets (#75920) 2023-05-16 17:24:41 +02:00
Frédéric Péters 312f1297ea misc: consider conditions when creating skeleton (#77553)
gitea/combo/pipeline/head This commit looks good Details
2023-05-15 22:20:07 +02:00
Valentin Deniaud 0efb99b3d8 misc: remove EnsureJsonbType migration operation (#77468)
gitea/combo/pipeline/head This commit looks good Details
2023-05-11 11:38:33 +02:00
Frédéric Péters 88f15a5324 misc: do not include private cells in skeleton page (#77347)
gitea/combo/pipeline/head This commit looks good Details
2023-05-06 20:15:46 +02:00
Frédéric Péters 3af0cfc1d0 translation update
gitea/combo/pipeline/head This commit looks good Details
2023-05-05 11:59:22 +02:00
Frédéric Péters 907394f048 trivial: apply s/groups/roles/ to leftover migrations (#77310) 2023-05-05 11:59:22 +02:00
Frédéric Péters ff38af3e82 trivial: also change "Groups" verbose name for cells (#77310) 2023-05-05 11:57:35 +02:00
Frédéric Péters a5fd1b452e misc: use "Roles:" as visible label (#77310)
gitea/combo/pipeline/head This commit looks good Details
2023-05-05 11:55:19 +02:00
Frédéric Péters 4c9f77ea4c misc: allow ckeditor to use frame for upload (#75614)
gitea/combo/pipeline/head Build queued... Details
2023-05-05 08:06:00 +02:00
Thomas NOËL 60315bf25e profile: use tel: and mailto: URL schema in search template (#76417)
gitea/combo/pipeline/head This commit looks good Details
2023-05-04 08:00:49 +02:00
Benjamin Dauvergne 1720b9a0ca translation update
gitea/combo/pipeline/head This commit looks good Details
2023-05-03 10:13:29 +02:00
Serghei Mihai 3cfc9bb8b2 calendar: delete app (#77207)
gitea/combo/pipeline/head This commit looks good Details
2023-05-03 09:30:32 +02:00
Thomas NOËL 9cdc479ebd profile: use tel: and mailto: URL schema in profile cell (#76418)
gitea/combo/pipeline/head This commit looks good Details
2023-05-03 09:28:42 +02:00
Valentin Deniaud 5e105e20f1 manager: use select2 widget for roles in visibility form (#76060)
gitea/combo/pipeline/head This commit looks good Details
2023-05-02 16:36:02 +02:00
Pierre Ducroquet 4b7470104f lingo: change remote_items fields to ArrayField (#76949)
gitea/combo/pipeline/head This commit looks good Details
2023-05-02 08:57:43 +02:00
Lauréline Guérin 26b52bbe23
misc: remove some templatefilters (#77138)
gitea/combo/pipeline/head This commit looks good Details
removeprefix & removesuffix are now in publik-django-templatetags
2023-04-29 12:13:12 +02:00
Frédéric Péters d85f3fe2cb dashboard: ignore newly created tiles (#76494)
gitea/combo/pipeline/head This commit looks good Details
2023-04-27 21:20:55 +02:00
Frédéric Péters b204bd2f5b manager: init select2 in dialogs (#76940)
gitea/combo/pipeline/head This commit looks good Details
2023-04-24 15:05:55 +02:00
Frédéric Péters d4d17eb9e4 misc: do not init multiselectwidget if relevant js is not loaded (#76847)
gitea/combo/pipeline/head This commit looks good Details
2023-04-20 19:47:01 +02:00
Valentin Deniaud de36c6fcd1 data: add select2 widget for page selection in link list cell (#76059)
gitea/combo/pipeline/head This commit looks good Details
2023-04-20 09:48:47 +02:00
Paul Marillonnet 705c8ae97a exclude underscore-prefixed groups from visibility settings (#76742)
gitea/combo/pipeline/head This commit looks good Details
2023-04-20 08:18:19 +02:00
Valentin Deniaud 7cb952ffc6 search: do not try to parse results in case of error (#76022)
gitea/combo/pipeline/head This commit looks good Details
2023-04-17 13:40:35 +02:00
Valentin Deniaud 4d17705f35 dataviz: hide y label for one dimension dot chart type (#73686)
gitea/combo/pipeline/head This commit looks good Details
2023-04-17 11:48:34 +02:00
Lauréline Guérin 6e68eee858
dataviz: use MultiSelectWidget from gadjo (#75656)
gitea/combo/pipeline/head This commit looks good Details
2023-04-14 09:38:39 +02:00
Valentin Deniaud eb91d694db dataviz: fix time range field cleaning when templated range (#76553)
gitea/combo/pipeline/head This commit looks good Details
2023-04-12 12:27:46 +02:00
Valentin Deniaud a039cb415d dataviz: move time range fields display logic from js to form (#76044)
gitea/combo/pipeline/head This commit looks good Details
2023-04-03 10:15:33 +02:00
Frédéric Péters 44748a5054 manager: do not load dashboard inner cells (#75999)
gitea/combo/pipeline/head This commit looks good Details
2023-03-30 20:08:36 +02:00
Valentin Deniaud 4efee15d86 dataviz: ignore validation errors in chart filters cell (#76036)
gitea/combo/pipeline/head This commit looks good Details
2023-03-30 16:35:41 +02:00
Valentin Deniaud b355ff74f4 dataviz: handle null duration value (#75568)
gitea/combo/pipeline/head This commit looks good Details
2023-03-30 11:15:03 +02:00
Valentin Deniaud 5b9e878fef misc: fix Django 3.2 default auto field warning (#75442)
gitea/combo/pipeline/head This commit looks good Details
2023-03-29 16:28:39 +02:00
Valentin Deniaud d7800d35e6 misc: bump djhtml version (#75442) 2023-03-29 16:28:39 +02:00
Valentin Deniaud 082ed60520 misc: bump black version (#75442) 2023-03-29 16:28:39 +02:00
Valentin Deniaud dd7b0235fb misc: change pyupgrade target version to 3.9 (#75442) 2023-03-29 16:14:41 +02:00
Valentin Deniaud 97a584de82 misc: move AppConfig subclasses from init to apps.py (#75442) 2023-03-29 16:14:41 +02:00
Valentin Deniaud 66b12b846a misc: change django-upgrade target version to 3.2 (#75442) 2023-03-29 16:14:41 +02:00
Valentin Deniaud a6b7634787 misc: require django 3.2 (#75442) 2023-03-29 16:14:41 +02:00
Lauréline Guérin 533a0d0919 wcs: fix WcsFormCell with too long form title (#75192)
gitea/combo/pipeline/head This commit looks good Details
2023-03-29 16:13:09 +02:00
Serghei Mihai 5f63dbbd3d dataviz: adapt chart's height to content (#62282)
gitea/combo/pipeline/head This commit looks good Details
2023-03-27 11:33:57 +02:00
Serghei Mihai 78bbd7718b dataviz: separe chart configuration by type (#62282) 2023-03-27 11:33:49 +02:00
Serghei Mihai a6a6b8c54c dataviz: fallback to cell's height while configurating chart (#62282) 2023-03-27 11:33:38 +02:00
Serghei Mihai b312c2fd11 dataviz: move method for chart configuration (#62282) 2023-03-27 11:33:18 +02:00
Valentin Deniaud 9b1b3ecfb9 dataviz: skip choices computation for boolean field in filters cell (#75322)
gitea/combo/pipeline/head This commit looks good Details
2023-03-15 11:13:12 +01:00
Valentin Deniaud ea3773be77 dataviz: refresh overridden filters in filters cell (#75324)
gitea/combo/pipeline/head This commit looks good Details
2023-03-15 10:39:02 +01:00
Valentin Deniaud 8701da0fa9 dataviz: do not rely on self.data on filters cell refresh (#74997)
gitea/combo/pipeline/head This commit looks good Details
2023-03-09 17:45:59 +01:00
Valentin Deniaud 345a643fa1 dataviz: always save boolean filter value as a string (#74997) 2023-03-09 16:05:31 +01:00
Frédéric Péters 27efc1c7f5 misc: do not repeat parent title in child text title (#75071)
gitea/combo/pipeline/head This commit looks good Details
2023-03-03 10:58:03 +01:00
Lauréline Guérin 13c5064f74
translation update
gitea/combo/pipeline/head This commit looks good Details
2023-03-03 10:06:10 +01:00
Lauréline Guérin 2750d6b3d6
dataviz: add invalid_reason_codes in ChartNgCell (#75064)
gitea/combo/pipeline/head This commit looks good Details
2023-03-03 09:19:39 +01:00
Frédéric Péters e3a8e0a480 debian: add new python3-xstatic-select2 dependency (#75003)
gitea/combo/pipeline/head This commit looks good Details
2023-03-02 07:22:40 +01:00
Corentin Sechet 07fdb7971c js: add xstatic select2 library (#74882)
gitea/combo/pipeline/head This commit looks good Details
2023-03-01 15:15:37 +01:00
Frédéric Péters 9c9638f0f6 ci: update tox.ini for new gitea URLs (#74957)
gitea/combo/pipeline/head This commit looks good Details
2023-03-01 08:15:32 +01:00
Valentin Deniaud a7b44346b5 dataviz: improve accessibility of multi select widget (#74919)
gitea/combo/pipeline/head This commit looks good Details
2023-02-28 13:57:57 +01:00
Lauréline Guérin 8d3ef1561d misc: add publik_django_templatetags app in INSTALLED_APPS (#74827)
gitea/combo/pipeline/head This commit looks good Details
2023-02-28 13:41:29 +01:00
Valentin Deniaud d0ea0f08ee translation update
gitea/combo/pipeline/head This commit looks good Details
2023-02-28 10:35:18 +01:00
Valentin Deniaud 16844f5700 dataviz: display required boolean filter as checkbox (#72896)
gitea/combo/pipeline/head This commit looks good Details
2023-02-28 10:26:23 +01:00
Valentin Deniaud b891fd1889 dataviz: move choice field contruction to separate method (#72896) 2023-02-28 10:26:23 +01:00
Valentin Deniaud 337259dc5a dataviz: add new widget to select multiple filter values (#74061)
gitea/combo/pipeline/head This commit looks good Details
2023-02-28 10:00:11 +01:00
Valentin Deniaud 378f4d6b53 dataviz: use |with_template for rendering forms (#74061) 2023-02-28 10:00:11 +01:00
Frédéric Péters b4845d84af ci: run tests against django 3.2 (#74893)
gitea/combo/pipeline/head This commit looks good Details
2023-02-27 12:51:17 +01:00
Valentin Deniaud 0a736adabe dataviz: handle deprecated statistic (#74735)
gitea/combo/pipeline/head This commit looks good Details
2023-02-27 09:44:15 +01:00
Agate 93d737a03b Empty commit to test package building on jenkins (#74572)
gitea/combo/pipeline/head This commit looks good Details
2023-02-22 11:31:31 +01:00
Agate d84c97351f Simplify / harmonize packaging step in Jenkinsfile (#74572)
gitea/combo/pipeline/head This commit looks good Details
2023-02-20 14:05:37 +01:00
Frédéric Péters ca1d73be17 translation update 2023-02-17 09:14:15 +01:00
Lauréline Guérin 1e03d6b0d4 wcs: card cell, display option for file field (#60371) 2023-02-17 08:52:02 +01:00
Valentin Deniaud 1a0b9c8266 dataviz: do not show total for single point data (#73685)
gitea/combo/pipeline/pr-main This commit looks good Details
2023-02-16 16:10:59 +01:00
Lauréline Guérin afab6c189a data: clear old snapshot pages (#74591) 2023-02-16 15:17:44 +01:00
Lauréline Guérin 4c63a9bdf1
data: fix page uuid in snapshots migration for menucell (#74579)
gitea/combo/pipeline/pr-main This commit looks good Details
2023-02-16 11:53:21 +01:00
Lauréline Guérin 5a34b60f13
data: fix page uuid in snapshots migration for linklistcell (#74579) 2023-02-16 11:44:43 +01:00
Frédéric Péters 074ed90356 wcs: use new tag/class for preformated fields (#74490) 2023-02-14 11:00:44 +01:00
Thomas NOËL 483f07cd61 lingo: hide TIPI service on payment backend creation (#74357)
gitea-wip/combo/pipeline/pr-main This commit looks good Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2023-02-12 17:15:29 +01:00
Benjamin Dauvergne b3977ab00b wcs: resolve card_ids lazily (#74306)
The class LazyValue is a thunk[1] like in lazy evaluated language it is
initialized with a function needing no argument and store its result on
first call.

Direct access to the context to get the ids is replaced by calls to new
method WcsCardCell.get_card_ids(context). Original get_card_ids() is
renamed resolve_card_ids() and used with LazyValue to implement the lazy
evaluation of card_ids.

[1]: https://en.wikipedia.org/wiki/Thunk
2023-02-10 09:13:28 +01:00
Benjamin Dauvergne 285fb4e7a0 tests: test lazy resolve of templated card ids (#74306)
This new test checks if the presence of a table mode card cell force
other cells in the page to make requests to w.c.s.
2023-02-10 09:13:28 +01:00
Valentin Deniaud 6850389ae4 misc: remove compatibility code for old django versions (#74247) 2023-02-10 09:13:00 +01:00
Frédéric Péters 6de6c84cdf trivial: remove debug calls to console.log (#74268)
gitea-wip/combo/pipeline/pr-main This commit looks good Details
2023-02-07 15:02:13 +01:00
Lauréline Guérin ce1eefeb50 data: remove wcs dependency in migration (#74107) 2023-02-02 19:57:04 +01:00
Lauréline Guérin 32b31a5d64
data: fix uuid migration with bad _search_services values (again) (#74094)
gitea-wip/combo/pipeline/pr-main This commit looks good Details
2023-02-02 15:27:34 +01:00
Lauréline Guérin a03bc46a1a
data: fix uuid migration with bad _search_services values (#74094)
gitea-wip/combo/pipeline/pr-main This commit looks good Details
2023-02-02 10:59:34 +01:00
Lauréline Guérin 31531e7133
data: snapshot migration, to replace slugs by uuids (#67710)
gitea-wip/combo/pipeline/pr-main This commit looks good Details
2023-02-01 17:45:47 +01:00
Lauréline Guérin 417932bf9b
data: add a data migration for old card cells 2023-02-01 17:45:47 +01:00
Lauréline Guérin b7d06c00d5
data: keep current page uuid on restore (#67710) 2023-02-01 17:45:47 +01:00
Lauréline Guérin 165ffc8527
data: import/export based on uuids (#67710) 2023-02-01 17:45:47 +01:00
Lauréline Guérin 8f21087df8
data: store a new uuid on page loading with snapshot (#67710) 2023-02-01 17:45:47 +01:00
Lauréline Guérin 50fe99e351
data: reset uuid on page duplication (#67710) 2023-02-01 17:45:47 +01:00
Lauréline Guérin 421cce8d82
data: add uuid to Page model (#67710) 2023-02-01 17:45:47 +01:00
Thomas NOËL 5f90155ba6 trivial: remove dict() usage (pylint use-dict-literal) 2023-02-01 15:04:02 +01:00
Thomas NOËL 0b2f7f5663 pylint: ignore broad-exception-raised (broad-except alias) 2023-02-01 14:39:43 +01:00
Lauréline Guérin 9fd735cf11 wcs: fix email field with empty value (#73877) 2023-02-01 09:37:04 +01:00
Lauréline Guérin 292c1c74e3 map: check geojson result (#73756) 2023-02-01 09:36:31 +01:00
Frédéric Péters 0874e90e54 ci: upgrade isort (#74044) 2023-02-01 09:33:22 +01:00
Corentin Sechet a9323d0acf misc: fix placeholder options inheritance from parent pages (#71846) 2023-01-20 10:37:37 +01:00
Benjamin Dauvergne ece7bc5541 gallery: set title max_length to 150 (#73279)
gitea-wip/combo/pipeline/pr-main This commit looks good Details
2023-01-16 10:38:53 +01:00
Valentin Deniaud 68387fbe3b dataviz: allow disabling filters in filters cell (#71655)
gitea-wip/combo/pipeline/head Build started... Details
2023-01-16 09:39:10 +01:00
Valentin Deniaud dce44a268f dataviz: merge grouped choices properly in filters cell (#72929) 2023-01-16 09:38:47 +01:00
Valentin Deniaud f07d72f3ab dataviz: do not add choice outside of group if using option groups (#72929) 2023-01-16 09:38:47 +01:00
Valentin Deniaud 5f23c06485 dataviz: automatically refresh cells on filter change (#72465) 2023-01-16 09:37:20 +01:00
Frédéric Péters 3f8536d41e search: raise 400 on queries without query (#73420) 2023-01-14 15:13:33 +01:00
Lauréline Guérin 1e5dcde158
translation update 2023-01-14 10:55:13 +01:00
Lauréline Guérin f6cfaff63e
wcs: display card variable in the link form (#72624)
gitea-wip/combo/pipeline/pr-main This commit looks good Details
2023-01-14 10:38:45 +01:00
Frédéric Péters e9a5e14196 tests: adjust to change in error message details 2023-01-13 10:41:04 +01:00
Frédéric Péters a172e0a0bd wcs: add setting to change user forms retrieval API limit (#73278) 2023-01-13 10:01:13 +01:00
Frédéric Péters 357295ca42 maps: give higher z-index to larger markers (#73159) 2023-01-13 10:01:07 +01:00
Frédéric Péters 153dbd1993 misc: rename inner footer block for skeletons (#73113) 2023-01-13 10:01:00 +01:00
Frédéric Péters fd302abcf2 wcs: use a sr-only <label> for tracking code label (#73097) 2023-01-13 10:00:51 +01:00
Paul Marillonnet 16e6fc8711 profile: format phone numbers at cell-rendering time (#72769)
gitea-wip/combo/pipeline/pr-main This commit looks good Details
2023-01-06 09:19:42 +01:00
Frédéric Péters e1046fa0b8 misc: disable "categories" cell (#23240)
gitea-wip/combo/pipeline/pr-main This commit looks good Details
2023-01-03 16:23:59 +01:00
Frédéric Péters 1243c94a1d translation update 2022-12-30 12:45:31 +01:00
Frédéric Péters ec67001318 search: do not allow search queries to contain a nul byte (#72722) 2022-12-28 16:13:07 +01:00
Frédéric Péters 2854541de5 general: update all pk URL fragments to only match on numbers (#72721) 2022-12-28 14:24:21 +01:00
Frédéric Péters e2ed6b0fa9 misc: only allow numbers as search cell id in URLs (#72721) 2022-12-28 14:24:21 +01:00
Frédéric Péters 5dd42bd44b general: render page synchronously for bots (#72807)
gitea-wip/combo/pipeline/pr-main This commit looks good Details
2022-12-28 10:36:07 +01:00
Frédéric Péters ea07d17071 maps: use a translated attribution on maps (#72806) 2022-12-27 18:26:26 +01:00
Frédéric Péters 481d4c73a3 manager: save page snapshot when modifying placeholder options (#72798)
gitea-wip/combo/pipeline/pr-main This commit looks good Details
2022-12-26 21:19:51 +01:00
Lauréline Guérin d990cb550c
translation update 2022-12-26 10:17:02 +01:00
Lauréline Guérin 557aec457e maps: custom error message for missing map layer on import (#72543) 2022-12-26 10:13:51 +01:00
Frédéric Péters 12b2c350be tests: update map marker test to not rely on layer id (#72756) 2022-12-26 09:45:24 +01:00
Frédéric Péters 02a5bf77a2 maps: keep marker colour widgets aligned (#72084) 2022-12-26 09:36:19 +01:00
Frédéric Péters 26135d507c maps: add option to set marker size (#72756) 2022-12-26 09:32:38 +01:00
Frédéric Péters e25df5f449 search: add missing space before custom title in cell configuration (#72637) 2022-12-23 08:58:18 +01:00
Frédéric Péters 4ad2d6c9cc utils: do not duplicate email/etc. query parameters (#72207) 2022-12-23 08:57:33 +01:00
Frédéric Péters 83e612e47b ci: only build package for bullseye (#72729) 2022-12-22 17:21:25 +01:00
Valentin Deniaud 19e0685d11 dataviz: clear subfilters on statistics change (#72315) 2022-12-13 10:55:22 +01:00
Lauréline Guérin a281481885
translation update 2022-12-13 08:44:10 +01:00
Lauréline Guérin a40e7a2feb wcs: card cell & link entry with file field (#71984) 2022-12-13 08:35:33 +01:00
Lauréline Guérin cf3840271a
translation update 2022-12-13 08:34:49 +01:00
Lauréline Guérin 174e92d76a
manager: associate a page to a card model (#68675) 2022-12-13 08:20:27 +01:00
Frédéric Péters aebfdb3959 gallery: fix access to cell management for non-admins (#72254) 2022-12-12 19:07:19 +01:00
Lauréline Guérin ec594a63a9
misc: fix black error 2022-12-12 18:46:16 +01:00
Frédéric Péters 22640154ef pylint: add lxml to extension-pkg-allow-list 2022-12-12 18:40:33 +01:00
Valentin Deniaud fecd0cddca dataviz: make filters cell autoreload work with tables (#72144) 2022-12-12 18:27:56 +01:00
Valentin Deniaud b5a9bc77a6 dataviz: handle no x_labels and dot chart type (#72154) 2022-12-12 18:27:20 +01:00
Valentin Deniaud acb71fcf59 dataviz: support integers in x_labels for tables (#72153) 2022-12-12 18:24:22 +01:00
Valentin Deniaud 49f8b08ed3 dataviz: restore dynamic hiding of time range fields (#72297) 2022-12-12 18:23:54 +01:00
Valentin Deniaud 52aeea1271 dataviz: improve seconds formatter (#72143) 2022-12-12 18:23:20 +01:00
Valentin Deniaud 16224324eb dataviz: add formatting for series in seconds (#72131) 2022-12-12 18:22:25 +01:00
Lauréline Guérin d41c49e724
translation update 2022-12-12 18:11:32 +01:00
Lauréline Guérin f94fedb8f5 data: mark json config cell as invalid if not found in settings (#71168) 2022-12-12 18:08:32 +01:00
Thomas NOËL 757f719aae tests: accept future dummy eopayment URL (#72235) 2022-12-09 15:54:49 +01:00
Lauréline Guérin f6083dcccd
manager: fix asset_files export (#71923)
gitea-wip/combo/pipeline/pr-main This commit looks good Details
2022-12-09 08:00:59 +01:00
Frédéric Péters 7f05710818 search: ignore links depending on cards/forms/request (#71992) 2022-12-09 07:49:50 +01:00
Thomas NOËL 07ad91b49b dataviz: ignore bad statistics provider result (#71963) 2022-12-09 00:57:54 +01:00
Thomas Jund 40fab918f4 html: add pk-data-table class to invoices table (#71414) 2022-11-29 09:56:44 +01:00
Lauréline Guérin c6d1326490 wcs: use include-xxx params to get a single card (#71481) 2022-11-28 14:15:05 +01:00
Lauréline Guérin 46042e9b6c
manager: fix assets ordering with bad settings (#71357)
gitea-wip/combo/pipeline/pr-main This commit looks good Details
2022-11-21 08:51:22 +01:00
Lauréline Guérin 3435da62b0
wcs: use include-xxx params instead of full (#71328)
gitea-wip/combo/pipeline/pr-main This commit looks good Details
2022-11-15 15:40:15 +01:00
Lauréline Guérin b2a365f625
search: always display custom title if defined (#71246) 2022-11-14 17:45:49 +01:00
Frédéric Péters 50bd817862 assets: use nginx X-Accel-Redirect to serve asset files (#71258) 2022-11-14 08:51:53 +01:00
Benjamin Dauvergne 8d01db8f41 pwa: set an explicit exp claim in vapid JWT token (#65858) 2022-11-04 08:41:16 +01:00
Lauréline Guérin 3ad0a23f71
data: fix snapshot restore with duplicated slug (#70884)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-11-03 22:19:06 +01:00
Corentin Sechet 8be066fd47 misc: show form description in links list even if initially hidden (#70836) 2022-11-02 10:58:59 +01:00
Lauréline Guérin c58b9e33e7
misc: fix pylint warning
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-10-29 09:54:52 +02:00
Lauréline Guérin d426d54ce1
misc: remove CELL_CONDITIONS_ENABLED setting (#70605) 2022-10-29 09:54:52 +02:00
Frédéric Péters c4dfdd6801 tests: move cell visibility tests to their own file (#70844)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-10-29 09:17:53 +02:00
Frédéric Péters fa247d8453 misc: do not migrate <h2> titles unless they're first (#70812)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-10-29 09:08:02 +02:00
Frédéric Péters 8b2e2c854e manager: use text cell title as additional label (#70804) 2022-10-28 10:11:01 +02:00
Frédéric Péters 96f21090ed misc: display text cells if they have a title (#70803) 2022-10-28 10:11:01 +02:00
Frédéric Péters d76484e64e ci: update pyupgrade to 3.1.0 (#70693) 2022-10-28 08:05:29 +02:00
Lauréline Guérin 6d2b0f9e0b
data: fix textcell title migration (#70646)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-10-25 11:39:49 +02:00
Frédéric Péters ad7923c07b misc: skip overlong titles in text cell title migration (#70630) 2022-10-24 20:44:29 +02:00
Frédéric Péters d21a6db35f trivial: remove debugging print() (#70608) 2022-10-24 15:21:39 +02:00
Benjamin Dauvergne d29dc19ee9 misc: show unlogged only cells to superuser (#45846)
In this case the .shown-because-admin class is present on the cell.
2022-10-20 10:31:52 +02:00
Benjamin Dauvergne 92e68e279a misc: fix shown_because_admin has no role and cell.restricted_to_unlogged is True (#45846) 2022-10-20 10:31:52 +02:00
Corentin Sechet afde19c1fe misc: add title field to text cells (#42968) 2022-10-19 11:57:34 +02:00
Thomas NOËL d31cf3a1b3 translation update (typo) 2022-10-18 16:52:46 +02:00
Thomas NOËL 15c18d5c22 translation update (restore missing \n) 2022-10-18 16:39:46 +02:00
Thomas NOËL f1f4124805 translation update (after djhtml modifications) 2022-10-18 16:27:36 +02:00
Serghei Mihai d31bfde62b wcs: do not mix category and form descriptions (#70349)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-10-17 16:51:49 +02:00
Agate 1d6c14ba80 misc: add livereload setup (#68827) 2022-10-14 16:39:42 +02:00
Frédéric Péters cdef4f6a1d manager: use native gadjo padding for cell form tabs (#70203) 2022-10-13 09:46:07 +02:00
Pierre Ducroquet 6f4dcd515f uwsgi: new configuration (#67580)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-10-10 18:07:36 +02:00
Lauréline Guérin dab16066fa
family: fix weekly agenda & current week init (#69836)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-10-04 08:46:24 +02:00
Valentin Deniaud 84fb0e9ab0 misc: add django-upgrade files/notes (#69798)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-10-03 14:22:44 +02:00
Valentin Deniaud cd498afcb0 misc: apply django-upgrade (#69798) 2022-10-03 14:22:16 +02:00
Valentin Deniaud 8e09b2f73d misc: fix incorrect pre-commit info in readme 2022-09-29 18:29:06 +02:00
Valentin Deniaud 54126b6ccf misc: add djhtml files/notes (#69709) 2022-09-29 16:54:54 +02:00
Valentin Deniaud 4784a3990e misc: apply djhtml (#69709) 2022-09-29 16:54:11 +02:00
Serghei Mihai 964461e27e wcs: uniformize forms links rendering (#65846) 2022-09-27 11:39:23 +02:00
Lauréline Guérin 45d40596bc
wcs: display richtext field as safe (#69271)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-09-26 08:49:26 +02:00
Lauréline Guérin cf068ec94a
manager: reduce num of queries on page view (#69400)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-09-26 08:27:41 +02:00
Lauréline Guérin c2afb605e0
family: weekly agenda & many weeks display (#69454)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-09-23 15:08:30 +02:00
Frédéric Péters 498f0db67e wcs: add css classes to emails and filenames values (#69351) 2022-09-23 08:56:50 +02:00
Frédéric Péters 8aade8287b wcs: use same style for tables in front and back (#69371) 2022-09-23 08:56:50 +02:00
Frédéric Péters 871742a416 misc: mark some cells as requiring a session (#69301) 2022-09-23 08:56:50 +02:00
Lauréline Guérin 5972b44739
data: fix parent selection on import (#69194)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-09-23 08:08:44 +02:00
Frédéric Péters 7e8b9318b2 translation update 2022-09-20 23:57:12 +02:00
Valentin Deniaud d4be69bddb dataviz: fix filters cell display of subfilters on empty filter values (#69112) 2022-09-15 12:30:13 +02:00
Lauréline Guérin f8fcd6b31a
wcs: do not show A anchor if URL and label are empty (#62137)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-09-15 10:08:50 +02:00
Lauréline Guérin 77fa421ef4
wcs: card cell, by default configured for 'all cards' (#69019)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-09-13 14:27:25 +02:00
Frédéric Péters 267cc62262 translation update 2022-09-12 16:46:20 +02:00
Frédéric Péters cdbb87ecf1 manager: add view to edit a single attribute with an ajax request (#64575) 2022-09-12 16:45:03 +02:00
Lauréline Guérin ff3a3a12ff
translation update 2022-09-12 16:17:29 +02:00
Lauréline Guérin 7a11f2ff8f
wcs: custom card, reset form when adding a cell (#68807)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-09-12 15:59:37 +02:00
Lauréline Guérin 29e4f084d3
data: fix condition with AttributeError (#68865)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-09-12 15:44:54 +02:00
Lauréline Guérin c43d003e54
wcs: display field labels an hide cell slug if possible (#68643)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-09-12 15:32:39 +02:00
Lauréline Guérin f47971df8a
wcs: add user fields from card schema (#66897)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-09-12 15:19:28 +02:00
Lauréline Guérin 0cacfc0349
wcs: add user fields in card custom_schema configuration (#66898)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-09-12 15:03:09 +02:00
Lauréline Guérin 612555a697
wcs: card cell and custom schema for table display (#68721)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-09-12 14:56:13 +02:00
Lauréline Guérin c44808a83c
search: export/import with target_page configured on card engine (#68606)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-09-12 14:48:42 +02:00
Lauréline Guérin b7b73533b7
search: configure target page for card engine (#68606) 2022-09-12 14:48:42 +02:00
Lauréline Guérin d051059544
wcs: import/export with link to page with sub_slug (#68534)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-09-12 14:40:42 +02:00
Lauréline Guérin 04165ad13c
wcs: custom schema, link item, target a page with sub_slug (#68534) 2022-09-12 14:40:42 +02:00
Lauréline Guérin 8cdbd1c0e7
wcs: add trailing / to card page urls to avoid redirect (#68534) 2022-09-12 14:40:42 +02:00
Lauréline Guérin bf62d743a5
misc: remove compatibility with old export format (#68554)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-09-09 10:18:37 +02:00
Lauréline Guérin 5ddfa0503a
misc: remove mathematic filters + split (#68673)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
they are defined in publik-django-templatetags
2022-09-09 08:20:21 +02:00
Agate 8e4c505524 Revert "django4: fix default AppConfig deprecation warnings (#68585)"
This reverts commit aa576e205f.
2022-09-05 16:19:59 +02:00
Agate aa576e205f django4: fix default AppConfig deprecation warnings (#68585) 2022-09-05 13:56:42 +02:00
Agate 9492991c9d django4: replaced deprecated request.is_ajax() call (#68585) 2022-09-05 13:56:42 +02:00
Frédéric Péters c81c303e0a misc: inherit placeholder options when using parent content cells (#68679)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-09-02 19:28:10 +02:00
Frédéric Péters 496dec06c9 translation update 2022-09-02 08:12:28 +02:00
Lauréline Guérin 3e53c6a1f0 wcs: adjust labels in card ids selection for card cell (#68531) 2022-09-02 08:11:18 +02:00
Frédéric Péters 6df8c9512a translation update 2022-09-02 07:53:24 +02:00
Valentin Deniaud 2bfbfca2de dataviz: build filter parameters outside of spooler (#65882) 2022-09-02 07:51:59 +02:00
Lauréline Guérin 99c021a4c9 wcs: fix card cell with incomplete schema (#67719) 2022-09-02 07:50:54 +02:00
Valentin Deniaud 4192343b05 dataviz: change message when page variable cannot be evaluated (#68372) 2022-09-02 07:50:07 +02:00
Lauréline Guérin d1b26246b7
wcs: fix card rendering with missing varname in custom_schema (#68598)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-08-31 22:23:32 +02:00
Agate 202c614849 accessibility: support hiding menus through escape keys (#40930) 2022-08-30 14:41:18 +02:00
Frédéric Péters bdf7e82f31 wcs: do not index card cells (#68539)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-08-30 11:44:01 +02:00
Frédéric Péters 973a0d13cb tests: limit length of database name 2022-08-30 11:39:35 +02:00
Corentin Sechet 11310342cf general: add BEM classes on menus (#68106) 2022-08-29 22:38:40 +02:00
Frédéric Péters bc5e0ae5f3 translation update 2022-08-29 16:38:04 +02:00
Frédéric Péters 2d0508e8b9 wcs: adjust labels of cards to display selection (#68517) 2022-08-29 16:23:26 +02:00
Lauréline Guérin 48aeff3105
translation update 2022-08-29 15:08:01 +02:00
Lauréline Guérin 574228c0b8
wcs: snapshot loading with old card cells (#68140)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-08-29 14:38:03 +02:00
Lauréline Guérin 4019081d34
wcs: add a css class depending on display mode on card cell (#68140) 2022-08-29 14:38:02 +02:00
Lauréline Guérin 3a3eae69ae
wcs: delete old card cell models (#68140) 2022-08-29 14:38:02 +02:00
Lauréline Guérin d8fcc66f4b
wcs: migration for card cell merge (#68140) 2022-08-29 14:38:02 +02:00
Lauréline Guérin 797f9af235
wcs: add new card cell model (#68140) 2022-08-29 14:38:02 +02:00
Lauréline Guérin 0712cbe5d6
wcs: card cell with table mode can use cards cell assets (#68063)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-08-29 14:37:33 +02:00
Lauréline Guérin 5a1f1a9a51
wcs: fix custom_title update (#68063) 2022-08-29 14:37:33 +02:00
Lauréline Guérin 7716113a51
wcs: render card cell with table mode (#68063) 2022-08-29 14:37:33 +02:00
Lauréline Guérin fed5465f98
wcs: rename cards context variable (#68063)
It can conflict with Cards context object from
publik-django-templatetags
2022-08-29 14:37:32 +02:00
Lauréline Guérin 411170695f
tests: rename card cell tests with card display mode (#68063) 2022-08-29 14:37:32 +02:00
Lauréline Guérin eca53776fb
tests: split wcs tests (#68063) 2022-08-29 14:37:32 +02:00
Lauréline Guérin 05d1897627
tests: move wcs tests (#68063) 2022-08-29 14:37:32 +02:00
Lauréline Guérin c832299962
wcs: add display_mode option for card cell (#68063) 2022-08-29 14:37:32 +02:00
Lauréline Guérin 25da1b91b7
wcs: option to list all cards (#68037)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-08-29 14:34:25 +02:00
Lauréline Guérin 8df6e51c73
wcs: opti, use detail url if only one id (#68037) 2022-08-29 14:34:25 +02:00
Lauréline Guérin 9d4ae7974c
wcs: add pagination for list of cards (#68037) 2022-08-29 14:34:25 +02:00
Lauréline Guérin c96fb54b04
wcs: use only_for_user & without_user flags (#68037) 2022-08-29 14:05:47 +02:00
Lauréline Guérin d23bb074b6
wcs: update card cell model (#68037)
add fields only_for_user & limit
2022-08-29 14:05:47 +02:00
Lauréline Guérin 652a6dff32
wcs: for card cell with multiple ids, get all cards in one request (#68015)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-08-29 14:05:00 +02:00
Agate ea510ca0f1 accessibility: replace focus at the beginning of list on pagination change (#41128) 2022-08-29 10:28:58 +02:00
Frédéric Péters 510a5c114e dataviz: truncate legend taking account the pie graph width (#68113) 2022-08-29 10:24:39 +02:00
Agate 194d78e5be replaced ungettext with ngettext equivalent (#68183) 2022-08-29 09:35:14 +02:00
Agate 25884a02cc replaced smart_text with smart_str equivalent (#68183) 2022-08-29 09:35:14 +02:00
Agate b8269f4d3c replaced urls.url with url.re_path equivalent (#68183) 2022-08-29 09:35:13 +02:00
Agate 54eafdbb62 replaced force_text with equivalent force_str (#68183) 2022-08-29 09:35:13 +02:00
Agate f77ad1bc2d replaced ugettext* calls with corresponding gettext* calls (#68183) 2022-08-29 09:35:13 +02:00
Frédéric Péters dcafe94f57 pylint: ignore unsupported-binary-operation (#68473)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-08-27 16:02:20 +02:00
Frédéric Péters b9f001f764 misc: add timeout to requests for feed (#68473) 2022-08-27 16:01:38 +02:00
Frédéric Péters 93eaa79607 tests: increase value of high id used in tests for 404 (bis) 2022-08-27 09:06:36 +02:00
Thomas NOËL 12087eb075 wcs: do not add tryauth for bots (#68052) 2022-08-18 14:37:26 +02:00
Pierre Ducroquet 76167cab03 LingoRecentTransactionsCell: optimize query (#68212)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
- use an union instead of an OR
- use the fact that union is an implicit distinct to simplify the code
- index the start_date field so it can be used to filter efficiently
2022-08-17 12:00:40 +02:00
Frédéric Péters a5fe6b569f translation update 2022-08-16 14:06:44 +02:00
Frédéric Péters 9e9b225eb1 manager: rephrase note on redirections used as a skeleton pages (#68160) 2022-08-16 14:02:42 +02:00
Frédéric Péters 11e8d1ce26 manager: also look at tab open hash to open cell (#68138) 2022-08-16 12:58:53 +02:00
Frédéric Péters 4a5c5350bc tests: increase value of high id used in tests for 404 2022-08-16 12:54:19 +02:00
Valentin Deniaud f9b51826a3 dataviz: remove automatic transpose in tables (#68049) 2022-08-09 12:45:01 +02:00
Frédéric Péters 4296391c82 translation update 2022-08-05 10:29:57 +02:00
Frédéric Péters 8078688a72 dataviz: add table style to invert rows/columns (#67937) 2022-08-05 10:29:27 +02:00
Frédéric Péters ffe75d522d dataviz: revert passing request to spooler (#67902)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
This reverts commits 7a4bc2c02f and
06b4b1dce5.
2022-08-03 11:05:33 +02:00
Frédéric Péters 8056ae790c debian: make cron quiet (#67897) 2022-08-03 10:01:32 +02:00
Valentin Deniaud 7ed7481968 dataviz: support required filter without default value (#66299) 2022-08-02 21:29:36 +02:00
Pierre Ducroquet 22daaad4ef PaymentBackend: skip poll faster if there is no transaction (#66487) 2022-08-02 21:27:58 +02:00
Serghei Mihai 8492dc2403 search: allow text placing before form (#67390) 2022-08-02 21:27:08 +02:00
Frédéric Péters f4bd375ba7 tests: adapt to pass with both 2.2 and 3.2 2022-08-02 17:01:08 +02:00
Frédéric Péters 3f20128208 tox: run against 2.2 in main (#67724) 2022-08-02 16:34:23 +02:00
Agate 7915dd26de tox: update targets to allow testing both under django 3.2 and 2.2 (#67724)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-08-02 16:26:31 +02:00
Agate 47a1a1314f tests: fix misc test failures (#67724) 2022-08-02 16:26:27 +02:00
Agate f95a39744d tests: fix broken tests after django-webtest upgrade (#67724) 2022-08-02 16:26:20 +02:00
Frédéric Péters 0fa62c1598 tox: allow any django-webtest (#67724) 2022-08-02 16:25:24 +02:00
Frédéric Péters 5243db93ab misc: allow django 3.2 (#67724) 2022-08-02 16:25:21 +02:00
Frédéric Péters b31f63bdc0 tox: use django 3.2 (#67724) 2022-08-02 16:25:16 +02:00
Frédéric Péters 32cc7cdb60 debian: remove obsolete standard error output config from systemd unit (#65101) 2022-08-02 10:09:25 +02:00
Valentin Deniaud 7a4bc2c02f dataviz: pass request to spooler refresh task (#65882) 2022-07-29 10:10:16 +02:00
Valentin Deniaud 06b4b1dce5 dataviz: pass request explicitly when needed (#65882) 2022-07-29 10:10:16 +02:00
Valentin Deniaud 6cac60262e dataviz: transpose table only for bijoe (#67068) 2022-07-29 10:08:32 +02:00
Valentin Deniaud 6370164fc8 data: handle duplicated page slugs at import (#59509) 2022-07-25 14:35:02 +02:00
Frédéric Péters 4a8f18d89b misc: wrap Page __str__ value in str() to please pylint (#67470) 2022-07-21 21:09:29 +02:00
Frédéric Péters b6e5eb1053 maps: unmark map cell as empty once a map has been set (#67467) 2022-07-19 10:25:16 +02:00
Frédéric Péters bfd296648b misc: allow djangorestframework 3.12 (#64288) 2022-07-19 09:46:26 +02:00
Frédéric Péters ada46b1764 translation update 2022-07-18 18:11:57 +02:00
Lauréline Guérin c240f32a66
misc: remove duplicated code (#67254)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-07-18 10:07:00 +02:00
Agate 4ed8ebcf21 manager: support for saving a page snapshot with a label (#56519) 2022-07-18 08:26:08 +02:00
Agate 346000f67f ci: speed up CI using multiple processes with pytest (#67088) 2022-07-18 08:23:22 +02:00
Lauréline Guérin 6e5eef1764
manager: fix duplicate cell view with unknown cell id (#63793)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-07-15 11:37:13 +02:00
Lauréline Guérin 809862bc2d
manager: prefix ConfigJsonCell parameter fields in form (#66603)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-07-15 10:07:42 +02:00
Lauréline Guérin 5486f4c068
wcs: fix use of custom view in card cell with related (#67260)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-07-15 09:48:54 +02:00
Lauréline Guérin 907a3d7408
wcs: fix card cell global context with misordered cells (#67214)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-07-15 09:37:42 +02:00
Frédéric Péters 67144e0ad8 Revert "PaymentBackend: skip poll faster if there is no transaction (#66487)"
This reverts commit 0ebe6d8233.
2022-07-12 16:28:55 +02:00
Pierre Ducroquet 0ebe6d8233 PaymentBackend: skip poll faster if there is no transaction (#66487) 2022-07-12 16:26:47 +02:00
Frédéric Péters 0b470987ba misc: set response.content as bytes, as appropriate (#67137) 2022-07-07 15:30:59 +02:00
Frédéric Péters d097bd581a misc: put page template name in exported skeleton context (#28225) 2022-07-07 10:29:30 +02:00
Frédéric Péters d61c514f0e maps: adjust rendering using sass variables and fixed pixel sizes (#49504) 2022-07-07 10:29:30 +02:00
Valentin Deniaud b558319204 tests: remove leftover comments 2022-07-06 13:50:36 +02:00
Agate 0d516c0bd6 ci: reduce test execution time by changing password hasher (#67020) 2022-07-05 14:03:00 +02:00
Valentin Deniaud f3c3e3301e dataviz: reconstruct ajax filters cell url on each call (#66956) 2022-07-05 11:36:30 +02:00
Valentin Deniaud 0996d0923a data: add cell get_ajax_url method (#66956) 2022-07-05 10:56:14 +02:00
Lauréline Guérin 1f94e32a17
data: inject global context in context for cell conditions (#66953)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-07-05 10:25:37 +02:00
Valentin Deniaud 10b127edd3 dataviz: do not refresh subfilters for bijoe stat (#66936) 2022-07-05 10:24:55 +02:00
Valentin Deniaud 6807e032a7 dataviz: fix jquery element presence test (#66947) 2022-07-04 16:22:23 +02:00
Lauréline Guérin e9d2e6c349
wcs: custom title for forms in your care cell (#61589)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-07-04 15:46:15 +02:00
Valentin Deniaud b5132df5f5 dataviz: reload chart filters cell to reflect subfilters (#62533) 2022-07-04 11:27:00 +02:00
Valentin Deniaud 7f7d75c509 dataviz: factorize querystring building code (#62533) 2022-07-04 11:27:00 +02:00
Benjamin Dauvergne 79f061c471 lingo: remove SIPS from listed backends (#66737) 2022-06-29 17:05:18 +02:00
Lauréline Guérin f97022233e
data: inject computed variables in context for cell condition (#66632)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-06-29 10:45:32 +02:00
Lauréline Guérin f4ecedaf35
misc: fix dataviz test
refs #63397, wrong merge ?
2022-06-28 15:11:40 +02:00
Valentin Deniaud 3e04d37cd7 dataviz: hide time range field from filters cell if templates are used (#63397) 2022-06-27 17:11:37 +02:00
Frédéric Péters 8f7a50217d misc: add css class to placeholder with flex grid (#65145) 2022-06-27 09:44:30 +02:00
Frédéric Péters 443c5a24a6 translation update 2022-06-25 16:41:16 +02:00
Lauréline Guérin accbb72ccd
manager: fix link-action-icon height (#65834)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-06-24 09:17:21 +02:00
Lauréline Guérin 794bb74c32
data: add display condition to cells (#66263)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
and also on each item of a linklist cell
2022-06-24 09:00:47 +02:00
Lauréline Guérin 9428431767
manager: add a visual mark to cell tabs with configured settings (#64936)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-06-24 08:48:10 +02:00
Serghei Mihai 02b80dbd53 tipi: update refdet's test valeur used for ROLMRE protocol (#66430) 2022-06-21 11:42:31 +02:00
Frédéric Péters f142f10ee7 debian: apply wrap-and-sort 2022-06-17 19:04:14 +02:00
Frédéric Péters 72dada8521 misc: use pre-commit-debian (#66191) 2022-06-17 19:03:37 +02:00
Frédéric Péters ef4ac9e12e misc: make makemessages remove obsolete strings by default (#66289) 2022-06-17 19:03:36 +02:00
Lauréline Guérin 9b5274a663
wcs: hide general tab for categories cell if no wcs_site selection (#66310)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-06-16 16:51:25 +02:00
Valentin Deniaud 17fb7f22e6 dataviz: catch invalid template error when updating subfilters (#65916) 2022-06-02 15:07:25 +02:00
Valentin Deniaud c25139c7d8 dataviz: fix access to context_processors on range template evaluation (#65908) 2022-06-02 13:33:34 +02:00
Frédéric Péters 827f68b629 manager: keep link label attribute on main tab (#65878) 2022-06-02 10:45:39 +02:00
Frédéric Péters fdad44bc89 trivial: join splitted strings 2022-06-01 19:00:46 +02:00
Frédéric Péters fa99abc37f pylint: allow unnecessary-lambda-assignment 2022-06-01 18:59:40 +02:00
Frédéric Péters 8ab20fe713 pylint: remove obsolete options 2022-06-01 18:59:11 +02:00
Frédéric Péters 8640f62d31 misc: allow verbatim templates in text links in skeleton pages (#65815) 2022-06-01 08:52:08 +02:00
Lauréline Guérin d8f7379952
misc: remove |get template filter from combo (#65540)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
it is defined publik-django-templatetags
2022-05-31 16:06:40 +02:00
Lauréline Guérin c6fd8e1ee0
data: fix extra_variables & VariableDoesNotExist (#65746)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-05-31 14:26:50 +02:00
Frédéric Péters 6466b0f5d8 dashboard: integrate tile stats from combo-plugin-gnm (#65794) 2022-05-31 14:06:44 +02:00
Frédéric Péters 43f56a6cb4 debian: remove unused statics_hash context processor (#65722) 2022-05-31 13:02:23 +02:00
Frédéric Péters 0afc53a6d7 general: do not preload package versions (#65722) 2022-05-31 13:02:23 +02:00
Frédéric Péters 2248f16cc9 templatetags: remove json_script as it's available in django (#65801)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-05-31 11:11:06 +02:00
Lauréline Guérin ebab8fdb4e
wcs: don't add cell's div if empty value have to be skipped (#64451)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-05-30 08:26:49 +02:00
Corentin Sechet 096d396c84 translation update 2022-05-27 14:00:18 +02:00
Corentin Sechet 3d4f53fde3 manager: add link to export page snapshot (#62023) 2022-05-27 13:57:02 +02:00
Corentin Sechet 2b68a863ed misc: move title fields to appearance tab (#64681) 2022-05-27 13:33:03 +02:00
Valentin Deniaud f3727f829d dataviz: do not raise error in check_validity (#65615)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-05-27 08:32:17 +02:00
Valentin Deniaud d05406d9a0 dataviz: get request when possible on subfilters update (#65615) 2022-05-27 08:32:15 +02:00
Valentin Deniaud eed78d8f6a dataviz: pass request in context during page variable evaluation (#65348) 2022-05-27 08:28:47 +02:00
Valentin Deniaud 348f9b50bf dataviz: add prefix to filters cell form (#65348) 2022-05-27 08:28:47 +02:00
Frédéric Péters 59a9d2f352 general: always unfold targetted cell (#64563) 2022-05-27 08:26:16 +02:00
Frédéric Péters af51c0c20b manager: increase width of textarea in cell properties (#65654) 2022-05-27 08:26:00 +02:00
Frédéric Péters f3295f1451 manager: switch page reordering request to POST (#65617) 2022-05-27 08:25:52 +02:00
Frédéric Péters ed358aed7e notifications: do not notify anonymous user (#64497)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-05-23 15:27:35 +02:00
Frédéric Péters cd71255ae5 notifications: rename "founded_uuids" to "existing_uuids" (#64497) 2022-05-23 15:27:35 +02:00
Frédéric Péters f10d789389 translation update 2022-05-17 09:16:24 +02:00
Valentin Deniaud ce0a65a35e manager: avoid validation errors on dynamic fields in cell edit form (#65163) 2022-05-16 17:44:06 +02:00
Valentin Deniaud b47c3e4219 manager: remove dead code (#65163) 2022-05-16 17:38:55 +02:00
Emmanuel Cazenave 0850accd99 misc: use legacy urls to call up to date urls (#65024)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-05-16 15:44:57 +02:00
Valentin Deniaud a97c740dac dataviz: add support for filter deprecation (#65140) 2022-05-16 11:01:07 +02:00
Valentin Deniaud c909f70ad6 dataviz: add option groups support (#65134) 2022-05-16 11:00:55 +02:00
Benjamin Dauvergne ccb841e289 lingo: move eopayment options from backend to regie when scope change (#65141) 2022-05-13 16:15:19 +02:00
Lauréline Guérin b6dc499003
wcs: publik-django-templatetags integration (#64803)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-05-13 14:18:12 +02:00
Frédéric Péters 4f9abf2d00 assets: return asset URL as response of "set" API (#64970)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-05-06 21:12:05 +02:00
Frédéric Péters 72a5123831 dataviz: set x-frame-options to sameorigin for embedded graphs (#64826) 2022-05-04 20:50:18 +02:00
Frédéric Péters b29a6140be translation update 2022-05-02 16:57:42 +02:00
Frédéric Péters 6b44b605c6 settings: add translatable strings for publik-base-theme (#62413) 2022-05-02 16:53:49 +02:00
Frédéric Péters b02b7b0e5b misc: add option for placeholders to create an outer tag (#62415)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-05-02 15:53:45 +02:00
Frédéric Péters 9da25d0f8c misc: extend cell size options, use a dedicated widget (#62072)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-04-22 14:03:13 +02:00
Frédéric Péters 5b19cf1113 general: add possibily to layout placeholder cells in flex grid (#62072) 2022-04-22 14:02:04 +02:00
Frédéric Péters 9dba7d1245 lingo: comment is_notifiable() and do not log non-errors (#64460) 2022-04-22 12:38:27 +02:00
Frédéric Péters 48b09b4105 translation update 2022-04-21 17:55:05 +02:00
Valentin Deniaud a52491ea5e dataviz: load table charts asynchronously (#64315) 2022-04-20 16:17:52 +02:00
Corentin Sechet 5f918b53c0 dataviz: using natural order for alphabetical data sort (#62317) 2022-04-20 10:52:21 +02:00
Frédéric Péters 411c0f025e translation update 2022-04-19 19:27:13 +02:00
Frédéric Péters 2d8a6cfb5c pwa: add support for maskable icons (#64297) 2022-04-19 19:20:42 +02:00
Frédéric Péters 68e7070377 misc: adapt skeletontemplate tag for 3.2 (#64298)
This follows this django change:

commit 04ac9b45a34440fa447feb6ae934687aacbfc5f4
Author: Alex Gaynor <alex.gaynor@gmail.com>
Date:   Tue Oct 8 22:25:20 2019 -0400

    Improved performance of django.template.base.Parser.

    pop(0), which is used to fetch each token, is O(n) in the length of the
    list. By reversing the list and operating off the end, we can perform
    next_token(), prepend_token(), and delete_first_token() in constant
    time.
2022-04-18 18:03:58 +02:00
Frédéric Péters b19febc92a translation update (spelling) 2022-04-18 18:03:35 +02:00
Frédéric Péters 4ab6c803f2 trivial: remove usage of obsolete python_2_unicode_compatible (#64292) 2022-04-17 11:15:43 +02:00
Frédéric Péters 76904d5e6c translation update 2022-04-15 21:19:57 +02:00
Frédéric Péters d9eeeeea61 misc: remove usage of django.utils.six (#63683)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-04-15 16:42:01 +02:00
Frédéric Péters 514d8bbaaf translation update 2022-04-15 16:14:32 +02:00
Lauréline Guérin dd74d196f6
style: add messages only if requested in styles demo page(#62172)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-04-15 15:12:07 +02:00
Lauréline Guérin bed3ff7ed8
cells: option to bypass LinkCell url validity check (#62136)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-04-15 09:14:50 +02:00
Lauréline Guérin 71be7b1d86
misc: git ignore combo.manager.css 2022-04-15 09:14:50 +02:00
Lauréline Guérin cf980643eb
wcs: fix cell rendering with anonymous users (#63220)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-04-15 08:37:51 +02:00
Frédéric Péters 6a33066ac9 wcs: add cache_duration advanced option to "forms in your care" cell (#63390) 2022-04-15 08:25:10 +02:00
Frédéric Péters 2ba7eb256e misc: add possibility to define tabs by specifying fields (#63390) 2022-04-15 08:25:10 +02:00
Frédéric Péters b406d1808c wcs: mark "forms in your care" cell as valid on save (#63807) 2022-04-15 08:25:10 +02:00
Frédéric Péters a3f5786473 wcs: force an (empty) value for custom schema (#63935) 2022-04-14 17:08:10 +02:00
Frédéric Péters 518bf6ca4e trivial: remove trailing spaces 2022-04-12 08:21:58 +02:00
Lauréline Guérin fe50a2bc05
manager: fix page reorder with missing param (#63650)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-04-08 09:19:21 +02:00
Lauréline Guérin d24640eb7b
family: use with_status param in chrono url (#63325)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-04-07 09:07:02 +02:00
Thomas Jund 11db1b1735 JS: put back 'combo:cellform-reloaded' event trigger (#63567) 2022-04-05 14:59:31 +02:00
Frédéric Péters f7913715a1 wcs: don't create crypto url for local path (#63476) 2022-04-01 17:51:38 +02:00
Frédéric Péters a1dd7f7569 translation update 2022-04-01 13:18:17 +02:00
Frédéric Péters 57dec72cb7 manager: use sidetabs to navigate between cell options (#62965)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-04-01 10:49:41 +02:00
Thomas NOËL fd6ae4e785 trivial: bump black version to 22.3.0 2022-03-31 12:16:57 +02:00
Valentin Deniaud 45f67ff368 dataviz: evaluate page variables in time range template field (#63181) 2022-03-31 10:35:16 +02:00
Lauréline Guérin 17742635e5
assets: fix delete and overwrite with bad filename (#63221)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-03-31 10:03:56 +02:00
Lauréline Guérin db8c99228d
dashboard: fix views when dashboard cell does not exist (#63233)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-03-28 16:52:01 +02:00
Nicolas Roche 0be1356882 notification: send a notification to several users (#60643) 2022-03-28 14:40:08 +02:00
Nicolas Roche 93d54bf08f tests: add test on add notification endpoint (#60643) 2022-03-28 14:40:08 +02:00
Valentin Deniaud d54a69c423 dataviz: translate received x_labels for months (#62530) 2022-03-28 11:27:00 +02:00
Valentin Deniaud 73e345a89d dataviz: hide future time range choices by default (#62862) 2022-03-28 11:09:22 +02:00
Frédéric Péters 6f040993b2 misc: add singleton accessor to site settings (#63223) 2022-03-28 09:46:09 +02:00
Frédéric Péters 07d52f797c misc: add required parameter when native 404 is called explicitely (#61816) 2022-03-25 09:29:57 +01:00
Frédéric Péters 749bcddd8f pylint: declare variable (#63176)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-03-25 08:03:28 +01:00
Frédéric Péters fd5230459a pylint: update render signature to match django (#63176) 2022-03-25 08:01:31 +01:00
Frédéric Péters e3f794cda0 build: add missing CompileError import (#62938) 2022-03-18 13:17:06 +01:00
Frédéric Péters 238b164a31 translation update 2022-03-15 22:03:51 +01:00
Lauréline Guérin ad3e0150e5
family: add button to booking form (#62244)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build queued... Details
2022-03-15 11:11:11 +01:00
Lauréline Guérin ebcdc01084
wcs: fix make_public_url templatetag used with simple Context (#62460)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build queued... Details
2022-03-15 10:02:14 +01:00
Frédéric Péters abae3b1a70
tests: add markup to string field value to check it's escaped properly (#61391)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build queued... Details
2022-03-15 09:25:23 +01:00
Lauréline Guérin ce69ea73d2
wcs: don't escape result of card cell custom fields (#61391) 2022-03-15 09:25:23 +01:00
Corentin Sechet 7288306d47 translation update 2022-03-09 08:25:02 +01:00
Corentin Sechet 888209400d dataviz: clearer name for filters time range (#62528) 2022-03-08 12:37:30 +01:00
Lauréline Guérin b39e5a9f57
misc: fix |has_role filter used on bad object (#62439)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-03-04 16:18:04 +01:00
Frédéric Péters c4de7aa569 dataviz: turn filters feature-flag on (#62417)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-03-04 09:10:45 +01:00
Frédéric Péters ae688c75e7 trivial: bump black version to 22.1.0 (#62312) 2022-03-01 19:30:32 +01:00
Frédéric Péters 5bf8f34912 translation update 2022-03-01 16:18:35 +01:00
Frédéric Péters 4214a6c324 misc: allow picking a custom view on card cell (#58878) 2022-03-01 15:52:00 +01:00
Valentin Deniaud 9d7cf579ce dataviz: allow page variable as filter value (#57616) 2022-03-01 15:28:16 +01:00
Valentin Deniaud 42eebde447 dataviz: use month short names instead of number in labels (#60738) 2022-03-01 15:18:36 +01:00
Frédéric Péters 035dc30548 misc: switch manager to scss (#62306) 2022-03-01 15:06:37 +01:00
Lauréline Guérin 443caa312d
translation update 2022-03-01 15:03:29 +01:00
Lauréline Guérin 9b94f12c2c
manager: duplicate cell and target a page (#60916)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-03-01 14:49:39 +01:00
Frédéric Péters 7a53141025 debian: add python3-distutils dependency, workaround for pyproj (#62269) 2022-02-28 17:59:35 +01:00
Valentin Deniaud 8d5df537b5 dataviz: set subfilter values in filters cell (#62227) 2022-02-28 13:28:17 +01:00
Frédéric Péters 78394b3962 wcs: allow "in your care" cell to be used in frontoffice (#58851) 2022-02-25 14:49:54 +01:00
Frédéric Péters c7530e8813 utils: add is_portal_agent function (#58851) 2022-02-25 14:49:54 +01:00
Frédéric Péters 75a5bac20a manager: add basic usage of native django permissions (#59505) 2022-02-25 14:47:58 +01:00
Lauréline Guérin a85d743b43
translation update 2022-02-25 10:53:43 +01:00
Serghei Mihai bf4c091ca1 wcs: uniformise form link and description (#62074) 2022-02-23 10:17:29 +01:00
Valentin Deniaud 898bf1d6b5 dataviz: avoid flagging extra time interval as unavailable (#62032) 2022-02-22 12:19:07 +01:00
Valentin Deniaud 02865103a9 dataviz: do not override time_range if not specified (#62030) 2022-02-21 17:31:32 +01:00
Valentin Deniaud e876fd6348 dataviz: do not update subfilters for bijoe cells (#61945) 2022-02-21 09:44:39 +01:00
Frédéric Péters ba79988743 debian: update django dependency to 2.2 2022-02-18 10:07:45 +01:00
Benjamin Dauvergne 78a28b6be9 views: pass ?next= to mellon views (#61430) 2022-02-18 08:47:25 +01:00
Benjamin Dauvergne 5accf65139 lingo: always log notification content on errors (#61401)
- rename models.PaymentException to LingoException to prevent confusion
- attach transaction to exceptions when possible
- log in views instead of model methods, it's a better point to make
  decisions on logging
2022-02-18 08:41:09 +01:00
Valentin Deniaud 30d1483f03 dataviz: add support for subfilters (#61083) 2022-02-15 17:01:14 +01:00
Frédéric Péters e42495fae1 trivial: distribute .svg file from dataviz templates directory (#61790) 2022-02-15 09:50:31 +01:00
Lauréline Guérin b5d82f30e4
wcs: use custom title as card cell additional label (#61040)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-02-14 20:57:11 +01:00
Valentin Deniaud 4244cfdd8f tox: move pylint to separate environment (#61493) 2022-02-14 09:55:43 +01:00
Valentin Deniaud 0f202b12fd dataviz: show filter value even if option is unavailable (#61607) 2022-02-14 09:54:34 +01:00
Mathieu Lirzin 18784da9a0 trivial: limit psycopg2 as django<2.3 doesn't work with newer versions (#61712)
https://github.com/django/django/pull/14530
2022-02-12 12:42:53 +01:00
Frédéric Péters 1a2bc922a4 trivial: update support django versions in requirements.txt 2022-02-12 08:58:03 +01:00
Mathieu Lirzin f4dc428e1e requirements: sync with setup.py 2022-02-12 08:55:08 +01:00
Mathieu Lirzin 631b5a20eb settings: fix documentation links
Documentation for Django 1.7 is not available online anymore.
2022-02-12 08:55:07 +01:00
Valentin Deniaud b64a4e6c08 data: fix link cell to url import (#61446) 2022-02-07 13:47:42 +01:00
Lauréline Guérin 757cd93c76
data: add page parameters (#59798)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-02-04 14:20:09 +01:00
Frédéric Péters 941ae66e2a translation update 2022-02-04 09:47:13 +01:00
Lauréline Guérin 4aa6747f4a wcs: explicit "id from URL" choice for card cell (#60957) 2022-02-04 07:51:25 +01:00
Lauréline Guérin c47c605e2f wcs: card_ids field width set to 100% (#60956) 2022-02-04 07:48:43 +01:00
Lauréline Guérin 2d8a0afeac cells: extra_css_class for links in link list cell (#61197) 2022-02-04 07:47:38 +01:00
Benjamin Dauvergne 1f4667c202 lingo: ignore empty response in return view (#61240) 2022-02-04 07:45:58 +01:00
Thomas Jund d6cfee4ec3 card-cell: use data-dynamic attr directly on fields group/parent (#61022) 2022-02-04 07:41:59 +01:00
Frédéric Péters bcdb70fff9 pwa: strip navigation to avoid trailing whitespaces from template (#61082) 2022-02-04 07:41:20 +01:00
Valentin Deniaud 4c6709c91e data: allow missing link_page on LinkCell import (#60320) 2022-01-31 13:10:31 +01:00
Frédéric Péters 287421122f misc: reorder migration (#58475) 2022-01-31 12:45:55 +01:00
Valentin Deniaud 850e99ac9b update translation 2022-01-31 11:56:33 +01:00
Valentin Deniaud 17400f7110 public: add page selector in site settings (#58475) 2022-01-31 11:38:16 +01:00
Thomas NOËL 6b1f7d1d4d lingo: refuse payment if an item cannot be trigged (#58210) 2022-01-28 17:33:04 +01:00
Lauréline Guérin 2c87ba2349
wcs: add |filter_by_number filter (#58782)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-01-28 14:06:29 +01:00
Frédéric Péters bc5dcf738c wcs: use <div> for all labels and values (#61033) 2022-01-28 09:59:38 +01:00
Valentin Deniaud cecde41751 dataviz: hide filters cell behind feature flag (#60915) 2022-01-24 11:18:23 +01:00
Lauréline Guérin 21cbc7ee35
wcs: fix card cell reverse relations (#60891)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-01-21 10:53:43 +01:00
Lauréline Guérin 9d4b785116
wcs: fix repeat_index in context in card cell (#60885)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-01-20 16:35:59 +01:00
Valentin Deniaud 9a41a5dc76 dataviz: fix #60857 test 2022-01-20 11:09:53 +01:00
Valentin Deniaud d2f084e609 dataviz: avoid crash in spooler if cell was deleted (#60857) 2022-01-20 10:52:29 +01:00
Valentin Deniaud f9e3823b85 translation update 2022-01-19 14:18:35 +01:00
Valentin Deniaud 5b16c4d292 dataviz: add new filters cell (#60547) 2022-01-19 13:59:18 +01:00
Valentin Deniaud 65407670c2 dataviz: move dynamic filters fields creation in mixin (#60547) 2022-01-19 13:59:18 +01:00
Valentin Deniaud 5d000519a8 dataviz: change time interval aggregation internals (#60547) 2022-01-19 13:59:18 +01:00
Valentin Deniaud 2d2f71f9cc dataviz: turn dataviz graph into class based view (#60547) 2022-01-19 13:59:18 +01:00
Valentin Deniaud 08ed56f90d data: add max_one_by_page cell class attribute (#60547) 2022-01-19 13:59:18 +01:00
Valentin Deniaud 465eaf9242 dataviz: allow selecting empty choice when field has default (#60734) 2022-01-19 13:58:53 +01:00
Valentin Deniaud 4fcef8f6c9 dataviz: set maximum number of scales in graphs (#60685) 2022-01-18 10:37:37 +01:00
Valentin Deniaud 85552f051a dataviz: fix typo in graphs font family (#60685) 2022-01-18 10:37:37 +01:00
Valentin Deniaud 7f97307941 dataviz: do not show floating point numbers when scaling (#60685) 2022-01-18 10:37:37 +01:00
Lauréline Guérin 67c42344a5
translation update 2022-01-18 09:55:07 +01:00
Lauréline Guérin 3973538812
family: subscribed mode, category & dates filtering (#58447)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-01-18 09:28:19 +01:00
Lauréline Guérin e21cef31af
translation update 2022-01-18 08:34:21 +01:00
Lauréline Guérin 1f0830ee01
wcs: fix js for card custom schema edition (#60707)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-01-17 09:54:53 +01:00
Thomas NOËL 4f0f331169 data: fake user-agent in link cell validitation (#60445) 2022-01-14 15:45:08 +01:00
Lauréline Guérin ea2a5b8521
translation update 2022-01-14 11:32:26 +01:00
Lauréline Guérin 15ae667a57
wcs: add |order_by filter (#60564)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-01-14 11:09:32 +01:00
Lauréline Guérin d4c8de1063
wcs: check related_card_path validity (#58833)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-01-14 10:58:23 +01:00
Lauréline Guérin 6b6040a667
wcs: get card ids from related (#58833) 2022-01-14 10:58:23 +01:00
Lauréline Guérin b605fae587
wcs: code factorization - get_card_data (#58833) 2022-01-14 10:58:23 +01:00
Lauréline Guérin 5df0ff15e0
wcs: build paths of related cards for form options (#58833) 2022-01-14 10:36:54 +01:00
Lauréline Guérin 367a3f0545
wcs: add link entry in card custom schema (#60370)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-01-14 09:48:31 +01:00
Lauréline Guérin 25bb7a4147
wcs: configure card field display in case of empty value (#58802)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-01-14 09:35:37 +01:00
Lauréline Guérin 1be114cafe
wcs: add subtitle display mode for card cell custom schema (#60369)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-01-14 09:13:11 +01:00
Lauréline Guérin 980fafe428
wcs: adapt card display with new schema (#58800)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-01-14 08:50:09 +01:00
Lauréline Guérin 3af712446a
wcs: split content and display_mode in card custom schema form (#58800) 2022-01-14 08:41:28 +01:00
Lauréline Guérin 223a9390cb
wcs: method to get migrated custom_schema (#58800) 2022-01-14 08:41:27 +01:00
Lauréline Guérin d666dd6f64
wcs: no div cell--body if custom schema is empty (#58326)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-01-14 08:25:44 +01:00
Emmanuel Cazenave fe2b81816b jenkins: show execution context in coverage reports (#60446)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-01-11 15:22:24 +01:00
Lauréline Guérin 8d1735134c
wcs: fix card cell display when card reference is not set (#60397)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2022-01-07 12:02:10 +01:00
Thomas NOËL 3a1b8aeade translation update 2022-01-06 14:50:02 +01:00
Thomas NOËL 82d3cede46 wcs: split tracking code presentation message (#60323) 2022-01-06 14:50:02 +01:00
Frédéric Péters 9e5fe63a9c search: increase allowed size for URLs (#60282) 2022-01-05 20:11:02 +01:00
Benjamin Dauvergne 02cda25460 wcs: add .clickable-rows to the "forms to process" table (#60309) 2022-01-05 19:46:54 +01:00
Frédéric Péters fd8bc9d51f tests: update error message test to match new python-requests version 2022-01-03 19:09:29 +01:00
Frédéric Péters c857d0a741 manager: fix spacing of "add search engine" section (#60042) 2022-01-03 18:50:51 +01:00
Frédéric Péters 6b0a078848 wcs: add ?cancelurl query parameter to links in forms of category cell (#58848) 2022-01-03 18:50:51 +01:00
Frédéric Péters c122f8892e wcs: add ?cancelurl query parameter to form links (#60114) 2022-01-03 18:50:51 +01:00
Lauréline Guérin 2ee49ebd45 wcs: add repeat_index in context (#59803)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2021-12-28 20:38:12 +01:00
Lauréline Guérin b6cb9194b7 wcs: change data logic in tests (#59803) 2021-12-28 20:29:01 +01:00
Lauréline Guérin 1e211d2643 wcs: use full context to render custom title and fields (#58735) 2021-12-28 15:19:20 +01:00
Lauréline Guérin c2cb07c2cc wcs: fix custom field template display when not defined (#59595) 2021-12-28 15:17:57 +01:00
Lauréline Guérin 4db81d3fce wcs: escape custom title (#59598) 2021-12-28 15:17:46 +01:00
Serghei Mihai 541ac61eba wcs: allow tracking code input placeholder override (#59983) 2021-12-22 09:30:38 +01:00
Frédéric Péters ec9ff0c493 wcs: urlize card content field values (#59931) 2021-12-20 16:32:42 +01:00
Frédéric Péters 99b0682efb build: update setup.py to require at least django 2.2 2021-12-19 16:55:35 +01:00
Lauréline Guérin 1c5cd8d562
wcs: add |filter_by_internal_id filter (#59801)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2021-12-17 15:13:28 +01:00
Lauréline Guérin 958bd2128c
translation update 2021-12-14 17:36:42 +01:00
Lauréline Guérin 39c7114cf7
wcs: card cell & multiple card identifiers (#58862)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2021-12-14 17:05:38 +01:00
Lauréline Guérin 09c30d619c
data: link cell url max_length 2000 (#59643)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build queued... Details
2021-12-14 09:17:38 +01:00
Frédéric Péters 15baba11f1 family: make sure previous week button has some margin (#59653) 2021-12-14 08:56:55 +01:00
Frédéric Péters b4074e6eef family: add a translatable label to family app (#59652) 2021-12-14 08:56:55 +01:00
Frédéric Péters 8abed78fa4 maps: don't call leaflet on map cell configuration panel (#59590) 2021-12-12 08:52:26 +01:00
Frédéric Péters 91ae6e2898 wcs: reduce some cache durations (#58462) 2021-12-12 08:52:26 +01:00
Frédéric Péters 95851f2b59 maps: add js trigger event to render maps (#59409) 2021-12-11 17:12:24 +01:00
Lauréline Guérin 232afadc29
gallery: return 404 if cell of image is not found (#58900)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2021-12-10 08:39:03 +01:00
Lauréline Guérin 6bdd45e321
misc: fix pylint error in tests 2021-12-07 08:50:52 +01:00
Frédéric Péters 0a055561c5 debian: remove transitional/obsolete dh-systemd dependency 2021-12-03 15:37:45 +01:00
Frédéric Péters a4e6fae909 jenkins: build packages for buster & bullseye 2021-12-03 15:25:22 +01:00
Frédéric Péters c814cdf888 maps: add django block to drive data-include-geoloc-button (#59213) 2021-12-03 09:05:51 +01:00
Frédéric Péters 4176ba8b30 misc: check access rights using is_superuser, not is_staff (#59186) 2021-12-01 15:41:36 +01:00
Benjamin Dauvergne d87a189500 lingo: use distinct when querying transactions through items (#59103) 2021-11-29 23:04:51 +01:00
Benjamin Dauvergne 3b39035e88 search: use description template from settings for users (#58548) 2021-11-29 10:29:36 +01:00
Lauréline Guérin 4a1937c4b6
family: reduce cache duration for weekly agenda cell (#58962)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2021-11-26 14:07:17 +01:00
Frédéric Péters ee5a7f91e0 search: index links from "list of links" cells (#59025) 2021-11-26 14:05:40 +01:00
Frédéric Péters 52a05d380b pylint: disable use-implicit-booleaness-not-comparison (same as #59021) 2021-11-25 21:09:26 +01:00
Frédéric Péters c35480431c fix doubled word in translation (#59024) 2021-11-25 20:52:33 +01:00
Frédéric Péters 537f45400e misc: allow calling {% make_public_url %} on empty or non-publik URLs (#58903) 2021-11-23 16:30:35 +01:00
Valentin Deniaud 217fa7541c update translation 2021-11-23 11:17:03 +01:00
Frédéric Péters 66a717f737 build: bump black version 2021-11-22 22:07:34 +01:00
Frédéric Péters ac42ed947d misc: fix typo in redirection info message translation tag (#58783) 2021-11-19 22:57:19 +01:00
Frédéric Péters 5969b5a7e6 translation update 2021-11-18 23:09:28 +01:00
Valentin Deniaud a00f7e1d5b data: include site settings in import/export (#57923) 2021-11-18 10:05:14 +01:00
Valentin Deniaud 0e9e217d90 manager: mention template only on absolute redirect url (#58073) 2021-11-18 10:00:49 +01:00
Valentin Deniaud 1695a760ce manager: show apply_to_subpages only if page has children (#58115) 2021-11-18 09:59:56 +01:00
Valentin Deniaud 204bc4b1bd manager: show apply_to_subpages only if not included in navigation (#58116) 2021-11-18 09:59:15 +01:00
Frédéric Péters 40013a2e56 translation update 2021-11-16 22:41:39 +01:00
Valentin Deniaud 3e8d664b37 maps: move layer request parameter options to separate form (#57761) 2021-11-16 17:31:40 +01:00
Valentin Deniaud efa733b2cf maps: duplicate map layer options (#57760) 2021-11-16 17:31:21 +01:00
Valentin Deniaud 04a4ff60d9 maps: move properties from layer to cell (#57760) 2021-11-16 17:31:21 +01:00
Valentin Deniaud 32a7ad0cf5 dataviz: allow only loop labels in response (#58438) 2021-11-16 17:21:30 +01:00
Valentin Deniaud a05a87ebc5 maps: rename marker color options (#57757) 2021-11-16 17:20:13 +01:00
Benjamin Dauvergne fe64dcf946 maps: keep the marker's colour property (#58072) 2021-11-16 13:09:08 +01:00
Paul Marillonnet 3a445d0b46 public: render template in initial login and welcome page urls (#58474) 2021-11-16 09:11:05 +01:00
Lauréline Guérin 45d44e0a1c
wcs: custom field for card cell (#57134)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Something is wrong with the build of this commit Details
2021-11-15 09:34:47 +01:00
Lauréline Guérin d766241f47
manager: fix export of link cell to internal page with redirect (#57707)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-11-14 14:16:15 +01:00
Lauréline Guérin 9a288e01b1
family: agenda_references_template field (#57927)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-11-14 13:54:02 +01:00
Lauréline Guérin d41302e20b
manager: redirect to new cell after cell duplicate (#58186)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-11-12 15:16:24 +01:00
Lauréline Guérin d983ecd850
wcs: empty card cell if card_id result is empty (#58413)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-11-12 14:45:09 +01:00
Nicolas Roche d49d0a7541 dashboard: correct add tile ordering (#58528) 2021-11-12 12:09:38 +01:00
Lauréline Guérin bbfc19618a
wcs: custom title for current forms cell (#57966)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-11-12 10:09:53 +01:00
Frédéric Péters f8138782f1 wcs: display email fields as mailto: links (#57954) 2021-11-12 09:04:06 +01:00
Frédéric Péters 8f02cb2fc2 wcs: add support for linking/displaying card file fields (#51994) 2021-11-12 09:03:54 +01:00
Lauréline Guérin df25b3a828
wcs: card cell, card_id increase max_length (#58412)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build queued... Details
2021-11-05 11:35:09 +01:00
Thomas NOËL 1673d2def7 settings: add wcs templatetags by default (#58415) 2021-11-05 11:32:27 +01:00
Valentin Deniaud a67233d6f5 search: index external links data independently (#58269) 2021-11-04 18:09:33 +01:00
Valentin Deniaud 3dd41a03db public: handle snapshot of redirected page (#57786) 2021-11-04 14:38:02 +01:00
Valentin Deniaud a4b2cecd0f maps: handle empty geojson (#58283) 2021-11-02 18:37:11 +01:00
Frédéric Péters a3d180eee9 translation update 2021-11-02 17:13:51 +01:00
Thomas NOËL 3b5b7081e2 lingo: clarify message on paid invoice (#57952) 2021-10-29 18:36:00 +02:00
Lauréline Guérin 71a362bb14
family: clean app, remove unused models (#56015)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build queued... Details
2021-10-29 09:34:13 +02:00
Valentin Deniaud b5e7234acd dataviz: fix templated time range fields display (#58117) 2021-10-27 12:12:06 +02:00
Valentin Deniaud 457fc9e47f data: allow blank values for urls in site settings (#58152) 2021-10-25 14:55:28 +02:00
Valentin Deniaud 4fd2151477 manager: handle unknown page slug in redirect to edit view (#58117) 2021-10-25 12:25:48 +02:00
Benjamin Dauvergne 74e33cdc80 lingo: add regie's label to transaction's CSV report (#58134) 2021-10-23 12:05:24 +02:00
Frédéric Péters bbaa7de67c update translation for new welcome/initial page settings 2021-10-22 14:56:57 +02:00
Frédéric Péters e01aba7b38 trivial: invert order of site parameters (#58086) 2021-10-22 10:41:56 +02:00
Benjamin Dauvergne 76d90b31d2 templates: replace STATIC_URL by static directive (#57851) 2021-10-21 20:28:14 +02:00
Benjamin Dauvergne 0b9bfe9b68 templates: stop using staticfiles template tag library (#57851) 2021-10-21 20:28:14 +02:00
Benjamin Dauvergne f92b855774 lingo: poll backend during asynchronous rendering (#57790) 2021-10-21 08:20:28 +02:00
Benjamin Dauvergne 0aac640e37 lingo: remove uselesss csrftoken directive (#57850) 2021-10-21 08:20:12 +02:00
Benjamin Dauvergne 65ed19ffaa lingo: remove closing label tag (#57850) 2021-10-21 08:20:12 +02:00
Frédéric Péters 360f47c2d1 translation update 2021-10-19 17:58:19 +02:00
Valentin Deniaud 4c7c20ac95 data: add site settings model to configure welcome urls (#15846) 2021-10-18 10:20:39 +02:00
Valentin Deniaud d13e801e24 dataviz: add select multiple options support (#57818) 2021-10-18 10:14:00 +02:00
Valentin Deniaud 9f6d68477a dataviz: allow setting time range using template (#57617) 2021-10-18 10:13:17 +02:00
Valentin Deniaud 981f2ee8a1 manager: add redirect view from frontoffice to backoffice (#57672) 2021-10-18 10:11:37 +02:00
Valentin Deniaud 18fcc5a35d public: move page resolve code to utils (#57672) 2021-10-18 10:11:37 +02:00
Valentin Deniaud 0b33600a00 manager: allow applying include in navigation to subpages (#56793) 2021-10-18 10:09:17 +02:00
Valentin Deniaud 937b1c2d12 reword dataviz translation (#55160) 2021-10-18 10:04:15 +02:00
Lauréline Guérin aa85ec295a
lingo: remove callback_url on backend page (#56650)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-10-15 09:15:15 +02:00
Lauréline Guérin a191ea942b
lingo: remove white background for transaction pages (#56651)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
transactions and failing transactions
2021-10-15 08:12:54 +02:00
Lauréline Guérin 54c277a36e
lingo: filter transaction list by regie (#56651) 2021-10-15 08:12:54 +02:00
Frédéric Péters f765f0be8a debian: switch to debhelper-compat 12 (#57538) 2021-10-09 18:58:02 +02:00
Lauréline Guérin e415373110
cells: jsoncell log error on syntax error only (#57626)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-10-07 11:44:51 +02:00
Frédéric Péters e1dcad5831 translation update 2021-10-05 22:06:57 +02:00
Thomas Jund 377cbcc955 search-cell: improve markup to target button and button label (#57570) 2021-10-05 17:37:59 +02:00
Lauréline Guérin 4262533fec
cells: log level error if error in url (jsoncell) (#56979)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-10-05 10:56:08 +02:00
Lauréline Guérin bee691aa15
search: title for search cell (#57323)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-10-05 10:38:54 +02:00
Lauréline Guérin 8e577d006f
family: display past events in weekly agenda cell (#57238)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-10-05 09:37:49 +02:00
Nicolas Roche eb11afca1b pwa: allow absolute redirection url from internal page (#56974) 2021-10-05 09:18:46 +02:00
Lauréline Guérin 541aed2379
misc: simplify lingo manager tests
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-10-01 15:34:23 +02:00
Lauréline Guérin 81ef39a3aa
lingo: show return url on regie edit page (#56649) 2021-10-01 15:34:22 +02:00
Lauréline Guérin 14b4adc38c
lingo: change log level when payment backend is not found (#56647)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-10-01 10:07:25 +02:00
Lauréline Guérin 5c566a3b0d
wcs: pagination configuration for cards cell (#57322)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-10-01 08:39:04 +02:00
Lauréline Guérin 013248d96e
lingo: callback_url is not displayed as a link (#56646)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-09-30 17:10:15 +02:00
Frédéric Péters 2a26f13c94 translation update 2021-09-28 19:20:36 +02:00
Frédéric Péters a4bb3ba324 maps: add possibility to set marker colour from a geojson property (#57296) 2021-09-28 10:42:51 +02:00
Frédéric Péters a6d8e779a3 maps: extend marker_colour to also be used for surfaces (#57279)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-09-27 10:21:42 +02:00
Frédéric Péters 9fa168c306 wcs: format text fields content like wcs (#56422) 2021-09-24 08:25:04 +02:00
Frédéric Péters 11b4ffca38 utils: do not require maps and pwa apps for export/import support (#56930) 2021-09-24 08:24:15 +02:00
Frédéric Péters c5670f97bb translation update 2021-09-21 19:18:53 +02:00
Frédéric Péters f4cafbc8be manager: pass over deleted fields in custom cell layout js (#57082) 2021-09-20 16:40:30 +02:00
Thomas NOËL 6fc3137064 translation update 2021-09-17 18:24:00 +02:00
Thomas NOËL 763c84e496 wcs: clarify name of "forms user can access" option (#57061) 2021-09-17 18:15:42 +02:00
Paul Marillonnet 754b1fdead pylint: discard frowned-upon uses of dict.keys() (#57044) 2021-09-17 12:48:36 +02:00
Paul Marillonnet 2614645d1d pylint: disable consider-using-f-string
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-09-17 12:28:10 +02:00
Valentin Deniaud 6bb24b1611 data: fix import of nested subpages (#56794) 2021-09-13 10:19:54 +02:00
Lauréline Guérin b62f5537bd
wcs: card cell title can be empty (#56584)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-09-10 15:11:31 +02:00
Lauréline Guérin cb7e32cd2e
wcs: option to get forms user can access in current form cell (#56477)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-09-10 14:06:19 +02:00
Frédéric Péters 69425c6200 general: handle /manage/ access to users with page edit roles (#56188)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-09-10 11:19:23 +02:00
Frédéric Péters af5caa199a general: attach edit_role and subpages_edit_role attributes to Page (#56188) 2021-09-10 10:11:56 +02:00
Lauréline Guérin 90aa90b991
family: user_external_template of agenda cell (#56730)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-09-07 11:10:18 +02:00
Lauréline Guérin 389465cb57
family: add also *.css in MANIFEST (#56720) 2021-09-07 10:41:39 +02:00
Lauréline Guérin 2bf33bba1c
family: add statics in MANIFEST (#56720)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-09-07 10:16:36 +02:00
Frédéric Péters 661a387db0 wcs: remove extra enclosing div in card info cell (#56596) 2021-09-07 10:13:12 +02:00
Frédéric Péters d0c5c209d2 translation update 2021-09-03 17:33:48 +02:00
Frédéric Péters 32dc12f446 family: switch original string to English 2021-09-03 17:33:32 +02:00
Lauréline Guérin 5d94130a44
family: weekly agenda cell (#56027)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-09-03 15:10:32 +02:00
Frédéric Péters 85f3ac7733 misc: directly give redirect url as target if possible (#56308) 2021-09-03 14:40:56 +02:00
Thomas Jund 5f34c09945 wcs: fix bad end div tag position on card cell (#56609)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-09-02 17:44:50 +02:00
Emmanuel Cazenave 44e00bc087 uwsgi: enable provisionning spooler (#55092) 2021-08-31 12:23:10 +02:00
Lauréline Guérin b060a3ab61
misc: fix reimported pylint error (#56288)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-08-31 10:06:48 +02:00
Lauréline Guérin 03d672a313
misc: fix undefined-variable pylint error (#56288) 2021-08-31 10:06:48 +02:00
Lauréline Guérin e61b40246e
misc: fix unnecessary-lambda pylint error (#56288) 2021-08-31 10:06:48 +02:00
Lauréline Guérin bb1487919a
misc: fix tests/test_lingo_remote_regie.py pylint error (#56288) 2021-08-31 10:06:48 +02:00
Lauréline Guérin b8d98ef862
misc: fix useless-return pylint error (#56288) 2021-08-31 10:06:48 +02:00
Lauréline Guérin d384b4dd95
misc: fix expression-not-assigned pylint error (#56288) 2021-08-31 10:06:47 +02:00
Lauréline Guérin ed8ca23d0c
misc: fix consider-iterating-dictionary pylint error (#56288) 2021-08-31 10:06:47 +02:00
Lauréline Guérin f16a542959
misc: fix raising-format-tuple pylint error (#56288) 2021-08-31 10:06:47 +02:00
Lauréline Guérin 3bf3327684
misc: fix dangerous-default-value pylint error (#56288) 2021-08-31 10:06:47 +02:00
Lauréline Guérin e3631bc285
misc: fix simplifiable-if-expression pylint errors (#56288) 2021-08-31 10:06:47 +02:00
Lauréline Guérin f334721b8c
misc: fix simplifiable-condition pylint error (#56288) 2021-08-31 10:06:47 +02:00
Lauréline Guérin da61304c4c
misc: fix function-redefined pylint error (#56288) 2021-08-31 10:06:47 +02:00
Lauréline Guérin 6e1c8b4f84
misc: fix consider-using-enumerate pylint error (#56288) 2021-08-31 10:06:47 +02:00
Lauréline Guérin 9ce912f39b
misc: fix missing-parentheses-for-call-in-test pylint error (#56288) 2021-08-31 10:06:47 +02:00
Lauréline Guérin 7a96ec095e
misc: fix bad-classmethod-argument pylint error (#56288) 2021-08-31 10:06:46 +02:00
Lauréline Guérin d9a0c58b07
misc: fix no-name-in-module pylint error (#56288) 2021-08-31 10:06:46 +02:00
Lauréline Guérin 7ef686fd79
misc: fix single-string-used-for-slots pylint error (#56288) 2021-08-31 10:06:46 +02:00
Lauréline Guérin 09bcda4d89
misc: fix no-else-break pylint error (#56288) 2021-08-31 10:06:46 +02:00
Lauréline Guérin fd6068163c
misc: fix bare-except pylint error (#56288) 2021-08-31 10:06:46 +02:00
Lauréline Guérin 000af39adf
misc: fix invalid-str-returned pylint error (#56288) 2021-08-31 10:06:46 +02:00
Lauréline Guérin 7545d99111
misc: fix no-else-raise pylint error (#56288) 2021-08-31 10:06:46 +02:00
Lauréline Guérin 48369ae634
misc: fix logging-not-lazy pylint error (#56288) 2021-08-31 10:06:46 +02:00
Lauréline Guérin fcf2778e99
misc: fix consider-using-with pylint error (#56288) 2021-08-31 10:06:46 +02:00
Lauréline Guérin ea10ae6b98
misc: fix import-error pylint error (#56288) 2021-08-31 10:06:45 +02:00
Lauréline Guérin aa12256f47
misc: fix line-too-long pylint error (#56288) 2021-08-31 10:00:39 +02:00
Lauréline Guérin 6bc2e43f12
misc: fix singleton-comparison pylint error (#56288) 2021-08-31 10:00:39 +02:00
Lauréline Guérin 02d5299c9a
misc: fix unused-variable pylint error (#56288) 2021-08-31 10:00:38 +02:00
Lauréline Guérin 9c3918ee98
misc: fix wrong-import-position pylint error (#56288) 2021-08-31 10:00:38 +02:00
Lauréline Guérin ee21324c66
misc: fix unused-import pylint error (#56288) 2021-08-31 10:00:38 +02:00
Lauréline Guérin d7e4849276
build: make it fail in case of pylint warnings (#56288) 2021-08-31 10:00:38 +02:00
Lauréline Guérin d0e444ee7e
misc: integrate pylint.rc in repo (#56288) 2021-08-31 10:00:38 +02:00
Valentin Deniaud e717381c85 misc: add |sum template filter (#56380) 2021-08-30 10:29:41 +02:00
Lauréline Guérin 9050ab6cd2
wcs: hamonize empty-message (#56190)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-08-30 10:02:18 +02:00
Valentin Deniaud 98b8e5aa63 Revert "wcs: option to get forms user can access in current form cell (#56012)" (#56387)
This reverts commit 299429195c and f8aae6b197.
2021-08-26 12:23:41 +02:00
Valentin Deniaud f8aae6b197 wcs: fix typo in forms api filter parameter (#56363) 2021-08-25 12:41:07 +02:00
Frédéric Péters 2ea17c49e6 translation update 2021-08-19 20:27:52 +02:00
Lauréline Guérin d77af71764
wcs: mark cell as invalid if wcs_site is unknown (#56200)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-08-19 14:42:20 +02:00
Frédéric Péters be732f5c9c translation update 2021-08-18 20:39:57 +02:00
Lauréline Guérin 299429195c
wcs: option to get forms user can access in current form cell (#56012)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-08-17 14:08:11 +02:00
Lauréline Guérin fe64a49258
wcs: fix cards list css class (#56029)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-08-17 10:00:50 +02:00
Lauréline Guérin 7c1821ce35
wcs: fix tracking code with unknown wcs_site (#54195)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-08-16 19:02:09 +02:00
Lauréline Guérin 42e11b6cef
wcs: fix empty-message for cards (#56028)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-08-16 16:54:32 +02:00
Frédéric Péters 23c3b5d066 debian: tell collectstatic to create symlinks (#56037) 2021-08-16 14:32:16 +02:00
Frédéric Péters b5e6aa907f misc: add custom makemessages command to skip line numbers by default (#56057) 2021-08-09 13:06:24 +02:00
Lauréline Guérin 0d353c15cc wcs: add migration to remove table (#56014) 2021-08-06 14:21:19 +02:00
Lauréline Guérin 4dcd011cae Revert "wcs: cell for forms the user can access (#55041)"
This reverts commit 18bb48c5b3.
2021-08-06 14:21:19 +02:00
Frédéric Péters cb5966dc71 translation update 2021-08-06 09:48:42 +02:00
Frédéric Péters f285d5cf80 wcs: add labels before layout/size values (#55860) 2021-08-05 22:33:04 +02:00
Benjamin Dauvergne 18e4d09053 lingo: make BasketItem creation idempotent for paid invoices (#55876)
Using get_or_create() based on the remote_item_id field inside a
transaction is idempotent, the @self.items.add(...)@ operation also (if
the item is already linked it does nothing).

The new behaviour garantee that even if we cannot notify the regie's
web-service, a BasketItem is created to show the user its payment has
been recorded.
2021-08-04 18:29:23 +02:00
Benjamin Dauvergne ef770b4579 lingo: add remote_item_id field to BasketItem (#55876) 2021-08-04 18:29:11 +02:00
Benjamin Dauvergne 050df6b0a1 lingo: factorize basket item creation for paid remote invoices (#55876) 2021-08-04 18:29:11 +02:00
Paul Marillonnet c5d17a82a5 translation fix 2021-08-04 16:18:55 +02:00
Lauréline Guérin 70d699b0a4
misc: add pyupgrade files/notes (#55868)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-07-27 16:46:20 +02:00
Lauréline Guérin f4615c5061
misc: apply pyupgrade (#55868) 2021-07-27 16:45:52 +02:00
Frédéric Péters 8514d0d00e wcs: remove useless dialog titles in card grid settings (#55863) 2021-07-27 15:17:28 +02:00
Frédéric Péters e74705144e translation typo fix 2021-07-27 15:03:25 +02:00
Lauréline Guérin fe4a5c59e3
maps: don't fail is geojson url is malformed (#55468)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-07-27 14:38:39 +02:00
Lauréline Guérin 14e3aa9f02
wcs: card custom title as a template (#55843)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-07-27 10:57:04 +02:00
Lauréline Guérin 666dacd26e
manager: fix page ordering with missing page from arguments (#55288)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-07-27 09:23:18 +02:00
Lauréline Guérin d1cd349cce
cells: check tempate_string is defined in configjsoncell settings (#55647)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-07-26 19:27:14 +02:00
Frédéric Péters dc02e65e18 translation update 2021-07-26 18:05:45 +02:00
Thomas Jund 93ea996af9 wcs: add support for custom content layout (#52073) 2021-07-26 15:18:45 +02:00
Lauréline Guérin 9761e251c5 wcs: add fields to customize Card display (#54259) 2021-07-26 15:18:45 +02:00
Lauréline Guérin 18bb48c5b3
wcs: cell for forms the user can access (#55041)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-07-26 14:56:17 +02:00
Frédéric Péters 86be2bb861 general: allow custom cell templates to define extra classes (#55792)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-07-26 10:17:28 +02:00
Frédéric Péters 7942c379ca general: use selected cell template to render cell (#55792) 2021-07-26 10:17:28 +02:00
Frédéric Péters 8fe4109d3f manage: add cell template selection in options dialog (#55792) 2021-07-26 10:17:28 +02:00
Frédéric Péters b19d5bcd19 general: introduce new template_name attribute on cells (#55792) 2021-07-26 10:17:28 +02:00
Frédéric Péters f823e6cbba general: rename cell template_name attribute to default_template_name (#55792) 2021-07-26 10:16:42 +02:00
Lauréline Guérin 22ce180d68
wcs: custom title for card cell (#55335)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-07-26 09:15:36 +02:00
Frédéric Péters 64859f4837 misc: add description to form & link cell contexts (#55776) 2021-07-23 16:56:03 +02:00
Frédéric Péters 1f871c2e6e wcs: add custom loading messages for drafts and forms (#55774) 2021-07-23 08:34:07 +02:00
Frédéric Péters ec871b6ba6 lingo: do not crash on missing invoice creation or payment limit dates (#55686) 2021-07-20 13:08:45 +02:00
Valentin Deniaud 56efce9b00 dataviz: fix time filters (#55621) 2021-07-19 12:10:45 +02:00
Frédéric Péters 7d584ee086 translation update 2021-07-15 20:25:37 +02:00
Valentin Deniaud aac7f4c63a dataviz: add week filters (#55417) 2021-07-15 15:47:43 +02:00
Valentin Deniaud dbc0fee3ad dataviz: do not crash on missing statistic url (#54862) 2021-07-15 10:25:38 +02:00
Lauréline Guérin ce5d2fbfca
misc: add |adjust_to_week_monday & |iterate_days_until filters (#55550)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-07-13 09:27:33 +02:00
Benjamin Dauvergne a23696d047 jsoncell: do not traceback if content-type is absent (#55360) 2021-07-12 10:02:08 +02:00
Frédéric Péters 88f0138995 manager: add validation of redirect url template syntax (#55363)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-07-03 12:54:38 +02:00
Frédéric Péters 75a0f2dab7 tox: stop testing against django 1.11 2021-07-02 14:33:42 +02:00
Frédéric Péters f7d517f789 settings: declare passerelle as a provider of statistics (#53856) 2021-07-02 07:55:45 +02:00
Frédéric Péters d2e9c2ed13 translation update 2021-07-01 21:06:49 +02:00
Lauréline Guérin 2911191e7e
data: change str of ConfigJsonCell model and use label (#55114)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-06-29 18:58:15 +02:00
Lauréline Guérin aaee753d02
data: help_text for sub_slug (#55103)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2021-06-29 15:25:57 +02:00
Benjamin Dauvergne 6668215594 lingo: make URL static in TipiPaymentFormCell (#55244) 2021-06-29 10:35:57 +02:00
Benjamin Dauvergne ed10d440fd lingo: fix help_text in migration (#55244) 2021-06-28 18:51:33 +02:00
Nicolas Roche 9ff0710b51 lingo: mark 'invoices' div with pk-table-wrapper CSS class (#54980) 2021-06-28 10:04:53 +02:00
Lauréline Guérin 48591c00cc
wcs: custom title for cards cell (#54549)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-06-25 14:18:01 +02:00
Frédéric Péters ec1b6ec146 tox: limit psycopg2 to < 2.9 (#54925) 2021-06-17 08:53:51 +02:00
Serghei Mihai d4df555e8b search: index page for cells with external links (#54763) 2021-06-16 09:57:03 +02:00
Frédéric Péters 65da4dd24e general: do not preload tile cells when rendering cell (#54882) 2021-06-15 15:00:45 +02:00
Thomas NOËL f5918c45da lingo: update default TIPI payment service URL (#47537) 2021-06-15 11:20:38 +02:00
Frédéric Péters 188be78eaf general: do not preload tile cells when rendering page (#54592) 2021-06-07 13:10:05 +02:00
Valentin Deniaud b023480505 settings: add chrono as statistics provider (#54562) 2021-06-04 07:16:16 +02:00
Frédéric Péters e7dbed7748 translation update 2021-06-03 21:58:59 +02:00
Lauréline Guérin 76f3bfa1df
misc: remove sqlite support (#52912)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-06-03 10:24:23 +02:00
Lauréline Guérin 657815e82a
misc: remove django-jsonfield dependency (#52912) 2021-06-03 10:24:21 +02:00
Thomas Jund 44ee86eded JS: add events 'cell:open' & 'cell:close' (#53780) 2021-06-01 15:53:42 +02:00
Thomas Jund 7f80d29f0e JS: move compute_max_height function to global scope (#53778) 2021-06-01 15:50:53 +02:00
Frédéric Péters 07a81ad81d
cells: json cell params (repeat, make_global, template_string) (#54236)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-05-28 11:02:51 +02:00
Lauréline Guérin 6b2b381a54
wcs: add an option to ignore user for cards cell (#53947)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-05-28 09:09:56 +02:00
Lauréline Guérin 9257955373
wcs: use category_slugs filter in wcs calls (#53376)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-05-21 11:12:55 +02:00
Frédéric Péters 1d209f2a5e translation update 2021-05-19 09:58:40 +02:00
Valentin Deniaud 4f17d61c93 dataviz: do not try to aggregate empty data (#53899) 2021-05-18 16:30:27 +02:00
Lauréline Guérin 9303dbc862
wcs: add some non-regression tests (#53213)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
cells on specific category or card: be sure that only the correct site
is requested
2021-05-17 11:43:56 +02:00
Lauréline Guérin 085e729f6d
wcs: add categories field to CareForms cell (#53213) 2021-05-17 11:43:56 +02:00
Benjamin Dauvergne 1822b1f3c7 lingo: use payment backend's slug in default callback URL (#49145) 2021-05-10 12:19:16 +02:00
Benjamin Dauvergne a8676ab897 lingo: add callback through payment backend's slug (#49145) 2021-05-10 12:19:16 +02:00
Benjamin Dauvergne 2bfb96ea95 lingo: show backend callback URL in update views (#49145) 2021-05-10 12:19:16 +02:00
Benjamin Dauvergne 829fc79e8e debian: add --all-backends option to lingo-poll-backend (#53861) 2021-05-07 18:06:07 +02:00
Benjamin Dauvergne 2e6b13399f lingo: prevent unexpected output in lingo-poll-backend (#53833)
This command is used with cron, so any output would produce unwanted
mails.
2021-05-06 20:15:13 +02:00
Benjamin Dauvergne 0853b3152a debian: fix typo in cron.d 2021-05-06 18:42:36 +02:00
Thomas NOËL f2a6aa4a49 translation update 2021-05-06 17:58:03 +02:00
Benjamin Dauvergne 760ccb41fd lingo: add poll_backend method to PaymentBackend and Transaction (#49149)
Some payment backends in eopayment (like PayFiP) allow polling the
status of currently running transaction, and can signal if a running
transaction has expired. The new can_poll_backend() and poll_backend()
method on Transaction implement this conditional behaviour in lingo.
2021-05-06 11:04:08 +02:00
Benjamin Dauvergne 5b5d046414 misc: add setting to force synchronous rendering of cells (#49149)
The setting is used through a new fixture, `synchronous_cells`. It's
necessary to test views with cells which always use ajax rendering
without an headless browser or using the request factory.
2021-05-06 10:34:04 +02:00
Benjamin Dauvergne 99a912e255 lingo: factorize eopayment response handling (#49149) 2021-05-06 00:31:15 +02:00
Benjamin Dauvergne 428033da0f pwa: use icon file basename during import (#53720)
Django 2.2.21 introduced the validation of FieldFile.save() name
argument, which cannot contain a path separator anymore. To use the
received FileField value as a base filename, we must apply
os.path.basename() on it first.

ref. https://docs.djangoproject.com/en/3.2/releases/2.2.21/
2021-05-04 16:01:46 +02:00
Benjamin Dauvergne d623ff4d8e jsoncell: copy Content-Disposition in raw cell's actions (#53666) 2021-05-04 11:34:50 +02:00
Thomas Jund b5463fb46a template: add .cell--body class to notification-cell (#53501) 2021-05-03 17:11:28 +02:00
Lauréline Guérin d9e1b61ecb
dataviz: fix sorting when data is empty (#53596)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-05-03 10:09:37 +02:00
Frédéric Péters db649f7d04 translation update 2021-04-30 18:00:47 +02:00
Lauréline Guérin 85d3f77a7f
misc: remove newsletter app (#53541)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-04-30 10:56:36 +02:00
Benjamin Dauvergne 2a151c45d5 tests: fix warning about uncollectable TestApp (#53577) 2021-04-29 23:12:45 +02:00
Benjamin Dauvergne b11518807c misc: fix warning about invalid escape sequences in regexps (#53577) 2021-04-29 23:12:45 +02:00
Benjamin Dauvergne 8855869cf8 misc: fix warning about unclosed resource in import-site (#53577) 2021-04-29 23:12:45 +02:00
Lauréline Guérin ea21ad5b00
map: don't fail on invalid geojson data (#53521)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-04-29 14:41:44 +02:00
Lauréline Guérin a71c673437
manager: page subslug style (#53221)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-04-29 09:56:10 +02:00
Lauréline Guérin 03f1ac9cd0
wcs: add card schema in manager form (#52498)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-04-29 08:56:16 +02:00
Thomas Jund e939770c0c templates: improve user lists of forms markup (#53234) 2021-04-28 10:33:06 +02:00
Valentin Deniaud 9ca7e0c17e dataviz: aggregate received data by time intervals (#53180) 2021-04-20 17:11:39 +02:00
Frédéric Péters 3bce1589f1 fix typo in translation (#53285) 2021-04-20 14:14:09 +02:00
Benjamin Dauvergne c1d958ffee translation update 2021-04-16 12:10:53 +02:00
Benjamin Dauvergne ccb2cb8500 lingo: check real payment status of remote_item when shown or paid (#53186)
Currently the remote_item.paid attribute comes from what the remote
regie is providing through a web service. But a remote_item can be
already paid if a transaction with a paid status (eopayment.PAID or
eopayment.ACCEPTED) already exist.

This patch add a RemoteItem.update_paid(regie, remote_items) which
update bunch of remote_item(s) by querying the correponding paid
transactions.

The methods Regie.get_invoices() and Regie.get_invoice() get a new
parameter update_paid=False, which is set to true in cells and views
which display or allow paying a remote item.
2021-04-15 21:24:01 +02:00
Frédéric Péters 39bc369443 tests: do not run dataviz job in cron tests 2021-04-13 14:43:05 +02:00
Thomas Jund 41f1e52ce0 profile-cell: move div content wrapper after cell title (#52968) 2021-04-13 14:28:18 +02:00
Frédéric Péters 98379eb839 dataviz: force graph redraw on initial display (#52959) 2021-04-13 14:27:06 +02:00
Frédéric Péters 3879710d48 settings: add wcs as statistics provider (#52731) 2021-04-13 14:27:06 +02:00
Valentin Deniaud 434ac12426 dataviz: add stacked bar percent chart type (#52845) 2021-04-13 13:49:38 +02:00
Frédéric Péters 15651beb91 translation update 2021-04-13 13:46:46 +02:00
Lauréline Guérin 6b2d3159a8
search: configure user search result template (#52439)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-04-12 15:26:21 +02:00
Lauréline Guérin 9ddf332711
search: add/edit search engines with options (#52439) 2021-04-12 15:25:47 +02:00
Frédéric Péters 54950b51b2 general: ensure jsonfields are using jsonb columns (fixup2, #52915) 2021-04-12 14:41:47 +02:00
Frédéric Péters b9ff8540d7 general: ensure jsonfields are using jsonb columns (fixup, #52915) 2021-04-12 14:11:01 +02:00
Frédéric Péters 21976d8f20 general: ensure jsonfields are using jsonb columns (#52915) 2021-04-12 14:03:43 +02:00
Lauréline Guérin 4021e5d3b0
misc: add isort reformat reference to git blame ignore list (#52797)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-04-08 16:53:53 +02:00
Lauréline Guérin 29bc8e66a9
misc: apply isort (#52797) 2021-04-08 15:54:31 +02:00
Lauréline Guérin 6c6d7bac40
misc: add isort (#52797) 2021-04-08 15:54:31 +02:00
Lauréline Guérin 1cfd7e90c4
search: don't index invalid cells (#52418)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-04-01 18:08:33 +02:00
Nicolas Roche d66fb50680 manager: correct asset file size display on export site popup (#45466) 2021-03-29 15:49:27 +02:00
Thomas Jund 3dd0b172d8 pwa-nav template: no newline at end of file (#51802)
to return an empty string if no item
2021-03-26 15:57:06 +01:00
Benjamin Dauvergne b9f1cbe2f5 spooler: add decorator to simplify use of spooler (#52221)
The new tenantspool decorator keep the function signature using pickle
serialization (through the pass_arguments=True parameter of @spool),
setup and teardown Django db connections and restore the current tenant.
2021-03-22 15:19:42 +01:00
Benjamin Dauvergne 631106097d setup.py: use feedparser>=6 with python 3.9 (#52263) 2021-03-20 12:20:36 +01:00
Thomas NOËL c6c87ccb4b uwsgi: recycling spooler after 20 tasks (#52261) 2021-03-19 23:49:39 +01:00
Lauréline Guérin 3b17fdace8
wcs: select categories for backoffice submission cell (#52055)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-03-19 09:17:22 +01:00
Frédéric Péters 49fc5fb3df translation update 2021-03-15 20:36:25 +01:00
Valentin Deniaud 7cf5e179fe dataviz: do not accept nested lists as valid data (#51680) 2021-03-15 09:59:00 +01:00
Lauréline Guérin a53f37499a
manager: mention on home page if a page is in menu (#51477)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-03-15 08:37:52 +01:00
Lauréline Guérin 0fd79d4c0a
manager: mention on home page is a page is a redirection (#51476)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-03-12 21:36:46 +01:00
Lauréline Guérin cb8019bee7
manager: duplicate cell (#51075)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-03-12 21:05:28 +01:00
Lauréline Guérin 051afa00d8
manager: take snapshots after page import (#51475)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-03-12 09:48:34 +01:00
Lauréline Guérin a8ab4035de
data: import site performances (#51472)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-03-12 09:04:46 +01:00
Lauréline Guérin a55d79e99b
data: snapshot restore performances (#51472) 2021-03-12 09:04:46 +01:00
Lauréline Guérin 95a466f1c7
data: don't build the cache on cell update (#51472) 2021-03-12 09:04:46 +01:00
Lauréline Guérin bf3cb40c5c
data: disconnect signals during snapshot loading (#51472) 2021-03-12 09:04:46 +01:00
Lauréline Guérin d1fe947574
data: don't load all cells for cache build (#51472) 2021-03-12 09:04:46 +01:00
Lauréline Guérin 0f6f0178a5
data: add some performances tests (#51472) 2021-03-12 09:04:46 +01:00
Lauréline Guérin 62d4386d54
misc: fix tests 2021-03-12 09:04:46 +01:00
Frédéric Péters 41ad295077 translation update 2021-03-02 17:23:13 +01:00
Lauréline Guérin a11d858b0d
misc: add |getlist template filter (#51184)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-03-01 14:11:40 +01:00
Valentin Deniaud 58de5909fe dataviz: update statistic data asynchronously (#50892) 2021-03-01 09:40:12 +01:00
Valentin Deniaud e7670e0ab1 dataviz: refresh statistics list more frequently using spooler (#50891) 2021-03-01 09:39:08 +01:00
Valentin Deniaud a6f2a755e1 misc: add uwsgi spooler (#50891) 2021-03-01 09:39:08 +01:00
Valentin Deniaud 38ba201a5f dataviz: improve queries on statistics list update (#50891) 2021-03-01 09:39:08 +01:00
Valentin Deniaud 452e671c7b dataviz: move statistic list update code (#50891) 2021-03-01 09:39:08 +01:00
Lauréline Guérin c8cf439f59 wcs: file display in card detail cell (#50971) 2021-02-23 09:14:09 +01:00
Frédéric Péters ee7deb4e7f debian: use uwsgi cheaper subsystem for workers (#51313) 2021-02-22 12:34:43 +01:00
Frédéric Péters 61d638ef83 dashboard: return bad request on invalid key given to auto_tile (#51282) 2021-02-19 11:54:50 +01:00
Lauréline Guérin 69d97b1c08
search: fix view when indexed cell has no page (#51251)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-02-18 14:14:51 +01:00
Frédéric Péters 72f2a0efb0 translation update 2021-02-15 18:16:06 +01:00
Frédéric Péters 172231a03f tox: add black (via pre-commit) to tests (#50927) 2021-02-15 18:05:25 +01:00
Frédéric Péters 538be371f1 misc: add black files/notes 2021-02-15 18:05:25 +01:00
Frédéric Péters 47d67c395e trivial: apply black 2021-02-15 18:02:39 +01:00
Valentin Deniaud bc9f6b9fba dataviz: display time_range fields only if relevant (#50836) 2021-02-15 17:14:10 +01:00
Lauréline Guérin 4a2dedf41a
search: option to display page's description (#51014)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-02-12 14:21:28 +01:00
Lauréline Guérin 1b66f4106c
assets: change key max_length (#50398)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-02-11 10:45:59 +01:00
Frédéric Péters 04f3797d07 translation update 2021-02-05 18:09:00 +01:00
Lauréline Guérin 2b5cfec6f9
wcs: report card info cell as invalid if 404 (#50663)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-02-05 11:16:13 +01:00
Lauréline Guérin 7b690fb0b6
misc: help_text for cache_duration fields (#50713)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-02-05 09:54:20 +01:00
Lauréline Guérin f333ce7b1c
misc: fix has_role filter when user is proxied (#50874)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-02-04 15:49:14 +01:00
Valentin Deniaud d0fc3f581c tests: remove dataviz migration test (#50845) 2021-02-04 10:14:35 +01:00
Benjamin Dauvergne 94e666f92c lingo: set Regie.can_pay_only_one_basket_item default to True (#48281) 2021-02-02 18:01:04 +01:00
Lauréline Guérin 1fb968ee3e
wcs: add an option to ignore user for card cell (#49388)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-01-25 09:34:32 +01:00
Frédéric Péters 6b0b6509b1 tests: parse query string to check if parameter is not present (#50467) 2021-01-23 12:36:28 +01:00
Frédéric Péters e346565ab7 translation update 2021-01-19 17:35:50 +01:00
Lauréline Guérin 36461f2d48
wcs: add card identifier on Card Info cell (#50001)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-01-18 16:34:06 +01:00
Thomas NOËL 71aa546fec lingo: support can_pay_only_one on remote regie (#50067) 2021-01-18 15:49:59 +01:00
Lauréline Guérin fb4f5fd33d
pages: sub_slug can contain only slug part, without regex (#50000)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-01-15 16:03:18 +01:00
Thomas NOËL 38c7ec1bd6 tests: shorten database name, again (#50075) 2021-01-12 14:28:59 +01:00
Thomas NOËL 37eebfff08 tests: shorten database name (#50075) 2021-01-12 14:14:39 +01:00
Lauréline Guérin 7d61835c25
lingo: don't fail on payment if error occured on remote item (#49863) 2021-01-08 14:05:54 +01:00
Frédéric Péters 469818dd3c tox: relax pylint dependencies 2021-01-08 13:10:08 +01:00
Lauréline Guérin 4f20319d4c
wcs: add get_full filter (#49854)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-01-05 15:31:29 +01:00
Lauréline Guérin 53651ec33e
search: return 404 if cell does not exist (#49876)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2021-01-05 14:51:15 +01:00
Valentin Deniaud f3f124e412 tests: rename mock in dataviz 2021-01-05 14:23:43 +01:00
Valentin Deniaud 8abb71b11c dataviz: avoid crash getting statistics from bad provider (#49692) 2021-01-05 13:56:09 +01:00
Valentin Deniaud a4546a919b misc: add authentic to statistics providers (#49700) 2021-01-05 13:50:18 +01:00
Frédéric Péters 7c88233d7d build: update to use origin/main 2020-12-26 15:21:15 +01:00
Valentin Deniaud 53e2ffc90b dataviz: include ChartNgCell in invalidity report (#49720) 2020-12-24 12:14:04 +01:00
Valentin Deniaud 46a0b8f5f9 dataviz: do not log errors when getting chart (#49720) 2020-12-24 12:13:49 +01:00
Valentin Deniaud 47c0c8a6b4 translation update 2020-12-22 12:25:12 +01:00
Valentin Deniaud 60ef41286f trivial: use named format string for proper localization 2020-12-22 12:24:08 +01:00
Valentin Deniaud c492cf4d02 dataviz: add time range fields (#49248) 2020-12-22 12:08:45 +01:00
Valentin Deniaud 6fe79041bf dataviz: get statistics from arbitrary urls (#49247) 2020-12-22 11:44:12 +01:00
Valentin Deniaud 1a629d57c7 manager: allow dynamic fields in cell edit form (#49175) 2020-12-22 11:41:07 +01:00
Valentin Deniaud e56b1e4b97 dataviz: handle api filters (#49175) 2020-12-22 11:41:07 +01:00
Valentin Deniaud 249c133d07 requests_wrapper: mind query params when caching (#49175) 2020-12-22 11:39:36 +01:00
Valentin Deniaud d8fbda7d42 dataviz: avoid crash if no table data (#48865) 2020-12-22 11:38:42 +01:00
Valentin Deniaud 18fdd8a8c4 dataviz: handle new api to get statistics from elsewhere (#48865) 2020-12-22 11:38:42 +01:00
Valentin Deniaud b615b7fa99 tests: add dataviz null visualization (#48865) 2020-12-22 11:38:42 +01:00
Valentin Deniaud 4f878ac96a dataviz: split get_chart into several methods (#48865) 2020-12-22 11:38:42 +01:00
Valentin Deniaud 356bb767f5 dataviz: save available visualizations in db (#49173) 2020-12-22 11:37:10 +01:00
Valentin Deniaud 5db0931f10 tests: format dataviz using black (#49173) 2020-12-22 11:37:10 +01:00
Valentin Deniaud de584c394f tests: decrease indentation level in dataviz (#49173) 2020-12-22 11:37:10 +01:00
Lauréline Guérin 6e89336233
wcs: add with_custom_view filter (#49406)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2020-12-18 15:53:15 +01:00
Lauréline Guérin 4840516b90
wcs: add filter_by_status filter (#49406) 2020-12-18 15:53:14 +01:00
Lauréline Guérin d074920a9c
misc: django 2 urlencode does not like None values 2020-12-18 15:53:14 +01:00
Lauréline Guérin 1a31eea2e3
wcs: add filter_by_user filter (#49406) 2020-12-18 15:53:14 +01:00
Lauréline Guérin 1ec70f4b16
wcs: add filter_by and filter_value filters (#49406) 2020-12-18 15:53:14 +01:00
Lauréline Guérin d1f886b46d
wcs: add access control (#49406) 2020-12-18 15:53:14 +01:00
Lauréline Guérin 3b1b0d3ca1
wcs: add count filter (#49406) 2020-12-18 15:53:14 +01:00
Lauréline Guérin 0e0042fa22
wcs: add cards in context and objects filter (#49406) 2020-12-18 15:53:14 +01:00
Frédéric Péters 45d7824966 wcs: enclose card data in a <div> (#49583) 2020-12-18 08:08:13 +01:00
Valentin Deniaud cdaec0de64 increase djangorestframework version limit 2020-12-17 10:28:26 +01:00
Lauréline Guérin 13b1780c13
newsletters: manage http errors (#48562)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2020-12-17 08:56:58 +01:00
Frédéric Péters 8f32f5df43 translation update 2020-12-14 18:10:14 +01:00
Nicolas Roche 689391b451 setup: relax feedparser version for python 3.9 (#49180) 2020-12-08 10:36:22 +01:00
Valentin Deniaud 2c07b59ab2 tests: fixed cell order to avoid random failures (#49182) 2020-12-07 16:29:35 +01:00
Frédéric Péters 81d5bc026d maps: add possibility to define map layer slots (#48978) 2020-12-01 17:29:49 +01:00
Frédéric Péters 794beda7a1 pwa: fix test for 410 Gone webpush error (#49013) 2020-12-01 15:27:42 +01:00
Frédéric Péters 083178f0f6 translation update 2020-11-30 15:51:12 +01:00
Frédéric Péters 4bad922e33 misc: declare eopayment as a django app, for translations (#45183) 2020-11-27 15:10:03 +01:00
Frédéric Péters 9c7d6e3cf8 general: split obtaining list of fields out of get_default_form_class (#48876) 2020-11-27 10:51:50 +01:00
Lauréline Guérin 870238eb41
lingo: do not retry notify on 4xx error (#48393)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2020-11-23 15:51:40 +01:00
Lauréline Guérin 8b749e4cfb
lingo: stop retrying notify after 4 days (#48393) 2020-11-23 15:51:34 +01:00
Lauréline Guérin e48ede714f
lingo: don't fail on invoices listing if err is returned (#48394)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2020-11-20 15:50:46 +01:00
Lauréline Guérin 2de1d73b57
lingo: display item popup with error details (#48338)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2020-11-20 14:15:15 +01:00
Frédéric Péters 5e1ae45bc1 translation update 2020-11-20 12:50:15 +01:00
Lauréline Guérin 32c79cd78f wcs: option to get cards only for current user (#48190) 2020-11-20 12:50:15 +01:00
Frédéric Péters 01694ddfaf wcs: don't add empty NameID to submission links (#48715) 2020-11-20 12:02:55 +01:00
Lauréline Guérin ca8f61ab2a
search: cards engine without current user (#48261)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2020-11-20 11:01:48 +01:00
Lauréline Guérin 3b745cf767
search: set custom view on cards engine (#48262)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2020-11-20 10:34:04 +01:00
Lauréline Guérin fd03e88b0b
misc: fix split template tag (#48507)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2020-11-20 09:15:35 +01:00
Frédéric Péters e460043cfb utils: allow export/import to work without lingo installed (#48644) 2020-11-18 13:22:11 +01:00
Valentin Deniaud 332f596b95 lingo: show help_text in payment backend form (#48636) 2020-11-18 09:55:26 +01:00
Frédéric Péters 9aeb8e5099 misc: add support for a template-name key in json cell declarations (#48637) 2020-11-18 09:53:35 +01:00
Benjamin Dauvergne 5604d4dada misc: add wcs-app js file to MANIFEST.in 2020-11-17 19:47:19 +01:00
Benjamin Dauvergne 0a43e4da90 translation update 2020-11-17 16:12:29 +01:00
Benjamin Dauvergne c48e3bf3c9 wcs: use sessionStorage to store current submission context (#48503) 2020-11-17 15:37:14 +01:00
Valentin Deniaud 3918e5f8ec manager: select sections in site export (#46895) 2020-11-16 14:32:29 +01:00
Valentin Deniaud fcc41caa81 lingo: add backends and regies in site import/export (#46895) 2020-11-16 14:32:29 +01:00
Lauréline Guérin 6765819f29
wcs: fix cards search engine when w.c.s. is down (#48409)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2020-11-16 08:09:33 +01:00
Lauréline Guérin 154df07a3c
wcs: fix card info cell for fields without varname (#48452)
gitea-wip/combo/pipeline/head There was a failure building this commit Details
gitea/combo/pipeline/head Build started... Details
2020-11-12 11:16:20 +01:00
Frédéric Péters ae38713746 wcs: reduce cache duration for "forms in your care" cell (#48357) 2020-11-06 15:58:15 +01:00
Lauréline Guérin b8a06e2641
wcs: cards & custom views (#46865)
gitea-wip/combo/pipeline/head Build started... Details
gitea/combo/pipeline/head Build started... Details
2020-11-06 15:13:17 +01:00
Frédéric Péters 8d4d5cd17f tests: remove siret references in lingo tests
It was removed from the dummy backend in #34064.
2020-11-04 13:45:45 +01:00
Frédéric Péters 31e944551e build: bump eopayment dependency 2020-11-04 13:28:56 +01:00
Benjamin Dauvergne db6675abb7 lingo: declare SAGA backend (#48276) 2020-11-04 11:12:02 +01:00
Benjamin Dauvergne 54f2c0ea50 lingo: factorize text in mail bodies (#22715) 2020-11-03 18:25:44 +01:00
Benjamin Dauvergne 902a52278e lingo: move email extraction to payment views (#47513)
There is too much non-linear logic around emails in handle_payment(),
it's better to locate this logic in the calling views where situations
are more constrained.
2020-11-03 11:13:18 +01:00
Frédéric Péters 4b88540880 translation update 2020-11-02 21:02:23 +01:00
Benjamin Dauvergne e5d9087f3e lingo: pass PayFip specific data to eopayment for unique item payment (#47477) 2020-10-28 23:32:13 +01:00
Benjamin Dauvergne 8f899a9376 lingo: pass reference_id to eopayment as orderid for unique item payment (#47477) 2020-10-28 23:32:13 +01:00
Benjamin Dauvergne 84f2f8fd10 lingo: allow passing custom kwargs to eopayment.request (#47477) 2020-10-28 23:32:13 +01:00
Benjamin Dauvergne 543debcc2e tests: factorize single payment regie fixture (#47477) 2020-10-28 23:32:13 +01:00
Benjamin Dauvergne e1887d0384 lingo: simplify extraction of subject for single payment (#47477) 2020-10-28 23:32:13 +01:00
Benjamin Dauvergne 1c22405e8b lingo: document handle_payment contract with assert (#47477) 2020-10-28 23:32:11 +01:00
Benjamin Dauvergne e0d4d2b2f5 lingo: move error message to PayView (#47477) 2020-10-28 23:31:37 +01:00
Benjamin Dauvergne 23314e0f33 lingo: add "up to and including" mention to payment limit date (#23507) 2020-10-26 20:24:19 +01:00
Valentin Deniaud 49d9a3023e basket-cell: improve markup (#47735) 2020-10-23 10:39:27 +02:00
Frédéric Péters b7e6a7ed1e publik: make it possible to create publik menu from pages (#45741) 2020-10-23 09:02:57 +02:00
Frédéric Péters e28819644f lingo: remove sp+ from eopayment supported platforms (#47596) 2020-10-13 09:07:24 +02:00
Benjamin Dauvergne 6973c7e249 debian: disable write exception in uwsgi.ini (#47506) 2020-10-13 00:25:58 +02:00
Frédéric Péters 778bd99a33 translation update 2020-10-12 19:32:20 +02:00
Benjamin Dauvergne 96b63710be lingo: add test value for ROLMRE (#16871) 2020-10-12 15:27:42 +02:00
Valentin Deniaud 736055caee apps: do not crash when request body is not json (#47452) 2020-10-12 14:02:51 +02:00
Serghei Mihai edd13a2b66 lingo: return different message for cancelled transaction (#47497) 2020-10-09 15:04:54 +02:00
Frédéric Péters 20f0556792 misc: handle multi-part subslugs (#47512) 2020-10-09 13:50:26 +02:00
Nicolas Roche d62592c745 manager: load (ajax) assets size on export site popup (#45466) 2020-10-09 11:07:58 +02:00
Lauréline Guérin f8ee44c3f5
wcs: handle network error on requests (#31438) 2020-10-09 09:30:57 +02:00
Benjamin Dauvergne 6c53fdd37f lingo: remove non tracing check Regie.can_pay_only_one_basket_item (#46504) 2020-10-08 12:13:47 +02:00
Benjamin Dauvergne d38d1a4347 lingo: coding style (#46504) 2020-10-08 12:13:47 +02:00
Benjamin Dauvergne d93ecda18f lingo: assert preconditions in handle_payment() (#46504)
It checks preconditions on item and remote_items, with specific
preconditions for regie flagged "can_pay_only_one_basket_item".
2020-10-08 12:13:47 +02:00
Benjamin Dauvergne 16d9a5cbfc lingo: pass item subject to eopayment (#46504) 2020-10-08 12:13:47 +02:00
Frédéric Péters 6db674205c tox: limit mock version for compatibility with python 3.5 2020-10-06 09:28:40 +02:00
Frédéric Péters 4263b5662e translation update 2020-10-01 19:43:27 +02:00
Valentin Deniaud 4ff5f921c1 lingo: allow requiring individual payment for regie (#46503) 2020-10-01 14:22:03 +02:00
Valentin Deniaud 3faa37405b lingo: add payment_url property to basket item (#46503) 2020-09-30 18:16:44 +02:00
Valentin Deniaud c5662aa6b6 lingo: move utils functions to file (#46503) 2020-09-30 18:16:44 +02:00
Valentin Deniaud 1dfb67c452 lingo: fix missing migration (#46503) 2020-09-30 18:16:44 +02:00
Lauréline Guérin 2c853fbe80
manager: check that sub_slug is a valid regex (#47099) 2020-09-30 08:45:24 +02:00
Lauréline Guérin dfc2337947
pages: sub_slug, regexp & '-' in group name (#47099) 2020-09-30 08:45:24 +02:00
Frédéric Péters ed19b99e35 translation update 2020-09-29 19:35:05 +02:00
Lauréline Guérin f9881c7a5b
wcs: add cell to display card details (#46767) 2020-09-28 15:48:24 +02:00
Lauréline Guérin e642164112
wcs: add cell to list cards of a card model (#46768) 2020-09-28 15:48:08 +02:00
Lauréline Guérin d7f4f2c4ee
misc: missing migrations 2020-09-28 15:48:08 +02:00
Frédéric Péters 89fe881e15 tox: get django-ckeditor using https (via #46904#note-2) 2020-09-24 20:00:42 +02:00
Frédéric Péters fc67a1921e translations: fix typos & spelling 2020-09-20 16:16:33 +02:00
Frédéric Péters 342f507ec9 translation update 2020-09-20 15:09:41 +02:00
Frédéric Péters 7ead2e16ac dashboard: ignore dashboards from snapshot pages (#46786) 2020-09-20 12:26:39 +02:00
Lauréline Guérin 65eb33e06b
import: form error if cell's related page not found (#44666) 2020-09-18 09:28:05 +02:00
Frédéric Péters ff1b3c009f translation update 2020-09-18 07:40:11 +02:00
Lauréline Guérin 34f9aa2a8b
assets: help text on import form page (#46396) 2020-09-17 15:29:58 +02:00
Frédéric Péters d6b79f48ef translation update 2020-09-15 15:14:05 +02:00
Frédéric Péters e38254d47d misc: use full URLs for href/src attributes in 404 skeleton pages (#45895) 2020-09-15 14:00:20 +02:00
Lauréline Guérin 1aa02b0fb4
lingo: fix payment view for remote regie and min amount (#46016) 2020-09-14 16:11:11 +02:00
Frédéric Péters d936639e81 misc: limit feedparser version to keep python 3.5 compatibility 2020-09-12 22:23:33 +02:00
Thomas NOËL a94f09121a settings: add django.contrib.humanize templatetags (#46337) 2020-09-04 10:40:52 +02:00
Nicolas Roche fe9a744119 translation update 2020-09-03 10:55:05 +02:00
Lauréline Guérin 887a88ad11
manager: create page from model (#10117) 2020-09-03 10:03:26 +02:00
Lauréline Guérin 48f7353309
data: get descendants of a page excluding page (#10117) 2020-09-03 10:03:26 +02:00
Lauréline Guérin 465c856895
manager: select template when adding a page (#10117) 2020-09-03 10:03:24 +02:00
Nicolas Roche c0158d6172 manager: add tar format for site export/import (#45128) 2020-09-02 13:04:33 +02:00
Nicolas Roche 0f856f1334 commands: add tar format for site export/import (#39425) 2020-09-02 11:59:07 +02:00
Nicolas Roche f0e0de43e5 assets: factorize import/export code (#39425) 2020-09-02 11:58:54 +02:00
Valentin Deniaud 8ae63cc075 tox: tell setuptools to use distutils from stdlib (#46252) 2020-09-01 14:22:08 +02:00
Frédéric Péters d33cdbb47e wcs: keep elements ordered as they were by wcs (#45952) 2020-08-31 14:45:10 +02:00
Frédéric Péters 593ba91669 dataviz: treat null values as zero when sorting on values (#45999) 2020-08-21 14:06:29 +02:00
Frédéric Péters 518102b076 translation update 2020-08-16 21:15:52 +02:00
Lauréline Guérin 95943cf340 manager: export page and subpages (#44667) 2020-08-16 21:08:07 +02:00
Frédéric Péters 685ad0376c publik: enable mellon passive authentication on services.js (#45508) 2020-08-14 13:29:27 +02:00
Nicolas Roche 8828b94050 tests: add null values to chart visualisation data (#45503) 2020-08-14 12:42:25 +02:00
Nicolas Roche ee8c7b2306 dataviz: add sort and hide empty data options on chart cell (#45503) 2020-08-14 12:11:40 +02:00
Frédéric Péters 05f16b523a misc: clear validity info when changing feed/json cells (#45842) 2020-08-14 11:47:14 +02:00
Nicolas Roche 95cb806044 pwa: import/export pwa application-icon (#44833) 2020-08-14 10:28:27 +02:00
Frédéric Péters 24ee60d321 tox: remove quixote installation as wcs is no longer a test dependency 2020-08-11 08:37:09 +02:00
Nicolas Roche ba3451e723 pep8: put 2 lines between function definitions (#45780) 2020-08-10 16:09:16 +02:00
Nicolas Roche 017b20e7f8 templatetags: add removeprefix and removesuffix filters (#45780) 2020-08-10 16:09:16 +02:00
Frédéric Péters 40e32ff67d manager: give cell links/button line its own class (#45760) 2020-08-06 12:24:41 +02:00
Lauréline Guérin d62dc92037
manager: keep some page attributes when snapshot restoration (#45243) 2020-07-31 13:30:03 +02:00
Frédéric Péters 04ad825760 manager: do not upscale page picture (#45510) 2020-07-28 09:50:57 +02:00
Frédéric Péters 7d6600b276 maps: keep state of enabled/disabled leaflet layers during reload (#45329) 2020-07-21 16:48:32 +02:00
Frédéric Péters 6029d7182a templates: take cell figure parameters from variables (#43161) 2020-07-20 14:25:54 +02:00
Frédéric Péters 1e70311a92 translation update 2020-07-20 14:25:54 +02:00
Lauréline Guérin 7d2ab21ad7
cells: don't hide invalid link list cell (#44795) 2020-07-17 16:10:06 +02:00
Thomas Jund 94bd9c0929 js: introduce ComboScrollY global function (#45040)
Launch callbacks when page scroll below or above a limit
2020-07-17 07:48:34 +02:00
Frédéric Péters 3ac4263250 translation update 2020-07-17 07:46:52 +02:00
Frédéric Péters 99afae6967 json cell: check json content type when determining template (#45150) 2020-07-16 19:21:16 +02:00
Frédéric Péters 24980ab6fe dashboard: don't allow GET request to autotile (#45053) 2020-07-15 19:58:06 +02:00
Frédéric Péters 3e97633723 dashboard: raise on missing parameters in auto-tile (#45053) 2020-07-15 19:58:06 +02:00
Frédéric Péters cccbc87b61 search: add option to set custom title for subpages search engine (#43888) 2020-07-14 20:05:24 +02:00
Frédéric Péters 1944e05dae translation update 2020-06-27 16:23:10 +02:00
Frédéric Péters 7b74d29c4e trivial: remove bullets from delete page popup 2020-06-27 16:23:10 +02:00
Frédéric Péters 0af30aa706 trivial: style "not defined" label in assets 2020-06-27 16:23:10 +02:00
Lauréline Guérin 0e39d59a35
assets: display only name wihout link when asset is not defined (#43385) 2020-06-26 14:47:18 +02:00
Paul Marillonnet 5356dbde4e search: provide title precedence over content for indexed cells (#43781) 2020-06-26 14:31:48 +02:00
Paul Marillonnet 2fd9129c86 tox: provide postgresql test venvs (#43916) 2020-06-26 14:30:10 +02:00
Paul Marillonnet d13df9f38f tests: fix overly-confident references to object identifiers (#43916) 2020-06-26 14:30:10 +02:00
Paul Marillonnet 5134fba117 Jenkins: increase timeout value (#43916) 2020-06-26 14:30:10 +02:00
Paul Marillonnet e26cdd7f3c import: allow pages with order 0 (#44361) 2020-06-26 14:28:17 +02:00
Benjamin Dauvergne c43d9ee6c8 lingo: keep order of regie's parameters (#44484) 2020-06-26 12:24:32 +02:00
Benjamin Dauvergne 832b8188fb lingo: allow item payment by any user (#41837) 2020-06-26 11:44:28 +02:00
Valentin Deniaud 236fa3557a manager: choose new page title when duplicating (#44166) 2020-06-26 10:35:18 +02:00
Thomas NOËL 7c1f5ef82a debian: depends on python3-cryptodome 2020-06-25 23:55:56 +02:00
Frédéric Péters 999b9c9b97 maps: don't add/remove layers that failed to load (#44303) 2020-06-22 11:39:05 +02:00
Benjamin Dauvergne 0ded4ae599 lingo: report errors on invoices retrieval (#43967) 2020-06-19 17:39:07 +02:00
Lauréline Guérin 01feda7431
templates: add tests for datetime_in_past filter (#44023)
rename date_in_past into datetime_in_past
2020-06-18 14:35:43 +02:00
Frédéric Péters c17a8f6cc5 translation update 2020-06-17 16:20:46 +02:00
Nicolas Roche 7494896e9f utils: switch to pycryptodomex, replace Crypto with Cryptodome (#43563) 2020-06-17 11:39:47 +02:00
Lauréline Guérin 91b89e6ed5
import: do not fail if page.parent is not found (#22889) 2020-06-16 10:23:33 +02:00
Frédéric Péters 320bcd2bc1 translation update 2020-06-15 19:18:41 +02:00
Valentin Deniaud bda15dbcef lingo: handle empty payload in ReturnView (#42581) 2020-06-15 12:13:31 +02:00
Valentin Deniaud ea0657f565 lingo: catch PaymentException on backend response (#42581) 2020-06-15 12:13:31 +02:00
Valentin Deniaud 31d9b1f960 lingo: add mollie and keyware backends (#42581) 2020-06-15 12:13:31 +02:00
Valentin Deniaud 1c0c36583e lingo: pass extra item info to eopayment backend (#42992) 2020-06-15 12:13:04 +02:00
Valentin Deniaud 1bdc236839 lingo: allow passing extra basket item info (#42992) 2020-06-15 12:13:04 +02:00
Valentin Deniaud fca2a33b33 lingo: remove user retrieval from email in basket api (#42992) 2020-06-15 12:13:04 +02:00
Lauréline Guérin 4a62897422
cells: display invalidity date or delay (#43605) 2020-06-12 14:45:21 +02:00
Frédéric Péters 6d332a9ca5 tests: update test_download_geojson after #42765 2020-06-12 14:40:52 +02:00
Frédéric Péters 8b98fe3544 maps: add javascript functions to disable/enable layers (#42994) 2020-06-12 07:30:52 +02:00
Frédéric Péters e989b8b866 maps: add support for additional query string in geojson url (#42984) 2020-06-12 07:30:52 +02:00
Frédéric Péters e7f053fde8 maps: add map.each_marker javascript utility function (#42767) 2020-06-12 07:30:52 +02:00
Frédéric Péters 179f77f4bc maps: give common layer properties to map cell (#42765) 2020-06-12 07:30:52 +02:00
Frédéric Péters 0da0669e80 maps: factor all geojson layer tracking into a single object (#42761) 2020-06-12 07:30:52 +02:00
Frédéric Péters 39cdfebe67 misc: don't let 404 redirect (#43851) 2020-06-12 07:30:52 +02:00
Frédéric Péters 5d75e92845 misc: include cells from homepage in error 404 page (#43851) 2020-06-12 07:10:26 +02:00
Frédéric Péters f36306ceea dataviz: don't crash on missing width value (#43726) 2020-06-10 11:09:15 +02:00
Thomas Jund dfd35288fa use .links-list class to notifications-cell list (#43698) 2020-06-05 16:32:28 +02:00
Lauréline Guérin c6b0ebb5a1
import: remove pk, page, placeholder in link list items (#43620) 2020-06-05 09:59:30 +02:00
Thomas Jund f5a5299a54 use .links-list class to form_of_category list (#42999) 2020-06-05 09:47:37 +02:00
Lauréline Guérin 3b4563b81a cells: don't check validity for json cells with multiple variables in url (#43604) 2020-06-04 16:54:30 +02:00
Frédéric Péters 3f0e460b94 translation update 2020-06-03 18:58:11 +02:00
Valentin Deniaud 82261bf404 lingo: use form instead of json input for backends (#6710) 2020-06-03 14:53:14 +02:00
Nicolas Roche 2892f355d4 templatetags: add 'endswith' template filter (#43558) 2020-06-03 11:59:28 +02:00
Frédéric Péters df6ca9ca4b misc: remove josefinsans xstatic dependency (#43498) 2020-06-01 10:03:15 +02:00
Lauréline Guérin 518825f9a4
search: don't search for engines on site indexation (#43228) 2020-05-22 16:11:57 +02:00
Thomas NOËL 32e714709a translation update 2020-05-22 11:37:55 +02:00
Frédéric Péters f51551101c pwa: delete pwa push subscription on 410 errors (#41304) 2020-05-19 10:45:32 +02:00
Lauréline Guérin 8f4a5869bd
search: add card search engins (#41845) 2020-05-15 18:44:14 +02:00
Lauréline Guérin e88e377679
cells: use get_or_create for ValidityInfo objects (#42823) 2020-05-15 16:54:50 +02:00
Lauréline Guérin 2723604596
lingo: return json error response (#42512) 2020-05-15 14:11:21 +02:00
Lauréline Guérin ee1b833ef4
lingo: fix add basket item view when display_name is missing (#42621) 2020-05-15 11:50:21 +02:00
Lauréline Guérin 35c8d05564
data: fix ConfigJsonCell form validation (#42884) 2020-05-15 11:33:30 +02:00
Thomas NOËL 1a6c51b999 templatetags: add phonenumber_fr filter (#41036) 2020-05-15 11:21:49 +02:00
Benjamin Dauvergne c3e4767425 misc: do not log requests errors in RecentDocumentsCell (#42907) 2020-05-15 10:52:10 +02:00
Lauréline Guérin 8b0e96ef16
tests: remove w.c.s. from test dependencies (#42706) 2020-05-15 09:44:55 +02:00
Frédéric Péters 7416fa2a9b manager: hide collapse space when disabled (#42658) 2020-05-13 17:02:33 +02:00
Nicolas Roche 6e493325d4 templatetags: correct add filter (#42650) 2020-05-12 09:06:17 +02:00
Frédéric Péters cfcd6bf423 maps: trigger map-markers-ready event when all layers are loaded (#42718) 2020-05-11 15:14:08 +02:00
Nicolas Roche 0633dac752 templatetags: add ceil, floor and abs filters (#41694) 2020-05-07 16:23:48 +02:00
Nicolas Roche 219344e0db templatetags: manage decimals on mathematics filters (#41868) 2020-05-07 16:22:31 +02:00
Thomas NOËL ca13ccb98d translation update 2020-05-07 15:53:23 +02:00
Frédéric Péters 437c12b7a5 maps: add django block around map data attributes (#42568) 2020-05-07 15:52:38 +02:00
Nicolas Roche 75875bfd5e templatetags: add strip filters (#41701) 2020-05-07 15:52:09 +02:00
Frédéric Péters e60da7345a maps: use data-disable-clustering-at-zoom if present (#42570) 2020-05-07 15:51:28 +02:00
Benjamin Dauvergne af97ab9ec8 lingo: fix typo on Transaction.bank_transaction_date (#42565) 2020-05-07 09:30:13 +02:00
Frédéric Péters 383178867e maps: let get_geojson work with None as request (#42520) 2020-05-06 08:30:48 +02:00
Frédéric Péters d729d85253 translation update 2020-05-05 20:59:16 +02:00
Lauréline Guérin 87e9957f0b
maps: delegate circle param to geojson service (#41993) 2020-05-05 16:09:51 +02:00
Lauréline Guérin 8aa3fa52fc
maps: pass query parameter to geojson url if defined (#41989) 2020-05-05 16:00:24 +02:00
Lauréline Guérin 1815ba6571
data: url validation in LinkCellForm (#41799) 2020-05-05 15:51:20 +02:00
Lauréline Guérin 1ed29c69a5
wcs: fix KeyError 'data' in BackofficeSubmissionCell (#42374) 2020-05-05 14:19:14 +02:00
Lauréline Guérin f48c4484f7
wcs: don't display empty sites in BackofficeSubmissionCell (#41090) 2020-05-05 13:56:38 +02:00
Benjamin Dauvergne 24dcabc653 lingo: send bank_transaction_date when notifying triggers (#41323) 2020-05-04 15:26:42 +02:00
Benjamin Dauvergne eebae4cbbd lingo: use bank_transaction_date instead of end_date when possible (#41323) 2020-05-04 15:26:26 +02:00
Benjamin Dauvergne 83a53952a4 lingo: force transaction_date to UTC in pay_invoice() (#41323) 2020-05-04 15:26:23 +02:00
Benjamin Dauvergne 4afe8c930c lingo: store received transaction_date (#41323) 2020-05-04 15:24:10 +02:00
Benjamin Dauvergne 1482ec599f lingo: add Transaction.bank_transaction_date (#41323) 2020-05-04 15:23:27 +02:00
Frédéric Péters f29c94d7b9 search: escape % signs in URLs (#42436) 2020-05-04 11:27:28 +02:00
Lauréline Guérin ef757e1e8a
misc: fix url split in request wrapper (#42309) 2020-04-30 17:43:11 +02:00
Lauréline Guérin c41b41de72
cells: add block in forms_of_category.html to make customization easier (#40883) 2020-04-30 09:46:36 +02:00
Lauréline Guérin 757db7d37b
cells: add blocks in link-list-cell.html to make customization easier (#40883) 2020-04-30 09:46:36 +02:00
Lauréline Guérin 870e132867
cells: more items & accessibility (#40883, #40884) 2020-04-30 09:46:36 +02:00
Frédéric Péters 157e12da26 translation update 2020-04-28 22:28:30 +02:00
Lauréline Guérin 855ac63f3b
assets: use assets views instead of ckeditor browser (#40249) 2020-04-28 15:29:45 +02:00
Lauréline Guérin e181863b9f
manager: toggle pages in manager home (#27618) 2020-04-28 09:51:22 +02:00
Frédéric Péters 6df22cd0fb misc: use minimal default 404 page when templating is down (#42162) 2020-04-27 20:25:21 +02:00
Lauréline Guérin fbda225b1a
assets: add css classes related to assets on cells (#41995) 2020-04-27 17:30:57 +02:00
Benjamin Dauvergne e4c329edaf debian: use PYBUILD_NAME (#41634) 2020-04-27 16:28:32 +02:00
Benjamin Dauvergne c81470efcf misc: hide debug toolbar in skeleton view (#41597) 2020-04-26 14:19:29 +02:00
Christophe Siraut 9ddff4fab0 Jenkinsfile: use default distribution target for hotfix branch (#41301) 2020-04-24 15:42:53 +02:00
Lauréline Guérin 01958cfafb
misc: fix error404 view when raised by combo (#42067) 2020-04-24 11:16:06 +02:00
Lauréline Guérin f8c6af9ed8
history: check if page exists (#41246) 2020-04-24 09:27:45 +02:00
Frédéric Péters 47e1b31b42 trivial: remove conditional Python 2 code (#42003) 2020-04-22 20:12:42 +02:00
Frédéric Péters 33814b5574 jenkins: build package for buster 2020-04-19 12:14:21 +02:00
Frédéric Péters 79aba69d82 search: allow : in engine slug pattern (#41859) 2020-04-19 12:08:34 +02:00
Frédéric Péters 4c8a445389 translation update 2020-04-17 12:00:05 +02:00
Lauréline Guérin e6a9ed2957
search: search on page and subpages (#40224) 2020-04-17 11:32:58 +02:00
Lauréline Guérin 1ab9684edb
search: new manager selection SearchCell engines (#40224) 2020-04-17 11:03:50 +02:00
Lauréline Guérin 0757cd7fca
maps: geojson url per geojson layer (#40742) 2020-04-16 16:57:02 +02:00
Frédéric Péters 8677ef2756 translation update 2020-04-15 14:18:01 +02:00
Frédéric Péters 3dc78494d1 maps: force box model of map markers (#41724) 2020-04-15 11:08:39 +02:00
Frédéric Péters d66e3b5562 misc: get cells from private placeholders in parent acquisition (#41276) 2020-04-14 17:47:19 +02:00
Valentin Deniaud e5195f4122 map: add "Country" zoom level (#41371) 2020-04-14 10:55:21 +02:00
Frédéric Péters a0c5e697a9 templates: attach back page id into menu items (#41624) 2020-04-14 10:39:31 +02:00
Serghei Mihai efc784d4e1 search: index all cell's external links (#41600) 2020-04-14 09:08:48 +02:00
Frédéric Péters 9c45076ded misc: include form css classes when inserted in links list cells (#41465) 2020-04-14 08:24:44 +02:00
Frédéric Péters 1eec3f6d6d misc: use same markup in links/forms/json list cells (#41643) 2020-04-13 19:46:47 +02:00
Frédéric Péters ef91364999 misc: always pass user as keyword argument to cell.is_visible (#41648) 2020-04-13 19:46:47 +02:00
Benjamin Dauvergne 90a7560e98 lingo: validate service_options in forms (#41439) 2020-04-11 22:22:55 +02:00
Benjamin Dauvergne ead8de9bcc lingo: backend options must be dict (#41439) 2020-04-11 22:22:08 +02:00
Frédéric Péters a40dc2e73f misc: use native 404 page if ~all pages are private (#41514) 2020-04-11 21:22:13 +02:00
Nicolas Roche 14aa0b62fe search: add a placeholder attribute to search input cell (#40993) 2020-04-10 14:22:22 +02:00
Frédéric Péters 4993fc23e5 wcs: put authenticating URL in context of form cell (#41469) 2020-04-08 17:01:39 +02:00
Frédéric Péters 028789e17e translation update 2020-04-07 16:26:24 +02:00
Frédéric Péters 2b14cc2c28 tests: hit harder on tracking codes to be sure to trigger rate limit 2020-04-03 15:00:24 +02:00
Frédéric Péters d86bfe5966 misc: display unknown error and http status code for unknown errors (#41228) 2020-04-03 13:57:00 +02:00
Frédéric Péters a7abc6e3aa tox: test against django 2.2 (#41285) 2020-04-03 13:55:36 +02:00
Frédéric Péters d863c40190 setup: allow djangorestframework 3.7, for django 2.2 compatibility (#41285) 2020-04-03 13:55:30 +02:00
Frédéric Péters 583e0fc484 setup: update to allow django 2.2 (#41285) 2020-04-03 08:25:37 +02:00
Frédéric Péters b686036244 tests: declare missing attributes when mocking known_services (#41285) 2020-04-03 08:25:37 +02:00
Frédéric Péters c87e491a29 misc: declare renderer argument in ckeditor.render monkeypatch (#41285) 2020-04-03 08:25:37 +02:00
Frédéric Péters 6ec82a369a misc: import token types using new TokenType enum (#41285) 2020-04-03 08:25:37 +02:00
Frédéric Péters b39ad119ea misc: update decorated includes for django 2 (#41285) 2020-04-02 20:42:35 +02:00
Frédéric Péters f4c0afc658 misc: use javascript catalog class view (#41285) 2020-04-02 20:42:32 +02:00
Frédéric Péters 7fa44cce97 misc: remove compatibility values for is_anonymous/is_authenticated (#41285) 2020-04-02 20:42:29 +02:00
Frédéric Péters 16cd985be2 trivial: import reverse from django.urls (#41285) 2020-04-02 20:42:23 +02:00
Frédéric Péters 9786d9b387 dashboard: skip invalid cells in dashboards (#41226) 2020-04-02 08:40:15 +02:00
Frédéric Péters 43c8ec28ca setup: allow micro django-ckeditor updates (#41233) 2020-04-01 16:36:13 +02:00
Frédéric Péters b561b38199 pwa: only accept JPEG and PNG for application icon files (#41211) 2020-04-01 11:24:31 +02:00
Frédéric Péters e6ff7d66f7 translation update 2020-03-31 18:59:59 +02:00
Lauréline Guérin f17b9cb3f5
lingo: hide items cell if empty (#41190) 2020-03-31 17:53:14 +02:00
Lauréline Guérin 5bc694d32d
search: don't index cells with an inactive placeholder (#40252) 2020-03-31 14:41:27 +02:00
Lauréline Guérin ee82f0e26e
search: remove bad test - wrong merge in past ? 2020-03-31 14:41:27 +02:00
Lauréline Guérin 3464f6d023
search: better queries for index_site (#40252) 2020-03-31 14:41:26 +02:00
Lauréline Guérin a83ce2c5ee
search: add a num queries test on index_site (#40252) 2020-03-31 14:41:26 +02:00
Lauréline Guérin 50b1101297
cells: exclude cells with inactive placeholders from invalid report
(#40252)
2020-03-31 14:41:26 +02:00
Lauréline Guérin 453a784007
cells: better perfs on invalid cells report (#40252) 2020-03-31 14:41:26 +02:00
Lauréline Guérin 80500d71a9
cells: don't check validity in is_visible if placeholder search (#40252) 2020-03-31 14:41:26 +02:00
Frédéric Péters 23966b3504 misc: don't check validity for links with multiple variables (#41139) 2020-03-31 11:55:44 +02:00
Frédéric Péters 27ce2bbaeb templates: mark selected menu item with a title for a11y (#40864) 2020-03-27 07:54:33 +01:00
Benjamin Dauvergne 3baed95c28 fargo: hide user-not-found API errors and only that (#35352) 2020-03-24 07:57:14 +01:00
Frédéric Péters cd2fcfebab misc: restrict ajax cell url regex (#40875) 2020-03-23 09:48:24 +01:00
Lauréline Guérin d04bb7cc89
maps: fix tiles map layer edition (#40866) 2020-03-20 16:32:58 +01:00
Thomas JUND 8d80c2650e html: tracking-code & search cells: add block for custom submit content (#40861) 2020-03-20 15:28:44 +01:00
Frédéric Péters fdb0ff0f36 translation update 2020-03-20 10:13:54 +01:00
Frédéric Péters 5a729a4c7e wcs: add message for empty states in drafts/forms cells (#13974) 2020-03-20 10:05:13 +01:00
Lauréline Guérin 74f5c41b57
wcs: mark cell as invalid if category is empty (#40564) 2020-03-19 10:22:01 +01:00
Thomas JUND 2dc8ffb13f css: add checkerboard background to image preview (#40762) 2020-03-18 12:23:23 +01:00
Serghei Mihai 1424afc2a1 pwa: allow navigation context without page (#40687) 2020-03-18 12:12:39 +01:00
Serghei Mihai b85eb35b2f pwa: mark current page's navigation entry as selected (#40687) 2020-03-18 11:23:06 +01:00
Frédéric Péters 7262e0e8fe misc: clean URIs missing a trailing slash (#40801) 2020-03-18 10:08:56 +01:00
Lauréline Guérin 8e7e5cbe3f
cells: list of links are foldable and expandable (#40703) 2020-03-17 11:03:54 +01:00
Frédéric Péters cf47dc1a23 translation update 2020-03-17 08:19:05 +01:00
Frédéric Péters 6b329e6b48 templatetags: backport json_script from django 2.1 (#40768) 2020-03-17 07:43:12 +01:00
Lauréline Guérin 90f17f8595
assets: display assets related to cell in popup (#40223) 2020-03-16 11:12:20 +01:00
Lauréline Guérin 624120dedf
assets: compatibility for wcs assets (#40223) 2020-03-16 11:10:42 +01:00
Lauréline Guérin 5d6d0a86fc
assets: add generic assets for cells (#40223) 2020-03-16 11:10:21 +01:00
Frédéric Péters 00131e4868 wcs: add customisation blocks to tracking code input cell (#40649) 2020-03-13 10:38:40 +01:00
Lauréline Guérin c5c2a66a60
wcs: reduce querysets on page detail for user (#40675) 2020-03-13 10:21:37 +01:00
Lauréline Guérin 3b75d3db28
wcs: test num queries on page display (#40675) 2020-03-13 10:21:37 +01:00
Thomas NOËL 95d761ebcf lingo: detect more errors in remove payload (#40708) 2020-03-13 10:09:47 +01:00
Frédéric Péters e458263be2 manager: don't check cell validity when reordering them (#40645) 2020-03-11 11:42:28 +01:00
Frédéric Péters ebcfd93e79 misc: accept link cells with just an anchor as valid (#40644) 2020-03-11 11:42:28 +01:00
Thomas NOËL 75957c0d23 add decimal templatetag (#40599) 2020-03-10 10:10:04 +01:00
Frédéric Péters fba76ea61a translation update 2020-03-06 09:55:21 +01:00
Lauréline Guérin cc5aaa93e4
cells: data cells invalid report (#38009) 2020-03-05 09:51:28 +01:00
Lauréline Guérin 7299d1d835
cells: invalid cell report (#38009) 2020-03-05 09:51:11 +01:00
Lauréline Guérin 7046049aa0
cells: w.c.s. cells invalid report (#38009) 2020-03-05 09:43:54 +01:00
Lauréline Guérin 6fa8bf883a
cells: if a cell is invalid, display it (#38009) 2020-03-05 09:43:54 +01:00
Lauréline Guérin 39234986fe
cells: a cell is not visible after 2 days of invalidity (#38009) 2020-03-05 09:43:54 +01:00
Lauréline Guérin 957864921b
cells: add invalid fields (#38009) 2020-03-05 09:43:54 +01:00
Frédéric Péters 0e445cf6b9 family: ignore non-primary services (#16429) 2020-03-04 11:04:03 +01:00
Frédéric Péters 2b770623d5 dataviz: truncate horizontal bar labels in narrow graphs (#39920) 2020-03-04 10:59:51 +01:00
Frédéric Péters 196c6ce028 dataviz: move legend to bottom in narrow graphs (#39920) 2020-03-04 10:59:51 +01:00
Frédéric Péters a20517cf23 wcs: add a11y label to tracking code input (#40281) 2020-03-04 10:59:05 +01:00
Valentin Deniaud 271e13b680 lingo: handle exceptions raised by backend.request (#40244) 2020-03-03 16:58:42 +01:00
Lauréline Guérin 72fe08ea5e
lingo: display amount_paid in invoice listing (#40364) 2020-03-03 10:25:03 +01:00
Lauréline Guérin 89ebef5aba
lingo: do not display pay limit date column if not provided (#40170) 2020-02-28 09:59:08 +01:00
Lauréline Guérin 7e81814aa5
lingo: display amount_paid if provided (#40170) 2020-02-28 09:59:08 +01:00
Thomas NOËL 3662751240 search: do not consider page with sub_slug (#40108) 2020-02-26 00:05:51 +01:00
Frédéric Péters 0215e2424b wcs: limit forms/tracking codes search engine to agent portal (#30508) 2020-02-25 22:37:39 +01:00
Frédéric Péters 46c3f975ac kb: don't include latest updates cell content in search index (#40095) 2020-02-25 22:37:39 +01:00
Valentin Deniaud c11b1b08dd dataviz: handle missing visualization (#40104) 2020-02-24 17:26:19 +01:00
Frédéric Péters 63aa76b8fa maps: extend opacity help text with note about 0=transparent, 1=opaque (#40092) 2020-02-21 19:11:02 +01:00
Frédéric Péters 0670a5f25e maps: add space before "default layer" label (#40091) 2020-02-21 19:11:02 +01:00
Frédéric Péters aa547dbfca search: pass dictionary configuration to search query (#40088) 2020-02-21 16:44:36 +01:00
Frédéric Péters 34bc84a5cf translation update 2020-02-21 16:35:57 +01:00
Frédéric Péters f5ed29d569 general: display correct page when same slug is repeated in hierarchy (#38152) 2020-02-21 16:33:54 +01:00
Lauréline Guérin 8363b36208
maps: variables in translation strings (#40081) 2020-02-21 14:43:45 +01:00
Frédéric Péters 40d8ef1d05 translation update 2020-02-21 14:17:35 +01:00
Lauréline Guérin a11801ba25
maps: MapLayer slug unicity (#39539) 2020-02-20 14:43:12 +01:00
Lauréline Guérin 9a08778110
maps: define tiles layers with opacity (#22639) 2020-02-20 09:37:21 +01:00
Lauréline Guérin 9881331665
maps: default tiles layer (#22639) 2020-02-20 09:37:21 +01:00
Lauréline Guérin 7b186a3dfb
maps: add tiles layer (#22639) 2020-02-20 09:37:21 +01:00
Lauréline Guérin 34e627b0d8
maps: new MapLayerOptions model (#22639) 2020-02-20 09:37:21 +01:00
Lauréline Guérin 3d3805d2cd
maps: add missing migrations 2020-02-20 09:37:20 +01:00
Frédéric Péters d482ba7e12 wcs: mark various cells as depending on user (#40042) 2020-02-20 08:42:31 +01:00
Frédéric Péters 038ab5d4a1 misc: fix method signature for json is_user_dependant (#40033) 2020-02-19 17:00:52 +01:00
Frédéric Péters f74979bb0c misc: mark json cell that are dependant on user (#40033) 2020-02-19 16:52:46 +01:00
Frédéric Péters 2f19eb1bd1 misc: don't use direct attribute access in search rendering (#40035) 2020-02-19 16:52:46 +01:00
Frédéric Péters 5e1b222e91 fargo: don't render anything for search index (#40032) 2020-02-19 16:52:46 +01:00
Frédéric Péters 3004989cdc search: allow failures to enable unaccent extension (#40024) 2020-02-19 16:45:08 +01:00
Frédéric Péters e2348d6977 search: create and use unaccentuated dictionary (#34191) 2020-02-19 14:11:15 +01:00
Frédéric Péters 149e68b7e7 kb: limit last updated pages content to visible pages (#39484) 2020-02-19 14:11:15 +01:00
Frédéric Péters 9c841ea861 wcs: return empty/error response for requests made to nonexistent sites (#39972) 2020-02-19 12:17:01 +01:00
Lauréline Guérin fa17bffadf
pages: add a default on Page.creation_timestamp field (#39865) 2020-02-18 14:02:09 +01:00
Frédéric Péters 7698d8a398 general: redo full text search using querysets (#33632) 2020-02-18 13:56:29 +01:00
Lauréline Guérin 06417b1ff9
cells: take a snapshot when a link is added to a list of links cell 2020-02-18 09:54:02 +01:00
Valentin Deniaud eda9d548a0 misc: allow importing objects with non existent fields (#39768) 2020-02-17 15:35:33 +01:00
Lauréline Guérin 7222ec141a
misc: reduce querysets on page edition page (#39761) 2020-02-17 14:13:01 +01:00
Lauréline Guérin 1f80623e2b
misc: add tests on num queries (#39761) 2020-02-17 14:11:50 +01:00
Frédéric Péters 44b3170137 manager: don't display forms for disabled cells (#39766) 2020-02-15 10:00:58 +01:00
Lauréline Guérin c78ba2936c
lingo: raise a 404 if regie or payment backend is not found (#39846) 2020-02-14 10:21:33 +01:00
Thomas NOËL be6253986c debian: log tenants names on migrate_schemas 2020-02-12 15:24:42 +01:00
Frédéric Péters fb1fd91e47 misc: distribute combo.apps.kb templates (#39617) 2020-02-06 19:20:44 +01:00
Frédéric Péters f54db26eb4 manager: restore display of pwa navigation entries (#39553) 2020-02-05 11:14:17 +01:00
Frédéric Péters cfa9eedcd0 gallery: add translation for application name (#39504) 2020-02-04 16:45:41 +01:00
Frédéric Péters 90f166505b translation: uniform/fix new "exclude from navigation" labels (#39518) 2020-02-04 12:13:45 +01:00
Benjamin Dauvergne 5412f0a58c lingo: use query string when request body is empty (#39491)
Some backends (PayFiP WS) put the response in the query string of a
POST.
2020-02-03 17:16:54 +01:00
Lauréline Guérin a188512c5b
lingo: return HttpResponseBadRequest instead of Exception (#39277) 2020-02-03 14:47:59 +01:00
Frédéric Péters 300d1b6579 translation update 2020-02-03 14:32:16 +01:00
Frédéric Péters d8d58e2268 kb: use correct page URLs (#39483) 2020-02-03 14:27:00 +01:00
Frédéric Péters d010ee0afd kb: add default limit (#39477) 2020-02-03 14:27:00 +01:00
Valentin Deniaud dcc6d9f97a assets: import/export slot assets (#37674) 2020-02-03 10:06:53 +01:00
Nicolas Roche e451097bef kb: add cell to display last page updates (#39091) 2020-02-03 09:21:27 +01:00
Nicolas Roche bd558163a8 data: get descendants of a page (#39091) 2020-02-03 09:21:27 +01:00
Nicolas Roche 3e5e80f6e6 data: deprecate get_last_update_time using signals (#39091) 2020-02-03 09:21:27 +01:00
Nicolas Roche c84c4751e2 data: add creation timestamp on Page object (#39091) 2020-02-03 09:21:27 +01:00
Frédéric Péters 9c02f4a883 misc: add link and subtitle to style demo page (#39462) 2020-02-02 19:25:27 +01:00
Lauréline Guérin f94bcff4d9
page: new pages are excluded from navigation by default (#17659) 2020-01-31 15:25:03 +01:00
Lauréline Guérin 8e7e745f5d
misc: PytestUnknownMarkWarning pytest.mark.freeze_time (#39438)
PytestUnknownMarkWarning: Unknown pytest.mark.freeze_time - is this a typo?
You can register custom marks to avoid this warning - for details,
see https://docs.pytest.org/en/latest/mark.html
2020-01-31 15:17:26 +01:00
Lauréline Guérin 11882c5810
misc: remove DeprecationWarning unescape (#39438)
DeprecationWarning: The unescape method is deprecated
and will be removed in 3.5, use html.unescape() instead.
2020-01-31 15:17:16 +01:00
Lauréline Guérin f2807ce2ec
misc: RemovedInDjango20Warning is_authenticated and is_anonymous (#39438)
RemovedInDjango20Warning: Using user.is_authenticated() and user.is_anonymous()
as a method is deprecated. Remove the parentheses to use it as an attribute.
2020-01-31 11:30:13 +01:00
Lauréline Guérin d217767257
misc: remove RuntimeWarning naive datetime (#39438)
RuntimeWarning: DateTimeField received a naive datetime
2020-01-31 11:30:13 +01:00
Lauréline Guérin 31f2899f8f
misc: remove RemovedInDjango21Warning render() method widget (#39438)
RemovedInDjango21Warning: Add the `renderer` argument to the render() method of
<Widget>. It will be mandatory in Django 2.1.
2020-01-31 11:30:13 +01:00
Lauréline Guérin c6198e6bc1
misc: remove DeprecationWarning encodestring (#39438)
DeprecationWarning: encodestring() is a deprecated alias since 3.1, use encodebytes()
2020-01-31 11:30:13 +01:00
Lauréline Guérin 126df789fe
misc: remove RemovedInDjango21Warning login() view (#39438)
RemovedInDjango21Warning: The login() view is superseded by the class-based LoginView().
2020-01-31 11:30:13 +01:00
Lauréline Guérin 9590604d49
misc: remove DeprecationWarning decodestring (#39438)
DeprecationWarning: decodestring() is a deprecated alias since Python 3.1, use decodebytes()
2020-01-31 11:30:12 +01:00
Frédéric Péters 7a5af339a4 tox: stop testing against python 2 (#39330) 2020-01-28 16:28:13 +01:00
Frédéric Péters 912f544a01 translation update 2020-01-28 11:43:54 +01:00
Frédéric Péters acbcbbc132 misc: use a more readable conversion of python to css class name (#39314) 2020-01-28 11:41:03 +01:00
Frédéric Péters 01e435d8f5 js: remove traces of removed parameters cell (#39315) 2020-01-28 11:19:05 +01:00
Valentin Deniaud baab48cd97 templatetags: add date filters (#36943) 2020-01-27 14:50:08 +01:00
Valentin Deniaud 7bc26d0365 dataviz: display total in tables (#37903) 2020-01-27 14:33:43 +01:00
Benjamin Dauvergne 6ab914d714 lingo: remove ':' character from return url (#39256) 2020-01-27 14:29:38 +01:00
Lauréline Guérin e40a3e8b67
placeholder: optional placeholders display and edition (#37135) 2020-01-27 10:53:24 +01:00
Lauréline Guérin cfdaeaf94d
misc: add missing migrations on lingo app (#18320) 2020-01-27 10:32:49 +01:00
Lauréline Guérin 4e6a7f4a5d
lingo: add labels to TIPI regies (#18320) 2020-01-27 10:32:48 +01:00
Frédéric Péters e3ae82a7be translation update 2020-01-26 19:02:19 +01:00
Frédéric Péters 3b02a99687 misc: remove obsolete parameters cell (#39087) 2020-01-24 10:57:38 +01:00
Lauréline Guérin 24619ce6db
cells: add a cell type "list of links" (#11006) 2020-01-24 10:31:43 +01:00
Frédéric Péters a00b640090 misc: add foldable cell and list of links to style demo page (#39112) 2020-01-24 09:03:33 +01:00
Frédéric Péters e54e61d2de maps: use Leaflet.GestureHandling to avoid zooming on map during scroll (#39057) 2020-01-24 09:03:33 +01:00
Frédéric Péters ab6373baf3 commands: filter on module name in cron command (#39090)
(instead of dotted name, e.g. lingo vs combo.apps.lingo)
2020-01-24 09:03:32 +01:00
Frédéric Péters 8556759486 manager: limit visibility summary width, to keep space for page title (#39089) 2020-01-24 09:03:32 +01:00
Frédéric Péters 0069c0495a misc: enable support for timeout in json cell post actions (#39084) 2020-01-24 09:03:32 +01:00
Frédéric Péters bdc78a81f4 misc: update migration to change upload path from bytes to strings 2020-01-21 20:27:08 +01:00
Emmanuel Cazenave 3177f5067d lingo: move transaction identifier to URL path (#39074) 2020-01-20 17:59:01 +01:00
Frédéric Péters d6ada27492 lingo: restore gettext call for caisse d’épargne 2020-01-17 15:43:25 +01:00
Frédéric Péters 6226d1ded4 tox: don't use pytest 5.3.3 as it's much slower 2020-01-17 14:53:18 +01:00
Frédéric Péters f17553257c misc: prefer latest match when looking for skeleton (#38899) 2020-01-17 14:30:02 +01:00
Frédéric Péters dea9a0484a lingo: don't mark untranslatable payment system names for translation (#39049) 2020-01-17 14:29:25 +01:00
Benjamin Dauvergne 495a311efd lingo: declare payfip backends (#39042) 2020-01-17 00:27:06 +01:00
Frédéric Péters a46ddba35b translation update 2020-01-16 12:36:27 +01:00
Frédéric Péters e55eb141e0 translations: spelling fix 2020-01-16 12:35:20 +01:00
Emmanuel Cazenave 5bc2214715 translation update 2020-01-16 12:22:00 +01:00
Emmanuel Cazenave 36588dd357 lingo: support anonymous and no basket payment (#36876) 2020-01-16 11:44:23 +01:00
Emmanuel Cazenave 266b37db6f lingo: check response signature later (#36876) 2020-01-16 11:44:23 +01:00
Frédéric Péters fb8c07a106 translation update 2020-01-15 16:23:25 +01:00
Frédéric Péters 866615b2e0 dataviz: display warning message when dataviz has no cached json (#38947) 2020-01-15 14:56:53 +01:00
Frédéric Péters 9fe9aaae62 misc: require newer(/latest) xstatic-roboto version (#38771) 2020-01-15 14:28:47 +01:00
Frédéric Péters 31d5c01c9f templatetags: add |startswith:"..." (#38897) 2020-01-13 17:55:12 +01:00
Frédéric Péters c8b764ff25 misc: remove obsolete user search cell (#32039) 2020-01-13 17:55:12 +01:00
Frédéric Péters aa5099c771 general: only apply accessible foldable support once (#38900) 2020-01-13 17:55:12 +01:00
Frédéric Péters fca2f06371 lingo: add compatibility with jsonfield used on postgresql < 9.4 (#38857) 2020-01-13 17:55:12 +01:00
Emmanuel Cazenave 7050df871a settings: use MIDDLEWARE (#38393) 2020-01-13 17:55:12 +01:00
Frédéric Péters 6c7449d49a tests: also run wcs tests in python 3 (#38679) 2020-01-11 13:21:26 +01:00
Thomas NOËL 800d9a1c9e jsoncell: increase url max length up to 500 chars (#38839) 2020-01-08 12:24:06 +01:00
Serghei Mihai cef6a37111 tipi: allow bigger payment amounts (#38799) 2020-01-07 10:30:03 +01:00
Frédéric Péters cc2f229cf8 translation update 2019-12-28 15:55:24 +01:00
Frédéric Péters 71421c4266 manager: fix handling of missing groups with python 3 (#38674) 2019-12-21 16:07:32 +01:00
Frédéric Péters 04244f5ad5 gitignore: ignore generated css file 2019-12-21 16:07:32 +01:00
Lauréline Guérin 9c076eceaf
lingo: reduce queryset number (#38115) 2019-12-20 10:48:02 +01:00
Lauréline Guérin 93f8357f4f
lingo: check num queries on transaction list page (#38115) 2019-12-20 10:45:50 +01:00
Frédéric Péters ac3b6c3d10 jenkins: add timeout to builds 2019-12-18 14:44:12 +01:00
Frédéric Péters ec483fae00 tox: update django-mellon requirement to a working version 2019-12-18 11:08:56 +01:00
Frédéric Péters f8364c3b48 debian: bump django-mellon dependency to working version 2019-12-18 10:49:37 +01:00
Frédéric Péters dbefbd3537 manager: limit page IDs in path to digits (#38607) 2019-12-18 10:07:24 +01:00
Frédéric Péters 43b6161159 a11y: add support for foldable/folding cells (#38192) 2019-12-17 14:54:03 +01:00
Frédéric Péters 904655c293 misc: add context elements to have navigation/footer in mellon pages (#19515) 2019-12-17 11:34:50 +01:00
Benjamin Dauvergne 499093ca7d tox.ini: use latest pytest (#38312) 2019-12-10 11:34:20 +01:00
Benjamin Dauvergne 037ee9863d Jenkinsfile: use mergeJunitResults() (#38312) 2019-12-10 09:26:33 +01:00
Frédéric Péters 5fafd3b13b misc: hide pagination links in folded cells (#38245) 2019-12-09 23:24:35 +01:00
Frédéric Péters b6443edfa3 dataviz: keep more of legend labels (unless narrow cell) (#37347) 2019-12-06 10:51:57 +01:00
Frédéric Péters fcafb76803 translation update 2019-12-06 10:51:57 +01:00
Lauréline Guérin ea11c7f03e
lingo: action mark_as_notified (#21626) 2019-12-06 10:35:02 +01:00
Lauréline Guérin 2920cf5d79
lingo: add search field (#21626) 2019-12-06 10:35:02 +01:00
Lauréline Guérin 964d0a87aa
lingo: new page to display payments in error (#21626) 2019-12-06 10:35:02 +01:00
Lauréline Guérin 850bab5600
wcs: include drafts or not in current forms cell (#20231) 2019-12-06 10:12:22 +01:00
Frédéric Péters ff792192d8 tests: update tracking code rate limit check for new domain check (#37996) 2019-11-29 14:29:13 +01:00
Frédéric Péters 71a950d1c0 tests: fix check against visualisation name 2019-11-29 12:05:56 +01:00
Frédéric Péters 95a159eb7a dataviz: display a single decimal in percent graphs/tables (#37928) 2019-11-29 11:58:11 +01:00
Frédéric Péters 9d5fc48e09 settings: add empty KNOWN_SERVICES (#37996) 2019-11-29 11:22:42 +01:00
Frédéric Péters 35e910d2aa wcs: disallow redirects to unknown services after tracking code error (#37996) 2019-11-29 11:22:42 +01:00
Frédéric Péters fd93d0b904 dataviz: mark as unsupported arrays with varying lengths (#37899) 2019-11-24 10:45:41 +01:00
Frédéric Péters a87445f075 translation update 2019-11-22 10:13:46 +01:00
Valentin Deniaud 8196e9c1eb wcs: add pagination for current forms/drafts cells (#10179) 2019-11-22 10:01:54 +01:00
Thomas NOËL 57542ab058 add combo templatetags in builtins (#37273) 2019-11-20 16:53:32 +01:00
Thomas NOËL f887b66ead search: add autofocus option on search input (#37811) 2019-11-20 16:51:49 +01:00
Lauréline Guérin 8109a2a5c9
wcs: add a filter categories to current drafts cell (#37116) 2019-11-20 16:39:51 +01:00
Lauréline Guérin be7053850f
cell: display error message on ajax call failure (#29124) 2019-11-20 16:36:44 +01:00
Frédéric Péters de7f5dbb15 manager: add CSS class to page actions (#37410) 2019-11-20 15:19:18 +01:00
Lauréline Guérin 8ed1923e44
page: fix slug on page duplication (#37733) 2019-11-19 15:27:21 +01:00
Nicolas Roche 879be68be4 utils: do not raise on Django syntax error in templated_url (#34518) 2019-11-18 16:00:34 +01:00
Frédéric Péters 8a80889198 translation update 2019-11-13 15:02:06 +01:00
Thomas NOËL 48bb5540f3 wcs: rename "Forms in your care" cell to "Forms to process" (#37567) 2019-11-13 15:00:22 +01:00
Frédéric Péters e14dcc0e72 setup: get back to correct directory if compilemessages fails 2019-11-13 15:00:22 +01:00
Lauréline Guérin dd08e622e7
wcs: exclude anonymised forms from search (#37175) 2019-11-07 15:03:32 +01:00
Frédéric Péters 6bc1152aca translation update 2019-11-06 09:15:00 +01:00
Lauréline Guérin 63a4bf975f
maps: add link to existing maps on manager map page (#16908) 2019-11-05 10:40:49 +01:00
Thomas NOËL 992f6fc71a skeleton: do not consider empty redirection URL (#37431) 2019-11-05 09:13:45 +01:00
Frédéric Péters aa54f9b070 translation update 2019-11-04 09:27:58 +01:00
Frédéric Péters 4656b50574 manager: add uppercases to page actions (#37398) 2019-11-04 08:57:20 +01:00
Lauréline Guérin 6be400fcfc
page: add a duplicate action (#24526) 2019-11-04 08:40:01 +01:00
Lauréline Guérin 09507f8988
pages: add navigation buttons on page view (#12437) 2019-11-03 17:28:17 +01:00
Lauréline Guérin 45309fd524
pages: add a param check_visibility to next/previous methods (#12437) 2019-11-03 17:28:17 +01:00
Lauréline Guérin fbfeb64a83
lingo: do not add a basket item to a remote regie (#27854) 2019-11-03 17:27:10 +01:00
Lauréline Guérin 818ae481c7
lingo: export transactions between 2 dates (#35988) 2019-11-03 17:25:48 +01:00
Lauréline Guérin 45fcb36131
assets: extends assets stable URI with sorl.thumbnail options (#25039) 2019-11-03 17:23:48 +01:00
Lauréline Guérin b7b06cbd58
page: form to add a child (#37200) 2019-10-31 16:59:27 +01:00
Lauréline Guérin 6c9803d241
page: put all secondary actions in a menu (#24526) 2019-10-31 16:59:27 +01:00
Lauréline Guérin 7da07bbf2b
wcs: add a cell "forms in your care" (#31989) 2019-10-31 10:09:39 +01:00
Frédéric Péters a7d3daa295 translation update 2019-10-29 16:15:37 +01:00
Frédéric Péters 2c54172cc3 manager: right align page/cell visibility info (#37285) 2019-10-29 10:26:11 +01:00
Lauréline Guérin 0c854a9e50
pages: always display placeholder blocks in skeleton (#26641) 2019-10-29 10:17:12 +01:00
Lauréline Guérin 7087aa3849
dj2: assignment_tag is deprecated (#36895)
remove RemovedInDjango20Warning:
assignment_tag() is deprecated. Use simple_tag() instead
2019-10-29 08:36:17 +01:00
Lauréline Guérin 3244864c36
dj2: fix include in urls.py (#36895)
remove RemovedInDjango20Warning:
Passing a 3-tuple to django.conf.urls.include() is deprecated.
Pass a 2-tuple containing the list of patterns and app_name, and provide the
namespace argument to include() instead.
2019-10-29 08:36:06 +01:00
Lauréline Guérin a7417488eb
dj2: use set and/or clear method for M2M (#36895)
remove RemovedInDjango20Warning:
Direct assignment to the forward side of a many-to-many set is deprecated due
to the implicit save() that happens.
Use items.set() instead.
2019-10-29 08:35:53 +01:00
Lauréline Guérin ccf99d908f
dj2: is_anonymous and is_authenticated are now properties (#36895)
remove RemovedInDjango20Warning:
Using user.is_authenticated() and user.is_anonymous() as a method is deprecated.
Remove the parentheses to use it as an attribute.
2019-10-29 08:35:46 +01:00
Lauréline Guérin 49830fc9c5
dj2: add explicit on_delete on all ForeignKey (#36895)
remove RemovedInDjango20Warning:
on_delete will be a required arg for ForeignKey in Django 2.0.
Set it to models.CASCADE on models and in existing migrations if you want to
maintain the current default behavior.
See https://docs.djangoproject.com/en/1.11/ref/models/
fields/#django.db.models.ForeignKey.on_delete
2019-10-29 08:35:21 +01:00
Thomas NOËL 94d25e1cd5 search: handle bad JSON engine results as empty (#37174) 2019-10-28 17:02:59 +01:00
Lauréline Guérin ce355fa833
page: display page visibility on page list (#16028) 2019-10-28 09:13:02 +01:00
Lauréline Guérin 82503cdd7d
feedcell: do not fail if service is not available (#21383) 2019-10-28 09:05:05 +01:00
Lauréline Guérin 8f0bc9a059
page: fix initial values for groups in visibility form (#37111) 2019-10-28 09:03:37 +01:00
Frédéric Péters 3be27ce16b lingo: make sure eopayment is given a string (#37214) 2019-10-25 10:15:37 +02:00
Frédéric Péters 226f93dbd7 lingo: don't crash sorting invoices that have no creation date (#37208) 2019-10-25 10:04:17 +02:00
Frédéric Péters 0f0353266e lingo: redirect to homepage if there's no basket page (#37210) 2019-10-25 09:55:29 +02:00
Frédéric Péters b2904f0291 search: split cell in multiple blocks (#37161) 2019-10-25 09:55:29 +02:00
Frédéric Péters 446495b2c2 translation update 2019-10-18 09:58:25 +02:00
Lauréline Guérin beefe2c348
assets: check file extension on overwrite (#30897) 2019-10-17 21:24:05 +02:00
Frédéric Péters ef58cc3235 lingo: retry payment notification asynchronously (#37036) 2019-10-17 15:06:59 +02:00
Frédéric Péters e394264606 maps: compute GeoJSON URLs from templates (#36124) 2019-10-16 14:54:45 +02:00
Frédéric Péters 8a999615a3 dataviz: format durations in graphs (#36874) 2019-10-15 09:57:34 +02:00
Lauréline Guérin 457c40d612
assets: use asset.name instead of asset.file.name (#33959)
when accessing asset.file, the file is opened, and this can lead to an
IOError
2019-10-14 15:02:24 +02:00
Lauréline Guérin 08a739e0d8
assets: fix asset_url templatetag when file does not exist (#33959) 2019-10-14 15:01:32 +02:00
Frédéric Péters baf081c2c2 debian: switch to Python 3 (#36233) 2019-10-13 15:41:19 +02:00
Frédéric Péters 848f36482d cron: use print as a function (#36233) 2019-10-13 15:41:19 +02:00
Frédéric Péters 2beee8e154 tests: make test_chartng_cell_view run standalone 2019-10-13 15:41:19 +02:00
Frédéric Péters 6c4ad54bf4 dataviz: add missing gettext import (#36836) 2019-10-10 13:21:06 +02:00
Frédéric Péters 8289e73fcb pwa: use local vapid keys even if python-cryptography is too old (#36818) 2019-10-09 21:39:13 +02:00
Emmanuel Cazenave 5b71bfb0f7 jenkins: use ci@entrouvert.org for notifications 2019-10-02 13:43:01 +02:00
Frédéric Péters a413d67417 maps: allow variables in geojson URLs (#36124) 2019-10-01 13:49:34 +02:00
Frédéric Péters d1c8438d7a debian: distribute manage.py in combo package, not python-combo (#36508) 2019-09-29 10:57:17 +02:00
Frédéric Péters eab1ee6750 add missing trailing space to translation 2019-09-25 23:49:27 +02:00
Frédéric Péters 0378b9fed5 misc: limit api/menu-badges parameters to digits (#36387) 2019-09-25 08:52:24 +02:00
Frédéric Péters b7763d995f general: remove combo.apps.momo (#32913) 2019-09-18 12:00:56 +02:00
Frédéric Péters 8832ba028c manager: recreate full hierarchy when moving page to a new parent (#36127) 2019-09-15 07:51:57 +02:00
Frédéric Péters b8eb8ee54a pwa: don't crash if X.962 serialization is not available (#35954) 2019-09-09 15:52:12 +02:00
Thomas NOËL 5cce8d3490 migrations: add missing validators on jsoncell template_string (#35670) 2019-09-07 14:01:43 +02:00
Thomas NOËL 9c4af78b04 maps: use ICONS as choices in icon field migrations (#35668) 2019-09-07 14:01:43 +02:00
Frédéric Péters b96880ace6 translation update 2019-09-03 15:51:25 +02:00
Frédéric Péters 3b2df2f522 dataviz: add support for loop, warn if there are three dimensions (#35698) 2019-09-02 18:20:41 +02:00
Frédéric Péters d2a2dd6c91 misc: use native admin login/logout views if public URLs are not loaded (#35678) 2019-09-01 10:44:28 +02:00
Frédéric Péters 9627292845 wcs: add template blocks for text parts of tracking code cell (#35623) 2019-08-28 09:34:02 +02:00
Frédéric Péters 08eb7763fc debian: use sassc to build css files (#35497) 2019-08-23 11:16:01 +02:00
Frédéric Péters 86519bca0e manager: force imported json to be read as text (#35425) 2019-08-18 14:47:00 +02:00
Frédéric Péters 5bb06f8200 pwa: force json request to be read as text (#35425) 2019-08-18 14:46:59 +02:00
Frédéric Péters 841d338970 lingo: force json request to be read as text (#35425) 2019-08-18 14:46:57 +02:00
Frédéric Péters f06322cf7a tox: load lasso (#35425) 2019-08-18 14:46:56 +02:00
Frédéric Péters f6e46c07e1 tests: update map tests to load json from textual response (#35425) 2019-08-18 14:46:54 +02:00
Frédéric Péters 58ee29aeb7 tests: update notifications to convert content before loading as json (#35425) 2019-08-18 14:46:52 +02:00
Frédéric Péters e5281adb2b tests: update lingo tests to load json from textual response (#35425) 2019-08-18 14:46:51 +02:00
Frédéric Péters fb96c6d4f3 dashboard: force json request to be read as text (#35425) 2019-08-18 14:46:49 +02:00
Frédéric Péters 826acc3b24 misc: use json method to load JSON responses (#35425) 2019-08-18 14:46:48 +02:00
Frédéric Péters e42143e009 tests: don't check for invalid parameter decoding in python 3 (#35425) 2019-08-18 14:46:46 +02:00
Frédéric Péters 1681ec8672 tests: fix check of mocked call arguments to work with python 3 (#35425) 2019-08-18 14:46:43 +02:00
Frédéric Péters 50c44019a5 python3: fix encoding/decoding of PWA private key (#35425) 2019-08-18 14:46:41 +02:00
Frédéric Péters 176576c5e3 tests: upload files as binaries (#35425) 2019-08-18 14:46:40 +02:00
Frédéric Péters f48c661980 tests: check against text version of responses (#35425) 2019-08-18 14:46:38 +02:00
Frédéric Péters cbb1814087 python3: get real value from .values() (#35425) 2019-08-18 14:46:26 +02:00
Frédéric Péters 00eb57befd python3: encode before hashing (#35425) 2019-08-18 14:46:22 +02:00
Frédéric Péters d34219379f Revert "tox: only test against Python 2 for now"
This reverts commit f85bcb4436.
2019-08-18 12:57:45 +02:00
Frédéric Péters f85bcb4436 tox: only test against Python 2 for now 2019-08-18 12:23:36 +02:00
Frédéric Péters 731eb7ee61 tox: don't force python2 2019-08-18 11:53:03 +02:00
Frédéric Péters 62456045e0 tox: limit django-jsonfield version for python2 2019-08-18 11:37:27 +02:00
Frédéric Péters 925e2efd44 settings: add django context_processors.request to context processors (#16460) 2019-08-15 16:29:34 +02:00
Christophe Siraut 2001523afa debian: update nginx-example.conf to set Access-Control-Allow-Origin (#22779) 2019-08-15 16:26:33 +02:00
Christophe Siraut a5df6a34b8 debian: update nginx-example.conf to serve theme resources (#22779) 2019-08-15 16:26:33 +02:00
Christophe Siraut 887c43cfc9 debian: update nginx-example.conf to redirect non-ssl requests (#22779) 2019-08-15 16:26:33 +02:00
Frédéric Péters f20ce6ecdc wcs: limit user forms cell to 100 items (#35407) 2019-08-15 15:21:42 +02:00
Benjamin Dauvergne eb8674866a requests_wrapper: log the real URL (#35353) 2019-08-15 15:21:35 +02:00
Thomas NOËL ff6878c39d lingo: accept french amount format (#35052) 2019-08-15 10:53:49 +02:00
Frédéric Péters 73a7643788 dataviz: don't display unconfigured graphs (#35405) 2019-08-14 17:05:43 +02:00
Thomas NOËL 073520e111 assets manager: consider missing files as empty (#35397) 2019-08-14 14:37:43 +02:00
Frédéric Péters 9ec36e807d translation update 2019-08-14 14:24:08 +02:00
Frédéric Péters 4836ed82d8 misc: add rate limiting to tracking code URLs (#35395) 2019-08-14 13:30:29 +02:00
Frédéric Péters 681c81c049 tests: adapt to changes in wcs runscript command (#34405) 2019-08-14 13:30:29 +02:00
Frédéric Péters 7a0e95a65b translation update 2019-08-13 19:28:14 +02:00
Frédéric Péters 6f82fe8eb4 dataviz: render graph locally using pygal (#20771) 2019-08-13 19:28:14 +02:00
Frédéric Péters ee2d6e7395 tox: update dependencies for new wcs django-ratelimit usage 2019-08-13 19:28:14 +02:00
Frédéric Péters e375bf09e8 assets: add basic API to upload an asset from wcs (#22391) 2019-08-13 16:28:00 +02:00
Nicolas Roche d782655ff4 data: raise a CommandError if import-site fails when roles are missing (#35289) 2019-08-12 15:28:34 +02:00
Nicolas Roche 8df192e455 data: make import_site an atomic query (#33948) 2019-08-06 13:51:54 +02:00
Benjamin Dauvergne 546336acc7 requests_wrapper: sign URL of prepared requests (#35225) 2019-08-06 11:34:48 +02:00
Thomas NOËL dda798b748 translation update 2019-07-31 15:10:28 +02:00
Frédéric Péters 226404bbbc misc: use unique cell slug as id on skeleton pages (#35087) 2019-07-31 14:55:40 +02:00
Thomas NOËL 4167cf082a manager: validate template syntax in json prototype cell (#34738) 2019-07-31 11:50:37 +02:00
Frédéric Péters 74f3fd09e8 trivial: remove debugging variable from context 2019-07-27 17:39:00 +02:00
Thomas NOËL 32663dc98e utils: rewrite previous patch with hobo.signature code (#35050) 2019-07-25 22:44:35 +02:00
Thomas NOËL e473667be1 utils: forbid argument after signature (#35050) 2019-07-25 16:35:34 +02:00
Benjamin Dauvergne 68b376d3f3 lingo: PEP8ize TipiPaymentFormCell (#35008) 2019-07-23 12:06:19 +02:00
Benjamin Dauvergne f93aeb232a lingo: reorder TIPI PESv2 fields (#35008) 2019-07-23 12:06:19 +02:00
Frédéric Péters 371dc7d827 maps: make sure marker icon backdrop is round (#34886) 2019-07-16 12:16:07 +02:00
Frédéric Péters d50ce40499 assets: add template tag to get URL property for CSS (#34812) 2019-07-15 15:08:12 +02:00
Frédéric Péters 34f9b6f512 misc: don't use duplicated slugs as HTML id attributes (#34746) 2019-07-15 15:08:12 +02:00
Frédéric Péters 0f1bf1be29 manager: sort groups in page visibility form (#34818) 2019-07-15 08:45:59 +02:00
Frédéric Péters a72c25dd59 misc: allow template URLs in feed and json cells (#15423) 2019-07-12 13:36:58 +02:00
Frédéric Péters c6b4b5b6bd notifications: don't try passive authentication on count API (#34780) 2019-07-12 10:05:44 +02:00
Frédéric Péters c72dc81afc translation update 2019-07-03 10:42:32 +02:00
Frédéric Péters 877da90fc4 jenkins: add support for hotfix releases (#34485) 2019-07-02 14:18:33 +02:00
Frédéric Péters fecb105a1b pwa: add missing forms.py (#31388) 2019-07-02 13:54:07 +02:00
Frédéric Péters fbcb4eb8c9 pwa: add push notifications migration (#31388) 2019-07-02 13:47:06 +02:00
Frédéric Péters 73648052ce pwa: add option to enable support for push notifications (#31388) 2019-07-02 11:11:31 +02:00
Frédéric Péters 9784211b5a misc: rename "json feed" cell as "json prototype" cell (#34471) 2019-07-01 19:51:18 +02:00
Frédéric Péters 43dad697ce notifications: handle cell being refreshed (#34244) 2019-06-30 09:54:39 +02:00
Frédéric Péters 167dbc1617 notifications: ack on click (#34244) 2019-06-25 18:08:43 +02:00
Frédéric Péters 26589e80a1 notifications: update markup for proper spacing (#34243) 2019-06-25 18:08:43 +02:00
Frédéric Péters 5b4598c901 misc: allow page picture to be svg (#34334) 2019-06-25 18:08:43 +02:00
Frédéric Péters 41d884e88f search: do not log as errors HTTP failures in search engines (#34314) 2019-06-25 16:25:27 +02:00
Frédéric Péters f4616afea8 notifications: accept more characters (: and _) in notification ids (#34242) 2019-06-25 08:16:21 +02:00
Serghei Mihai daa3a1da27 lingo: mark when pesv2 payment protocol is active in tipi form (#34119) 2019-06-20 12:16:52 +02:00
Frédéric Péters 33b4197ea5 wcs: ignore case/spaces in tracking code (#34156) 2019-06-19 10:33:29 +02:00
Frédéric Péters 23f70f720b dataviz: increase length of url field (#34047) 2019-06-19 10:01:01 +02:00
Frédéric Péters 6f55f4489a misc: add a focus-in class when a text element has the focus (#33824) 2019-06-19 10:01:01 +02:00
Benjamin Dauvergne c6095a099c add django request context to requests error logs (#33740) 2019-06-06 16:49:07 +02:00
Frédéric Péters 7aab01c92b misc: return 400 when an improrer next parameter is given to login (#33082) 2019-06-03 12:30:13 +02:00
Frédéric Péters 6439c43b48 wcs: raise a bad request when tracking code is missing from request (#33079) 2019-06-03 12:30:13 +02:00
Frédéric Péters 0863f0fa7a wcs: include form digest in list of forms (#31824) 2019-05-28 12:58:48 +02:00
Frédéric Péters 8808dc8d4b translation update 2019-05-27 14:39:58 +02:00
Frédéric Péters b1ba798e34 dashboard: include dashboard add url in autotile response (#33380) 2019-05-27 14:32:35 +02:00
Frédéric Péters 496bdb525f general: stop supporting django 1.8 (#33423) 2019-05-27 14:32:35 +02:00
Frédéric Péters 1f5bc01627 search: don't index pages with sub slugs (#33419) 2019-05-27 10:00:40 +02:00
Frédéric Péters 6b7d5938fc utils: flatten context passed to get_templated_url (#33393) 2019-05-27 10:00:40 +02:00
Serghei Mihai afe102d95d pwa: add application title parameter (#32371) 2019-05-27 10:00:40 +02:00
Frédéric Péters af9a647b57 misc: don't upscale category pictures (#33340) 2019-05-22 13:46:18 +02:00
Frédéric Péters 92efdda84b maps: limit layer slugs to 50 characters (#33311) 2019-05-21 15:46:56 +02:00
Frédéric Péters d5770b18d7 translation update 2019-05-20 17:40:55 +02:00
Frédéric Péters 19011ae0d3 manager: keep cell open after edit actions (#33103) 2019-05-20 17:40:55 +02:00
Emmanuel Cazenave 07562beebd bump django-ckeditor version compatibility (#33250) 2019-05-20 16:31:12 +02:00
Emmanuel Cazenave 2ab28842af bump eopayment requirement (#33223) 2019-05-20 11:14:24 +02:00
Emmanuel Cazenave d956627fb0 lingo: i18n on Regie.payment_backend (#33221) 2019-05-20 11:13:17 +02:00
Frédéric Péters d4f858b830 misc: give redirect_url access to request(.user) (#33235) 2019-05-20 10:10:50 +02:00
Frédéric Péters 43f25b588f misc: don't crash rendering a text cell without content (#33227) 2019-05-19 10:12:10 +02:00
Christophe Siraut 9aaa49f523 gallery: display gallery title (#33200) 2019-05-18 13:08:53 +02:00
Frédéric Péters ea8b55e4bf misc: declare gallery template and static files in MANIFEST 2019-05-17 11:23:35 +02:00
Thomas NOËL b9c0eee70f map: use Entr'ouvert tiles by default (#33181) 2019-05-17 09:53:06 +02:00
782 changed files with 63361 additions and 16043 deletions

5
.coveragerc Normal file
View File

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

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

@ -0,0 +1,12 @@
# trivial: apply black
47d67c395ef124ea4534ea7c56d48d4e302db430
# misc: apply isort (#52797)
29bc8e66a978b1bbadbc05186599a70ce1b8ef98
# misc: apply pyupgrade (#55868)
f4615c506194cf4eace0af551f14f54552f09dc5
# misc: apply djhtml (#69709)
4784a3990eec0aadadce99bf1da21c0b531b289d
# misc: apply django-upgrade (#69798)
cd498afcb0bcd8d3f2fce98c9747ca1406d9a449
# misc: apply double-quote-string-fixer (#79788)
9d8876e155e8a433ebafbd4ccb9de4960d1830b4

9
.gitignore vendored
View File

@ -11,8 +11,17 @@ combo.egg-info/
.sass-cache/
combo/apps/maps/static/css/combo.map.css
combo/apps/maps/static/css/combo.map.css.map
combo/apps/pwa/static/css/combo.manager.pwa.css
combo/apps/pwa/static/css/combo.manager.pwa.css.map
combo/apps/family/static/css/combo.weekly_agenda.css
combo/apps/dataviz/static/css/combo.multiselectwidget.css
combo/manager/static/css/combo.manager.css
data/themes/gadjo/static/css/agent-portal.css
data/themes/gadjo/static/css/agent-portal.css.map
.cache
.coverage
.pytest_cache/
node_modules/
coverage/
package.json
package-lock.json

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

@ -0,0 +1,36 @@
# 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

29
Jenkinsfile vendored
View File

@ -1,30 +1,43 @@
@Library('eo-jenkins-lib@master') import eo.Utils
@Library('eo-jenkins-lib@main') import eo.Utils
pipeline {
agent any
options { disableConcurrentBuilds() }
options {
disableConcurrentBuilds()
timeout(time: 20, unit: 'MINUTES')
}
stages {
stage('Unit Tests') {
steps {
sh 'tox -rv'
sh 'nox'
}
post {
always {
script {
utils = new Utils()
utils.publish_coverage('coverage.xml')
utils.publish_coverage('coverage.xml,coverage/cobertura-coverage.xml')
utils.publish_coverage_native('index.html')
utils.publish_pylint('pylint.out')
}
junit '*_results.xml'
mergeJunitResults()
}
}
}
stage('Packaging') {
steps {
script {
if (env.JOB_NAME == 'combo' && env.GIT_BRANCH == 'origin/master') {
sh 'sudo -H -u eobuilder /usr/local/bin/eobuilder combo'
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}"
}
}
}
@ -34,7 +47,7 @@ pipeline {
always {
script {
utils = new Utils()
utils.mail_notify(currentBuild, env, 'admin+jenkins-combo@entrouvert.com')
utils.mail_notify(currentBuild, env, 'ci+jenkins-combo@entrouvert.org')
}
}
success {

View File

@ -1,14 +1,16 @@
# locales
recursive-include combo/locale *.po *.mo
recursive-include combo/locales/locale *.po *.mo
# static
recursive-include combo/apps/usersearch/static *.css *.js *.ico *.gif *.png *.jpg
recursive-include combo/apps/lingo/static *.css *.js *.ico *.gif *.png *.jpg
recursive-include combo/apps/dataviz/static *.css *.js *.ico *.gif *.png *.jpg
recursive-include combo/apps/dataviz/static *.css *.js *.ico *.gif *.png *.jpg *.scss
recursive-include combo/apps/dashboard/static *.js
recursive-include combo/apps/family/static *.css *.scss *.js
recursive-include combo/apps/gallery/static *.js
recursive-include combo/apps/maps/static *.css *.scss *.js
recursive-include combo/apps/pwa/static *.css *.scss *.js *.svg
recursive-include combo/manager/static *.css *.js *.ico *.gif *.png *.jpg
recursive-include combo/apps/wcs/static *.js *.css
recursive-include combo/manager/static *.scss *.css *.js *.ico *.gif *.png *.jpg
recursive-include combo/public/static *.css *.js *.ico *.gif *.png *.jpg
# templates
@ -17,13 +19,13 @@ recursive-include combo/apps/assets/templates *.html
recursive-include combo/apps/calendar/templates *.html
recursive-include combo/apps/dashboard/templates *.html
recursive-include combo/apps/search/templates *.html
recursive-include combo/apps/usersearch/templates *.html
recursive-include combo/apps/dataviz/templates *.html
recursive-include combo/apps/dataviz/templates *.html *.svg
recursive-include combo/apps/family/templates *.html
recursive-include combo/apps/fargo/templates *.html
recursive-include combo/apps/gallery/templates *.html
recursive-include combo/apps/kb/templates *.html
recursive-include combo/apps/lingo/templates *.html
recursive-include combo/apps/maps/templates *.html
recursive-include combo/apps/momo/templates *.html
recursive-include combo/apps/newsletters/templates *.html
recursive-include combo/apps/notifications/templates *.html
recursive-include combo/apps/pwa/templates *.html *.js *.json

38
README
View File

@ -13,7 +13,7 @@ Dependencies can be installed with pip,
$ pip install -r requirements.txt
It's then required to get the database configured (./manage.py migrate); by
default it will create a db.sqlite3 file.
default it will create a postgresqsl DB.
You can then run the Django test server for a quick try (you should refer to
the Django documentation for production deployments).
@ -88,11 +88,32 @@ Unit tests are written using py.test, and its pytest-django support library.
DJANGO_SETTINGS_MODULE=combo.settings COMBO_SETTINGS_FILE=tests/settings.py py.test
Tests for w.c.s. cells do require access to the wcsctl script, its location has
to be given in a WCSCTL environment variable, this give a full command line:
WCSCTL=$(pwd)/wcs/wcsctl.py \
DJANGO_SETTINGS_MODULE=combo.settings COMBO_SETTINGS_FILE=tests/settings.py py.test
Code Style
----------
black is used to format the code, using thoses parameters:
black --target-version py37 --skip-string-normalization --line-length 110
isort is used to format the imports, using those parameters:
isort --profile black --line-length 110
pyupgrade is used to automatically upgrade syntax, using those parameters:
pyupgrade --keep-percent-format --py37-plus
djhtml is used to automatically indent html files, using those parameters:
djhtml --tabwidth 2
django-upgrade is used to automatically upgrade Django syntax, using those parameters:
django-upgrade --target-version 3.2
There is .pre-commit-config.yaml to use pre-commit to automatically run these tools
before commits. (execute `pre-commit install` to install the git hook.)
License
@ -120,3 +141,10 @@ Gauge.js
License: MIT
Comment:
From http://bernii.github.io/gauge.js/
Pygal.tooltip.js
Files: combo/apps/dataviz/static/js/pygal.tooltip.js
Copyright: 2015, Florian Mounier Kozea
License: LGPL-3+
Comment:
From https://github.com/Kozea/pygal.js/

View File

@ -1,34 +0,0 @@
# combo - content management system
# Copyright (C) 2017-2018 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import django.apps
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
class AppConfig(django.apps.AppConfig):
name = 'combo.apps.assets'
verbose_name = _('Assets')
def get_before_urls(self):
from . import urls
return urls.urlpatterns
def get_extra_manager_actions(self):
return [{'href': reverse('combo-manager-assets'),
'text': _('Assets')}]
default_app_config = 'combo.apps.assets.AppConfig'

View File

@ -0,0 +1,62 @@
# combo - content management system
# Copyright (C) 2019 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import base64
from io import BytesIO
from django.core.files import File
from rest_framework import permissions, serializers, status
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from .models import Asset
class FileSerializer(serializers.Serializer):
content = serializers.CharField(required=True, allow_blank=False)
content_type = serializers.CharField(required=False, allow_null=True)
filename = serializers.CharField(required=False, allow_null=True)
def validate_content(self, value):
try:
return base64.decodebytes(value.encode('ascii'))
except Exception as e:
raise serializers.ValidationError('content must be base64 (%r)' % e)
class AssetSerializer(serializers.Serializer):
asset = FileSerializer(required=True)
class Set(GenericAPIView):
permission_classes = (permissions.IsAuthenticated,)
serializer_class = AssetSerializer
def post(self, request, key, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
if not serializer.is_valid():
response = {'err': 1, 'err_desc': serializer.errors}
return Response(response, status.HTTP_400_BAD_REQUEST)
data = serializer.validated_data
asset, dummy = Asset.objects.get_or_create(key=key)
asset.asset = File(BytesIO(data['asset']['content']), name=data['asset'].get('filename'))
asset.save()
response = {'err': 0, 'url': request.build_absolute_uri(f'/assets/{key}')}
return Response(response)
view_set = Set.as_view()

32
combo/apps/assets/apps.py Normal file
View File

@ -0,0 +1,32 @@
# combo - content management system
# Copyright (C) 2017-2018 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import django.apps
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
class AppConfig(django.apps.AppConfig):
name = 'combo.apps.assets'
verbose_name = _('Assets')
def get_before_urls(self):
from . import urls
return urls.urlpatterns
def get_extra_manager_actions(self):
return [{'href': reverse('combo-manager-assets'), 'text': _('Assets')}]

View File

@ -14,14 +14,30 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import PIL
from django import forms
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
def validate_asset_file(value):
try:
PIL.Image.open(value.file)
except PIL.UnidentifiedImageError:
pass # not an image
except PIL.Image.DecompressionBombError as expt:
raise ValidationError(
_('Uploaded image exceeds size limits: %(detail)s'), params={'detail': str(expt)}
)
return True
class AssetUploadForm(forms.Form):
upload = forms.FileField(label=_('File'))
upload = forms.FileField(label=_('File'), validators=[validate_asset_file])
class AssetsImportForm(forms.Form):
assets_file = forms.FileField(label=_('Assets File'))
assets_file = forms.FileField(
label=_('Assets File'), help_text=_('Archive (.tar) with asset files as content.')
)
overwrite = forms.BooleanField(label=_('Overwrite Existing Files'), required=False)

View File

@ -1,24 +1,23 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-06-12 11:42
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
dependencies = []
operations = [
migrations.CreateModel(
name='Asset',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('key', models.CharField(max_length=128, unique=True)),
('asset', models.FileField(upload_to=b'assets')),
('asset', models.FileField(upload_to='assets')),
],
),
]

View File

@ -0,0 +1,15 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='asset',
name='key',
field=models.CharField(max_length=256, unique=True),
),
]

View File

@ -19,6 +19,7 @@ import json
from django.core import serializers
from django.db import models
class AssetManager(models.Manager):
def get_by_natural_key(self, key):
return self.get(key=key)
@ -27,7 +28,7 @@ class AssetManager(models.Manager):
class Asset(models.Model):
objects = AssetManager()
key = models.CharField(max_length=128, unique=True)
key = models.CharField(max_length=256, unique=True)
asset = models.FileField(upload_to='assets')
@classmethod
@ -35,8 +36,11 @@ class Asset(models.Model):
return [x.get_as_serialized_object() for x in Asset.objects.all()]
def get_as_serialized_object(self):
serialized_asset = json.loads(serializers.serialize('json', [self],
use_natural_foreign_keys=True, use_natural_primary_keys=True))[0]
serialized_asset = json.loads(
serializers.serialize(
'json', [self], use_natural_foreign_keys=True, use_natural_primary_keys=True
)
)[0]
del serialized_asset['model']
del serialized_asset['pk']
return serialized_asset
@ -49,7 +53,7 @@ class Asset(models.Model):
@classmethod
def load_serialized_object(cls, json_asset):
json_asset['model'] = 'assets.asset'
asset, created = Asset.objects.get_or_create(key=json_asset['fields']['key'])
asset, dummy = Asset.objects.get_or_create(key=json_asset['fields']['key'])
json_asset['pk'] = asset.id
asset = [x for x in serializers.deserialize('json', json.dumps([json_asset]))][0]
asset = next(serializers.deserialize('json', json.dumps([json_asset]), ignorenonexistent=True))
asset.save()

View File

@ -2,16 +2,16 @@
{% load i18n %}
{% block appbar %}
<h2>{% trans 'Delete Asset' %}</h2>
<h2>{% trans 'Delete Asset' %}</h2>
{% endblock %}
{% block content %}
<form method="post">
{% csrf_token %}
{% blocktrans %}Are you sure you want to delete this?{% endblocktrans %}
<div class="buttons">
<button class="delete-button">{% trans 'Delete' %}</button>
<a class="cancel" href="{% url 'combo-manager-assets' %}">{% trans 'Cancel' %}</a>
</div>
</form>
<form method="post">
{% csrf_token %}
{% blocktrans %}Are you sure you want to delete this?{% endblocktrans %}
<div class="buttons">
<button class="delete-button">{% trans 'Delete' %}</button>
<a class="cancel" href="{% url 'combo-manager-assets' %}">{% trans 'Cancel' %}</a>
</div>
</form>
{% endblock %}

View File

@ -2,20 +2,20 @@
{% load i18n %}
{% block appbar %}
<h2>{% trans "Overwrite Asset" %}</h2>
<h2>{% trans "Overwrite Asset" %}</h2>
{% endblock %}
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>
{% trans "This will erase the existing file and replace it with a new one." %}
</p>
{{ form.as_p }}
<div class="buttons">
<button class="submit-button">{% trans "Upload" %}</button>
<a class="cancel" href="{% url 'combo-manager-assets' %}">{% trans 'Cancel' %}</a>
</div>
</form>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>
{% trans "This will erase the existing file and replace it with a new one." %}
</p>
{{ form.as_p }}
<div class="buttons">
<button class="submit-button">{% trans "Upload" %}</button>
<a class="cancel" href="{% url 'combo-manager-assets' %}">{% trans 'Cancel' %}</a>
</div>
</form>
{% endblock %}

View File

@ -2,17 +2,17 @@
{% load i18n %}
{% block appbar %}
<h2>{% trans "Asset Upload" %}</h2>
<h2>{% trans "Asset Upload" %}</h2>
{% endblock %}
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<div class="buttons">
<button class="submit-button">{% trans "Upload" %}</button>
<a class="cancel" href="{% url 'combo-manager-assets' %}">{% trans 'Cancel' %}</a>
</div>
</form>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<div class="buttons">
<button class="submit-button">{% trans "Upload" %}</button>
<a class="cancel" href="{% url 'combo-manager-assets' %}">{% trans 'Cancel' %}</a>
</div>
</form>
{% endblock %}

View File

@ -2,83 +2,49 @@
{% load i18n %}
{% block appbar %}
<h2>{% trans 'Assets' %}</h2>
<span class="actions">
<a class="extra-actions-menu-opener"></a>
<a href="{% url 'combo-manager-asset-upload' %}" rel="popup">{% trans 'Upload' %}</a>
<ul class="extra-actions-menu">
<li><a href="{% url 'combo-manager-assets-export' %}">{% trans 'Export Assets' %}</a></li>
<li><a rel="popup" href="{% url 'combo-manager-assets-import' %}">{% trans 'Import Assets' %}</a></li>
</ul>
</span>
<h2>{% trans 'Assets' %}</h2>
<span class="actions">
<a class="extra-actions-menu-opener"></a>
<a href="{% url 'combo-manager-asset-upload' %}" rel="popup">{% trans 'Upload' %}</a>
<ul class="extra-actions-menu">
<li><a href="{% url 'combo-manager-assets-export' %}">{% trans 'Export assets as archive' %}</a></li>
<li><a rel="popup" href="{% url 'combo-manager-assets-import' %}">{% trans 'Import archive of assets' %}</a></li>
</ul>
</span>
{% endblock %}
{% block breadcrumb %}
{{ block.super }}
<a href="{% url 'combo-manager-assets' %}">{% trans 'Assets' %}</a>
{{ block.super }}
<a href="{% url 'combo-manager-assets' %}">{% trans 'Assets' %}</a>
{% endblock %}
{% block content %}
{% if not object_list and not query %}
<div class="big-msg-info">
{% blocktrans %}
This site doesn't have any asset yet. You can add some directly when editing
pages, in the "Upload Image" dialog.
{% endblocktrans %}
</div>
{% if not object_list and not query %}
<div class="big-msg-info">
{% blocktrans %}
This site doesn't have any asset yet. You can add some directly when editing
pages, in the "Upload Image" dialog.
{% endblocktrans %}
</div>
{% else %}
{% else %}
<form>
<p><input name="q" type="search" value="{{query}}"> <button>{% trans 'Search' %}</button>
<span class="help_text">{% trans "(case insensitive search over filenames)" %}</span>
</p>
</form>
<form>
<p><input name="q" type="search" value="{{query}}"> <button>{% trans 'Search' %}</button>
<span class="help_text">{% trans "(case insensitive search over filenames)" %}</span>
</p>
</form>
<div id="assets-browser">
<div id="assets-listing">
<table class="main">
<thead>
<tr>
<th>{% trans "Name" %}</th>
<th>{% trans "Size" %}</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
{% for asset in object_list %}
<tr class="{{ asset.css_classes }}">
<td><a href="{{ asset.src }}">{{ asset.name }}</a></td>
<td>{% if asset.size %}{{ asset.size|filesizeformat }}{% else %}-{% endif %}</td>
<td class="image">{% if asset.is_image %}<img data-href="{{ asset.src }}" src="{{ asset.thumb }}"/>{% endif %}</td>
<td class="actions">
{% if asset.key %}{# theme asset #}
<a href="{% url 'combo-manager-slot-asset-upload' key=asset.key %}"
class="overwrite" rel="popup">{% trans 'Overwrite' %}</a>
{% if asset.asset %}
<a href="{% url 'combo-manager-slot-asset-delete' key=asset.key %}"
class="delete" rel="popup">{% trans 'Delete' %}</a>
{% endif %}
{% else %}
<a href="{% url 'combo-manager-asset-overwrite' %}?img={{asset.filepath|iriencode}}"
class="overwrite" rel="popup">{% trans 'Overwrite' %}</a>
<a href="{% url 'combo-manager-asset-delete' %}?img={{asset.filepath|iriencode}}"
class="delete" rel="popup">{% trans 'Delete' %}</a>
{% endif %}
</td>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div id="asset-preview"></div>
</div>
<div id="assets-browser">
<div id="assets-listing">
{% include "combo/manager_assets_fragment.html" %}
</div>
<div id="asset-preview"></div>
</div>
{% include "gadjo/pagination.html" %}
{% include "gadjo/pagination.html" %}
{% endif %}
{% endif %}
{% endblock %}

View File

@ -0,0 +1,73 @@
{% extends "gadjo/base.html" %}
{% load gadjo static i18n %}
{% block css %}
<link rel="stylesheet" type="text/css" media="all" href="{% static "css/combo.manager.css" %}?{% start_timestamp %}"/>
{% endblock %}
{% block extrascripts %}
<script src="{% static "js/combo.manager.js" %}?{% start_timestamp %}"></script>
{% endblock %}
{% block user-links %}{% endblock %}
{% block sidepage %}{% endblock %}
{% block site-header %}{% endblock %}
{% block bodyargs %}class="no-header"{% endblock %}
{% block footer %}{% endblock %}
{% block appbar %}
<h2>{% trans "Browse for the file you want, then click 'Embed File' to continue..." %}</h2>
{% endblock %}
{% block content %}
{% if not object_list and not query %}
<div class="big-msg-info">
{% trans "No files found. Upload files using the 'Image Button' or 'Link Button' dialog's 'Upload' tab." %}
</div>
{% else %}
<form>
{% for k, v in request.GET.items %}
{% if k != 'q' %}<input type="hidden" name="{{ k }}" value="{{ v }}" />{% endif %}
{% endfor %}
<p><input name="q" type="search" value="{{query}}"> <button>{% trans 'Search' %}</button>
<span class="help_text">{% trans "(case insensitive search over filenames)" %}</span>
</p>
</form>
<div id="assets-browser" class="assets-ckeditor">
<div id="assets-listing">
{% with asset_for_ckeditor=True %}
{% include "combo/manager_assets_fragment.html" %}
{% endwith %}
</div>
<div id="asset-preview"></div>
</div>
{% include "gadjo/pagination.html" %}
<div class="buttons">
<input href="" id="asset-ckeditor-embed" type="submit" name="_embed" value="{% trans "Embed File" %}" disabled />
</div>
{% endif %}
<script type="text/javascript">
// helper functions
function getUrlParam(paramName) {
var reParam = new RegExp('(?:[\?&]|&amp;)' + paramName + '=([^&]+)', 'i') ;
var match = window.location.search.match(reParam) ;
return (match && match.length > 1) ? match[1] : '' ;
}
// embedder
$(document).on('click', '#asset-ckeditor-embed', function() {
var funcNum = getUrlParam('CKEditorFuncNum');
var fileUrl = $(this).attr('href');
window.opener.CKEDITOR.tools.callFunction(funcNum, fileUrl);
window.close();
});
</script>
{% endblock %}

View File

@ -0,0 +1 @@
({{ size|filesizeformat }})

View File

@ -0,0 +1,46 @@
{% load i18n %}
<table class="main">
<thead>
<tr>
<th>{% trans "Name" %}</th>
<th>{% trans "Size" %}</th>
<th></th>
{% if not asset_for_ckeditor %}
<th></th>
{% endif %}
</tr>
</thead>
<tbody>
{% for asset in object_list %}
<tr class="{{ asset.css_classes }}" data-href="{{ asset.src }}">
<td>
{% if asset_for_ckeditor %}
{{ asset.name }}
{% elif asset.src %}
<a href="{{ asset.src }}">{{ asset.name }}</a>
{% else %}
{{ asset.name }} <span class="not-defined">({% trans "not defined" %})</span>
{% endif %}
<td>{% if asset.size %}{{ asset.size|filesizeformat }}{% else %}-{% endif %}</td>
<td class="image">{% if asset.is_image %}<img data-href="{{ asset.src }}" src="{{ asset.thumb }}"/>{% endif %}</td>
{% if not asset_for_ckeditor %}
<td class="actions">
{% if asset.key %}{# theme asset #}
<a href="{% url 'combo-manager-slot-asset-upload' key=asset.key %}{% if cell_reference %}?cell_reference={{ cell_reference }}{% endif %}"
class="overwrite" rel="popup">{% trans 'Overwrite' %}</a>
{% if asset.asset %}
<a href="{% url 'combo-manager-slot-asset-delete' key=asset.key %}{% if cell_reference %}?cell_reference={{ cell_reference }}{% endif %}"
class="delete" rel="popup">{% trans 'Delete' %}</a>
{% endif %}
{% else %}
<a href="{% url 'combo-manager-asset-overwrite' %}?img={{asset.filepath|iriencode}}"
class="overwrite" rel="popup">{% trans 'Overwrite' %}</a>
<a href="{% url 'combo-manager-asset-delete' %}?img={{asset.filepath|iriencode}}"
class="delete" rel="popup">{% trans 'Delete' %}</a>
{% endif %}
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>

View File

@ -2,17 +2,20 @@
{% load i18n %}
{% block appbar %}
<h2>{% trans "Assets Import" %}</h2>
<h2>{% trans "Assets Archive Import" %}</h2>
{% endblock %}
{% block content %}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<div class="buttons">
<button class="submit-button">{% trans "Import" %}</button>
<a class="cancel" href="{% url 'combo-manager-assets' %}">{% trans 'Cancel' %}</a>
</div>
</form>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>
{% trans "Assets archive import allows you to integrate assets exported from another site." %}
</p>
{{ form.as_p }}
<div class="buttons">
<button class="submit-button">{% trans "Import" %}</button>
<a class="cancel" href="{% url 'combo-manager-assets' %}">{% trans 'Cancel' %}</a>
</div>
</form>
{% endblock %}

View File

@ -0,0 +1,19 @@
{% extends "combo/manager_base.html" %}
{% load i18n %}
{% block appbar %}
<h2>{% trans 'Assets' %}</h2>
{% endblock %}
{% block breadcrumb %}
{{ block.super }}
<a href="{% url 'combo-manager-assets' %}">{% trans 'Assets' %}</a>
{% endblock %}
{% block content %}
<div id="assets-listing">
{% include "combo/manager_assets_fragment.html" %}
</div>
{% endblock %}

View File

@ -14,10 +14,10 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
from django import template
from django.db.models.fields.files import ImageFieldFile
from django.utils import six
from sorl.thumbnail.shortcuts import get_thumbnail
from ..models import Asset
@ -37,7 +37,7 @@ def asset_url(*args, **kwargs):
asset = asset_object
break
if isinstance(asset_object, six.string_types):
if isinstance(asset_object, str):
try:
asset = Asset.objects.get(key=asset_object).asset
break
@ -51,15 +51,49 @@ def asset_url(*args, **kwargs):
if not asset:
return ''
if not os.path.exists(asset.path):
return asset.url
geometry_string = kwargs.pop('size', None)
if not geometry_string or asset.file.name.endswith('svg'):
if not geometry_string or asset.name.endswith('svg'):
return asset.url
return get_thumbnail(asset, geometry_string, **kwargs).url
@register.assignment_tag
def get_asset(key):
@register.simple_tag
def asset_css_url(*args, **kwargs):
url = asset_url(*args, **kwargs)
if url:
return 'url(%s)' % url
else:
return 'none'
@register.simple_tag(takes_context=True)
def get_asset(context, *args, **kwargs):
if context.get('traverse_cells'):
# assets are not required when we are just searching for page placeholders
return None
key = None
if 'cell' in kwargs and 'type' in kwargs:
cell = kwargs['cell']
cell_type = kwargs['type']
try:
if not cell.can_have_assets():
return None
key = cell.get_asset_slot_key(cell_type)
except AttributeError:
return None
if hasattr(cell, '_assets'):
return cell._assets.get(key)
elif len(args) == 1:
key = args[0]
if not key:
return None
try:
return Asset.objects.get(key=key)
except Asset.DoesNotExist:

View File

@ -14,25 +14,27 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.conf.urls import url, include
from django.urls import include, path, re_path
from combo.urls_utils import decorated_includes, manager_required
from combo.urls_utils import decorated_includes, staff_required
from . import views
from . import api_views, views
assets_manager_urls = [
url(r'^$', views.assets, name='combo-manager-assets'),
url(r'^delete$', views.asset_delete, name='combo-manager-asset-delete'),
url(r'^overwrite/$', views.asset_overwrite, name='combo-manager-asset-overwrite'),
url(r'^upload/$', views.asset_upload, name='combo-manager-asset-upload'),
url(r'^upload/(?P<key>[\w_:-]+)/$', views.slot_asset_upload, name='combo-manager-slot-asset-upload'),
url(r'^delete/(?P<key>[\w_:-]+)/$', views.slot_asset_delete, name='combo-manager-slot-asset-delete'),
url(r'^export/$', views.assets_export, name='combo-manager-assets-export'),
url(r'^import/$', views.assets_import, name='combo-manager-assets-import'),
path('', views.assets, name='combo-manager-assets'),
re_path(r'^slots/(?P<cell_reference>[\w_-]+)/$', views.slot_assets, name='combo-manager-slot-assets'),
path('delete', views.asset_delete, name='combo-manager-asset-delete'),
path('overwrite/', views.asset_overwrite, name='combo-manager-asset-overwrite'),
path('upload/', views.asset_upload, name='combo-manager-asset-upload'),
re_path(r'^upload/(?P<key>[\w_:-]+)/$', views.slot_asset_upload, name='combo-manager-slot-asset-upload'),
re_path(r'^delete/(?P<key>[\w_:-]+)/$', views.slot_asset_delete, name='combo-manager-slot-asset-delete'),
path('export/', views.assets_export, name='combo-manager-assets-export'),
path('import/', views.assets_import, name='combo-manager-assets-import'),
]
urlpatterns = [
url(r'^assets/(?P<key>[\w_:-]+)$', views.serve_asset),
url(r'^manage/assets/', decorated_includes(manager_required,
include(assets_manager_urls))),
re_path(r'^assets/(?P<key>[\w_:-]+)$', views.serve_asset),
re_path(r'^manage/assets/', decorated_includes(staff_required, include(assets_manager_urls))),
re_path(r'^api/assets/set/(?P<key>[\w_:-]+)/$', api_views.view_set, name='api-assets-set'),
path('ajax/assets-export-size/', views.assets_export_size, name='combo-manager-assets-export-size'),
]

View File

@ -0,0 +1,96 @@
# combo - content management system
# Copyright (C) 2020 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import json
import os
import tarfile
from io import BytesIO
from django.core.files.storage import default_storage
from .models import Asset
ASSET_DIRS = [
'assets',
'page-pictures',
'uploads',
]
def is_asset_dir(basedir):
# exclude dirs like cache or applications, which contain non asset files
media_prefix = default_storage.path('')
asset_basedirs = [os.path.join(media_prefix, ad) for ad in ASSET_DIRS]
for adb in asset_basedirs:
if basedir.startswith(adb):
return True
return False
def clean_assets_files():
media_prefix = default_storage.path('')
for basedir, dummy, filenames in os.walk(media_prefix):
if not is_asset_dir(basedir):
continue
for filename in filenames:
os.remove('%s/%s' % (basedir, filename))
def add_tar_content(tar, filename, content):
file = tarfile.TarInfo(filename)
fd = BytesIO()
fd.write(content.encode('utf-8'))
file.size = fd.tell()
fd.seek(0)
tar.addfile(file, fileobj=fd)
fd.close()
def untar_assets_files(tar, overwrite=False):
media_prefix = default_storage.path('')
data = {}
for tarinfo in tar.getmembers():
filepath = default_storage.path(tarinfo.name)
if not overwrite and os.path.exists(filepath):
continue
if tarinfo.name == '_assets.json':
json_assets = tar.extractfile(tarinfo).read()
data = json.loads(json_assets.decode('utf-8'))
elif tarinfo.name != '_site.json':
tar.extract(tarinfo, path=media_prefix)
return data
def tar_assets_files(tar):
media_prefix = default_storage.path('')
for basedir, dummy, filenames in os.walk(media_prefix):
if not is_asset_dir(basedir):
continue
for filename in filenames:
tar.add(os.path.join(basedir, filename), os.path.join(basedir, filename)[len(media_prefix) :])
export = {'assets': Asset.export_all_for_json()}
add_tar_content(tar, '_assets.json', json.dumps(export, indent=2))
def import_assets(fd, overwrite=False):
with tarfile.open(mode='r', fileobj=fd) as tar:
data = untar_assets_files(tar, overwrite=overwrite)
Asset.load_serialized_objects(data.get('assets') or [])
def export_assets(fd):
with tarfile.open(mode='w', fileobj=fd) as tar:
tar_assets_files(tar)

View File

@ -14,30 +14,31 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import tarfile
import os
from django.conf import settings
from django.contrib import messages
from django.core.exceptions import PermissionDenied
from django.core.files.storage import default_storage
from django.core.urlresolvers import reverse, reverse_lazy
from django.http import Http404, HttpResponse
from django.shortcuts import redirect
from django.utils.six import BytesIO
from django.utils.translation import ugettext_lazy as _
from django.views.generic import TemplateView, ListView, FormView
import tarfile
from io import BytesIO
import ckeditor
from django.conf import settings
from django.contrib import messages
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
from django.core.files.storage import default_storage
from django.http import Http404, HttpResponse
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse, reverse_lazy
from django.utils.translation import gettext_lazy as _
from django.views.generic import FormView, ListView, TemplateView
from sorl.thumbnail.shortcuts import get_thumbnail
from combo.apps.assets.utils import export_assets, import_assets
from combo.apps.maps.models import MapLayer
from combo.data.models import CellBase
from .forms import AssetUploadForm, AssetsImportForm
from .forms import AssetsImportForm, AssetUploadForm
from .models import Asset
class CkEditorAsset(object):
class CkEditorAsset:
def __init__(self, filepath):
self.filepath = filepath
self.name = os.path.basename(filepath)
@ -58,8 +59,7 @@ class CkEditorAsset(object):
def thumb(self):
if getattr(settings, 'CKEDITOR_IMAGE_BACKEND', None):
thumb = ckeditor.utils.get_media_url(
ckeditor.utils.get_thumb_filename(self.filepath))
thumb = ckeditor.utils.get_media_url(ckeditor.utils.get_thumb_filename(self.filepath))
else:
thumb = self.src
return thumb
@ -68,7 +68,7 @@ class CkEditorAsset(object):
return ckeditor.views.is_image(self.src)
class SlotAsset(object):
class SlotAsset:
def __init__(self, key=None, name=None, asset_type='image', asset=None):
self.key = key
self.name = name
@ -80,7 +80,10 @@ class SlotAsset(object):
def size(self):
if self.asset:
return os.stat(self.asset.asset.path).st_size
try:
return os.stat(self.asset.asset.path).st_size
except OSError:
pass
return None
def src(self):
@ -94,25 +97,32 @@ class SlotAsset(object):
@classmethod
def get_assets(cls):
assets = dict([(x.key, x) for x in Asset.objects.all()])
assets = {x.key: x for x in Asset.objects.all()}
uniq_slots = {}
uniq_slots.update(settings.COMBO_ASSET_SLOTS)
for cell in CellBase.get_cells(
cell_filter=lambda x: bool(x.get_asset_slots)):
cells = CellBase.get_cells(select_related={'__all__': ['page'], 'data_linkcell': ['link_page']})
for cell in cells:
uniq_slots.update(cell.get_asset_slots())
for map_layer in MapLayer.objects.filter(kind='geojson'):
uniq_slots.update(map_layer.get_asset_slots())
for key, value in uniq_slots.items():
yield cls(key,
name=value.get('label'),
asset_type=value.get('asset-type', 'image'),
asset=assets.get(key))
yield cls(
key,
name=value.get('label') or '',
asset_type=value.get('asset-type', 'image'),
asset=assets.get(key),
)
class Assets(ListView):
template_name = 'combo/manager_assets.html'
paginate_by = 10
def get_files(self):
return list(SlotAsset.get_assets()) + CkEditorAsset.get_assets(self.request)
def get_queryset(self):
files = list(SlotAsset.get_assets()) + CkEditorAsset.get_assets(self.request)
files = self.get_files()
q = self.request.GET.get('q')
if q:
files = [x for x in files if q.lower() in x.name.lower()]
@ -120,7 +130,7 @@ class Assets(ListView):
return files
def get_context_data(self, **kwargs):
context = super(Assets, self).get_context_data(**kwargs)
context = super().get_context_data(**kwargs)
context['query'] = self.request.GET.get('q') or ''
return context
@ -134,9 +144,21 @@ class Assets(ListView):
return url + '?page=%s' % ((i // self.paginate_by) + 1)
return url
assets = Assets.as_view()
class AssetsBrowse(Assets):
template_name = 'combo/manager_assets_browse.html'
paginate_by = 7
def get_files(self):
return CkEditorAsset.get_assets(self.request)
browse = AssetsBrowse.as_view()
class AssetUpload(FormView):
form_class = AssetUploadForm
template_name = 'combo/manager_asset_upload.html'
@ -145,13 +167,14 @@ class AssetUpload(FormView):
# use native ckeditor view so it's available from ckeditor file/image
# dialogs.
ckeditor_upload_view = ckeditor.views.ImageUploadView()
self.request.GET = {'CKEditorFuncNum': '-'} # hack
self.request.GET = {'CKEditorFuncNum': '-'} # hack
ckeditor_upload_view.post(self.request)
return super(AssetUpload, self).form_valid(form)
return super().form_valid(form)
def get_success_url(self):
return Assets(request=self.request).get_anchored_url(name=self.request.FILES['upload'].name)
asset_upload = AssetUpload.as_view()
@ -163,21 +186,39 @@ class AssetOverwrite(FormView):
def form_valid(self, form):
img_orig = self.request.GET['img']
if '..' in img_orig:
raise PermissionDenied() # better safe than sorry
raise PermissionDenied() # better safe than sorry
base_path = settings.CKEDITOR_UPLOAD_PATH
if getattr(settings, 'CKEDITOR_RESTRICT_BY_USER', False):
base_path = os.path.join(base_path, self.request.user.username)
if not img_orig.startswith(base_path):
raise PermissionDenied()
try:
os.stat(default_storage.path(img_orig))
except ValueError:
raise PermissionDenied()
if '\x00' in img_orig:
# os.stat should have raised "embedded null byte" but double check
raise PermissionDenied()
upload = self.request.FILES['upload']
# check that the new file and the original have the same extension
ext_orig = os.path.splitext(img_orig)[1].lower()
ext_upload = os.path.splitext(upload.name)[1].lower()
if ext_orig != ext_upload:
messages.error(
self.request,
_('You have to upload a file with the same extension (%(ext)s).') % {'ext': ext_orig},
)
return super().form_valid(form)
default_storage.delete(img_orig)
if getattr(settings, 'CKEDITOR_IMAGE_BACKEND', None):
thumb = ckeditor.utils.get_thumb_filename(img_orig)
default_storage.delete(thumb)
saved_path = default_storage.save(img_orig, upload)
backend = ckeditor.image_processing.get_backend()
upload.seek(0) # rewind file to be sure
upload.seek(0) # rewind file to be sure
try:
backend.image_verify(upload)
except ckeditor.utils.NotAnImageException:
@ -185,12 +226,13 @@ class AssetOverwrite(FormView):
else:
if backend.should_create_thumbnail(saved_path):
backend.create_thumbnail(saved_path)
return super(AssetOverwrite, self).form_valid(form)
return super().form_valid(form)
def get_success_url(self):
img_orig = self.request.GET['img']
return Assets(request=self.request).get_anchored_url(name=os.path.basename(img_orig))
asset_overwrite = AssetOverwrite.as_view()
@ -200,20 +242,59 @@ class AssetDelete(TemplateView):
def post(self, request):
img_orig = request.GET['img']
if '..' in img_orig:
raise PermissionDenied() # better safe than sorry
raise PermissionDenied() # better safe than sorry
base_path = settings.CKEDITOR_UPLOAD_PATH
if getattr(settings, 'CKEDITOR_RESTRICT_BY_USER', False):
base_path = os.path.join(base_path, request.user.username)
if not img_orig.startswith(base_path):
raise PermissionDenied()
try:
os.stat(default_storage.path(img_orig))
except ValueError:
raise PermissionDenied()
if '\x00' in img_orig:
# os.stat should have raised "embedded null byte" but double check
raise PermissionDenied()
default_storage.delete(img_orig)
return redirect(
Assets(request=self.request).get_anchored_url(
name=os.path.basename(img_orig)))
return redirect(Assets(request=self.request).get_anchored_url(name=os.path.basename(img_orig)))
asset_delete = AssetDelete.as_view()
class SlotAssets(ListView):
template_name = 'combo/manager_slot_assets.html'
def get_assets(self, cell):
asset_slots = cell.get_asset_slots()
assets = {x.key: x for x in Asset.objects.filter(key__in=asset_slots.keys())}
for key, value in asset_slots.items():
yield SlotAsset(
key,
name=value.get('short_label'),
asset_type=value.get('asset-type', 'image'),
asset=assets.get(key),
)
def get_queryset(self):
cell_reference = self.kwargs['cell_reference']
try:
cell = CellBase.get_cell(cell_reference)
except ObjectDoesNotExist:
raise Http404()
return self.get_assets(cell)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['cell_reference'] = self.kwargs['cell_reference']
return context
slot_assets = SlotAssets.as_view()
class SlotAssetUpload(FormView):
form_class = AssetUploadForm
template_name = 'combo/manager_asset_upload.html'
@ -226,11 +307,25 @@ class SlotAssetUpload(FormView):
self.asset = Asset(key=self.kwargs['key'])
self.asset.asset = self.request.FILES['upload']
self.asset.save()
return super(SlotAssetUpload, self).form_valid(form)
return super().form_valid(form)
def get_success_url(self):
if self.request.GET.get('cell_reference'):
cell_reference = self.request.GET['cell_reference']
try:
cell = CellBase.get_cell(cell_reference)
except ObjectDoesNotExist:
pass
else:
return (
reverse('combo-manager-page-view', kwargs={'pk': cell.page_id})
+ '#cell-'
+ cell_reference
)
return Assets(request=self.request).get_anchored_url(key=self.kwargs['key'])
slot_asset_upload = SlotAssetUpload.as_view()
@ -239,8 +334,21 @@ class SlotAssetDelete(TemplateView):
def post(self, request, *args, **kwargs):
Asset.objects.filter(key=kwargs['key']).delete()
if self.request.GET.get('cell_reference'):
cell_reference = self.request.GET['cell_reference']
try:
cell = CellBase.get_cell(cell_reference)
except ObjectDoesNotExist:
pass
else:
return redirect(
reverse('combo-manager-page-view', kwargs={'pk': cell.page_id})
+ '#cell-'
+ cell_reference
)
return redirect(Assets(request=self.request).get_anchored_url(key=kwargs['key']))
slot_asset_delete = SlotAssetDelete.as_view()
@ -252,38 +360,67 @@ class AssetsImport(FormView):
def form_valid(self, form):
overwrite = form.cleaned_data.get('overwrite')
try:
assets = tarfile.open(fileobj=form.cleaned_data['assets_file'])
import_assets(form.cleaned_data['assets_file'], overwrite)
except tarfile.TarError:
messages.error(self.request, _('The assets file is not valid.'))
return super(AssetsImport, self).form_valid(form)
media_prefix = default_storage.path('')
for tarinfo in assets.getmembers():
filepath = default_storage.path(tarinfo.name)
if not overwrite and os.path.exists(filepath):
continue
assets.extract(tarinfo, path=media_prefix)
return super().form_valid(form)
messages.success(self.request, _('The assets file has been imported.'))
return super(AssetsImport, self).form_valid(form)
return super().form_valid(form)
assets_import = AssetsImport.as_view()
def assets_export(request, *args, **kwargs):
fd = BytesIO()
assets_file = tarfile.open('assets.tar', 'w', fileobj=fd)
media_prefix = default_storage.path('')
for basedir, dirnames, filenames in os.walk(media_prefix):
for filename in filenames:
assets_file.add(
os.path.join(basedir, filename),
os.path.join(basedir, filename)[len(media_prefix):])
assets_file.close()
export_assets(fd)
return HttpResponse(fd.getvalue(), content_type='application/x-tar')
def serve_asset(request, key):
try:
asset = Asset.objects.get(key=key)
return redirect(asset.asset.url)
except (Asset.DoesNotExist, AttributeError):
asset = get_object_or_404(Asset, key=key)
if not os.path.exists(asset.asset.path):
raise Http404()
# get options for thumbnail
thumb_options = request.GET.dict()
width = thumb_options.pop('width', None)
height = thumb_options.pop('height', None)
geometry_string = ''
if width:
geometry_string += width
if height:
geometry_string += 'x%s' % height
# no thumbnail whithout geometry_string or for a svg file
if not geometry_string or asset.asset.name.endswith('svg'):
url = asset.asset.url
else:
# get or create thumbnail
url = get_thumbnail(asset.asset, geometry_string, **thumb_options).url
if settings.COMBO_X_ACCEL_ASSETS and url.startswith(settings.MEDIA_URL):
response = HttpResponse(content_type='') # let nginx set it
response['X-Accel-Redirect'] = url
return response
return redirect(url)
class AssetsExportSize(TemplateView):
template_name = 'combo/manager_assets_export_size.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
media_prefix = default_storage.path('')
computed_size = 0
for basedir, dummy, filenames in os.walk(media_prefix):
for filename in filenames:
computed_size += os.stat(os.path.join(basedir, filename)).st_size
context['size'] = computed_size
return context
assets_export_size = AssetsExportSize.as_view()

View File

@ -1,79 +0,0 @@
# combo - content management system
# Copyright (C) 2017 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django import forms
from django.utils.translation import ugettext_lazy as _
from django.utils.dateparse import parse_datetime, parse_time
from .models import BookingCalendar
from .utils import get_agendas
from combo.apps.wcs.utils import get_wcs_options
class BookingCalendarForm(forms.ModelForm):
class Meta:
model = BookingCalendar
fields = (
'title', 'agenda_reference', 'formdef_reference',
'slot_duration', 'minimal_booking_duration', 'days_displayed')
def __init__(self, *args, **kwargs):
super(BookingCalendarForm, self).__init__(*args, **kwargs)
agenda_references = get_agendas()
formdef_references = get_wcs_options('/api/formdefs/')
self.fields['agenda_reference'].widget = forms.Select(choices=agenda_references)
self.fields['formdef_reference'].widget = forms.Select(choices=formdef_references)
class BookingForm(forms.Form):
def __init__(self, *args, **kwargs):
self.cell = kwargs.pop('cell')
super(BookingForm, self).__init__(*args, **kwargs)
self.cleaned_data = {}
def is_valid(self):
slots = getattr(self.data, 'getlist', lambda x: [])('slots')
# check that at least one slot if selected
if not slots:
raise ValueError(_('Please select slots'))
offset = self.cell.slot_duration
start_dt = parse_datetime(slots[0])
end_dt = parse_datetime(slots[-1]) + offset
slots.append(end_dt.isoformat())
# check that all slots are part of the same day
for slot in slots:
if parse_datetime(slot).date() != start_dt.date():
raise ValueError(_('Please select slots of the same day'))
# check that slots datetime are contiguous
start = start_dt
while start <= end_dt:
if start.isoformat() not in slots:
raise ValueError(_('Please select contiguous slots'))
start = start + offset
# check that event booking duration >= minimal booking duration
min_duration = self.cell.minimal_booking_duration
if not (end_dt - start_dt) >= min_duration:
str_min_duration = parse_time(str(min_duration)).strftime('%H:%M')
message = _("Minimal booking duration is %s") % str_min_duration
raise ValueError(message)
self.cleaned_data['start'] = start_dt
self.cleaned_data['end'] = end_dt
return True

View File

@ -1,39 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import datetime
class Migration(migrations.Migration):
dependencies = [
('data', '0027_page_picture'),
('auth', '0006_require_contenttypes_0002'),
]
operations = [
migrations.CreateModel(
name='BookingCalendar',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('placeholder', models.CharField(max_length=20)),
('order', models.PositiveIntegerField()),
('slug', models.SlugField(verbose_name='Slug', blank=True)),
('extra_css_class', models.CharField(max_length=100, verbose_name='Extra classes for CSS styling', blank=True)),
('public', models.BooleanField(default=True, verbose_name='Public')),
('restricted_to_unlogged', models.BooleanField(default=False, verbose_name='Restrict to unlogged users')),
('last_update_timestamp', models.DateTimeField(auto_now=True)),
('title', models.CharField(max_length=128, null=True, verbose_name='Title', blank=True)),
('agenda_reference', models.CharField(max_length=128, verbose_name='Agenda')),
('formdef_reference', models.CharField(max_length=128, verbose_name='Form')),
('slot_duration', models.DurationField(default=datetime.timedelta(0, 1800), help_text='Format is hours:minutes:seconds', verbose_name='Slot duration')),
('minimal_booking_duration', models.DurationField(default=datetime.timedelta(0, 3600), help_text='Format is hours:minutes:seconds', verbose_name='Minimal booking duration')),
('groups', models.ManyToManyField(to='auth.Group', verbose_name='Groups', blank=True)),
('page', models.ForeignKey(to='data.Page')),
],
options={
'verbose_name': 'Booking Calendar',
},
),
]

View File

@ -1,19 +0,0 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('calendar', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='bookingcalendar',
name='days_displayed',
field=models.PositiveSmallIntegerField(default=7, verbose_name='Number of days to display'),
),
]

View File

@ -1,68 +0,0 @@
# combo - content management system
# Copyright (C) 2017 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import datetime
from django.conf import settings
from django.db import models
from django.utils.translation import ugettext_lazy as _
from combo.data.models import CellBase
from combo.data.library import register_cell_class
from .utils import (is_chrono_enabled, is_wcs_enabled,
get_chrono_events, get_calendar_context_vars)
@register_cell_class
class BookingCalendar(CellBase):
title = models.CharField(_('Title'), max_length=128, blank=True, null=True)
agenda_reference = models.CharField(_('Agenda'), max_length=128)
formdef_reference = models.CharField(_('Form'), max_length=128)
slot_duration = models.DurationField(
_('Slot duration'), default=datetime.timedelta(minutes=30),
help_text=_('Format is hours:minutes:seconds'))
minimal_booking_duration = models.DurationField(
_('Minimal booking duration'), default=datetime.timedelta(hours=1),
help_text=_('Format is hours:minutes:seconds'))
days_displayed = models.PositiveSmallIntegerField(_('Number of days to display'), default=7)
template_name = 'calendar/booking_calendar_cell.html'
class Meta:
verbose_name = _('Booking Calendar')
def get_default_form_class(self):
from .forms import BookingCalendarForm
return BookingCalendarForm
@classmethod
def is_enabled(cls):
return settings.BOOKING_CALENDAR_CELL_ENABLED and is_chrono_enabled() and is_wcs_enabled()
def is_visible(self, user=None):
return self.agenda_reference and self.formdef_reference \
and super(BookingCalendar, self).is_visible(user=user)
def get_cell_extra_context(self, context):
if context.get('placeholder_search_mode'):
return {}
extra_context = super(BookingCalendar, self).get_cell_extra_context(context)
events_data = get_chrono_events(self.agenda_reference, not(context.get('synchronous')))
extra_context.update(get_calendar_context_vars(
context['request'], extra_context['cell'], events_data))
return extra_context

View File

@ -1,27 +0,0 @@
{% load i18n calendar %}
{% block cell-content %}
{% if cell.title %}
<h2>
<span>{{cell.title}}</span>
{% if calendar %}
<span class="calinfo">
{% with calendar.get_first_available_slot as slot %}
({% if slot %}{% trans "Next available slot:" %} {{ slot.date_time|date:"DATETIME_FORMAT"}}{% else %}{% trans "No available slots." %}{% endif %})
{% endwith %}
</span>
{% endif %}
</h2>
{% endif %}
{% if error %}
<div><p>{{ error }}</p></div>
{% else %}
<div class="calcontent">
{% include 'calendar/booking_calendar_content.html' %}
</div>
{% endif %}
<style>.calinfo { font-style: italic; font-size: 80%; }</style>
{% endblock %}

View File

@ -1,57 +0,0 @@
{% load i18n calendar %}
{% if calendar_days.has_other_pages %}
<p class="paginator">
{% if calendar_days.has_previous %}
<a class="previous calchunk" href="?chunk_{{ cell.pk }}={{ calendar_days.previous_page_number }}" data-content-url="{% url 'ajax-calendar-content' pk=cell.pk %}?chunk_{{cell.pk}}={{ calendar_days.previous_page_number }}">{% trans "previous" %}</a>
{% else %}
<span class="previous">{% trans "previous" %}</span>
{% endif %}
<span class="current">
{{ calendar_days.number }} / {{ calendar_days.paginator.num_pages }}
</span>
{% if calendar_days.has_next %}
<a class="next calchunk" href="?chunk_{{ cell.pk }}={{ calendar_days.next_page_number }}" data-content-url="{% url 'ajax-calendar-content' pk=cell.pk %}?chunk_{{cell.pk}}={{ calendar_days.next_page_number }}">{% trans "next" %}</a>
{% else %}
<span class="next">{% trans "next" %}</span>
{% endif %}
</p>
{% endif %}
{% if calendar_days %}
<form method="POST" action="{% url 'calendar-booking' pk=cell.pk %}?chunk_{{cell.pk}}={{calendar_days.number}}">
{% csrf_token %}
<table id="cal-table-{{cell.pk}}">
<thead>
<tr>
<th></th>
{% for day in calendar_days %}
<th>{{day|date:"SHORT_DATE_FORMAT"}}</th>
{% endfor %}
</tr>
</thead>
<tbody>
{% for slot in calendar_slots %}
<tr>
<th>{{slot|date:"TIME_FORMAT"}}</th>
{% for day in calendar_days %}
{% get_day_slot calendar day=day slot=slot as value %}
{% if not value.exist %}
<td class="absent"></td>
{% elif value.available %}
<td class="available">
<input type="checkbox" name="slots" value="{{value.label}}" id="slot-{{cell.pk}}-{{value.label}}"/>
<label for="slot-{{cell.pk}}-{{value.label}}"></label>
</td>
{% else %}
<td class="unavailable"></td>
{% endif %}
{% endfor %}
</tr>
{% endfor %}
</tbody>
</table>
<button class="submit-button">{% trans "Book" context "booking" %}</button>
</form>
{% endif %}

View File

@ -1,24 +0,0 @@
# combo - content management system
# Copyright (C) 2017 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.conf.urls import url
from .views import BookingView, CalendarContentAjaxView
urlpatterns = [
url(r'^calendar/book/(?P<pk>[\w,-]+)/$', BookingView.as_view(), name='calendar-booking'),
url(r'^ajax/calendar/content/(?P<pk>\w+)/$', CalendarContentAjaxView.as_view(), name='ajax-calendar-content'),
]

View File

@ -1,240 +0,0 @@
# combo - content management system
# Copyright (C) 2017 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import datetime
import math
from django.conf import settings
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.utils.dateparse import parse_datetime
from django.utils.http import urlencode
from django.utils.timezone import localtime, make_aware
from django.utils.translation import ugettext_lazy as _
from combo.utils import requests
def get_services(service_name):
if hasattr(settings, 'KNOWN_SERVICES') and settings.KNOWN_SERVICES.get(service_name):
return settings.KNOWN_SERVICES[service_name]
return {}
def get_wcs_services():
return get_services('wcs')
def get_chrono_service():
for chrono_key, chrono_site in get_services('chrono').items():
if not chrono_site.get('secondary', True):
chrono_site['slug'] = chrono_key
return chrono_site
return {}
def is_chrono_enabled():
return bool(get_chrono_service())
def is_wcs_enabled():
return bool(get_wcs_services())
def get_agendas():
chrono = get_chrono_service()
references = []
response = requests.get('api/agenda/', remote_service=chrono, without_user=True)
try:
result = response.json()
except ValueError:
return references
for agenda in result.get('data'):
references.append((
'%s:%s' % (chrono['slug'], agenda['id']), agenda['text']))
return references
def get_chrono_events(agenda_reference, synchronous):
chrono_key, chrono_slug = agenda_reference.split(':')
chrono = get_chrono_service()
response = requests.get('api/agenda/%s/datetimes/' % chrono_slug, remote_service=chrono,
without_user=True, raise_if_not_cached=synchronous)
try:
if response.status_code != 200:
raise ValueError
result = response.json()
except ValueError:
return {'error': _('An error occurred while retrieving calendar\'s availabilities.')}
return result
def get_calendar_context_vars(request, cell, events_data):
page = request.GET.get('chunk_%s' % cell.pk, 1)
if 'error' in events_data:
return events_data
events = events_data['data']
calendar = get_calendar(events, cell.slot_duration, cell.days_displayed,
cell.minimal_booking_duration)
paginator = Paginator(calendar.get_computed_days(), cell.days_displayed)
try:
cal_page = paginator.page(page)
except PageNotAnInteger:
cal_page = paginator.page(1)
except (EmptyPage,):
cal_page = paginator.page(paginator.num_pages)
return {
'calendar': calendar,
'calendar_days': cal_page,
'calendar_slots': calendar.get_slots()
}
def get_calendar(events, offset, days_displayed, min_duration):
calendar = Calendar(offset, days_displayed, min_duration)
for event in events:
event_datetime = parse_datetime(event['datetime'])
if not calendar.has_day(event_datetime.date()):
day = WeekDay(event_datetime.date())
calendar.days.append(day)
else:
day = calendar.get_day(event_datetime.date())
# add slots to day
day.add_slots(DaySlot(
event_datetime, True if not event.get('disabled', True) else False))
return calendar
def get_form_url_with_params(cell, data):
session_vars = {
"session_var_booking_agenda_slug": cell.agenda_reference.split(':')[1],
"session_var_booking_start": data['start'].isoformat(),
"session_var_booking_end": data['end'].isoformat()
}
wcs_key, wcs_slug = cell.formdef_reference.split(':')
wcs = get_wcs_services().get(wcs_key)
url = '%s%s/?%s' % (wcs['url'], wcs_slug, urlencode(session_vars))
return url
class DaySlot(object):
def __init__(self, date_time, available, exist=True):
self.date_time = localtime(make_aware(date_time))
self.available = available
self.exist = exist
def __repr__(self):
return '<DaySlot date_time=%s - available=%s>' % (self.date_time.isoformat(), self.available)
@property
def label(self):
return '%s' % self.date_time.isoformat()
class WeekDay(object):
def __init__(self, date):
self.date = date
self.slots = []
def __repr__(self):
return '<WeekDay %s >' % self.date.isoformat()
def add_slots(self, slot):
if slot not in self.slots:
self.slots.append(slot)
def get_slot(self, slot_time):
for slot in self.slots:
if slot.date_time.time() == slot_time:
return slot
slot_datetime = datetime.datetime.combine(self.date, slot_time)
return DaySlot(slot_datetime, False, exist=False)
def get_minimum_slot(self):
return min(self.slots, key=lambda x: x.date_time.time())
def get_maximum_slot(self):
return max(self.slots, key=lambda x: x.date_time.time())
class Calendar(object):
def __init__(self, offset, days_displayed, min_duration):
self.offset = offset
self.days_displayed = days_displayed
self.days = []
self.min_duration = min_duration
def __repr__(self):
return '<Calendar>'
def get_first_available_slot(self):
"""return the first available slot that has enough
consecutive available slots to be allowed for booking
"""
required_contiguous_slots = self.min_duration.seconds // self.offset.seconds
for day in self.days:
slots = day.slots
for idx in range(len(slots) - required_contiguous_slots):
if all([x.available for x in slots[idx:idx+required_contiguous_slots]]):
return slots[idx]
return None
def get_slots(self):
start = self.get_minimum_slot()
end = self.get_maximum_slot()
while start <= end:
yield start
start = datetime.datetime.combine(
datetime.date.today(), start) + self.offset
start = start.time()
def get_computed_days(self):
if not self.days:
return []
computed_days = []
base_day = self.days[0].date
days_diff = (self.days[-1].date - self.days[0].date).days
# find a number which ensures calendar days are equally chunked
days_range = int(self.days_displayed * math.ceil(float(days_diff + 1) / self.days_displayed))
for index in range(days_range):
day = base_day + datetime.timedelta(days=index)
computed_days.append(day)
return computed_days
def get_day(self, date):
for day in self.days:
if day.date == date:
return day
return None
def has_day(self, date):
return bool(self.get_day(date))
def get_availability(self, slot):
if not self.has_day(slot.date()):
return DaySlot(slot, False, exist=False)
day = self.get_day(slot.date())
return day.get_slot(slot.time())
def get_minimum_slot(self):
return min([day.get_minimum_slot().date_time.time() for day in self.days])
def get_maximum_slot(self):
return max([day.get_maximum_slot().date_time.time() for day in self.days])

View File

@ -1,58 +0,0 @@
# combo - content management system
# Copyright (C) 2017 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.contrib import messages
from django.http import HttpResponseRedirect
from django.utils.encoding import force_text
from django.views.generic import View, DetailView
from django.views.generic.detail import SingleObjectMixin
from .forms import BookingForm
from .models import BookingCalendar
from .utils import (get_form_url_with_params, get_chrono_events,
get_calendar_context_vars)
class BookingView(SingleObjectMixin, View):
http_method_names = ['post']
model = BookingCalendar
def post(self, request, *args, **kwargs):
cell = self.get_object()
form = BookingForm(request.POST, cell=cell)
try:
form.is_valid()
except ValueError as exc:
messages.error(request, force_text(exc))
redirect_url = '%s?%s' % (
cell.page.get_online_url(), request.GET.urlencode())
return HttpResponseRedirect(redirect_url)
data = form.cleaned_data
url = get_form_url_with_params(cell, data)
return HttpResponseRedirect(url)
class CalendarContentAjaxView(DetailView):
model = BookingCalendar
template_name = 'calendar/booking_calendar_content.html'
def get_context_data(self, **kwargs):
context = super(CalendarContentAjaxView, self).get_context_data(**kwargs)
context['cell'] = self.object
events_data = get_chrono_events(self.object.agenda_reference, context.get('synchronous'))
context.update(get_calendar_context_vars(self.request, self.object, events_data))
return context

View File

@ -1,35 +0,0 @@
# combo - content management system
# Copyright (C) 2014-2017 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
import django.apps
from django.utils.timezone import now, timedelta
from django.utils.translation import ugettext_lazy as _
class AppConfig(django.apps.AppConfig):
name = 'combo.apps.dashboard'
verbose_name = _('Dashboard')
def get_before_urls(self):
from . import urls
return urls.urlpatterns
def hourly(self):
self.clean_autotiles()
def clean_autotiles(self):
from combo.data.models import ConfigJsonCell
ConfigJsonCell.objects.filter(placeholder='_auto_tile',
last_update_timestamp__lte=now() - timedelta(days=2)).delete()
default_app_config = 'combo.apps.dashboard.AppConfig'

View File

@ -0,0 +1,36 @@
# combo - content management system
# Copyright (C) 2014-2017 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
import django.apps
from django.utils.timezone import now, timedelta
from django.utils.translation import gettext_lazy as _
class AppConfig(django.apps.AppConfig):
name = 'combo.apps.dashboard'
verbose_name = _('Dashboard')
def get_before_urls(self):
from . import urls
return urls.urlpatterns
def hourly(self):
self.clean_autotiles()
def clean_autotiles(self):
from combo.data.models import ConfigJsonCell
ConfigJsonCell.objects.filter(
placeholder='_auto_tile', last_update_timestamp__lte=now() - timedelta(days=2)
).delete()

View File

@ -1,12 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('contenttypes', '0002_remove_content_type_name'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
@ -18,16 +14,27 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='DashboardCell',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('placeholder', models.CharField(max_length=20)),
('order', models.PositiveIntegerField()),
('slug', models.SlugField(verbose_name='Slug', blank=True)),
('extra_css_class', models.CharField(max_length=100, verbose_name='Extra classes for CSS styling', blank=True)),
(
'extra_css_class',
models.CharField(
max_length=100, verbose_name='Extra classes for CSS styling', blank=True
),
),
('public', models.BooleanField(default=True, verbose_name='Public')),
('restricted_to_unlogged', models.BooleanField(default=False, verbose_name='Restrict to unlogged users')),
(
'restricted_to_unlogged',
models.BooleanField(default=False, verbose_name='Restrict to unlogged users'),
),
('last_update_timestamp', models.DateTimeField(auto_now=True)),
('groups', models.ManyToManyField(to='auth.Group', verbose_name='Groups', blank=True)),
('page', models.ForeignKey(to='data.Page')),
('groups', models.ManyToManyField(to='auth.Group', verbose_name='Roles', blank=True)),
('page', models.ForeignKey(to='data.Page', on_delete=models.CASCADE)),
],
options={
'verbose_name': 'Dashboard',
@ -36,12 +43,15 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='Tile',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('cell_pk', models.PositiveIntegerField()),
('order', models.PositiveIntegerField()),
('cell_type', models.ForeignKey(to='contenttypes.ContentType')),
('dashboard', models.ForeignKey(to='dashboard.DashboardCell')),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
('cell_type', models.ForeignKey(to='contenttypes.ContentType', on_delete=models.CASCADE)),
('dashboard', models.ForeignKey(to='dashboard.DashboardCell', on_delete=models.CASCADE)),
('user', models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)),
],
options={
'ordering': ('order',),

View File

@ -1,11 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dashboard', '0001_initial'),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 2.2.21 on 2021-07-23 11:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dashboard', '0002_auto_20180105_0846'),
]
operations = [
migrations.AddField(
model_name='dashboardcell',
name='template_name',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='Cell Template'),
),
]

View File

@ -0,0 +1,15 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dashboard', '0003_dashboardcell_template_name'),
]
operations = [
migrations.AddField(
model_name='dashboardcell',
name='condition',
field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 3.2.16 on 2024-01-09 09:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dashboard', '0004_display_condition'),
]
operations = [
migrations.AlterField(
model_name='dashboardcell',
name='extra_css_class',
field=models.CharField(blank=True, max_length=500, verbose_name='Extra classes for CSS styling'),
),
]

View File

@ -14,14 +14,17 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import fields
from django.db import models
from django.utils.translation import ugettext_lazy as _
import datetime
from django.conf import settings
from django.contrib.contenttypes import fields
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
from combo.data.models import CellBase
from combo.data.library import register_cell_class
from combo.data.models import CellBase, ValidityInfo
@register_cell_class
@ -40,21 +43,26 @@ class DashboardCell(CellBase):
return settings.COMBO_DASHBOARD_ENABLED
def is_relevant(self, context):
if not (getattr(context['request'], 'user', None) and context['request'].user.is_authenticated()):
if not (getattr(context['request'], 'user', None) and context['request'].user.is_authenticated):
return False
return True
def render(self, context):
context['tiles'] = Tile.objects.filter(dashboard=self, user=context['user'])
return super(DashboardCell, self).render(context)
tiles = Tile.objects.filter(dashboard=self, user=context['user'])
validity_info_dict = {
(x.content_type_id, x.object_id): True
for x in ValidityInfo.objects.filter(invalid_since__lt=now() - datetime.timedelta(days=2))
}
context['tiles'] = [x for x in tiles if (x.cell_type_id, x.cell_pk) not in validity_info_dict]
return super().render(context)
class Tile(models.Model):
dashboard = models.ForeignKey(DashboardCell)
cell_type = models.ForeignKey(ContentType)
dashboard = models.ForeignKey(DashboardCell, on_delete=models.CASCADE)
cell_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
cell_pk = models.PositiveIntegerField()
cell = fields.GenericForeignKey('cell_type', 'cell_pk')
user = models.ForeignKey(settings.AUTH_USER_MODEL)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
order = models.IntegerField()
class Meta:

View File

@ -1,18 +1,18 @@
{% load i18n dashboard %}
{% block content %}
{% if user.is_authenticated %}
<span class="dashboard-cell-icons">
{% if not in_dashboard %}
{% with tile=cell|as_dashboard_cell:request.user %}
{% if tile %}
<a class="remove-from-dashboard" href="{% url 'combo-dashboard-remove-tile' cell_reference=tile.cell.get_reference %}"></a>
{% else %}
<a class="add-to-dashboard" href="{% url 'combo-dashboard-add-tile' cell_reference=cell.get_reference %}"></a>
{% endif %}
{% endwith %}
{% else %}
<a class="remove-from-dashboard" href="{% url 'combo-dashboard-remove-tile' cell_reference=cell.get_reference %}"></a>
{% endif %}
</span>
{% endif %}
{% if user.is_authenticated %}
<span class="dashboard-cell-icons">
{% if not in_dashboard %}
{% with tile=cell|as_dashboard_cell:request.user %}
{% if tile %}
<a class="remove-from-dashboard" href="{% url 'combo-dashboard-remove-tile' cell_reference=tile.cell.get_reference %}"></a>
{% else %}
<a class="add-to-dashboard" href="{% url 'combo-dashboard-add-tile' cell_reference=cell.get_reference %}"></a>
{% endif %}
{% endwith %}
{% else %}
<a class="remove-from-dashboard" href="{% url 'combo-dashboard-remove-tile' cell_reference=cell.get_reference %}"></a>
{% endif %}
</span>
{% endif %}
{% endblock %}

View File

@ -1,13 +1,14 @@
{% load combo i18n %}
{% load i18n %}
{% block cell-content %}
{% for tile in tiles %}
{% with cell=tile.cell %}
<div class="cell {{ cell.css_class_names }} {% if cell.slug %}{{cell.slug}}{% endif %}"
data-ajax-cell-url="{{ site_base }}{% url 'combo-public-ajax-page-cell' page_pk=cell.page.id cell_reference=cell.get_reference %}"
data-ajax-cell-loading-message="{{ cell.loading_message }}"
{% if cell.ajax_refresh %}
data-ajax-cell-refresh="{{ cell.ajax_refresh }}"
{% endif %}><div>{% render_cell cell %}</div></div>
{% endwith %}
{% endfor %}
{% for tile in tiles %}
{% with cell=tile.cell %}
<div class="cell {{ cell.css_class_names }} {% if cell.slug %}{{cell.slug}}{% endif %}"
data-ajax-cell-url="{{ site_base }}{{ cell.get_ajax_url }}"
data-ajax-cell-loading-message="{{ cell.loading_message }}"
data-ajax-cell-error-message="{% trans "Loading error" %}"
{% if cell.ajax_refresh %}
data-ajax-cell-refresh="{{ cell.ajax_refresh }}"
{% endif %}><div>{% render_cell cell %}</div></div>
{% endwith %}
{% endfor %}
{% endblock %}

View File

@ -21,17 +21,26 @@ from ..models import Tile
register = template.Library()
def get_cell_data(cell):
# return a dictionary with cell parameters relevant for tile comparison
if cell is None:
return {}
cell_data = serializers.serialize('python', [cell])[0]
del cell_data['pk']
for key in ('restricted_to_unlogged', 'groups', 'last_update_timestamp',
'order', 'placeholder', 'public', 'page'):
for key in (
'restricted_to_unlogged',
'groups',
'last_update_timestamp',
'order',
'placeholder',
'public',
'page',
):
del cell_data['fields'][key]
return cell_data
@register.filter
def as_dashboard_cell(cell, user):
cell_data = get_cell_data(cell)

View File

@ -14,21 +14,34 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.conf.urls import url
from django.urls import path, re_path
from . import views
urlpatterns = [
url(r'^api/dashboard/add/(?P<cell_reference>[\w_-]+)/$',
re_path(
r'^api/dashboard/add/(?P<cell_reference>[\w_-]+)/$',
views.dashboard_add_tile,
name='combo-dashboard-add-tile'),
url(r'^api/dashboard/remove/(?P<cell_reference>[\w_-]+)/$',
name='combo-dashboard-add-tile',
),
re_path(
r'^api/dashboard/remove/(?P<cell_reference>[\w_-]+)/$',
views.dashboard_remove_tile,
name='combo-dashboard-remove-tile'),
url(r'^api/dashboard/auto-tile/(?P<key>[\w_-]+)/$',
name='combo-dashboard-remove-tile',
),
re_path(
r'^api/dashboard/auto-tile/(?P<key>[\w_-]+)/$',
views.dashboard_auto_tile,
name='combo-dashboard-auto-tile'),
url(r'^api/dashboard/reorder/(?P<dashboard_id>[\w]+)/$',
name='combo-dashboard-auto-tile',
),
re_path(
r'^api/dashboard/reorder/(?P<dashboard_id>[\w]+)/$',
views.dashboard_reorder_tiles,
name='combo-dashboard-reorder-tiles'),
name='combo-dashboard-reorder-tiles',
),
path(
'api/dashboard/tile-stats/',
views.dashboard_tile_stats,
name='combo-dashboard-tile-stats',
),
]

View File

@ -17,17 +17,27 @@
import json
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import User
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse
from django.db.models import Max, Min
from django.http import Http404, HttpResponse, HttpResponseRedirect
from django.http import (
Http404,
HttpResponse,
HttpResponseBadRequest,
HttpResponseNotAllowed,
HttpResponseRedirect,
)
from django.urls import reverse
from django.utils.encoding import force_str
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import View
from rest_framework import permissions
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from combo.data.models import CellBase, ConfigJsonCell
from combo.data.library import get_cell_class
from combo.public.views import render_cell
from combo.utils import is_ajax
from .models import DashboardCell, Tile
from .templatetags.dashboard import get_cell_data
@ -36,52 +46,58 @@ from .templatetags.dashboard import get_cell_data
def dashboard_success(request, dashboard, cell_data):
dashboard_url = dashboard.page.get_online_url()
if request.is_ajax():
if is_ajax(request):
return HttpResponse(
json.dumps({
'err': 0,
'url': request.build_absolute_uri(dashboard_url),
'cell_data': cell_data}),
content_type='application/json')
json.dumps({'err': 0, 'url': request.build_absolute_uri(dashboard_url), 'cell_data': cell_data}),
content_type='application/json',
)
return HttpResponseRedirect(dashboard_url)
class DashboardAddTileView(View):
def get(self, request, *args, **kwargs):
if not request.user.is_authenticated():
if not request.user.is_authenticated:
raise PermissionDenied()
dashboard = DashboardCell.objects.all()[0]
dashboard = DashboardCell.objects.filter(page__snapshot__isnull=True).first()
if dashboard is None:
raise Http404()
cell = CellBase.get_cell(kwargs['cell_reference'])
if not cell.page.is_visible(request.user):
raise PermissionDenied()
if not cell.is_visible(request.user):
if not cell.is_visible(request):
raise PermissionDenied()
cell.pk = None
cell.page = dashboard.page
cell.placeholder = '_dashboard'
cell.save()
tile = Tile(dashboard=dashboard,
cell=cell,
user=request.user,
order=0)
tile = Tile(dashboard=dashboard, cell=cell, user=request.user, order=0)
if settings.COMBO_DASHBOARD_NEW_TILE_POSITION == 'first':
order = Tile.objects.filter(dashboard=dashboard, user=request.user).aggregate(Min('order')).get('order__min')
order = (
Tile.objects.filter(dashboard=dashboard, user=request.user)
.aggregate(Min('order'))
.get('order__min')
)
tile.order = order - 1 if order is not None else 0
elif settings.COMBO_DASHBOARD_NEW_TILE_POSITION == 'last':
order = Tile.objects.filter(dashboard=dashboard, user=request.user).aggregate(Max('order')).get('order_max')
order = (
Tile.objects.filter(dashboard=dashboard, user=request.user)
.aggregate(Max('order'))
.get('order__max')
)
tile.order = order + 1 if order is not None else 0
tile.save()
cell_data = get_cell_data(cell)
cell_data['remove_url'] = reverse(
'combo-dashboard-remove-tile',
kwargs={'cell_reference': cell.get_reference()})
'combo-dashboard-remove-tile', kwargs={'cell_reference': cell.get_reference()}
)
return dashboard_success(request, dashboard, cell_data)
dashboard_add_tile = DashboardAddTileView.as_view()
@ -100,38 +116,55 @@ class DashboardRemoveTileView(View):
# do not remove cell so it can directly be added back
cell_data['add_url'] = reverse(
'combo-dashboard-add-tile',
kwargs={'cell_reference': cell.get_reference()})
'combo-dashboard-add-tile', kwargs={'cell_reference': cell.get_reference()}
)
return dashboard_success(request, dashboard, cell_data)
dashboard_remove_tile = DashboardRemoveTileView.as_view()
@csrf_exempt
def dashboard_auto_tile(request, *args, **kwargs):
dashboard = DashboardCell.objects.all()[0]
cell = ConfigJsonCell(key=kwargs.get('key'), order=1,
page_id=dashboard.page_id, placeholder='_auto_tile')
if request.method != 'POST':
return HttpResponseNotAllowed(['post'])
try:
request_body = json.loads(force_str(request.body))
except json.JSONDecodeError:
return HttpResponseBadRequest('bad json request: "%s"' % request.body)
dashboard = DashboardCell.objects.filter(page__snapshot__isnull=True).first()
if dashboard is None:
raise Http404()
cell = ConfigJsonCell(key=kwargs.get('key'), order=1, page_id=dashboard.page_id, placeholder='_auto_tile')
if cell.key not in settings.JSON_CELL_TYPES:
return HttpResponseBadRequest('bad request, invalid cell type: "%s"' % cell.key)
# only keep parameters that are actually defined for this cell type.
cell_form_keys = [x['varname'] for x in settings.JSON_CELL_TYPES[cell.key].get('form') or {}]
cell.parameters = {}
request_body = json.loads(request.body)
for key in cell_form_keys:
for field in settings.JSON_CELL_TYPES[cell.key].get('form') or []:
key = field['varname']
cell.parameters[key] = request_body.get(key)
if cell.parameters[key] is None and field.get('required', True):
return HttpResponseBadRequest('missing key: %s' % key)
# save cell so it can be reused later, for example to be added to
# dashboard, or to be used as reference in another page, etc.
cell.save()
return render_cell(request, cell=cell)
response = render_cell(request, cell=cell)
response['x-add-to-dashboard-url'] = reverse(
'combo-dashboard-add-tile', kwargs={'cell_reference': cell.get_reference()}
)
return response
def dashboard_reorder_tiles(request, *args, **kwargs):
dashboard = DashboardCell.objects.all()[0]
new_order = request.GET['order'].split(',')
tiles = dict((str(x.id), x) for x in Tile.objects.filter(id__in=new_order))
tiles = {str(x.id): x for x in Tile.objects.filter(id__in=new_order)}
for i, tile_id in enumerate(new_order):
tile = tiles.get(tile_id)
if tile.user != request.user:
@ -141,3 +174,63 @@ def dashboard_reorder_tiles(request, *args, **kwargs):
for tile in tiles.values():
tile.save()
return HttpResponse(status=204)
class TileStats(GenericAPIView):
permission_classes = (permissions.IsAuthenticated,)
def get(self, request, *args, **kwargs):
data = {}
data['users'] = {}
data['users']['count'] = User.objects.all().count()
data['tiles'] = {}
tiles_by_user = {}
manual_tiles_by_user = {}
# preload
cells = {}
for cell in ConfigJsonCell.objects.filter(placeholder__in=['_dashboard', '_suggested_tile']):
cells[cell.id] = cell
for tile in Tile.objects.filter(dashboard__isnull=False):
try:
cell = cells[tile.cell_pk] # no db access
except KeyError:
# likely added after the preload request
continue
if cell.key not in settings.JSON_CELL_TYPES:
continue
if tile.user_id not in tiles_by_user:
tiles_by_user[tile.user_id] = []
tiles_by_user[tile.user_id].append(cell.key)
if cell.key not in data['tiles']:
data['tiles'][cell.key] = {
'name': settings.JSON_CELL_TYPES[cell.key]['name'],
'count': 0,
'manual': 0,
}
data['tiles'][cell.key]['count'] += 1
if cell.placeholder != '_suggested_tile':
data['tiles'][cell.key]['manual'] += 1
if tile.user_id not in manual_tiles_by_user:
manual_tiles_by_user[tile.user_id] = []
manual_tiles_by_user[tile.user_id].append(cell.key)
data['users']['have-tiles'] = len(tiles_by_user.keys())
data['users']['have-more-than-suggested-tiles'] = len(manual_tiles_by_user.keys())
data['users']['have-no-tiles'] = data['users']['count'] - data['users']['have-tiles']
dashboard_lengths = [len(x) for x in tiles_by_user.values()]
if dashboard_lengths:
dashboard_lengths.sort()
data['users']['max-tiles'] = max(dashboard_lengths)
data['users']['median-tiles'] = dashboard_lengths[len(dashboard_lengths) // 2]
return Response(data)
dashboard_tile_stats = TileStats.as_view()

View File

@ -1,33 +0,0 @@
# combo - content management system
# Copyright (C) 2015 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import re
import django.apps
from django.core import checks
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
class AppConfig(django.apps.AppConfig):
name = 'combo.apps.dataviz'
verbose_name = _('Data Visualisation')
def get_before_urls(self):
from . import urls
return urls.urlpatterns
default_app_config = 'combo.apps.dataviz.AppConfig'

View File

@ -0,0 +1,36 @@
# combo - content management system
# Copyright (C) 2015 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import django.apps
from django.utils.translation import gettext_lazy as _
class AppConfig(django.apps.AppConfig):
name = 'combo.apps.dataviz'
verbose_name = _('Data Visualisation')
def get_before_urls(self):
from . import urls
return urls.urlpatterns
def hourly(self):
from .models import ChartNgCell
from .utils import update_available_statistics
update_available_statistics()
for cell in ChartNgCell.objects.all():
cell.check_validity()

View File

@ -0,0 +1,8 @@
from django import forms
class StaticField(forms.Field):
widget = forms.HiddenInput
def bound_data(self, data, initial):
return initial

View File

@ -14,12 +14,39 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import dataclasses
import datetime
from collections import OrderedDict, defaultdict
from django import forms
from django.conf import settings
from django.core.cache import cache
from django.db import transaction
from django.db.models import Q
from django.db.models.fields import BLANK_CHOICE_DASH
from django.template import Context, Template, TemplateSyntaxError, VariableDoesNotExist
from django.utils.translation import gettext_lazy as _
from combo.utils import requests
from combo.utils import cache_during_request, requests, spooler
from .models import ChartCell
from .fields import StaticField
from .models import ChartCell, ChartFiltersCell, ChartNgCell
from .widgets import MultipleSelect2Widget, Select2Widget
@dataclasses.dataclass
class Choice:
id: str
label: str
group: str = None
@staticmethod
def get_field_choices(choices):
choices_by_group = defaultdict(list)
for choice in choices:
choices_by_group[choice.group].append((choice.id, choice.label))
return list(choices_by_group.items())
class ChartForm(forms.ModelForm):
@ -28,12 +55,456 @@ class ChartForm(forms.ModelForm):
fields = ('title', 'url')
def __init__(self, *args, **kwargs):
super(ChartForm, self).__init__(*args, **kwargs)
super().__init__(*args, **kwargs)
available_charts = []
for site_key, site_dict in settings.KNOWN_SERVICES.get('bijoe').items():
result = requests.get('/visualization/json/',
remote_service=site_dict, without_user=True,
headers={'accept': 'application/json'}).json()
for site_dict in (settings.KNOWN_SERVICES.get('bijoe') or {}).values():
result = requests.get(
'/visualization/json/',
remote_service=site_dict,
without_user=True,
headers={'accept': 'application/json'},
).json()
available_charts.extend([(x['path'], x['name']) for x in result])
available_charts.sort(key=lambda x: x[1])
self.fields['url'].widget = forms.Select(choices=available_charts)
@cache_during_request
def trigger_statistics_list_refresh():
transaction.on_commit(spooler.refresh_statistics_list)
class ChartFiltersMixin:
ajax_choices = True
time_intervals = (
Choice('week', _('Week')),
Choice('month', _('Month')),
Choice('year', _('Year')),
Choice('weekday', _('Week day')),
)
def get_filter_fields(self, cell):
fields = OrderedDict()
for filter_ in cell.available_filters:
filter_id = filter_['id']
if filter_.get('deprecated') and (
filter_id not in cell.filter_params
or cell.filter_params.get(filter_id) == filter_.get('default')
):
continue
initial = cell.filter_params.get(filter_id, filter_.get('default'))
required = filter_.get('required', False)
if required and {x['id'] for x in filter_['options'] if isinstance(x, dict)} == {'true', 'false'}:
field = self.build_boolean_field(cell, filter_, initial)
else:
field = self.build_choice_field(cell, filter_, initial)
fields[filter_id] = field
if filter_.get('deprecated'):
fields[filter_id].label += ' (%s)' % _('deprecated')
fields[filter_id].help_text = filter_.get('deprecation_hint')
fields[filter_id].is_filter_field = True
return fields
@classmethod
def get_filter_options(cls, cell, filter_, initial):
filter_id = filter_['id']
filter_options = filter_['options']
if not isinstance(filter_options[0], list):
# no option groups, add empty one for consistency
filter_options = [(None, filter_options)]
choices = [
Choice(id=opt['id'], label=opt['label'], group=group)
for group, options in filter_options
for opt in options
]
if filter_id == 'time_interval':
cls.extend_time_interval_choices(choices)
required = filter_.get('required', False)
multiple = filter_.get('multiple')
if not required and not multiple:
choices.insert(0, Choice(*BLANK_CHOICE_DASH[0]))
extra_variables = cell.page.get_extra_variables_keys()
variable_choices = [
Choice(id='variable:' + key, label=key, group=_('Page variables')) for key in extra_variables
]
for choice in initial if isinstance(initial, list) else [initial]:
if not choice:
continue
if choice.startswith('variable:'):
variable = choice.replace('variable:', '')
if not variable in extra_variables:
variable_choices.append(
Choice(id=choice, label=_('%s (unavailable)') % variable, group=_('Page variables'))
)
elif not any(x.id == choice for x in choices):
choices.append(Choice(id=choice, label=_('%s (unavailable)') % choice))
if variable_choices and not multiple and filter_id != 'time_interval':
choices.extend(variable_choices)
return choices
def build_choice_field(self, cell, filter_, initial):
multiple = filter_.get('multiple')
required = filter_.get('required', False)
choices = self.get_filter_options(cell, filter_, initial)
widget_class = MultipleSelect2Widget if multiple else Select2Widget
widget = widget_class(cell, filter_['id'], choices, initial, self.ajax_choices)
field_class = forms.MultipleChoiceField if multiple else forms.ChoiceField
field = field_class(
label=filter_['label'],
choices=Choice.get_field_choices(choices),
required=required,
initial=initial,
)
field.widget = widget
field.dataviz_choices = choices
return field
def build_boolean_field(self, cell, filter_, initial):
return forms.BooleanField(
label=filter_['label'],
required=False,
initial=bool(initial == 'true'),
)
@classmethod
def extend_time_interval_choices(cls, choices):
if any(choice.id == 'day' for choice in choices):
for choice in cls.time_intervals:
if choice not in choices:
choices.append(choice)
def update_time_range_choices(self, statistic, exclude_template_choice=False):
choices = self.fields['time_range'].choices
if not statistic.has_future_data:
choices = [choice for choice in choices if not choice[0].startswith('next')]
if exclude_template_choice:
choices = [choice for choice in choices if choice[0] != 'range-template']
self.fields['time_range'].choices = choices
def clean(self):
for field, value in self.cleaned_data.items():
if hasattr(self.fields[field], 'is_filter_field') and isinstance(value, bool):
self.cleaned_data[field] = 'true' if value is True else 'false'
class ChartNgForm(ChartFiltersMixin, forms.ModelForm):
class Meta:
model = ChartNgCell
fields = (
'statistic',
'time_range',
'time_range_start',
'time_range_end',
'time_range_start_template',
'time_range_end_template',
'chart_type',
'display_total',
'height',
'sort_order',
'hide_null_values',
'print_values',
)
widgets = {
'time_range_start': forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'),
'time_range_end': forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'),
}
def __init__(self, *args, **kwargs):
trigger_statistics_list_refresh()
super().__init__(*args, **kwargs)
stat_field = self.fields['statistic']
if not self.instance.statistic:
stat_field.queryset = stat_field.queryset.filter(available=True)
else:
# display current statistic in choices even if unavailable
stat_field.queryset = stat_field.queryset.filter(
Q(available=True) | Q(pk=self.instance.statistic.pk)
)
self.add_filter_fields()
self.update_time_range_choices(self.instance.statistic)
if not self.instance.statistic or self.instance.statistic.service_slug == 'bijoe':
for field in (
'time_range',
'time_range_start',
'time_range_end',
'time_range_start_template',
'time_range_end_template',
'display_total',
):
del self.fields[field]
else:
if self.instance.time_range != 'range':
del self.fields['time_range_start']
del self.fields['time_range_end']
if self.instance.time_range != 'range-template':
del self.fields['time_range_start_template']
del self.fields['time_range_end_template']
if not self.instance.is_table_chart() or self.instance.statistic.data_type:
del self.fields['display_total']
def add_filter_fields(self):
new_fields = OrderedDict()
for field_name, field in self.fields.items():
new_fields[field_name] = field
if field_name == 'statistic':
# insert filter fields after statistic field
new_fields.update(self.get_filter_fields(self.instance))
self.fields = new_fields
def save(self, *args, **kwargs):
if 'statistic' in self.changed_data:
self.instance.filter_params.clear()
self.instance.time_range = ''
self.instance.subfilters.clear()
for filter_ in self.instance.available_filters:
if 'default' in filter_:
self.instance.filter_params[filter_['id']] = filter_['default']
elif filter_.get('required'):
options = (
filter_['options'][0][1]
if isinstance(filter_['options'][0], list)
else filter_['options']
)
self.instance.filter_params[filter_['id']] = options[0]['id']
else:
for filter_ in self.instance.available_filters:
if filter_['id'] in self.cleaned_data:
self.instance.filter_params[filter_['id']] = self.cleaned_data[filter_['id']]
cell = super().save(*args, **kwargs)
for filter_ in cell.available_filters:
if filter_.get('has_subfilters') and filter_['id'] in self.changed_data:
cell.update_subfilters()
self.fields = OrderedDict(
(name, field)
for name, field in self.fields.items()
if not hasattr(field, 'is_filter_field')
)
self.add_filter_fields()
break
return cell
def clean(self):
super().clean()
for template_field in ('time_range_start_template', 'time_range_end_template'):
if not self.cleaned_data.get(template_field):
continue
context = {'now': datetime.datetime.now, 'today': datetime.datetime.now}
try:
Template('{{ %s|date:"Y-m-d" }}' % self.cleaned_data[template_field]).render(Context(context))
except (VariableDoesNotExist, TemplateSyntaxError) as e:
self.add_error(template_field, e)
class ChartNgPartialForm(ChartFiltersMixin, forms.ModelForm):
overridden_filters = forms.CharField()
prefix = 'filter'
class Meta:
model = ChartNgCell
fields = (
'time_range',
'time_range_start',
'time_range_end',
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields.update(self.get_filter_fields(self.instance))
for field in self.fields.values():
field.required = False
def clean(self):
super().clean()
for field in self._meta.fields:
if field not in self.cleaned_data['overridden_filters']:
self.cleaned_data[field] = self.initial[field]
for filter_ in self.instance.available_filters:
if filter_['id'] in self.cleaned_data['overridden_filters']:
self.instance.filter_params[filter_['id']] = self.cleaned_data.get(filter_['id'])
def clean_overridden_filters(self):
if not self.cleaned_data['overridden_filters']:
return []
return self.cleaned_data['overridden_filters'].split(',')
class ChartFiltersForm(ChartFiltersMixin, forms.ModelForm):
ajax_choices = False
overridden_filters = StaticField()
prefix = 'filter'
class Meta:
model = ChartNgCell
fields = (
'time_range',
'time_range_start',
'time_range_end',
)
widgets = {
'time_range_start': forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'),
'time_range_end': forms.DateInput(attrs={'type': 'date'}, format='%Y-%m-%d'),
}
def __init__(self, *args, **kwargs):
page = kwargs.pop('page')
filters_cell = kwargs.pop('filters_cell')
filters_cell_id = kwargs.pop('filters_cell_id', None)
super().__init__(*args, **kwargs)
chart_cells = []
for cell in ChartNgCell.objects.filter(page=page, statistic__isnull=False).order_by('order'):
cell.page = page # use cached placeholders
if cell.is_placeholder_active(traverse_cells=False):
chart_cells.append(cell)
if not chart_cells:
self.fields.clear()
return
if filters_cell_id:
for cell in chart_cells:
cell.subfilters = cache.get(cell.get_cache_key(filters_cell_id), cell.subfilters)
first_cell = chart_cells[0]
for field in self._meta.fields:
self.fields[field].initial = getattr(first_cell, field)
dynamic_fields = self.get_filter_fields(first_cell)
dynamic_fields_values = {k: v for k, v in first_cell.filter_params.items()}
if first_cell.time_range == 'range-template':
for field in self._meta.fields:
self.fields.pop(field, None)
else:
self.update_time_range_choices(first_cell.statistic, exclude_template_choice=True)
if not self.is_bound and first_cell.time_range != 'range':
del self.fields['time_range_start']
del self.fields['time_range_end']
for cell in chart_cells[1:]:
cell_filter_fields = self.get_filter_fields(cell)
# keep only common fields
dynamic_fields = {k: v for k, v in dynamic_fields.items() if k in cell_filter_fields}
# keep only same value fields
for field, value in cell.filter_params.items():
if field in dynamic_fields and value != dynamic_fields_values.get(field, ''):
del dynamic_fields[field]
if cell.time_range != first_cell.time_range:
for field in self._meta.fields:
self.fields.pop(field, None)
# ensure compatible choices lists
for field_name, field in cell_filter_fields.items():
if field_name not in dynamic_fields or isinstance(field, forms.BooleanField):
continue
dynamic_fields[field_name].dataviz_choices = [
x for x in dynamic_fields[field_name].dataviz_choices if x in field.dataviz_choices
]
dynamic_fields[field_name].choices = Choice.get_field_choices(
dynamic_fields[field_name].dataviz_choices
)
if dynamic_fields[field_name].choices == []:
del dynamic_fields[field_name]
if 'time_range' in self.fields:
self.update_time_range_choices(cell.statistic)
self.update_backoffice_filter_choices(filters_cell, dynamic_fields)
dynamic_fields = {
name: field for name, field in dynamic_fields.items() if filters_cell.filters[name]['enabled']
}
self.fields.update(dynamic_fields)
self.fields['overridden_filters'].initial = ','.join(self.fields)
@staticmethod
def update_backoffice_filter_choices(filters_cell, dynamic_fields):
# remove absent filters from cell configuration, except if it was disabled
filters_cell.filters = {
k: v for k, v in filters_cell.filters.items() if k in dynamic_fields or not v['enabled']
}
# add filters to cell configuration
for field_name, field in dynamic_fields.items():
if not field_name in filters_cell.filters:
filters_cell.filters[field_name] = {'label': str(field.label), 'enabled': True}
continue
filters_cell.save()
def clean(self):
super().clean()
if self.cleaned_data.get('time_range') != 'range':
self.fields.pop('time_range_start', None)
self.fields.pop('time_range_end', None)
class ChartFiltersConfigForm(forms.ModelForm):
filters = forms.MultipleChoiceField(
label=_('Filters'), widget=forms.CheckboxSelectMultiple, required=False
)
class Meta:
model = ChartFiltersCell
fields = []
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if not self.instance.filters:
del self.fields['filters']
return
self.initial['filters'] = []
self.fields['filters'].choices = []
for filter_id, config in self.instance.filters.items():
self.fields['filters'].choices.append((filter_id, config['label']))
if config['enabled']:
self.initial['filters'].append(filter_id)
def save(self, *args, **kwargs):
for filter_id in self.instance.filters:
self.instance.filters[filter_id]['enabled'] = bool(filter_id in self.cleaned_data['filters'])
return super().save(*args, **kwargs)
class ChartNgExportForm(forms.Form):
export_format = forms.ChoiceField(
label=_('Format'),
choices=(
('svg', _('Picture (SVG)')),
('ods', _('Table (ODS)')),
),
)

View File

@ -1,11 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('auth', '0001_initial'),
('data', '0012_auto_20151029_1535'),
@ -15,18 +11,27 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='Gauge',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('placeholder', models.CharField(max_length=20)),
('order', models.PositiveIntegerField()),
('slug', models.SlugField(verbose_name='Slug', blank=True)),
('public', models.BooleanField(default=True, verbose_name='Public')),
('restricted_to_unlogged', models.BooleanField(default=False, verbose_name='Restrict to unlogged users')),
(
'restricted_to_unlogged',
models.BooleanField(default=False, verbose_name='Restrict to unlogged users'),
),
('title', models.CharField(max_length=150, null=True, verbose_name='Title', blank=True)),
('url', models.CharField(max_length=150, null=True, verbose_name='URL', blank=True)),
('data_source', models.CharField(max_length=150, null=True, verbose_name='Data Source', blank=True)),
(
'data_source',
models.CharField(max_length=150, null=True, verbose_name='Data Source', blank=True),
),
('max_value', models.PositiveIntegerField(null=True, verbose_name='Max Value', blank=True)),
('groups', models.ManyToManyField(to='auth.Group', verbose_name='Groups', blank=True)),
('page', models.ForeignKey(to='data.Page')),
('groups', models.ManyToManyField(to='auth.Group', verbose_name='Roles', blank=True)),
('page', models.ForeignKey(to='data.Page', on_delete=models.CASCADE)),
],
options={
'verbose_name': 'Gauge',

View File

@ -1,11 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0001_initial'),
]

View File

@ -1,11 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('auth', '0001_initial'),
('data', '0012_auto_20151029_1535'),
@ -16,21 +12,39 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='CubesBarChart',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('placeholder', models.CharField(max_length=20)),
('order', models.PositiveIntegerField()),
('slug', models.SlugField(verbose_name='Slug', blank=True)),
('public', models.BooleanField(default=True, verbose_name='Public')),
('restricted_to_unlogged', models.BooleanField(default=False, verbose_name='Restrict to unlogged users')),
(
'restricted_to_unlogged',
models.BooleanField(default=False, verbose_name='Restrict to unlogged users'),
),
('title', models.CharField(max_length=150, null=True, verbose_name='Title', blank=True)),
('url', models.URLField(max_length=150, null=True, verbose_name='URL', blank=True)),
('cube', models.CharField(max_length=256, null=True, verbose_name='Cube', blank=True)),
('aggregate1', models.CharField(max_length=64, null=True, verbose_name='Aggregate', blank=True)),
('drilldown1', models.CharField(max_length=64, null=True, verbose_name='Drilldown 1', blank=True)),
('drilldown2', models.CharField(max_length=64, null=True, verbose_name='Drilldown 2', blank=True)),
('other_parameters', models.TextField(null=True, verbose_name='Other parameters', blank=True)),
('groups', models.ManyToManyField(to='auth.Group', verbose_name='Groups', blank=True)),
('page', models.ForeignKey(to='data.Page')),
(
'aggregate1',
models.CharField(max_length=64, null=True, verbose_name='Aggregate', blank=True),
),
(
'drilldown1',
models.CharField(max_length=64, null=True, verbose_name='Drilldown 1', blank=True),
),
(
'drilldown2',
models.CharField(max_length=64, null=True, verbose_name='Drilldown 2', blank=True),
),
(
'other_parameters',
models.TextField(null=True, verbose_name='Other parameters', blank=True),
),
('groups', models.ManyToManyField(to='auth.Group', verbose_name='Roles', blank=True)),
('page', models.ForeignKey(to='data.Page', on_delete=models.CASCADE)),
],
options={
'verbose_name': 'Cubes Barchart',
@ -40,21 +54,39 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='CubesTable',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('placeholder', models.CharField(max_length=20)),
('order', models.PositiveIntegerField()),
('slug', models.SlugField(verbose_name='Slug', blank=True)),
('public', models.BooleanField(default=True, verbose_name='Public')),
('restricted_to_unlogged', models.BooleanField(default=False, verbose_name='Restrict to unlogged users')),
(
'restricted_to_unlogged',
models.BooleanField(default=False, verbose_name='Restrict to unlogged users'),
),
('title', models.CharField(max_length=150, null=True, verbose_name='Title', blank=True)),
('url', models.URLField(max_length=150, null=True, verbose_name='URL', blank=True)),
('cube', models.CharField(max_length=256, null=True, verbose_name='Cube', blank=True)),
('aggregate1', models.CharField(max_length=64, null=True, verbose_name='Aggregate', blank=True)),
('drilldown1', models.CharField(max_length=64, null=True, verbose_name='Drilldown 1', blank=True)),
('drilldown2', models.CharField(max_length=64, null=True, verbose_name='Drilldown 2', blank=True)),
('other_parameters', models.TextField(null=True, verbose_name='Other parameters', blank=True)),
('groups', models.ManyToManyField(to='auth.Group', verbose_name='Groups', blank=True)),
('page', models.ForeignKey(to='data.Page')),
(
'aggregate1',
models.CharField(max_length=64, null=True, verbose_name='Aggregate', blank=True),
),
(
'drilldown1',
models.CharField(max_length=64, null=True, verbose_name='Drilldown 1', blank=True),
),
(
'drilldown2',
models.CharField(max_length=64, null=True, verbose_name='Drilldown 2', blank=True),
),
(
'other_parameters',
models.TextField(null=True, verbose_name='Other parameters', blank=True),
),
('groups', models.ManyToManyField(to='auth.Group', verbose_name='Roles', blank=True)),
('page', models.ForeignKey(to='data.Page', on_delete=models.CASCADE)),
],
options={
'verbose_name': 'Cubes Table',

View File

@ -1,11 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0003_cubesbarchart_cubestable'),
]

View File

@ -1,11 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0004_auto_20160108_1048'),
]

View File

@ -1,11 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('data', '0020_auto_20160928_1152'),
('dataviz', '0005_auto_20160928_1152'),
@ -15,17 +11,28 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='ChartCell',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
(
'id',
models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True),
),
('placeholder', models.CharField(max_length=20)),
('order', models.PositiveIntegerField()),
('slug', models.SlugField(verbose_name='Slug', blank=True)),
('extra_css_class', models.CharField(max_length=100, verbose_name='Extra classes for CSS styling', blank=True)),
(
'extra_css_class',
models.CharField(
max_length=100, verbose_name='Extra classes for CSS styling', blank=True
),
),
('public', models.BooleanField(default=True, verbose_name='Public')),
('restricted_to_unlogged', models.BooleanField(default=False, verbose_name='Restrict to unlogged users')),
(
'restricted_to_unlogged',
models.BooleanField(default=False, verbose_name='Restrict to unlogged users'),
),
('title', models.CharField(max_length=150, null=True, verbose_name='Title', blank=True)),
('url', models.URLField(max_length=150, null=True, verbose_name='URL', blank=True)),
('groups', models.ManyToManyField(to='auth.Group', verbose_name='Groups', blank=True)),
('page', models.ForeignKey(to='data.Page')),
('groups', models.ManyToManyField(to='auth.Group', verbose_name='Roles', blank=True)),
('page', models.ForeignKey(to='data.Page', on_delete=models.CASCADE)),
],
options={
'verbose_name': 'Chart',

View File

@ -1,13 +1,10 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import datetime
from django.db import migrations, models
import datetime
from django.utils.timezone import utc
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0006_chartcell'),
]

View File

@ -1,12 +1,9 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2019-03-28 07:57
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0007_auto_20170214_2006'),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 1.11.12 on 2019-06-17 10:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0008_auto_20190328_0857'),
]
operations = [
migrations.AlterField(
model_name='chartcell',
name='url',
field=models.URLField(blank=True, max_length=250, null=True, verbose_name='URL'),
),
]

View File

@ -0,0 +1,81 @@
# Generated by Django 1.11.12 on 2019-03-28 10:11
import django.db.models.deletion
from django.db import migrations, models
from django.db.models import JSONField
class Migration(migrations.Migration):
dependencies = [
('data', '0036_page_sub_slug'),
('dataviz', '0009_auto_20190617_1214'),
]
operations = [
migrations.CreateModel(
name='ChartNgCell',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('placeholder', models.CharField(max_length=20)),
('order', models.PositiveIntegerField()),
('slug', models.SlugField(blank=True, verbose_name='Slug')),
(
'extra_css_class',
models.CharField(
blank=True, max_length=100, verbose_name='Extra classes for CSS styling'
),
),
('public', models.BooleanField(default=True, verbose_name='Public')),
(
'restricted_to_unlogged',
models.BooleanField(default=False, verbose_name='Restrict to unlogged users'),
),
('last_update_timestamp', models.DateTimeField(auto_now=True)),
('data_reference', models.CharField(max_length=150, verbose_name='Data')),
('title', models.CharField(blank=True, max_length=150, verbose_name='Title')),
('cached_json', JSONField(blank=True, default=dict)),
(
'chart_type',
models.CharField(
choices=[
(b'bar', 'Bar'),
(b'horizontal-bar', 'Horizontal Bar'),
(b'stacked-bar', 'Stacked Bar'),
(b'line', 'Line'),
(b'pie', 'Pie'),
(b'dot', 'Dot'),
(b'table', 'Table'),
],
default=b'bar',
max_length=20,
verbose_name='Chart Type',
),
),
(
'height',
models.CharField(
choices=[
(b'150', 'Short (150px)'),
(b'250', 'Average (250px)'),
(b'350', 'Tall (350px)'),
],
default=b'250',
max_length=20,
verbose_name='Height',
),
),
('groups', models.ManyToManyField(blank=True, to='auth.Group', verbose_name='Roles')),
('page', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='data.Page')),
],
options={
'verbose_name': 'Chart',
},
),
migrations.AlterModelOptions(
name='chartcell',
options={'verbose_name': 'Chart (legacy)'},
),
]

View File

@ -0,0 +1,68 @@
# Generated by Django 1.11.18 on 2020-08-13 09:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0010_auto_20190328_1111'),
]
operations = [
migrations.AddField(
model_name='chartngcell',
name='hide_null_values',
field=models.BooleanField(
default=False,
help_text='This setting only applies for one-dimensional charts.',
verbose_name='Hide null values',
),
),
migrations.AddField(
model_name='chartngcell',
name='sort_order',
field=models.CharField(
choices=[
('none', 'None'),
('alpha', 'Alphabetically'),
('asc', 'Increasing values'),
('desc', 'Decreasing values'),
],
default='none',
help_text='This setting only applies for one-dimensional charts.',
max_length=5,
verbose_name='Sort data',
),
),
migrations.AlterField(
model_name='chartngcell',
name='chart_type',
field=models.CharField(
choices=[
('bar', 'Bar'),
('horizontal-bar', 'Horizontal Bar'),
('stacked-bar', 'Stacked Bar'),
('stacked-bar-percent', 'Stacked Bar (%)'),
('line', 'Line'),
('pie', 'Pie'),
('pie-percent', 'Pie (%)'),
('dot', 'Dot'),
('table', 'Table'),
('table-inverted', 'Table (inverted)'),
],
default='bar',
max_length=20,
verbose_name='Chart Type',
),
),
migrations.AlterField(
model_name='chartngcell',
name='height',
field=models.CharField(
choices=[('150', 'Short (150px)'), ('250', 'Average (250px)'), ('350', 'Tall (350px)')],
default='250',
max_length=20,
verbose_name='Height',
),
),
]

View File

@ -0,0 +1,49 @@
# Generated by Django 1.11.29 on 2020-11-26 14:57
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0011_auto_20200813_1100'),
]
operations = [
migrations.CreateModel(
name='Statistic',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('slug', models.SlugField(max_length=256, verbose_name='Slug')),
('label', models.CharField(max_length=256, verbose_name='Label')),
('site_slug', models.SlugField(max_length=256, verbose_name='Site slug')),
('service_slug', models.SlugField(max_length=256, verbose_name='Service slug')),
('site_title', models.CharField(max_length=256, verbose_name='Site title')),
('url', models.URLField(verbose_name='Data URL')),
('last_update', models.DateTimeField(auto_now=True, null=True, verbose_name='Last update')),
('available', models.BooleanField(default=True, verbose_name='Available data')),
],
options={
'ordering': ['-available', 'site_title', 'label'],
},
),
migrations.AlterUniqueTogether(
name='statistic',
unique_together={('slug', 'site_slug', 'service_slug')},
),
migrations.AddField(
model_name='chartngcell',
name='statistic',
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name='cells',
to='dataviz.Statistic',
verbose_name='Data',
help_text='This list may take a few seconds to be updated, please refresh the page if an item is missing.',
),
),
]

View File

@ -0,0 +1,37 @@
# Generated by Django 1.11.29 on 2020-11-30 14:26
from django.conf import settings
from django.db import migrations
def update_cells(apps, schema_editor):
Statistic = apps.get_model('dataviz', 'Statistic')
ChartNgCell = apps.get_model('dataviz', 'ChartNgCell')
bijoe_sites = settings.KNOWN_SERVICES.get('bijoe', {})
for cell in ChartNgCell.objects.filter(statistic__isnull=True):
if not cell.data_reference or not cell.cached_json:
continue
site_slug, slug = cell.data_reference.split(':')
statistic, _ = Statistic.objects.get_or_create(
slug=slug,
site_slug=site_slug,
service_slug='bijoe',
defaults={
'label': cell.cached_json['name'],
'url': cell.cached_json['data-url'],
'site_title': bijoe_sites.get(site_slug, {}).get('title'),
},
)
cell.statistic = statistic
cell.save()
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0012_auto_20201126_1557'),
]
operations = [
migrations.RunPython(update_cells, migrations.RunPython.noop),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 1.11.29 on 2020-11-30 14:34
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0013_update_chartng_cells'),
]
operations = [
migrations.RemoveField(
model_name='chartngcell',
name='cached_json',
),
migrations.RemoveField(
model_name='chartngcell',
name='data_reference',
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 1.11.29 on 2020-12-02 13:24
from django.db import migrations
from django.db.models import JSONField
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0014_auto_20201130_1534'),
]
operations = [
migrations.AddField(
model_name='chartngcell',
name='filter_params',
field=JSONField(default=dict),
),
migrations.AddField(
model_name='statistic',
name='filters',
field=JSONField(default=list),
),
]

View File

@ -0,0 +1,34 @@
# Generated by Django 1.11.29 on 2020-12-15 15:24
from django.db import migrations, models
from combo.apps.dataviz.models import TIME_FILTERS
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0015_auto_20201202_1424'),
]
operations = [
migrations.AddField(
model_name='chartngcell',
name='time_range',
field=models.CharField(
blank=True,
choices=TIME_FILTERS,
max_length=20,
verbose_name='Shown period',
),
),
migrations.AddField(
model_name='chartngcell',
name='time_range_end',
field=models.DateField(blank=True, null=True, verbose_name='To'),
),
migrations.AddField(
model_name='chartngcell',
name='time_range_start',
field=models.DateField(blank=True, null=True, verbose_name='From'),
),
]

View File

@ -0,0 +1,11 @@
# Generated by Django 2.2.12 on 2021-04-11 15:54
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0016_auto_20201215_1624'),
]
operations = []

View File

@ -0,0 +1,27 @@
# Generated by Django 2.2.21 on 2021-07-23 11:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0017_text_to_jsonb'),
]
operations = [
migrations.AddField(
model_name='chartcell',
name='template_name',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='Cell Template'),
),
migrations.AddField(
model_name='chartngcell',
name='template_name',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='Cell Template'),
),
migrations.AddField(
model_name='gauge',
name='template_name',
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='Cell Template'),
),
]

View File

@ -0,0 +1,38 @@
# Generated by Django 2.2.19 on 2021-10-06 13:25
from django.db import migrations, models
import combo.data.models
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0018_auto_20210723_1318'),
]
operations = [
migrations.AddField(
model_name='chartngcell',
name='time_range_end_template',
field=models.CharField(
blank=True,
max_length=200,
validators=[combo.data.models.django_template_validator],
verbose_name='To',
),
),
migrations.AddField(
model_name='chartngcell',
name='time_range_start_template',
field=models.CharField(
blank=True,
max_length=200,
validators=[combo.data.models.django_template_validator],
verbose_name='From',
help_text=(
'Template code returning a date. For example, Monday in two weeks would be '
'today|add_days:"14"|adjust_to_week_monday. Page variables are also accessible.'
),
),
),
]

View File

@ -0,0 +1,26 @@
# Generated by Django 2.2.19 on 2022-01-18 10:03
from django.db import migrations
def update_time_intervals(apps, schema_editor):
ChartNgCell = apps.get_model('dataviz', 'ChartNgCell')
for cell in ChartNgCell.objects.all():
save = False
for param in cell.filter_params:
if param == 'time_interval' and cell.filter_params[param].startswith('_'):
cell.filter_params[param] = cell.filter_params[param][1:]
save = True
if save:
cell.save()
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0019_auto_20211006_1525'),
]
operations = [
migrations.RunPython(update_time_intervals, migrations.RunPython.noop),
]

View File

@ -0,0 +1,48 @@
# Generated by Django 2.2.19 on 2022-01-18 10:17
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('data', '0051_link_cell_max_length'),
('auth', '0011_update_proxy_permissions'),
('dataviz', '0020_auto_20220118_1103'),
]
operations = [
migrations.CreateModel(
name='ChartFiltersCell',
fields=[
(
'id',
models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
),
('placeholder', models.CharField(max_length=20)),
('order', models.PositiveIntegerField()),
('slug', models.SlugField(blank=True, verbose_name='Slug')),
(
'extra_css_class',
models.CharField(
blank=True, max_length=100, verbose_name='Extra classes for CSS styling'
),
),
(
'template_name',
models.CharField(blank=True, max_length=50, null=True, verbose_name='Cell Template'),
),
('public', models.BooleanField(default=True, verbose_name='Public')),
(
'restricted_to_unlogged',
models.BooleanField(default=False, verbose_name='Restrict to unlogged users'),
),
('last_update_timestamp', models.DateTimeField(auto_now=True)),
('groups', models.ManyToManyField(blank=True, to='auth.Group', verbose_name='Roles')),
('page', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='data.Page')),
],
options={
'verbose_name': 'Filters',
},
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.19 on 2022-01-25 16:21
import django.contrib.postgres.fields.jsonb
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0021_chartfilterscell'),
]
operations = [
migrations.AddField(
model_name='chartngcell',
name='subfilters',
field=django.contrib.postgres.fields.jsonb.JSONField(default=list),
),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 2.2.19 on 2022-03-17 10:31
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0022_chartngcell_subfilters'),
]
operations = [
migrations.AddField(
model_name='statistic',
name='has_future_data',
field=models.BooleanField(default=False),
),
]

View File

@ -0,0 +1,30 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0023_statistic_has_future_data'),
]
operations = [
migrations.AddField(
model_name='chartcell',
name='condition',
field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
),
migrations.AddField(
model_name='chartfilterscell',
name='condition',
field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
),
migrations.AddField(
model_name='chartngcell',
name='condition',
field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
),
migrations.AddField(
model_name='gauge',
name='condition',
field=models.CharField(blank=True, max_length=1000, null=True, verbose_name='Display condition'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.26 on 2022-12-06 17:03
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0024_display_condition'),
]
operations = [
migrations.AddField(
model_name='statistic',
name='data_type',
field=models.CharField(default='', max_length=32),
preserve_default=False,
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 2.2.26 on 2022-12-12 13:28
import django.contrib.postgres.fields.jsonb
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0025_statistic_data_type'),
]
operations = [
migrations.AddField(
model_name='chartfilterscell',
name='filters',
field=django.contrib.postgres.fields.jsonb.JSONField(default=dict, verbose_name='Filters'),
),
]

View File

@ -0,0 +1,21 @@
# Generated by Django 2.2.26 on 2023-02-22 09:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0026_chartfilterscell_filters'),
]
operations = [
migrations.AlterModelOptions(
name='statistic',
options={'ordering': ['-available', 'deprecated', 'site_title', 'label']},
),
migrations.AddField(
model_name='statistic',
name='deprecated',
field=models.BooleanField(default=False),
),
]

View File

@ -0,0 +1,42 @@
# Generated by Django 3.2.16 on 2024-01-09 09:49
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0027_auto_20230222_1001'),
]
operations = [
migrations.AlterField(
model_name='chartcell',
name='extra_css_class',
field=models.CharField(blank=True, max_length=500, verbose_name='Extra classes for CSS styling'),
),
migrations.AlterField(
model_name='chartfilterscell',
name='extra_css_class',
field=models.CharField(blank=True, max_length=500, verbose_name='Extra classes for CSS styling'),
),
migrations.AlterField(
model_name='chartfilterscell',
name='filters',
field=models.JSONField(default=dict, verbose_name='Filters'),
),
migrations.AlterField(
model_name='chartngcell',
name='extra_css_class',
field=models.CharField(blank=True, max_length=500, verbose_name='Extra classes for CSS styling'),
),
migrations.AlterField(
model_name='chartngcell',
name='subfilters',
field=models.JSONField(default=list),
),
migrations.AlterField(
model_name='gauge',
name='extra_css_class',
field=models.CharField(blank=True, max_length=500, verbose_name='Extra classes for CSS styling'),
),
]

View File

@ -0,0 +1,27 @@
# Generated by Django 3.2.18 on 2024-02-14 11:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0028_increase_extra_css_class'),
]
operations = [
migrations.AddField(
model_name='chartngcell',
name='display_total',
field=models.CharField(
choices=[
('none', 'None'),
('line-and-column', 'Total line and total column'),
('line', 'Total line'),
('column', 'Total column'),
],
default='line-and-column',
max_length=20,
verbose_name='Display of total',
),
),
]

View File

@ -0,0 +1,29 @@
# Generated by Django 3.2.18 on 2024-04-29 12:05
from django.db import migrations
def remove_all_forms_option(apps, schema_editor):
ChartNgCell = apps.get_model('dataviz', 'ChartNgCell')
for cell in ChartNgCell.objects.filter(statistic__slug='forms_counts'):
form_filter = cell.filter_params.get('form')
if not form_filter or not isinstance(form_filter, list):
continue
try:
form_filter.remove('_all')
except ValueError:
continue
cell.save()
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0029_chartngcell_display_total'),
]
operations = [
migrations.RunPython(remove_all_forms_option, migrations.RunPython.noop),
]

View File

@ -0,0 +1,21 @@
# Generated by Django 3.2.18 on 2024-04-30 09:07
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('dataviz', '0030_remove_all_forms_option'),
]
operations = [
migrations.AddField(
model_name='chartngcell',
name='print_values',
field=models.BooleanField(
default=False,
help_text='When not checked values are printed on hover.',
verbose_name='Print values on chart',
),
),
]

View File

@ -14,14 +14,47 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.core.urlresolvers import reverse
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
import copy
import os
import re
import urllib.parse
from collections import OrderedDict
from datetime import date, datetime, timedelta
import pygal
import pygal.util
from dateutil.relativedelta import MO, relativedelta
from django.conf import settings
from django.db import models, transaction
from django.db.models import JSONField
from django.template import RequestContext, Template, TemplateSyntaxError, VariableDoesNotExist
from django.template.defaultfilters import date as format_date
from django.urls import reverse
from django.utils import timezone
from django.utils.dates import WEEKDAYS
from django.utils.functional import cached_property
from django.utils.text import slugify
from django.utils.timesince import timesince
from django.utils.translation import gettext
from django.utils.translation import gettext_lazy as _
from requests.exceptions import RequestException
from combo.data.models import CellBase
from combo.data.library import register_cell_class
from combo.utils import get_templated_url
from combo.data.models import CellBase, django_template_validator
from combo.middleware import get_request
from combo.utils import get_templated_url, requests, spooler
class UnsupportedDataSet(Exception):
pass
class MissingRequest(Exception):
pass
class MissingVariable(Exception):
pass
@register_cell_class
@ -32,7 +65,7 @@ class Gauge(CellBase):
jsonp_data_source = models.BooleanField(_('Use JSONP to get data'), default=True)
max_value = models.PositiveIntegerField(_('Max Value'), blank=True, null=True)
template_name = 'combo/gauge-cell.html'
default_template_name = 'combo/gauge-cell.html'
class Media:
js = ('js/gauge.min.js', 'js/combo.gauge.js')
@ -51,31 +84,37 @@ class Gauge(CellBase):
data_source_url = get_templated_url(self.data_source)
else:
data_source_url = reverse('combo-ajax-gauge-count', kwargs={'cell': self.id})
return {'cell': self,
'title': self.title,
'url': get_templated_url(self.url) if self.url else None,
'max_value': self.max_value,
'data_source_url': data_source_url,
'jsonp': self.jsonp_data_source,
}
return {
'cell': self,
'title': self.title,
'url': get_templated_url(self.url) if self.url else None,
'max_value': self.max_value,
'data_source_url': data_source_url,
'jsonp': self.jsonp_data_source,
}
@register_cell_class
class ChartCell(CellBase):
template_name = 'combo/dataviz-chart.html'
default_template_name = 'combo/dataviz-chart.html'
title = models.CharField(_('Title'), max_length=150, blank=True, null=True)
url = models.URLField(_('URL'), max_length=150, blank=True, null=True)
url = models.URLField(_('URL'), max_length=250, blank=True, null=True)
class Meta:
verbose_name = _('Chart')
verbose_name = _('Chart (legacy)')
@classmethod
def is_enabled(self):
return hasattr(settings, 'KNOWN_SERVICES') and settings.KNOWN_SERVICES.get('bijoe')
def is_enabled(cls):
return (
settings.LEGACY_CHART_CELL_ENABLED
and hasattr(settings, 'KNOWN_SERVICES')
and settings.KNOWN_SERVICES.get('bijoe')
)
def get_default_form_class(self):
from .forms import ChartForm
return ChartForm
def get_additional_label(self):
@ -84,7 +123,773 @@ class ChartCell(CellBase):
return ''
def get_cell_extra_context(self, context):
context = super(ChartCell, self).get_cell_extra_context(context)
context = super().get_cell_extra_context(context)
context['title'] = self.title
context['url'] = self.url
return context
class StatisticManager(models.Manager):
def get_by_natural_key(self, slug, site_slug, service_slug):
return self.get_or_create(slug=slug, site_slug=site_slug, service_slug=service_slug)[0]
class Statistic(models.Model):
slug = models.SlugField(_('Slug'), max_length=256)
label = models.CharField(_('Label'), max_length=256)
site_slug = models.SlugField(_('Site slug'), max_length=256)
service_slug = models.SlugField(_('Service slug'), max_length=256)
site_title = models.CharField(_('Site title'), max_length=256)
url = models.URLField(_('Data URL'))
filters = JSONField(default=list)
has_future_data = models.BooleanField(default=False)
data_type = models.CharField(max_length=32)
deprecated = models.BooleanField(default=False)
available = models.BooleanField(_('Available data'), default=True)
last_update = models.DateTimeField(_('Last update'), null=True, auto_now=True)
objects = StatisticManager()
class Meta:
ordering = ['-available', 'deprecated', 'site_title', 'label']
unique_together = ['slug', 'site_slug', 'service_slug']
def __str__(self):
name = _('%(title)s: %(label)s') % {'title': self.site_title or self.site_slug, 'label': self.label}
if not self.available:
name = _('%s (unavailable)') % name
elif self.deprecated:
name = _('%s (deprecated)') % name
return name
def natural_key(self):
return (self.slug, self.site_slug, self.service_slug)
def has_native_support_for_interval(self, time_interval):
# pylint: disable=not-an-iterable
return any(
time_interval == x['id']
for filter_ in self.filters
for x in filter_['options']
if filter_['id'] == 'time_interval'
)
TIME_FILTERS = [
('previous-year', _('Previous year')),
('current-year', _('Current year')),
('next-year', _('Next year')),
('previous-month', _('Previous month')),
('current-month', _('Current month')),
('next-month', _('Next month')),
('previous-week', _('Previous week')),
('current-week', _('Current week')),
('next-week', _('Next week')),
('range', _('Free range (date)')),
('range-template', _('Free range (template)')),
]
@register_cell_class
class ChartNgCell(CellBase):
statistic = models.ForeignKey(
verbose_name=_('Data'),
to=Statistic,
blank=False,
null=True,
on_delete=models.SET_NULL,
related_name='cells',
help_text=_(
'This list may take a few seconds to be updated, please refresh the page if an item is missing.'
),
)
subfilters = JSONField(default=list)
filter_params = JSONField(default=dict)
title = models.CharField(_('Title'), max_length=150, blank=True)
time_range = models.CharField(
_('Shown period'),
max_length=20,
blank=True,
choices=TIME_FILTERS,
)
time_range_start = models.DateField(_('From'), null=True, blank=True)
time_range_end = models.DateField(_('To'), null=True, blank=True)
time_range_start_template = models.CharField(
_('From'),
max_length=200,
blank=True,
validators=[django_template_validator],
help_text=_(
'Template code returning a date. For example, Monday in two weeks would be '
'today|add_days:"14"|adjust_to_week_monday. Page variables are also accessible.'
),
)
time_range_end_template = models.CharField(
_('To'),
max_length=200,
blank=True,
validators=[django_template_validator],
)
chart_type = models.CharField(
_('Chart Type'),
max_length=20,
default='bar',
choices=(
('bar', _('Bar')),
('horizontal-bar', _('Horizontal Bar')),
('stacked-bar', _('Stacked Bar')),
('stacked-bar-percent', _('Stacked Bar (%)')),
('line', _('Line')),
('pie', _('Pie')),
('pie-percent', _('Pie (%)')),
('dot', _('Dot')),
('table', _('Table')),
('table-inverted', _('Table (inverted)')),
),
)
display_total = models.CharField(
_('Display of total'),
max_length=20,
default='line-and-column',
choices=(
('none', _('None')),
('line-and-column', _('Total line and total column')),
('line', _('Total line')),
('column', _('Total column')),
),
)
height = models.CharField(
_('Height'),
max_length=20,
default='250',
choices=(
('150', _('Short (150px)')),
('250', _('Average (250px)')),
('350', _('Tall (350px)')),
),
)
sort_order = models.CharField(
_('Sort data'),
max_length=5,
default='none',
help_text=_('This setting only applies for one-dimensional charts.'),
choices=(
('none', _('None')),
('alpha', _('Alphabetically')),
('asc', _('Increasing values')),
('desc', _('Decreasing values')),
),
)
hide_null_values = models.BooleanField(
default=False,
verbose_name=_('Hide null values'),
help_text=_('This setting only applies for one-dimensional charts.'),
)
print_values = models.BooleanField(
default=False,
verbose_name=_('Print values on chart'),
help_text=_('When not checked values are printed on hover.'),
)
manager_form_template = 'combo/chartngcell_form.html'
invalid_reason_codes = {
'missing_statistic_url': _('No statistic URL set'),
'statistic_data_not_found': _('Statistic URL seems to unexist'),
'statistic_url_invalid': _('Statistic URL seems to be invalid'),
}
class Meta:
verbose_name = _('Chart')
class Media:
js = ('js/chartngcell.js',)
css = {'all': ('css/combo.chartngcell.css',)}
@classmethod
def is_enabled(cls):
return settings.KNOWN_SERVICES.get('bijoe') or settings.STATISTICS_PROVIDERS
def get_default_form_class(self):
from .forms import ChartNgForm
return ChartNgForm
def get_additional_label(self):
return self.title
def get_download_filename(self):
label = slugify(self.title or self.statistic.label)
return 'export-%s-%s' % (label, date.today().strftime('%Y%m%d'))
def is_relevant(self, context):
return bool(self.statistic)
def is_table_chart(self):
return bool(self.chart_type in ('table', 'table-inverted'))
def check_validity(self):
if not self.statistic:
return
if not self.statistic.url:
self.mark_as_invalid('missing_statistic_url')
return
resp = None
try:
resp = self.get_statistic_data()
except (RequestException, MissingRequest, MissingVariable):
pass
self.set_validity_from_url(
resp, not_found_code='statistic_data_not_found', invalid_code='statistic_url_invalid'
)
def get_statistic_data(self, filter_params=None, raise_if_not_cached=False, invalidate_cache=False):
headers = {
'X-Statistics-Page-URL': urllib.parse.urljoin(
settings.SITE_BASE_URL,
reverse('combo-manager-page-view', kwargs={'pk': self.page_id})
+ '#cell-%s' % self.get_reference(),
)
}
return requests.get(
self.statistic.url,
params=filter_params or self.get_filter_params(),
headers=headers,
cache_duration=300,
remote_service='auto',
without_user=True,
raise_if_not_cached=raise_if_not_cached,
log_errors=False,
invalidate_cache=invalidate_cache,
)
def get_chart(self, width=None, height=None, raise_if_not_cached=False):
filter_params = self.get_filter_params()
transaction.on_commit(
lambda: spooler.refresh_statistics_data(cell_pk=self.pk, filter_params=filter_params)
)
response = self.get_statistic_data(filter_params, raise_if_not_cached)
response.raise_for_status()
response = response.json()
style = pygal.style.DefaultStyle(font_family='"Open Sans", sans-serif', background='transparent')
chart = {
'bar': pygal.Bar,
'horizontal-bar': pygal.HorizontalBar,
'stacked-bar': pygal.StackedBar,
'stacked-bar-percent': pygal.StackedBar,
'line': pygal.Line,
'pie': pygal.Pie,
'pie-percent': pygal.Pie,
'dot': pygal.Dot,
'table': pygal.Bar,
'table-inverted': pygal.Bar,
}[self.chart_type](
config=pygal.Config(
style=copy.copy(style), order_min=0.1, max_scale=5, print_values=self.print_values
)
)
if self.statistic.service_slug == 'bijoe':
x_labels, y_labels, data = self.parse_response(response, chart)
chart.x_labels = x_labels
if chart.axis_count == 1:
data = self.process_one_dimensional_data(chart, data)
if getattr(chart, 'compute_sum', True) and self.is_table_chart():
data = self.add_total_to_line_table(chart, data)
self.add_data_to_chart(chart, data, y_labels)
else:
data = response['data']
interval = self.filter_params.get('time_interval', '')
if data['x_labels'] and interval:
if interval == 'day' or not self.statistic.has_native_support_for_interval(interval):
self.aggregate_data(data, interval)
elif interval == 'month':
data['x_labels'] = [
format_date(datetime.strptime(x, '%Y-%m'), 'M Y') for x in data['x_labels']
]
chart.x_labels = data['x_labels']
chart.axis_count = min(len(data['series']), 2)
chart.compute_sum = False
if self.statistic.data_type:
chart.config.value_formatter = self.get_value_formatter(self.statistic.data_type)
if chart.axis_count == 1:
data['series'][0]['data'] = self.process_one_dimensional_data(
chart, data['series'][0]['data']
)
if self.chart_type in ('pie', 'pie-percent'):
data['series'] = [
{'label': chart.config.x_value_formatter(label), 'data': [data]}
for label, data in zip(chart.x_labels, data['series'][0]['data'])
if data
]
elif self.chart_type == 'dot':
data['series'][0]['label'] = ''
if self.chart_type == 'stacked-bar-percent':
self.make_percent([serie['data'] for serie in data['series']])
elif self.chart_type == 'pie-percent':
self.make_global_percent([serie['data'] for serie in data['series']])
chart.config.value_formatter = self.get_value_formatter(measure='percent')
for serie in data['series']:
chart.add(serie['label'], serie['data'])
if self.is_table_chart() and not self.statistic.data_type:
self.add_total_to_table(chart, [serie['data'] for serie in data['series']])
self.configure_chart(chart, width, height)
return chart
def get_filter_params(self):
params = {k: self.evaluate_filter_value(v) for k, v in self.filter_params.items() if v}
now = timezone.now().date()
if self.time_range == 'current-year':
params['start'] = date(year=now.year, month=1, day=1)
params['end'] = date(year=now.year + 1, month=1, day=1)
elif self.time_range == 'previous-year':
params['start'] = date(year=now.year - 1, month=1, day=1)
params['end'] = date(year=now.year, month=1, day=1)
elif self.time_range == 'next-year':
params['start'] = date(year=now.year + 1, month=1, day=1)
params['end'] = date(year=now.year + 2, month=1, day=1)
elif self.time_range == 'current-month':
params['start'] = now.replace(day=1)
params['end'] = now + relativedelta(day=1, months=1)
elif self.time_range == 'previous-month':
params['start'] = now + relativedelta(day=1, months=-1)
params['end'] = now.replace(day=1)
elif self.time_range == 'next-month':
params['start'] = now + relativedelta(day=1, months=1)
params['end'] = now + relativedelta(day=1, months=2)
elif self.time_range == 'current-week':
params['start'] = now + relativedelta(weekday=MO(-1))
params['end'] = now + relativedelta(weekday=MO(+1), days=+1)
elif self.time_range == 'previous-week':
params['start'] = now + relativedelta(weekday=MO(-2))
params['end'] = now + relativedelta(weekday=MO(-1))
elif self.time_range == 'next-week':
params['start'] = now + relativedelta(weekday=MO(+1), days=+1)
params['end'] = now + relativedelta(weekday=MO(+2), days=+1)
elif self.time_range == 'range':
if self.time_range_start:
params['start'] = self.time_range_start
if self.time_range_end:
params['end'] = self.time_range_end
elif self.time_range == 'range-template':
if self.time_range_start_template:
start = self.evaluate_range_template(self.time_range_start_template)
if start:
params['start'] = start
if self.time_range_end_template:
end = self.evaluate_range_template(self.time_range_end_template)
if end:
params['end'] = end
if 'time_interval' in params and not self.statistic.has_native_support_for_interval(
params['time_interval']
):
params['time_interval'] = 'day'
return params
def evaluate_range_template(self, value):
if value in self.page.extra_variables:
value = self.page.extra_variables[value].strip('{ }')
context = self.request_context
context.update({'now': datetime.now, 'today': datetime.now})
try:
return Template('{{ %s|date:"Y-m-d" }}' % value).render(context)
except (VariableDoesNotExist, TemplateSyntaxError):
return None
def evaluate_filter_value(self, value):
if isinstance(value, list) or not value.startswith('variable:'):
return value
try:
variable = self.page.extra_variables[value.replace('variable:', '')]
except KeyError:
raise MissingVariable
return Template(variable).render(self.request_context)
@cached_property
def request_context(self):
if not getattr(self, '_request', None):
raise MissingRequest
ctx = RequestContext(self._request, getattr(self._request, 'extra_context', {}))
ctx['request'] = self._request
return ctx
def parse_response(self, response, chart):
# normalize axis to have a fake axis when there are no dimensions and
# always a x axis when there is a single dimension.
data = response['data']
loop_labels = response['axis'].get('loop') or []
x_labels = response['axis'].get('x_labels') or []
y_labels = response['axis'].get('y_labels') or []
if loop_labels:
if 'x_labels' in response['axis'] and 'y_labels' in response['axis']:
# no support for three dimensions
raise UnsupportedDataSet()
if not y_labels:
y_labels = loop_labels
else:
x_labels, y_labels = y_labels, loop_labels
if (
x_labels
and y_labels
and (len(y_labels) != len(data) or not all([len(x) == len(x_labels) for x in data]))
):
# varying dimensions
raise UnsupportedDataSet()
if not x_labels and not y_labels: # unidata
x_labels = ['']
y_labels = ['']
data = [data]
chart.axis_count = 0
elif not x_labels:
x_labels = y_labels
y_labels = ['']
chart.axis_count = 1
elif not y_labels:
y_labels = ['']
chart.axis_count = 1
else:
chart.axis_count = 2
chart.compute_sum = bool(response.get('measure') == 'integer' and chart.axis_count > 0)
formatter = self.get_value_formatter(response.get('unit'), response.get('measure'))
if formatter is not None:
chart.config.value_formatter = formatter
return x_labels, y_labels, data
def configure_dot_chart(self, chart, width, height):
chart.show_legend = False
# use a single colour for dots
chart.config.style.colors = ('#1f77b4',) * max(len(chart.x_labels), 1)
def configure_horizontal_bar_chart(self, chart, width, height):
if width and width < 500:
# truncate labels
chart.x_labels = [pygal.util.truncate(x, 15) for x in chart.x_labels]
def configure_pie_chart(self, chart, width, height):
chart.show_legend = True
if width and height:
# pies are as tall as wide, reserve the appropriate space and distribute
# the rest for the legend.
chart.truncate_legend = (width - height) // 10
elif width:
chart.truncate_legend = width // 20
def configure_pie_percent_chart(self, *args, **kwargs):
self.configure_pie_chart(*args, **kwargs)
def configure_chart(self, chart, width, height):
auto_height_scale = pygal.style.DefaultStyle.legend_font_size * 1.75
chart.config.margin = 0
if width:
chart.config.width = width
height = height or int(self.height)
# adapt chart's height to legend length
chart.config.height = max(height, auto_height_scale * len(chart.raw_series))
if width or height:
chart.config.explicit_size = True
chart.config.js = [os.path.join(settings.STATIC_URL, 'js/pygal-tooltips.js')]
chart.show_legend = bool(chart.axis_count > 1)
chart.truncate_legend = 30
# matplotlib tab10 palette
chart.config.style.colors = (
'#1f77b4',
'#ff7f0e',
'#2ca02c',
'#d62728',
'#9467bd',
'#8c564b',
'#e377c2',
'#7f7f7f',
'#bcbd22',
'#17becf',
)
custom_configure_method_name = 'configure_%s_chart' % self.chart_type.replace('-', '_')
if hasattr(self, custom_configure_method_name):
getattr(self, custom_configure_method_name)(chart, width, height)
if self.chart_type not in ('pie', 'pie-percent'):
if width and width < 500:
chart.legend_at_bottom = True
# restore demanded chart's height
chart.config.height = height
def process_one_dimensional_data(self, chart, data):
if self.hide_null_values:
data = self.hide_values(chart, data)
if data and self.sort_order != 'none':
data = self.sort_values(chart, data)
return data
@staticmethod
def hide_values(chart, data):
x_labels, new_data = [], []
for label, value in zip(chart.x_labels, data):
if value:
x_labels.append(label)
new_data.append(value)
chart.x_labels = x_labels
return new_data
def sort_values(self, chart, data):
if self.sort_order == 'alpha':
digit_re = re.compile('([0-9]+)')
def natural_sort_key(item):
return [
int(text) if text.isdigit() else text.lower() for text in digit_re.split(str(item[0]))
]
tmp_items = sorted(zip(chart.x_labels, data), key=natural_sort_key)
elif self.sort_order == 'asc':
tmp_items = sorted(zip(chart.x_labels, data), key=lambda x: (x[1] or 0))
elif self.sort_order == 'desc':
tmp_items = sorted(zip(chart.x_labels, data), key=lambda x: (x[1] or 0), reverse=True)
x_labels, sorted_data = zip(*((label, value) for label, value in tmp_items))
chart.x_labels = list(x_labels)
return list(sorted_data)
@staticmethod
def add_total_to_line_table(chart, data):
# workaround pygal
chart.compute_sum = False
# do not add total for single point
if len(data) <= 1:
return data
data.append(sum(x for x in data if x is not None))
chart.x_labels.append(gettext('Total'))
return data
def add_total_to_table(self, chart, series_data):
if chart.axis_count == 0:
return
# do not add total for single point
if len(series_data) == 1 and len(series_data[0]) == 1:
return
if self.display_total in ('line', 'line-and-column'):
chart.x_labels.append(gettext('Total'))
for serie in series_data:
serie.append(sum(x for x in serie if x is not None))
if chart.axis_count == 1:
return
if self.display_total in ('column', 'line-and-column'):
line_totals = []
for line in zip(*series_data):
line_totals.append(sum(x for x in line if x is not None))
chart.add(gettext('Total'), line_totals)
def add_data_to_chart(self, chart, data, y_labels):
if self.chart_type not in ('pie', 'pie-percent'):
series_data = []
for i, serie_label in enumerate(y_labels):
if chart.axis_count < 2:
values = data
else:
values = [data[i][j] for j in range(len(chart.x_labels))]
series_data.append(values)
chart.add(serie_label, values)
if self.chart_type == 'stacked-bar-percent':
self.make_percent(series_data)
else:
# pie, create a serie by data, to get different colours
values = data
for label, value in zip(chart.x_labels, values):
if not value:
continue
chart.add(label, value)
@staticmethod
def get_value_formatter(unit=None, measure='duration'):
if unit == 'seconds' or measure == 'duration':
def format_duration(value):
if value is None:
return '-'
base_date = datetime(1871, 3, 18)
return timesince(base_date, base_date + timedelta(seconds=value))
return format_duration
elif measure == 'percent':
percent_formatter = lambda x: f'{x:.1f}%'
return percent_formatter
def make_percent(self, series_data):
for i, values in enumerate(zip(*series_data)):
sum_values = sum(v for v in values if v is not None)
if sum_values == 0:
continue
factor = 100 / sum_values
for values in series_data:
if values[i] is not None:
values[i] = round(values[i] * factor, 1)
def make_global_percent(self, series_data):
sum_values = sum(v for values in series_data for v in values if v is not None)
if sum_values == 0:
return
factor = 100 / sum_values
for serie in series_data:
for i, value in enumerate(serie):
if value is not None:
serie[i] = round(value * factor, 1)
@staticmethod
def aggregate_data(data, interval):
series_data = [serie['data'] for serie in data['series']]
dates = [datetime.strptime(label, '%Y-%m-%d') for label in data['x_labels']]
min_date, max_date = min(dates), max(dates)
date_formats = {
'day': 'd M Y',
# Translators: This indicates week number followed by year, for example it can yield W2-2021.
# First "W" is the first letter of the word "week" and should be translated accordingly, second
# "W" and "o" are interpreted by Django's date filter and should be left as is. First W is
# backslash escaped to prevent it from being interpreted, translators should refer to Django's
# documentation in order to know if the new letter resulting of translation should be escaped or not.
'week': gettext(r'\WW-o'),
'month': 'M Y',
'year': 'Y',
'weekday': 'l',
}
if interval == 'day':
x_labels = [
format_date(min_date + timedelta(days=i), date_formats['day'])
for i in range((max_date - min_date).days + 1)
]
elif interval == 'month':
month_difference = max_date.month - min_date.month + (max_date.year - min_date.year) * 12
x_labels = [
format_date(min_date + relativedelta(months=i), date_formats['month'])
for i in range(month_difference + 1)
]
elif interval == 'year':
x_labels = [str(year) for year in range(min_date.year, max_date.year + 1)]
elif interval == 'weekday':
x_labels = [str(label) for label in WEEKDAYS.values()]
elif interval == 'week':
x_labels = []
date, last_date = min_date, max_date
if min_date.weekday() > max_date.weekday():
last_date += relativedelta(weeks=1)
while date <= last_date:
x_labels.append(format_date(date, date_formats['week']))
date += relativedelta(weeks=1)
aggregates = OrderedDict((label, [0] * len(series_data)) for label in x_labels)
for i, date in enumerate(dates):
key = format_date(date, date_formats[interval])
for j, dummy in enumerate(series_data):
aggregates[key][j] += series_data[j][i] or 0
data['x_labels'] = x_labels
for i, serie in enumerate(data['series']):
serie['data'] = [values[i] for values in aggregates.values()]
@property
def available_filters(self):
return self.statistic.filters + self.subfilters
def update_subfilters(self, filter_params=None):
self._request = get_request()
try:
response = self.get_statistic_data(filter_params=filter_params)
except (TemplateSyntaxError, VariableDoesNotExist):
return
try:
response.raise_for_status()
data = response.json()['data']
except Exception:
return
new_subfilters = data.get('subfilters', [])
if self.subfilters != new_subfilters:
self.subfilters = new_subfilters
subfilter_ids = {filter_['id'] for filter_ in self.available_filters}
self.filter_params = {k: v for k, v in self.filter_params.items() if k in subfilter_ids}
self.save()
def get_cache_key(self, filters_cell_id):
return 'dataviz:%s:%s' % (filters_cell_id, self.pk)
@register_cell_class
class ChartFiltersCell(CellBase):
filters = JSONField(_('Filters'), default=dict)
title = _('Filters')
default_template_name = 'combo/chart-filters.html'
manager_form_template = 'combo/chartfilterscell_form.html'
max_one_by_page = True
class Meta:
verbose_name = _('Filters')
class Media:
js = ('xstatic/select2.min.js', 'xstatic/i18n/fr.js')
css = {'all': ('xstatic/select2.min.css',)}
@classmethod
def is_enabled(cls):
return settings.CHART_FILTERS_CELL_ENABLED and settings.STATISTICS_PROVIDERS
def get_cell_extra_context(self, context):
from .forms import ChartFiltersForm
ctx = super().get_cell_extra_context(context)
if 'filters_cell_id' in context['request'].GET: # detect refresh on submit
ctx['form'] = ChartFiltersForm(
data=context['request'].GET,
page=self.page,
filters_cell=self,
filters_cell_id=context['request'].GET['filters_cell_id'],
)
ctx['form'].errors.clear()
else:
ctx['form'] = ChartFiltersForm(page=self.page, filters_cell=self)
return ctx
def get_default_form_class(self):
from .forms import ChartFiltersConfigForm
return ChartFiltersConfigForm

View File

@ -0,0 +1,21 @@
.cell.chart-ng-cell {
position: relative;
}
.chart-ng-cell .download-button {
position: absolute;
bottom: 0px;
right: 0px;
line-height: unset;
}
.chart-ng-cell .download-button:after {
font-family: FontAwesome;
content: "\f019"; /* download */
}
.dataviz-table.total-line tr:last-child,
.dataviz-table.total-line-and-column tr:last-child {
font-weight: 600;
background: #f7f7f7;
}

View File

@ -0,0 +1,14 @@
function get_graph_querystring(extra_context, width=undefined) {
qs = [];
if ($('#chart-filters').length) {
qs.push($('#chart-filters').serialize());
qs.push('filters_cell_id=' + $('body').data('filters-cell-id'));
}
if (extra_context)
qs.push('ctx=' + extra_context);
if (window.location.search)
qs.push(window.location.search.slice(1));
if (width)
qs.push('width=' + width);
return '?' + qs.join('&');
};

View File

@ -0,0 +1,353 @@
(function() {
var $, get_translation, init, init_svg, matches, padding, r_translation, sibl, svg_ns, tooltip_timeout, xlink_ns;
svg_ns = 'http://www.w3.org/2000/svg';
xlink_ns = 'http://www.w3.org/1999/xlink';
$ = function(sel, ctx) {
if (ctx == null) {
ctx = null;
}
ctx = ctx || document;
return Array.prototype.slice.call(ctx.querySelectorAll(sel), 0).filter(function(e) {
return e !== ctx;
});
};
matches = function(el, selector) {
return (el.matches || el.matchesSelector || el.msMatchesSelector || el.mozMatchesSelector || el.webkitMatchesSelector || el.oMatchesSelector).call(el, selector);
};
sibl = function(el, match) {
if (match == null) {
match = null;
}
return Array.prototype.filter.call(el.parentElement.children, function(child) {
return child !== el && (!match || matches(child, match));
});
};
Array.prototype.one = function() {
return this.length > 0 && this[0] || {};
};
padding = 5;
tooltip_timeout = null;
r_translation = /translate\((\d+)[ ,]+(\d+)\)/;
get_translation = function(el) {
return (r_translation.exec(el.getAttribute('transform')) || []).slice(1).map(function(x) {
return +x;
});
};
init = function(ctx) {
var bbox, box, config, el, graph, inner_svg, num, parent, tooltip, tooltip_el, tt, uid, untooltip, xconvert, yconvert, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3;
if ($('svg', ctx).length) {
inner_svg = $('svg', ctx).one();
parent = inner_svg.parentElement;
box = inner_svg.viewBox.baseVal;
bbox = parent.getBBox();
xconvert = function(x) {
return ((x - box.x) / box.width) * bbox.width;
};
yconvert = function(y) {
return ((y - box.y) / box.height) * bbox.height;
};
} else {
xconvert = yconvert = function(x) {
return x;
};
}
if (((_ref = window.pygal) != null ? _ref.config : void 0) != null) {
if (window.pygal.config.no_prefix != null) {
config = window.pygal.config;
} else {
uid = ctx.id.replace('chart-', '');
config = window.pygal.config[uid];
}
} else {
config = window.config;
}
tooltip_el = null;
graph = $('.graph').one();
tt = $('.tooltip', ctx).one();
_ref1 = $('.reactive', ctx);
for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
el = _ref1[_i];
el.addEventListener('mouseenter', (function(el) {
return function() {
return el.classList.add('active');
};
})(el));
el.addEventListener('mouseleave', (function(el) {
return function() {
return el.classList.remove('active');
};
})(el));
}
_ref2 = $('.activate-serie', ctx);
for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
el = _ref2[_j];
num = el.id.replace('activate-serie-', '');
el.addEventListener('mouseenter', (function(num) {
return function() {
var re, _k, _l, _len2, _len3, _ref3, _ref4, _results;
_ref3 = $('.serie-' + num + ' .reactive', ctx);
for (_k = 0, _len2 = _ref3.length; _k < _len2; _k++) {
re = _ref3[_k];
re.classList.add('active');
}
_ref4 = $('.serie-' + num + ' .showable', ctx);
_results = [];
for (_l = 0, _len3 = _ref4.length; _l < _len3; _l++) {
re = _ref4[_l];
_results.push(re.classList.add('shown'));
}
return _results;
};
})(num));
el.addEventListener('mouseleave', (function(num) {
return function() {
var re, _k, _l, _len2, _len3, _ref3, _ref4, _results;
_ref3 = $('.serie-' + num + ' .reactive', ctx);
for (_k = 0, _len2 = _ref3.length; _k < _len2; _k++) {
re = _ref3[_k];
re.classList.remove('active');
}
_ref4 = $('.serie-' + num + ' .showable', ctx);
_results = [];
for (_l = 0, _len3 = _ref4.length; _l < _len3; _l++) {
re = _ref4[_l];
_results.push(re.classList.remove('shown'));
}
return _results;
};
})(num));
el.addEventListener('click', (function(el, num) {
return function() {
var ov, re, rect, show, _k, _l, _len2, _len3, _ref3, _ref4, _results;
rect = $('rect', el).one();
show = rect.style.fill !== '';
rect.style.fill = show ? '' : 'transparent';
_ref3 = $('.serie-' + num + ' .reactive', ctx);
for (_k = 0, _len2 = _ref3.length; _k < _len2; _k++) {
re = _ref3[_k];
re.style.display = show ? '' : 'none';
}
_ref4 = $('.text-overlay .serie-' + num, ctx);
_results = [];
for (_l = 0, _len3 = _ref4.length; _l < _len3; _l++) {
ov = _ref4[_l];
_results.push(ov.style.display = show ? '' : 'none');
}
return _results;
};
})(el, num));
}
_ref3 = $('.tooltip-trigger', ctx);
for (_k = 0, _len2 = _ref3.length; _k < _len2; _k++) {
el = _ref3[_k];
el.addEventListener('mouseenter', (function(el) {
return function() {
return tooltip_el = tooltip(el);
};
})(el));
}
tt.addEventListener('mouseenter', function() {
return tooltip_el != null ? tooltip_el.classList.add('active') : void 0;
});
tt.addEventListener('mouseleave', function() {
return tooltip_el != null ? tooltip_el.classList.remove('active') : void 0;
});
ctx.addEventListener('mouseleave', function() {
if (tooltip_timeout) {
clearTimeout(tooltip_timeout);
}
return untooltip(0);
});
graph.addEventListener('mousemove', function(el) {
if (tooltip_timeout) {
return;
}
if (!matches(el.target, '.background')) {
return;
}
return untooltip(1000);
});
tooltip = function(el) {
var a, baseline, cls, current_x, current_y, dy, h, i, key, keys, label, legend, name, plot_x, plot_y, rect, serie_index, subval, text, text_group, texts, traversal, value, w, x, x_elt, x_label, xlink, y, y_elt, _l, _len3, _len4, _len5, _m, _n, _ref4, _ref5, _ref6, _ref7, _ref8;
clearTimeout(tooltip_timeout);
tooltip_timeout = null;
tt.style.opacity = 1;
tt.style.display = '';
text_group = $('g.text', tt).one();
rect = $('rect', tt).one();
text_group.innerHTML = '';
label = sibl(el, '.label').one().textContent;
x_label = sibl(el, '.x_label').one().textContent;
value = sibl(el, '.value').one().textContent;
xlink = sibl(el, '.xlink').one().textContent;
serie_index = null;
parent = el;
traversal = [];
while (parent) {
traversal.push(parent);
if (parent.classList.contains('series')) {
break;
}
parent = parent.parentElement;
}
if (parent) {
_ref4 = parent.classList;
for (_l = 0, _len3 = _ref4.length; _l < _len3; _l++) {
cls = _ref4[_l];
if (cls.indexOf('serie-') === 0) {
serie_index = +cls.replace('serie-', '');
break;
}
}
}
legend = null;
if (serie_index !== null) {
legend = config.legends[serie_index];
}
dy = 0;
keys = [[label, 'label']];
_ref5 = value.split('\n');
for (i = _m = 0, _len4 = _ref5.length; _m < _len4; i = ++_m) {
subval = _ref5[i];
keys.push([subval, 'value-' + i]);
}
if (config.tooltip_fancy_mode) {
keys.push([xlink, 'xlink']);
keys.unshift([x_label, 'x_label']);
keys.unshift([legend, 'legend']);
}
texts = {};
for (_n = 0, _len5 = keys.length; _n < _len5; _n++) {
_ref6 = keys[_n], key = _ref6[0], name = _ref6[1];
if (key) {
text = document.createElementNS(svg_ns, 'text');
text.textContent = key;
text.setAttribute('x', padding);
text.setAttribute('dy', dy);
text.classList.add(name.indexOf('value') === 0 ? 'value' : name);
if (name.indexOf('value') === 0 && config.tooltip_fancy_mode) {
text.classList.add('color-' + serie_index);
}
if (name === 'xlink') {
a = document.createElementNS(svg_ns, 'a');
a.setAttributeNS(xlink_ns, 'href', key);
a.textContent = void 0;
a.appendChild(text);
text.textContent = 'Link >';
text_group.appendChild(a);
} else {
text_group.appendChild(text);
}
dy += text.getBBox().height + padding / 2;
baseline = padding;
if (text.style.dominantBaseline !== void 0) {
text.style.dominantBaseline = 'text-before-edge';
} else {
baseline += text.getBBox().height * .8;
}
text.setAttribute('y', baseline);
texts[name] = text;
}
}
w = text_group.getBBox().width + 2 * padding;
h = text_group.getBBox().height + 2 * padding;
rect.setAttribute('width', w);
rect.setAttribute('height', h);
if (texts.value) {
texts.value.setAttribute('dx', (w - texts.value.getBBox().width) / 2 - padding);
}
if (texts.x_label) {
texts.x_label.setAttribute('dx', w - texts.x_label.getBBox().width - 2 * padding);
}
if (texts.xlink) {
texts.xlink.setAttribute('dx', w - texts.xlink.getBBox().width - 2 * padding);
}
x_elt = sibl(el, '.x').one();
y_elt = sibl(el, '.y').one();
x = parseInt(x_elt.textContent);
if (x_elt.classList.contains('centered')) {
x -= w / 2;
} else if (x_elt.classList.contains('left')) {
x -= w;
} else if (x_elt.classList.contains('auto')) {
x = xconvert(el.getBBox().x + el.getBBox().width / 2) - w / 2;
}
y = parseInt(y_elt.textContent);
if (y_elt.classList.contains('centered')) {
y -= h / 2;
} else if (y_elt.classList.contains('top')) {
y -= h;
} else if (y_elt.classList.contains('auto')) {
y = yconvert(el.getBBox().y + el.getBBox().height / 2) - h / 2;
}
_ref7 = get_translation(tt.parentElement), plot_x = _ref7[0], plot_y = _ref7[1];
if (x + w + plot_x > config.width) {
x = config.width - w - plot_x;
}
if (y + h + plot_y > config.height) {
y = config.height - h - plot_y;
}
if (x + plot_x < 0) {
x = -plot_x;
}
if (y + plot_y < 0) {
y = -plot_y;
}
_ref8 = get_translation(tt), current_x = _ref8[0], current_y = _ref8[1];
if (current_x === x && current_y === y) {
return el;
}
tt.setAttribute('transform', "translate(" + x + " " + y + ")");
return el;
};
return untooltip = function(ms) {
return tooltip_timeout = setTimeout(function() {
tt.style.display = 'none';
tt.style.opacity = 0;
if (tooltip_el != null) {
tooltip_el.classList.remove('active');
}
return tooltip_timeout = null;
}, ms);
};
};
init_svg = function() {
var chart, charts, _i, _len, _results;
charts = $('.pygal-chart');
if (charts.length) {
_results = [];
for (_i = 0, _len = charts.length; _i < _len; _i++) {
chart = charts[_i];
_results.push(init(chart));
}
return _results;
}
};
if (document.readyState !== 'loading') {
init_svg();
} else {
document.addEventListener('DOMContentLoaded', function() {
return init_svg();
});
}
window.pygal = window.pygal || {};
window.pygal.init = init;
window.pygal.init_svg = init_svg;
}).call(this);

View File

@ -0,0 +1,63 @@
{% load i18n gadjo %}
{% block cell-content %}
<h2>{{ cell.title }}</h2>
<div>
{% if form.fields|length > 1 %}
<form method='get' enctype='multipart/form-data' id='chart-filters'>
{{ form|with_template }}
<div class='buttons'>
<button class='submit-button'>{% trans 'Refresh' %}</button>
</div>
</form>
{% else %}
<p>
{% blocktrans trimmed %}
No filters are available. Note that only filters that are shared between all chart cells will appear. Furthermore, in case they have a value, it must be the same accross all cells.
{% endblocktrans %}
</p>
{% endif %}
</div>
<script>
$(function () {
const form = $('#chart-filters');
var loaded_cell_count = 0;
if (!$('body').data('filters-cell-id')) {
$('body').data('filters-cell-id', Math.random().toString(36).slice(2, 7));
function load_filters_cell_last() {
if (++loaded_cell_count == $('div.chartngcell').length) {
combo_load_cell($('.chart-filters-cell'));
loaded_cell_count = 0;
}
}
document.querySelectorAll('div.dataviz-table').forEach(graph => {
graph.addEventListener('DOMSubtreeModified', load_filters_cell_last);
});
document.querySelectorAll('div.chartngcell embed').forEach(graph => {
graph.addEventListener('load', load_filters_cell_last)
});
}
form.submit(function(e) {
e.preventDefault();
$(window).trigger('combo:refresh-graphs');
chart_cell = $(this).parents('.cell');
ajax_cell_url = "{{ site_base }}{{ cell.get_ajax_url }}";
new_url = ajax_cell_url + '?filters_cell_id=' + $('body').data('filters-cell-id') + '&' + $(this).serialize();
chart_cell.data('ajax-cell-url', new_url);
});
form.change(function() {
if(loaded_cell_count == 0) {
form.submit();
}
});
});
</script>
{% endblock %}

View File

@ -0,0 +1,6 @@
{% load gadjo %}
{% block cell-form %}
{{form|with_template}}
{% endblock %}

View File

@ -0,0 +1,71 @@
{% load i18n %}
{% if cell.title %}<h2>{{cell.title}}</h2>{% endif %}
{% if cell.is_table_chart %}
<div id="chart-{{cell.id}}" class="dataviz-table total-{{ cell.display_total }}"></div>
<script>
$(function() {
var extra_context = $('#chart-{{cell.id}}').parents('.cell').data('extra-context');
$(window).on('combo:refresh-graphs', function() {
var url = "{% url 'combo-dataviz-graph' cell=cell.id %}" + get_graph_querystring(extra_context);
$('#chart-{{cell.id}}-download').attr('href', url + '&export-format=ods');
$.ajax({
url : url,
type: 'GET',
success: function(data) {
$('#chart-{{cell.id}}').html(data);
}
});
}).trigger('combo:refresh-graphs');
});
</script>
{% else %}
<div style="min-height: {{cell.height}}px">
<embed id="chart-{{cell.id}}" type="image/svg+xml" style="width: 100%"/>
</div>
<script>
$(function() {
var last_width = 1;
var extra_context = $('#chart-{{cell.id}}').parents('.cell').data('extra-context');
$(window).on('load resize gadjo:sidepage-toggled combo:resize-graphs', function() {
var chart_cell = $('#chart-{{cell.id}}').parent();
var new_width = Math.floor($(chart_cell).width());
var ratio = new_width / last_width;
if (ratio > 1.2 || ratio < 0.8) {
var querystring = get_graph_querystring(extra_context, new_width);
$('#chart-{{cell.id}}').attr('src', "{% url 'combo-dataviz-graph' cell=cell.id %}" + querystring);
$('#chart-{{cell.id}}-download').attr('href', "{% url 'combo-dataviz-graph-export' cell=cell.id %}" + querystring);
last_width = new_width;
}
}).trigger('combo:resize-graphs');
$(window).on('combo:refresh-graphs', function() {
var querystring = get_graph_querystring(extra_context, last_width);
$('#chart-{{cell.id}}').attr('src', "{% url 'combo-dataviz-graph' cell=cell.id %}" + querystring);
$('#chart-{{cell.id}}-download').attr('href', "{% url 'combo-dataviz-graph-export' cell=cell.id %}" + querystring);
});
});
</script>
{% endif %}
<a
class="button download-button"
id="chart-{{ cell.id }}-download"
title="{% trans "Download" %}"
href="{% url 'combo-dataviz-graph-export' cell=cell.id %}"
{% if cell.is_table_chart %}
download
{% else %}
rel="popup"
data-autoclose-dialog="true"
{% endif %}
>
<span class="sr-only">{% trans "Download" %}</span>
</a>
<script>
$(function() {
$('#chart-{{cell.id}}').parents('.cell').on('mouseenter', function() {
$('#chart-{{ cell.id }}-download').show();
}).on('mouseleave', function() {
$('#chart-{{ cell.id }}-download').hide();
}).trigger('mouseleave');
});
</script>

View File

@ -0,0 +1,17 @@
{% extends "combo/manager_base.html" %}
{% load i18n %}
{% block appbar %}
<h2>{% trans "Export data" %}</h2>
{% endblock %}
{% block content %}
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<div class="buttons">
<button class="submit-button">{% trans "Download" %}</button>
<a class="cancel" href="{% url 'combo-manager-page-view' pk=object.pk %}">{% trans 'Cancel' %}</a>
</div>
</form>
{% endblock %}

View File

@ -0,0 +1,18 @@
{% load gadjo %}
<div style="position: relative">
{{ form|with_template }}
{% if cell.statistic and not cell.is_table_chart %}
<div style="position: absolute; right: 0; top: 0; width: 300px; height: 150px">
<embed type="image/svg+xml" src="{% url 'combo-dataviz-graph' cell=cell.id %}?width=300&height=150"/>
</div>
{% endif %}
</div>
<script>
$(function () {
$('div#panel-dataviz_chartngcell-{{ cell.pk }}-general div.content').change(function() {
$('div#cell-dataviz_chartngcell-{{ cell.pk }} button.save').click();
});
});
</script>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1"
viewBox="0 0 {{ width }} 30" width="{{ width }}" height="30">
<text
y="20"
x="10"
style="font-family: sans-serif; font-size: 16px; fill:#000000;">{{ text }}</text>
</svg>

After

Width:  |  Height:  |  Size: 305 B

View File

@ -1,16 +1,16 @@
{% block cell-content %}
<div
data-combo-gauge="true"
{% if jsonp %}
data-gauge-count-jsonp-url="{{data_source_url}}"
{% else %}
data-gauge-count-url="{% url 'combo-ajax-gauge-count' cell=cell.id %}"
{% endif %}
data-gauge-max-value="{{max_value|default_if_none:100}}" class="bo-block">
<canvas style="width: 100%;">
</canvas>
{% if title %}
{% if url %}<a href="{{url}}">{% endif %}{{title}}{% if url %}</a>{% endif %}
{% endif %}
</div>
<div
data-combo-gauge="true"
{% if jsonp %}
data-gauge-count-jsonp-url="{{data_source_url}}"
{% else %}
data-gauge-count-url="{% url 'combo-ajax-gauge-count' cell=cell.id %}"
{% endif %}
data-gauge-max-value="{{max_value|default_if_none:100}}" class="bo-block">
<canvas style="width: 100%;">
</canvas>
{% if title %}
{% if url %}<a href="{{url}}">{% endif %}{{title}}{% if url %}</a>{% endif %}
{% endif %}
</div>
{% endblock %}

View File

@ -14,11 +14,21 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.conf.urls import url
from django.urls import path, re_path
from .views import ajax_gauge_count
from combo.urls_utils import manager_required
from .views import ajax_gauge_count, dataviz_choices, dataviz_graph, dataviz_graph_export
urlpatterns = [
url(r'^ajax/gauge-count/(?P<cell>[\w_-]+)/$',
ajax_gauge_count, name='combo-ajax-gauge-count'),
re_path(r'^ajax/gauge-count/(?P<cell>[\w_-]+)/$', ajax_gauge_count, name='combo-ajax-gauge-count'),
re_path(r'^api/dataviz/graph/(?P<cell>[\w_-]+)/$', dataviz_graph, name='combo-dataviz-graph'),
re_path(
r'^dataviz/graph/(?P<cell>[\w_-]+)/export/$', dataviz_graph_export, name='combo-dataviz-graph-export'
),
path(
'api/dataviz/graph/<int:cell_id>/<filter_id>/ajax-choices',
manager_required(dataviz_choices),
name='combo-dataviz-choices',
),
]

122
combo/apps/dataviz/utils.py Normal file
View File

@ -0,0 +1,122 @@
import datetime
import json
import logging
from django.conf import settings
from django.utils.timezone import now
from requests.exceptions import RequestException
from combo.utils import requests
from .models import Statistic
logger = logging.getLogger('combo.apps.dataviz')
def update_available_statistics():
if not settings.KNOWN_SERVICES:
return
results = []
temporary_unavailable_sites = []
for provider in settings.STATISTICS_PROVIDERS:
if isinstance(provider, dict):
url = provider['url']
sites = {provider['id']: {'title': provider['name']}}
provider = provider['id']
else:
sites = settings.KNOWN_SERVICES.get(provider, {})
url = '/visualization/json/' if provider == 'bijoe' else '/api/statistics/'
for site_key, site_dict in sites.items():
try:
response = requests.get(
url,
allow_redirects=False,
timeout=5,
remote_service=site_dict if provider in settings.KNOWN_SERVICES else {},
without_user=True,
headers={'accept': 'application/json'},
log_errors='warn',
)
response.raise_for_status()
except RequestException:
temporary_unavailable_sites.append((provider, site_key))
continue
try:
result = response.json()
except json.JSONDecodeError:
continue
if isinstance(result, dict):
result = result['data'] # detect new api
for stat in result:
results.append(
Statistic(
slug=stat.get('slug') or stat['id'],
site_slug=site_key,
service_slug=provider,
label=stat['name'],
url=stat.get('data-url') or stat['url'],
site_title=site_dict.get('title', ''),
filters=stat.get('filters', []),
has_future_data=stat.get('future_data', False),
data_type=stat.get('data_type', ''),
deprecated=stat.get('deprecated', False),
available=True,
)
)
update_fields = (
'label',
'url',
'site_title',
'filters',
'available',
'has_future_data',
'data_type',
'deprecated',
)
all_statistics = {stat.natural_key(): stat for stat in Statistic.objects.all()}
statistics_to_create = []
statistics_to_update = {}
for stat in results:
existing_stat = all_statistics.get(stat.natural_key())
if existing_stat:
for field in update_fields:
new_value = getattr(stat, field)
if getattr(existing_stat, field) != new_value:
setattr(existing_stat, field, new_value)
statistics_to_update[existing_stat.pk] = existing_stat
else:
statistics_to_create.append(stat)
Statistic.objects.bulk_create(statistics_to_create)
Statistic.objects.bulk_update(statistics_to_update.values(), update_fields)
available_stats = Statistic.objects.filter(available=True)
for stat in results:
available_stats = available_stats.exclude(
slug=stat.slug, site_slug=stat.site_slug, service_slug=stat.service_slug
)
# set last_update on all seen statistics
Statistic.objects.exclude(pk__in=available_stats).update(last_update=now())
for service_slug, site_slug in temporary_unavailable_sites:
available_stats = available_stats.exclude(site_slug=site_slug, service_slug=service_slug)
available_stats.update(available=False)
# log errors for outdated statistics
sites_with_outdated_statistics = set()
outdated_hours = 48
for available_stat in Statistic.objects.filter(available=True):
time_since_last_update = now() - available_stat.last_update
if time_since_last_update > datetime.timedelta(hours=outdated_hours):
sites_with_outdated_statistics.add(available_stat.site_title)
for title in sites_with_outdated_statistics:
logger.error(
f'statistics from "{title}" have not been available for more than %s hours.', outdated_hours
)

View File

@ -1,5 +1,5 @@
# combo - content management system
# Copyright (C) 2015 Entr'ouvert
# Copyright (C) 2015-2019 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
@ -14,13 +14,234 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.http import HttpResponse
import io
import unicodedata
from combo.utils import get_templated_url, requests
from .models import Gauge
import pyexcel_ods
from django.core import signing
from django.core.cache import cache
from django.core.exceptions import PermissionDenied
from django.http import FileResponse, Http404, HttpResponse, HttpResponseBadRequest, JsonResponse
from django.shortcuts import render, reverse
from django.template import TemplateSyntaxError, VariableDoesNotExist
from django.utils.translation import gettext_lazy as _
from django.views.decorators.clickjacking import xframe_options_sameorigin
from django.views.generic import DetailView, FormView
from django.views.generic.detail import SingleObjectMixin
from requests.exceptions import HTTPError
from combo.utils import NothingInCacheException, get_templated_url, requests
from .forms import ChartFiltersMixin, ChartNgExportForm, ChartNgPartialForm, Choice
from .models import ChartNgCell, Gauge, MissingVariable, UnsupportedDataSet
def ajax_gauge_count(request, *args, **kwargs):
gauge = Gauge.objects.get(id=kwargs['cell'])
response = requests.get(get_templated_url(gauge.data_source))
return HttpResponse(response.content, content_type='text/json')
class DatavizGraphView(DetailView):
model = ChartNgCell
pk_url_kwarg = 'cell'
def dispatch(self, request, *args, **kwargs):
self.cell = self.get_object()
self.filters_cell_id = request.GET.get('filters_cell_id')
if not self.cell.page.is_visible(request.user):
raise PermissionDenied()
if not self.cell.is_visible(request):
raise PermissionDenied()
if not self.cell.statistic or not self.cell.statistic.url:
raise Http404('misconfigured cell')
if self.filters_cell_id:
self.cell.subfilters = cache.get(
self.cell.get_cache_key(self.filters_cell_id), self.cell.subfilters
)
return super().dispatch(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
form = ChartNgPartialForm(request.GET, instance=self.cell)
if not form.is_valid():
return self.error(_('Wrong parameters.'))
if request.GET.get('ctx'):
try:
request.extra_context = signing.loads(request.GET['ctx'])
except signing.BadSignature:
return HttpResponseBadRequest('bad signature')
form.instance._request = request
try:
chart = form.instance.get_chart(
width=int(request.GET['width']) if request.GET.get('width') else None,
height=int(request.GET['height']) if request.GET.get('height') else None,
)
except UnsupportedDataSet:
return self.error(_('Unsupported dataset.'))
except MissingVariable:
return self.error(_('Page variable not found.'))
except TemplateSyntaxError:
return self.error(_('Syntax error in page variable.'))
except VariableDoesNotExist:
return self.error(_('Backoffice preview unavailable.'))
except HTTPError as e:
if e.response.status_code == 404:
return self.error(_('Visualization not found.'))
else:
return self.error(_('Unknown HTTP error: %s' % e))
if self.filters_cell_id and self.cell.statistic.service_slug != 'bijoe':
self.update_subfilters_cache(form.instance)
export_format = request.GET.get('export-format')
if export_format == 'svg':
return self.export_to_svg(chart)
elif export_format == 'ods':
return self.export_to_ods(chart)
if self.cell.is_table_chart():
if not chart.raw_series:
return self.error(_('No data'))
# force x_value_formatter on x_labels (avoid bug https://github.com/Kozea/pygal/issues/372)
chart.x_labels = [chart.config.x_value_formatter(x) for x in chart.x_labels]
rendered = chart.render_table(
transpose=bool(self.cell.chart_type == 'table-inverted'),
total=bool(self.cell.statistic.service_slug == 'bijoe' and chart.compute_sum),
)
rendered = rendered.replace('<table>', '<table class="main">')
return HttpResponse(rendered)
return HttpResponse(chart.render(), content_type='image/svg+xml')
def error(self, error_text):
if self.cell.is_table_chart():
return HttpResponse('<p>%s</p>' % error_text)
context = {
'width': self.request.GET.get('width', 200),
'text': error_text,
}
return render(self.request, 'combo/dataviz-error.svg', context=context, content_type='image/svg+xml')
def update_subfilters_cache(self, cell):
try:
data = cell.get_statistic_data(raise_if_not_cached=True)
except NothingInCacheException:
pass # should not happen
else:
cache.set(
cell.get_cache_key(self.filters_cell_id), data.json()['data'].get('subfilters', []), 300
)
def export_to_svg(self, chart):
response = HttpResponse(chart.render(), content_type='image/svg+xml')
response['Content-Disposition'] = 'attachment; filename="%s.svg"' % self.cell.get_download_filename()
return response
def export_to_ods(self, chart):
data = [[''] + chart.x_labels] if any(chart.x_labels) else []
for serie in chart.raw_series:
line = [serie[1]['title']] + serie[0]
line = [x or 0 for x in line]
data.append(line)
data = [list(line) for line in zip(*data)]
output = io.BytesIO()
pyexcel_ods.save_data(output, {self.cell.title or self.cell.statistic.label: data})
output.seek(0)
return FileResponse(
output,
as_attachment=True,
content_type='application/vnd.oasis.opendocument.spreadsheet',
filename='%s.ods' % self.cell.get_download_filename(),
)
dataviz_graph = xframe_options_sameorigin(DatavizGraphView.as_view())
class DatavizGraphExportView(SingleObjectMixin, FormView):
model = ChartNgCell
pk_url_kwarg = 'cell'
form_class = ChartNgExportForm
template_name = 'combo/chartngcell_export_form.html'
def dispatch(self, *args, **kwargs):
self.object = self.get_object()
return super().dispatch(*args, **kwargs)
def form_valid(self, form):
self.querystring = self.request.GET.copy()
self.querystring['export-format'] = form.cleaned_data['export_format']
return super().form_valid(form)
def get_success_url(self):
return '%s?%s' % (
reverse('combo-dataviz-graph', kwargs={'cell': self.object.pk}),
self.querystring.urlencode(),
)
dataviz_graph_export = DatavizGraphExportView.as_view()
class DatavizChoicesView(DetailView):
model = ChartNgCell
pk_url_kwarg = 'cell_id'
def dispatch(self, *args, **kwargs):
self.cell = self.get_object()
filter_id = self.kwargs.get('filter_id')
for filter_ in self.cell.available_filters:
if filter_['id'] == filter_id:
self.filter = filter_
break
else:
raise Http404()
return super().dispatch(*args, **kwargs)
def get(self, request, *args, **kwargs):
search_term = request.GET.get('term', '')
search_term = unicodedata.normalize('NFKC', search_term).casefold()
try:
page_number = int(request.GET.get('page', 1))
except ValueError:
page_number = 1
initial = self.cell.filter_params.get(self.filter['id'], self.filter.get('default'))
objects = ChartFiltersMixin.get_filter_options(self.cell, self.filter, initial)
objects = [x for x in objects if search_term in unicodedata.normalize('NFKC', str(x)).casefold()]
return JsonResponse(
{
'results': self.format_results(objects, (page_number - 1) * 10, page_number * 10),
'pagination': {'more': bool(len(objects) >= page_number * 10)},
}
)
def format_results(self, objects, start_index, end_index):
page_objects = objects[start_index:end_index]
if start_index > 0:
last_displayed_group = objects[start_index - 1].group
for option in page_objects:
if option.group == last_displayed_group:
option.group = None
return [
{'text': group, 'children': [{'id': k, 'text': v} for k, v in choices]}
for group, choices in Choice.get_field_choices(page_objects)
]
dataviz_choices = DatavizChoicesView.as_view()

View File

@ -0,0 +1,64 @@
# combo - content management system
# Copyright (C) 2014-2022 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.conf import settings
from django.forms.widgets import Select, SelectMultiple
from django.urls import reverse
class Select2WidgetMixin:
class Media:
js = 'xstatic/select2.min.js'
css = {'all': ('xstatic/select2.min.css',)}
def __init__(self, cell, filter_id, choices, initial, ajax_choices):
from .forms import Choice
attrs = {}
if self.enable_select2(choices):
attrs['data-combo-autocomplete'] = 'true'
attrs['lang'] = settings.LANGUAGE_CODE
if ajax_choices:
attrs['data-select2-url'] = reverse(
'combo-dataviz-choices', kwargs={'cell_id': cell.pk, 'filter_id': filter_id}
)
choices = self.filter_choices(choices, initial)
super().__init__(choices=Choice.get_field_choices(choices), attrs=attrs)
def enable_select2(self, choices):
return True
class Select2Widget(Select2WidgetMixin, Select):
min_choices = 20
@staticmethod
def filter_choices(choices, initial):
return [x for x in choices if x.id == initial]
def enable_select2(self, choices):
return bool(len(choices) > self.min_choices)
class MultipleSelect2Widget(Select2WidgetMixin, SelectMultiple):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.attrs['multiple'] = 'multiple'
@staticmethod
def filter_choices(choices, initial):
return [x for x in choices if x.id in (initial or [])]

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