Compare commits

..

236 Commits

Author SHA1 Message Date
Joel Falknau
055077fa77 Version Bump 4.6.4 2025-03-03 11:49:43 +10:00
Joel Falknau
f342ccbc6a Revert "[REMOVE] Unused imports"
This reverts commit f497c18e5b.
2025-03-03 11:47:19 +10:00
Joel Falknau
37ffd0a1ac Version Bump 4.6.3 2025-03-03 11:29:26 +10:00
Ariel Rin
a1f705381e Merge branch 'translations_7f31a07ccd4e4a66b1dd7b6bc2dbddb5' into 'master'
Updates for project Alliance Auth

See merge request allianceauth/allianceauth!1687
2025-03-03 01:28:36 +00:00
Ariel Rin
c0970ad4fa Updates for project Alliance Auth 2025-03-03 01:28:36 +00:00
Ariel Rin
3818d0c6d1 Merge branch 'crontab' into 'master'
Crontab

See merge request allianceauth/allianceauth!1695
2025-03-03 01:19:32 +00:00
Ariel Rin
95411c79cb Crontab 2025-03-03 01:19:32 +00:00
Ariel Rin
eeccbbacfc Merge branch 'limit-slixmpp' into 'master'
[CHANGE] Limit `slixmpp` version

See merge request allianceauth/allianceauth!1694
2025-03-03 01:19:09 +00:00
Peter Pfeufer
f6c4180502 [CHANGE] Limit slixmpp version
An update to `slixmpp` v1.9.0 pulls in `pyasn1` v0.6.1, which causes a dependency issue with `python-jose`

> ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
> python-jose 3.4.0 requires pyasn1<0.5.0,>=0.4.1, but you have pyasn1 0.6.1 which is incompatible.
2025-02-28 11:09:22 +01:00
Ariel Rin
f4d3d6c0b1 Merge branch 'sorting-data' into 'master'
[ADD] Sorting data to the tables datetime fields

See merge request allianceauth/allianceauth!1691
2025-02-27 03:37:30 +00:00
Ariel Rin
e9d2d11297 Merge branch 'correct-locale-link' into 'master'
[FIX] Correct locale link for moment js

See merge request allianceauth/allianceauth!1692
2025-02-27 03:37:27 +00:00
Peter Pfeufer
96204b29e8 [REMOVE] Redundant locale information
Since momentJS is already loaded with `locale=True`, this is not needed anymore.
2025-02-26 22:57:19 +01:00
Peter Pfeufer
47842c1243 [FIX] Correct locale link for moment js
The other one was the directory for the node modules …
2025-02-26 21:53:27 +01:00
Peter Pfeufer
9b494106bc [ADD] Sorting data to the table field
Since the localized date and time values can have different formats, sorting with them wasn't gonna work reliably, so we add the original datetime string as sorting data.
2025-02-26 21:33:21 +01:00
Ariel Rin
d51e730a7f Merge branch 'mumble-stats-date-fixes' into 'master'
[CHANGE] Datetime Improvements for Mumble Connection History

See merge request allianceauth/allianceauth!1689
2025-02-26 10:21:39 +00:00
Ariel Rin
363909c0c4 Merge branch 'remove-stray-a-tag' into 'master'
[REMOVE] Stray `a` tag from `header-nav-brand`

See merge request allianceauth/allianceauth!1688
2025-02-26 10:19:38 +00:00
Ariel Rin
82273f68fe Merge branch 'expand-checks' into 'master'
Expand checks

See merge request allianceauth/allianceauth!1685
2025-02-26 10:19:10 +00:00
Ariel Rin
12fa38b446 Merge branch 'lcode-links' into 'master'
[ADD] Links to available languages for mapping

See merge request allianceauth/allianceauth!1690
2025-02-26 10:18:31 +00:00
Peter Pfeufer
c26af593ff [ADD] Links to available languaes for mapping
Makes it easier for adding new languages in the future
2025-02-26 11:11:18 +01:00
Peter Pfeufer
8e9a53c494 [CHANGE] Localze datetime output 2025-02-25 18:17:51 +01:00
Peter Pfeufer
5559ce5fbb [CHANGE] Return empty field when no datetime available 2025-02-25 18:01:58 +01:00
Peter Pfeufer
faa529a55b [REMOVE] Stray a tag from header-nav-brand 2025-02-25 17:33:02 +01:00
Peter Pfeufer
4ccfe20c14 [CHANGE] Set email to dummy@example.net 2025-02-25 11:52:25 +01:00
Joel Falknau
960c9625fe Version Bump 4.6.2a 2025-02-25 19:29:36 +10:00
Ariel Rin
7b92d103d6 Merge branch 'crontab' into 'master'
Crontab fixes

See merge request allianceauth/allianceauth!1686
2025-02-25 09:16:59 +00:00
Ariel Rin
c1e2449084 Crontab fixes 2025-02-25 09:16:59 +00:00
Peter Pfeufer
3acb651650 [CHANGE] Update test 2025-02-19 10:18:35 +01:00
Peter Pfeufer
2de57b334b [ADD] ESI_USER_CONTACT_EMAIL to test settings 2025-02-19 10:09:00 +01:00
Peter Pfeufer
0498f5bb1b [MISC] Format for better readability 2025-02-19 10:00:18 +01:00
Peter Pfeufer
929485a8f9 [ADD] Check for empty SITE_URL 2025-02-19 09:39:22 +01:00
Peter Pfeufer
28cb62f373 [ADD] Check for ESI_USER_CONTACT_EMAIL 2025-02-19 09:31:49 +01:00
Ariel Rin
02214b74d0 Merge branch 'cleanup-unused-imports' into 'master'
[REMOVE] Unused imports

See merge request allianceauth/allianceauth!1684
2025-02-16 23:25:58 +00:00
Peter Pfeufer
f497c18e5b [REMOVE] Unused imports 2025-02-16 23:20:36 +01:00
Ariel Rin
cb57d922e6 Merge branch 'pre-commit-updates' into 'master'
Pre commit updates

See merge request allianceauth/allianceauth!1682
2025-02-11 05:15:48 +00:00
Ariel Rin
805d138b09 Merge branch 'fix-timerboard-initial-sort-order' into 'master'
[FIX] Timerboard initial sort order

See merge request allianceauth/allianceauth!1681
2025-02-11 05:14:48 +00:00
Ariel Rin
09a583fb1d Merge branch 'authorized-apps-url' into 'master'
[CHANGE] Update URL to apps management

See merge request allianceauth/allianceauth!1683
2025-02-09 08:18:23 +00:00
Peter Pfeufer
146c4c8d94 [CHANGE] Update URL to apps management 2025-02-09 08:55:51 +01:00
Peter Pfeufer
c2ae680f72 [CHANGE] Apply new pre-commit config 2025-02-01 16:22:36 +01:00
Peter Pfeufer
b5ad1c8a1a [CHANGE] Use global exclude instead of per hook 2025-02-01 16:18:08 +01:00
Peter Pfeufer
8be2760fc4 [CHANGNE] Update repos and dependencies 2025-02-01 16:14:36 +01:00
Peter Pfeufer
f047943eb7 [CHANGE] Define language versions 2025-02-01 16:11:44 +01:00
Peter Pfeufer
43906f41b3 [FIX] Timerboard initial sort order 2025-02-01 16:00:03 +01:00
Ariel Rin
a18ec98877 Merge branch 'django-checks' into 'master'
Improve Django checks

See merge request allianceauth/allianceauth!1679
2025-01-28 09:16:14 +00:00
Ariel Rin
14163d2c0c Merge branch 'fix-menu-chevron' into 'master'
[FIX] Active menu chevron

See merge request allianceauth/allianceauth!1680
2025-01-28 09:13:20 +00:00
Peter Pfeufer
81d9c41cf6 [FIX] Active menu chevron 2025-01-26 19:24:24 +01:00
Peter Pfeufer
58f5a5b41d [CHANGE] elif to safe potential if checks and time 2025-01-22 04:23:19 +01:00
Peter Pfeufer
6363bb706a [CHANGE] Put some of the hint links in variables 2025-01-22 04:12:33 +01:00
Peter Pfeufer
baf3be4cb2 [CHANGE] Simplify if blocks for MariaDB and MySQL checks 2025-01-22 04:05:02 +01:00
Joel Falknau
e69444fe79 Version Bump 4.6.1 2025-01-20 19:03:17 +10:00
Ariel Rin
7483fcb876 Merge branch 'offsetfix' into 'master'
Cron Offset Fix

See merge request allianceauth/allianceauth!1678
2025-01-20 08:58:24 +00:00
Joel Falknau
a57d55504d Cosmetic fix to * cron 2025-01-20 18:45:37 +10:00
Joel Falknau
affb30e9f4 add migration 2025-01-20 18:37:22 +10:00
Joel Falknau
588cb1b7ca Version Bump 4.6.0 2025-01-20 15:37:29 +10:00
Ariel Rin
a609faa91b Merge branch 'language-code-mapping' into 'master'
[ADD] Locale mapping for DataTables and Moment.JS

See merge request allianceauth/allianceauth!1677
2025-01-20 04:14:27 +00:00
Peter Pfeufer
856e939c21 [ADD] Locale mapping for DataTables and Moment.JS 2025-01-20 04:35:10 +01:00
Ariel Rin
1b6cf98885 Merge branch '1416-timerboard-datatables' into 'master'
[CHANGE] Timertables to Datatables

Closes #1416

See merge request allianceauth/allianceauth!1676
2025-01-18 03:50:34 +00:00
Peter Pfeufer
92c2af9975 [CHANGE] Timertables to Datatables 2025-01-12 17:12:23 +01:00
Ariel Rin
5ef70bb031 Merge branch 'cronbuilder' into 'master'
Cron Offset Tasks

See merge request allianceauth/allianceauth!1672
2025-01-10 12:10:50 +00:00
Ariel Rin
60998bffc2 Cron Offset Tasks 2025-01-10 12:10:49 +00:00
Ariel Rin
a5971314f5 Merge branch 'structure-timer-updates' into 'master'
Timerboard Updates

See merge request allianceauth/allianceauth!1674
2025-01-10 11:22:24 +00:00
Peter Pfeufer
a03c766840 [FIX] Spelling mistakes
Thx @soratidus999
2025-01-10 11:41:31 +01:00
Ariel Rin
ad47ff2c54 Merge branch 'dockerbaremetalanalytics' into 'master'
Docker/Baremetal Analytics

See merge request allianceauth/allianceauth!1671
2025-01-10 08:29:02 +00:00
Ariel Rin
3efdb8f12b Docker/Baremetal Analytics 2025-01-10 08:29:02 +00:00
Ariel Rin
823fc82d19 Merge branch 'sri-hashes' into 'master'
[ADD] `django-sri` to provide integrity hashes for local static files

See merge request allianceauth/allianceauth!1673
2025-01-10 08:27:28 +00:00
Ariel Rin
a93e510895 Merge branch 'fix-moment.js-language-code' into 'master'
[FIX] Moment.JS Localisation

See merge request allianceauth/allianceauth!1675
2025-01-10 08:21:20 +00:00
Peter Pfeufer
d99f5858d8 [FIX] Moment.JS Localisation
Load the right JS when our language code has 2 parts
- fr_FR
- it_IT
- pl_PL
And so on
2025-01-08 23:04:29 +01:00
Peter Pfeufer
4578ecf21d [ADD] Missing migration 2025-01-06 18:17:21 +01:00
Peter Pfeufer
b737504d52 [REMOVE] Unused imports 2025-01-06 18:08:52 +01:00
Peter Pfeufer
c6b6443901 [CHANGE] Move structure label BG detection to Python instead of Django
Python is faster and needs less memory …
It's also much mire readable that way.
2025-01-06 18:08:20 +01:00
Peter Pfeufer
f51523dc07 [CHANGE] Use TextChoices displayed name in template 2025-01-06 17:03:00 +01:00
Peter Pfeufer
bd4dd60c98 [CHANGE] Rename I-Hub to Sovereignty Hub 2025-01-06 16:48:02 +01:00
Peter Pfeufer
a4ea48e14e [CHANGE] Group timer types by BG color BS class 2025-01-06 16:43:59 +01:00
Peter Pfeufer
646d3f5408 [ADD] Mercenary Dens to structure type selection 2025-01-06 16:24:30 +01:00
Peter Pfeufer
0f057ffa84 [ADD] django-sri to provide integrity hashes for local static files 2025-01-02 16:54:37 +01:00
Joel Falknau
7033406ba6 Version Bump 4.5.0 2024-12-10 13:07:03 +10:00
Ariel Rin
6b395ca1d4 Merge branch 'executableflag' into 'master'
Executableflag

See merge request allianceauth/allianceauth!1667
2024-12-09 23:57:02 +00:00
Ariel Rin
795a7e006f Merge branch 'randomdelay' into 'master'
Spread esi tasks over 10 minutes

See merge request allianceauth/allianceauth!1666
2024-12-09 23:56:13 +00:00
Ariel Rin
2a894cd62c Merge branch 'dockermariadbcnf' into 'master'
DockerMariaDB Config Template

See merge request allianceauth/allianceauth!1668
2024-12-09 23:54:33 +00:00
Ariel Rin
9ada26e849 DockerMariaDB Config Template 2024-12-09 23:54:33 +00:00
Ariel Rin
7120b3956c Merge branch 'group_display' into 'master'
fix group display for Groups that are Group Leaders

See merge request allianceauth/allianceauth!1670
2024-12-09 23:54:18 +00:00
root
4da67cfaf6 fix group display for Groups that are Group Leaders 2024-12-08 13:01:59 -06:00
Joel Falknau
0a940810bd dont need this now the flag is set correctly, more consistent 2024-12-05 11:49:54 +10:00
Joel Falknau
a868438492 force these flags on setup 2024-12-05 11:49:32 +10:00
Joel Falknau
dc1ed8c570 +x 2024-12-05 11:48:45 +10:00
Joel Falknau
8489f204dd fix test patch 2024-12-04 22:10:06 +10:00
Ariel Rin
1478588016 Merge branch 'add-js-type' into 'master'
[ADD] `js_type` parameter to allow JS modules to be loaded

See merge request allianceauth/allianceauth!1664
2024-12-04 11:24:21 +00:00
Ariel Rin
a16eb4b7f7 Merge branch 'image-overflow-fix' into 'master'
[FIX] Prevent images from overflowing their parent element

See merge request allianceauth/allianceauth!1665
2024-12-04 11:23:38 +00:00
Joel Falknau
292fb7b29d Add docs for smoothing out task execution 2024-12-04 18:35:07 +10:00
Joel Falknau
c6890dd2c6 Spread esi tasks over 10 minutes 2024-12-04 18:01:01 +10:00
Ariel Rin
702564d15e correct top level indentation to be able to be directly copy-pasteable 2024-12-03 09:50:55 +00:00
Peter Pfeufer
cef2e86ea1 [FIX] Prevent images from overflowing their parent element 2024-11-26 22:28:35 +01:00
Peter Pfeufer
50681b023b [CHANGE] Move the if inside the script tag
Makes the code better readable and maintainable.
2024-11-23 01:55:04 +01:00
Peter Pfeufer
2822775fb8 [ADD] js_type parameter to allow JS modules to be loaded 2024-11-22 15:01:32 +01:00
Ariel Rin
ef7c8be7b5 Merge branch 'fix/default-collation' into 'master'
Fix Different collations being used by docker and baremetal installations

See merge request allianceauth/allianceauth!1662
2024-11-17 01:57:59 +00:00
Ariel Rin
d639617eba Merge branch 'master' into 'master'
Celery documentation improvements

See merge request allianceauth/allianceauth!1663
2024-11-17 01:57:05 +00:00
r0kym
2125192f72 Fix typo in the long-running tasks example 2024-11-15 15:13:25 +01:00
r0kym
8d63801b00 Change logging imports and commands
Now uses `get_extension_logger` as is recommanded by https://allianceauth.readthedocs.io/en/v4.4.2/development/custom/logging.html
2024-11-15 15:12:03 +01:00
Matteo Ghia
e053fb7d96 also in baremetal docs 2024-11-09 12:45:59 +01:00
Matteo Ghia
ae7ed5c297 add default keyword required by mariadb 2024-11-09 12:45:31 +01:00
Matteo Ghia
d624ba4427 set default collation in startup script 2024-11-09 12:01:57 +01:00
Joel Falknau
164cd4fbb2 Merge branch 'master' of gitlab.com:allianceauth/allianceauth 2024-11-08 12:48:21 +10:00
Joel Falknau
94b52c850e Version Bump 4.4.2 2024-11-08 12:47:55 +10:00
Ariel Rin
4d19ceb388 Merge branch 'translations_7f31a07ccd4e4a66b1dd7b6bc2dbddb5' into 'master'
Updates for project Alliance Auth

See merge request allianceauth/allianceauth!1660
2024-11-08 02:43:11 +00:00
Ariel Rin
9a64728311 Updates for project Alliance Auth 2024-11-08 02:43:11 +00:00
Ariel Rin
dcc0c78daf Merge branch 'fix-language-selector' into 'master'
[FIX] Language selector

See merge request allianceauth/allianceauth!1661
2024-11-08 02:17:30 +00:00
Peter Pfeufer
5507c30af4 [CHANGE] Use language local name 2024-11-07 10:49:31 +01:00
Peter Pfeufer
66b97835d4 [FIX] Language selector 2024-10-22 21:20:00 +02:00
Ariel Rin
fd66a7cb20 Merge branch 'fix-mumble-chart-font-color' into 'master'
[FIX] Make chart labels better readable for darker themes

See merge request allianceauth/allianceauth!1659
2024-10-22 02:14:36 +00:00
Peter Pfeufer
c0f0f8db73 [FIX] Make chart labels better readable for darker themes 2024-10-21 05:35:22 +02:00
Joel Falknau
4210b2eabc Version Bump 4.4.0 2024-10-21 13:00:43 +10:00
Ariel Rin
225e68647e Merge branch 'add-missing-mumble-migration' into 'master'
[ADD] Missing Mumble migration

See merge request allianceauth/allianceauth!1655
2024-10-18 02:37:50 +00:00
Ariel Rin
7e2f864ebf Merge branch 'optimer-fix' into 'master'
Fix maximum character limit for duration in the optimer form

See merge request allianceauth/allianceauth!1656
2024-10-18 02:37:19 +00:00
Ariel Rin
f2384ba45b Merge branch 'master' into 'master'
Use https where applicable

See merge request allianceauth/allianceauth!1657
2024-10-18 02:29:33 +00:00
salartarium
c5918b9b3c Use https where applicable 2024-10-18 02:29:33 +00:00
Aaron Kable
ffedc4103d fix max chars on duration 2024-10-14 19:22:46 +08:00
Peter Pfeufer
0467b23a1a [ADD] Missing Mumble migration
Running migrations:
  No migrations to apply.
  Your models in app(s): 'mumble' have changes that are not yet reflected in a migration, and so won't be applied.
  Run 'manage.py makemigrations' to make new migrations, and then re-run 'manage.py migrate' to apply them.
2024-10-07 15:42:51 +02:00
Ariel Rin
cda5ce739f Merge branch 'mumbleconnectionhistory' into 'master'
Mumble Connection History Page

See merge request allianceauth/allianceauth!1648
2024-10-04 12:12:17 +00:00
Ariel Rin
e5c8426ea3 Merge branch 'fixing-bs3-side-menu' into 'master'
[REMOVE] Duplicate menu item

See merge request allianceauth/allianceauth!1654
2024-10-04 12:07:22 +00:00
Ariel Rin
b2bd489ddc Merge branch 'translations_7f31a07ccd4e4a66b1dd7b6bc2dbddb5' into 'master'
Updates for project Alliance Auth

See merge request allianceauth/allianceauth!1653
2024-10-04 12:06:53 +00:00
Ariel Rin
6397cf358a Updates for project Alliance Auth 2024-10-04 12:06:53 +00:00
Ariel Rin
90fcc4a811 Merge branch 'master' into 'master'
Add an example to the notification documentation

See merge request allianceauth/allianceauth!1652
2024-10-04 12:05:17 +00:00
Ariel Rin
ec7472fe22 Merge branch 'bootstrap-dark-fixes' into 'master'
[FIX] Re-add the dark bootstrap html tag

See merge request allianceauth/allianceauth!1651
2024-10-04 12:03:25 +00:00
Peter Pfeufer
5148b1914d [REMOVE] Duplicate menu item 2024-10-04 13:56:57 +02:00
Ariel Rin
2e0716f5ae Merge branch 'mumbleconnectionhistory-improvements' into 'mumbleconnectionhistory'
[CHANGE] Some improvements

See merge request soratidus999/allianceauth!12
2024-09-23 23:33:01 +00:00
Peter Pfeufer
1fb091acb2 [CHANGE] Some improvements
- Fixed Bootstraps cards, rows and cols
- Replaced style argument with Bootstrap class
- Removed unused Django templatetag
- JS modernized and moved to its own script tag instead of concatenating it with other more or less "global" scripts
- Fixed Bootstrap classes
2024-09-23 15:09:35 +02:00
r0kym
07c62ed32a Add an example to the notification documentation 2024-09-23 13:15:19 +02:00
Aaron Kable
660fe79d08 re-add the dark bootstrap html tag 2024-09-17 21:24:01 +08:00
Joel Falknau
42ee06470c Version Bump 4.3.1 2024-09-13 20:22:31 +10:00
Joel Falknau
69aaa9652f compilemessages 2024-09-13 20:09:46 +10:00
Ariel Rin
1ccfff50e5 Merge branch 'translations_7f31a07ccd4e4a66b1dd7b6bc2dbddb5' into 'master'
Updates for project Alliance Auth

See merge request allianceauth/allianceauth!1649
2024-09-13 10:09:10 +00:00
Ariel Rin
57a39557fd Updates for project Alliance Auth 2024-09-13 10:09:09 +00:00
Joel Falknau
c36dea08e3 compilemessages 2024-09-13 20:01:33 +10:00
Joel Falknau
d3acd821b7 remove excess translations 2024-09-13 20:01:20 +10:00
Joel Falknau
0a17427169 update language codes 2024-09-13 19:59:40 +10:00
Ariel Rin
ce8935e621 Merge branch 'translations_7f31a07ccd4e4a66b1dd7b6bc2dbddb5' into 'master'
Updates for project Alliance Auth

See merge request allianceauth/allianceauth!1647
2024-09-13 08:55:07 +00:00
Ariel Rin
efff946a56 Updates for project Alliance Auth 2024-09-13 08:55:07 +00:00
Joel Falknau
1dea92ed76 add Connection History 2024-09-12 15:54:14 +10:00
Joel Falknau
3f54d49d8b update help text from mumble definition 2024-09-12 15:54:06 +10:00
Joel Falknau
77da6928b2 add Chart.js 4.4.1 to Bundles 2024-09-12 15:53:53 +10:00
Joel Falknau
bd17b95cac Version Bump 4.3.0 2024-09-09 14:05:58 +10:00
Joel Falknau
4ed1c5b7c4 bring test celery in line with project 2024-09-09 13:59:43 +10:00
Ariel Rin
271fd8e8c4 Merge branch 'docs-user-creation-fix' into 'master'
[Docs] Add shell option to adduser command for Ubuntu install docs

See merge request allianceauth/allianceauth!1622
2024-09-09 03:58:47 +00:00
colcrunch
9b4321281a [Docs] Add shell option to adduser command for Ubuntu install docs 2024-09-09 03:58:47 +00:00
Ariel Rin
052c35c8e5 Merge branch 'metenox' into 'master'
Timerboard Improvements

Closes #1384

See merge request allianceauth/allianceauth!1645
2024-09-09 03:57:21 +00:00
Ariel Rin
0fcb517b0b Timerboard Improvements 2024-09-09 03:57:21 +00:00
Ariel Rin
dcfddf0add Merge branch 'docs2' into 'master'
Documentation

See merge request allianceauth/allianceauth!1646
2024-09-09 03:43:30 +00:00
Joel Falknau
4a4258d0e6 use docker command inside docker 2024-09-09 13:32:18 +10:00
Joel Falknau
dd15a221aa split repo from install 2024-09-09 13:32:06 +10:00
Joel Falknau
737e02293a expand mumble service install docs 2024-09-09 12:59:49 +10:00
Joel Falknau
c34efebacf define charsets for db backed apps 2024-09-03 12:27:50 +10:00
Joel Falknau
4602097399 remove DB step from mumble install, sqlite has been reccommended for a while 2024-09-03 12:27:02 +10:00
Joel Falknau
7051e06564 update features from readme 2024-09-03 12:26:43 +10:00
Joel Falknau
9767ce79d8 cleanup some local.py references 2024-09-03 12:26:31 +10:00
Joel Falknau
0c090f1486 Ubuntu 2004 should not be reccommended for new isntalls 2024-09-03 12:25:01 +10:00
Joel Falknau
618ee81f9b remove unneeded analytics docs for removed features 2024-09-03 12:24:37 +10:00
Joel Falknau
98efb9f887 split maintenance tasks into bare metal / docker 2024-09-03 12:24:17 +10:00
Ariel Rin
cbe6c821cc Merge branch 'custom-css' into 'master'
[ADD] Custom CSS Module

See merge request allianceauth/allianceauth!1643
2024-08-21 05:01:38 +00:00
Ariel Rin
de9d2b39a6 Merge branch 'theme-html-tags' into 'master'
[ADD] Theme html tags

See merge request allianceauth/allianceauth!1642
2024-08-21 04:59:18 +00:00
Peter Pfeufer
0d5f22288b Merge branch 'switch-to-django-solo' into custom-css 2024-08-20 14:41:52 +02:00
Peter Pfeufer
e0d76dc268 [CHANGE] Switch to Django Solo 2024-08-20 14:41:43 +02:00
Peter Pfeufer
ecc9e68330 [CHANGE] Consolidate migrations 2024-08-14 13:25:49 +02:00
Peter Pfeufer
710149ec21 [FIX] Check if the CustomCSS object exists 2024-08-14 13:22:23 +02:00
Peter Pfeufer
3c2c137dad [CHANGE] improve try block in template tag 2024-08-14 13:05:32 +02:00
Peter Pfeufer
a8271c4189 [CHANGE] Remove custom CSS file when it will be empty 2024-08-14 12:58:01 +02:00
Peter Pfeufer
3315ae7778 [ADD] Module to base settings file 2024-08-14 12:47:38 +02:00
Peter Pfeufer
d2f048f8fe [ADD Example template for admin overrides
For when we might want to add syntax highlight ti it, which is a completely different can of worms though.
2024-08-14 12:45:57 +02:00
Peter Pfeufer
0fe2855faa [ADD] Custom CSS to base file
Check if the CSS file exists and add it to the HTML output
2024-08-14 12:44:51 +02:00
Peter Pfeufer
79a1fa3d7c [ADD] CSS compression on save 2024-08-14 12:42:52 +02:00
Peter Pfeufer
96fe88d5c7 [REMOVE] highlight.js and leave it as an example in the widget code 2024-08-14 11:53:02 +02:00
Peter Pfeufer
59391ad3c5 [ADD] Custom CSS module (First steps) 2024-08-11 22:34:16 +02:00
Peter Pfeufer
94e9c08422 [ADD] Theme html tags 2024-08-08 10:22:14 +02:00
Joel Falknau
acff3695bc Version Bump 4.2.2 2024-08-06 12:43:06 +10:00
Ariel Rin
43ec8514aa Merge branch 'improve-redis-installation-on-ubuntu' into 'master'
[MISC] Improve Redis installation instructions for Ubuntu

See merge request allianceauth/allianceauth!1641
2024-08-05 01:26:07 +00:00
Peter Pfeufer
4c629b193f [CHANGE] Redis hint link 2024-08-05 03:21:14 +02:00
Peter Pfeufer
c651da4011 [MISC] Improve Redis installation instructions for Ubuntu 2024-08-05 03:12:24 +02:00
Ariel Rin
da382cffd1 Merge branch 'fix-mumble-url' into 'master'
[FIX] Mumble URL in service card

See merge request allianceauth/allianceauth!1637
2024-08-05 01:04:24 +00:00
Ariel Rin
4ecfc3afd8 Merge branch 'docs-docker-headlines' into 'master'
[CHANGE] Clarify that these instructions are for Docker

See merge request allianceauth/allianceauth!1638
2024-08-05 01:02:57 +00:00
Ariel Rin
4eb7dbbe62 Merge branch 'add-margin' into 'master'
[ADD] A bit margin to the notifications

See merge request allianceauth/allianceauth!1639
2024-08-05 01:02:52 +00:00
Ariel Rin
c96ba65296 Merge branch 'avoid-KeyError-in-checks' into 'master'
[FIX] Avoid `KeyError` in `celery_settings` checks

See merge request allianceauth/allianceauth!1640
2024-08-05 01:02:40 +00:00
Peter Pfeufer
ff2f60f7f3 [FIX] Avoid KeyError in celery_settings checks 2024-08-04 18:29:43 +02:00
Peter Pfeufer
3000545c98 [ADD] A bit margin to the notifications 2024-07-26 23:09:15 +02:00
Peter Pfeufer
f3ad092ef2 [CHANGNE] Clarify that these instructions are for Docker
This should also fix the menu item title, hopefully …
2024-07-19 19:13:48 +02:00
Peter Pfeufer
a012e7df2f [FIX] Mumble URL in service card 2024-07-19 17:33:32 +02:00
Ariel Rin
1fa77412c0 Merge branch 'fix/missing-celery-setting' into 'master'
fix missing setting in celery

See merge request allianceauth/allianceauth!1636
2024-07-15 13:39:28 +00:00
Matteo Ghia
e56caeb22b fix missing setting in celery 2024-07-15 14:53:45 +02:00
Joel Falknau
ceb07ebc67 Version Bump 4.2.1 2024-07-15 22:20:32 +10:00
Ariel Rin
237075d45c Merge branch 'checklib' into 'master'
More Detail on the SQL charset check

See merge request allianceauth/allianceauth!1635
2024-07-15 12:18:47 +00:00
Ariel Rin
7099b1946d More Detail on the SQL charset check 2024-07-15 12:18:47 +00:00
Joel Falknau
e416ab8ff2 Version Bump 4.2.0 2024-07-15 21:25:00 +10:00
Joel Falknau
2802ed03a5 Add Celery to classifiers 2024-07-15 21:22:58 +10:00
Ariel Rin
4af73c76fe Merge branch 'analytics' into 'master'
Analytics

See merge request allianceauth/allianceauth!1632
2024-07-15 11:00:27 +00:00
Ariel Rin
b6149979aa Analytics 2024-07-15 11:00:26 +00:00
Ariel Rin
cb20288427 Merge branch 'checklib' into 'master'
More Checks for System Packages and Configs

See merge request allianceauth/allianceauth!1633
2024-07-15 11:00:06 +00:00
Ariel Rin
db6f4c91dc More Checks for System Packages and Configs 2024-07-15 11:00:06 +00:00
Ariel Rin
57ac7a5277 Merge branch 'center-error-messages' into 'master'
[CHANGE] Center HTTPError messages

See merge request allianceauth/allianceauth!1634
2024-07-15 03:32:34 +00:00
Peter Pfeufer
136438f9c2 [CHANGE] Center HTTPError messages 2024-07-13 00:31:44 +02:00
Ariel Rin
e2be8b3440 Merge branch 'composelint' into 'master'
Optimize Dockerfile

See merge request allianceauth/allianceauth!1630
2024-06-17 08:42:39 +00:00
Ariel Rin
04f3473ef3 Optimize Dockerfile 2024-06-17 08:42:39 +00:00
Ariel Rin
255cb0da8d Merge branch 'bootstrap-class-fixes' into 'master'
[CHANGE] Remove unnecessary bootstrap classes from the dashboard

See merge request allianceauth/allianceauth!1631
2024-06-17 08:42:34 +00:00
Peter Pfeufer
069352fb0f [FIX] Switch to bottom margin instead of top/bottom padding
This is the much more commonly used approach.
2024-06-01 12:01:44 +02:00
Peter Pfeufer
66e8ddb684 [CHANGE] Remove unnecessary bootstrap classes from the dashboard 2024-05-31 21:59:24 +02:00
Ariel Rin
179c26975c Version Bump 4.1.0 2024-05-27 14:31:41 +10:00
Ariel Rin
e17f6e799b correct capitalization 2024-05-27 14:31:21 +10:00
Ariel Rin
7cd8294104 Merge branch 'widget-title-to-framework' into 'master'
[ADD] Widget title partial to AA framework

See merge request allianceauth/allianceauth!1629
2024-05-27 04:25:56 +00:00
Peter Pfeufer
ede5540335 [ADD] Widget title partial to AA framework 2024-05-26 16:52:15 +02:00
Ariel Rin
747279b773 Merge branch 'yet-another-padding-fix' into 'master'
Some more widget fixes

See merge request allianceauth/allianceauth!1628
2024-05-26 09:08:00 +00:00
Peter Pfeufer
44f8b1c477 [FIX] Don't try to underline these links
Fixing this while I'm at it …
2024-05-26 10:06:07 +02:00
Peter Pfeufer
7c6ebd9bf6 [FIX] Yet another padding in the dashboard widgets 2024-05-26 09:52:03 +02:00
Ariel Rin
430469b708 Merge branch 'widget-padding-fix' into 'master'
[FIX] Dashboard widget padding

See merge request allianceauth/allianceauth!1627
2024-05-26 06:51:20 +00:00
Peter Pfeufer
efbb3cee31 [FIX] Dashboard widget padding 2024-05-26 08:23:57 +02:00
Ariel Rin
21094ed4dd lint 2024-05-26 13:30:21 +10:00
Ariel Rin
5f326efc7e Merge branch 'v4docs' into 'master'
Expand Tuning Doccs

See merge request allianceauth/allianceauth!1624
2024-05-26 03:22:08 +00:00
Ariel Rin
b6e34ace35 Expand Tuning Doccs 2024-05-26 03:22:08 +00:00
Ariel Rin
fe4a8965e3 Merge branch 'master' into 'master'
Improve Error Troubleshooting with Log Documentation

See merge request allianceauth/allianceauth!1619
2024-05-26 02:58:23 +00:00
Ariel Rin
23371c233d Merge branch 'bmtuk-master-patch-12042' into 'master'
Timerboard - Fix Timer deletion

See merge request allianceauth/allianceauth!1625
2024-05-26 02:56:35 +00:00
Ariel Rin
7a3bbf0d7f Merge branch 'staff-menu-item' into 'master'
Allow staff access to admin button in user menu

See merge request allianceauth/allianceauth!1626
2024-05-26 02:56:05 +00:00
Aaron Kable
89a1bec9c1 Allow staff access to admin button in user menu 2024-05-26 02:56:04 +00:00
Ben Thomas
1c1e70619a Fix ability to delete timers 2024-05-21 23:42:19 +00:00
entropylaw
634e7357be Update troubleshooting.md. Fixed Typo. 2024-04-13 21:21:13 -05:00
entropylaw
08dc88da1a Update troubleshooting.md 2024-04-13 20:59:19 -05:00
entropylaw
3d206e445c Update troubleshooting.md 2024-04-13 20:55:10 -05:00
entropylaw
64686cdad1 Update troubleshooting.md 2024-04-13 20:49:46 -05:00
AnomicDev
d7fe09bdf1 Update troubleshooting.md 2024-04-14 01:19:51 +00:00
AnomicDev
6da50da92f Update troubleshooting.md 2024-04-14 01:10:38 +00:00
AnomicDev
51e4dd986f Update troubleshooting.md 2024-04-14 01:09:56 +00:00
AnomicDev
bee6522182 Update troubleshooting.md 2024-04-14 01:07:08 +00:00
AnomicDev
1711a9dd33 Update troubleshooting.md 2024-04-14 01:05:25 +00:00
AnomicDev
3914626379 Update troubleshooting.md 2024-04-14 01:04:59 +00:00
AnomicDev
df276cb32d Update troubleshooting.md 2024-04-14 00:59:19 +00:00
AnomicDev
daad7d8b10 Update troubleshooting.md 2024-04-14 00:58:44 +00:00
AnomicDev
3bf5bc0fe3 Update troubleshooting.md 2024-04-14 00:58:09 +00:00
AnomicDev
96abae553a Update troubleshooting.md 2024-04-14 00:57:12 +00:00
AnomicDev
f9cbfb1562 Update troubleshooting.md 2024-04-14 00:55:59 +00:00
AnomicDev
8eaa94e179 Update troubleshooting.md 2024-04-14 00:55:24 +00:00
AnomicDev
4f876b648b Update troubleshooting.md 2024-04-14 00:53:16 +00:00
AnomicDev
cd738137c0 Update troubleshooting.md 2024-04-14 00:52:07 +00:00
209 changed files with 13879 additions and 5189 deletions

View File

@@ -25,7 +25,7 @@ before_script:
pre-commit-check:
<<: *only-default
stage: pre-commit
image: python:3.11-bullseye
image: python:3.11-bookworm
# variables:
# PRE_COMMIT_HOME: ${CI_PROJECT_DIR}/.cache/pre-commit
# cache:
@@ -53,7 +53,7 @@ secret_detection:
test-3.8-core:
<<: *only-default
image: python:3.8-bullseye
image: python:3.8-bookworm
script:
- tox -e py38-core
artifacts:
@@ -65,7 +65,7 @@ test-3.8-core:
test-3.9-core:
<<: *only-default
image: python:3.9-bullseye
image: python:3.9-bookworm
script:
- tox -e py39-core
artifacts:
@@ -77,7 +77,7 @@ test-3.9-core:
test-3.10-core:
<<: *only-default
image: python:3.10-bullseye
image: python:3.10-bookworm
script:
- tox -e py310-core
artifacts:
@@ -89,7 +89,7 @@ test-3.10-core:
test-3.11-core:
<<: *only-default
image: python:3.11-bullseye
image: python:3.11-bookworm
script:
- tox -e py311-core
artifacts:
@@ -101,7 +101,7 @@ test-3.11-core:
test-3.12-core:
<<: *only-default
image: python:3.12-rc-bullseye
image: python:3.12-bookworm
script:
- tox -e py312-core
artifacts:
@@ -113,7 +113,7 @@ test-3.12-core:
test-3.8-all:
<<: *only-default
image: python:3.8-bullseye
image: python:3.8-bookworm
script:
- tox -e py38-all
artifacts:
@@ -125,7 +125,7 @@ test-3.8-all:
test-3.9-all:
<<: *only-default
image: python:3.9-bullseye
image: python:3.9-bookworm
script:
- tox -e py39-all
artifacts:
@@ -137,7 +137,7 @@ test-3.9-all:
test-3.10-all:
<<: *only-default
image: python:3.10-bullseye
image: python:3.10-bookworm
script:
- tox -e py310-all
artifacts:
@@ -149,7 +149,7 @@ test-3.10-all:
test-3.11-all:
<<: *only-default
image: python:3.11-bullseye
image: python:3.11-bookworm
script:
- tox -e py311-all
artifacts:
@@ -162,7 +162,7 @@ test-3.11-all:
test-3.12-all:
<<: *only-default
image: python:3.12-rc-bullseye
image: python:3.12-bookworm
script:
- tox -e py312-all
artifacts:
@@ -174,7 +174,7 @@ test-3.12-all:
build-test:
stage: test
image: python:3.11-bullseye
image: python:3.11-bookworm
before_script:
- python -m pip install --upgrade pip
@@ -193,13 +193,13 @@ build-test:
test-docs:
<<: *only-default
image: python:3.11-bullseye
image: python:3.11-bookworm
script:
- tox -e docs
deploy_production:
stage: deploy
image: python:3.11-bullseye
image: python:3.11-bookworm
before_script:
- python -m pip install --upgrade pip

View File

@@ -3,22 +3,41 @@
# Update this file:
# pre-commit autoupdate
# Set the default language versions for the hooks
default_language_version:
python: python3 # Force all Python hooks to use Python 3
node: 22.12.0 # Force all Node hooks to use Node 22.12.0
# Globally exclude files
# https://pre-commit.com/#top_level-exclude
exclude: |
(?x)(
LICENSE|
allianceauth\/static\/allianceauth\/css\/themes\/bootstrap-locals.less|
\.min\.css|
\.min\.js|
\.po|
\.mo|
swagger\.json|
static/(.*)/libs/
)
repos:
# Code Upgrades
- repo: https://github.com/asottile/pyupgrade
rev: v3.15.2
rev: v3.19.1
hooks:
- id: pyupgrade
args: [--py38-plus]
- repo: https://github.com/adamchainz/django-upgrade
rev: 1.17.0
rev: 1.22.2
hooks:
- id: django-upgrade
args: [--target-version=4.2]
# Formatting
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
rev: v5.0.0
hooks:
# Identify invalid files
- id: check-ast
@@ -33,9 +52,9 @@ repos:
- id: detect-private-key
- id: check-case-conflict
# Python checks
# - id: check-docstring-first
# - id: check-docstring-first
- id: debug-statements
# - id: requirements-txt-fixer
# - id: requirements-txt-fixer
- id: fix-encoding-pragma
args: [--remove]
- id: fix-byte-order-marker
@@ -44,40 +63,22 @@ repos:
args: [--fix=lf]
- id: trailing-whitespace
args: [--markdown-linebreak-ext=md]
exclude: |
(?x)(
\.min\.css|
\.min\.js|
\.po|
\.mo|
swagger\.json
)
- id: check-executables-have-shebangs
- id: end-of-file-fixer
exclude: |
(?x)(
\.min\.css|
\.min\.js|
\.po|
\.mo|
swagger\.json
)
- repo: https://github.com/editorconfig-checker/editorconfig-checker.python
rev: 2.7.3
rev: 3.2.0
hooks:
- id: editorconfig-checker
exclude: |
(?x)(
LICENSE|
allianceauth\/static\/allianceauth\/css\/themes\/bootstrap-locals.less|
\.po|
\.mo|
swagger\.json
)
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.44.0
hooks:
- id: markdownlint
language: node
args:
- --disable=MD013
# Infrastructure
- repo: https://github.com/tox-dev/pyproject-fmt
rev: 2.0.3
rev: v2.5.0
hooks:
- id: pyproject-fmt
name: pyproject.toml formatter
@@ -85,9 +86,9 @@ repos:
args:
- --indent=4
additional_dependencies:
- tox==4.15.0 # https://github.com/tox-dev/tox/releases/latest
- tox==4.24.1 # https://github.com/tox-dev/tox/releases/latest
- repo: https://github.com/abravalheri/validate-pyproject
rev: v0.17
rev: v0.23
hooks:
- id: validate-pyproject
name: Validate pyproject.toml

View File

@@ -5,7 +5,7 @@
[![django](https://img.shields.io/pypi/djversions/allianceauth?label=django)](https://pypi.org/project/allianceauth/)
[![version](https://img.shields.io/pypi/v/allianceauth?label=release)](https://pypi.org/project/allianceauth/)
[![pipeline status](https://gitlab.com/allianceauth/allianceauth/badges/master/pipeline.svg)](https://gitlab.com/allianceauth/allianceauth/commits/master)
[![Documentation Status](https://readthedocs.org/projects/allianceauth/badge/?version=latest)](http://allianceauth.readthedocs.io/?badge=latest)
[![Documentation Status](https://readthedocs.org/projects/allianceauth/badge/?version=latest)](https://allianceauth.readthedocs.io/?badge=latest)
[![coverage report](https://gitlab.com/allianceauth/allianceauth/badges/master/coverage.svg)](https://gitlab.com/allianceauth/allianceauth/commits/master)
[![Chat on Discord](https://img.shields.io/discord/399006117012832262.svg)](https://discord.gg/fjnHAmk)
@@ -14,7 +14,7 @@ An auth system for EVE Online to help in-game organizations manage online servic
## Content
- [Overview](#overview)
- [Documentation](http://allianceauth.rtfd.io)
- [Documentation](https://allianceauth.rtfd.io)
- [Support](#support)
- [Release Notes](https://gitlab.com/allianceauth/allianceauth/-/releases)
- [Developer Team](#development-team)
@@ -38,7 +38,7 @@ Main features:
- English :flag_gb:, Chinese :flag_cn:, German :flag_de:, Spanish :flag_es:, Korean :flag_kr:, Russian :flag_ru:, Italian :flag_it:, French :flag_fr:, Japanese :flag_jp: and Ukrainian :flag_ua: Localization
For further details about AA - including an installation guide and a full list of included services and plugin apps - please see the [official documentation](http://allianceauth.rtfd.io).
For further details about AA - including an installation guide and a full list of included services and plugin apps - please see the [official documentation](https://allianceauth.rtfd.io).
## Screenshot

View File

@@ -5,7 +5,7 @@ manage online service access.
# This will make sure the app is always imported when
# Django starts so that shared_task will use this app.
__version__ = '4.0.2'
__version__ = '4.6.4'
__title__ = 'Alliance Auth'
__url__ = 'https://gitlab.com/allianceauth/allianceauth'
NAME = f'{__title__} v{__version__}'

View File

@@ -1,15 +1,16 @@
from django.contrib import admin
from .models import AnalyticsIdentifier, AnalyticsTokens
from solo.admin import SingletonModelAdmin
@admin.register(AnalyticsIdentifier)
class AnalyticsIdentifierAdmin(admin.ModelAdmin):
class AnalyticsIdentifierAdmin(SingletonModelAdmin):
search_fields = ['identifier', ]
list_display = ('identifier',)
list_display = ['identifier', ]
@admin.register(AnalyticsTokens)
class AnalyticsTokensAdmin(admin.ModelAdmin):
search_fields = ['name', ]
list_display = ('name', 'type',)
list_display = ['name', 'type', ]

View File

@@ -0,0 +1,17 @@
# Generated by Django 4.2.16 on 2024-12-11 02:17
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('analytics', '0009_remove_analyticstokens_ignore_paths_and_more'),
]
operations = [
migrations.AlterModelOptions(
name='analyticsidentifier',
options={'verbose_name': 'Analytics Identifier'},
),
]

View File

@@ -1,23 +1,19 @@
from typing import Literal
from django.db import models
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
from solo.models import SingletonModel
from uuid import uuid4
class AnalyticsIdentifier(models.Model):
class AnalyticsIdentifier(SingletonModel):
identifier = models.UUIDField(
default=uuid4,
editable=False)
identifier = models.UUIDField(default=uuid4, editable=False)
def save(self, *args, **kwargs):
if not self.pk and AnalyticsIdentifier.objects.exists():
# Force a single object
raise ValidationError('There is can be only one \
AnalyticsIdentifier instance')
self.pk = self.id = 1 # If this happens to be deleted and recreated, force it to be 1
return super().save(*args, **kwargs)
def __str__(self) -> Literal['Analytics Identifier']:
return "Analytics Identifier"
class Meta:
verbose_name = "Analytics Identifier"
class AnalyticsTokens(models.Model):

View File

@@ -5,6 +5,7 @@ from django.apps import apps
from celery import shared_task
from .models import AnalyticsTokens, AnalyticsIdentifier
from .utils import (
existence_baremetal_or_docker,
install_stat_addons,
install_stat_tokens,
install_stat_users)
@@ -67,8 +68,8 @@ def analytics_event(namespace: str,
value=value).apply_async(priority=9)
@shared_task()
def analytics_daily_stats():
@shared_task
def analytics_daily_stats() -> None:
"""Celery Task: Do not call directly
Gathers a series of daily statistics
@@ -77,6 +78,7 @@ def analytics_daily_stats():
users = install_stat_users()
tokens = install_stat_tokens()
addons = install_stat_addons()
existence_type = existence_baremetal_or_docker()
logger.debug("Running Daily Analytics Upload")
analytics_event(namespace='allianceauth.analytics',
@@ -84,6 +86,11 @@ def analytics_daily_stats():
label='existence',
value=1,
event_type='Stats')
analytics_event(namespace='allianceauth.analytics',
task='send_install_stats',
label=existence_type,
value=1,
event_type='Stats')
analytics_event(namespace='allianceauth.analytics',
task='send_install_stats',
label='users',
@@ -99,16 +106,42 @@ def analytics_daily_stats():
label='addons',
value=addons,
event_type='Stats')
for appconfig in apps.get_app_configs():
analytics_event(namespace='allianceauth.analytics',
task='send_extension_stats',
label=appconfig.label,
value=1,
event_type='Stats')
if appconfig.label in [
"django_celery_beat",
"bootstrapform",
"messages",
"sessions",
"auth",
"staticfiles",
"users",
"addons",
"admin",
"humanize",
"contenttypes",
"sortedm2m",
"django_bootstrap5",
"tokens",
"authentication",
"services",
"framework",
"notifications"
"eveonline",
"navhelper",
"analytics",
"menu",
"theme"
]:
pass
else:
analytics_event(namespace='allianceauth.analytics',
task='send_extension_stats',
label=appconfig.label,
value=1,
event_type='Stats')
@shared_task()
@shared_task
def send_ga_tracking_celery_event(
measurement_id: str,
secret: str,
@@ -138,7 +171,7 @@ def send_ga_tracking_celery_event(
}
payload = {
'client_id': AnalyticsIdentifier.objects.get(id=1).identifier.hex,
'client_id': AnalyticsIdentifier.get_solo().identifier.hex,
"user_properties": {
"allianceauth_version": {
"value": __version__

View File

@@ -1,9 +1,8 @@
from allianceauth.analytics.models import AnalyticsIdentifier
from django.core.exceptions import ValidationError
from django.test.testcases import TestCase
from uuid import UUID, uuid4
from uuid import uuid4
# Identifiers
@@ -14,14 +13,4 @@ uuid_2 = "7aa6bd70701f44729af5e3095ff4b55c"
class TestAnalyticsIdentifier(TestCase):
def test_identifier_random(self):
self.assertNotEqual(AnalyticsIdentifier.objects.get(), uuid4)
def test_identifier_singular(self):
AnalyticsIdentifier.objects.all().delete()
AnalyticsIdentifier.objects.create(identifier=uuid_1)
# Yeah i have multiple asserts here, they all do the same thing
with self.assertRaises(ValidationError):
AnalyticsIdentifier.objects.create(identifier=uuid_2)
self.assertEqual(AnalyticsIdentifier.objects.count(), 1)
self.assertEqual(AnalyticsIdentifier.objects.get(
pk=1).identifier, UUID(uuid_1))
self.assertNotEqual(AnalyticsIdentifier.get_solo(), uuid4)

View File

@@ -1,3 +1,4 @@
import os
from django.apps import apps
from allianceauth.authentication.models import User
from esi.models import Token
@@ -34,3 +35,16 @@ def install_stat_addons() -> int:
The Number of Installed Apps"""
addons = len(list(apps.get_app_configs()))
return addons
def existence_baremetal_or_docker() -> str:
"""Checks the Installation Type of an install
Returns
-------
str
existence_baremetal or existence_docker"""
docker_tag = os.getenv('AA_DOCKER_TAG')
if docker_tag:
return "existence_docker"
return "existence_baremetal"

View File

@@ -1,30 +1,8 @@
from django.apps import AppConfig
from django.core.checks import Warning, Error, register
class AllianceAuthConfig(AppConfig):
name = 'allianceauth'
@register()
def check_settings(app_configs, **kwargs):
from django.conf import settings
errors = []
if hasattr(settings, "SITE_URL"):
if settings.SITE_URL[-1] == "/":
errors.append(Warning(
"'SITE_URL' Has a trailing slash. This may lead to incorrect links being generated by Auth."))
else:
errors.append(Error(
"No 'SITE_URL' found is settings. This may lead to incorrect links being generated by Auth or Errors in 3rd party modules."))
if hasattr(settings, "CSRF_TRUSTED_ORIGINS"):
if hasattr(settings, "SITE_URL"):
if settings.SITE_URL not in settings.CSRF_TRUSTED_ORIGINS:
errors.append(Warning(
"'SITE_URL' not found in 'CSRF_TRUSTED_ORIGINS'. Auth may not load pages correctly until this is rectified."))
else:
errors.append(Error(
"No 'CSRF_TRUSTED_ORIGINS' found is settings, Auth may not load pages correctly until this is rectified"))
return errors
def ready(self) -> None:
import allianceauth.checks # noqa

View File

@@ -0,0 +1,18 @@
# Generated by Django 4.2 on 2024-09-13 09:46
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('authentication', '0023_alter_userprofile_language'),
]
operations = [
migrations.AlterField(
model_name='userprofile',
name='language',
field=models.CharField(blank=True, choices=[('en', 'English'), ('cs-cz', 'Czech'), ('de', 'German'), ('es', 'Spanish'), ('it-it', 'Italian'), ('ja', 'Japanese'), ('ko-kr', 'Korean'), ('fr-fr', 'French'), ('ru', 'Russian'), ('nl-nl', 'Dutch'), ('pl-pl', 'Polish'), ('uk', 'Ukrainian'), ('zh-hans', 'Simplified Chinese')], default='', max_length=10, verbose_name='Language'),
),
]

View File

@@ -67,18 +67,20 @@ class UserProfile(models.Model):
"""
Choices for UserProfile.language
"""
# Sorted by Language Code alphabetical order + English at top
ENGLISH = 'en', _('English')
CZECH = 'cs-cz', _("Czech") # Not yet at 50% translated
GERMAN = 'de', _('German')
SPANISH = 'es', _('Spanish')
CHINESE = 'zh-hans', _('Chinese Simplified')
RUSSIAN = 'ru', _('Russian')
KOREAN = 'ko', _('Korean')
FRENCH = 'fr', _('French')
ITALIAN = 'it-it', _('Italian')
JAPANESE = 'ja', _('Japanese')
ITALIAN = 'it', _('Italian')
KOREAN = 'ko-kr', _('Korean')
FRENCH = 'fr-fr', _('French')
RUSSIAN = 'ru', _('Russian')
DUTCH = 'nl-nl', _("Dutch")
POLISH = 'pl-pl', _("Polish")
UKRAINIAN = 'uk', _('Ukrainian')
POLISH = 'pl', _("Polish")
CHINESE = 'zh-hans', _('Simplified Chinese')
user = models.OneToOneField(
User,

View File

@@ -7,7 +7,7 @@
{% translate "Dashboard" %}
{% endblock %}
{% block content %}
<div class="d-flex justify-content-around align-self-center flex-wrap">
<div class="row">
{% for dash in views %}
{{ dash | safe }}
{% endfor %}

View File

@@ -1,13 +1,11 @@
{% load i18n %}
<div id="aa-dashboard-panel-characters" class="col-12 col-xl-8 align-self-stretch p-2 ps-0 pe-0 ps-xl-0 pe-xl-2">
<div class="card">
<div id="aa-dashboard-panel-characters" class="col-12 col-xl-8 mb-3">
<div class="card h-100">
<div class="card-body">
<div class="d-flex align-items-center">
<h4 class="ms-auto me-auto">
{% translate "Characters" %}
</h4>
</div>
<div class="card-body">
{% translate "Characters" as widget_title %}
{% include "framework/dashboard/widget-title.html" with title=widget_title %}
<div>
<div style="height: 300px; overflow-y:auto;">
<div class="d-flex">
<a href="{% url 'authentication:add_character' %}" class="btn btn-primary flex-fill m-1" title="{% translate 'Add Character' %}">

View File

@@ -1,9 +1,11 @@
{% load i18n %}
<div id="aa-dashboard-panel-membership" class="col-12 col-xl-4 align-self-stretch py-2 ps-xl-2">
<div id="aa-dashboard-panel-membership" class="col-12 col-xl-4 mb-3">
<div class="card h-100">
<div class="card-body">
<h4 class="card-title text-center">{% translate "Membership" %}</h4>
<div class="card-body">
{% translate "Membership" as widget_title %}
{% include "framework/dashboard/widget-title.html" with title=widget_title %}
<div>
<div style="height: 300px; overflow-y:auto;">
<h5 class="text-center">{% translate "State:" %} {{ request.user.profile.state }}</h5>
<table class="table">

View File

@@ -1,5 +1,6 @@
{% extends "allianceauth/base-bs5.html" %}
{% load aa_i18n %}
{% load i18n %}
{% block page_title %}
@@ -13,7 +14,7 @@
{% block content %}
<div>
<p class="mb-3">
{% translate "This page is a best attempt, but backups or database logs can still contain your tokens. Always revoke tokens on https://community.eveonline.com/support/third-party-applications/ where possible."|urlize %}
{% translate "This page is a best attempt, but backups or database logs can still contain your tokens. Always revoke tokens on https://developers.eveonline.com/authorized-apps where possible."|urlize %}
</p>
<table class="table w-100" id="table_tokens">
@@ -50,20 +51,23 @@
{% block extra_javascript %}
{% include "bundles/datatables-js-bs5.html" %}
{% get_datatables_language_static LANGUAGE_CODE as DT_LANG_PATH %}
<script>
$(document).ready(() => {
let grp = 2;
const table = $('#table_tokens').DataTable({
$('#table_tokens').DataTable({
"language": {"url": '{{ DT_LANG_PATH }}'},
'columnDefs': [{orderable: false, targets: [0, 1]}, {
'visible': false,
'targets': grp
}],
'order': [[grp, 'asc']],
'drawCallback': function (settings) {
var api = this.api();
var rows = api.rows({page: 'current'}).nodes();
var last = null;
const api = this.api();
const rows = api.rows({page: 'current'}).nodes();
let last = null;
api.column(grp, {page: 'current'})
.data()
.each((group, i) => {

View File

@@ -5,11 +5,11 @@
{% csrf_token %}
<select class="form-select" onchange="this.form.submit()" class="form-control" id="lang-select" name="language">
{% get_language_info_list for LANGUAGES as languages %}
{% get_available_languages as LANGUAGES %}
{% for language in languages %}
<option lang="{{ language.code }}" value="{{ language.code }}"{% if language.code == LANGUAGE_CODE %} selected="selected"{% endif %}>
{{ language.name_local|capfirst }} ({{ language.code }})
{% for lang_code, lang_name in LANGUAGES %}
<option lang="{{ lang_code }}" value="{{ lang_code }}"{% if lang_code == LANGUAGE_CODE %} selected{% endif %}>
{{ lang_code|language_name_local|capfirst }} ({{ lang_code }})
</option>
{% endfor %}
</select>

View File

@@ -29,7 +29,7 @@
</p>
<p class="text-center">
<a class="text-reset" href="https://community.eveonline.com/support/third-party-applications/" target="_blank" rel="noopener noreferrer">
<a class="text-reset" href="https://developers.eveonline.com/authorized-apps" target="_blank" rel="noopener noreferrer">
{% translate "Manage ESI Applications" %}
</a>
</p>

562
allianceauth/checks.py Normal file
View File

@@ -0,0 +1,562 @@
"""
Django system checks for Alliance Auth
"""
from typing import List
from django import db
from django.core.checks import CheckMessage, Error, register, Warning
from allianceauth.utils.cache import get_redis_client
from django.utils import timezone
from packaging.version import InvalidVersion, Version as Pep440Version
from celery import current_app
from django.conf import settings
from sqlite3.dbapi2 import sqlite_version_info
"""
A = System Packages
B = Configuration
"""
@register()
def django_settings(app_configs, **kwargs) -> List[CheckMessage]:
"""
Check that Django settings are correctly configured
:param app_configs:
:type app_configs:
:param kwargs:
:type kwargs:
:return:
:rtype:
"""
errors: List[CheckMessage] = []
# Check for SITE_URL
if hasattr(settings, "SITE_URL"):
# Check if SITE_URL is empty
if settings.SITE_URL == "":
errors.append(
Error(
msg="'SITE_URL' is empty.",
hint="Make sure to set 'SITE_URL' to the URL of your Auth instance. (Without trailing slash)",
id="allianceauth.checks.B011",
)
)
# Check if SITE_URL has a trailing slash
elif settings.SITE_URL[-1] == "/":
errors.append(
Warning(
msg="'SITE_URL' has a trailing slash. This may lead to incorrect links being generated by Auth.",
hint="",
id="allianceauth.checks.B005",
)
)
# SITE_URL not found
else:
errors.append(
Error(
msg="No 'SITE_URL' found is settings. This may lead to incorrect links being generated by Auth or Errors in 3rd party modules.",
hint="",
id="allianceauth.checks.B006",
)
)
# Check for CSRF_TRUSTED_ORIGINS
if hasattr(settings, "CSRF_TRUSTED_ORIGINS") and hasattr(settings, "SITE_URL"):
# Check if SITE_URL is not in CSRF_TRUSTED_ORIGINS
if settings.SITE_URL not in settings.CSRF_TRUSTED_ORIGINS:
errors.append(
Warning(
msg="'SITE_URL' not found in 'CSRF_TRUSTED_ORIGINS'. Auth may not load pages correctly until this is rectified.",
hint="",
id="allianceauth.checks.B007",
)
)
# CSRF_TRUSTED_ORIGINS not found
else:
errors.append(
Error(
msg="No 'CSRF_TRUSTED_ORIGINS' found is settings, Auth may not load pages correctly until this is rectified",
hint="",
id="allianceauth.checks.B008",
)
)
# Check for ESI_USER_CONTACT_EMAIL
if hasattr(settings, "ESI_USER_CONTACT_EMAIL"):
# Check if ESI_USER_CONTACT_EMAIL is empty
if settings.ESI_USER_CONTACT_EMAIL == "":
errors.append(
Error(
msg="'ESI_USER_CONTACT_EMAIL' is empty. A valid email is required as maintainer contact for CCP.",
hint="",
id="allianceauth.checks.B009",
)
)
# ESI_USER_CONTACT_EMAIL not found
else:
errors.append(
Error(
msg="No 'ESI_USER_CONTACT_EMAIL' found is settings. A valid email is required as maintainer contact for CCP.",
hint="",
id="allianceauth.checks.B010",
)
)
return errors
@register()
def system_package_redis(app_configs, **kwargs) -> List[CheckMessage]:
"""
Check that Redis is a supported version
:param app_configs:
:type app_configs:
:param kwargs:
:type kwargs:
:return:
:rtype:
"""
allianceauth_redis_install_link = "https://allianceauth.readthedocs.io/en/latest/installation/allianceauth.html#redis-and-other-tools"
errors: List[CheckMessage] = []
try:
redis_version = Pep440Version(get_redis_client().info()["redis_version"])
except InvalidVersion:
errors.append(Warning("Unable to confirm Redis Version"))
return errors
if (
redis_version.major == 7
and redis_version.minor == 2
and timezone.now()
> timezone.datetime(year=2025, month=8, day=31, tzinfo=timezone.utc)
):
errors.append(
Error(
msg=f"Redis {redis_version.public} in Security Support only, Updating Suggested",
hint=allianceauth_redis_install_link,
id="allianceauth.checks.A001",
)
)
elif redis_version.major == 7 and redis_version.minor == 0:
errors.append(
Warning(
msg=f"Redis {redis_version.public} in Security Support only, Updating Suggested",
hint=allianceauth_redis_install_link,
id="allianceauth.checks.A002",
)
)
elif redis_version.major == 6 and redis_version.minor == 2:
errors.append(
Warning(
msg=f"Redis {redis_version.public} in Security Support only, Updating Suggested",
hint=allianceauth_redis_install_link,
id="allianceauth.checks.A018",
)
)
elif redis_version.major in [6, 5]:
errors.append(
Error(
msg=f"Redis {redis_version.public} EOL",
hint=allianceauth_redis_install_link,
id="allianceauth.checks.A003",
)
)
return errors
@register()
def system_package_mysql(app_configs, **kwargs) -> List[CheckMessage]:
"""
Check that MySQL is a supported version
:param app_configs:
:type app_configs:
:param kwargs:
:type kwargs:
:return:
:rtype:
"""
mysql_quick_guide_link = "https://dev.mysql.com/doc/mysql-apt-repo-quick-guide/en/"
errors: List[CheckMessage] = []
for connection in db.connections.all():
if connection.vendor == "mysql":
try:
mysql_version = Pep440Version(
".".join(str(i) for i in connection.mysql_version)
)
except InvalidVersion:
errors.append(Warning("Unable to confirm MySQL Version"))
return errors
# MySQL 8
if mysql_version.major == 8:
if mysql_version.minor == 4 and timezone.now() > timezone.datetime(
year=2032, month=4, day=30, tzinfo=timezone.utc
):
errors.append(
Error(
msg=f"MySQL {mysql_version.public} EOL",
hint=mysql_quick_guide_link,
id="allianceauth.checks.A004",
)
)
elif mysql_version.minor == 3:
errors.append(
Warning(
msg=f"MySQL {mysql_version.public} Non LTS",
hint=mysql_quick_guide_link,
id="allianceauth.checks.A005",
)
)
elif mysql_version.minor == 2:
errors.append(
Warning(
msg=f"MySQL {mysql_version.public} Non LTS",
hint=mysql_quick_guide_link,
id="allianceauth.checks.A006",
)
)
elif mysql_version.minor == 1:
errors.append(
Error(
msg=f"MySQL {mysql_version.public} EOL",
hint=mysql_quick_guide_link,
id="allianceauth.checks.A007",
)
)
elif mysql_version.minor == 0 and timezone.now() > timezone.datetime(
year=2026, month=4, day=30, tzinfo=timezone.utc
):
errors.append(
Error(
msg=f"MySQL {mysql_version.public} EOL",
hint=mysql_quick_guide_link,
id="allianceauth.checks.A008",
)
)
# MySQL below 8
# This will also catch Mariadb 5.x
elif mysql_version.major < 8:
errors.append(
Error(
msg=f"MySQL or MariaDB {mysql_version.public} EOL",
hint=mysql_quick_guide_link,
id="allianceauth.checks.A009",
)
)
return errors
@register()
def system_package_mariadb(app_configs, **kwargs) -> List[CheckMessage]:
"""
Check that MariaDB is a supported version
:param app_configs:
:type app_configs:
:param kwargs:
:type kwargs:
:return:
:rtype:
"""
mariadb_download_link = "https://mariadb.org/download/?t=repo-config"
errors: List[CheckMessage] = []
for connection in db.connections.all():
# TODO: Find a way to determine MySQL vs. MariaDB
if connection.vendor == "mysql":
try:
mariadb_version = Pep440Version(
".".join(str(i) for i in connection.mysql_version)
)
except InvalidVersion:
errors.append(Warning("Unable to confirm MariaDB Version"))
return errors
# MariaDB 11
if mariadb_version.major == 11:
if mariadb_version.minor == 4 and timezone.now() > timezone.datetime(
year=2029, month=5, day=19, tzinfo=timezone.utc
):
errors.append(
Error(
msg=f"MariaDB {mariadb_version.public} EOL",
hint=mariadb_download_link,
id="allianceauth.checks.A010",
)
)
elif mariadb_version.minor == 2:
errors.append(
Warning(
msg=f"MariaDB {mariadb_version.public} Non LTS",
hint=mariadb_download_link,
id="allianceauth.checks.A018",
)
)
if timezone.now() > timezone.datetime(
year=2024, month=11, day=21, tzinfo=timezone.utc
):
errors.append(
Error(
msg=f"MariaDB {mariadb_version.public} EOL",
hint=mariadb_download_link,
id="allianceauth.checks.A011",
)
)
elif mariadb_version.minor == 1:
errors.append(
Warning(
msg=f"MariaDB {mariadb_version.public} Non LTS",
hint=mariadb_download_link,
id="allianceauth.checks.A019",
)
)
if timezone.now() > timezone.datetime(
year=2024, month=8, day=21, tzinfo=timezone.utc
):
errors.append(
Error(
msg=f"MariaDB {mariadb_version.public} EOL",
hint=mariadb_download_link,
id="allianceauth.checks.A012",
)
)
# Demote versions down here once EOL
elif mariadb_version.minor in [0, 3]:
errors.append(
Error(
msg=f"MariaDB {mariadb_version.public} EOL",
hint=mariadb_download_link,
id="allianceauth.checks.A013",
)
)
# MariaDB 10
elif mariadb_version.major == 10:
if mariadb_version.minor == 11 and timezone.now() > timezone.datetime(
year=2028, month=2, day=10, tzinfo=timezone.utc
):
errors.append(
Error(
msg=f"MariaDB {mariadb_version.public} EOL",
hint=mariadb_download_link,
id="allianceauth.checks.A014",
)
)
elif mariadb_version.minor == 6 and timezone.now() > timezone.datetime(
year=2026, month=7, day=6, tzinfo=timezone.utc
):
errors.append(
Error(
msg=f"MariaDB {mariadb_version.public} EOL",
hint=mariadb_download_link,
id="allianceauth.checks.A0015",
)
)
elif mariadb_version.minor == 5 and timezone.now() > timezone.datetime(
year=2025, month=6, day=24, tzinfo=timezone.utc
):
errors.append(
Error(
msg=f"MariaDB {mariadb_version.public} EOL",
hint=mariadb_download_link,
id="allianceauth.checks.A016",
)
)
# Demote versions down here once EOL
elif mariadb_version.minor in [0, 1, 2, 3, 4, 7, 9, 10]:
errors.append(
Error(
msg=f"MariaDB {mariadb_version.public} EOL",
hint=mariadb_download_link,
id="allianceauth.checks.A017",
)
)
return errors
@register()
def system_package_sqlite(app_configs, **kwargs) -> List[CheckMessage]:
"""
Check that SQLite is a supported version
:param app_configs:
:type app_configs:
:param kwargs:
:type kwargs:
:return:
:rtype:
"""
errors: List[CheckMessage] = []
for connection in db.connections.all():
if connection.vendor == "sqlite":
try:
sqlite_version = Pep440Version(
".".join(str(i) for i in sqlite_version_info)
)
except InvalidVersion:
errors.append(Warning("Unable to confirm SQLite Version"))
return errors
if sqlite_version.major == 3 and sqlite_version.minor < 27:
errors.append(
Error(
msg=f"SQLite {sqlite_version.public} Unsupported by Django",
hint="https://pkgs.org/download/sqlite3",
id="allianceauth.checks.A020",
)
)
return errors
@register()
def sql_settings(app_configs, **kwargs) -> List[CheckMessage]:
"""
Check that SQL settings are correctly configured
:param app_configs:
:type app_configs:
:param kwargs:
:type kwargs:
:return:
:rtype:
"""
errors: List[CheckMessage] = []
for connection in db.connections.all():
if connection.vendor == "mysql":
try:
if connection.settings_dict["OPTIONS"]["charset"] != "utf8mb4":
errors.append(
Error(
msg=f"SQL Charset is not set to utf8mb4 DB: {connection.alias}",
hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8",
id="allianceauth.checks.B001",
)
)
except KeyError:
errors.append(
Error(
msg=f"SQL Charset is not set to utf8mb4 DB: {connection.alias}",
hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8",
id="allianceauth.checks.B001",
)
)
# This hasn't actually been set on AA yet
# try:
# if (
# connection.settings_dict["OPTIONS"]["collation"]
# != "utf8mb4_unicode_ci"
# ):
# errors.append(
# Error(
# msg=f"SQL Collation is not set to utf8mb4_unicode_ci DB:{connection.alias}",
# hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8",
# id="allianceauth.checks.B001",
# )
# )
# except KeyError:
# errors.append(
# Error(
# msg=f"SQL Collation is not set to utf8mb4_unicode_ci DB:{connection.alias}",
# hint="https://gitlab.com/allianceauth/allianceauth/-/commit/89be2456fb2d741b86417e889da9b6129525bec8",
# id="allianceauth.checks.B001",
# )
# )
# if connection.vendor == "sqlite":
return errors
@register()
def celery_settings(app_configs, **kwargs) -> List[CheckMessage]:
"""
Check that Celery settings are correctly configured
:param app_configs:
:type app_configs:
:param kwargs:
:type kwargs:
:return:
:rtype:
"""
errors: List[CheckMessage] = []
try:
if current_app.conf.broker_transport_options != {
"priority_steps": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"queue_order_strategy": "priority",
}:
errors.append(
Error(
msg="Celery Priorities are not set correctly",
hint="https://gitlab.com/allianceauth/allianceauth/-/commit/8861ec0a61790eca0261f1adc1cc04ca5f243cbc",
id="allianceauth.checks.B003",
)
)
except KeyError:
errors.append(
Error(
msg="Celery Priorities are not set",
hint="https://gitlab.com/allianceauth/allianceauth/-/commit/8861ec0a61790eca0261f1adc1cc04ca5f243cbc",
id="allianceauth.checks.B003",
)
)
try:
if not current_app.conf.broker_connection_retry_on_startup:
errors.append(
Error(
msg="Celery broker_connection_retry_on_startup not set correctly",
hint="https://gitlab.com/allianceauth/allianceauth/-/commit/380c41400b535447839e5552df2410af35a75280",
id="allianceauth.checks.B004",
)
)
except KeyError:
errors.append(
Error(
msg="Celery broker_connection_retry_on_startup not set",
hint="https://gitlab.com/allianceauth/allianceauth/-/commit/380c41400b535447839e5552df2410af35a75280",
id="allianceauth.checks.B004",
)
)
return errors
# IDEAS
# Any other celery things weve manually changed over the years
# I'd be happy to add Community App checks, old versions the owners dont want to support etc.
# Check Default Collation on DB
# Check Charset Collation on all tables

View File

@@ -1,5 +1,6 @@
{% extends 'corputils/base.html' %}
{% load aa_i18n %}
{% load i18n %}
{% load humanize %}
@@ -238,9 +239,12 @@
{% block extra_javascript %}
{% include 'bundles/datatables-js-bs5.html' %}
{% get_datatables_language_static LANGUAGE_CODE as DT_LANG_PATH %}
<script>
$(document).ready(() => {
$('#table-mains').DataTable({
"language": {"url": '{{ DT_LANG_PATH }}'},
"columnDefs": [
{ "sortable": false, "targets": [1] },
],
@@ -249,6 +253,7 @@
});
$('#table-members').DataTable({
"language": {"url": '{{ DT_LANG_PATH }}'},
"columnDefs": [
{ "searchable": false, "targets": [0, 2] },
{ "sortable": false, "targets": [0, 2] },
@@ -259,6 +264,7 @@
});
$('#table-unregistered').DataTable({
"language": {"url": '{{ DT_LANG_PATH }}'},
"columnDefs": [
{ "searchable": false, "targets": [0, 2] },
{ "sortable": false, "targets": [0, 2] },

View File

@@ -1,5 +1,6 @@
{% extends "corputils/base.html" %}
{% load aa_i18n %}
{% load i18n %}
{% block member_data %}
@@ -42,9 +43,12 @@
{% block extra_javascript %}
{% include 'bundles/datatables-js-bs5.html' %}
{% get_datatables_language_static LANGUAGE_CODE as DT_LANG_PATH %}
<script>
$(document).ready(() => {
$('#table-search').DataTable({
"language": {"url": '{{ DT_LANG_PATH }}'},
"stateSave": true,
"stateDuration": 0
});

View File

@@ -0,0 +1,3 @@
"""
Alliance Auth Crontab Utilities
"""

View File

@@ -0,0 +1,14 @@
"""
Crontab App Config
"""
from django.apps import AppConfig
class CrontabConfig(AppConfig):
"""
Crontab App Config
"""
name = "allianceauth.crontab"
label = "crontab"

View File

@@ -0,0 +1,29 @@
# Generated by Django 4.2.16 on 2025-01-20 06:16
import allianceauth.crontab.models
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='CronOffset',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('minute', models.FloatField(default=allianceauth.crontab.models.random_default, verbose_name='Minute Offset')),
('hour', models.FloatField(default=allianceauth.crontab.models.random_default, verbose_name='Hour Offset')),
('day_of_month', models.FloatField(default=allianceauth.crontab.models.random_default, verbose_name='Day of Month Offset')),
('month_of_year', models.FloatField(default=allianceauth.crontab.models.random_default, verbose_name='Month of Year Offset')),
('day_of_week', models.FloatField(default=allianceauth.crontab.models.random_default, verbose_name='Day of Week Offset')),
],
options={
'verbose_name': 'Cron Offsets',
},
),
]

View File

@@ -0,0 +1,23 @@
from random import random
from django.db import models
from django.utils.translation import gettext_lazy as _
from solo.models import SingletonModel
def random_default() -> float:
return random()
class CronOffset(SingletonModel):
minute = models.FloatField(_("Minute Offset"), default=random_default)
hour = models.FloatField(_("Hour Offset"), default=random_default)
day_of_month = models.FloatField(_("Day of Month Offset"), default=random_default)
month_of_year = models.FloatField(_("Month of Year Offset"), default=random_default)
day_of_week = models.FloatField(_("Day of Week Offset"), default=random_default)
def __str__(self) -> str:
return "Cron Offsets"
class Meta:
verbose_name = "Cron Offsets"

View File

@@ -0,0 +1,70 @@
from django.core.exceptions import ObjectDoesNotExist
from django_celery_beat.schedulers import (
DatabaseScheduler
)
from django_celery_beat.models import CrontabSchedule
from django.db.utils import OperationalError, ProgrammingError
from celery import schedules
from celery.utils.log import get_logger
from allianceauth.crontab.models import CronOffset
from allianceauth.crontab.utils import offset_cron
logger = get_logger(__name__)
class OffsetDatabaseScheduler(DatabaseScheduler):
"""
Customization of Django Celery Beat, Database Scheduler
Takes the Celery Schedule from local.py and applies our AA Framework Cron Offset, if apply_offset is true
Otherwise it passes it through as normal
"""
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
def update_from_dict(self, mapping):
s = {}
try:
cron_offset = CronOffset.get_solo()
except (OperationalError, ProgrammingError, ObjectDoesNotExist) as exc:
# This is just incase we haven't migrated yet or something
logger.warning(
"OffsetDatabaseScheduler: Could not fetch CronOffset (%r). "
"Defering to DatabaseScheduler",
exc
)
return super().update_from_dict(mapping)
for name, entry_fields in mapping.items():
try:
apply_offset = entry_fields.pop("apply_offset", False) # Ensure this pops before django tries to save to ORM
entry = self.Entry.from_entry(name, app=self.app, **entry_fields)
if apply_offset:
entry_fields.update({"apply_offset": apply_offset}) # Reapply this as its gets pulled from config inconsistently.
schedule_obj = entry.schedule
if isinstance(schedule_obj, schedules.crontab):
offset_cs = CrontabSchedule.from_schedule(offset_cron(schedule_obj))
offset_cs, created = CrontabSchedule.objects.get_or_create(
minute=offset_cs.minute,
hour=offset_cs.hour,
day_of_month=offset_cs.day_of_month,
month_of_year=offset_cs.month_of_year,
day_of_week=offset_cs.day_of_week,
timezone=offset_cs.timezone,
)
entry.schedule = offset_cron(schedule_obj) # This gets passed into Celery Beats Memory, important to keep it in sync with the model/DB
entry.model.crontab = offset_cs
entry.model.save()
logger.debug(f"Offset applied for '{name}' due to 'apply_offset' = True.")
if entry.model.enabled:
s[name] = entry
except Exception as e:
logger.exception("Error updating schedule for %s: %r", name, e)
self.schedule.update(s)

View File

View File

@@ -0,0 +1,63 @@
from unittest.mock import patch
from django.test import TestCase
from allianceauth.crontab.models import CronOffset
class CronOffsetModelTest(TestCase):
def test_cron_offset_is_singleton(self):
"""
Test that CronOffset is indeed a singleton and that
multiple calls to get_solo() return the same instance.
"""
offset1 = CronOffset.get_solo()
offset2 = CronOffset.get_solo()
# They should be the exact same object in memory
self.assertEqual(offset1.pk, offset2.pk)
def test_default_values_random(self):
"""
Test that the default values are set via random_default() when
no explicit value is provided. We'll patch 'random.random' to
produce predictable output.
"""
with patch('allianceauth.crontab.models.random', return_value=0.1234):
# Force creation of a new CronOffset by clearing the existing one
CronOffset.objects.all().delete()
offset = CronOffset.get_solo() # This triggers creation
# All fields should be 0.1234, because we patched random()
self.assertAlmostEqual(offset.minute, 0.1234)
self.assertAlmostEqual(offset.hour, 0.1234)
self.assertAlmostEqual(offset.day_of_month, 0.1234)
self.assertAlmostEqual(offset.month_of_year, 0.1234)
self.assertAlmostEqual(offset.day_of_week, 0.1234)
def test_update_offset_values(self):
"""
Test that we can update the offsets and retrieve them.
"""
offset = CronOffset.get_solo()
offset.minute = 0.5
offset.hour = 0.25
offset.day_of_month = 0.75
offset.month_of_year = 0.99
offset.day_of_week = 0.33
offset.save()
# Retrieve again to ensure changes persist
saved_offset = CronOffset.get_solo()
self.assertEqual(saved_offset.minute, 0.5)
self.assertEqual(saved_offset.hour, 0.25)
self.assertEqual(saved_offset.day_of_month, 0.75)
self.assertEqual(saved_offset.month_of_year, 0.99)
self.assertEqual(saved_offset.day_of_week, 0.33)
def test_str_representation(self):
"""
Verify the __str__ method returns 'Cron Offsets'.
"""
offset = CronOffset.get_solo()
self.assertEqual(str(offset), "Cron Offsets")

View File

@@ -0,0 +1,80 @@
# myapp/tests/test_tasks.py
import logging
from unittest.mock import patch
from django.test import TestCase
from django.db import ProgrammingError
from celery.schedules import crontab
from allianceauth.crontab.utils import offset_cron
from allianceauth.crontab.models import CronOffset
logger = logging.getLogger(__name__)
class TestOffsetCron(TestCase):
def test_offset_cron_normal(self):
"""
Test that offset_cron modifies the minute/hour fields
based on the CronOffset values when everything is normal.
"""
# We'll create a mock CronOffset instance
mock_offset = CronOffset(minute=0.5, hour=0.5)
# Our initial crontab schedule
original_schedule = crontab(
minute=[0, 5, 55],
hour=[0, 3, 23],
day_of_month='*',
month_of_year='*',
day_of_week='*'
)
# Patch CronOffset.get_solo to return our mock offset
with patch('allianceauth.crontab.models.CronOffset.get_solo', return_value=mock_offset):
new_schedule = offset_cron(original_schedule)
# Check the new minute/hour
# minute 0 -> 0 + round(60 * 0.5) = 30 % 60 = 30
# minute 5 -> 5 + 30 = 35 % 60 = 35
# minute 55 -> 55 + 30 = 85 % 60 = 25 --> sorted => 25,30,35
self.assertEqual(new_schedule._orig_minute, '25,30,35')
# hour 0 -> 0 + round(24 * 0.5) = 12 % 24 = 12
# hour 3 -> 3 + 12 = 15 % 24 = 15
# hour 23 -> 23 + 12 = 35 % 24 = 11 --> sorted => 11,12,15
self.assertEqual(new_schedule._orig_hour, '11,12,15')
# Check that other fields are unchanged
self.assertEqual(new_schedule._orig_day_of_month, '*')
self.assertEqual(new_schedule._orig_month_of_year, '*')
self.assertEqual(new_schedule._orig_day_of_week, '*')
def test_offset_cron_programming_error(self):
"""
Test that if a ProgrammingError is raised (e.g. before migrations),
offset_cron just returns the original schedule.
"""
original_schedule = crontab(minute=[0, 15, 30], hour=[1, 2, 3])
# Force get_solo to raise ProgrammingError
with patch('allianceauth.crontab.models.CronOffset.get_solo', side_effect=ProgrammingError()):
new_schedule = offset_cron(original_schedule)
# Should return the original schedule unchanged
self.assertEqual(new_schedule, original_schedule)
def test_offset_cron_unexpected_exception(self):
"""
Test that if any other exception is raised, offset_cron
also returns the original schedule, and logs the error.
"""
original_schedule = crontab(minute='0', hour='0')
# Force get_solo to raise a generic Exception
with patch('allianceauth.crontab.models.CronOffset.get_solo', side_effect=Exception("Something bad")):
new_schedule = offset_cron(original_schedule)
# Should return the original schedule unchanged
self.assertEqual(new_schedule, original_schedule)

View File

@@ -0,0 +1,50 @@
from celery.schedules import crontab
import logging
from allianceauth.crontab.models import CronOffset
from django.db import ProgrammingError
logger = logging.getLogger(__name__)
def offset_cron(schedule: crontab) -> crontab:
"""Take a crontab and apply a series of precalculated offsets to spread out tasks execution on remote resources
Args:
schedule (crontab): celery.schedules.crontab()
Returns:
crontab: A crontab with offsetted Minute and Hour fields
"""
try:
cron_offset = CronOffset.get_solo()
# Stops this shit from happening 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23
# It is only cosmetic, but still annoying
if schedule._orig_minute == '*':
new_minute = '*'
else:
new_minute = [(m + (round(60 * cron_offset.minute))) % 60 for m in schedule.minute]
if schedule._orig_hour == '*':
new_hour = '*'
else:
new_hour = [(m + (round(24 * cron_offset.hour))) % 24 for m in schedule.hour]
return crontab(
minute=",".join(str(m) for m in sorted(new_minute)),
hour=",".join(str(h) for h in sorted(new_hour)),
day_of_month=schedule._orig_day_of_month,
month_of_year=schedule._orig_month_of_year,
day_of_week=schedule._orig_day_of_week)
except ProgrammingError as e:
# If this is called before migrations are run hand back the default schedule
# These offsets are stored in a Singleton Model,
logger.error(e)
return schedule
except Exception as e:
# We absolutely cant fail to hand back a schedule
logger.error(e)
return schedule

View File

@@ -0,0 +1,3 @@
"""
Initializes the custom_css module.
"""

View File

@@ -0,0 +1,25 @@
"""
Admin classes for custom_css app
"""
# Django
from django.contrib import admin
# Django Solos
from solo.admin import SingletonModelAdmin
# Alliance Auth Custom CSS
from allianceauth.custom_css.models import CustomCSS
from allianceauth.custom_css.forms import CustomCSSAdminForm
@admin.register(CustomCSS)
class CustomCSSAdmin(SingletonModelAdmin):
"""
Custom CSS Admin
"""
form = CustomCSSAdminForm
# Leave this here for when we decide to add syntax highlighting to the CSS editor
# change_form_template = 'custom_css/admin/change_form.html'

View File

@@ -0,0 +1,13 @@
"""
Django app configuration for custom_css
"""
# Django
from django.apps import AppConfig
from django.utils.translation import gettext_lazy as _
class CustomCSSConfig(AppConfig):
name = "allianceauth.custom_css"
label = "custom_css"
verbose_name = _("Custom CSS")

View File

@@ -0,0 +1,29 @@
"""
Forms for custom_css app
"""
# Alliance Auth Custom CSS
from allianceauth.custom_css.models import CustomCSS
from allianceauth.custom_css.widgets import CssEditorWidget
# Django
from django import forms
class CustomCSSAdminForm(forms.ModelForm):
"""
Form for editing custom CSS
"""
class Meta:
model = CustomCSS
fields = ("css",)
widgets = {
"css": CssEditorWidget(
attrs={
"style": "width: 90%; height: 100%;",
"data-editor": "code-highlight",
"data-language": "css",
}
)
}

View File

@@ -0,0 +1,42 @@
# Generated by Django 4.2.15 on 2024-08-14 11:25
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = []
operations = [
migrations.CreateModel(
name="CustomCSS",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"css",
models.TextField(
blank=True,
help_text="This CSS will be added to the site after the default CSS.",
null=True,
verbose_name="Your custom CSS",
),
),
("timestamp", models.DateTimeField(auto_now=True)),
],
options={
"verbose_name": "Custom CSS",
"verbose_name_plural": "Custom CSS",
"default_permissions": (),
},
),
]

View File

@@ -0,0 +1,143 @@
"""
Models for the custom_css app
"""
import os
import re
# Django Solo
from solo.models import SingletonModel
# Django
from django.conf import settings
from django.db import models
from django.utils.translation import gettext_lazy as _
class CustomCSS(SingletonModel):
"""
Model for storing custom CSS for the site
"""
css = models.TextField(
blank=True,
null=True,
verbose_name=_("Your custom CSS"),
help_text=_("This CSS will be added to the site after the default CSS."),
)
timestamp = models.DateTimeField(auto_now=True)
class Meta:
"""
Meta for CustomCSS
"""
default_permissions = ()
verbose_name = _("Custom CSS")
verbose_name_plural = _("Custom CSS")
def __str__(self) -> str:
"""
String representation of CustomCSS
:return:
:rtype:
"""
return str(_("Custom CSS"))
def save(self, *args, **kwargs):
"""
Save method for CustomCSS
:param args:
:type args:
:param kwargs:
:type kwargs:
:return:
:rtype:
"""
self.pk = 1
if self.css and len(self.css.replace(" ", "")) > 0:
# Write the custom CSS to a file
custom_css_file = open(
f"{settings.STATIC_ROOT}allianceauth/custom-styles.css", "w+"
)
custom_css_file.write(self.compress_css())
custom_css_file.close()
else:
# Remove the custom CSS file
try:
os.remove(f"{settings.STATIC_ROOT}allianceauth/custom-styles.css")
except FileNotFoundError:
pass
super().save(*args, **kwargs)
def compress_css(self) -> str:
"""
Compress CSS
:return:
:rtype:
"""
css = self.css
new_css = ""
# Remove comments
css = re.sub(pattern=r"\s*/\*\s*\*/", repl="$$HACK1$$", string=css)
css = re.sub(pattern=r"/\*[\s\S]*?\*/", repl="", string=css)
css = css.replace("$$HACK1$$", "/**/")
# url() doesn't need quotes
css = re.sub(pattern=r'url\((["\'])([^)]*)\1\)', repl=r"url(\2)", string=css)
# Spaces may be safely collapsed as generated content will collapse them anyway.
css = re.sub(pattern=r"\s+", repl=" ", string=css)
# Shorten collapsable colors: #aabbcc to #abc
css = re.sub(
pattern=r"#([0-9a-f])\1([0-9a-f])\2([0-9a-f])\3(\s|;)",
repl=r"#\1\2\3\4",
string=css,
)
# Fragment values can loose zeros
css = re.sub(
pattern=r":\s*0(\.\d+([cm]m|e[mx]|in|p[ctx]))\s*;", repl=r":\1;", string=css
)
for rule in re.findall(pattern=r"([^{]+){([^}]*)}", string=css):
# We don't need spaces around operators
selectors = [
re.sub(
pattern=r"(?<=[\[\(>+=])\s+|\s+(?=[=~^$*|>+\]\)])",
repl=r"",
string=selector.strip(),
)
for selector in rule[0].split(",")
]
# Order is important, but we still want to discard repetitions
properties = {}
porder = []
for prop in re.findall(pattern="(.*?):(.*?)(;|$)", string=rule[1]):
key = prop[0].strip().lower()
if key not in porder:
porder.append(key)
properties[key] = prop[1].strip()
# output rule if it contains any declarations
if properties:
new_css += "{}{{{}}}".format(
",".join(selectors),
"".join([f"{key}:{properties[key]};" for key in porder])[:-1],
)
return new_css

View File

@@ -0,0 +1,48 @@
{% extends "admin/change_form.html" %}
{% block field_sets %}
{% for fieldset in adminform %}
<fieldset class="module aligned {{ fieldset.classes }}">
{% if fieldset.name %}<h2>{{ fieldset.name }}</h2>{% endif %}
{% if fieldset.description %}
<div class="description">{{ fieldset.description|safe }}</div>
{% endif %}
{% for line in fieldset %}
<div class="form-row{% if line.fields|length == 1 and line.errors %} errors{% endif %}{% if not line.has_visible_field %} hidden{% endif %}{% for field in line %}{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% endfor %}">
{% if line.fields|length == 1 %}{{ line.errors }}{% else %}<div class="flex-container form-multiline">{% endif %}
{% for field in line %}
<div>
{% if not line.fields|length == 1 and not field.is_readonly %}{{ field.errors }}{% endif %}
<div class="flex-container{% if not line.fields|length == 1 %} fieldBox{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% if not field.is_readonly and field.errors %} errors{% endif %}{% if field.field.is_hidden %} hidden{% endif %}{% elif field.is_checkbox %} checkbox-row{% endif %}">
{% if field.is_checkbox %}
{{ field.field }}{{ field.label_tag }}
{% else %}
{{ field.label_tag }}
{% if field.is_readonly %}
<div class="readonly">{{ field.contents }}</div>
{% else %}
{{ field.field }}
{% endif %}
{% endif %}
</div>
{% if field.field.help_text %}
<div class="help"{% if field.field.id_for_label %} id="{{ field.field.id_for_label }}_helptext"{% endif %}>
<div>{{ field.field.help_text|safe }}</div>
</div>
{% endif %}
</div>
{% endfor %}
{% if not line.fields|length == 1 %}</div>{% endif %}
</div>
{% endfor %}
</fieldset>
{% endfor %}
{% endblock %}
{% block after_field_sets %}{% endblock %}

View File

@@ -0,0 +1,3 @@
{% load custom_css %}
{% custom_css_static 'allianceauth/custom-styles.css' %}

View File

@@ -0,0 +1,3 @@
"""
Init file for custom_css templatetags
"""

View File

@@ -0,0 +1,48 @@
"""
Custom template tags for custom_css app
"""
# Alliance Auth Custom CSS
from allianceauth.custom_css.models import CustomCSS
# Django
from django.conf import settings
from django.template.defaulttags import register
from django.templatetags.static import static
from django.utils.safestring import mark_safe
from pathlib import Path
@register.simple_tag
def custom_css_static(path: str) -> str:
"""
Versioned static URL
This is to make sure to break the browser cache on CSS updates.
Example: /static/allianceauth/custom-styles.css?v=1234567890
:param path:
:type path:
:return:
:rtype:
"""
try:
Path(f"{settings.STATIC_ROOT}{path}").resolve(strict=True)
except FileNotFoundError:
return ""
else:
try:
custom_css = CustomCSS.objects.get(pk=1)
except CustomCSS.DoesNotExist:
return ""
else:
custom_css_changed = custom_css.timestamp.timestamp()
custom_css_version = (
str(custom_css_changed).replace(" ", "").replace(":", "").replace("-", "")
) # remove spaces, colons, and dashes
static_url = static(path)
versioned_url = static_url + "?v=" + custom_css_version
return mark_safe(f'<link rel="stylesheet" href="{versioned_url}">')

View File

@@ -0,0 +1,38 @@
"""
Form widgets for custom_css app
"""
# Django
from django import forms
# Alliance Auth
from allianceauth.custom_css.models import CustomCSS
class CssEditorWidget(forms.Textarea):
"""
Widget for editing CSS
"""
def __init__(self, attrs=None):
default_attrs = {"class": "custom-css-editor"}
if attrs:
default_attrs.update(attrs)
super().__init__(default_attrs)
# For when we want to add some sort of syntax highlight to it, which is not that
# easy to do on a textarea field though.
# `highlight.js` is just used as an example here, and doesn't work on a textarea field.
# class Media:
# css = {
# "all": (
# "/static/custom_css/libs/highlight.js/11.10.0/styles/github.min.css",
# )
# }
# js = (
# "/static/custom_css/libs/highlight.js/11.10.0/highlight.min.js",
# "/static/custom_css/libs/highlight.js/11.10.0/languages/css.min.js",
# "/static/custom_css/javascript/custom-css.min.js",
# )

View File

@@ -10,7 +10,7 @@ from . import (
)
_BASE_URL = 'http://evemaps.dotlan.net'
_BASE_URL = 'https://evemaps.dotlan.net'
def _build_url(category: str, name: str) -> str:

View File

@@ -31,29 +31,29 @@ class TestDotlan(TestCase):
def test_alliance_url(self):
self.assertEqual(
dotlan.alliance_url('Wayne Enterprices'),
'http://evemaps.dotlan.net/alliance/Wayne_Enterprices'
'https://evemaps.dotlan.net/alliance/Wayne_Enterprices'
)
def test_corporation_url(self):
self.assertEqual(
dotlan.corporation_url('Wayne Technology'),
'http://evemaps.dotlan.net/corp/Wayne_Technology'
'https://evemaps.dotlan.net/corp/Wayne_Technology'
)
self.assertEqual(
dotlan.corporation_url('Crédit Agricole'),
'http://evemaps.dotlan.net/corp/Cr%C3%A9dit_Agricole'
'https://evemaps.dotlan.net/corp/Cr%C3%A9dit_Agricole'
)
def test_region_url(self):
self.assertEqual(
dotlan.region_url('Black Rise'),
'http://evemaps.dotlan.net/map/Black_Rise'
'https://evemaps.dotlan.net/map/Black_Rise'
)
def test_solar_system_url(self):
self.assertEqual(
dotlan.solar_system_url('Jita'),
'http://evemaps.dotlan.net/system/Jita'
'https://evemaps.dotlan.net/system/Jita'
)

View File

@@ -1,4 +1,5 @@
import logging
from random import randint
from celery import shared_task
@@ -9,7 +10,8 @@ from . import providers
logger = logging.getLogger(__name__)
TASK_PRIORITY = 7
CHUNK_SIZE = 500
CHARACTER_AFFILIATION_CHUNK_SIZE = 500
EVEONLINE_TASK_JITTER = 600
def chunks(lst, n):
@@ -19,13 +21,13 @@ def chunks(lst, n):
@shared_task
def update_corp(corp_id):
def update_corp(corp_id: int) -> None:
"""Update given corporation from ESI"""
EveCorporationInfo.objects.update_corporation(corp_id)
@shared_task
def update_alliance(alliance_id):
def update_alliance(alliance_id: int) -> None:
"""Update given alliance from ESI"""
EveAllianceInfo.objects.update_alliance(alliance_id).populate_alliance()
@@ -37,23 +39,30 @@ def update_character(character_id: int) -> None:
@shared_task
def run_model_update():
def run_model_update() -> None:
"""Update all alliances, corporations and characters from ESI"""
#update existing corp models
# Queue update tasks for Known Corporation Models
for corp in EveCorporationInfo.objects.all().values('corporation_id'):
update_corp.apply_async(args=[corp['corporation_id']], priority=TASK_PRIORITY)
update_corp.apply_async(
args=[corp['corporation_id']],
priority=TASK_PRIORITY,
countdown=randint(1, EVEONLINE_TASK_JITTER))
# update existing alliance models
# Queue update tasks for Known Alliance Models
for alliance in EveAllianceInfo.objects.all().values('alliance_id'):
update_alliance.apply_async(args=[alliance['alliance_id']], priority=TASK_PRIORITY)
update_alliance.apply_async(
args=[alliance['alliance_id']],
priority=TASK_PRIORITY,
countdown=randint(1, EVEONLINE_TASK_JITTER))
# update existing character models
# Queue update tasks for Known Character Models
character_ids = EveCharacter.objects.all().values_list('character_id', flat=True)
for character_ids_chunk in chunks(character_ids, CHUNK_SIZE):
for character_ids_chunk in chunks(character_ids, CHARACTER_AFFILIATION_CHUNK_SIZE):
update_character_chunk.apply_async(
args=[character_ids_chunk], priority=TASK_PRIORITY
)
args=[character_ids_chunk],
priority=TASK_PRIORITY,
countdown=randint(1, EVEONLINE_TASK_JITTER))
@shared_task
@@ -68,8 +77,9 @@ def update_character_chunk(character_ids_chunk: list):
logger.info("Failed to bulk update characters. Attempting single updates")
for character_id in character_ids_chunk:
update_character.apply_async(
args=[character_id], priority=TASK_PRIORITY
)
args=[character_id],
priority=TASK_PRIORITY,
countdown=randint(1, EVEONLINE_TASK_JITTER))
return
affiliations = {
@@ -107,5 +117,5 @@ def update_character_chunk(character_ids_chunk: list):
if corp_changed or alliance_changed or name_changed:
update_character.apply_async(
args=[character.get('character_id')], priority=TASK_PRIORITY
)
args=[character.get('character_id')],
priority=TASK_PRIORITY)

View File

@@ -723,5 +723,5 @@ class TestEveSwaggerProvider(TestCase):
my_client = my_provider.client
operation = my_client.Universe.get_universe_factions()
self.assertEqual(
operation.future.request.headers['User-Agent'], 'allianceauth v1.0.0'
operation.future.request.headers['User-Agent'], 'allianceauth v1.0.0 dummy@example.net'
)

View File

@@ -84,7 +84,7 @@ class TestUpdateTasks(TestCase):
@override_settings(CELERY_ALWAYS_EAGER=True)
@patch('allianceauth.eveonline.providers.esi_client_factory')
@patch('allianceauth.eveonline.tasks.providers')
@patch('allianceauth.eveonline.tasks.CHUNK_SIZE', 2)
@patch('allianceauth.eveonline.tasks.CHARACTER_AFFILIATION_CHUNK_SIZE', 2)
class TestRunModelUpdate(TransactionTestCase):
def test_should_run_updates(self, mock_providers, mock_esi_client_factory):
# given
@@ -139,7 +139,7 @@ class TestRunModelUpdate(TransactionTestCase):
@patch('allianceauth.eveonline.tasks.update_character', wraps=update_character)
@patch('allianceauth.eveonline.providers.esi_client_factory')
@patch('allianceauth.eveonline.tasks.providers')
@patch('allianceauth.eveonline.tasks.CHUNK_SIZE', 2)
@patch('allianceauth.eveonline.tasks.CHARACTER_AFFILIATION_CHUNK_SIZE', 2)
class TestUpdateCharacterChunk(TestCase):
@staticmethod
def _updated_character_ids(spy_update_character) -> set:

View File

@@ -13,6 +13,15 @@
}
}
/* Image overflow fix
------------------------------------------------------------------------------------- */
@media all {
img {
max-width: 100%;
height: auto;
}
}
/* Side Navigation
------------------------------------------------------------------------------------- */
@media all {
@@ -40,14 +49,14 @@
}
/* Chevron icons */
#sidebar-menu [data-bs-toggle="collapse"] > i.fa-chevron-down,
#sidebar-menu [data-bs-toggle="collapse"].collapsed > i.fa-chevron-right {
#sidebar-menu span[data-bs-toggle="collapse"][aria-expanded="true"] > i.fa-chevron-down,
#sidebar-menu span[data-bs-toggle="collapse"][aria-expanded="false"] > i.fa-chevron-right {
display: block;
width: 16px;
}
#sidebar-menu [data-bs-toggle="collapse"] > i.fa-chevron-right,
#sidebar-menu [data-bs-toggle="collapse"].collapsed > i.fa-chevron-down {
#sidebar-menu span[data-bs-toggle="collapse"][aria-expanded="true"] > i.fa-chevron-right,
#sidebar-menu span[data-bs-toggle="collapse"][aria-expanded="false"] > i.fa-chevron-down {
display: none;
}
}

View File

@@ -0,0 +1,8 @@
{#Usage:#}
{# {% include "framework/dashboard/widget-title.html" with title="Foobar" %}#}
<div class="text-center">
<h4 class="ms-auto me-auto mb-3">
{{ title }}
</h4>
</div>

View File

@@ -49,7 +49,7 @@ class GroupsMenuItem(MenuItemHook):
MenuItemHook.__init__(
self,
text=_("Groups"),
classes="fa-solid fa-user",
classes="fa-solid fa-users",
url_name="groupmanagement:groups",
order=25,
navactive=[

View File

@@ -1,5 +1,6 @@
{% extends "allianceauth/base-bs5.html" %}
{% load aa_i18n %}
{% load static %}
{% load i18n %}
{% load navactive %}
@@ -73,6 +74,8 @@
{% include 'bundles/moment-js.html' with locale=True %}
{% include 'bundles/filterdropdown-js.html' %}
{% get_datatables_language_static LANGUAGE_CODE as DT_LANG_PATH %}
<script>
$.fn.dataTable.moment = (format, locale) => {
const types = $.fn.dataTable.ext.type;
@@ -94,6 +97,7 @@
$.fn.dataTable.moment('YYYY-MMM-D, HH:mm');
$('#log-entries').DataTable({
language: {url: '{{ DT_LANG_PATH }}'},
order: [[0, 'desc'], [1, 'asc']],
filterDropDown:
{

View File

@@ -1,6 +1,7 @@
{% extends "allianceauth/base-bs5.html" %}
{% load static %}
{% load aa_i18n %}
{% load i18n %}
{% load evelinks %}
{% load navactive %}
@@ -86,9 +87,12 @@
{% block extra_javascript %}
{% include 'bundles/datatables-js-bs5.html' %}
{% get_datatables_language_static LANGUAGE_CODE as DT_LANG_PATH %}
<script>
$(document).ready(() => {
$('#tab_group_members').DataTable({
language: {url: '{{ DT_LANG_PATH }}'},
order: [[0, "asc"]],
columnDefs: [
{

View File

@@ -1,6 +1,7 @@
{% extends "allianceauth/base-bs5.html" %}
{% load static %}
{% load aa_i18n %}
{% load i18n %}
{% block page_title %}
@@ -56,7 +57,7 @@
{% endif %}
{% endfor %}
{% endif %}
{% if g.group.authgroup.group_leaders.all.count %}
{% if g.group.authgroup.group_leader_groups.all.count %}
{% for group in g.group.authgroup.group_leader_groups.all %}
<span class="my-1 me-1 badge bg-secondary">{{group.name}}</span>
{% endfor %}
@@ -103,9 +104,13 @@
{% block extra_javascript %}
{% include 'bundles/datatables-js-bs5.html' %}
{% get_datatables_language_static LANGUAGE_CODE as DT_LANG_PATH %}
<script>
$(document).ready(() => {
$('#groupsTable').DataTable();
$('#groupsTable').DataTable({
language: {url: '{{ DT_LANG_PATH }}'},
});
});
</script>
{% endblock %}

Binary file not shown.

View File

@@ -11,14 +11,14 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-05-12 19:15+1000\n"
"POT-Creation-Date: 2024-09-13 19:57+1000\n"
"PO-Revision-Date: 2023-11-08 13:50+0000\n"
"Last-Translator: Tomas Skarecky <t.skarecky@gmail.com>, 2024\n"
"Language-Team: Czech (https://app.transifex.com/alliance-auth/teams/107430/cs/)\n"
"Language-Team: Czech (Czech Republic) (https://app.transifex.com/alliance-auth/teams/107430/cs_CZ/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: cs\n"
"Language: cs_CZ\n"
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n"
#: allianceauth/analytics/models.py:26
@@ -54,66 +54,90 @@ msgstr ""
"přístupem:%s"
#: allianceauth/authentication/models.py:71
#: allianceauth/project_template/project_name/settings/base.py:99
msgid "English"
msgstr "Angličtina"
#: allianceauth/authentication/models.py:72
msgid "Czech"
msgstr ""
#: allianceauth/authentication/models.py:73
#: allianceauth/project_template/project_name/settings/base.py:101
msgid "German"
msgstr "Němčina"
#: allianceauth/authentication/models.py:73
#: allianceauth/authentication/models.py:74
#: allianceauth/project_template/project_name/settings/base.py:102
msgid "Spanish"
msgstr "Španělština"
#: allianceauth/authentication/models.py:74
msgid "Chinese Simplified"
msgstr "Zjednodušená čínština"
#: allianceauth/authentication/models.py:75
msgid "Russian"
msgstr "Ruština"
#: allianceauth/authentication/models.py:76
msgid "Korean"
msgstr "Korejština"
#: allianceauth/authentication/models.py:77
msgid "French"
msgstr "Francouzština"
#: allianceauth/authentication/models.py:78
msgid "Japanese"
msgstr "Japonština"
#: allianceauth/authentication/models.py:79
#: allianceauth/project_template/project_name/settings/base.py:103
msgid "Italian"
msgstr "Italština"
#: allianceauth/authentication/models.py:76
#: allianceauth/project_template/project_name/settings/base.py:104
msgid "Japanese"
msgstr "Japonština"
#: allianceauth/authentication/models.py:77
#: allianceauth/project_template/project_name/settings/base.py:105
msgid "Korean"
msgstr "Korejština"
#: allianceauth/authentication/models.py:78
#: allianceauth/project_template/project_name/settings/base.py:106
msgid "French"
msgstr "Francouzština"
#: allianceauth/authentication/models.py:79
#: allianceauth/project_template/project_name/settings/base.py:109
msgid "Russian"
msgstr "Ruština"
#: allianceauth/authentication/models.py:80
#: allianceauth/project_template/project_name/settings/base.py:107
msgid "Dutch"
msgstr ""
#: allianceauth/authentication/models.py:81
#: allianceauth/project_template/project_name/settings/base.py:108
msgid "Polish"
msgstr ""
#: allianceauth/authentication/models.py:82
#: allianceauth/project_template/project_name/settings/base.py:110
msgid "Ukrainian"
msgstr "Ukrajinština"
#: allianceauth/authentication/models.py:96
#: allianceauth/authentication/models.py:83
#: allianceauth/project_template/project_name/settings/base.py:111
msgid "Simplified Chinese"
msgstr ""
#: allianceauth/authentication/models.py:99
#: allianceauth/menu/templates/menu/menu-user.html:42
msgid "Language"
msgstr "Jazyk"
#: allianceauth/authentication/models.py:101
#: allianceauth/authentication/models.py:104
#: allianceauth/templates/allianceauth/night-toggle.html:6
msgid "Night Mode"
msgstr "Noční režim"
#: allianceauth/authentication/models.py:105
#: allianceauth/authentication/models.py:108
#: allianceauth/menu/templates/menu/menu-user.html:46
msgid "Theme"
msgstr "Motiv"
#: allianceauth/authentication/models.py:122
#: allianceauth/authentication/models.py:125
#, python-format
msgid "State changed to: %s"
msgstr "Status změněn na: %s"
#: allianceauth/authentication/models.py:123
#: allianceauth/authentication/models.py:126
#, python-format
msgid "Your user's state is now: %(state)s"
msgstr "Váš uživatelský status je nyní: %(state)s"
@@ -125,27 +149,27 @@ msgstr "Váš uživatelský status je nyní: %(state)s"
msgid "Dashboard"
msgstr ""
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:7
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:5
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticscorpview.html:33
#: allianceauth/hrapplications/templates/hrapplications/view.html:54
msgid "Characters"
msgstr "Postavy"
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:13
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:14
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:11
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:12
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:4
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:6
msgid "Add Character"
msgstr "Přidat postavu"
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:16
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:17
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:14
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:15
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:10
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:12
msgid "Change Main"
msgstr "Změnit postavu"
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:24
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:22
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:89
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:23
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:31
@@ -154,12 +178,12 @@ msgstr "Změnit postavu"
msgid "Name"
msgstr "Jméno"
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:25
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:23
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticsview.html:33
msgid "Corp"
msgstr "Korporace"
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:26
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:24
#: allianceauth/corputils/templates/corputils/corpstats.html:125
#: allianceauth/hrapplications/templates/hrapplications/view.html:63
msgid "Alliance"
@@ -169,7 +193,7 @@ msgstr "Aliance"
msgid "Membership"
msgstr "Členství"
#: allianceauth/authentication/templates/authentication/dashboard_groups.html:8
#: allianceauth/authentication/templates/authentication/dashboard_groups.html:10
msgid "State:"
msgstr "Stav:"
@@ -409,6 +433,19 @@ msgstr "Vybraná korporace již má statistický modul"
msgid "Failed to gather corporation statistics with selected token."
msgstr "Nepodařilo se získat statistiky korporace pomocí vybraného tokenu."
#: allianceauth/custom_css/apps.py:13 allianceauth/custom_css/models.py:36
#: allianceauth/custom_css/models.py:37 allianceauth/custom_css/models.py:47
msgid "Custom CSS"
msgstr ""
#: allianceauth/custom_css/models.py:25
msgid "Your custom CSS"
msgstr ""
#: allianceauth/custom_css/models.py:26
msgid "This CSS will be added to the site after the default CSS."
msgstr ""
#: allianceauth/fleetactivitytracking/auth_hooks.py:10
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/characternotexisting.html:10
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkcreate.html:11
@@ -501,8 +538,8 @@ msgstr "Uživatel"
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:37
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:42
#: allianceauth/optimer/form.py:13 allianceauth/timerboard/form.py:59
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:16
#: allianceauth/optimer/form.py:13 allianceauth/timerboard/form.py:37
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:17
#: allianceauth/timerboard/templates/timerboard/timertable.html:9
msgid "System"
msgstr "Systém"
@@ -826,7 +863,7 @@ msgstr ""
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31
#: allianceauth/optimer/templates/optimer/dashboard.ops.html:15
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:15
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:16
msgid "Type"
msgstr ""
@@ -918,7 +955,7 @@ msgid "Hidden"
msgstr ""
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:45
#: allianceauth/templates/allianceauth/admin-status/overview.html:19
#: allianceauth/templates/allianceauth/admin-status/overview.html:16
msgid "Open"
msgstr ""
@@ -1394,16 +1431,16 @@ msgstr ""
msgid "Super User"
msgstr ""
#: allianceauth/menu/templates/menu/menu-user.html:68
#: allianceauth/menu/templates/menu/menu-user.html:70
#: allianceauth/templates/allianceauth/top-menu-admin.html:9
msgid "Admin"
msgstr ""
#: allianceauth/menu/templates/menu/menu-user.html:80
#: allianceauth/menu/templates/menu/menu-user.html:82
msgid "Sign Out"
msgstr ""
#: allianceauth/menu/templates/menu/menu-user.html:84
#: allianceauth/menu/templates/menu/menu-user.html:86
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:17
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:18
msgid "Sign In"
@@ -1553,7 +1590,7 @@ msgid "Form Up System"
msgstr ""
#: allianceauth/optimer/templates/optimer/dashboard.ops.html:17
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:17
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:18
msgid "EVE Time"
msgstr ""
@@ -1748,17 +1785,17 @@ msgid ""
msgstr ""
#: allianceauth/services/modules/discord/templates/services/discord/discord_service_ctrl.html:26
#: allianceauth/services/modules/mumble/templates/services/mumble/mumble_service_ctrl.html:20
#: allianceauth/services/modules/mumble/templates/services/mumble/mumble_service_ctrl.html:22
msgid "Activate"
msgstr ""
#: allianceauth/services/modules/discord/templates/services/discord/discord_service_ctrl.html:32
#: allianceauth/services/modules/mumble/templates/services/mumble/mumble_service_ctrl.html:32
#: allianceauth/services/modules/mumble/templates/services/mumble/mumble_service_ctrl.html:34
msgid "Reset Password"
msgstr ""
#: allianceauth/services/modules/discord/templates/services/discord/discord_service_ctrl.html:38
#: allianceauth/services/modules/mumble/templates/services/mumble/mumble_service_ctrl.html:38
#: allianceauth/services/modules/mumble/templates/services/mumble/mumble_service_ctrl.html:40
msgid "Deactivate"
msgstr ""
@@ -1839,12 +1876,12 @@ msgstr ""
msgid "Deactivated IPSuite4 account."
msgstr ""
#: allianceauth/services/modules/mumble/templates/services/mumble/mumble_service_ctrl.html:26
#: allianceauth/services/modules/mumble/templates/services/mumble/mumble_service_ctrl.html:28
#: allianceauth/services/templates/services/service_password.html:26
msgid "Set Password"
msgstr ""
#: allianceauth/services/modules/mumble/templates/services/mumble/mumble_service_ctrl.html:44
#: allianceauth/services/modules/mumble/templates/services/mumble/mumble_service_ctrl.html:46
msgid "Connect"
msgstr ""
@@ -2407,56 +2444,56 @@ msgstr ""
msgid "Your Server received an ESI error response code of "
msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:11
#: allianceauth/templates/allianceauth/admin-status/overview.html:8
msgid "Alliance Auth Notifications"
msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:21
#: allianceauth/templates/allianceauth/admin-status/overview.html:18
msgid "Closed"
msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:27
#: allianceauth/templates/allianceauth/admin-status/overview.html:24
msgid "No notifications at this time"
msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:36
#: allianceauth/templates/allianceauth/admin-status/overview.html:33
msgid "Powered by GitLab"
msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:42
#: allianceauth/templates/allianceauth/admin-status/overview.html:39
msgid "Support Discord"
msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:59
#: allianceauth/templates/allianceauth/admin-status/overview.html:63
#: allianceauth/templates/allianceauth/admin-status/overview.html:53
#: allianceauth/templates/allianceauth/admin-status/overview.html:57
msgid "Software Version"
msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:66
#: allianceauth/templates/allianceauth/admin-status/overview.html:60
msgid "Current"
msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:73
#: allianceauth/templates/allianceauth/admin-status/overview.html:67
msgid "Latest Stable"
msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:78
#: allianceauth/templates/allianceauth/admin-status/overview.html:72
msgid "Update available"
msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:86
#: allianceauth/templates/allianceauth/admin-status/overview.html:80
msgid "Latest Pre-Release"
msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:91
#: allianceauth/templates/allianceauth/admin-status/overview.html:85
msgid "Pre-Release available"
msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:102
#: allianceauth/templates/allianceauth/admin-status/overview.html:95
msgid "Task Queue"
msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:107
#: allianceauth/templates/allianceauth/admin-status/overview.html:100
#, python-format
msgid ""
"\n"
@@ -2464,11 +2501,11 @@ msgid ""
" "
msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:123
#: allianceauth/templates/allianceauth/admin-status/overview.html:116
msgid "running"
msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:124
#: allianceauth/templates/allianceauth/admin-status/overview.html:117
msgid "queued"
msgstr ""
@@ -2497,114 +2534,216 @@ msgstr ""
msgid "Select Theme"
msgstr ""
#: allianceauth/timerboard/form.py:53
#: allianceauth/timerboard/templates/timerboard/timertable.html:172
msgid "Other"
msgstr ""
#: allianceauth/timerboard/form.py:54
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:38
#: allianceauth/timerboard/templates/timerboard/timertable.html:44
msgid "Friendly"
msgstr ""
#: allianceauth/timerboard/form.py:55
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:33
#: allianceauth/timerboard/templates/timerboard/timertable.html:38
msgid "Hostile"
msgstr ""
#: allianceauth/timerboard/form.py:56
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:43
#: allianceauth/timerboard/templates/timerboard/timertable.html:50
msgid "Neutral"
msgstr ""
#: allianceauth/timerboard/form.py:58
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:13
#: allianceauth/timerboard/form.py:36
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:14
#: allianceauth/timerboard/templates/timerboard/timertable.html:7
msgid "Details"
msgstr ""
#: allianceauth/timerboard/form.py:60
#: allianceauth/timerboard/form.py:38
msgid "Planet/Moon"
msgstr ""
#: allianceauth/timerboard/form.py:61
#: allianceauth/timerboard/form.py:39
msgid "Structure Type"
msgstr ""
#: allianceauth/timerboard/form.py:62
#: allianceauth/timerboard/form.py:40
msgid "Timer Type"
msgstr ""
#: allianceauth/timerboard/form.py:63
#: allianceauth/timerboard/form.py:41
#: allianceauth/timerboard/templates/timerboard/timertable.html:8
msgid "Objective"
msgstr ""
#: allianceauth/timerboard/form.py:64
#: allianceauth/timerboard/form.py:42
msgid "Absolute Timer"
msgstr ""
#: allianceauth/timerboard/form.py:65
#: allianceauth/timerboard/form.py:43
msgid "Date and Time"
msgstr ""
#: allianceauth/timerboard/form.py:66
#: allianceauth/timerboard/form.py:44
msgid "Days Remaining"
msgstr ""
#: allianceauth/timerboard/form.py:67
#: allianceauth/timerboard/form.py:45
msgid "Hours Remaining"
msgstr ""
#: allianceauth/timerboard/form.py:69
#: allianceauth/timerboard/form.py:47
msgid "Minutes Remaining"
msgstr ""
#: allianceauth/timerboard/form.py:71
#: allianceauth/timerboard/form.py:48
msgid "Important"
msgstr ""
#: allianceauth/timerboard/form.py:72
#: allianceauth/timerboard/form.py:49
msgid "Corp-Restricted"
msgstr ""
#: allianceauth/timerboard/models.py:14
msgid "Not Specified"
msgstr ""
#: allianceauth/timerboard/models.py:15
msgid "Shield"
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:39
#: allianceauth/timerboard/templates/timerboard/timertable.html:36
msgid "Friendly"
msgstr ""
#: allianceauth/timerboard/models.py:16
msgid "Armor"
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:34
#: allianceauth/timerboard/templates/timerboard/timertable.html:34
msgid "Hostile"
msgstr ""
#: allianceauth/timerboard/models.py:17
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:44
#: allianceauth/timerboard/templates/timerboard/timertable.html:38
msgid "Neutral"
msgstr ""
#: allianceauth/timerboard/models.py:24
#: allianceauth/timerboard/templates/timerboard/timertable.html:48
msgid "POCO"
msgstr ""
#: allianceauth/timerboard/models.py:25
#: allianceauth/timerboard/templates/timerboard/timertable.html:50
msgid "Orbital Skyhook"
msgstr ""
#: allianceauth/timerboard/models.py:26
#: allianceauth/timerboard/templates/timerboard/timertable.html:52
msgid "I-HUB"
msgstr ""
#: allianceauth/timerboard/models.py:27
#: allianceauth/timerboard/templates/timerboard/timertable.html:55
msgid "TCU"
msgstr ""
#: allianceauth/timerboard/models.py:28
#: allianceauth/timerboard/templates/timerboard/timertable.html:57
msgid "POS [S]"
msgstr ""
#: allianceauth/timerboard/models.py:29
#: allianceauth/timerboard/templates/timerboard/timertable.html:59
msgid "POS [M]"
msgstr ""
#: allianceauth/timerboard/models.py:30
#: allianceauth/timerboard/templates/timerboard/timertable.html:61
msgid "POS [L]"
msgstr ""
#: allianceauth/timerboard/models.py:31
#: allianceauth/timerboard/templates/timerboard/timertable.html:63
msgid "Astrahus"
msgstr ""
#: allianceauth/timerboard/models.py:32
#: allianceauth/timerboard/templates/timerboard/timertable.html:65
msgid "Fortizar"
msgstr ""
#: allianceauth/timerboard/models.py:33
#: allianceauth/timerboard/templates/timerboard/timertable.html:67
msgid "Keepstar"
msgstr ""
#: allianceauth/timerboard/models.py:34
#: allianceauth/timerboard/templates/timerboard/timertable.html:69
msgid "Raitaru"
msgstr ""
#: allianceauth/timerboard/models.py:35
#: allianceauth/timerboard/templates/timerboard/timertable.html:71
msgid "Azbel"
msgstr ""
#: allianceauth/timerboard/models.py:36
#: allianceauth/timerboard/templates/timerboard/timertable.html:73
msgid "Sotiyo"
msgstr ""
#: allianceauth/timerboard/models.py:37
#: allianceauth/timerboard/templates/timerboard/timertable.html:75
msgid "Athanor"
msgstr ""
#: allianceauth/timerboard/models.py:38
#: allianceauth/timerboard/templates/timerboard/timertable.html:77
msgid "Tatara"
msgstr ""
#: allianceauth/timerboard/models.py:39
msgid "Pharolux Cyno Beacon"
msgstr ""
#: allianceauth/timerboard/models.py:40
msgid "Tenebrex Cyno Jammer"
msgstr ""
#: allianceauth/timerboard/models.py:41
#: allianceauth/timerboard/templates/timerboard/timertable.html:83
msgid "Ansiblex Jump Gate"
msgstr ""
#: allianceauth/timerboard/models.py:42
#: allianceauth/timerboard/templates/timerboard/timertable.html:85
msgid "Moon Mining Cycle"
msgstr ""
#: allianceauth/timerboard/models.py:43
#: allianceauth/timerboard/templates/timerboard/timertable.html:87
msgid "Metenox Moon Drill"
msgstr ""
#: allianceauth/timerboard/models.py:44
#: allianceauth/timerboard/templates/timerboard/timertable.html:89
msgid "Other"
msgstr ""
#: allianceauth/timerboard/models.py:51
msgid "Not Specified"
msgstr ""
#: allianceauth/timerboard/models.py:52
msgid "Shield"
msgstr ""
#: allianceauth/timerboard/models.py:53
msgid "Armor"
msgstr ""
#: allianceauth/timerboard/models.py:54
msgid "Hull"
msgstr ""
#: allianceauth/timerboard/models.py:18
#: allianceauth/timerboard/models.py:55
msgid "Final"
msgstr ""
#: allianceauth/timerboard/models.py:19
#: allianceauth/timerboard/models.py:56
msgid "Anchoring"
msgstr ""
#: allianceauth/timerboard/models.py:20
#: allianceauth/timerboard/models.py:57
msgid "Unanchoring"
msgstr ""
#: allianceauth/timerboard/models.py:58
msgid "Abandoned"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:7
#: allianceauth/timerboard/templates/timerboard/view.html:53
msgid "Upcoming Timers"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:14
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:15
msgid "Timer"
msgstr ""
@@ -2648,78 +2787,14 @@ msgstr ""
msgid "Structure"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/timertable.html:64
msgid "POCO"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/timertable.html:70
msgid "I-HUB"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/timertable.html:76
msgid "TCU"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/timertable.html:82
msgid "POS [S]"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/timertable.html:88
msgid "POS [M]"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/timertable.html:94
msgid "POS [L]"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/timertable.html:100
msgid "Astrahus"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/timertable.html:106
msgid "Fortizar"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/timertable.html:112
msgid "Keepstar"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/timertable.html:118
msgid "Raitaru"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/timertable.html:124
msgid "Azbel"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/timertable.html:130
msgid "Sotiyo"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/timertable.html:136
msgid "Athanor"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/timertable.html:142
msgid "Tatara"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/timertable.html:148
#: allianceauth/timerboard/templates/timerboard/timertable.html:79
msgid "Cyno Beacon"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/timertable.html:154
#: allianceauth/timerboard/templates/timerboard/timertable.html:81
msgid "Cyno Jammer"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/timertable.html:160
msgid "Ansiblex Jump Gate"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/timertable.html:166
msgid "Moon Mining Cycle"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/view.html:9
msgid "Structure Timer Management"
msgstr ""

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@@ -12,14 +12,14 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-05-12 19:15+1000\n"
"POT-Creation-Date: 2024-09-13 19:57+1000\n"
"PO-Revision-Date: 2023-11-08 13:50+0000\n"
"Last-Translator: Agent Fuse, 2024\n"
"Language-Team: Dutch (https://app.transifex.com/alliance-auth/teams/107430/nl/)\n"
"Language-Team: Dutch (Netherlands) (https://app.transifex.com/alliance-auth/teams/107430/nl_NL/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: nl\n"
"Language: nl_NL\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: allianceauth/analytics/models.py:26
@@ -62,66 +62,90 @@ msgstr ""
"Je bent niet gemachtigd om de volgende beperkte groepen te verwijderen: %s"
#: allianceauth/authentication/models.py:71
#: allianceauth/project_template/project_name/settings/base.py:99
msgid "English"
msgstr "Engels"
#: allianceauth/authentication/models.py:72
msgid "Czech"
msgstr ""
#: allianceauth/authentication/models.py:73
#: allianceauth/project_template/project_name/settings/base.py:101
msgid "German"
msgstr "Duits"
#: allianceauth/authentication/models.py:73
#: allianceauth/authentication/models.py:74
#: allianceauth/project_template/project_name/settings/base.py:102
msgid "Spanish"
msgstr "Spaans"
#: allianceauth/authentication/models.py:74
msgid "Chinese Simplified"
msgstr "Vereenvoudigd Chinees"
#: allianceauth/authentication/models.py:75
msgid "Russian"
msgstr "Russisch"
#: allianceauth/authentication/models.py:76
msgid "Korean"
msgstr "Koreaans"
#: allianceauth/authentication/models.py:77
msgid "French"
msgstr "Frans"
#: allianceauth/authentication/models.py:78
msgid "Japanese"
msgstr "Japans"
#: allianceauth/authentication/models.py:79
#: allianceauth/project_template/project_name/settings/base.py:103
msgid "Italian"
msgstr "Italiaans"
#: allianceauth/authentication/models.py:76
#: allianceauth/project_template/project_name/settings/base.py:104
msgid "Japanese"
msgstr "Japans"
#: allianceauth/authentication/models.py:77
#: allianceauth/project_template/project_name/settings/base.py:105
msgid "Korean"
msgstr "Koreaans"
#: allianceauth/authentication/models.py:78
#: allianceauth/project_template/project_name/settings/base.py:106
msgid "French"
msgstr "Frans"
#: allianceauth/authentication/models.py:79
#: allianceauth/project_template/project_name/settings/base.py:109
msgid "Russian"
msgstr "Russisch"
#: allianceauth/authentication/models.py:80
#: allianceauth/project_template/project_name/settings/base.py:107
msgid "Dutch"
msgstr ""
#: allianceauth/authentication/models.py:81
#: allianceauth/project_template/project_name/settings/base.py:108
msgid "Polish"
msgstr ""
#: allianceauth/authentication/models.py:82
#: allianceauth/project_template/project_name/settings/base.py:110
msgid "Ukrainian"
msgstr "Oekraïens"
#: allianceauth/authentication/models.py:96
#: allianceauth/authentication/models.py:83
#: allianceauth/project_template/project_name/settings/base.py:111
msgid "Simplified Chinese"
msgstr ""
#: allianceauth/authentication/models.py:99
#: allianceauth/menu/templates/menu/menu-user.html:42
msgid "Language"
msgstr "Taal"
#: allianceauth/authentication/models.py:101
#: allianceauth/authentication/models.py:104
#: allianceauth/templates/allianceauth/night-toggle.html:6
msgid "Night Mode"
msgstr "Nachtstand"
#: allianceauth/authentication/models.py:105
#: allianceauth/authentication/models.py:108
#: allianceauth/menu/templates/menu/menu-user.html:46
msgid "Theme"
msgstr "Thema"
#: allianceauth/authentication/models.py:122
#: allianceauth/authentication/models.py:125
#, python-format
msgid "State changed to: %s"
msgstr "State gewijzigd naar: %s"
#: allianceauth/authentication/models.py:123
#: allianceauth/authentication/models.py:126
#, python-format
msgid "Your user's state is now: %(state)s"
msgstr "De gebruikers staat is nu: %(state)s"
@@ -133,27 +157,27 @@ msgstr "De gebruikers staat is nu: %(state)s"
msgid "Dashboard"
msgstr "Dashboard"
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:7
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:5
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticscorpview.html:33
#: allianceauth/hrapplications/templates/hrapplications/view.html:54
msgid "Characters"
msgstr "Karakter"
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:13
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:14
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:11
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:12
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:4
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:6
msgid "Add Character"
msgstr "Personages toevoegen"
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:16
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:17
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:14
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:15
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:10
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:12
msgid "Change Main"
msgstr "Verander Main"
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:24
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:22
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:89
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:23
#: allianceauth/groupmanagement/templates/groupmanagement/groups.html:31
@@ -162,12 +186,12 @@ msgstr "Verander Main"
msgid "Name"
msgstr "Naam"
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:25
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:23
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkstatisticsview.html:33
msgid "Corp"
msgstr "Corp"
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:26
#: allianceauth/authentication/templates/authentication/dashboard_characters.html:24
#: allianceauth/corputils/templates/corputils/corpstats.html:125
#: allianceauth/hrapplications/templates/hrapplications/view.html:63
msgid "Alliance"
@@ -177,7 +201,7 @@ msgstr "Alliantie"
msgid "Membership"
msgstr "Lidmaatschap"
#: allianceauth/authentication/templates/authentication/dashboard_groups.html:8
#: allianceauth/authentication/templates/authentication/dashboard_groups.html:10
msgid "State:"
msgstr "Status:"
@@ -422,6 +446,19 @@ msgstr ""
"Het is niet gelukt om bedrijfsstatistieken te verzamelen met het "
"geselecteerde token."
#: allianceauth/custom_css/apps.py:13 allianceauth/custom_css/models.py:36
#: allianceauth/custom_css/models.py:37 allianceauth/custom_css/models.py:47
msgid "Custom CSS"
msgstr ""
#: allianceauth/custom_css/models.py:25
msgid "Your custom CSS"
msgstr ""
#: allianceauth/custom_css/models.py:26
msgid "This CSS will be added to the site after the default CSS."
msgstr ""
#: allianceauth/fleetactivitytracking/auth_hooks.py:10
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/characternotexisting.html:10
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkcreate.html:11
@@ -514,8 +551,8 @@ msgstr "Gebruiker"
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkmodify.html:37
#: allianceauth/fleetactivitytracking/templates/fleetactivitytracking/fatlinkview.html:42
#: allianceauth/optimer/form.py:13 allianceauth/timerboard/form.py:59
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:16
#: allianceauth/optimer/form.py:13 allianceauth/timerboard/form.py:37
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:17
#: allianceauth/timerboard/templates/timerboard/timertable.html:9
msgid "System"
msgstr "Systeem"
@@ -837,7 +874,7 @@ msgstr "Aanvrager"
#: allianceauth/groupmanagement/templates/groupmanagement/audit.html:31
#: allianceauth/optimer/templates/optimer/dashboard.ops.html:15
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:15
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:16
msgid "Type"
msgstr "Type"
@@ -929,7 +966,7 @@ msgid "Hidden"
msgstr "verborgen"
#: allianceauth/groupmanagement/templates/groupmanagement/groupmembership.html:45
#: allianceauth/templates/allianceauth/admin-status/overview.html:19
#: allianceauth/templates/allianceauth/admin-status/overview.html:16
msgid "Open"
msgstr "Open"
@@ -1405,16 +1442,16 @@ msgstr "Notificatie"
msgid "Super User"
msgstr ""
#: allianceauth/menu/templates/menu/menu-user.html:68
#: allianceauth/menu/templates/menu/menu-user.html:70
#: allianceauth/templates/allianceauth/top-menu-admin.html:9
msgid "Admin"
msgstr "Administrator"
#: allianceauth/menu/templates/menu/menu-user.html:80
#: allianceauth/menu/templates/menu/menu-user.html:82
msgid "Sign Out"
msgstr ""
#: allianceauth/menu/templates/menu/menu-user.html:84
#: allianceauth/menu/templates/menu/menu-user.html:86
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:17
#: allianceauth/templates/allianceauth/top-menu-rh-default.html:18
msgid "Sign In"
@@ -1564,7 +1601,7 @@ msgid "Form Up System"
msgstr ""
#: allianceauth/optimer/templates/optimer/dashboard.ops.html:17
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:17
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:18
msgid "EVE Time"
msgstr ""
@@ -1759,17 +1796,17 @@ msgid ""
msgstr ""
#: allianceauth/services/modules/discord/templates/services/discord/discord_service_ctrl.html:26
#: allianceauth/services/modules/mumble/templates/services/mumble/mumble_service_ctrl.html:20
#: allianceauth/services/modules/mumble/templates/services/mumble/mumble_service_ctrl.html:22
msgid "Activate"
msgstr ""
#: allianceauth/services/modules/discord/templates/services/discord/discord_service_ctrl.html:32
#: allianceauth/services/modules/mumble/templates/services/mumble/mumble_service_ctrl.html:32
#: allianceauth/services/modules/mumble/templates/services/mumble/mumble_service_ctrl.html:34
msgid "Reset Password"
msgstr ""
#: allianceauth/services/modules/discord/templates/services/discord/discord_service_ctrl.html:38
#: allianceauth/services/modules/mumble/templates/services/mumble/mumble_service_ctrl.html:38
#: allianceauth/services/modules/mumble/templates/services/mumble/mumble_service_ctrl.html:40
msgid "Deactivate"
msgstr ""
@@ -1850,12 +1887,12 @@ msgstr ""
msgid "Deactivated IPSuite4 account."
msgstr ""
#: allianceauth/services/modules/mumble/templates/services/mumble/mumble_service_ctrl.html:26
#: allianceauth/services/modules/mumble/templates/services/mumble/mumble_service_ctrl.html:28
#: allianceauth/services/templates/services/service_password.html:26
msgid "Set Password"
msgstr ""
#: allianceauth/services/modules/mumble/templates/services/mumble/mumble_service_ctrl.html:44
#: allianceauth/services/modules/mumble/templates/services/mumble/mumble_service_ctrl.html:46
msgid "Connect"
msgstr ""
@@ -2418,56 +2455,56 @@ msgstr ""
msgid "Your Server received an ESI error response code of "
msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:11
#: allianceauth/templates/allianceauth/admin-status/overview.html:8
msgid "Alliance Auth Notifications"
msgstr "Alliantie Authenticatie Notificaties"
#: allianceauth/templates/allianceauth/admin-status/overview.html:21
#: allianceauth/templates/allianceauth/admin-status/overview.html:18
msgid "Closed"
msgstr "Gesloten"
#: allianceauth/templates/allianceauth/admin-status/overview.html:27
#: allianceauth/templates/allianceauth/admin-status/overview.html:24
msgid "No notifications at this time"
msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:36
#: allianceauth/templates/allianceauth/admin-status/overview.html:33
msgid "Powered by GitLab"
msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:42
#: allianceauth/templates/allianceauth/admin-status/overview.html:39
msgid "Support Discord"
msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:59
#: allianceauth/templates/allianceauth/admin-status/overview.html:63
#: allianceauth/templates/allianceauth/admin-status/overview.html:53
#: allianceauth/templates/allianceauth/admin-status/overview.html:57
msgid "Software Version"
msgstr "Software Versie"
#: allianceauth/templates/allianceauth/admin-status/overview.html:66
#: allianceauth/templates/allianceauth/admin-status/overview.html:60
msgid "Current"
msgstr "Huidige"
#: allianceauth/templates/allianceauth/admin-status/overview.html:73
#: allianceauth/templates/allianceauth/admin-status/overview.html:67
msgid "Latest Stable"
msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:78
#: allianceauth/templates/allianceauth/admin-status/overview.html:72
msgid "Update available"
msgstr "Update Beschikbaar"
#: allianceauth/templates/allianceauth/admin-status/overview.html:86
#: allianceauth/templates/allianceauth/admin-status/overview.html:80
msgid "Latest Pre-Release"
msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:91
#: allianceauth/templates/allianceauth/admin-status/overview.html:85
msgid "Pre-Release available"
msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:102
#: allianceauth/templates/allianceauth/admin-status/overview.html:95
msgid "Task Queue"
msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:107
#: allianceauth/templates/allianceauth/admin-status/overview.html:100
#, python-format
msgid ""
"\n"
@@ -2475,11 +2512,11 @@ msgid ""
" "
msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:123
#: allianceauth/templates/allianceauth/admin-status/overview.html:116
msgid "running"
msgstr ""
#: allianceauth/templates/allianceauth/admin-status/overview.html:124
#: allianceauth/templates/allianceauth/admin-status/overview.html:117
msgid "queued"
msgstr ""
@@ -2508,114 +2545,216 @@ msgstr ""
msgid "Select Theme"
msgstr "Selecteer Thema"
#: allianceauth/timerboard/form.py:53
#: allianceauth/timerboard/templates/timerboard/timertable.html:172
msgid "Other"
msgstr "Andere"
#: allianceauth/timerboard/form.py:54
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:38
#: allianceauth/timerboard/templates/timerboard/timertable.html:44
msgid "Friendly"
msgstr "Vriendelijk"
#: allianceauth/timerboard/form.py:55
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:33
#: allianceauth/timerboard/templates/timerboard/timertable.html:38
msgid "Hostile"
msgstr "Vijandig"
#: allianceauth/timerboard/form.py:56
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:43
#: allianceauth/timerboard/templates/timerboard/timertable.html:50
msgid "Neutral"
msgstr "Neutraal"
#: allianceauth/timerboard/form.py:58
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:13
#: allianceauth/timerboard/form.py:36
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:14
#: allianceauth/timerboard/templates/timerboard/timertable.html:7
msgid "Details"
msgstr "Details"
#: allianceauth/timerboard/form.py:60
#: allianceauth/timerboard/form.py:38
msgid "Planet/Moon"
msgstr "Planeet/Maan"
#: allianceauth/timerboard/form.py:61
#: allianceauth/timerboard/form.py:39
msgid "Structure Type"
msgstr ""
#: allianceauth/timerboard/form.py:62
#: allianceauth/timerboard/form.py:40
msgid "Timer Type"
msgstr "Timer Type"
#: allianceauth/timerboard/form.py:63
#: allianceauth/timerboard/form.py:41
#: allianceauth/timerboard/templates/timerboard/timertable.html:8
msgid "Objective"
msgstr "Doel"
#: allianceauth/timerboard/form.py:64
#: allianceauth/timerboard/form.py:42
msgid "Absolute Timer"
msgstr "Absolute Timer"
#: allianceauth/timerboard/form.py:65
#: allianceauth/timerboard/form.py:43
msgid "Date and Time"
msgstr "Datum en Tijd"
#: allianceauth/timerboard/form.py:66
#: allianceauth/timerboard/form.py:44
msgid "Days Remaining"
msgstr "Resterende Dagen"
#: allianceauth/timerboard/form.py:67
#: allianceauth/timerboard/form.py:45
msgid "Hours Remaining"
msgstr "Resterende Uren"
#: allianceauth/timerboard/form.py:69
#: allianceauth/timerboard/form.py:47
msgid "Minutes Remaining"
msgstr "Resterende Minuten"
#: allianceauth/timerboard/form.py:71
#: allianceauth/timerboard/form.py:48
msgid "Important"
msgstr "Belangrijk"
#: allianceauth/timerboard/form.py:72
#: allianceauth/timerboard/form.py:49
msgid "Corp-Restricted"
msgstr ""
#: allianceauth/timerboard/models.py:14
#: allianceauth/timerboard/models.py:15
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:39
#: allianceauth/timerboard/templates/timerboard/timertable.html:36
msgid "Friendly"
msgstr "Vriendelijk"
#: allianceauth/timerboard/models.py:16
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:34
#: allianceauth/timerboard/templates/timerboard/timertable.html:34
msgid "Hostile"
msgstr "Vijandig"
#: allianceauth/timerboard/models.py:17
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:44
#: allianceauth/timerboard/templates/timerboard/timertable.html:38
msgid "Neutral"
msgstr "Neutraal"
#: allianceauth/timerboard/models.py:24
#: allianceauth/timerboard/templates/timerboard/timertable.html:48
msgid "POCO"
msgstr ""
#: allianceauth/timerboard/models.py:25
#: allianceauth/timerboard/templates/timerboard/timertable.html:50
msgid "Orbital Skyhook"
msgstr ""
#: allianceauth/timerboard/models.py:26
#: allianceauth/timerboard/templates/timerboard/timertable.html:52
msgid "I-HUB"
msgstr ""
#: allianceauth/timerboard/models.py:27
#: allianceauth/timerboard/templates/timerboard/timertable.html:55
msgid "TCU"
msgstr ""
#: allianceauth/timerboard/models.py:28
#: allianceauth/timerboard/templates/timerboard/timertable.html:57
msgid "POS [S]"
msgstr ""
#: allianceauth/timerboard/models.py:29
#: allianceauth/timerboard/templates/timerboard/timertable.html:59
msgid "POS [M]"
msgstr ""
#: allianceauth/timerboard/models.py:30
#: allianceauth/timerboard/templates/timerboard/timertable.html:61
msgid "POS [L]"
msgstr ""
#: allianceauth/timerboard/models.py:31
#: allianceauth/timerboard/templates/timerboard/timertable.html:63
msgid "Astrahus"
msgstr "Astrahus"
#: allianceauth/timerboard/models.py:32
#: allianceauth/timerboard/templates/timerboard/timertable.html:65
msgid "Fortizar"
msgstr "Fortizar"
#: allianceauth/timerboard/models.py:33
#: allianceauth/timerboard/templates/timerboard/timertable.html:67
msgid "Keepstar"
msgstr "Keepstar"
#: allianceauth/timerboard/models.py:34
#: allianceauth/timerboard/templates/timerboard/timertable.html:69
msgid "Raitaru"
msgstr "Raitaru"
#: allianceauth/timerboard/models.py:35
#: allianceauth/timerboard/templates/timerboard/timertable.html:71
msgid "Azbel"
msgstr "Sotiyo"
#: allianceauth/timerboard/models.py:36
#: allianceauth/timerboard/templates/timerboard/timertable.html:73
msgid "Sotiyo"
msgstr "Sotiyo"
#: allianceauth/timerboard/models.py:37
#: allianceauth/timerboard/templates/timerboard/timertable.html:75
msgid "Athanor"
msgstr "Athanor"
#: allianceauth/timerboard/models.py:38
#: allianceauth/timerboard/templates/timerboard/timertable.html:77
msgid "Tatara"
msgstr "Tatara"
#: allianceauth/timerboard/models.py:39
msgid "Pharolux Cyno Beacon"
msgstr ""
#: allianceauth/timerboard/models.py:40
msgid "Tenebrex Cyno Jammer"
msgstr ""
#: allianceauth/timerboard/models.py:41
#: allianceauth/timerboard/templates/timerboard/timertable.html:83
msgid "Ansiblex Jump Gate"
msgstr "Ansiblex Jump Gate"
#: allianceauth/timerboard/models.py:42
#: allianceauth/timerboard/templates/timerboard/timertable.html:85
msgid "Moon Mining Cycle"
msgstr "Maan mijn Cyclus"
#: allianceauth/timerboard/models.py:43
#: allianceauth/timerboard/templates/timerboard/timertable.html:87
msgid "Metenox Moon Drill"
msgstr ""
#: allianceauth/timerboard/models.py:44
#: allianceauth/timerboard/templates/timerboard/timertable.html:89
msgid "Other"
msgstr "Andere"
#: allianceauth/timerboard/models.py:51
msgid "Not Specified"
msgstr "Niet gespecifieerd"
#: allianceauth/timerboard/models.py:15
#: allianceauth/timerboard/models.py:52
msgid "Shield"
msgstr "Schild"
#: allianceauth/timerboard/models.py:16
#: allianceauth/timerboard/models.py:53
msgid "Armor"
msgstr "Pantser"
#: allianceauth/timerboard/models.py:17
#: allianceauth/timerboard/models.py:54
msgid "Hull"
msgstr "Romp"
#: allianceauth/timerboard/models.py:18
#: allianceauth/timerboard/models.py:55
msgid "Final"
msgstr "Laatste"
#: allianceauth/timerboard/models.py:19
#: allianceauth/timerboard/models.py:56
msgid "Anchoring"
msgstr "Ankeren"
#: allianceauth/timerboard/models.py:20
#: allianceauth/timerboard/models.py:57
msgid "Unanchoring"
msgstr "ontankeren"
#: allianceauth/timerboard/models.py:58
msgid "Abandoned"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:7
#: allianceauth/timerboard/templates/timerboard/view.html:53
msgid "Upcoming Timers"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:14
#: allianceauth/timerboard/templates/timerboard/dashboard.timers.html:15
msgid "Timer"
msgstr "Timer"
@@ -2659,78 +2798,14 @@ msgstr ""
msgid "Structure"
msgstr "Constructie"
#: allianceauth/timerboard/templates/timerboard/timertable.html:64
msgid "POCO"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/timertable.html:70
msgid "I-HUB"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/timertable.html:76
msgid "TCU"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/timertable.html:82
msgid "POS [S]"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/timertable.html:88
msgid "POS [M]"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/timertable.html:94
msgid "POS [L]"
msgstr ""
#: allianceauth/timerboard/templates/timerboard/timertable.html:100
msgid "Astrahus"
msgstr "Astrahus"
#: allianceauth/timerboard/templates/timerboard/timertable.html:106
msgid "Fortizar"
msgstr "Fortizar"
#: allianceauth/timerboard/templates/timerboard/timertable.html:112
msgid "Keepstar"
msgstr "Keepstar"
#: allianceauth/timerboard/templates/timerboard/timertable.html:118
msgid "Raitaru"
msgstr "Raitaru"
#: allianceauth/timerboard/templates/timerboard/timertable.html:124
msgid "Azbel"
msgstr "Sotiyo"
#: allianceauth/timerboard/templates/timerboard/timertable.html:130
msgid "Sotiyo"
msgstr "Sotiyo"
#: allianceauth/timerboard/templates/timerboard/timertable.html:136
msgid "Athanor"
msgstr "Athanor"
#: allianceauth/timerboard/templates/timerboard/timertable.html:142
msgid "Tatara"
msgstr "Tatara"
#: allianceauth/timerboard/templates/timerboard/timertable.html:148
#: allianceauth/timerboard/templates/timerboard/timertable.html:79
msgid "Cyno Beacon"
msgstr "Cyno Beacon"
#: allianceauth/timerboard/templates/timerboard/timertable.html:154
#: allianceauth/timerboard/templates/timerboard/timertable.html:81
msgid "Cyno Jammer"
msgstr "Cyno Jammer"
#: allianceauth/timerboard/templates/timerboard/timertable.html:160
msgid "Ansiblex Jump Gate"
msgstr "Ansiblex Jump Gate"
#: allianceauth/timerboard/templates/timerboard/timertable.html:166
msgid "Moon Mining Cycle"
msgstr "Maan mijn Cyclus"
#: allianceauth/timerboard/templates/timerboard/view.html:9
msgid "Structure Timer Management"
msgstr ""

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -11,8 +11,8 @@
data-bs-target="#{{ item.html_id }}"
aria-expanded="false"
aria-controls=""
{% endif %}>
</i>
{% endif %}
></i>
<a
class="nav-link flex-fill align-self-center me-auto {% if item.navactive %}{% navactive request item.navactive|join:' ' %}{% endif %}"
{% if item.is_folder %}
@@ -36,7 +36,7 @@
{% if item.is_folder %}
<span
class="pill m-2 align-self-center collapsed"
class="pill m-2 align-self-center"
type="button"
data-bs-toggle="collapse"
data-bs-target="#{{ item.html_id }}"

View File

@@ -60,15 +60,17 @@
<li>
<a class="dropdown-item" href="https://discord.gg/fjnHAmk" title="Alliance Auth Discord"><i class="fa-brands fa-discord fa-fw"></i> Alliance Auth Discord</a>
</li>
<li>
<a class="dropdown-item" href="https://gitlab.com/allianceauth/allianceauth" title="Alliance Auth Git"><i class="fa-brands fa-gitlab fa-fw"></i> Alliance Auth Git</a>
</li>
<li>
<a class="dropdown-item" href="{% url 'admin:index' %}">
<i class="fa-solid fa-gear fa-fw"></i> {% translate "Admin" %}
</a>
<a class="dropdown-item" href="https://gitlab.com/allianceauth/allianceauth" title="Alliance Auth Git"><i class="fa-brands fa-gitlab fa-fw"></i> Alliance Auth Git</a>
</li>
{% endif %}
{% if user.is_staff %}
<li>
<a class="dropdown-item" href="{% url 'admin:index' %}">
<i class="fa-solid fa-gear fa-fw"></i> {% translate "Admin" %}
</a>
</li>
{% endif %}
<li><hr class="dropdown-divider"></li>
{% if user.is_authenticated %}
<li>

View File

@@ -1 +1,21 @@
"""
Example
=======
.. code-block:: python
from allianceauth.notifications.models import Notification
def notify_user_view(request):
'''Simple view sending a notification to the user'''
Notification.objects.notify_user(
user=request.user,
title="Some title",
message="Some message",
level=Notification.Level.INFO,
)
"""
from .core import notify # noqa: F401

View File

@@ -15,7 +15,7 @@ class OpForm(forms.Form):
operation_name = forms.CharField(max_length=254, required=True, label=_("Operation Name"))
type = forms.CharField(required=False, label=_("Operation Type"))
fc = forms.CharField(max_length=254, required=True, label=_("Fleet Commander"))
duration = forms.CharField(max_length=254, required=True, label=_("Duration"))
duration = forms.CharField(max_length=25, required=True, label=_("Duration"))
description = forms.CharField(
widget=forms.Textarea(attrs={"rows": 10, "cols": 20, "input_type": "textarea"}),
required=False,

View File

@@ -1,41 +1,40 @@
{% load i18n %}
{% load evelinks %}
<div class="col-12 align-self-stretch py-2">
<div class="col-12 mb-3">
<div class="card h-100">
<div class="card-body">
<h4 class="card-title text-center">{% translate "Upcoming Fleets" %}</h4>
{% translate "Upcoming Fleets" as widget_title %}
{% include "framework/dashboard/widget-title.html" with title=widget_title %}
<div class="card-body">
<div>
<table class="table">
<thead>
<div>
<table class="table">
<thead>
<tr>
<th class="text-center">{% translate "Operation" %}</th>
<th class="text-center">{% translate "Type" %}</th>
<th class="text-center">{% translate "Form Up System" %}</th>
<th class="text-center">{% translate "EVE Time" %}</th>
</tr>
</thead>
<tbody>
{% for ops in timers %}
<tr>
<th class="text-center">{% translate "Operation" %}</th>
<th class="text-center">{% translate "Type" %}</th>
<th class="text-center">{% translate "Form Up System" %}</th>
<th class="text-center">{% translate "EVE Time" %}</th>
<td class="text-center">
{{ ops.operation_name }}
</td>
<td class="text-center">
{{ ops.type }}
</td>
<td class="text-center">
<a href="{{ ops.system|dotlan_solar_system_url }}">{{ ops.system }}</a>
</td>
<td class="text-center" nowrap>{{ ops.start | date:"Y-m-d H:i" }}</td>
</tr>
</thead>
<tbody>
{% for ops in timers %}
<tr>
<td class="text-center">
{{ ops.operation_name }}
</td>
<td class="text-center">
{{ ops.type }}
</td>
<td class="text-center">
<a href="{{ ops.system|dotlan_solar_system_url }}">{{ ops.system }}</a>
</td>
<td class="text-center" nowrap>{{ ops.start | date:"Y-m-d H:i" }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>

View File

@@ -1,5 +1,6 @@
{% extends "allianceauth/base-bs5.html" %}
{% load aa_i18n %}
{% load i18n %}
{% block page_title %}
@@ -57,11 +58,14 @@
{% include "bundles/datatables-js-bs5.html" %}
{% include "bundles/filterdropdown-js.html" %}
{% get_datatables_language_static LANGUAGE_CODE as DT_LANG_PATH %}
<script>
$(document).ready(() => {
const groupColumn = 0;
$('#tab_permissions_audit').DataTable({
language: {url: '{{ DT_LANG_PATH }}'},
columnDefs: [{
"visible": false,
"targets": groupColumn

View File

@@ -1,5 +1,6 @@
{% extends "allianceauth/base-bs5.html" %}
{% load aa_i18n %}
{% load i18n %}
{% block page_title %}
@@ -62,11 +63,14 @@
{% include "bundles/datatables-js-bs5.html" %}
{% include "bundles/filterdropdown-js.html" %}
{% get_datatables_language_static LANGUAGE_CODE as DT_LANG_PATH %}
<script>
$(document).ready(() => {
const groupColumn = 0;
$('#tab_permissions_overview').DataTable({
language: {url: '{{ DT_LANG_PATH }}'},
columnDefs: [{
"visible": false,
"targets": groupColumn

View File

@@ -12,6 +12,8 @@ from celery.schedules import crontab
from django.contrib import messages
from django.utils.translation import gettext_lazy as _
INSTALLED_APPS = [
'allianceauth', # needs to be on top of this list to support favicons in Django admin (see https://gitlab.com/allianceauth/allianceauth/-/issues/1301)
'django.contrib.admin',
@@ -22,6 +24,7 @@ INSTALLED_APPS = [
'django.contrib.staticfiles',
'django.contrib.humanize',
'django_celery_beat',
'solo',
'bootstrapform',
'django_bootstrap5', # https://github.com/zostera/django-bootstrap5
'sortedm2m',
@@ -39,13 +42,17 @@ INSTALLED_APPS = [
'allianceauth.theme.darkly',
'allianceauth.theme.flatly',
'allianceauth.theme.materia',
"allianceauth.custom_css",
'allianceauth.crontab',
'sri',
]
SRI_ALGORITHM = "sha512"
SECRET_KEY = "wow I'm a really bad default secret key"
# Celery configuration
BROKER_URL = 'redis://localhost:6379/0'
CELERYBEAT_SCHEDULER = "django_celery_beat.schedulers.DatabaseScheduler"
CELERYBEAT_SCHEDULER = "allianceauth.crontab.schedulers.OffsetDatabaseScheduler"
CELERYBEAT_SCHEDULE = {
'esi_cleanup_callbackredirect': {
'task': 'esi.tasks.cleanup_callbackredirect',
@@ -58,10 +65,12 @@ CELERYBEAT_SCHEDULE = {
'run_model_update': {
'task': 'allianceauth.eveonline.tasks.run_model_update',
'schedule': crontab(minute='0', hour="*/6"),
'apply_offset': True
},
'check_all_character_ownership': {
'task': 'allianceauth.authentication.tasks.check_all_character_ownership',
'schedule': crontab(minute='0', hour='*/4'),
'apply_offset': True
},
'analytics_daily_stats': {
'task': 'allianceauth.analytics.tasks.analytics_daily_stats',
@@ -69,6 +78,7 @@ CELERYBEAT_SCHEDULE = {
}
}
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
PROJECT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
BASE_DIR = os.path.dirname(PROJECT_DIR)
@@ -91,20 +101,62 @@ LOCALE_PATHS = (
os.path.join(BASE_DIR, 'locale/'),
)
LANGUAGES = (
("en", "English"),
("de", "German"),
("es", "Spanish"),
("zh-hans", "Chinese Simplified"),
("ru", "Russian"),
("ko", "Korean"),
("fr", "French"),
("ja", "Japanese"),
("it", "Italian"),
("uk", "Ukrainian"),
("pl", "Polish"),
LANGUAGES = ( # Sorted by Language Code alphabetical order + English at top
("en", _("English")),
# ("cs-cz", _("Czech")), #Not yet at 50% translated
("de", _("German")),
("es", _("Spanish")),
("it-it", _("Italian")),
("ja", _("Japanese")),
("ko-kr", _("Korean")),
("fr-fr", _("French")),
("nl-nl", _("Dutch")),
("pl-pl", _("Polish")),
("ru", _("Russian")),
("uk", _("Ukrainian")),
("zh-hans", _("Simplified Chinese")),
)
# Django's language codes are different from some of the libraries we use,
# so we need to map them.
# When adding a new language, please remember to add it to the mapping
# and add the language files to their respective directories under `allianceauth/static/allianceauth/libs/`.
LANGUAGE_MAPPING = {
# See https://github.com/DataTables/Plugins/tree/master/i18n for available languages
# (We use the JSON files)
# `allianceauth/static/allianceauth/libs/DataTables/Plugins/{version}/i18n/` for the files
"DataTables": {
"cs-cz": "cs",
"de": "de-DE",
"es": "es-ES",
"fr-fr": "fr-FR",
"it-it": "it-IT",
"ja": "ja",
"ko-kr": "ko",
"nl-nl": "nl-NL",
"pl-pl": "pl",
"ru": "ru",
"uk": "uk",
"zh-hans": "zh-HANT",
},
# See https://github.com/moment/moment/tree/master/locale for available languages
# `allianceauth/static/allianceauth/libs/moment.js/{version}/locale/` for the files
"MomentJS": {
"cs-cz": "cs",
"de": "de",
"es": "es",
"fr-fr": "fr",
"it-it": "it",
"ja": "ja",
"ko-kr": "ko",
"nl-nl": "nl",
"pl-pl": "pl",
"ru": "ru",
"uk": "uk",
"zh-hans": "zh-cn",
},
}
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',

View File

@@ -0,0 +1,37 @@
# Generated by Django 4.2.16 on 2024-10-07 13:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("mumble", "0012_mumble_client_info"),
]
operations = [
migrations.AlterModelOptions(
name="mumbleuser",
options={
"permissions": (
("access_mumble", "Can access the Mumble service"),
(
"view_connection_history",
"Can access the connection history of the Mumble service",
),
)
},
),
migrations.AlterField(
model_name="mumbleuser",
name="release",
field=models.TextField(
blank=True,
editable=False,
help_text="Client release. For official releases, this equals the version. For snapshots and git compiles, this will be something else.",
max_length=254,
null=True,
verbose_name="Mumble Release",
),
),
]

View File

@@ -90,7 +90,7 @@ class MumbleUser(AbstractServiceModel):
blank=True,
null=True,
editable=False,
help_text="The Mumble Release the user last authenticated with"
help_text="Client release. For official releases, this equals the version. For snapshots and git compiles, this will be something else."
)
version = models.IntegerField(
verbose_name="Mumble Version",
@@ -159,4 +159,5 @@ class MumbleUser(AbstractServiceModel):
class Meta:
permissions = (
("access_mumble", "Can access the Mumble service"),
("view_connection_history", "Can access the connection history of the Mumble service"),
)

View File

@@ -0,0 +1,232 @@
{% extends "allianceauth/base-bs5.html" %}
{% load aa_i18n %}
{% load i18n %}
{% block page_title %}
{% translate "Mumble" %}
{% endblock page_title %}
{% block header_nav_brand %}
{% trans "Mumble History" %} - {{ mumble_url }}
{% endblock header_nav_brand %}
{% block header_nav_collapse_left %}
{% endblock header_nav_collapse_left %}
{% block header_nav_collapse_right %}
{% endblock header_nav_collapse_right %}
{% block content %}
<div class="card col-lg-12 mb-3">
<div class="card-header">
<span class="card-title">{% translate "Server Connection History" %}</span>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table w-100" id="table-mumble-connection-history">
<thead>
<tr>
<th class="text-start">{% translate "User" %}</th>
<th class="text-start">{% translate "Displayed Name" %}</th>
<th class="text-start">{% translate "Release" %}</th>
<th class="text-start">{% translate "Version" %}</th>
<th class="text-end">{% translate "Last Connect" %}</th>
<th class="text-end">{% translate "Last Disconnect" %}</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6">
<div class="card">
<div class="card-header">
<span class="card-title">{% translate "Server Connection Breakdown" %}</span>
</div>
<div class="card-body">
<canvas id="pieChart"></canvas> <!-- Canvas element for the pie chart -->
</div>
</div>
</div>
<div class="col-lg-6">
<div class="card">
<div class="card-header">
<span class="card-title">{% translate "Server Connection Breakdown" %}</span>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table w-100" id="table-mumble-connection-stats">
<thead>
<tr>
<th class="text-start">{% translate "Version" %}</th>
<th class="text-end">{% translate "Number" %}</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
</div>
{% endblock content %}
{% block extra_javascript %}
{% include "bundles/datatables-js-bs5.html" %}
{% include "bundles/filterdropdown-js.html" %}
{% include "bundles/chart-js.html" %}
{% include "bundles/moment-js.html" with locale=True %}
{% get_datatables_language_static LANGUAGE_CODE as DT_LANG_PATH %}
<script>
$(document).ready(function () {
const MUMBLESTATS_DATETIME_FORMAT = 'LLL';
'use strict';
$("#table-mumble-connection-history").DataTable({
language: {url: '{{ DT_LANG_PATH }}'},
ajax: {
url: '{% url "mumble:connection_history_data" %}',
dataSrc: 'connection_history_data',
},
columns: [
{ data: 'user' },
{ data: 'display_name' },
{ data: 'release' },
{ data: 'version' },
{
data: 'last_connect',
render: {
_: (data) => {
return data === null
? ''
: moment(data)
.utc()
.format(MUMBLESTATS_DATETIME_FORMAT);
},
sort: (data) => {
return data === null ? '' : data;
}
},
className: 'text-end',
},
{
data: 'last_disconnect',
render: {
_: (data) => {
return data === null
? ''
: moment(data)
.utc()
.format(MUMBLESTATS_DATETIME_FORMAT);
},
sort: (data) => {
return data === null ? '' : data;
}
},
className: 'text-end',
},
],
order: [[4, 'desc']],
processing: true,
stateSave: true,
stateDuration: 0,
filterDropDown: {
columns: [
{
idx: 2,
},
{
idx: 3,
},
],
bootstrap: true,
bootstrap_version: 5,
},
});
$("#table-mumble-connection-stats").DataTable({
language: {url: '{{ DT_LANG_PATH }}'},
ajax: {
url: '{% url "mumble:release_counts_data" %}',
dataSrc: 'release_counts_data',
},
columns: [
{ data: 'release' },
{ data: 'user_count', className: 'text-end' },
],
order: [[1, 'desc']],
processing: true,
stateSave: true,
stateDuration: 0,
});
// Initialize empty Pie chart
const elementBody = document.querySelector('body');
const elementBodyCss = getComputedStyle(elementBody);
const ctx = document.getElementById('pieChart').getContext('2d');
const pieChart = new Chart(ctx, {
type: 'pie',
data: {
labels: [], // Initially empty
datasets: [
{
label: 'Server Connection Breakdown',
data: [], // Initially empty
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)'
],
borderColor: [
'rgba(255, 99, 132, 1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)'
],
borderWidth: 1
}
]
},
options: {
responsive: true,
plugins: {
legend: {
position: 'top'
}
}
}
});
Chart.defaults.color = elementBodyCss.color;
// AJAX call to dynamically update the chart
$.ajax({
url: '{% url "mumble:release_pie_chart_data" %}', // Your Django view URL that returns chart data
method: "GET",
success: (data) => {
// Replace chart data with the data from the AJAX response
pieChart.data.labels = data.labels; // Set the new labels
pieChart.data.datasets[0].data = data.values; // Set the new values
// Update the chart to reflect the new data
pieChart.update();
},
error: (xhr, status, error) => {
console.error('Error fetching pie chart data:', status, error);
},
});
});
</script>
{% endblock extra_javascript %}
{% block extra_css %}
{% include "bundles/datatables-css-bs5.html" %}
{% endblock extra_css %}

View File

@@ -7,7 +7,9 @@
{% endblock %}
{% block url %}
<a href="{{ service_url }}">{{ service_url }}</a>
{% if username != '' %}
<a href="mumble://{{ connect_url }}">{{ service_url }}</a>
{% endif %}
{% endblock %}
{% block user %}
@@ -46,4 +48,9 @@
</a>
{% endif %}
{% endif %}
{% if request.user.is_superuser %}
<a class="btn btn-primary" type="button" id="btnMumbleConnectionHistory" href="{% url 'mumble:connection_history' %}" title="{% translate 'Mumble Connection History' %}">
<i class="fa-solid fa-clock-rotate-left"></i> History
</a>
{% endif %}
{% endblock %}

View File

@@ -10,6 +10,10 @@ module_urls = [
path('deactivate/', views.DeleteMumbleView.as_view(), name='deactivate'),
path('reset_password/', views.ResetPasswordMumbleView.as_view(), name='reset_password'),
path('set_password/', views.SetPasswordMumbleView.as_view(), name='set_password'),
path('connection_history/', views.connection_history, name="connection_history"),
path('ajax/connection_history_data', views.connection_history_data, name="connection_history_data"),
path('ajax/release_counts_data', views.release_counts_data, name="release_counts_data"),
path('ajax/release_pie_chart_data', views.release_pie_chart_data, name="release_pie_chart_data"),
]
urlpatterns = [

View File

@@ -3,6 +3,11 @@ import logging
from allianceauth.services.forms import ServicePasswordModelForm
from allianceauth.services.abstract import BaseCreatePasswordServiceAccountView, BaseDeactivateServiceAccountView, \
BaseResetPasswordServiceAccountView, BaseSetPasswordServiceAccountView
from django.conf import settings
from django.contrib.auth.decorators import login_required, permission_required
from django.db.models import Count
from django.http import HttpResponse, JsonResponse
from django.shortcuts import render
from .models import MumbleUser
@@ -35,3 +40,51 @@ class ResetPasswordMumbleView(MumbleViewMixin, BaseResetPasswordServiceAccountVi
class SetPasswordMumbleView(MumbleViewMixin, BaseSetPasswordServiceAccountView):
form_class = MumblePasswordForm
@login_required
@permission_required('mumble.view_connection_history')
def connection_history(request) -> HttpResponse:
context = {
"mumble_url": settings.MUMBLE_URL,
}
return render(request, 'services/mumble/mumble_connection_history.html', context)
@login_required
@permission_required("mumble.view_connection_history")
def connection_history_data(request) -> JsonResponse:
connection_history_data = MumbleUser.objects.all(
).values(
'user',
'display_name',
'release',
'version',
'last_connect',
'last_disconnect',
)
return JsonResponse({"connection_history_data": list(connection_history_data)})
@login_required
@permission_required("mumble.view_connection_history")
def release_counts_data(request) -> JsonResponse:
release_counts_data = MumbleUser.objects.values('release').annotate(user_count=Count('user_id')).order_by('release')
return JsonResponse({
"release_counts_data": list(release_counts_data),
})
@login_required
@permission_required("mumble.view_connection_history")
def release_pie_chart_data(request) -> JsonResponse:
release_counts = MumbleUser.objects.values('release').annotate(user_count=Count('user_id')).order_by('release')
return JsonResponse({
"labels": list(release_counts.values_list("release", flat=True)),
"values": list(release_counts.values_list("user_count", flat=True)),
})

View File

@@ -1,4 +1,5 @@
{% extends "allianceauth/base-bs5.html" %}
{% load aa_i18n %}
{% load i18n %}
{% load humanize %}
@@ -168,6 +169,8 @@ ESC to cancel{% endblocktranslate %}" id="blah"></i></th>
{% include "bundles/moment-js.html" %}
{% include "bundles/clipboard-js.html" %}
{% get_datatables_language_static LANGUAGE_CODE as DT_LANG_PATH %}
<script>
const clipboard = new ClipboardJS('.copy-text-fa-icon');
@@ -237,6 +240,7 @@ ESC to cancel{% endblocktranslate %}" id="blah"></i></th>
});
elementTableSrpList.DataTable({
language: {url: '{{ DT_LANG_PATH }}'},
"order": [[ 6, "asc" ]],
"paging": false,
"columnDefs": [

View File

@@ -0,0 +1,247 @@
{
"emptyTable": "Tabulka neobsahuje žádná data",
"info": "Zobrazuji _START_ až _END_ z celkem _TOTAL_ záznamů",
"infoEmpty": "Zobrazuji 0 až 0 z 0 záznamů",
"infoFiltered": "(filtrováno z celkem _MAX_ záznamů)",
"loadingRecords": "Načítám...",
"zeroRecords": "Žádné záznamy nebyly nalezeny",
"paginate": {
"first": "První",
"last": "Poslední",
"next": "Další",
"previous": "Předchozí"
},
"searchBuilder": {
"add": "Přidat podmínku",
"clearAll": "Smazat vše",
"condition": "Podmínka",
"conditions": {
"date": {
"after": "po",
"before": "před",
"between": "mezi",
"empty": "prázdné",
"equals": "rovno",
"not": "není",
"notBetween": "není mezi",
"notEmpty": "není prázdné"
},
"number": {
"between": "mezi",
"empty": "prázdné",
"equals": "rovno",
"gt": "větší",
"gte": "rovno a větší",
"lt": "menší",
"lte": "rovno a menší",
"not": "není",
"notBetween": "není mezi",
"notEmpty": "není prázdné"
},
"string": {
"contains": "obsahuje",
"empty": "prázdné",
"endsWith": "končí na",
"equals": "rovno",
"not": "není",
"notEmpty": "není prázdné",
"startsWith": "začíná na",
"notContains": "Podmínka",
"notStartsWith": "Nezačíná",
"notEndsWith": "Nekončí"
},
"array": {
"equals": "rovno",
"empty": "prázdné",
"contains": "obsahuje",
"not": "není",
"notEmpty": "není prázdné",
"without": "neobsahuje"
}
},
"data": "Sloupec",
"logicAnd": "A",
"logicOr": "NEBO",
"title": {
"0": "Rozšířený filtr",
"_": "Rozšířený filtr (%d)"
},
"value": "Hodnota",
"button": {
"0": "Rozšířený filtr",
"_": "Rozšířený filtr (%d)"
},
"deleteTitle": "Smazat filtrovací pravidlo",
"leftTitle": "Zrušení odsazení podmínky",
"rightTitle": "Odsazení podmínky"
},
"autoFill": {
"cancel": "Zrušit",
"fill": "Vyplň všechny buňky textem <i>%d<i><\/i><\/i>",
"fillHorizontal": "Vyplň všechny buňky horizontálně",
"fillVertical": "Vyplň všechny buňky vertikálně",
"info": "Příklad automatického vyplňování"
},
"buttons": {
"collection": "Kolekce <span class=\"ui-button-icon-primary ui-icon ui-icon-triangle-1-s\"><\/span>",
"copy": "Kopírovat",
"copyTitle": "Kopírovat do schránky",
"csv": "CSV",
"excel": "Excel",
"pageLength": {
"-1": "Zobrazit všechny řádky",
"_": "Zobrazit %d řádků",
"1": "Zobraz 1 řádek"
},
"pdf": "PDF",
"print": "Tisknout",
"colvis": "Viditelnost sloupců",
"colvisRestore": "Resetovat sloupce",
"copyKeys": "Zmáčkněte ctrl or u2318 + C pro zkopírování dat. Pro zrušení klikněte na tuto zprávu nebo zmáčkněte esc..",
"copySuccess": {
"1": "Zkopírován 1 řádek do schránky",
"_": "Zkopírováno %d řádků do schránky"
},
"createState": "Vytvořit Stav",
"removeAllStates": "Vymazat všechny Stavy",
"removeState": "Odstranit",
"renameState": "Odstranit",
"savedStates": "Uložit Stavy",
"stateRestore": "Stav %d",
"updateState": "Aktualizovat"
},
"searchPanes": {
"clearMessage": "Smazat vše",
"collapse": {
"0": "Vyhledávací Panely",
"_": "Vyhledávací Panely (%d)"
},
"count": "{total}",
"countFiltered": "{shown} ({total})",
"emptyPanes": "Žádné Vyhledávací Panely",
"loadMessage": "Načítám Vyhledávací Panely",
"title": "Aktivních filtrů - %d",
"showMessage": "Zobrazit Vše",
"collapseMessage": "Sbalit Vše"
},
"select": {
"cells": {
"1": "Vybrán 1 záznam",
"_": "Vybráno %d záznamů"
},
"columns": {
"1": "Vybrán 1 sloupec",
"_": "Vybráno %d sloupců"
},
"rows": {
"1": "Vybrán 1 řádek",
"_": "Vybráno %d řádků"
}
},
"aria": {
"sortAscending": "Aktivujte pro seřazení vzestupně",
"sortDescending": "Aktivujte pro seřazení sestupně"
},
"lengthMenu": "Zobrazit _MENU_ výsledků",
"processing": "Zpracovávání...",
"search": "Vyhledávání:",
"datetime": {
"previous": "Předchozí",
"next": "Další",
"hours": "Hodiny",
"minutes": "Minuty",
"seconds": "Vteřiny",
"unknown": "-",
"amPm": [
"Dopoledne",
"Odpoledne"
],
"months": [
"Leden",
"Únor",
"Březen",
"Duben",
"Květen",
"Červen",
"Červenec",
"Srpen",
"Září",
"Říjen",
"Listopad",
"Prosinec"
],
"weekdays": [
"Ne",
"Po",
"Út",
"St",
"Čt",
"Pá",
"So"
]
},
"editor": {
"close": "Zavřít",
"create": {
"button": "Nový",
"title": "Nový záznam",
"submit": "Vytvořit"
},
"edit": {
"button": "Změnit",
"title": "Změnit záznam",
"submit": "Aktualizovat"
},
"remove": {
"button": "Vymazat",
"title": "Smazání",
"submit": "Vymazat",
"confirm": {
"_": "Opravdu chcete smazat tyto %d řádky?",
"1": "Opravdu chcete smazat tento 1 řádek?"
}
},
"multi": {
"title": "Mnohočetný výběr",
"restore": "Vrátit změny",
"noMulti": "Toto pole může být editováno individuálně, ale ne jako soušást skupiny.",
"info": "Vybrané položky obsahují různé hodnoty pro tento vstup. Chcete-li upravit a nastavit všechny položky tohoto vstupu na stejnou hodnotu, klikněte nebo klepněte sem, jinak si zachovají své individuální hodnoty."
},
"error": {
"system": "Došlo k systémové chybě (&lt;a target=\"\\\" rel=\"nofollow\" href=\"\\\"&gt;Více informací&lt;\/a&gt;)."
}
},
"infoThousands": " ",
"decimal": ",",
"thousands": " ",
"stateRestore": {
"creationModal": {
"button": "Vytvořit",
"columns": {
"search": "Vyhledávání v buňce",
"visible": "Viditelnost buňky"
},
"name": "Název:",
"order": "Řazení",
"paging": "Stránkování",
"scroller": "Pozice skrolování",
"select": "Výběr",
"title": "Vytvořit nový Stav",
"toggleLabel": "Zahrnout",
"search": "Filtrování",
"searchBuilder": "Rozšířené filtrování"
},
"duplicateError": "Stav s tímto názvem ji existuje.",
"emptyError": "Název nemůže být prázný.",
"emptyStates": "Žádné uložené stavy",
"removeConfirm": "Opravdu chcete odstranbit %s?",
"removeError": "Chyba při odstraňování stavu.",
"removeJoiner": "a",
"removeSubmit": "Odstranit",
"removeTitle": "Odstranit Stav",
"renameButton": "Vymazat",
"renameLabel": "Nové jméno pro %s:",
"renameTitle": "Přejmenování Stavu"
},
"searchPlaceholder": "Příklad zástupného prvku"
}

View File

@@ -0,0 +1,243 @@
{
"emptyTable": "Keine Daten in der Tabelle vorhanden",
"info": "_START_ bis _END_ von _TOTAL_ Einträgen",
"infoEmpty": "Keine Daten vorhanden",
"infoFiltered": "(gefiltert von _MAX_ Einträgen)",
"infoThousands": ".",
"loadingRecords": "Wird geladen ..",
"processing": "Bitte warten ..",
"paginate": {
"first": "Erste",
"next": "Nächste",
"last": "Letzte",
"previous": "Vorherige"
},
"aria": {
"sortAscending": ": aktivieren, um Spalte aufsteigend zu sortieren",
"sortDescending": ": aktivieren, um Spalte absteigend zu sortieren"
},
"select": {
"rows": {
"_": "%d Zeilen ausgewählt",
"1": "1 Zeile ausgewählt"
},
"cells": {
"1": "1 Zelle ausgewählt",
"_": "%d Zellen ausgewählt"
},
"columns": {
"1": "1 Spalte ausgewählt",
"_": "%d Spalten ausgewählt"
}
},
"buttons": {
"print": "Drucken",
"copy": "Kopieren",
"copyTitle": "In Zwischenablage kopieren",
"copySuccess": {
"_": "%d Zeilen kopiert",
"1": "1 Zeile kopiert"
},
"collection": "Aktionen <span class=\"ui-button-icon-primary ui-icon ui-icon-triangle-1-s\"><\/span>",
"colvis": "Spaltensichtbarkeit",
"colvisRestore": "Sichtbarkeit wiederherstellen",
"csv": "CSV",
"excel": "Excel",
"pageLength": {
"-1": "Alle Zeilen anzeigen",
"1": "Zeige 1 Zeile",
"_": "Zeige %d Zeilen"
},
"pdf": "PDF",
"createState": "Ansicht erstellen",
"removeAllStates": "Alle Ansichten entfernen",
"removeState": "Entfernen",
"renameState": "Umbenennen",
"savedStates": "Gespeicherte Ansicht",
"stateRestore": "Ansicht %d",
"updateState": "Aktualisieren",
"copyKeys": "Taste <i>STRG&lt;\\\/i&gt; oder <i>⌘&lt;\\\/i&gt; + <i>C&lt;\\\/i&gt; drücken um die Tabelle<br \/>in den Zwischenspeicher zu kopieren.<br \/><br \/>Um den Vorgang abzubrechen, Nachricht anklicken oder Escape-Taste drücken.<\/i><\/i><\/i>"
},
"autoFill": {
"cancel": "Abbrechen",
"fill": "Alle Zellen mit <i>%d<i> füllen<\/i><\/i>",
"fillHorizontal": "Alle horizontalen Zellen füllen",
"fillVertical": "Alle vertikalen Zellen füllen",
"info": "Automatische Vervollständigung"
},
"decimal": ",",
"search": "Suche:",
"searchBuilder": {
"add": "Bedingung hinzufügen",
"button": {
"0": "Such-Baukasten",
"_": "Such-Baukasten (%d)"
},
"condition": "Bedingung",
"conditions": {
"date": {
"after": "Nach",
"before": "Vor",
"between": "Zwischen",
"empty": "Leer",
"not": "Nicht",
"notBetween": "Nicht zwischen",
"notEmpty": "Nicht leer",
"equals": "Gleich"
},
"number": {
"between": "Zwischen",
"empty": "Leer",
"equals": "Entspricht",
"gt": "Größer als",
"gte": "Größer als oder gleich",
"lt": "Kleiner als",
"lte": "Kleiner als oder gleich",
"not": "Nicht",
"notBetween": "Nicht zwischen",
"notEmpty": "Nicht leer"
},
"string": {
"contains": "Beinhaltet",
"empty": "Leer",
"endsWith": "Endet mit",
"equals": "Entspricht",
"not": "Nicht",
"notEmpty": "Nicht leer",
"startsWith": "Startet mit",
"notContains": "enthält nicht",
"notStartsWith": "startet nicht mit",
"notEndsWith": "endet nicht mit"
},
"array": {
"equals": "ist gleich",
"empty": "ist leer",
"contains": "enthält",
"not": "ist ungleich",
"notEmpty": "ist nicht leer",
"without": "aber nicht"
}
},
"data": "Daten",
"deleteTitle": "Filterregel entfernen",
"leftTitle": "Äußere Kriterien",
"rightTitle": "Innere Kriterien",
"title": {
"0": "Such-Baukasten",
"_": "Such-Baukasten (%d)"
},
"value": "Wert",
"clearAll": "Alle entfernen",
"logicAnd": "Und",
"logicOr": "Oder"
},
"searchPanes": {
"clearMessage": "Leeren",
"collapse": {
"0": "Suchmasken",
"_": "Suchmasken (%d)"
},
"countFiltered": "{shown} ({total})",
"emptyPanes": "Keine Suchmasken",
"title": "Aktive Filter: %d",
"showMessage": "zeige Alle",
"collapseMessage": "Alle einklappen",
"count": "{total}",
"loadMessage": "Lade Suchmasken .."
},
"thousands": ".",
"zeroRecords": "Keine passenden Einträge gefunden",
"lengthMenu": "_MENU_ Zeilen anzeigen",
"datetime": {
"previous": "Vorher",
"next": "Nachher",
"hours": "Stunden",
"minutes": "Minuten",
"seconds": "Sekunden",
"unknown": "Unbekannt",
"weekdays": [
"Sonntag",
"Montag",
"Dienstag",
"Mittwoch",
"Donnerstag",
"Freitag",
"Samstag"
],
"months": [
"Januar",
"Februar",
"März",
"April",
"Mai",
"Juni",
"Juli",
"August",
"September",
"Oktober",
"November",
"Dezember"
]
},
"editor": {
"close": "Schließen",
"create": {
"button": "Neu",
"title": "Neuen Eintrag erstellen",
"submit": "Erstellen"
},
"remove": {
"confirm": {
"_": "Sollen %d Zeilen gelöscht werden?",
"1": "Soll diese Zeile gelöscht werden?"
},
"button": "Entfernen",
"title": "Entfernen",
"submit": "Entfernen"
},
"error": {
"system": "Ein Systemfehler ist aufgetreten"
},
"multi": {
"title": "Mehrere Werte",
"restore": "Änderungen zurücksetzen",
"noMulti": "Dieses Feld kann nur einzeln bearbeitet werden, nicht als Teil einer Mengen-Änderung.",
"info": "Die ausgewählten Elemente enthalten mehrere Werte für dieses Feld. Um alle Elemente für dieses Feld zu bearbeiten und auf denselben Wert zu setzen, hier klicken oder tippen, andernfalls behalten diese ihre individuellen Werte bei."
},
"edit": {
"button": "Bearbeiten",
"title": "Eintrag bearbeiten",
"submit": "Bearbeiten"
}
},
"searchPlaceholder": "Suchen...",
"stateRestore": {
"creationModal": {
"button": "Erstellen",
"columns": {
"search": "Spalten Suche",
"visible": "Spalten Sichtbarkeit"
},
"name": "Name:",
"order": "Sortieren",
"paging": "Seiten",
"scroller": "Scroll Position",
"search": "Suche",
"searchBuilder": "Such-Baukasten",
"select": "Auswahl",
"title": "Neue Ansicht erstellen",
"toggleLabel": "Inkludiert:"
},
"duplicateError": "Eine Ansicht mit diesem Namen existiert bereits.",
"emptyError": "Name darf nicht leer sein.",
"emptyStates": "Keine gespeicherten Ansichten",
"removeError": "Entfernen der Ansicht fehlgeschlagen.",
"removeJoiner": " und ",
"removeSubmit": "Entfernen",
"removeTitle": "Ansicht entfernen",
"renameButton": "Umbenennen",
"renameLabel": "Neuer Name für %s:",
"renameTitle": "Ansicht umbenennen",
"removeConfirm": "Sicher dass %s entfernt werden soll?"
}
}

View File

@@ -0,0 +1,244 @@
{
"processing": "Procesando...",
"lengthMenu": "Mostrar _MENU_ registros",
"zeroRecords": "No se encontraron resultados",
"emptyTable": "Ningún dato disponible en esta tabla",
"infoEmpty": "Mostrando registros del 0 al 0 de un total de 0 registros",
"infoFiltered": "(filtrado de un total de _MAX_ registros)",
"search": "Buscar:",
"loadingRecords": "Cargando...",
"paginate": {
"first": "Primero",
"last": "Último",
"next": "Siguiente",
"previous": "Anterior"
},
"aria": {
"sortAscending": ": Activar para ordenar la columna de manera ascendente",
"sortDescending": ": Activar para ordenar la columna de manera descendente"
},
"buttons": {
"copy": "Copiar",
"colvis": "Visibilidad",
"collection": "Colección",
"colvisRestore": "Restaurar visibilidad",
"copyKeys": "Presione ctrl o u2318 + C para copiar los datos de la tabla al portapapeles del sistema. <br \/> <br \/> Para cancelar, haga clic en este mensaje o presione escape.",
"copySuccess": {
"1": "Copiada 1 fila al portapapeles",
"_": "Copiadas %ds fila al portapapeles"
},
"copyTitle": "Copiar al portapapeles",
"csv": "CSV",
"excel": "Excel",
"pageLength": {
"-1": "Mostrar todas las filas",
"_": "Mostrar %d filas"
},
"pdf": "PDF",
"print": "Imprimir",
"renameState": "Cambiar nombre",
"updateState": "Actualizar",
"createState": "Crear Estado",
"removeAllStates": "Remover Estados",
"removeState": "Remover",
"savedStates": "Estados Guardados",
"stateRestore": "Estado %d"
},
"autoFill": {
"cancel": "Cancelar",
"fill": "Rellene todas las celdas con <i>%d<\/i>",
"fillHorizontal": "Rellenar celdas horizontalmente",
"fillVertical": "Rellenar celdas verticalmente"
},
"decimal": ",",
"searchBuilder": {
"add": "Añadir condición",
"button": {
"0": "Constructor de búsqueda",
"_": "Constructor de búsqueda (%d)"
},
"clearAll": "Borrar todo",
"condition": "Condición",
"conditions": {
"date": {
"before": "Antes",
"between": "Entre",
"empty": "Vacío",
"equals": "Igual a",
"notBetween": "No entre",
"not": "Diferente de",
"after": "Después",
"notEmpty": "No Vacío"
},
"number": {
"between": "Entre",
"equals": "Igual a",
"gt": "Mayor a",
"gte": "Mayor o igual a",
"lt": "Menor que",
"lte": "Menor o igual que",
"notBetween": "No entre",
"notEmpty": "No vacío",
"not": "Diferente de",
"empty": "Vacío"
},
"string": {
"contains": "Contiene",
"empty": "Vacío",
"endsWith": "Termina en",
"equals": "Igual a",
"startsWith": "Empieza con",
"not": "Diferente de",
"notContains": "No Contiene",
"notStartsWith": "No empieza con",
"notEndsWith": "No termina con",
"notEmpty": "No Vacío"
},
"array": {
"not": "Diferente de",
"equals": "Igual",
"empty": "Vacío",
"contains": "Contiene",
"notEmpty": "No Vacío",
"without": "Sin"
}
},
"data": "Data",
"deleteTitle": "Eliminar regla de filtrado",
"leftTitle": "Criterios anulados",
"logicAnd": "Y",
"logicOr": "O",
"rightTitle": "Criterios de sangría",
"title": {
"0": "Constructor de búsqueda",
"_": "Constructor de búsqueda (%d)"
},
"value": "Valor"
},
"searchPanes": {
"clearMessage": "Borrar todo",
"collapse": {
"0": "Paneles de búsqueda",
"_": "Paneles de búsqueda (%d)"
},
"count": "{total}",
"countFiltered": "{shown} ({total})",
"emptyPanes": "Sin paneles de búsqueda",
"loadMessage": "Cargando paneles de búsqueda",
"title": "Filtros Activos - %d",
"showMessage": "Mostrar Todo",
"collapseMessage": "Colapsar Todo"
},
"select": {
"cells": {
"1": "1 celda seleccionada",
"_": "%d celdas seleccionadas"
},
"columns": {
"1": "1 columna seleccionada",
"_": "%d columnas seleccionadas"
},
"rows": {
"1": "1 fila seleccionada",
"_": "%d filas seleccionadas"
}
},
"thousands": ".",
"datetime": {
"previous": "Anterior",
"hours": "Horas",
"minutes": "Minutos",
"seconds": "Segundos",
"unknown": "-",
"amPm": [
"AM",
"PM"
],
"months": {
"0": "Enero",
"1": "Febrero",
"10": "Noviembre",
"11": "Diciembre",
"2": "Marzo",
"3": "Abril",
"4": "Mayo",
"5": "Junio",
"6": "Julio",
"7": "Agosto",
"8": "Septiembre",
"9": "Octubre"
},
"weekdays": {
"0": "Dom",
"1": "Lun",
"2": "Mar",
"4": "Jue",
"5": "Vie",
"3": "Mié",
"6": "Sáb"
},
"next": "Próximo"
},
"editor": {
"close": "Cerrar",
"create": {
"button": "Nuevo",
"title": "Crear Nuevo Registro",
"submit": "Crear"
},
"edit": {
"button": "Editar",
"title": "Editar Registro",
"submit": "Actualizar"
},
"remove": {
"button": "Eliminar",
"title": "Eliminar Registro",
"submit": "Eliminar",
"confirm": {
"_": "¿Está seguro de que desea eliminar %d filas?",
"1": "¿Está seguro de que desea eliminar 1 fila?"
}
},
"error": {
"system": "Ha ocurrido un error en el sistema (<a target=\"\\\" rel=\"\\ nofollow\" href=\"\\\">Más información&lt;\\\/a&gt;).<\/a>"
},
"multi": {
"title": "Múltiples Valores",
"restore": "Deshacer Cambios",
"noMulti": "Este registro puede ser editado individualmente, pero no como parte de un grupo.",
"info": "Los elementos seleccionados contienen diferentes valores para este registro. Para editar y establecer todos los elementos de este registro con el mismo valor, haga clic o pulse aquí, de lo contrario conservarán sus valores individuales."
}
},
"info": "Mostrando _START_ a _END_ de _TOTAL_ registros",
"stateRestore": {
"creationModal": {
"button": "Crear",
"name": "Nombre:",
"order": "Clasificación",
"paging": "Paginación",
"select": "Seleccionar",
"columns": {
"search": "Búsqueda de Columna",
"visible": "Visibilidad de Columna"
},
"title": "Crear Nuevo Estado",
"toggleLabel": "Incluir:",
"scroller": "Posición de desplazamiento",
"search": "Búsqueda",
"searchBuilder": "Búsqueda avanzada"
},
"removeJoiner": "y",
"removeSubmit": "Eliminar",
"renameButton": "Cambiar Nombre",
"duplicateError": "Ya existe un Estado con este nombre.",
"emptyStates": "No hay Estados guardados",
"removeTitle": "Remover Estado",
"renameTitle": "Cambiar Nombre Estado",
"emptyError": "El nombre no puede estar vacío.",
"removeConfirm": "¿Seguro que quiere eliminar %s?",
"removeError": "Error al eliminar el Estado",
"renameLabel": "Nuevo nombre para %s:"
},
"infoThousands": "."
}

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