Compare commits

..

316 Commits

Author SHA1 Message Date
Xu 3fb5120142 phpstan ignore config.php 2024-05-26 17:34:04 +02:00
Xu 833af4c62e add edit and back macros to twig 2024-05-26 17:34:04 +02:00
Xu b380d7e68e m.button with opt 2024-05-26 17:34:04 +02:00
Xu 6f8dad070c load day config from env, fixed multiple texts 2024-05-26 17:34:04 +02:00
Xu 40bd61fd32 Angeltype view: Add requires info for not members 2024-05-26 13:07:39 +02:00
Xu 300786e5d0 shift colors when signup_requires_arrival and not arrived 2024-05-26 12:55:10 +02:00
Xu 13ded8de49 supporters can view users_myshifts 2024-05-26 12:27:04 +02:00
Xu c82e3183d6 fix shifttypes permission 2024-05-21 19:22:00 +02:00
Xu f1f5cd7c01 Add users.arrive.list permission & refactored LegacyMiddleware test 2024-05-07 00:25:59 +02:00
Xu b2951b7337 refactor permissions and groups 2024-05-07 00:25:59 +02:00
Xu 4e2e929c7e fix schedule logs 2024-05-04 17:51:48 +02:00
Xu 4a0f5c2e78 throw error on goodie page if no goodie 2024-05-04 17:51:48 +02:00
Xu 0fb09280b3 angeltype view member dect to link 2024-05-04 17:51:48 +02:00
Xu 3972998ba0 rename has_permission_to(_any) to can(Any) 2024-05-04 13:43:27 +02:00
Igor Scheller e514685444 Use settings page for API key resets 2024-05-04 13:43:27 +02:00
Igor Scheller 87f7a74f27 Implement canAny handling for controllers 2024-05-04 13:43:27 +02:00
Igor Scheller a214e0ff8f Auth: Added canAny 2024-05-04 13:43:27 +02:00
Xu d18e203560 docker dev setup on localhost 2024-04-27 00:15:07 +02:00
Xu 0f2c7c5394 refactor shirt to goodie 2024-04-27 00:12:23 +02:00
Xu 3fd8267f12 rename goody into goodie 2024-04-23 00:21:08 +02:00
Xu d0388e8344 fix colors of shifts for admins 2024-04-17 23:09:14 +02:00
Igor Scheller 8f6bd547d3 Move shifts event handler to right location 2024-04-14 21:13:40 +02:00
Igor Scheller f397d809de Extract controller delete blocks to methods 2024-04-14 21:13:40 +02:00
Igor Scheller dd096b0f46 Move Shifts helper methods to Shift model 2024-04-14 21:13:40 +02:00
Igor Scheller cb82ad9c74 Updated config parser to use consistent formatting 2024-04-14 21:13:40 +02:00
Daniel Poelzleithner 0155a33beb Add external_register_url to link to external registration site, hide password form if enable_password is false
Our users have problems with the disabled register button and
keep creating tickets. Add option to link to external site instead
of a disabled button.

People get confused by the login form if they should only
use the oauth login.
Now the login form is hidden if enable_password is false.
It can be shown by clicking the welcome text, in case an
admin needs access
2024-04-10 21:49:55 +02:00
Igor Scheller ab967c6f9c docker dev: Add config for phpMyAdmin, cleanup 2024-04-10 21:24:50 +02:00
Igor Scheller 87b50507f3 Fixed linter warnings 2024-04-10 21:24:50 +02:00
Igor Scheller 905d91d6ed Added/updated comments 2024-04-10 21:24:50 +02:00
Xu 9bf9bd2823 add 'enable_email_goody' config and fix wording 2024-04-07 22:05:33 +02:00
Xu d7d99900f8 Use icons in user edit view 2024-04-07 21:52:14 +02:00
Xu 7e06923ed0 Add user.fa.edit permission 2024-04-07 21:52:14 +02:00
Xu e6251256b3 fix certificates button on AngelTypes_view, add id to location change log 2024-04-07 21:27:03 +02:00
Xu 6e76843db4 add ifsg and drive confirmed stats to metrics 2024-04-07 21:27:03 +02:00
Igor Scheller fcf23d3824 Active angels page: Fix fa angels sorting 2024-03-29 14:31:59 +01:00
Igor Scheller c82e902360 CI: Remove base path from unittest results 2024-03-24 18:01:12 +01:00
Igor Scheller fc9b4d6da4 Fix worklog edit user & rename user on profile page 2024-03-24 18:01:12 +01:00
Igor Scheller 63c70c0ec2 Fixed notification translations & show larger map in locations 2024-03-24 18:01:12 +01:00
Igor Scheller 087d1cf31e Show password reset count and fix query 2024-03-24 18:01:12 +01:00
Igor Scheller f9059161ec Fix oauth name autofill for registration 2024-03-24 18:01:12 +01:00
Xu 541789ae27 add shifts column to Location index view 2024-03-24 16:06:21 +01:00
Xu 865ffe5e5a rename "Shifts" translation 2024-03-24 16:06:21 +01:00
Xu 49cc935ceb driving license: can be confirmed and edited by admins and config options 2024-03-24 14:34:54 +01:00
Igor Scheller 9639a0fe3f Fix coverage warning 2024-03-19 20:33:14 +01:00
Igor Scheller 8c24b78333 Shift deletion: Simplify notification workflow 2024-03-19 20:28:02 +01:00
Igor Scheller 1217de096a Fix disabled choices select color on dark theme 2024-03-16 17:36:34 +01:00
Igor Scheller ba908bf849 Fix translations when removing users from angeltype 2024-03-16 17:36:34 +01:00
Igor Scheller 0642bff804 Fix group rights edit buttons 2024-03-16 17:36:34 +01:00
Igor Scheller aea88b3579 Move footer to page bottom 2024-03-16 17:36:34 +01:00
Igor Scheller 460b416ff1 Shifts view: Highlight current time 2024-03-16 17:36:34 +01:00
Igor Scheller 8dda9a0dc3 Fix night shift calculation & update times 2024-03-16 17:36:34 +01:00
Igor Scheller 4ad8385386 isNightShift: Sync with sql query 2024-03-16 17:36:34 +01:00
Igor Scheller f56e9c534c Add title to "Enough" goodie score 2024-03-16 17:36:34 +01:00
Xu c0088d6601 add hints to myshifts table 2024-03-16 16:31:27 +01:00
Xu f3a12ebda8 explain freeloads for users and shicos 2024-03-16 16:31:27 +01:00
Xu 100d62134f fix table on admin_active length and score 2024-03-10 19:29:58 +01:00
Xu 873803eb2d ifsg: can be confirmed and edited by admins 2024-03-10 19:08:05 +01:00
Jens Brandt 400edd9a19 Fix typo in english language file 2024-03-09 09:59:53 +01:00
Xu a94aa36fa4 Password reset in User state 2024-02-27 16:00:58 +01:00
Xu 9a07a7afb3 add other contact options to questions page 2024-02-27 16:00:58 +01:00
Xu ef3f58e999 differentiate Answer questions page 2024-02-27 16:00:58 +01:00
Xu e18cddae86 success join angeltype button 2024-02-27 16:00:58 +01:00
Xu 5b8b59008a number of mails send when news saved 2024-02-19 20:25:25 +01:00
Xu ec7fb0615c add target="_blank" to links 2024-02-19 20:25:25 +01:00
Xu 759a4f9a14 add night shift multiplier to translations
more night shift hints
2024-02-18 14:14:34 +01:00
Igor Scheller d8f8a4f67d Schedule: Update to parse newer specification 2024-02-17 19:34:03 +01:00
Igor Scheller b63eb44b39 Schedule: Simplify & cleanup data classes 2024-02-17 19:34:03 +01:00
Igor Scheller e3e0fb33a2 Added tests for schedule import 2024-02-17 19:34:03 +01:00
Igor Scheller 790a04dc14 Schedule Import: Moved file, initial cleanup, updated schedules 2024-02-17 19:34:03 +01:00
Igor Scheller aef53a306b Fix formatting 2024-02-17 17:20:43 +01:00
Igor Scheller 4fa5db8a42 Use confirmation dialog and redirect back when deleting shifts 2024-02-17 17:02:09 +01:00
Igor Scheller b10264d6ef Fix random shifts test 2024-02-17 16:45:30 +01:00
Igor Scheller ff1edaad10 Added rough system requirements to readme 2024-02-17 16:45:30 +01:00
Lotte Steenbrink 5e505cb8d2
note that shift length is in minutes 2024-02-17 16:32:18 +01:00
Xu cf570502f4 make force active a config option 2024-01-29 19:54:15 +01:00
Xu e7ff3b657a supporters can freeload 2024-01-29 19:54:15 +01:00
Xu 7e26e20608 User view pronoun after name 2024-01-29 19:54:15 +01:00
Xu 67b24032a0 add shift no signout hint and disable buttons 2024-01-29 19:54:15 +01:00
Xu 76c37a5f18 config options supporters can promote 2024-01-29 19:54:15 +01:00
weeman 496d75b9ef Autofocus confirm modal submit button
So that it can be confirmed with enter
2024-01-29 14:39:09 +01:00
weeman c03ccf94c7 Add console log to design submit modal 2024-01-29 14:39:09 +01:00
weeman e47da9b8dd
Fix linter setup 2024-01-29 11:20:39 +01:00
Xu 0476083e77 add needed angels from to shift view 2024-01-28 21:11:55 +01:00
xuwhite 89d8a070d9
add hint admin_shifts no location, shift type or angeltype (#1337) 2024-01-28 20:57:59 +01:00
Lotte Steenbrink f387bc655d active_angels.php, default.po: fix typo 2024-01-27 17:01:35 +01:00
Xu 276b1aa976 add shirt sizes information hint and optional link 2024-01-27 16:37:13 +01:00
Lotte Steenbrink 50b1abe0d1 README: make it clearer that you have to run `migrate` on fresh installs too 2024-01-16 14:33:33 +01:00
Xu b20124e57e error admin_user.php nick or email already exits 2024-01-13 17:20:58 +01:00
Igor Scheller 4d1502d092 CI: Remove api key on db dump 2024-01-10 15:04:49 +01:00
Igor Scheller 00f039dbaa CI: Use tag as release version, fix delete 2024-01-10 12:58:06 +01:00
Igor Scheller dbbf30e233 Replace empty or too short api keys instead of resetting them 2024-01-08 10:57:09 +01:00
Igor Scheller 3f03d6b1d8 API: Set access-control-allow-origin header on api responses, trim bearer api key 2024-01-08 10:57:09 +01:00
Igor Scheller 8c64447273 API: Show from schedule via shift type, added comments 2024-01-08 10:57:09 +01:00
Igor Scheller 3432829c91 API: Document x-api-key header auth 2024-01-08 10:57:09 +01:00
Igor Scheller 6e4c9b2405 API: Added event time 2024-01-08 10:57:09 +01:00
Xu 7637e0c66e refactor user info icon 2024-01-03 19:43:58 +01:00
Xu 8cff7aa205 add user_info to admin_active 2024-01-03 19:43:58 +01:00
Xu 0239bf1988 goodie score visible with admin_user permission 2023-12-29 23:31:21 +01:00
Xu 1798ccda83 add shifttypes and angeltypes count to metrics 2023-12-29 17:50:54 +01:00
Xu 7d5837c5f1 fix needed angeltypes in shifttypes and locations, search for more user related data in log 2023-12-29 17:44:36 +01:00
msquare 8603d47fe0 fix random shift button, again, again 2023-12-29 16:05:39 +01:00
Xu 4a0c0994f0 fix errors when tshirt-size required 2023-12-29 16:02:19 +01:00
Xu ffa531f311 disable shirt size edit for user in settings if got shirt 2023-12-29 13:52:00 +01:00
xuwhite 89d68a56e7
Add needed angel types to location & shift type and permission to edit shift types (#1316) 2023-12-29 13:40:01 +01:00
Xu 9dac2a53db set arrive in user edit view 2023-12-29 13:08:19 +01:00
Igor Scheller 33209ea70b Remove unused functions and methods 2023-12-29 13:06:53 +01:00
Igor Scheller 05725cd58c Eager load relations and optimize queries 2023-12-29 13:06:53 +01:00
Igor Scheller 5fccc7e421 Remove inline js 2023-12-29 13:06:53 +01:00
Igor Scheller b76144b23d Remove unused reference references 2023-12-29 13:06:53 +01:00
Xu 2883a66f49 add user info to metrics 2023-12-28 18:15:52 +01:00
weeman d6412605f2 Add 37c3 theme 2023-12-28 17:56:50 +01:00
Xu 57373c846a Metrics: Added not arrived users 2023-12-28 17:17:26 +01:00
Igor Scheller 1d3509ba3c Fix error messages 2023-12-28 12:48:00 +01:00
Igor Scheller 57862ba722 random shift: check for schedule load flag 2023-12-28 01:08:57 +01:00
Igor Scheller b229d697a3 Only send notifications on new news by default 2023-12-27 18:16:32 +01:00
Igor Scheller 3c0cbe55b6 Cleanup the Fix 2023-12-27 18:16:32 +01:00
Igor Scheller 8140ebd1cc Added edit link on shift type page 2023-12-27 18:16:32 +01:00
Igor Scheller b62d4b4dce Improve history by showing more information, fix users list 2023-12-27 18:16:32 +01:00
Igor Scheller d803591a91 Shifts creation: Simplify log message, fix "room angels" validation 2023-12-27 18:16:32 +01:00
Igor Scheller 6d4f059b3a Fix join angeltype message 2023-12-27 18:16:32 +01:00
Igor Scheller f3e1192695 Worklog: log deletion, User page: fix voucher calculation 2023-12-27 18:16:32 +01:00
Igor Scheller 8833506e04 Cleanup template styles 2023-12-27 18:16:32 +01:00
Igor Scheller cc160e3e20 Error page: Add translation 2023-12-27 18:16:32 +01:00
Igor Scheller bcfcb95786 Use type_bg_class() macro in templates 2023-12-27 18:16:32 +01:00
Igor Scheller f7b0ee9ebb Fix random test 2023-12-27 17:46:13 +01:00
Igor Scheller 0f794be25e Update composer packages 2023-12-27 16:57:58 +01:00
Igor Scheller c5ae5d4aa0 Formatting 2023-12-27 16:43:55 +01:00
Leandra Eberle 64fef42087
Merge pull request #1306 from xuwhite/fixes
small bug fixes
2023-12-27 13:26:10 +01:00
msquare ea9aa9ef40 fixes 2023-12-27 12:50:03 +01:00
Igor Scheller caa699ff05 Added room selection for schedules 2023-12-27 12:50:03 +01:00
Xu 99dd081651 small bug fixes 2023-12-27 12:42:47 +01:00
Igor Scheller 197d0d724a Shifts creation: Add reset to previous input 2023-12-26 20:47:05 +01:00
Igor Scheller 7888dfad78 Send notifications on news updates, make notifications optional 2023-12-26 20:36:02 +01:00
Igor Scheller 4429516a22 Added shift update event to send templated emails 2023-12-26 16:32:02 +01:00
Igor Scheller 4bbeb93d64 Implemented loading angels by shift type on schedule import 2023-12-26 16:32:02 +01:00
Igor Scheller 909f7bba5a Fix formatting... 2023-12-26 16:32:02 +01:00
Igor Scheller 2baa101a8f Random shit: Fix selection to exclude parallel shifts 2023-12-26 12:54:21 +01:00
Igor Scheller a3a36de985 Fixed formatting 2023-12-25 13:38:44 +01:00
Igor Scheller 4244acfb4d Header/Footer items config: Add configurable permissions 2023-12-25 13:32:49 +01:00
Xu 4fa99b8a04 Show "not marked as arrived" Notification only from set arrival date 2023-12-24 18:15:31 +01:00
Igor Scheller 14dbe7f5d9 Added random shift button to shifts view 2023-12-23 11:17:50 +01:00
Igor Scheller dc7c62ffe5 API: Added event / api info endpoint, simplified spec 2023-12-22 15:14:01 +01:00
Igor Scheller da8178b0bc API: Allow usage of self in user routes 2023-12-22 15:14:01 +01:00
Igor Scheller 02f998fc38 API: Added user info and shifts by user and angeltype, simplified neededAngelTypes 2023-12-22 15:14:01 +01:00
Igor Scheller 4de882ef85 API: Add API settings page 2023-12-22 15:14:01 +01:00
Igor Scheller fe836e281e API: Add openapi endpoint to view specification 2023-12-22 15:14:01 +01:00
Igor Scheller 8894f183f2 API: Use resource classes to serialize models 2023-12-22 15:14:01 +01:00
Igor Scheller 5b237febf8 API: Add /users/{id}/angeltypes, updated openapi formatting 2023-12-22 15:14:01 +01:00
Igor Scheller 497c1772f7 API: Rename rooms to locations and start/end to starts_at/ends_at 2023-12-22 15:14:01 +01:00
Igor Scheller 2e38b55167 API: Added API value tests 2023-12-22 15:14:01 +01:00
Igor Scheller 1a250dc250 API: Update index response codes and api descriptions 2023-12-22 15:14:01 +01:00
Igor Scheller ef3bd7c319 API: Show needed/added users by angeltype in shifts 2023-12-22 15:14:01 +01:00
Igor Scheller ea93e27a9d API: Add urls to response, configure required fields 2023-12-22 15:14:01 +01:00
Igor Scheller a5cebc8535 API: Add /angeltypes, /rooms, /rooms/{id}/shifts 2023-12-22 15:14:01 +01:00
Igor Scheller 72649c9522 API: Load api info from yaml 2023-12-22 15:14:01 +01:00
Igor Scheller b5d94971bc API: Split to multiple controllers, removed / from routes 2023-12-22 15:14:01 +01:00
Igor Scheller ca0a69b17d API: Wrapped lists in data objects, specified datetime format 2023-12-22 15:14:01 +01:00
Igor Scheller 1505d0229d API: Documented additional endpoints in OpenAPI 2023-12-22 15:14:01 +01:00
Igor Scheller e2e18db460 API: Moved json handling and route-api tagging to ApiRouteHandler 2023-12-22 15:14:01 +01:00
Igor Scheller 8adad075bf API: Init with news endpoint (ro) 2023-12-22 15:14:01 +01:00
Igor Scheller f826cee63c Move api key reset to authenticator, set api_key on registration 2023-12-22 15:14:01 +01:00
Igor Scheller 0dbf88ad1c Cleanup short api keys 2023-12-22 15:14:01 +01:00
Igor Scheller 8185a74edc Added user id to logs, implemented filter by user 2023-12-22 11:17:07 +01:00
Xu 162116998c edit user nick needs buerocrat 2023-12-21 14:01:40 +01:00
xuwhite ac73489aed
Icons (#1279)
* add icons to design page
* make icons more consistent
2023-12-21 13:08:29 +01:00
Xu f4c3f7a39e highlight freeloaded shifts in user myshifts view 2023-12-21 11:35:40 +01:00
Igor Scheller 23de3579af Set cookie secure attribute matching request 2023-12-15 11:13:30 +01:00
Igor Scheller ba4ba8f2f8 Lint & fix translations 2023-12-15 11:13:30 +01:00
Igor Scheller 8b1cd8130e Fix & cleanup page titles 2023-12-15 11:13:30 +01:00
xuwhite 48ea35562e
show user-info icon with admin_arrive permission 2023-12-14 22:15:17 +01:00
Xu a35a3580e0 highlight actual time in users shifts list 2023-12-14 22:09:28 +01:00
Igor Scheller 15a6ba1c52 Implemented schedule deletion 2023-12-13 22:18:20 +01:00
Xu d89fe01ddd shirt edit in edit user requires bureaucrat 2023-12-07 20:26:33 +01:00
Xu f292ce5331 Edit Shirt requires bureaucrat permission 2023-12-07 20:26:33 +01:00
Xu 269541293c password minimal length hint 2023-12-07 13:15:29 +01:00
Xu 545d2a7ccf fix email formatting 2023-12-07 12:59:56 +01:00
Xu dfd72d2d69 fix footer translations 2023-12-07 12:59:56 +01:00
Igor Scheller f2edb1a45c Merge tag 'v3.4.1' 2023-12-06 19:08:38 +01:00
Igor Scheller efda1ffc1c Escape text outputs in includes 2023-12-06 18:18:05 +01:00
dependabot[bot] b8095492ec Updated build dependencies 2023-12-06 14:51:20 +01:00
Igor Scheller 4e6ba3d684 Fix password reset e-mail wording in de translation 2023-12-01 12:03:10 +01:00
Igor Scheller 1b91d84b5f Added error page translations 2023-11-24 17:10:10 +01:00
Xu 47ad0a6133 fix translation keys in twig templates 2023-11-24 17:10:10 +01:00
Xu fec2f17bea fix date translations 2023-11-24 17:10:10 +01:00
xuwhite fd56966435
add user angeltype info in shift entries (#1262) 2023-11-24 15:11:36 +01:00
Xu d8310ed6e7 minor improvements on angeltypes 2023-11-24 14:44:55 +01:00
Xu 3ffb0a38e8 add user-info hint 2023-11-19 20:51:04 +01:00
Xu e9b8977728 add user info to user view 2023-11-19 20:51:04 +01:00
Igor Scheller 9acd06cb04 Feed export: Convert to string/int, updated docs 2023-11-19 19:04:13 +01:00
Igor Scheller b17dbf46e0 Route resolving: Show better error message when method is not callable 2023-11-19 19:04:13 +01:00
Igor Scheller 7f2f5ab7ed Templates: Update spacing handling 2023-11-19 19:04:13 +01:00
Igor Scheller 58c457be86 Fix choices dropdown search order and z-index to show on top of shifts table 2023-11-19 19:04:13 +01:00
Igor Scheller 36c7db40a7 Test all factories 2023-11-19 19:04:13 +01:00
Igor Scheller 0b165bc24c Recreated shift type admin backend 2023-11-19 18:55:06 +01:00
Igor Scheller bf83e6a300 Recreated shifts history page 2023-11-19 18:32:47 +01:00
Igor Scheller ac74ab489d Migration: Fix worklog times and schedule shift type 2023-11-19 18:18:26 +01:00
Igor Scheller f3347ba140 Migration: Trim api key length before down migration 2023-11-19 18:18:26 +01:00
Igor Scheller 39dbfabea7 Enabled foreign keys in sqlite tests, added missing fields to factories 2023-11-19 18:18:26 +01:00
Igor Scheller 0a0cf5265c Cleanup migrations: removed old install files 2023-11-19 18:18:26 +01:00
Igor Scheller 176a0b65c5 Decouple setting admin password from auth provider 2023-11-19 18:18:26 +01:00
Xu 7fda1fc14b unify T-shirt wording 2023-11-19 13:09:49 +01:00
Xu 66738298a9 clarify hint for nightshifts, simplify username regex, add missing translation
fix untranslated string
2023-11-19 13:09:49 +01:00
Xu 5d14109dbd unify e-mail wording 2023-11-19 13:09:49 +01:00
Xu 44efd910c6 Add day of event to date dropdowns, move task notice 2023-11-18 21:04:50 +01:00
Igor Scheller ecc3976c27 Show specific error message titles 2023-11-13 18:28:27 +01:00
Xu 93270a10fd Added more confirmation dialogs to delete forms 2023-11-13 18:28:27 +01:00
Igor Scheller ff179360cc Added confirmation dialog to delete forms 2023-11-13 18:28:27 +01:00
Igor Scheller 1b21bcf769 Added bootstrap modals 2023-11-13 18:28:27 +01:00
Xu b6bd3eba56 Replace page_link_to() with url() 2023-11-13 17:37:52 +01:00
Xu 6477e5dabd refactor driving license into the user certificate settings 2023-11-13 16:36:07 +01:00
Xu 24f91ce9b5 disable needed angels input if shift is imported 2023-11-13 01:09:31 +01:00
Xu 6022d792dc add hints for required profile settings 2023-11-12 18:46:19 +01:00
Igor Scheller 9e3adf6179 Rename user sign up to registration 2023-11-12 18:35:50 +01:00
Xu 6564056f16 Sign-up page wording fixed 2023-11-12 18:35:50 +01:00
Xu 343ce8241c wording on user page 2023-11-12 18:35:50 +01:00
Igor Scheller 7f6e1ff18e Rename rooms to locations 2023-11-06 20:39:01 +01:00
Igor Scheller a31534d9b7 Shifts feed: Added URL to shift and shifttype name & description 2023-11-03 18:45:30 +01:00
Xu 24204b1f3c add more buttons 2023-11-03 16:23:39 +01:00
xuwhite 27323bfba5
make required fields configurable 2023-11-03 15:15:44 +01:00
Xu 1397fe90ce creation of new entity's from overview pages (+ sign) 2023-11-03 13:32:39 +01:00
Xu 89321306bc back buttons on edit and deletion pages 2023-11-03 12:29:17 +01:00
Xu 185b7e3fb6 rename shirt size keys G to F 2023-11-03 12:09:17 +01:00
Igor Scheller 9ffe739b24 Fixed erroneous translation replacements 2023-10-20 23:31:14 +02:00
Xu 9fb6bd4d10 clear way more translations 2023-10-20 21:38:34 +02:00
Xu fe37258b35 clear even more translations 2023-10-20 21:38:34 +02:00
Xu 6195692d3d cleaning more translations 2023-10-20 21:38:34 +02:00
dependabot[bot] fc58d55274 Bump @babel/traverse from 7.20.13 to 7.23.2
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.20.13 to 7.23.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-19 17:44:51 +02:00
Igor Scheller 9fcee133eb Updated email_goody opt in text 2023-10-15 23:14:22 +02:00
Xu fed27210eb removing pt translation 2023-10-15 23:04:01 +02:00
Xu aeea3067b0 cleaning translations 2023-10-15 21:30:26 +02:00
Igor Scheller 38838352e2 Handle email send errors in Mailer class 2023-10-15 18:45:37 +02:00
Igor Scheller d251b4c7f7 Shifts: Fix sign up text when self sign up is disallowed 2023-10-15 18:44:11 +02:00
Igor Scheller 7dbc0481b9 Fix form buttons in btn-group 2023-10-15 18:37:27 +02:00
Igor Scheller 0aa4cdd2b0 Tests: Fix flaky NewsController tests 2023-10-15 18:21:41 +02:00
Igor Scheller 1d5f16a59e Tests: Fix flaky freeloader test 2023-10-15 18:21:41 +02:00
Igor Scheller fddae62669 Tests: Fix string length validation tests 2023-10-15 18:21:41 +02:00
Igor Scheller 599fff26d4 Tests: Fix --repeat in EventDispatcherTest 2023-10-15 18:21:41 +02:00
Igor Scheller cd8c01c080 Speedup password tests 2023-10-15 18:21:41 +02:00
Igor Scheller a70bc6ded8 Upgrade composer packages 2023-10-15 18:13:43 +02:00
Igor Scheller 7ce2cca052 Update composer packages 2023-10-15 18:13:43 +02:00
Igor Scheller cf4dc63495 Profile settings page: Add "back to my shifts" menu item and icons 2023-10-15 18:10:12 +02:00
Igor Scheller dc9441d925 SettingsControllerTest: Fix assertion order 2023-10-15 18:10:12 +02:00
Igor Scheller 8438b8dc51 Legacy logger: be less noisy on cli (no double logging and oneliners) 2023-10-15 18:10:12 +02:00
xuwhite 00f4afa2ab
rename rooms to locations (#1226) 2023-10-13 11:53:13 +02:00
Tobias Fiebig 9f113958ca
Update SECURITY.md
Included statement on the use of external reporting services / bug bounty services.
2023-10-13 10:16:17 +02:00
dependabot[bot] b3dd2b1d47 Bump postcss from 8.4.21 to 8.4.31
Bumps [postcss](https://github.com/postcss/postcss) from 8.4.21 to 8.4.31.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.4.21...8.4.31)

---
updated-dependencies:
- dependency-name: postcss
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-10-08 12:48:02 +02:00
xuwhite adf00b2739
add hide angeltype in shift view option for angeltypes (#1195) 2023-09-27 20:25:10 +02:00
xuwhite 8ebaffd71a
rename is_important to is_highlighted (#1215) 2023-09-27 18:15:12 +02:00
Xu 383f8ebde5 refactor no self signup to shift self signup 2023-09-26 18:02:23 +02:00
Igor Scheller 4cd7103121 Fix users page link 2023-09-24 22:50:44 +02:00
weeman 4267a76adb Redirect to sign-up if already logged in 2023-09-24 22:50:44 +02:00
msquare a2a57ec852 prefill fields from oauth 2023-09-24 22:50:44 +02:00
Igor Scheller 29a4b244dc Fixed some smaller error messages due to null values 2023-09-24 22:50:44 +02:00
Michael Weimann 4329ee4af9 Implement new sign up page 2023-09-24 22:50:44 +02:00
Tim Neumann c2dd25fc7c
Allow admins to remove entries from some config arrays (#1155)
This is done by setting the respective value to null
2023-09-24 21:42:44 +02:00
xuwhite 3b241529b7
Add event name and day of event to mail footer (#1214) 2023-09-24 15:10:10 +02:00
Igor Scheller 40b93e3d8b Sessions: Only show part of the session ID 2023-09-18 19:00:51 +02:00
Igor Scheller c06cb767da Delete all other sessions after setting a new password 2023-09-18 18:10:40 +02:00
Igor Scheller dbb089315f Delete all sessions on password reset 2023-09-18 18:10:40 +02:00
Igor Scheller 5c59fec1cf Add page to view and delete user sessions 2023-09-18 18:10:40 +02:00
Igor Scheller 102c8428c8 Save user id in Session model 2023-09-18 18:10:40 +02:00
Igor Scheller 67d5950926 Add Session model 2023-09-18 18:10:40 +02:00
Igor Scheller ee7d30b339 Schedule import: Show error message on schedule parsing errors 2023-09-18 17:27:15 +02:00
msquare 87bd4f4fa1
Create SECURITY.md 2023-09-12 12:39:38 +02:00
Igor Scheller 9a9ffcfdaf Hide arrival hint if signup does not require arrival 2023-09-02 18:26:14 +02:00
Xu f3ec62e121 update models with defaults 2023-08-30 20:25:13 +02:00
Xu 1ca9b99612 added tests for config ifsg_light_enabled 2023-08-30 20:04:32 +02:00
Xu 6b273288bd add config option for ifsg_light 2023-08-30 20:04:32 +02:00
Igor Scheller d1d0acf622 Fixed styling 2023-08-30 17:32:00 +02:00
Igor Scheller 68dd73e333 Shifts creation: Fix start/end date value after going back 2023-08-30 17:21:03 +02:00
Igor Scheller 94ba51bc46 Shifts creation: Fix start hours before 10:00 2023-08-30 17:21:03 +02:00
Igor Scheller 24ecea0d65 Shifts: Fix day marker on start of day 2023-08-29 20:11:37 +02:00
Igor Scheller 5e702cd177 News creation: Show error on duplicates 2023-08-29 20:11:37 +02:00
Igor Scheller f966b1521f Arrive search: Ignore datetime 2023-08-29 20:11:37 +02:00
Igor Scheller 2252819800 Shifts preview: added end year on hover and hours 2023-08-29 20:11:37 +02:00
Igor Scheller 931f3ba10d Sort shift types by name 2023-08-29 20:11:37 +02:00
Igor Scheller a60c5987ab Worklog: create log entry on successfull creation 2023-08-29 20:11:37 +02:00
Igor Scheller 80bec733bd Add table for ifsg to angeltype 2023-08-29 20:11:37 +02:00
msquare c63a671dc4 goodie manager needs to see shifts and worklogs from users 2023-08-18 16:42:16 +02:00
Igor Scheller 009b0f3f27 Fixed tests 2023-08-18 15:18:45 +02:00
Michael Weimann f4030b86af Add day of event in footer and on dashboard 2023-08-18 14:46:19 +02:00
Igor Scheller e0b552d18b Show angel name in tshirt view
resolves #1186
2023-08-17 20:13:46 +02:00
Jan-Philipp Litza 74989df119 Fix msgfmt error: keyword "settings" unknown 2023-08-17 15:22:00 +02:00
Xu b5803caf44 fix work log suggestion during the event 2023-08-16 20:54:35 +02:00
Michael Weimann e03f2936e7 Make news footer responsive 2023-08-15 22:06:34 +02:00
msquare 6c3bb7521f fixes #1181: fix misleading back link in admin edit worklog 2023-08-14 16:49:17 +02:00
Michael Weimann 85bc95fea9
Fix code style 2023-08-14 16:38:44 +02:00
msquare 0a3a3c3b56 make tests happy again 2023-08-14 16:30:14 +02:00
msquare df4f744f6d fixes #1180: meeting/news pagination 2023-08-14 16:02:11 +02:00
msquare e11b0db526 fixes #1157: secure cookies???ßßß 2023-08-14 11:11:27 +02:00
msquare 7f41d5eb1e fixes #1179: stop choices.js from escaping choices 2023-08-14 10:52:02 +02:00
hexchen 4a907600b7 Improve translation on admin_user page 2023-08-14 00:02:44 +02:00
msquare 47f0587cd9 fixes #1169: improve shift change hour sanitation 2023-08-13 16:52:41 +02:00
msquare cffc9854f8 fixes #1173: removes double escapes form input twig macro 2023-08-13 15:56:35 +02:00
msquare af2ac1bc3e fixes #1175: time lane scrolling out of screen 2023-08-13 15:31:51 +02:00
Igor Scheller 49300900d6 News: Catch all throwable exceptions 2023-08-13 13:47:37 +02:00
Michael Weimann 86da8758a4 Fix camp23 input group 2023-08-12 20:06:42 +02:00
msquare 3ae8424aea so much space... 2023-08-12 13:09:09 +02:00
msquare 98d2316b08 further improvements on hint link colors 2023-08-12 12:57:07 +02:00
msquare 6622680baf add ifsg tests 2023-08-12 12:19:24 +02:00
Xu 21423ef305 renaming ifsg 2023-08-12 12:19:24 +02:00
msquare c93c241dc9 make info+danger hint links readable again 2023-08-12 12:19:24 +02:00
Xu 4378fa2d7d fix tests after ifsg implementation 2023-08-12 12:19:24 +02:00
Xu 8dd4af1bb6 add requires ifsg certificate 2023-08-12 12:19:24 +02:00
Xu f345942e46 add ifsg certificates 2023-08-12 12:19:24 +02:00
Michael Weimann da2baa75bb Fix pre-code blocks 2023-08-03 19:37:36 +02:00
Michael Weimann 2c702fc67d Increase camp23 contrast 2023-08-03 19:37:36 +02:00
Igor Scheller d9b93e4236 News: Show updated date besides "updated" state 2023-07-30 18:57:49 +02:00
Igor Scheller 1c4c164c39 Fix shift sign up angel button not always displayed 2023-07-30 18:57:49 +02:00
Igor Scheller e407a3b780 OAuth: Fix error when user id is numeric 2023-07-30 18:57:49 +02:00
Igor Scheller 24f958b00d Fix 500 error on angeltypes edit page when logged out 2023-07-30 18:57:49 +02:00
msquare db4c5eec1c improme cccamp23 theme contrast 2023-07-26 20:25:28 +02:00
msquare 0a1c85d6bd cccamp23 theme improvements on badges 2023-07-24 18:49:32 +02:00
msquare c2e6dc5223 cccamp23 theme much prettier 2023-07-18 21:21:25 +02:00
msquare 19a5673231 cccamp23 theme 2023-07-18 21:02:31 +02:00
Michael Weimann 9feed46d4e Add TrimMiddleware to trim all request values 2023-07-10 12:59:02 +02:00
460 changed files with 21751 additions and 11577 deletions

View File

@ -27,6 +27,8 @@ max_line_length = unset
indent_size = 2 indent_size = 2
[*.js] [*.js]
indent_size = 2
max_line_length = unset
quote_type = single quote_type = single
[{LICENSE,db/*.sql}] [{LICENSE,db/*.sql}]

View File

@ -1,17 +1,10 @@
{ {
"parser": "@babel/eslint-parser", "parser": "@babel/eslint-parser",
"extends": [ "plugin:editorconfig/all" ], "extends": ["plugin:editorconfig/all", "prettier"],
"plugins": [ "editorconfig" ], "plugins": ["editorconfig"],
"rules": { "rules": {
"prefer-arrow-callback": "error", "prefer-arrow-callback": "error",
"prefer-template": "error", "prefer-template": "error",
"no-var": "error", "no-var": "error"
"quotes": [
"error",
"single",
{
"avoidEscape": true
}
]
} }
} }

View File

@ -113,7 +113,14 @@ generate-version:
before_script: before_script:
- apk add -q git - apk add -q git
script: script:
- VERSION="$(git describe --abbrev=0 --tags)-${CI_COMMIT_REF_NAME}+${CI_PIPELINE_ID}.${CI_COMMIT_SHORT_SHA}" - >
VERSION="$(\
git describe --exact-match --tags HEAD 2> /dev/null\
|| (\
(git describe --abbrev=0 --tags | tr -d '\n')\
&& echo "-${CI_COMMIT_REF_NAME}+${CI_PIPELINE_ID}.${CI_COMMIT_SHORT_SHA}"\
)\
)"
- echo "${VERSION}" - echo "${VERSION}"
- echo -n "${VERSION}" > storage/app/VERSION - echo -n "${VERSION}" > storage/app/VERSION
@ -155,6 +162,15 @@ yarn lint:
- apk add --no-cache git - apk add --no-cache git
- yarn lint - yarn lint
translations lint:
image: alpine
stage: prepare
before_script:
- apk add gettext
script:
- find resources/lang -type f -name '*.po' -exec sh -c 'msgfmt "${1%.*}.po" -o"${1%.*}.mo"' shell {} \;
- '[[ $(find resources/lang -type f -name "*.po" | wc -l) == $(find resources/lang -type f -name "*.mo" | wc -l) ]]'
# #
# Build # Build
# #
@ -177,6 +193,7 @@ build-image:
- composer validate - composer validate
- yarn check - yarn check
- yarn lint - yarn lint
- translations lint
- generate-version - generate-version
dependencies: dependencies:
- generate-version - generate-version
@ -222,6 +239,7 @@ test:
--coverage-text --coverage-html "${HOMEDIR}/coverage/" --coverage-text --coverage-html "${HOMEDIR}/coverage/"
--log-junit "${HOMEDIR}/unittests.xml" --log-junit "${HOMEDIR}/unittests.xml"
after_script: after_script:
- sed -i 's~/var/www/~~' unittests.xml
- '"${DOCROOT}/bin/migrate" down' - '"${DOCROOT}/bin/migrate" down'
dump-database: dump-database:
@ -242,6 +260,9 @@ dump-database:
- cd "${DOCROOT}" - cd "${DOCROOT}"
- ./bin/migrate - ./bin/migrate
script: script:
- >-
mysql -h "${MYSQL_HOST}" -u "${MYSQL_USER}" -p"${MYSQL_PASSWORD}" "${MYSQL_DATABASE}"
-e 'UPDATE users SET api_key="" WHERE name="admin"'
- >- - >-
mysqldump -h "${MYSQL_HOST}" -u "${MYSQL_USER}" -p"${MYSQL_PASSWORD}" "${MYSQL_DATABASE}" mysqldump -h "${MYSQL_HOST}" -u "${MYSQL_USER}" -p"${MYSQL_PASSWORD}" "${MYSQL_DATABASE}"
> "${HOMEDIR}/initial-install.sql" > "${HOMEDIR}/initial-install.sql"
@ -431,7 +452,8 @@ deploy:
GIT_STRATEGY: none GIT_STRATEGY: none
when: manual when: manual
script: script:
- kubectl delete all,ingress,pvc -l app=$CI_PROJECT_PATH_SLUG -l environment=$CI_ENVIRONMENT_SLUG - TARGETS=all,ingress,pvc,certificate
- kubectl -n "${KUBE_NAMESPACE}" delete $TARGETS -l app=$CI_PROJECT_PATH_SLUG -l environment=$CI_ENVIRONMENT_SLUG
deploy-k8s-review: deploy-k8s-review:
<<: *deploy_k8s <<: *deploy_k8s

View File

@ -13,10 +13,14 @@ Please ensure that your pull requests follow the [PSR-12](https://www.php-fig.or
You can check that by running You can check that by running
```bash ```bash
composer run phpcs composer run phpcs
# with docker
docker exec engelsystem_dev-es_workspace-1 composer run phpcs
``` ```
You may auto fix reported issues by running You may auto fix reported issues by running
```bash ```bash
composer run phpcbf composer run phpcbf
# with docker
docker exec engelsystem_dev-es_workspace-1 composer run phpcbf
``` ```
## Pre-commit hooks ## Pre-commit hooks
@ -68,7 +72,7 @@ docker compose exec es_workspace yarn build
docker compose exec -e THEMES=0,1 es_workspace yarn build docker compose exec -e THEMES=0,1 es_workspace yarn build
# Update the translation files # Update the translation files
docker compose exec es_workspace find /var/www/resources/lang -type f -name '*.po' -exec sh -c 'file="{}"; msgfmt "${file%.*}.po" -o "${file%.*}.mo"' \; docker compose exec es_workspace find /var/www/resources/lang -type f -name '*.po' -exec sh -c 'msgfmt "${1%.*}.po" -o"${1%.*}.mo"' shell {} \;
# Run the migrations # Run the migrations
docker compose exec es_workspace bin/migrate docker compose exec es_workspace bin/migrate
@ -84,6 +88,11 @@ docker compose exec es_workspace yarn build:watch
docker compose exec -e THEMES=0,1 es_workspace yarn build:watch docker compose exec -e THEMES=0,1 es_workspace yarn build:watch
``` ```
It might also be useful to have an interactive database interface for which a phpMyAdmin instance can be startet at [http://localhost:8888](http://localhost:8888).
```bash
docker compose --profile dev up
```
## Localhost ## Localhost
You can find your local Engelsystem on [http://localhost:5080](http://localhost:5080). You can find your local Engelsystem on [http://localhost:5080](http://localhost:5080).
@ -114,7 +123,7 @@ The following instructions explain how to get, build and run the latest Engelsys
``` ```
* Generate translation files * Generate translation files
```bash ```bash
find resources/lang/ -type f -name '*.po' -exec sh -c 'file="{}"; msgfmt "${file%.*}.po" -o "${file%.*}.mo"' \; find resources/lang/ -type f -name '*.po' -exec sh -c 'msgfmt "${1%.*}.po" -o"${1%.*}.mo"' shell {} \;
``` ```
## Testing ## Testing

View File

@ -28,6 +28,8 @@ The Engelsystem may be installed manually or by using the provided [docker setup
* MySQL-Server >= 5.7.8 or MariaDB-Server >= 10.2.2 * MySQL-Server >= 5.7.8 or MariaDB-Server >= 10.2.2
* Webserver, i.e. lighttpd, nginx, or Apache * Webserver, i.e. lighttpd, nginx, or Apache
From previous experience, 2 cores and 2GB ram are roughly enough for up to 1000 Angels (~700 arrived + 500 arrived but not working) during an event.
### Download ### Download
* Go to the [Releases](https://github.com/engelsystem/engelsystem/releases) page and download the latest stable release file. * Go to the [Releases](https://github.com/engelsystem/engelsystem/releases) page and download the latest stable release file.
* Extract the files to your webroot and continue with the directions for configurations and setup. * Extract the files to your webroot and continue with the directions for configurations and setup.
@ -40,7 +42,14 @@ The Engelsystem may be installed manually or by using the provided [docker setup
* Recommended: Directory Listing should be disabled. * Recommended: Directory Listing should be disabled.
* There must be a MySQL database set up with a user who has full rights to that database. * There must be a MySQL database set up with a user who has full rights to that database.
* If necessary, create a `config/config.php` to override values from `config/config.default.php`. * If necessary, create a `config/config.php` to override values from `config/config.default.php`.
* To edit values from the `footer_items`, `themes`, `locales`, `tshirt_sizes` or `headers` lists, directly modify the `config/config.default.php` file or rename it to `config/config.php`. * To disable/remove values from the following lists, set the value of the entry to `null`:
* `themes`
* `tshirt_sizes`
* `headers`
* `header_items`
* `footer_items`
* `locales`
* `contact_options`
* To import the database, the `bin/migrate` script has to be run. If you can't execute scripts, you can use the `initial-install.sql` file from the release zip. * To import the database, the `bin/migrate` script has to be run. If you can't execute scripts, you can use the `initial-install.sql` file from the release zip.
* In the browser, login with credentials `admin` : `asdfasdf` and change the password. * In the browser, login with credentials `admin` : `asdfasdf` and change the password.
@ -70,8 +79,8 @@ cd docker
docker compose up -d docker compose up -d
``` ```
#### Migrate #### Set Up / Migrate Database
Import database changes to migrate it to the newest version Create the Database Schema (on a fresh install) or import database changes to migrate it to the newest version
```bash ```bash
cd docker cd docker
docker compose exec es_server bin/migrate docker compose exec es_server bin/migrate

16
SECURITY.md Normal file
View File

@ -0,0 +1,16 @@
# Security Policy
## Reporting a Vulnerability
If you want to contact us directly regarding a security concern, please write an e-mail to contact@engelsystem.de and explain your findings.
Thank you!
## Use of external reporting / bug bounty services
We kindly ask you to not use any external reporting / bug bounty service. We do not collaborate with any external service and experiences in the past showed that these services usually add a lot of unnecessary overhead.
Please send security critical bug reports to contact@engelsystem.de.
If you feel like we are not reacting fast enough (generally no more than 14 days should go by until an initial response; This is a volunteer project mostly used internally after all), please feel free to go for full disclosure via our github issue tracker, and tag the issue there by creating a title prefixed with [SECURITY].
If you find a critical vulnerability that warrants a CVE, we will also take care of issuing a CVE without any bug bounty platform having to be involved.

View File

@ -3,10 +3,24 @@
# immediate exit after an error # immediate exit after an error
set -e set -e
testing() {
echo
echo "🔎 Checking ${1}"
}
testing 'JS & CSS 🎨'
yarn check yarn check
yarn lint yarn lint
testing 'PHP ⚙️'
composer validate composer validate
composer phpcs composer phpcs
composer phpstan composer phpstan
./vendor/bin/phpunit ./vendor/bin/phpunit
testing 'translations 🗺️'
find resources/lang -type f -name '*.po' -exec sh -c 'msgfmt "${1%.*}.po" -o"${1%.*}.mo"' shell {} \;
[ "$(find resources/lang -type f -name '*.po' | wc -l)" -eq "$(find resources/lang -type f -name '*.mo' | wc -l)" ]
find resources/lang -type f -name '*.mo' -exec rm {} \;
echo '✅ Done 🎉'

View File

@ -35,38 +35,40 @@
"ext-pdo": "*", "ext-pdo": "*",
"ext-simplexml": "*", "ext-simplexml": "*",
"ext-xml": "*", "ext-xml": "*",
"doctrine/dbal": "^3.5", "doctrine/dbal": "^3.7",
"erusev/parsedown": "^1.7", "erusev/parsedown": "^1.7",
"gettext/gettext": "^5.7", "gettext/gettext": "^5.7",
"gettext/translator": "^1.1", "gettext/translator": "^1.2",
"guzzlehttp/guzzle": "^7.5", "guzzlehttp/guzzle": "^7.8",
"illuminate/container": "^9.43", "illuminate/container": "^10.38",
"illuminate/database": "^9.43", "illuminate/database": "^10.38",
"illuminate/support": "^9.43", "illuminate/support": "^10.38",
"league/oauth2-client": "^2.6", "league/oauth2-client": "^2.7",
"league/openapi-psr7-validator": "^0.21",
"nikic/fast-route": "^1.3", "nikic/fast-route": "^1.3",
"nyholm/psr7": "^1.5", "nyholm/psr7": "^1.8",
"psr/container": "^2.0", "psr/container": "^2.0",
"psr/http-message": "^1.1",
"psr/http-server-middleware": "^1.0", "psr/http-server-middleware": "^1.0",
"psr/log": "^3.0", "psr/log": "^3.0",
"rcrowe/twigbridge": "^0.14.0", "rcrowe/twigbridge": "^0.14.1",
"respect/validation": "^1.1", "respect/validation": "^1.1",
"symfony/http-foundation": "^6.2", "symfony/http-foundation": "^6.4",
"symfony/mailer": "^6.2", "symfony/mailer": "^6.4",
"symfony/psr-http-message-bridge": "^2.1", "symfony/psr-http-message-bridge": "^2.3",
"twig/twig": "^3.4", "twig/twig": "^3.8",
"vlucas/phpdotenv": "^5.5" "vlucas/phpdotenv": "^5.6"
}, },
"require-dev": { "require-dev": {
"dms/phpunit-arraysubset-asserts": "^0.4", "dms/phpunit-arraysubset-asserts": "^0.5",
"fakerphp/faker": "^1.20", "fakerphp/faker": "^1.23",
"fig/log-test": "^1.1", "fig/log-test": "^1.1",
"filp/whoops": "^2.14", "filp/whoops": "^2.15",
"phpstan/phpstan": "^1.9", "phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^9.5", "phpunit/phpunit": "^9.6",
"slevomat/coding-standard": "^8.6", "slevomat/coding-standard": "^8.14",
"squizlabs/php_codesniffer": "^3.7", "squizlabs/php_codesniffer": "^3.8",
"symfony/var-dumper": "^6.2" "symfony/var-dumper": "^6.4"
}, },
"autoload": { "autoload": {
"psr-4": { "psr-4": {

2480
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -28,7 +28,6 @@ return [
\Engelsystem\Renderer\TwigServiceProvider::class, \Engelsystem\Renderer\TwigServiceProvider::class,
\Engelsystem\Middleware\RouteDispatcherServiceProvider::class, \Engelsystem\Middleware\RouteDispatcherServiceProvider::class,
\Engelsystem\Middleware\RequestHandlerServiceProvider::class, \Engelsystem\Middleware\RequestHandlerServiceProvider::class,
\Engelsystem\Middleware\SessionHandlerServiceProvider::class,
\Engelsystem\Http\Validation\ValidationServiceProvider::class, \Engelsystem\Http\Validation\ValidationServiceProvider::class,
\Engelsystem\Http\RedirectServiceProvider::class, \Engelsystem\Http\RedirectServiceProvider::class,
@ -38,6 +37,7 @@ return [
\Engelsystem\Http\HttpClientServiceProvider::class, \Engelsystem\Http\HttpClientServiceProvider::class,
\Engelsystem\Helpers\DumpServerServiceProvider::class, \Engelsystem\Helpers\DumpServerServiceProvider::class,
\Engelsystem\Helpers\UuidServiceProvider::class, \Engelsystem\Helpers\UuidServiceProvider::class,
\Engelsystem\Controllers\Api\UsesAuthServiceProvider::class,
], ],
// Application middleware // Application middleware
@ -50,9 +50,11 @@ return [
\Engelsystem\Middleware\SetLocale::class, \Engelsystem\Middleware\SetLocale::class,
\Engelsystem\Middleware\ETagHandler::class, \Engelsystem\Middleware\ETagHandler::class,
\Engelsystem\Middleware\AddHeaders::class, \Engelsystem\Middleware\AddHeaders::class,
\Engelsystem\Middleware\TrimInput::class,
// The application code // The application code
\Engelsystem\Middleware\ErrorHandler::class, \Engelsystem\Middleware\ErrorHandler::class,
\Engelsystem\Middleware\ApiRouteHandler::class,
\Engelsystem\Middleware\VerifyCsrfToken::class, \Engelsystem\Middleware\VerifyCsrfToken::class,
\Engelsystem\Middleware\RouteDispatcher::class, \Engelsystem\Middleware\RouteDispatcher::class,
\Engelsystem\Middleware\SessionHandler::class, \Engelsystem\Middleware\SessionHandler::class,
@ -74,12 +76,15 @@ return [
'message.created' => \Engelsystem\Events\Listener\Messages::class . '@created', 'message.created' => \Engelsystem\Events\Listener\Messages::class . '@created',
'news.created' => \Engelsystem\Events\Listener\News::class . '@created', 'news.created' => \Engelsystem\Events\Listener\News::class . '@created',
'news.updated' => \Engelsystem\Events\Listener\News::class . '@updated',
'oauth2.login' => \Engelsystem\Events\Listener\OAuth2::class . '@login', 'oauth2.login' => \Engelsystem\Events\Listener\OAuth2::class . '@login',
'shift.entry.deleting' => [ 'shift.deleting' => [
\Engelsystem\Events\Listener\Shift::class . '@deletedEntryCreateWorklog', \Engelsystem\Events\Listener\Shifts::class . '@deletingCreateWorklogs',
\Engelsystem\Events\Listener\Shift::class . '@deletedEntrySendEmail', \Engelsystem\Events\Listener\Shifts::class . '@deletingSendEmails',
], ],
'shift.updating' => \Engelsystem\Events\Listener\Shifts::class . '@updatedSendEmail',
], ],
]; ];

View File

@ -26,25 +26,42 @@ return [
'environment' => env('ENVIRONMENT', 'production'), 'environment' => env('ENVIRONMENT', 'production'),
// Application URL and base path to use instead of the auto-detected one // Application URL and base path to use instead of the auto-detected one
'url' => env('APP_URL', null), 'url' => env('APP_URL'),
// Header links // Header links
// Available link placeholders: %lang% // Available link placeholders: %lang%
// To disable a header_item in the config.php, you can set its value to null
'header_items' => [ 'header_items' => [
//'Foo' => 'https://foo.bar/batz-%lang%.html', // Name can be a translation string, permission is a engelsystem privilege
// 'Name' => 'URL',
// 'Name' => ['URL', 'permission'],
//'Foo' => ['https://foo.bar/batz-%lang%.html', 'logout'], // Permission: for logged-in users
], ],
// Footer links // Footer links
// To disable a footer item in the config.php, you can set its value to null
'footer_items' => [ 'footer_items' => [
// Name can be a translation string, permission is a engelsystem privilege
// 'Name' => 'URL',
// 'Name' => ['URL', 'permission'],
// URL to the angel faq and job description // URL to the angel faq and job description
'FAQ' => env('FAQ_URL', '/faq'), 'faq.faq' => [env('FAQ_URL', '/faq'), 'faq.view'],
// Contact email address, linked on every page // Contact email address, linked on every page
'Contact' => env('CONTACT_EMAIL', 'mailto:ticket@c3heaven.de'), 'Contact' => env('CONTACT_EMAIL', 'mailto:ticket@c3heaven.de'),
], ],
// Other ways to ask the heaven
// Multiple contact options / links are possible, analogue to footer_items
'contact_options' => [
// E-mail address
'general.email' => env('CONTACT_EMAIL', 'mailto:ticket@c3heaven.de'),
],
// Text displayed on the FAQ page, rendered as markdown // Text displayed on the FAQ page, rendered as markdown
'faq_text' => env('FAQ_TEXT', null), 'faq_text' => env('FAQ_TEXT'),
// Link to documentation/help // Link to documentation/help
'documentation_url' => env('DOCUMENTATION_URL', 'https://engelsystem.de/doc/'), 'documentation_url' => env('DOCUMENTATION_URL', 'https://engelsystem.de/doc/'),
@ -62,17 +79,20 @@ return [
'host' => env('MAIL_HOST', 'localhost'), 'host' => env('MAIL_HOST', 'localhost'),
'port' => env('MAIL_PORT', 587), 'port' => env('MAIL_PORT', 587),
// If tls transport encryption should be used // If tls transport encryption should be used
'tls' => env('MAIL_TLS', null), 'tls' => env('MAIL_TLS'),
'username' => env('MAIL_USERNAME'), 'username' => env('MAIL_USERNAME'),
'password' => env('MAIL_PASSWORD'), 'password' => env('MAIL_PASSWORD'),
'sendmail' => env('MAIL_SENDMAIL', '/usr/sbin/sendmail -bs'), 'sendmail' => env('MAIL_SENDMAIL', '/usr/sbin/sendmail -bs'),
], ],
# Your privacy@ contact address // Your privacy@ contact address
'privacy_email' => env('PRIVACY_EMAIL', null), 'privacy_email' => env('PRIVACY_EMAIL'),
// Show opt in to save some personal data after the event on user profile and registration pages
'enable_email_goodie' => (bool) env('ENABLE_EMAIL_GOODIE', true),
// Initial admin password // Initial admin password
'setup_admin_password' => env('SETUP_ADMIN_PASSWORD', null), 'setup_admin_password' => env('SETUP_ADMIN_PASSWORD'),
'oauth' => [ 'oauth' => [
// '[name]' => [config] // '[name]' => [config]
@ -129,11 +149,18 @@ return [
], ],
// Default theme, 1=style1.css // Default theme, 1=style1.css
'theme' => env('THEME', 16), 'theme' => env('THEME', 1),
// Supported themes
// To disable a theme in the config.php, you can set its value to null
'themes' => [ 'themes' => [
17 => [
'name' => 'Engelsystem 37c3 (2023)',
'type' => 'dark',
'navbar_classes' => 'navbar-dark',
],
16 => [ 16 => [
'name' => 'Engelsystem IGER 2023', 'name' => 'Engelsystem cccamp23 (2023)',
'type' => 'dark', 'type' => 'dark',
'navbar_classes' => 'navbar-dark', 'navbar_classes' => 'navbar-dark',
], ],
@ -229,12 +256,28 @@ return [
// Users are able to sign up // Users are able to sign up
'registration_enabled' => (bool) env('REGISTRATION_ENABLED', true), 'registration_enabled' => (bool) env('REGISTRATION_ENABLED', true),
// URL to external registration page, used on login page
'external_registration_url' => env('EXTERNAL_REGISTRATION_URL'),
// Required user fields
'required_user_fields' => [
'pronoun' => (bool) env('PRONOUN_REQUIRED', false),
'firstname' => (bool) env('FIRSTNAME_REQUIRED', false),
'lastname' => (bool) env('LASTNAME_REQUIRED', false),
'tshirt_size' => (bool) env('TSHIRT_SIZE_REQUIRED', true),
'mobile' => (bool) env('MOBILE_REQUIRED', false),
'dect' => (bool) env('DECT_REQUIRED', false),
],
// Only arrived angels can sign up for shifts // Only arrived angels can sign up for shifts
'signup_requires_arrival' => (bool) env('SIGNUP_REQUIRES_ARRIVAL', false), 'signup_requires_arrival' => (bool) env('SIGNUP_REQUIRES_ARRIVAL', false),
// Whether newly-registered user should automatically be marked as arrived // Whether newly-registered user should automatically be marked as arrived
'autoarrive' => (bool) env('ANGEL_AUTOARRIVE', false), 'autoarrive' => (bool) env('ANGEL_AUTOARRIVE', false),
// Supporters of an angeltype can promote other angels of the angeltype to supporter
'supporters_can_promote' => (bool) env('SUPPORTERS_CAN_PROMOTE', false),
// Only allow shift signup this number of hours in advance // Only allow shift signup this number of hours in advance
// Setting this to 0 disables the feature // Setting this to 0 disables the feature
'signup_advance_hours' => env('SIGNUP_ADVANCE_HOURS', 0), 'signup_advance_hours' => env('SIGNUP_ADVANCE_HOURS', 0),
@ -260,9 +303,8 @@ return [
// The minimum length for passwords // The minimum length for passwords
'min_password_length' => env('PASSWORD_MINIMUM_LENGTH', 8), 'min_password_length' => env('PASSWORD_MINIMUM_LENGTH', 8),
// Whether the Password field should be enabled on registration. // Whether the login and registration via password should be enabled (login will be hidden)
// This is useful when using oauth, disabling it also disables normal // This is useful when using oauth, disabling it also disables normal registration without oauth
// registration without oauth.
'enable_password' => (bool) env('ENABLE_PASSWORD', true), 'enable_password' => (bool) env('ENABLE_PASSWORD', true),
// Whether the DECT field should be enabled // Whether the DECT field should be enabled
@ -273,7 +315,7 @@ return [
// Regular expression describing a FALSE username. // Regular expression describing a FALSE username.
// Per default usernames must only contain alphanumeric chars, "-", "_" or ".". // Per default usernames must only contain alphanumeric chars, "-", "_" or ".".
'username_regex' => (string) env('USERNAME_REGEX', '/([^\p{L}\p{N}\-_.]+)/ui'), 'username_regex' => (string) env('USERNAME_REGEX', '/([^\p{L}\p{N}_.-]+)/ui'),
// Enables first name and last name // Enables first name and last name
'enable_user_name' => (bool) env('ENABLE_USER_NAME', false), 'enable_user_name' => (bool) env('ENABLE_USER_NAME', false),
@ -288,6 +330,9 @@ return [
// Enables the planned arrival/leave date // Enables the planned arrival/leave date
'enable_planned_arrival' => (bool) env('ENABLE_PLANNED_ARRIVAL', true), 'enable_planned_arrival' => (bool) env('ENABLE_PLANNED_ARRIVAL', true),
// Whether force active should be enabled
'enable_force_active' => (bool) env('ENABLE_FORCE_ACTIVE', true),
// Resembles the Goodie Type. There are three options: // Resembles the Goodie Type. There are three options:
// 'none' => no goodie at all // 'none' => no goodie at all
// 'goodie' => a goodie which has no sizing options // 'goodie' => a goodie which has no sizing options
@ -306,11 +351,12 @@ return [
// Local timezone // Local timezone
'timezone' => env('TIMEZONE', 'Europe/Berlin'), 'timezone' => env('TIMEZONE', 'Europe/Berlin'),
// Multiply 'night shifts' and freeloaded shifts (start or end between 2 and 6 exclusive) by 2 // Multiply 'night shifts' and freeloaded shifts (start or end between 2 and 8 exclusive) by 2 in goodie score
// Goodies must be enabled to use this feature
'night_shifts' => [ 'night_shifts' => [
'enabled' => (bool) env('NIGHT_SHIFTS', true), // Disable to weigh every shift the same 'enabled' => (bool) env('NIGHT_SHIFTS', true), // Disable to weigh every shift the same
'start' => env('NIGHT_SHIFTS_START', 2), 'start' => env('NIGHT_SHIFTS_START', 2), // Starting from hour
'end' => env('NIGHT_SHIFTS_END', 6), 'end' => env('NIGHT_SHIFTS_END', 8), // Ends at (without including) hour
'multiplier' => env('NIGHT_SHIFTS_MULTIPLIER', 2), 'multiplier' => env('NIGHT_SHIFTS_MULTIPLIER', 2),
], ],
@ -320,10 +366,20 @@ return [
'shifts_per_voucher' => env('SHIFTS_PER_VOUCHER', 0), 'shifts_per_voucher' => env('SHIFTS_PER_VOUCHER', 0),
'hours_per_voucher' => env('HOURS_PER_VOUCHER', 2), 'hours_per_voucher' => env('HOURS_PER_VOUCHER', 2),
// 'Y-m-d' formatted // 'Y-m-d' formatted
'voucher_start' => env('VOUCHER_START', null) ?: null, 'voucher_start' => env('VOUCHER_START') ?: null,
], ],
// Enables Driving License
'driving_license_enabled' => (bool) env('DRIVING_LICENSE_ENABLED', true),
# Instruction in accordance with § 43 Para. 1 of the German Infection Protection Act (IfSG)
'ifsg_enabled' => (bool) env('IFSG_ENABLED', false),
# Instruction only onsite in accordance with § 43 Para. 1 of the German Infection Protection Act (IfSG)
'ifsg_light_enabled' => env('IFSG_LIGHT_ENABLED', false) && env('IFSG_ENABLED', false),
// Available locales in /resources/lang/ // Available locales in /resources/lang/
// To disable a locale in the config.php, you can set its value to null
'locales' => [ 'locales' => [
'de_DE' => 'Deutsch', 'de_DE' => 'Deutsch',
'en_US' => 'English', 'en_US' => 'English',
@ -332,21 +388,31 @@ return [
// The default locale to use // The default locale to use
'default_locale' => env('DEFAULT_LOCALE', 'en_US'), 'default_locale' => env('DEFAULT_LOCALE', 'en_US'),
// Available T-Shirt sizes, set value to null if not available // Available T-Shirt sizes
// To disable a t-shirt size in the config.php, you can set its value to null
'tshirt_sizes' => [ 'tshirt_sizes' => [
'S' => 'Small Straight-Cut', 'S' => 'Small Straight-Cut',
'S-G' => 'Small Fitted-Cut', 'S-F' => 'Small Fitted-Cut',
'M' => 'Medium Straight-Cut', 'M' => 'Medium Straight-Cut',
'M-G' => 'Medium Fitted-Cut', 'M-F' => 'Medium Fitted-Cut',
'L' => 'Large Straight-Cut', 'L' => 'Large Straight-Cut',
'L-G' => 'Large Fitted-Cut', 'L-F' => 'Large Fitted-Cut',
'XL' => 'XLarge Straight-Cut', 'XL' => 'XLarge Straight-Cut',
'XL-G' => 'XLarge Fitted-Cut', 'XL-F' => 'XLarge Fitted-Cut',
'2XL' => '2XLarge Straight-Cut', '2XL' => '2XLarge Straight-Cut',
'3XL' => '3XLarge Straight-Cut', '3XL' => '3XLarge Straight-Cut',
'4XL' => '4XLarge Straight-Cut', '4XL' => '4XLarge Straight-Cut',
], ],
// T-shirt Size-Guide link
'tshirt_link' => env('TSHIRT_LINK'),
// Whether to show the current day of the event (-2, -1, 0, 1, 2…) in footer and on the dashboard.
// The event start date has to be set for it to appear.
'enable_show_day_of_event' => (bool) env('ENABLE_SHOW_DAY_OF_EVENT', false),
// If true there will be a day 0 (-1, 0, 1…). If false there won't (-1, 1…)
'event_has_day0' => (bool) env('EVENT_HAS_DAY0', true),
'metrics' => [ 'metrics' => [
// User work buckets in seconds // User work buckets in seconds
'work' => [1 * 60 * 60, 1.5 * 60 * 60, 2 * 60 * 60, 3 * 60 * 60, 5 * 60 * 60, 10 * 60 * 60, 20 * 60 * 60], 'work' => [1 * 60 * 60, 1.5 * 60 * 60, 2 * 60 * 60, 3 * 60 * 60, 5 * 60 * 60, 10 * 60 * 60, 20 * 60 * 60],
@ -375,11 +441,16 @@ return [
// Add additional headers // Add additional headers
'add_headers' => (bool) env('ADD_HEADERS', true), 'add_headers' => (bool) env('ADD_HEADERS', true),
// Predefined headers
// To disable a header in the config.php, you can set its value to null
'headers' => [ 'headers' => [
'X-Content-Type-Options' => 'nosniff', 'X-Content-Type-Options' => 'nosniff',
'X-Frame-Options' => 'sameorigin', 'X-Frame-Options' => 'sameorigin',
'Referrer-Policy' => 'strict-origin-when-cross-origin', 'Referrer-Policy' => 'strict-origin-when-cross-origin',
'Content-Security-Policy' => 'default-src \'self\' \'unsafe-inline\' \'unsafe-eval\'; img-src \'self\' data:;', 'Content-Security-Policy' =>
'default-src \'self\'; '
. ' style-src \'self\' \'unsafe-inline\'; '
. 'img-src \'self\' data:;',
'X-XSS-Protection' => '1; mode=block', 'X-XSS-Protection' => '1; mode=block',
'Feature-Policy' => 'autoplay \'none\'', 'Feature-Policy' => 'autoplay \'none\'',
//'Strict-Transport-Security' => 'max-age=7776000', //'Strict-Transport-Security' => 'max-age=7776000',

View File

@ -8,6 +8,8 @@ use FastRoute\RouteCollector;
// Pages // Pages
$route->get('/', 'HomeController@index'); $route->get('/', 'HomeController@index');
$route->get('/register', 'RegistrationController@view');
$route->post('/register', 'RegistrationController@save');
$route->get('/credits', 'CreditsController@index'); $route->get('/credits', 'CreditsController@index');
$route->get('/health', 'HealthController@index'); $route->get('/health', 'HealthController@index');
@ -38,7 +40,24 @@ $route->addGroup(
$route->post('/theme', 'SettingsController@saveTheme'); $route->post('/theme', 'SettingsController@saveTheme');
$route->get('/language', 'SettingsController@language'); $route->get('/language', 'SettingsController@language');
$route->post('/language', 'SettingsController@saveLanguage'); $route->post('/language', 'SettingsController@saveLanguage');
$route->get('/certificates', 'SettingsController@certificate');
$route->post('/certificates/ifsg', 'SettingsController@saveIfsgCertificate');
$route->post('/certificates/driving', 'SettingsController@saveDrivingLicense');
$route->get('/api', 'SettingsController@api');
$route->post('/api', 'SettingsController@apiKeyReset');
$route->get('/oauth', 'SettingsController@oauth'); $route->get('/oauth', 'SettingsController@oauth');
$route->get('/sessions', 'SettingsController@sessions');
$route->post('/sessions', 'SettingsController@sessionsDelete');
}
);
// User admin settings
$route->addGroup(
'/users/{user_id:\d+}',
function (RouteCollector $route): void {
$route->get('/certificates', 'Admin\\UserSettingsController@certificate');
$route->post('/certificates/ifsg', 'Admin\\UserSettingsController@saveIfsgCertificate');
$route->post('/certificates/driving', 'Admin\\UserSettingsController@saveDrivingLicense');
} }
); );
@ -62,6 +81,11 @@ $route->addGroup('/angeltypes', function (RouteCollector $route): void {
$route->get('/about', 'AngelTypesController@about'); $route->get('/about', 'AngelTypesController@about');
}); });
// Shifts
$route->addGroup('/shifts', function (RouteCollector $route): void {
$route->get('/random', 'ShiftsController@random');
});
// News // News
$route->get('/meetings', 'NewsController@meetings'); $route->get('/meetings', 'NewsController@meetings');
$route->addGroup( $route->addGroup(
@ -101,7 +125,42 @@ $route->addGroup(
); );
// API // API
$route->get('/api[/{resource:.+}]', 'ApiController@index'); $route->addGroup(
'/api',
function (RouteCollector $route): void {
$route->get('', 'Api\IndexController@index');
$route->addGroup(
'/v0-beta',
function (RouteCollector $route): void {
$route->addRoute(['OPTIONS'], '[/{resource:.+}]', 'Api\IndexController@options');
$route->get('', 'Api\IndexController@indexV0');
$route->get('/openapi', 'Api\IndexController@openApiV0');
$route->get('/info', 'Api\IndexController@info');
$route->get('/angeltypes', 'Api\AngelTypeController@index');
$route->get('/angeltypes/{angeltype_id:\d+}/shifts', 'Api\ShiftsController@entriesByAngeltype');
$route->get('/locations', 'Api\LocationsController@index');
$route->get('/locations/{location_id:\d+}/shifts', 'Api\ShiftsController@entriesByLocation');
$route->get('/news', 'Api\NewsController@index');
$route->get('/users/{user_id:(?:\d+|self)}', 'Api\UsersController@user');
$route->get('/users/{user_id:(?:\d+|self)}/angeltypes', 'Api\AngelTypeController@ofUser');
$route->get('/users/{user_id:(?:\d+|self)}/shifts', 'Api\ShiftsController@entriesByUser');
$route->addRoute(
['POST', 'PUT', 'DELETE', 'PATCH'],
'/[{resource:.+}]',
'Api\IndexController@notImplemented'
);
$route->get('/[{resource:.+}]', 'Api\IndexController@notFound');
}
);
$route->get('/[{resource:.+}]', 'Api\IndexController@notFound');
}
);
// Feeds // Feeds
$route->get('/atom', 'FeedController@atom'); $route->get('/atom', 'FeedController@atom');
@ -138,11 +197,32 @@ $route->addGroup(
$route->addGroup( $route->addGroup(
'/schedule', '/schedule',
function (RouteCollector $route): void { function (RouteCollector $route): void {
$route->get('', 'Admin\\Schedule\\ImportSchedule@index'); $route->get('', 'Admin\\ScheduleController@index');
$route->get('/edit[/{schedule_id:\d+}]', 'Admin\\Schedule\\ImportSchedule@edit'); $route->get('/edit[/{schedule_id:\d+}]', 'Admin\\ScheduleController@edit');
$route->post('/edit[/{schedule_id:\d+}]', 'Admin\\Schedule\\ImportSchedule@save'); $route->post('/edit[/{schedule_id:\d+}]', 'Admin\\ScheduleController@save');
$route->get('/load/{schedule_id:\d+}', 'Admin\\Schedule\\ImportSchedule@loadSchedule'); $route->get('/load/{schedule_id:\d+}', 'Admin\\ScheduleController@loadSchedule');
$route->post('/import/{schedule_id:\d+}', 'Admin\\Schedule\\ImportSchedule@importSchedule'); $route->post('/import/{schedule_id:\d+}', 'Admin\\ScheduleController@importSchedule');
}
);
// Shifts
$route->addGroup(
'/shifts',
function (RouteCollector $route): void {
$route->get('/history', 'Admin\\ShiftsController@history');
$route->post('/history', 'Admin\\ShiftsController@deleteTransaction');
}
);
// Shift types
$route->addGroup(
'/shifttypes',
function (RouteCollector $route): void {
$route->get('', 'Admin\\ShiftTypesController@index');
$route->post('', 'Admin\\ShiftTypesController@delete');
$route->get('/{shift_type_id:\d+}', 'Admin\\ShiftTypesController@view');
$route->get('/edit[/{shift_type_id:\d+}]', 'Admin\\ShiftTypesController@edit');
$route->post('/edit[/{shift_type_id:\d+}]', 'Admin\\ShiftTypesController@save');
} }
); );
@ -157,14 +237,14 @@ $route->addGroup(
} }
); );
// Rooms // Locations
$route->addGroup( $route->addGroup(
'/rooms', '/locations',
function (RouteCollector $route): void { function (RouteCollector $route): void {
$route->get('', 'Admin\\RoomsController@index'); $route->get('', 'Admin\\LocationsController@index');
$route->post('', 'Admin\\RoomsController@delete'); $route->post('', 'Admin\\LocationsController@delete');
$route->get('/edit[/{room_id:\d+}]', 'Admin\\RoomsController@edit'); $route->get('/edit[/{location_id:\d+}]', 'Admin\\LocationsController@edit');
$route->post('/edit[/{room_id:\d+}]', 'Admin\\RoomsController@save'); $route->post('/edit[/{location_id:\d+}]', 'Admin\\LocationsController@save');
} }
); );
@ -172,12 +252,12 @@ $route->addGroup(
$route->addGroup( $route->addGroup(
'/user/{user_id:\d+}', '/user/{user_id:\d+}',
function (RouteCollector $route): void { function (RouteCollector $route): void {
// Shirts // Goodies
$route->addGroup( $route->addGroup(
'/goodie', '/goodie',
function (RouteCollector $route): void { function (RouteCollector $route): void {
$route->get('', 'Admin\\UserShirtController@editShirt'); $route->get('', 'Admin\\UserGoodieController@editGoodie');
$route->post('', 'Admin\\UserShirtController@saveShirt'); $route->post('', 'Admin\\UserGoodieController@saveGoodie');
} }
); );

View File

@ -24,9 +24,11 @@ class AngelTypeFactory extends Factory
'restricted' => $this->faker->boolean(), 'restricted' => $this->faker->boolean(),
'requires_driver_license' => $this->faker->boolean(), 'requires_driver_license' => $this->faker->boolean(),
'no_self_signup' => $this->faker->boolean(), 'requires_ifsg_certificate' => $this->faker->boolean(),
'shift_self_signup' => $this->faker->boolean(),
'show_on_dashboard' => $this->faker->boolean(), 'show_on_dashboard' => $this->faker->boolean(),
'hide_register' => $this->faker->boolean(), 'hide_register' => $this->faker->boolean(),
'hide_on_shift_view' => $this->faker->boolean(),
]; ];
} }
} }

View File

@ -4,13 +4,13 @@ declare(strict_types=1);
namespace Database\Factories\Engelsystem\Models; namespace Database\Factories\Engelsystem\Models;
use Engelsystem\Models\Room; use Engelsystem\Models\Location;
use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\Factory;
class RoomFactory extends Factory class LocationFactory extends Factory
{ {
/** @var string */ /** @var string */
protected $model = Room::class; // phpcs:ignore protected $model = Location::class; // phpcs:ignore
public function definition(): array public function definition(): array
{ {
@ -18,6 +18,7 @@ class RoomFactory extends Factory
'name' => $this->faker->unique()->firstName(), 'name' => $this->faker->unique()->firstName(),
'map_url' => $this->faker->url(), 'map_url' => $this->faker->url(),
'description' => $this->faker->text(), 'description' => $this->faker->text(),
'dect' => $this->faker->optional()->numberBetween(1000, 9999),
]; ];
} }
} }

View File

@ -20,7 +20,7 @@ class NewsFactory extends Factory
'text' => $this->faker->realText(), 'text' => $this->faker->realText(),
'is_meeting' => $this->faker->boolean(), 'is_meeting' => $this->faker->boolean(),
'is_pinned' => $this->faker->boolean(.1), 'is_pinned' => $this->faker->boolean(.1),
'is_important' => $this->faker->boolean(.1), 'is_highlighted' => $this->faker->boolean(.1),
'user_id' => User::factory(), 'user_id' => User::factory(),
]; ];
} }

View File

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace Database\Factories\Engelsystem\Models;
use Engelsystem\Models\OAuth;
use Engelsystem\Models\User\User;
use Illuminate\Database\Eloquent\Factories\Factory;
class OAuthFactory extends Factory
{
/** @var class-string */
protected $model = OAuth::class; // phpcs:ignore
/**
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'user_id' => User::factory(),
'provider' => $this->faker->unique()->word(),
'identifier' => $this->faker->unique()->word(),
'access_token' => $this->faker->unique()->word(),
'refresh_token' => $this->faker->unique()->word(),
'expires_at' => $this->faker->dateTimeInInterval('+5 days', '+3 months')->format('Y-m-d'),
];
}
}

View File

@ -0,0 +1,24 @@
<?php
declare(strict_types=1);
namespace Database\Factories\Engelsystem\Models;
use Engelsystem\Models\Session;
use Engelsystem\Models\User\User;
use Illuminate\Database\Eloquent\Factories\Factory;
class SessionFactory extends Factory
{
/** @var string */
protected $model = Session::class; // phpcs:ignore
public function definition(): array
{
return [
'id' => $this->faker->lexify('????????????????????????????????'),
'payload' => $this->faker->text(100),
'user_id' => $this->faker->optional()->passthrough(User::factory()),
];
}
}

View File

@ -5,7 +5,7 @@ declare(strict_types=1);
namespace Database\Factories\Engelsystem\Models\Shifts; namespace Database\Factories\Engelsystem\Models\Shifts;
use Engelsystem\Models\AngelType; use Engelsystem\Models\AngelType;
use Engelsystem\Models\Room; use Engelsystem\Models\Location;
use Engelsystem\Models\Shifts\NeededAngelType; use Engelsystem\Models\Shifts\NeededAngelType;
use Engelsystem\Models\Shifts\Shift; use Engelsystem\Models\Shifts\Shift;
use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\Factory;
@ -17,11 +17,12 @@ class NeededAngelTypeFactory extends Factory
public function definition(): array public function definition(): array
{ {
$forRoom = $this->faker->boolean(); $type = $this->faker->numberBetween(0, 2);
return [ return [
'room_id' => $forRoom ? Room::factory() : null, 'location_id' => $type == 0 ? Location::factory() : null,
'shift_id' => $forRoom ? null : Shift::factory(), 'shift_id' => $type == 1 ? null : Shift::factory(),
'shift_type_id' => $type == 2 ? null : Shift::factory(),
'angel_type_id' => AngelType::factory(), 'angel_type_id' => AngelType::factory(),
'count' => $this->faker->numberBetween(1, 5), 'count' => $this->faker->numberBetween(1, 5),
]; ];

View File

@ -18,6 +18,7 @@ class ScheduleFactory extends Factory
'name' => $this->faker->unique()->words(4, true), 'name' => $this->faker->unique()->words(4, true),
'url' => $this->faker->parse('https://{{safeEmailDomain}}/{{slug}}.xml'), 'url' => $this->faker->parse('https://{{safeEmailDomain}}/{{slug}}.xml'),
'shift_type' => $this->faker->numberBetween(1, 5), 'shift_type' => $this->faker->numberBetween(1, 5),
'needed_from_shift_type' => $this->faker->boolean(.2),
'minutes_before' => 15, 'minutes_before' => 15,
'minutes_after' => 15, 'minutes_after' => 15,
]; ];

View File

@ -4,7 +4,7 @@ declare(strict_types=1);
namespace Database\Factories\Engelsystem\Models\Shifts; namespace Database\Factories\Engelsystem\Models\Shifts;
use Engelsystem\Models\Room; use Engelsystem\Models\Location;
use Engelsystem\Models\Shifts\Shift; use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftType; use Engelsystem\Models\Shifts\ShiftType;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
@ -25,7 +25,7 @@ class ShiftFactory extends Factory
'start' => $start, 'start' => $start,
'end' => $this->faker->dateTimeInInterval($start, '+3 hours'), 'end' => $this->faker->dateTimeInInterval($start, '+3 hours'),
'shift_type_id' => ShiftType::factory(), 'shift_type_id' => ShiftType::factory(),
'room_id' => Room::factory(), 'location_id' => Location::factory(),
'transaction_id' => $this->faker->optional()->uuid(), 'transaction_id' => $this->faker->optional()->uuid(),
'created_by' => User::factory(), 'created_by' => User::factory(),
'updated_by' => $this->faker->optional(.3)->boolean() ? User::factory() : null, 'updated_by' => $this->faker->optional(.3)->boolean() ? User::factory() : null,

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Database\Factories\Engelsystem\Models\User; namespace Database\Factories\Engelsystem\Models\User;
use Engelsystem\Models\User\Contact; use Engelsystem\Models\User\Contact;
use Engelsystem\Models\User\User;
use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\Factory;
class ContactFactory extends Factory class ContactFactory extends Factory
@ -15,6 +16,7 @@ class ContactFactory extends Factory
public function definition(): array public function definition(): array
{ {
return [ return [
'user_id' => User::factory(),
'dect' => $this->faker->optional()->numberBetween(1000, 9999), 'dect' => $this->faker->optional()->numberBetween(1000, 9999),
'email' => $this->faker->unique()->optional()->safeEmail(), 'email' => $this->faker->unique()->optional()->safeEmail(),
'mobile' => $this->faker->optional(.2)->phoneNumber(), 'mobile' => $this->faker->optional(.2)->phoneNumber(),

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Database\Factories\Engelsystem\Models\User; namespace Database\Factories\Engelsystem\Models\User;
use Engelsystem\Models\User\License; use Engelsystem\Models\User\License;
use Engelsystem\Models\User\User;
use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\Factory;
class LicenseFactory extends Factory class LicenseFactory extends Factory
@ -20,14 +21,30 @@ class LicenseFactory extends Factory
$drive_12t = $drive_7_5t && $this->faker->boolean(.3); $drive_12t = $drive_7_5t && $this->faker->boolean(.3);
$drive_forklift = ($drive_car && $this->faker->boolean(.1)) $drive_forklift = ($drive_car && $this->faker->boolean(.1))
|| ($drive_12t && $this->faker->boolean(.7)); || ($drive_12t && $this->faker->boolean(.7));
$drive_confirmed = $this->faker->boolean(0.5) && (
$drive_car
|| $drive_3_5t
|| $drive_7_5t
|| $drive_12t
|| $drive_forklift
);
$ifsg_certificate = $this->faker->boolean(0.1);
$ifsg_certificate_light = $this->faker->boolean(0.5) && !$ifsg_certificate;
$ifsg_confirmed = $this->faker->boolean(0.5) && ($ifsg_certificate || $ifsg_certificate_light);
return [ return [
'user_id' => User::factory(),
'has_car' => $drive_car && $this->faker->boolean(.7), 'has_car' => $drive_car && $this->faker->boolean(.7),
'drive_forklift' => $drive_forklift, 'drive_forklift' => $drive_forklift,
'drive_car' => $drive_car, 'drive_car' => $drive_car,
'drive_3_5t' => $drive_3_5t, 'drive_3_5t' => $drive_3_5t,
'drive_7_5t' => $drive_7_5t, 'drive_7_5t' => $drive_7_5t,
'drive_12t' => $drive_12t, 'drive_12t' => $drive_12t,
'drive_confirmed' => $drive_confirmed,
'ifsg_certificate' => $ifsg_certificate,
'ifsg_certificate_light' => $ifsg_certificate_light,
'ifsg_confirmed' => $ifsg_confirmed,
]; ];
} }
} }

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Database\Factories\Engelsystem\Models\User; namespace Database\Factories\Engelsystem\Models\User;
use Engelsystem\Models\User\PasswordReset; use Engelsystem\Models\User\PasswordReset;
use Engelsystem\Models\User\User;
use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\Factory;
class PasswordResetFactory extends Factory class PasswordResetFactory extends Factory
@ -15,6 +16,7 @@ class PasswordResetFactory extends Factory
public function definition(): array public function definition(): array
{ {
return [ return [
'user_id' => User::factory(),
'token' => bin2hex(random_bytes(16)), 'token' => bin2hex(random_bytes(16)),
]; ];
} }

View File

@ -6,6 +6,7 @@ namespace Database\Factories\Engelsystem\Models\User;
use Carbon\Carbon; use Carbon\Carbon;
use Engelsystem\Models\User\PersonalData; use Engelsystem\Models\User\PersonalData;
use Engelsystem\Models\User\User;
use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\Factory;
class PersonalDataFactory extends Factory class PersonalDataFactory extends Factory
@ -19,6 +20,7 @@ class PersonalDataFactory extends Factory
$departure = $this->faker->optional()->dateTimeThisMonth('2 weeks'); $departure = $this->faker->optional()->dateTimeThisMonth('2 weeks');
return [ return [
'user_id' => User::factory(),
'first_name' => $this->faker->optional(.7)->firstName(), 'first_name' => $this->faker->optional(.7)->firstName(),
'last_name' => $this->faker->optional()->lastName(), 'last_name' => $this->faker->optional()->lastName(),
'pronoun' => $this->faker->optional(.3)->pronoun(), 'pronoun' => $this->faker->optional(.3)->pronoun(),

View File

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Database\Factories\Engelsystem\Models\User; namespace Database\Factories\Engelsystem\Models\User;
use Engelsystem\Models\User\Settings; use Engelsystem\Models\User\Settings;
use Engelsystem\Models\User\User;
use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\Factory;
class SettingsFactory extends Factory class SettingsFactory extends Factory
@ -15,11 +16,12 @@ class SettingsFactory extends Factory
public function definition(): array public function definition(): array
{ {
return [ return [
'user_id' => User::factory(),
'language' => $this->faker->locale(), 'language' => $this->faker->locale(),
'theme' => $this->faker->numberBetween(1, 20), 'theme' => $this->faker->numberBetween(1, 20),
'email_human' => $this->faker->boolean(), 'email_human' => $this->faker->boolean(),
'email_messages' => $this->faker->boolean(), 'email_messages' => $this->faker->boolean(),
'email_goody' => $this->faker->boolean(), 'email_goodie' => $this->faker->boolean(),
'email_shiftinfo' => $this->faker->boolean(), 'email_shiftinfo' => $this->faker->boolean(),
'email_news' => $this->faker->boolean(), 'email_news' => $this->faker->boolean(),
'mobile_show' => $this->faker->boolean(), 'mobile_show' => $this->faker->boolean(),

View File

@ -6,6 +6,7 @@ namespace Database\Factories\Engelsystem\Models\User;
use Carbon\Carbon; use Carbon\Carbon;
use Engelsystem\Models\User\State; use Engelsystem\Models\User\State;
use Engelsystem\Models\User\User;
use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\Factory;
class StateFactory extends Factory class StateFactory extends Factory
@ -18,11 +19,13 @@ class StateFactory extends Factory
$arrival = $this->faker->optional()->dateTimeThisMonth(); $arrival = $this->faker->optional()->dateTimeThisMonth();
return [ return [
'user_id' => User::factory(),
'arrived' => (bool) $arrival, 'arrived' => (bool) $arrival,
'arrival_date' => $arrival ? Carbon::instance($arrival) : null, 'arrival_date' => $arrival ? Carbon::instance($arrival) : null,
'user_info' => $this->faker->optional(.1)->text(),
'active' => $this->faker->boolean(.3), 'active' => $this->faker->boolean(.3),
'force_active' => $this->faker->boolean(.1), 'force_active' => $this->faker->boolean(.1),
'got_shirt' => $this->faker->boolean(), 'got_goodie' => $this->faker->boolean(),
'got_voucher' => $this->faker->numberBetween(0, 10), 'got_voucher' => $this->faker->numberBetween(0, 10),
]; ];
} }

View File

@ -19,6 +19,7 @@ class UserFactory extends Factory
'password' => crypt(random_bytes(16), '$1$salt$'), 'password' => crypt(random_bytes(16), '$1$salt$'),
'email' => $this->faker->unique()->safeEmail(), 'email' => $this->faker->unique()->safeEmail(),
'api_key' => bin2hex(random_bytes(32)), 'api_key' => bin2hex(random_bytes(32)),
'updated_at' => $this->faker->dateTimeInInterval('-3 months', 'now'),
]; ];
} }
} }

View File

@ -1,802 +0,0 @@
-- phpMyAdmin SQL Dump
-- version 4.5.2
-- http://www.phpmyadmin.net
--
-- Host: localhost
-- Erstellungszeit: 27. Sep 2016 um 17:48
-- Server-Version: 10.1.10-MariaDB
-- PHP-Version: 7.0.4
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
SET time_zone = "+00:00";
--
-- Datenbank: `engelsystem`
--
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `AngelTypes`
--
DROP TABLE IF EXISTS `AngelTypes`;
CREATE TABLE `AngelTypes` (
`id` int(11) NOT NULL,
`name` varchar(50) NOT NULL DEFAULT '',
`restricted` int(1) NOT NULL,
`description` text NOT NULL,
`requires_driver_license` tinyint(1) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `EventConfig`
--
DROP TABLE IF EXISTS `EventConfig`;
CREATE TABLE `EventConfig` (
`event_name` varchar(255) DEFAULT NULL,
`buildup_start_date` int(11) DEFAULT NULL,
`event_start_date` int(11) DEFAULT NULL,
`event_end_date` int(11) DEFAULT NULL,
`teardown_end_date` int(11) DEFAULT NULL,
`event_welcome_msg` varchar(255) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `GroupPrivileges`
--
DROP TABLE IF EXISTS `GroupPrivileges`;
CREATE TABLE `GroupPrivileges` (
`id` int(11) NOT NULL,
`group_id` int(11) NOT NULL,
`privilege_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Daten für Tabelle `GroupPrivileges`
--
INSERT INTO `GroupPrivileges` (`id`, `group_id`, `privilege_id`) VALUES
(85, -7, 10),
(87, -7, 18),
(86, -7, 21),
(216, -6, 5),
(212, -6, 6),
(207, -6, 7),
(211, -6, 12),
(208, -6, 13),
(210, -6, 14),
(214, -6, 16),
(209, -6, 21),
(213, -6, 28),
(206, -6, 31),
(215, -6, 33),
(257, -6, 38),
(219, -5, 14),
(221, -5, 25),
(220, -5, 33),
(241, -4, 5),
(238, -4, 14),
(240, -4, 16),
(237, -4, 19),
(242, -4, 25),
(235, -4, 27),
(239, -4, 28),
(236, -4, 32),
(218, -4, 39),
(258, -3, 31),
(247, -2, 3),
(246, -2, 4),
(255, -2, 8),
(252, -2, 9),
(254, -2, 11),
(248, -2, 15),
(251, -2, 17),
(256, -2, 24),
(253, -2, 26),
(245, -2, 30),
(244, -2, 34),
(249, -2, 35),
(243, -2, 36),
(250, -2, 37),
(88, -1, 1),
(23, -1, 2),
(24, -1, 5);
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `Groups`
--
DROP TABLE IF EXISTS `Groups`;
CREATE TABLE `Groups` (
`Name` varchar(35) NOT NULL,
`UID` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Daten für Tabelle `Groups`
--
INSERT INTO `Groups` (`Name`, `UID`) VALUES
('6-Developer', -7),
('5-Bürokrat', -6),
('4-Team Coordinator', -5),
('3-Shift Coordinator', -4),
('Shirt-Manager', -3),
('2-Engel', -2),
('1-Gast', -1);
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `LogEntries`
--
DROP TABLE IF EXISTS `LogEntries`;
CREATE TABLE `LogEntries` (
`id` int(11) NOT NULL,
`timestamp` int(11) NOT NULL,
`nick` text NOT NULL,
`message` text NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `Messages`
--
DROP TABLE IF EXISTS `Messages`;
CREATE TABLE `Messages` (
`id` int(11) NOT NULL,
`Datum` int(11) NOT NULL,
`SUID` int(11) NOT NULL DEFAULT '0',
`RUID` int(11) NOT NULL DEFAULT '0',
`isRead` char(1) NOT NULL DEFAULT 'N',
`Text` text NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Fuers interen Communikationssystem';
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `NeededAngelTypes`
--
DROP TABLE IF EXISTS `NeededAngelTypes`;
CREATE TABLE `NeededAngelTypes` (
`id` int(11) NOT NULL,
`room_id` int(11) DEFAULT NULL,
`shift_id` int(11) DEFAULT NULL,
`angel_type_id` int(11) NOT NULL,
`count` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `News`
--
DROP TABLE IF EXISTS `News`;
CREATE TABLE `News` (
`ID` int(11) NOT NULL,
`Datum` int(11) NOT NULL,
`Betreff` varchar(150) NOT NULL DEFAULT '',
`Text` text NOT NULL,
`UID` int(11) NOT NULL DEFAULT '0',
`Treffen` tinyint(4) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `NewsComments`
--
DROP TABLE IF EXISTS `NewsComments`;
CREATE TABLE `NewsComments` (
`ID` bigint(11) NOT NULL,
`Refid` int(11) NOT NULL DEFAULT '0',
`Datum` datetime NOT NULL DEFAULT '0001-01-01 00:00:00',
`Text` text NOT NULL,
`UID` int(11) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `Privileges`
--
DROP TABLE IF EXISTS `Privileges`;
CREATE TABLE `Privileges` (
`id` int(11) NOT NULL,
`name` varchar(128) NOT NULL,
`desc` varchar(1024) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Daten für Tabelle `Privileges`
--
INSERT INTO `Privileges` (`id`, `name`, `desc`) VALUES
(1, 'start', 'Startseite für Gäste/Nicht eingeloggte User'),
(2, 'login', 'Logindialog'),
(3, 'news', 'Anzeigen der News-Seite'),
(4, 'logout', 'User darf sich ausloggen'),
(5, 'register', 'Einen neuen Engel registerieren'),
(6, 'admin_rooms', 'Räume administrieren'),
(7, 'admin_angel_types', 'Engel Typen administrieren'),
(8, 'user_settings', 'User profile settings'),
(9, 'user_messages', 'Writing and reading messages from user to user'),
(10, 'admin_groups', 'Manage usergroups and their rights'),
(11, 'user_questions', 'Let users ask questions'),
(12, 'admin_questions', 'Answer user''s questions'),
(13, 'admin_faq', 'Edit FAQs'),
(14, 'admin_news', 'Administrate the news section'),
(15, 'news_comments', 'User can comment news'),
(16, 'admin_user', 'Administrate the angels'),
(17, 'user_meetings', 'Lists meetings (news)'),
(18, 'admin_language', 'Translate the system'),
(19, 'admin_log', 'Display recent changes'),
(20, 'user_wakeup', 'User wakeup-service organization'),
(21, 'admin_import', 'Import rooms and shifts from pentabarf'),
(22, 'credits', 'View credits'),
(23, 'faq', 'View FAQ'),
(24, 'user_shifts', 'Signup for shifts'),
(25, 'user_shifts_admin', 'Signup other angels for shifts.'),
(26, 'user_myshifts', 'Allow angels to view their own shifts and cancel them.'),
(27, 'admin_arrive', 'Mark angels when they arrive.'),
(28, 'admin_shifts', 'Create shifts'),
(30, 'ical', 'iCal shift export'),
(31, 'admin_active', 'Mark angels as active and if they got a t-shirt.'),
(32, 'admin_free', 'Show a list of free/unemployed angels.'),
(33, 'admin_user_angeltypes', 'Confirm restricted angel types'),
(34, 'atom', ' Atom news export'),
(35, 'shifts_json_export', 'Export shifts in JSON format'),
(36, 'angeltypes', 'View angeltypes'),
(37, 'user_angeltypes', 'Join angeltypes.'),
(38, 'shifttypes', 'Administrate shift types'),
(39, 'admin_event_config', 'Allow editing event config');
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `Questions`
--
DROP TABLE IF EXISTS `Questions`;
CREATE TABLE `Questions` (
`QID` bigint(20) NOT NULL,
`UID` int(11) NOT NULL DEFAULT '0',
`Question` text NOT NULL,
`AID` int(11) DEFAULT NULL,
`Answer` text
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Fragen und Antworten';
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `Room`
--
DROP TABLE IF EXISTS `Room`;
CREATE TABLE `Room` (
`RID` int(11) NOT NULL,
`Name` varchar(35) NOT NULL DEFAULT '',
`Man` text,
`FromPentabarf` char(1) NOT NULL DEFAULT 'N',
`show` char(1) NOT NULL DEFAULT 'Y',
`Number` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Daten für Tabelle `Room`
--
INSERT INTO `Room` (`RID`, `Name`, `Man`, `FromPentabarf`, `show`, `Number`) VALUES
(1, 'Testraum', NULL, '', 'Y', 0);
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `ShiftEntry`
--
DROP TABLE IF EXISTS `ShiftEntry`;
CREATE TABLE `ShiftEntry` (
`id` int(11) NOT NULL,
`SID` int(11) NOT NULL DEFAULT '0',
`TID` int(11) NOT NULL DEFAULT '0',
`UID` int(11) NOT NULL DEFAULT '0',
`Comment` text,
`freeload_comment` text,
`freeloaded` tinyint(1) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `Shifts`
--
DROP TABLE IF EXISTS `Shifts`;
CREATE TABLE `Shifts` (
`SID` int(11) NOT NULL,
`title` text,
`shifttype_id` int(11) NOT NULL,
`start` int(11) NOT NULL,
`end` int(11) NOT NULL,
`RID` int(11) NOT NULL DEFAULT '0',
`URL` text,
`PSID` int(11) DEFAULT NULL,
`created_by_user_id` int(11) DEFAULT NULL,
`created_at_timestamp` int(11) NOT NULL,
`edited_by_user_id` int(11) DEFAULT NULL,
`edited_at_timestamp` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `ShiftTypes`
--
DROP TABLE IF EXISTS `ShiftTypes`;
CREATE TABLE `ShiftTypes` (
`id` int(11) NOT NULL,
`name` varchar(255) NOT NULL,
`angeltype_id` int(11) DEFAULT NULL,
`description` text NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Daten für Tabelle `ShiftTypes`
--
INSERT INTO `ShiftTypes` (`id`, `name`, `angeltype_id`, `description`) VALUES
(4, 'Schichttyp1', NULL, '');
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `User`
--
DROP TABLE IF EXISTS `User`;
CREATE TABLE `User` (
`UID` int(11) NOT NULL,
`Nick` varchar(23) NOT NULL DEFAULT '',
`Name` varchar(23) DEFAULT NULL,
`Vorname` varchar(23) DEFAULT NULL,
`Alter` int(4) DEFAULT NULL,
`Telefon` varchar(40) DEFAULT NULL,
`DECT` varchar(5) DEFAULT NULL,
`Handy` varchar(40) DEFAULT NULL,
`email` varchar(123) DEFAULT NULL,
`email_shiftinfo` tinyint(1) NOT NULL DEFAULT '0' COMMENT 'User wants to be informed by mail about changes in his shifts',
`jabber` varchar(200) DEFAULT NULL,
`Size` varchar(4) DEFAULT NULL,
`Passwort` varchar(128) DEFAULT NULL,
`password_recovery_token` varchar(32) DEFAULT NULL,
`Gekommen` tinyint(4) NOT NULL DEFAULT '0',
`Aktiv` tinyint(4) NOT NULL DEFAULT '0',
`force_active` tinyint(1) NOT NULL,
`Tshirt` tinyint(4) DEFAULT '0',
`color` tinyint(4) DEFAULT '10',
`Sprache` char(64) NOT NULL,
`Menu` char(1) NOT NULL DEFAULT 'L',
`lastLogIn` int(11) NOT NULL,
`CreateDate` datetime NOT NULL DEFAULT '0001-01-01 00:00:00',
`Art` varchar(30) DEFAULT NULL,
`kommentar` text,
`Hometown` varchar(255) NOT NULL DEFAULT '',
`api_key` varchar(32) NOT NULL,
`got_voucher` int(11) NOT NULL,
`arrival_date` int(11) DEFAULT NULL,
`planned_arrival_date` int(11) NOT NULL,
`planned_departure_date` int(11) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Daten für Tabelle `User`
--
INSERT INTO `User` (`UID`, `Nick`, `Name`, `Vorname`, `Alter`, `Telefon`, `DECT`, `Handy`, `email`, `email_shiftinfo`, `jabber`, `Size`, `Passwort`, `password_recovery_token`, `Gekommen`, `Aktiv`, `force_active`, `Tshirt`, `color`, `Sprache`, `Menu`, `lastLogIn`, `CreateDate`, `Art`, `kommentar`, `Hometown`, `api_key`, `got_voucher`, `arrival_date`, `planned_arrival_date`, `planned_departure_date`) VALUES
(1, 'admin', 'Gates', 'Bill', 42, '', '-', '', 'admin@example.com', 1, '', 'XL', '$6$rounds=5000$hjXbIhoRTH3vKiRa$Wl2P2iI5T9iRR.HHu/YFHswBW0WVn0yxCfCiX0Keco9OdIoDK6bIAADswP6KvMCJSwTGdV8PgA8g8Xfw5l8BD1', NULL, 1, 1, 0, 1, 0, 'de_DE.UTF-8', 'L', 1474990948, '0001-01-01 00:00:00', '', '', '', '038850abdd1feb264406be3ffa746235', 0, 1439490478, 1436964455, 1440161255);
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `UserAngelTypes`
--
DROP TABLE IF EXISTS `UserAngelTypes`;
CREATE TABLE `UserAngelTypes` (
`id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`angeltype_id` int(11) NOT NULL,
`confirm_user_id` int(11) DEFAULT NULL,
`coordinator` tinyint(1) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `UserDriverLicenses`
--
DROP TABLE IF EXISTS `UserDriverLicenses`;
CREATE TABLE `UserDriverLicenses` (
`user_id` int(11) NOT NULL,
`has_car` tinyint(1) NOT NULL,
`has_license_car` tinyint(1) NOT NULL,
`has_license_3_5t_transporter` tinyint(1) NOT NULL,
`has_license_7_5t_truck` tinyint(1) NOT NULL,
`has_license_12_5t_truck` tinyint(1) NOT NULL,
`has_license_forklift` tinyint(1) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Daten für Tabelle `UserDriverLicenses`
--
INSERT INTO `UserDriverLicenses` (`user_id`, `has_car`, `has_license_car`, `has_license_3_5t_transporter`, `has_license_7_5t_truck`, `has_license_12_5t_truck`, `has_license_forklift`) VALUES
(1, 1, 1, 1, 1, 1, 1);
-- --------------------------------------------------------
--
-- Tabellenstruktur für Tabelle `UserGroups`
--
DROP TABLE IF EXISTS `UserGroups`;
CREATE TABLE `UserGroups` (
`id` int(11) NOT NULL,
`uid` int(11) NOT NULL,
`group_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
--
-- Daten für Tabelle `UserGroups`
--
INSERT INTO `UserGroups` (`id`, `uid`, `group_id`) VALUES
(3, 1, -7),
(4, 1, -6),
(12, 1, -5),
(2, 1, -4),
(1, 1, -2);
--
-- Indizes der exportierten Tabellen
--
--
-- Indizes für die Tabelle `AngelTypes`
--
ALTER TABLE `AngelTypes`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `Name` (`name`);
--
-- Indizes für die Tabelle `GroupPrivileges`
--
ALTER TABLE `GroupPrivileges`
ADD PRIMARY KEY (`id`),
ADD KEY `group_id` (`group_id`,`privilege_id`),
ADD KEY `privilege_id` (`privilege_id`);
--
-- Indizes für die Tabelle `Groups`
--
ALTER TABLE `Groups`
ADD PRIMARY KEY (`UID`);
--
-- Indizes für die Tabelle `LogEntries`
--
ALTER TABLE `LogEntries`
ADD PRIMARY KEY (`id`),
ADD KEY `timestamp` (`timestamp`);
--
-- Indizes für die Tabelle `Messages`
--
ALTER TABLE `Messages`
ADD PRIMARY KEY (`id`),
ADD KEY `Datum` (`Datum`),
ADD KEY `SUID` (`SUID`),
ADD KEY `RUID` (`RUID`);
--
-- Indizes für die Tabelle `NeededAngelTypes`
--
ALTER TABLE `NeededAngelTypes`
ADD PRIMARY KEY (`id`),
ADD KEY `room_id` (`room_id`,`angel_type_id`),
ADD KEY `shift_id` (`shift_id`),
ADD KEY `angel_type_id` (`angel_type_id`);
--
-- Indizes für die Tabelle `News`
--
ALTER TABLE `News`
ADD PRIMARY KEY (`ID`),
ADD KEY `UID` (`UID`);
--
-- Indizes für die Tabelle `NewsComments`
--
ALTER TABLE `NewsComments`
ADD PRIMARY KEY (`ID`),
ADD KEY `Refid` (`Refid`),
ADD KEY `UID` (`UID`);
--
-- Indizes für die Tabelle `Privileges`
--
ALTER TABLE `Privileges`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `name` (`name`);
--
-- Indizes für die Tabelle `Questions`
--
ALTER TABLE `Questions`
ADD PRIMARY KEY (`QID`),
ADD KEY `UID` (`UID`),
ADD KEY `AID` (`AID`);
--
-- Indizes für die Tabelle `Room`
--
ALTER TABLE `Room`
ADD PRIMARY KEY (`RID`),
ADD UNIQUE KEY `Name` (`Name`);
--
-- Indizes für die Tabelle `ShiftEntry`
--
ALTER TABLE `ShiftEntry`
ADD PRIMARY KEY (`id`),
ADD KEY `TID` (`TID`),
ADD KEY `UID` (`UID`),
ADD KEY `SID` (`SID`,`TID`),
ADD KEY `freeloaded` (`freeloaded`);
--
-- Indizes für die Tabelle `Shifts`
--
ALTER TABLE `Shifts`
ADD PRIMARY KEY (`SID`),
ADD UNIQUE KEY `PSID` (`PSID`),
ADD KEY `RID` (`RID`),
ADD KEY `shifttype_id` (`shifttype_id`),
ADD KEY `created_by_user_id` (`created_by_user_id`),
ADD KEY `edited_by_user_id` (`edited_by_user_id`);
--
-- Indizes für die Tabelle `ShiftTypes`
--
ALTER TABLE `ShiftTypes`
ADD PRIMARY KEY (`id`),
ADD KEY `angeltype_id` (`angeltype_id`);
--
-- Indizes für die Tabelle `User`
--
ALTER TABLE `User`
ADD PRIMARY KEY (`UID`),
ADD UNIQUE KEY `Nick` (`Nick`),
ADD KEY `api_key` (`api_key`),
ADD KEY `password_recovery_token` (`password_recovery_token`),
ADD KEY `force_active` (`force_active`),
ADD KEY `arrival_date` (`arrival_date`,`planned_arrival_date`),
ADD KEY `planned_departure_date` (`planned_departure_date`);
--
-- Indizes für die Tabelle `UserAngelTypes`
--
ALTER TABLE `UserAngelTypes`
ADD PRIMARY KEY (`id`),
ADD KEY `user_id` (`user_id`,`angeltype_id`,`confirm_user_id`),
ADD KEY `angeltype_id` (`angeltype_id`),
ADD KEY `confirm_user_id` (`confirm_user_id`),
ADD KEY `coordinator` (`coordinator`);
--
-- Indizes für die Tabelle `UserDriverLicenses`
--
ALTER TABLE `UserDriverLicenses`
ADD PRIMARY KEY (`user_id`);
--
-- Indizes für die Tabelle `UserGroups`
--
ALTER TABLE `UserGroups`
ADD PRIMARY KEY (`id`),
ADD KEY `uid` (`uid`,`group_id`),
ADD KEY `group_id` (`group_id`);
--
-- AUTO_INCREMENT für exportierte Tabellen
--
--
-- AUTO_INCREMENT für Tabelle `AngelTypes`
--
ALTER TABLE `AngelTypes`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4;
--
-- AUTO_INCREMENT für Tabelle `GroupPrivileges`
--
ALTER TABLE `GroupPrivileges`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=259;
--
-- AUTO_INCREMENT für Tabelle `LogEntries`
--
ALTER TABLE `LogEntries`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT für Tabelle `Messages`
--
ALTER TABLE `Messages`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT für Tabelle `NeededAngelTypes`
--
ALTER TABLE `NeededAngelTypes`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT für Tabelle `News`
--
ALTER TABLE `News`
MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT für Tabelle `NewsComments`
--
ALTER TABLE `NewsComments`
MODIFY `ID` bigint(11) NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT für Tabelle `Privileges`
--
ALTER TABLE `Privileges`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=40;
--
-- AUTO_INCREMENT für Tabelle `Questions`
--
ALTER TABLE `Questions`
MODIFY `QID` bigint(20) NOT NULL AUTO_INCREMENT;
--
-- AUTO_INCREMENT für Tabelle `Room`
--
ALTER TABLE `Room`
MODIFY `RID` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;
--
-- AUTO_INCREMENT für Tabelle `ShiftEntry`
--
ALTER TABLE `ShiftEntry`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;
--
-- AUTO_INCREMENT für Tabelle `Shifts`
--
ALTER TABLE `Shifts`
MODIFY `SID` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=13;
--
-- AUTO_INCREMENT für Tabelle `ShiftTypes`
--
ALTER TABLE `ShiftTypes`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=5;
--
-- AUTO_INCREMENT für Tabelle `User`
--
ALTER TABLE `User`
MODIFY `UID` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;
--
-- AUTO_INCREMENT für Tabelle `UserAngelTypes`
--
ALTER TABLE `UserAngelTypes`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=2;
--
-- AUTO_INCREMENT für Tabelle `UserGroups`
--
ALTER TABLE `UserGroups`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=13;
--
-- Constraints der exportierten Tabellen
--
--
-- Constraints der Tabelle `GroupPrivileges`
--
ALTER TABLE `GroupPrivileges`
ADD CONSTRAINT `groupprivileges_ibfk_1` FOREIGN KEY (`group_id`) REFERENCES `Groups` (`UID`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `groupprivileges_ibfk_2` FOREIGN KEY (`privilege_id`) REFERENCES `Privileges` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
--
-- Constraints der Tabelle `Messages`
--
ALTER TABLE `Messages`
ADD CONSTRAINT `messages_ibfk_1` FOREIGN KEY (`SUID`) REFERENCES `User` (`UID`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `messages_ibfk_2` FOREIGN KEY (`RUID`) REFERENCES `User` (`UID`) ON DELETE CASCADE ON UPDATE CASCADE;
--
-- Constraints der Tabelle `NeededAngelTypes`
--
ALTER TABLE `NeededAngelTypes`
ADD CONSTRAINT `neededangeltypes_ibfk_1` FOREIGN KEY (`room_id`) REFERENCES `Room` (`RID`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `neededangeltypes_ibfk_2` FOREIGN KEY (`shift_id`) REFERENCES `Shifts` (`SID`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `neededangeltypes_ibfk_3` FOREIGN KEY (`angel_type_id`) REFERENCES `AngelTypes` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
--
-- Constraints der Tabelle `News`
--
ALTER TABLE `News`
ADD CONSTRAINT `news_ibfk_1` FOREIGN KEY (`UID`) REFERENCES `User` (`UID`) ON DELETE CASCADE ON UPDATE CASCADE;
--
-- Constraints der Tabelle `NewsComments`
--
ALTER TABLE `NewsComments`
ADD CONSTRAINT `newscomments_ibfk_1` FOREIGN KEY (`Refid`) REFERENCES `News` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `newscomments_ibfk_2` FOREIGN KEY (`UID`) REFERENCES `User` (`UID`) ON DELETE CASCADE ON UPDATE CASCADE;
--
-- Constraints der Tabelle `Questions`
--
ALTER TABLE `Questions`
ADD CONSTRAINT `questions_ibfk_1` FOREIGN KEY (`UID`) REFERENCES `User` (`UID`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `questions_ibfk_2` FOREIGN KEY (`AID`) REFERENCES `User` (`UID`) ON DELETE CASCADE ON UPDATE CASCADE;
--
-- Constraints der Tabelle `ShiftEntry`
--
ALTER TABLE `ShiftEntry`
ADD CONSTRAINT `shiftentry_ibfk_1` FOREIGN KEY (`SID`) REFERENCES `Shifts` (`SID`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `shiftentry_ibfk_2` FOREIGN KEY (`UID`) REFERENCES `User` (`UID`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `shiftentry_ibfk_3` FOREIGN KEY (`TID`) REFERENCES `AngelTypes` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
--
-- Constraints der Tabelle `Shifts`
--
ALTER TABLE `Shifts`
ADD CONSTRAINT `shifts_ibfk_1` FOREIGN KEY (`RID`) REFERENCES `Room` (`RID`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `shifts_ibfk_2` FOREIGN KEY (`shifttype_id`) REFERENCES `ShiftTypes` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `shifts_ibfk_3` FOREIGN KEY (`created_by_user_id`) REFERENCES `User` (`UID`) ON DELETE SET NULL ON UPDATE CASCADE,
ADD CONSTRAINT `shifts_ibfk_4` FOREIGN KEY (`edited_by_user_id`) REFERENCES `User` (`UID`) ON DELETE SET NULL ON UPDATE CASCADE;
--
-- Constraints der Tabelle `ShiftTypes`
--
ALTER TABLE `ShiftTypes`
ADD CONSTRAINT `shifttypes_ibfk_1` FOREIGN KEY (`angeltype_id`) REFERENCES `AngelTypes` (`id`) ON DELETE CASCADE ON UPDATE CASCADE;
--
-- Constraints der Tabelle `UserAngelTypes`
--
ALTER TABLE `UserAngelTypes`
ADD CONSTRAINT `userangeltypes_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `User` (`UID`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `userangeltypes_ibfk_2` FOREIGN KEY (`angeltype_id`) REFERENCES `AngelTypes` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `userangeltypes_ibfk_3` FOREIGN KEY (`confirm_user_id`) REFERENCES `User` (`UID`) ON DELETE SET NULL ON UPDATE CASCADE;
--
-- Constraints der Tabelle `UserDriverLicenses`
--
ALTER TABLE `UserDriverLicenses`
ADD CONSTRAINT `userdriverlicenses_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `User` (`UID`) ON DELETE CASCADE ON UPDATE CASCADE;
--
-- Constraints der Tabelle `UserGroups`
--
ALTER TABLE `UserGroups`
ADD CONSTRAINT `usergroups_ibfk_1` FOREIGN KEY (`group_id`) REFERENCES `Groups` (`UID`) ON DELETE CASCADE ON UPDATE CASCADE,
ADD CONSTRAINT `usergroups_ibfk_2` FOREIGN KEY (`uid`) REFERENCES `User` (`UID`) ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -29,24 +29,9 @@ class ImportInstallSql extends Migration
'UserAngelTypes', 'UserAngelTypes',
'UserDriverLicenses', 'UserDriverLicenses',
'UserGroups', 'UserGroups',
'UserWorkLog',
]; ];
/**
* Run the migration
*/
public function up(): void
{
foreach ($this->oldTables as $table) {
if ($this->schema->hasTable($table)) {
return;
}
}
$sql = file_get_contents(__DIR__ . '/../install.sql');
$this->schema->getConnection()->unprepared($sql);
}
/** /**
* Reverse the migration * Reverse the migration
*/ */
@ -54,6 +39,7 @@ class ImportInstallSql extends Migration
{ {
$this->schema->getConnection()->statement('SET FOREIGN_KEY_CHECKS=0;'); $this->schema->getConnection()->statement('SET FOREIGN_KEY_CHECKS=0;');
// Delete all remaining tables
foreach ($this->oldTables as $table) { foreach ($this->oldTables as $table) {
if ($this->schema->hasTable($table)) { if ($this->schema->hasTable($table)) {
$this->schema->dropIfExists($table); $this->schema->dropIfExists($table);

View File

@ -8,24 +8,5 @@ use Engelsystem\Database\Migration\Migration;
class ImportUpdateSql extends Migration class ImportUpdateSql extends Migration
{ {
/** // Do nothing as the tables will be created by later migrations and deleted by ImportInstall
* Run the migration
*/
public function up(): void
{
if ($this->schema->hasTable('UserWorkLog')) {
return;
}
$sql = file_get_contents(__DIR__ . '/../update.sql');
$this->schema->getConnection()->unprepared($sql);
}
/**
* Reverse the migration
*/
public function down(): void
{
$this->schema->dropIfExists('UserWorkLog');
}
} }

View File

@ -9,7 +9,6 @@ use Illuminate\Database\Schema\Blueprint;
class ChangeUsersContactDectFieldSize extends Migration class ChangeUsersContactDectFieldSize extends Migration
{ {
/** @var array */
protected array $tables = [ protected array $tables = [
'AngelTypes' => 'contact_dect', 'AngelTypes' => 'contact_dect',
'users_contact' => 'dect', 'users_contact' => 'dect',

View File

@ -11,7 +11,6 @@ use stdClass;
class CreateRoomsTable extends Migration class CreateRoomsTable extends Migration
{ {
use ChangesReferences; use ChangesReferences;
use Reference;
/** /**
* Run the migration * Run the migration

View File

@ -37,6 +37,7 @@ class CreateWorklogsTable extends Migration
->get(); ->get();
foreach ($previousRecords as $previousRecord) { foreach ($previousRecords as $previousRecord) {
$worked_at = Carbon::createFromTimestamp($previousRecord->work_timestamp);
$created_at = Carbon::createFromTimestamp($previousRecord->created_timestamp); $created_at = Carbon::createFromTimestamp($previousRecord->created_timestamp);
$this->schema->getConnection() $this->schema->getConnection()
->table('worklogs') ->table('worklogs')
@ -44,7 +45,7 @@ class CreateWorklogsTable extends Migration
'id' => $previousRecord->id, 'id' => $previousRecord->id,
'user_id' => $previousRecord->user_id, 'user_id' => $previousRecord->user_id,
'creator_id' => $previousRecord->created_user_id, 'creator_id' => $previousRecord->created_user_id,
'worked_at' => $previousRecord->work_timestamp, 'worked_at' => $worked_at,
'hours' => $previousRecord->work_hours, 'hours' => $previousRecord->work_hours,
'comment' => $previousRecord->comment, 'comment' => $previousRecord->comment,
'created_at' => $created_at, 'created_at' => $created_at,
@ -87,11 +88,11 @@ class CreateWorklogsTable extends Migration
->insert([ ->insert([
'id' => $record->id, 'id' => $record->id,
'user_id' => $record->user_id, 'user_id' => $record->user_id,
'work_timestamp' => $record->worked_at->timestamp, 'work_timestamp' => Carbon::createFromFormat('Y-m-d', $record->worked_at)->timestamp,
'work_hours' => $record->hours, 'work_hours' => $record->hours,
'comment' => $record->comment, 'comment' => $record->comment,
'created_user_id' => $record->creator_id, 'created_user_id' => $record->creator_id,
'created_timestamp' => $record->created_at->timestamp, 'created_timestamp' => Carbon::createFromFormat('Y-m-d H:i:s', $record->created_at)->timestamp,
]); ]);
} }

View File

@ -10,8 +10,6 @@ use Illuminate\Database\Schema\Blueprint;
class AddTimestampsToQuestions extends Migration class AddTimestampsToQuestions extends Migration
{ {
use ChangesReferences;
/** /**
* Run the migration * Run the migration
*/ */

View File

@ -21,7 +21,7 @@ class AddNameMinutesAndTimestampsToSchedules extends Migration
$this->schema->table('schedules', function (Blueprint $table): void { $this->schema->table('schedules', function (Blueprint $table): void {
$table->string('name')->default('')->after('id'); $table->string('name')->default('')->after('id');
$table->integer('shift_type')->default(0)->after('name'); $table->unsignedInteger('shift_type')->default(0)->after('name');
$table->integer('minutes_before')->default(0)->after('shift_type'); $table->integer('minutes_before')->default(0)->after('shift_type');
$table->integer('minutes_after')->default(0)->after('minutes_before'); $table->integer('minutes_after')->default(0)->after('minutes_before');
$table->timestamps(); $table->timestamps();
@ -36,7 +36,7 @@ class AddNameMinutesAndTimestampsToSchedules extends Migration
$this->schema->table('schedules', function (Blueprint $table): void { $this->schema->table('schedules', function (Blueprint $table): void {
$table->string('name')->default(null)->change(); $table->string('name')->default(null)->change();
$table->integer('shift_type')->default(null)->change(); $table->unsignedInteger('shift_type')->default(null)->change();
$table->integer('minutes_before')->default(null)->change(); $table->integer('minutes_before')->default(null)->change();
$table->integer('minutes_after')->default(null)->change(); $table->integer('minutes_after')->default(null)->change();
}); });

View File

@ -9,8 +9,6 @@ use Illuminate\Database\Schema\Blueprint;
class AddEmailNewsToUsersSettings extends Migration class AddEmailNewsToUsersSettings extends Migration
{ {
use Reference;
/** /**
* Run the migration * Run the migration
*/ */

View File

@ -9,8 +9,6 @@ use Illuminate\Database\Schema\Blueprint;
class OauthAddTokens extends Migration class OauthAddTokens extends Migration
{ {
use Reference;
/** /**
* Run the migration * Run the migration
*/ */

View File

@ -9,8 +9,6 @@ use Illuminate\Database\Schema\Blueprint;
class NewsAddIsPinned extends Migration class NewsAddIsPinned extends Migration
{ {
use Reference;
/** /**
* Run the migration * Run the migration
*/ */

View File

@ -9,8 +9,6 @@ use Illuminate\Database\Schema\Blueprint;
class OauthChangeTokensToText extends Migration class OauthChangeTokensToText extends Migration
{ {
use Reference;
/** /**
* Run the migration * Run the migration
*/ */

View File

@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Config\Config;
use Engelsystem\Database\Migration\Migration;
use Engelsystem\Helpers\Carbon;
use Illuminate\Database\Schema\Builder as SchemaBuilder;
use stdClass;
class CreateFirstUser extends Migration
{
public function __construct(SchemaBuilder $schemaBuilder, protected Config $config)
{
parent::__construct($schemaBuilder);
}
/**
* Run the migration
*/
public function up(): void
{
$db = $this->schema->getConnection();
if ($db->table('users')->count() > 0) {
return;
}
$db->table('users')->insert([
'name' => 'admin',
'email' => 'admin@localhost',
'password' => password_hash('asdfasdf', PASSWORD_DEFAULT),
'api_key' => bin2hex(random_bytes(16)),
'created_at' => Carbon::now(),
]);
/** @var stdClass $admin */
$admin = $db->table('users')->where('name', 'admin')->first();
foreach (['users_contact', 'users_personal_data', 'users_state'] as $table) {
$db->table($table)->insert(['user_id' => $admin->id]);
}
$db->table('users_settings')->insert(['user_id' => $admin->id, 'language' => 'en_US', 'theme' => 0]);
}
}

View File

@ -6,14 +6,12 @@ namespace Engelsystem\Migrations;
use Engelsystem\Config\Config; use Engelsystem\Config\Config;
use Engelsystem\Database\Migration\Migration; use Engelsystem\Database\Migration\Migration;
use Engelsystem\Helpers\Authenticator;
use Illuminate\Database\Schema\Builder as SchemaBuilder; use Illuminate\Database\Schema\Builder as SchemaBuilder;
use stdClass;
class SetAdminPassword extends Migration class SetAdminPassword extends Migration
{ {
use Reference; public function __construct(SchemaBuilder $schemaBuilder, protected Config $config)
public function __construct(SchemaBuilder $schemaBuilder, protected Authenticator $auth, protected Config $config)
{ {
parent::__construct($schemaBuilder); parent::__construct($schemaBuilder);
} }
@ -23,12 +21,21 @@ class SetAdminPassword extends Migration
*/ */
public function up(): void public function up(): void
{ {
$admin = $this->auth->authenticate('admin', 'asdfasdf'); $db = $this->schema->getConnection();
/** @var stdClass $admin */
$admin = $db->table('users')->where('name', 'admin')->first();
$setupPassword = $this->config->get('setup_admin_password'); $setupPassword = $this->config->get('setup_admin_password');
if (!$admin || !$setupPassword) {
if (
!$admin
|| !password_verify('asdfasdf', $admin->password)
|| !$setupPassword
) {
return; return;
} }
$this->auth->setPassword($admin, $setupPassword); $db->table('users')
->where('id', $admin->id)
->update(['password' => password_hash($setupPassword, PASSWORD_DEFAULT)]);
} }
} }

View File

@ -9,8 +9,6 @@ use Illuminate\Database\Schema\Blueprint;
class AddShiftsDescription extends Migration class AddShiftsDescription extends Migration
{ {
use Reference;
/** /**
* Run the migration * Run the migration
*/ */

View File

@ -9,8 +9,6 @@ use Illuminate\Database\Schema\Blueprint;
class UsersSettingsAddEmailGoody extends Migration class UsersSettingsAddEmailGoody extends Migration
{ {
use Reference;
/** /**
* Run the migration * Run the migration
*/ */

View File

@ -0,0 +1,158 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
use stdClass;
class FillPrivilegesAndGroupsRelatedTables extends Migration
{
/**
* Inserts missing data into permissions & groups related tables
*/
public function up(): void
{
$db = $this->schema->getConnection();
if ($db->table('privileges')->count() > 0) {
return;
}
$db->table('groups')
->insert([
['id' => 10, 'name' => 'Guest'],
['id' => 20, 'name' => 'Angel'],
['id' => 30, 'name' => 'Welcome Angel'],
['id' => 35, 'name' => 'Voucher Angel'],
['id' => 50, 'name' => 'Shirt Manager'],
['id' => 60, 'name' => 'Shift Coordinator'],
['id' => 65, 'name' => 'Team Coordinator'],
['id' => 80, 'name' => 'Bureaucrat'],
['id' => 85, 'name' => 'News Admin'],
['id' => 90, 'name' => 'Developer'],
]);
$db->table('privileges')
->insert([
['id' => 1, 'name' => 'start', 'description' => 'Startseite für Gäste/Nicht eingeloggte User'],
['id' => 2, 'name' => 'login', 'description' => 'Logindialog'],
['id' => 3, 'name' => 'news', 'description' => 'Anzeigen der News-Seite'],
['id' => 4, 'name' => 'logout', 'description' => 'User darf sich ausloggen'],
['id' => 5, 'name' => 'register', 'description' => 'Einen neuen Engel registerieren'],
['id' => 6, 'name' => 'admin_rooms', 'description' => 'Räume administrieren'],
['id' => 7, 'name' => 'admin_angel_types', 'description' => 'Engel Typen administrieren'],
['id' => 8, 'name' => 'user_settings', 'description' => 'User profile settings'],
['id' => 9, 'name' => 'user_messages',
'description' => 'Writing and reading messages from user to user'],
['id' => 10, 'name' => 'admin_groups', 'description' => 'Manage usergroups and their rights'],
['id' => 14, 'name' => 'admin_news', 'description' => 'Administrate the news section'],
['id' => 15, 'name' => 'news_comments', 'description' => 'User can comment news'],
['id' => 16, 'name' => 'admin_user', 'description' => 'Administrate the angels'],
['id' => 17, 'name' => 'user_meetings', 'description' => 'Lists meetings (news)'],
['id' => 18, 'name' => 'admin_language', 'description' => 'Translate the system'],
['id' => 19, 'name' => 'admin_log', 'description' => 'Display recent changes'],
['id' => 21, 'name' => 'schedule.import', 'description' => 'Import rooms and shifts from schedule.xml'],
['id' => 24, 'name' => 'user_shifts', 'description' => 'Signup for shifts'],
['id' => 25, 'name' => 'user_shifts_admin', 'description' => 'Signup other angels for shifts.'],
['id' => 26, 'name' => 'user_myshifts',
'description' => 'Allow angels to view their own shifts and cancel them.'],
['id' => 27, 'name' => 'admin_arrive', 'description' => 'Mark angels when they arrive.'],
['id' => 28, 'name' => 'admin_shifts', 'description' => 'Create shifts'],
['id' => 30, 'name' => 'ical', 'description' => 'iCal shift export'],
['id' => 31, 'name' => 'admin_active',
'description' => 'Mark angels as active and if they got a t-shirt.'],
['id' => 32, 'name' => 'admin_free', 'description' => 'Show a list of free/unemployed angels.'],
['id' => 33, 'name' => 'admin_user_angeltypes', 'description' => 'Confirm restricted angel types'],
['id' => 34, 'name' => 'atom', 'description' => ' Atom news export'],
['id' => 35, 'name' => 'shifts_json_export', 'description' => 'Export shifts in JSON format'],
['id' => 36, 'name' => 'angeltypes', 'description' => 'View angeltypes'],
['id' => 37, 'name' => 'user_angeltypes', 'description' => 'Join angeltypes.'],
['id' => 38, 'name' => 'shifttypes', 'description' => 'Administrate shift types'],
['id' => 39, 'name' => 'admin_event_config', 'description' => 'Allow editing event config'],
['id' => 40, 'name' => 'view_rooms', 'description' => 'User can view rooms'],
['id' => 41, 'name' => 'shiftentry_edit_angeltype_supporter',
'description' => 'If user with this privilege is angeltype supporter, '
. 'he can put users in shifts for their angeltype'],
['id' => 43, 'name' => 'admin_user_worklog', 'description' => 'Manage user work log entries.'],
['id' => 44, 'name' => 'faq.view', 'description' => 'View FAQ entries'],
['id' => 45, 'name' => 'faq.edit', 'description' => 'Edit FAQ entries'],
['id' => 46, 'name' => 'question.add', 'description' => 'Ask questions'],
['id' => 47, 'name' => 'question.edit', 'description' => 'Answer questions'],
['id' => 48, 'name' => 'user.edit.shirt', 'description' => 'Edit user shirts'],
['id' => 49, 'name' => 'voucher.edit', 'description' => 'Edit vouchers'],
]);
$db->table('group_privileges')->insert([
['id' => 23, 'group_id' => 10, 'privilege_id' => 2],
['id' => 24, 'group_id' => 10, 'privilege_id' => 5],
['id' => 85, 'group_id' => 90, 'privilege_id' => 10],
['id' => 86, 'group_id' => 90, 'privilege_id' => 21],
['id' => 87, 'group_id' => 90, 'privilege_id' => 18],
['id' => 88, 'group_id' => 10, 'privilege_id' => 1],
['id' => 206, 'group_id' => 80, 'privilege_id' => 31],
['id' => 207, 'group_id' => 80, 'privilege_id' => 7],
['id' => 209, 'group_id' => 80, 'privilege_id' => 21],
['id' => 210, 'group_id' => 80, 'privilege_id' => 14],
['id' => 212, 'group_id' => 80, 'privilege_id' => 6],
['id' => 213, 'group_id' => 80, 'privilege_id' => 28],
['id' => 214, 'group_id' => 80, 'privilege_id' => 16],
['id' => 215, 'group_id' => 80, 'privilege_id' => 33],
['id' => 216, 'group_id' => 80, 'privilege_id' => 5],
['id' => 218, 'group_id' => 60, 'privilege_id' => 39],
['id' => 219, 'group_id' => 65, 'privilege_id' => 14],
['id' => 220, 'group_id' => 65, 'privilege_id' => 33],
['id' => 221, 'group_id' => 65, 'privilege_id' => 25],
['id' => 235, 'group_id' => 60, 'privilege_id' => 27],
['id' => 236, 'group_id' => 60, 'privilege_id' => 32],
['id' => 237, 'group_id' => 60, 'privilege_id' => 19],
['id' => 238, 'group_id' => 60, 'privilege_id' => 14],
['id' => 239, 'group_id' => 60, 'privilege_id' => 28],
['id' => 240, 'group_id' => 60, 'privilege_id' => 16],
['id' => 241, 'group_id' => 60, 'privilege_id' => 5],
['id' => 242, 'group_id' => 60, 'privilege_id' => 25],
['id' => 243, 'group_id' => 20, 'privilege_id' => 36],
['id' => 244, 'group_id' => 20, 'privilege_id' => 34],
['id' => 245, 'group_id' => 20, 'privilege_id' => 30],
['id' => 246, 'group_id' => 20, 'privilege_id' => 4],
['id' => 247, 'group_id' => 20, 'privilege_id' => 3],
['id' => 248, 'group_id' => 20, 'privilege_id' => 15],
['id' => 249, 'group_id' => 20, 'privilege_id' => 35],
['id' => 250, 'group_id' => 20, 'privilege_id' => 37],
['id' => 251, 'group_id' => 20, 'privilege_id' => 17],
['id' => 252, 'group_id' => 20, 'privilege_id' => 9],
['id' => 253, 'group_id' => 20, 'privilege_id' => 26],
['id' => 255, 'group_id' => 20, 'privilege_id' => 8],
['id' => 256, 'group_id' => 20, 'privilege_id' => 24],
['id' => 257, 'group_id' => 80, 'privilege_id' => 38],
['id' => 258, 'group_id' => 50, 'privilege_id' => 31],
['id' => 259, 'group_id' => 20, 'privilege_id' => 40],
['id' => 260, 'group_id' => 85, 'privilege_id' => 14],
['id' => 262, 'group_id' => 60, 'privilege_id' => 43],
['id' => 263, 'group_id' => 20, 'privilege_id' => 41],
['id' => 264, 'group_id' => 30, 'privilege_id' => 27],
['id' => 265, 'group_id' => 10, 'privilege_id' => 44],
['id' => 266, 'group_id' => 20, 'privilege_id' => 44],
['id' => 267, 'group_id' => 60, 'privilege_id' => 45],
['id' => 268, 'group_id' => 20, 'privilege_id' => 46],
['id' => 269, 'group_id' => 60, 'privilege_id' => 47],
['id' => 270, 'group_id' => 60, 'privilege_id' => 48],
['id' => 271, 'group_id' => 50, 'privilege_id' => 48],
['id' => 272, 'group_id' => 50, 'privilege_id' => 27],
['id' => 273, 'group_id' => 60, 'privilege_id' => 49],
['id' => 274, 'group_id' => 35, 'privilege_id' => 49],
['id' => 275, 'group_id' => 35, 'privilege_id' => 27],
]);
/** @var stdClass $admin */
$admin = $db->table('users')->where('name', 'admin')->first();
if (!$admin) {
return;
}
// Angel, ShiCo, Team coordinator, Bureaucrat, Dev
foreach ([20, 60, 65, 80, 90] as $group) {
$db->table('users_groups')->insert(['user_id' => $admin->id, 'group_id' => $group]);
}
}
}

View File

@ -6,6 +6,7 @@ namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration; use Engelsystem\Database\Migration\Migration;
use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Str;
class ChangeApiKeyLength extends Migration class ChangeApiKeyLength extends Migration
{ {
@ -24,6 +25,19 @@ class ChangeApiKeyLength extends Migration
*/ */
public function down(): void public function down(): void
{ {
$connection = $this->schema->getConnection();
$data = $connection->table('users')->get(['id', 'api_key']);
foreach ($data as $user) {
if (Str::length($user->api_key) <= 32) {
continue;
}
$key = Str::substr($user->api_key, 0, 32);
$connection->table('users')
->where('id', $user->id)
->update(['api_key' => $key]);
}
$this->schema->table('users', function (Blueprint $table): void { $this->schema->table('users', function (Blueprint $table): void {
$table->string('api_key', 32)->change(); $table->string('api_key', 32)->change();
}); });

View File

@ -9,8 +9,6 @@ use Illuminate\Database\Schema\Blueprint;
class AddEmailMessagesToUsersSettings extends Migration class AddEmailMessagesToUsersSettings extends Migration
{ {
use Reference;
/** /**
* Run the migration * Run the migration
*/ */

View File

@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
class CreateApiPermissions extends Migration
{
/**
* Run the migration
*/
public function up(): void
{
$db = $this->schema->getConnection();
$db->table('privileges')->insert([
['name' => 'api', 'description' => 'Use the API'],
]);
$db->table('groups')->insert([
['id' => 40, 'name' => 'API'],
]);
$bureaucratGroup = 80;
$apiId = $db->table('privileges')->where('name', 'api')->first()->id;
$db->table('group_privileges')->insert([
['group_id' => $bureaucratGroup, 'privilege_id' => $apiId],
['group_id' => 40, 'privilege_id' => $apiId],
]);
}
/**
* Reverse the migration
*/
public function down(): void
{
$db = $this->schema->getConnection();
$db->table('privileges')
->where('name', 'api')
->delete();
$db->table('groups')
->where('id', 40)
->delete();
}
}

View File

@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
use Illuminate\Support\Str;
class CleanupShortApiKeys extends Migration
{
/**
* Run the migration
*/
public function up(): void
{
$db = $this->schema->getConnection();
foreach ($db->table('users')->get() as $user) {
if (Str::length($user->api_key) > 42) {
continue;
}
$db->table('users')
->where('id', $user->id)
->update(['api_key' => bin2hex(random_bytes(32))]);
}
}
}

View File

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
use Illuminate\Database\Schema\Blueprint;
class AddIfsgCerificatesToUsersLicenses extends Migration
{
/**
* Run the migration
*/
public function up(): void
{
$this->schema->table('users_licenses', function (Blueprint $table): void {
$table->boolean('ifsg_certificate_light')->default(false)->after('drive_12t');
$table->boolean('ifsg_certificate')->default(false)->after('ifsg_certificate_light');
});
}
/**
* Reverse the migration
*/
public function down(): void
{
$this->schema->table('users_licenses', function (Blueprint $table): void {
$table->dropColumn('ifsg_certificate_light');
$table->dropColumn('ifsg_certificate');
});
}
}

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
use Illuminate\Database\Schema\Blueprint;
class AddRequiresIfsgCerificateToAngeltypes extends Migration
{
/**
* Run the migration
*/
public function up(): void
{
$this->schema->table('angel_types', function (Blueprint $table): void {
$table->boolean('requires_ifsg_certificate')->default(false)->after('requires_driver_license');
});
}
/**
* Reverse the migration
*/
public function down(): void
{
$this->schema->table('angel_types', function (Blueprint $table): void {
$table->dropColumn('requires_ifsg_certificate');
});
}
}

View File

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
use Illuminate\Database\Schema\Blueprint;
class AngeltypesRenameNoSelfSignupToShiftSelfSignup extends Migration
{
/**
* Run the migration
*/
public function up(): void
{
$this->schema->table('angel_types', function (Blueprint $table): void {
$table->renameColumn('no_self_signup', 'shift_self_signup')->default(true);
$connection = $this->schema->getConnection();
$connection->table('angel_types')
->update(['no_self_signup' => $connection->raw('NOT no_self_signup'),
]);
});
}
/**
* Reverse the migration
*/
public function down(): void
{
$this->schema->table('angel_types', function (Blueprint $table): void {
$table->renameColumn('shift_self_signup', 'no_self_signup');
$connection = $this->schema->getConnection();
$connection->table('angel_types')
->update(['shift_self_signup' => $connection->raw('NOT shift_self_signup'),
]);
});
}
}

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
use Illuminate\Database\Schema\Blueprint;
class AddHideOnShiftViewToAngeltypes extends Migration
{
/**
* Run the migration
*/
public function up(): void
{
$this->schema->table('angel_types', function (Blueprint $table): void {
$table->boolean('hide_on_shift_view')->default(false)->after('hide_register');
});
}
/**
* Reverse the migration
*/
public function down(): void
{
$this->schema->table('angel_types', function (Blueprint $table): void {
$table->dropColumn('hide_on_shift_view');
});
}
}

View File

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
use Illuminate\Database\Schema\Blueprint;
class AddUserToSessionsTable extends Migration
{
use Reference;
/**
* Run the migration
*/
public function up(): void
{
$this->schema->table('sessions', function (Blueprint $table): void {
$this->referencesUser($table)->nullable()->index()->after('payload');
});
}
/**
* Reverse the migration
*/
public function down(): void
{
$this->schema->table('sessions', function (Blueprint $table): void {
$table->dropForeign('sessions_user_id_foreign');
$table->dropColumn('user_id');
});
}
}

View File

@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
use Illuminate\Database\Schema\Blueprint;
class NewsRenameImportantToHighlight extends Migration
{
/**
* Run the migration
*/
public function up(): void
{
$this->schema->table('news', function (Blueprint $table): void {
$table->renameColumn('is_important', 'is_highlighted');
});
$this->schema->getConnection()
->table('privileges')
->where('name', 'news.important')
->update(['name' => 'news.highlight', 'description' => 'Highlight News']);
}
/**
* Reverse the migration
*/
public function down(): void
{
$this->schema->table('news', function (Blueprint $table): void {
$table->renameColumn('is_highlighted', 'is_important');
});
$this->schema->getConnection()
->table('privileges')
->where('name', 'news.highlight')
->update(['name' => 'news.important', 'description' => 'Make News Important']);
}
}

View File

@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
use Illuminate\Database\Schema\Blueprint;
class RenameRoomsToLocations extends Migration
{
/**
* Run the migration
*/
public function up(): void
{
$this->schema->rename('rooms', 'locations');
$this->schema->table('shifts', function (Blueprint $table): void {
$table->renameColumn('room_id', 'location_id');
});
$this->schema->table('needed_angel_types', function (Blueprint $table): void {
$table->renameColumn('room_id', 'location_id');
});
$db = $this->schema->getConnection();
$db->table('privileges')->where('name', 'admin_rooms')->update([
'name' => 'admin_locations',
'description' => 'Manage locations',
]);
$db->table('privileges')->where('name', 'view_rooms')->update([
'name' => 'view_locations',
'description' => 'User can view locations',
]);
$db->table('privileges')->where('name', 'schedule.import')->update([
'description' => 'Import locations and shifts from schedule.xml',
]);
}
/**
* Reverse the migration
*/
public function down(): void
{
$this->schema->rename('locations', 'rooms');
$this->schema->table('shifts', function (Blueprint $table): void {
$table->renameColumn('location_id', 'room_id');
});
$this->schema->table('needed_angel_types', function (Blueprint $table): void {
$table->renameColumn('location_id', 'room_id');
});
$db = $this->schema->getConnection();
$db->table('privileges')->where('name', 'admin_locations')->update([
'name' => 'admin_rooms',
'description' => 'Räume administrieren',
]);
$db->table('privileges')->where('name', 'view_locations')->update([
'name' => 'view_rooms',
'description' => 'User can view rooms',
]);
$db->table('privileges')->where('name', 'schedule.import')->update([
'description' => 'Import rooms and shifts from schedule.xml',
]);
}
}

View File

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
use Illuminate\Database\Schema\Blueprint;
class AddMissingScheduleForeignKeys extends Migration
{
use Reference;
/**
* Run the migration
*/
public function up(): void
{
$schemaManager = $this->schema->getConnection()->getDoctrineSchemaManager();
$hasShiftTypeReference = $schemaManager->introspectTable('schedules')
->hasIndex('schedules_shift_type_foreign');
if (!$hasShiftTypeReference) {
$this->schema->table('schedules', function (Blueprint $table): void {
$table->unsignedInteger('shift_type')->change();
$this->addReference($table, 'shift_type', 'shift_types');
});
}
$hasShiftIdReference = $schemaManager->introspectTable('schedule_shift')
->hasIndex('schedule_shift_schedule_id_foreign');
if (!$hasShiftIdReference) {
$this->schema->table('schedule_shift', function (Blueprint $table): void {
$table->unsignedInteger('shift_id')->change();
$this->addReference($table, 'shift_id', 'shifts');
});
}
}
}

View File

@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
class DegenderShirtSizes extends Migration
{
/** @var string[] */
protected array $sizes = [
'S-G' => 'S-F',
'M-G' => 'M-F',
'L-G' => 'L-F',
'XL-G' => 'XL-F',
];
/**
* Run the migration
*/
public function up(): void
{
$this->migrate($this->sizes);
}
/**
* Reverse the migration
*/
public function down(): void
{
$this->migrate(array_flip($this->sizes));
}
/**
* @param string[] $sizes
*/
private function migrate(array $sizes): void
{
$connection = $this->schema->getConnection();
foreach ($sizes as $from => $to) {
$connection
->table('users_personal_data')
->where('shirt_size', $from)
->update([
'shirt_size' => $to,
]);
}
}
}

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
use Illuminate\Database\Schema\Blueprint;
class AddUserInfoToUsersState extends Migration
{
/**
* Run the migration
*/
public function up(): void
{
$this->schema->table('users_state', function (Blueprint $table): void {
$table->string('user_info')->nullable()->default(null)->after('arrival_date');
});
}
/**
* Reverse the migration
*/
public function down(): void
{
$this->schema->table('users_state', function (Blueprint $table): void {
$table->dropColumn('user_info');
});
}
}

View File

@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
class AddUserInfoPermissions extends Migration
{
/**
* Run the migration
*/
public function up(): void
{
$db = $this->schema->getConnection();
$db->table('privileges')
->insert([
['name' => 'user.info.show', 'description' => 'Show User Info'],
['name' => 'user.info.edit', 'description' => 'Edit User Info'],
]);
$showUserInfo = $db->table('privileges')
->where('name', 'user.info.show')
->get(['id'])
->first();
$editUserInfo = $db->table('privileges')
->where('name', 'user.info.edit')
->get(['id'])
->first();
$buerocrat = 80;
$shico = 60;
$db->table('group_privileges')
->insertOrIgnore([
['group_id' => $buerocrat, 'privilege_id' => $editUserInfo->id],
['group_id' => $shico, 'privilege_id' => $showUserInfo->id],
]);
}
/**
* Reverse the migration
*/
public function down(): void
{
$db = $this->schema->getConnection();
$db->table('privileges')
->whereIn('name', ['user.info.edit', 'user.info.show'])
->delete();
}
}

View File

@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
use Illuminate\Database\Schema\Blueprint;
class AddUserIdToLogEntries extends Migration
{
use Reference;
/**
* Run the migration
*/
public function up(): void
{
$this->schema->table('log_entries', function (Blueprint $table): void {
$table->unsignedInteger('user_id')->after('id')->nullable()->default(null);
$table->foreign('user_id')
->references('id')->on('users')
->onUpdate('cascade')
->nullOnDelete();
});
}
/**
* Reverse the migration
*/
public function down(): void
{
$this->schema->table('log_entries', function (Blueprint $table): void {
$table->dropForeign('log_entries_user_id_foreign');
$table->dropColumn('user_id');
});
}
}

View File

@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
use Illuminate\Database\Connection;
use Illuminate\Database\Schema\Builder as SchemaBuilder;
class ChangeEditShirtRequireBureaucrat extends Migration
{
protected int $bureaucrat = 80;
protected int $shiCo = 60;
protected int $editShirt;
protected Connection $db;
public function __construct(SchemaBuilder $schema)
{
parent::__construct($schema);
$this->db = $this->schema->getConnection();
$this->editShirt = $this->db->table('privileges')
->where('name', 'user.edit.shirt')
->get(['id'])
->first()->id;
}
/**
* Run the migration
*/
public function up(): void
{
$this->movePermission($this->editShirt, $this->shiCo, $this->bureaucrat);
}
/**
* Reverse the migration
*/
public function down(): void
{
$this->movePermission($this->editShirt, $this->bureaucrat, $this->shiCo);
}
protected function movePermission(int $privilege, int $oldGroup, int $newGroup): void
{
$this->db->table('group_privileges')
->insertOrIgnore(['group_id' => $newGroup, 'privilege_id' => $privilege]);
$this->db->table('group_privileges')
->where(['group_id' => $oldGroup, 'privilege_id' => $privilege])
->delete();
}
}

View File

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
class AddLogsAllPermission extends Migration
{
/**
* Run the migration
*/
public function up(): void
{
$db = $this->schema->getConnection();
$db->table('privileges')
->insert([
['name' => 'logs.all', 'description' => 'View all logs'],
]);
$logsAll = $db->table('privileges')
->where('name', 'logs.all')
->get(['id'])
->first();
$bureaucrat = 80;
$db->table('group_privileges')
->insertOrIgnore([
['group_id' => $bureaucrat, 'privilege_id' => $logsAll->id],
]);
}
/**
* Reverse the migration
*/
public function down(): void
{
$db = $this->schema->getConnection();
$db->table('privileges')
->where('name', 'logs.all')
->delete();
}
}

View File

@ -0,0 +1,40 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
use Illuminate\Database\Schema\Blueprint;
class ScheduleShiftTypeNeededAngelTypes extends Migration
{
use Reference;
/**
* Run the migration
*/
public function up(): void
{
$this->schema->table('schedules', function (Blueprint $table): void {
$table->boolean('needed_from_shift_type')->after('shift_type')->default(false);
});
$this->schema->table('needed_angel_types', function (Blueprint $table): void {
$this->references($table, 'shift_types')->after('shift_id')->nullable();
});
}
/**
* Reverse the migration
*/
public function down(): void
{
$this->schema->table('schedules', function (Blueprint $table): void {
$table->dropColumn('needed_from_shift_type');
});
$this->schema->table('needed_angel_types', function (Blueprint $table): void {
$table->dropForeign('needed_angel_types_shift_type_id_foreign');
$table->dropColumn('shift_type_id');
});
}
}

View File

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
class AddUserEditPermission extends Migration
{
/**
* Run the migration
*/
public function up(): void
{
$db = $this->schema->getConnection();
$db->table('privileges')
->insert([
'name' => 'user.edit', 'description' => 'Edit user',
]);
$editUser = $db->table('privileges')
->where('name', 'user.edit')
->get(['id'])
->first();
$buerocrat = 80;
$db->table('group_privileges')
->insertOrIgnore([
'group_id' => $buerocrat, 'privilege_id' => $editUser->id,
]);
}
/**
* Reverse the migration
*/
public function down(): void
{
$db = $this->schema->getConnection();
$db->table('privileges')
->where('name', 'user.edit')
->delete();
}
}

View File

@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
use Illuminate\Database\Schema\Blueprint;
class CreateScheduleLocationsTable extends Migration
{
use ChangesReferences;
use Reference;
/**
* Creates the new table
*/
public function up(): void
{
$connection = $this->schema->getConnection();
$this->schema->create('schedule_locations', function (Blueprint $table): void {
$table->increments('id');
$this->references($table, 'schedules');
$this->references($table, 'locations');
$table->index(['schedule_id', 'location_id']);
});
$scheduleLocations = $connection
->table('schedule_shift')
->select(['schedules.id AS schedule_id', 'locations.id AS location_id'])
->leftJoin('schedules', 'schedules.id', 'schedule_shift.schedule_id')
->leftJoin('shifts', 'shifts.id', 'schedule_shift.shift_id')
->leftJoin('locations', 'locations.id', 'shifts.location_id')
->groupBy(['schedules.id', 'locations.id'])
->get();
foreach ($scheduleLocations as $scheduleLocation) {
$connection->table('schedule_locations')
->insert((array) $scheduleLocation);
}
}
/**
* Drops the table
*/
public function down(): void
{
$this->schema->drop('schedule_locations');
}
}

View File

@ -0,0 +1,77 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
use Illuminate\Database\Connection;
use Illuminate\Database\Schema\Builder as SchemaBuilder;
class AddShifttypesEditPermissionAndShifttypesRequiresShico extends Migration
{
protected int $bureaucrat = 80;
protected int $shiCo = 60;
protected int $shifttypes;
protected Connection $db;
public function __construct(SchemaBuilder $schema)
{
parent::__construct($schema);
$this->db = $this->schema->getConnection();
$this->shifttypes = $this->db->table('privileges')
->where('name', 'shifttypes')
->get(['id'])
->first()->id;
}
/**
* Run the migration
*/
public function up(): void
{
$db = $this->schema->getConnection();
$db->table('privileges')
->insert([
'name' => 'shifttypes.edit', 'description' => 'Edit shift types',
]);
$editShifttypes = $db->table('privileges')
->where('name', 'shifttypes.edit')
->get(['id'])
->first();
$this->movePermission($this->shifttypes, $this->bureaucrat, $this->shiCo);
$db->table('group_privileges')
->insertOrIgnore([
'group_id' => $this->bureaucrat, 'privilege_id' => $editShifttypes->id,
]);
}
/**
* Reverse the migration
*/
public function down(): void
{
$db = $this->schema->getConnection();
$db->table('privileges')
->where('name', 'shifttypes.edit')
->delete();
$this->movePermission($this->shifttypes, $this->shiCo, $this->bureaucrat);
}
protected function movePermission(int $privilege, int $oldGroup, int $newGroup): void
{
$this->db->table('group_privileges')
->insertOrIgnore(['group_id' => $newGroup, 'privilege_id' => $privilege]);
$this->db->table('group_privileges')
->where(['group_id' => $oldGroup, 'privilege_id' => $privilege])
->delete();
}
}

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
use Illuminate\Database\Schema\Blueprint;
class AddIfsgConfirmedToUsersLicenses extends Migration
{
/**
* Run the migration
*/
public function up(): void
{
$this->schema->table('users_licenses', function (Blueprint $table): void {
$table->boolean('ifsg_confirmed')->default(false)->after('ifsg_certificate');
});
}
/**
* Reverse the migration
*/
public function down(): void
{
$this->schema->table('users_licenses', function (Blueprint $table): void {
$table->dropColumn('ifsg_confirmed');
});
}
}

View File

@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
class AddUserIfsgEditPermission extends Migration
{
/**
* Run the migration
*/
public function up(): void
{
$db = $this->schema->getConnection();
$db->table('privileges')
->insert([
'name' => 'user.ifsg.edit', 'description' => 'Edit IfSG Certificate',
]);
$editIfsg = $db->table('privileges')
->where('name', 'user.ifsg.edit')
->get(['id'])
->first();
$shico = 60;
$team_coordinator = 65;
$db->table('group_privileges')
->insertOrIgnore([
['group_id' => $shico, 'privilege_id' => $editIfsg->id],
['group_id' => $team_coordinator, 'privilege_id' => $editIfsg->id],
]);
}
/**
* Reverse the migration
*/
public function down(): void
{
$db = $this->schema->getConnection();
$db->table('privileges')
->where('name', 'user.ifsg.edit')
->delete();
}
}

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
use Illuminate\Database\Schema\Blueprint;
class AddDriveConfirmedToUsersLicenses extends Migration
{
/**
* Run the migration
*/
public function up(): void
{
$this->schema->table('users_licenses', function (Blueprint $table): void {
$table->boolean('drive_confirmed')->default(false)->after('drive_12t');
});
}
/**
* Reverse the migration
*/
public function down(): void
{
$this->schema->table('users_licenses', function (Blueprint $table): void {
$table->dropColumn('drive_confirmed');
});
}
}

View File

@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
class AddUserDriveEditPermission extends Migration
{
/**
* Run the migration
*/
public function up(): void
{
$db = $this->schema->getConnection();
$db->table('privileges')
->insert([
'name' => 'user.drive.edit', 'description' => 'Edit Driving License',
]);
$editDrive = $db->table('privileges')
->where('name', 'user.drive.edit')
->get(['id'])
->first();
$shico = 60;
$team_coordinator = 65;
$db->table('group_privileges')
->insertOrIgnore([
['group_id' => $shico, 'privilege_id' => $editDrive->id],
['group_id' => $team_coordinator, 'privilege_id' => $editDrive->id],
]);
}
/**
* Reverse the migration
*/
public function down(): void
{
$db = $this->schema->getConnection();
$db->table('privileges')
->where('name', 'user.drive.edit')
->delete();
}
}

View File

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
class AddUserFaEditPermission extends Migration
{
/**
* Run the migration
*/
public function up(): void
{
$db = $this->schema->getConnection();
$db->table('privileges')
->insert([
'name' => 'user.fa.edit', 'description' => 'Edit User Force Active State',
]);
$editFa = $db->table('privileges')
->where('name', 'user.fa.edit')
->get(['id'])
->first();
$bureaucrat = 80;
$db->table('group_privileges')
->insertOrIgnore([
['group_id' => $bureaucrat, 'privilege_id' => $editFa->id],
]);
}
/**
* Reverse the migration
*/
public function down(): void
{
$db = $this->schema->getConnection();
$db->table('privileges')
->where('name', 'user.fa.edit')
->delete();
}
}

View File

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
use Illuminate\Database\Schema\Blueprint;
class RenameGoodyToGoodie extends Migration
{
/**
* Run the migration
*/
public function up(): void
{
$this->schema->table('users_settings', function (Blueprint $table): void {
$table->renameColumn('email_goody', 'email_goodie');
});
}
/**
* Reverse the migration
*/
public function down(): void
{
$this->schema->table('users_settings', function (Blueprint $table): void {
$table->renameColumn('email_goodie', 'email_goody');
});
}
}

View File

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
use Illuminate\Database\Schema\Blueprint;
class RenameShirtToGoodie extends Migration
{
/**
* Run the migration
*/
public function up(): void
{
$this->schema->table('users_state', function (Blueprint $table): void {
$table->renameColumn('got_shirt', 'got_goodie');
});
$db = $this->schema->getConnection();
$db->table('privileges')->where('name', 'admin_active')->update([
'description' => 'Mark angels as active and if they got a goodie.',
]);
$db->table('privileges')->where('name', 'user.edit.shirt')->update([
'name' => 'user.goodie.edit',
'description' => 'Edit user goodies',
]);
}
/**
* Reverse the migration
*/
public function down(): void
{
$this->schema->table('users_state', function (Blueprint $table): void {
$table->renameColumn('got_goodie', 'got_shirt');
});
$db = $this->schema->getConnection();
$db->table('privileges')->where('name', 'admin_active')->update([
'description' => 'Mark angels as active and if they got a t-shirt.',
]);
$db->table('privileges')->where('name', 'user.goodie.edit')->update([
'name' => 'user.edit.shirt',
'description' => 'Edit user shirts',
]);
}
}

View File

@ -0,0 +1,205 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
use Illuminate\Database\Connection;
use Illuminate\Database\Schema\Builder as SchemaBuilder;
class RefactorPermissionsAndGroups extends Migration
{
protected int $developer = 90;
protected int $bureaucrat = 80;
protected int $shiCo = 60;
protected int $newsAdmin = 85;
protected int $teamCoordinator = 65;
protected int $angel = 20;
protected int $active;
protected int $driveEdit;
protected int $eventConfig;
protected int $goodieEdit;
protected int $ifsgEdit;
protected int $log;
protected int $news;
protected int $register;
protected int $scheduleImport;
protected int $shifts;
protected int $user;
protected int $userAngeltypes;
protected int $userShifts;
protected string $shiftentry = 'shiftentry_edit_angeltype_supporter';
protected string $language = 'admin_language';
protected string $userEdit = 'user.edit';
protected string $userNickEdit = 'user.nick.edit';
protected string $shifttypes = 'shifttypes';
protected string $shifttypesView = 'shifttypes.view';
protected Connection $db;
public function __construct(SchemaBuilder $schema)
{
parent::__construct($schema);
$this->db = $this->schema->getConnection();
$this->active = $this->getPrivilegeId('admin_active');
$this->driveEdit = $this->getPrivilegeId('user.drive.edit');
$this->eventConfig = $this->getPrivilegeId('admin_event_config');
$this->goodieEdit = $this->getPrivilegeId('user.goodie.edit');
$this->ifsgEdit = $this->getPrivilegeId('user.ifsg.edit');
$this->log = $this->getPrivilegeId('admin_log');
$this->news = $this->getPrivilegeId('admin_news');
$this->register = $this->getPrivilegeId('register');
$this->scheduleImport = $this->getPrivilegeId('schedule.import');
$this->shifts = $this->getPrivilegeId('admin_shifts');
$this->user = $this->getPrivilegeId('admin_user');
$this->userAngeltypes = $this->getPrivilegeId('admin_user_angeltypes');
$this->userShifts = $this->getPrivilegeId('user_shifts_admin');
}
/**
* Run the migration
*/
public function up(): void
{
$this->deletePermission($this->shiftentry);
$this->deletePermission($this->language);
$this->movePermission($this->active, $this->bureaucrat, $this->shiCo);
$this->movePermission($this->userAngeltypes, $this->bureaucrat, $this->shiCo);
$this->movePermission($this->eventConfig, $this->shiCo, $this->developer);
$this->movePermission($this->goodieEdit, $this->bureaucrat, $this->shiCo);
$this->insertGroupPermission($this->log, $this->bureaucrat);
$this->deleteGroupPermission($this->news, $this->bureaucrat);
$this->deleteGroupPermission($this->shifts, $this->bureaucrat);
$this->deleteGroupPermission($this->user, $this->bureaucrat);
$this->deleteGroupPermission($this->register, $this->bureaucrat);
$this->deleteGroupPermission($this->scheduleImport, $this->developer);
$this->updatePermission($this->shifttypes, $this->shifttypesView, 'View shift types');
$this->updatePermission($this->userEdit, $this->userNickEdit, 'Edit user nick');
$this->deleteGroup($this->newsAdmin);
$this->deleteGroup($this->teamCoordinator);
}
/**
* Reverse the migration
*/
public function down(): void
{
$this->insertPermission(
$this->shiftentry,
'If user with this privilege is angeltype supporter, he can put users in shifts for their angeltype',
$this->angel
);
$this->insertPermission(
$this->language,
'Translate the system',
$this->developer
);
$this->movePermission($this->active, $this->shiCo, $this->bureaucrat);
$this->movePermission($this->userAngeltypes, $this->shiCo, $this->bureaucrat);
$this->movePermission($this->eventConfig, $this->developer, $this->shiCo);
$this->movePermission($this->goodieEdit, $this->shiCo, $this->bureaucrat);
$this->deleteGroupPermission($this->log, $this->bureaucrat);
$this->insertGroupPermission($this->news, $this->bureaucrat);
$this->insertGroupPermission($this->shifts, $this->bureaucrat);
$this->insertGroupPermission($this->user, $this->bureaucrat);
$this->insertGroupPermission($this->register, $this->bureaucrat);
$this->insertGroupPermission($this->scheduleImport, $this->developer);
$this->updatePermission($this->shifttypesView, $this->shifttypes, 'Administrate shift types');
$this->updatePermission($this->userNickEdit, $this->userEdit, 'Edit user');
$this->insertGroup($this->newsAdmin, 'News Admin', [$this->news]);
$this->insertGroup($this->teamCoordinator, 'Team Coordinator', [
$this->news,
$this->userAngeltypes,
$this->driveEdit,
$this->ifsgEdit,
$this->userShifts,
]);
}
protected function getPrivilegeId(string $privilege): int
{
return $this->db->table('privileges')
->where('name', $privilege)
->get(['id'])
->first()->id;
}
protected function deleteGroup(int $group): void
{
$this->db->table('groups')
->where(['id' => $group])
->delete();
}
protected function insertGroup(int $id, string $name, array $privileges): void
{
$this->db->table('groups')
->insertOrIgnore([
'name' => $name,
'id' => $id,
]);
foreach ($privileges as $privilege) {
$this->insertGroupPermission($privilege, $id);
}
}
protected function deleteGroupPermission(int $privilege, int $group): void
{
$this->db->table('group_privileges')
->where(['group_id' => $group, 'privilege_id' => $privilege])
->delete();
}
protected function insertGroupPermission(int $privilege, int $group): void
{
$this->db->table('group_privileges')
->insertOrIgnore([
['group_id' => $group, 'privilege_id' => $privilege],
]);
}
protected function movePermission(int $privilege, int $oldGroup, int $newGroup): void
{
$this->insertGroupPermission($privilege, $newGroup);
$this->deleteGroupPermission($privilege, $oldGroup);
}
protected function insertPermission(string $name, string $description, int $group): void
{
$this->db->table('privileges')
->insertOrIgnore([
'name' => $name, 'description' => $description,
]);
$permission = $this->getPrivilegeId($name);
$this->insertGroupPermission($permission, $group);
}
protected function deletePermission(string $privilege): void
{
$this->db->table('privileges')
->where(['name' => $privilege])
->delete();
}
protected function updatePermission(string $oldName, string $newName, string $description): void
{
$this->db->table('privileges')->where('name', $oldName)->update([
'name' => $newName,
'description' => $description,
]);
}
}

View File

@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
use Illuminate\Database\Connection;
use Illuminate\Database\Schema\Builder as SchemaBuilder;
class AddUsersArriveListPermission extends Migration
{
protected int $voucher = 35;
protected int $arrive;
protected Connection $db;
public function __construct(SchemaBuilder $schema)
{
parent::__construct($schema);
$this->db = $this->schema->getConnection();
$this->arrive = $this->db->table('privileges')
->where('name', 'admin_arrive')
->get(['id'])
->first()->id;
}
/**
* Run the migration
*/
public function up(): void
{
$this->db->table('privileges')
->insert([
'name' => 'users.arrive.list', 'description' => 'View arrive angels list',
]);
$arriveList = $this->db->table('privileges')
->where('name', 'users.arrive.list')
->get(['id'])
->first()->id;
// Goodie Manager, Shift Coordinator, Voucher Angel, Welcome Angel
$groups = [50, 60, 35, 30];
foreach ($groups as $group) {
$this->db->table('group_privileges')
->insertOrIgnore([
['group_id' => $group, 'privilege_id' => $arriveList],
]);
}
$this->db->table('group_privileges')
->where(['group_id' => $this->voucher, 'privilege_id' => $this->arrive])
->delete();
}
/**
* Reverse the migration
*/
public function down(): void
{
$this->db->table('privileges')
->where('name', 'users.arrive.list')
->delete();
$this->db->table('group_privileges')
->insertOrIgnore([
['group_id' => $this->voucher, 'privilege_id' => $this->arrive],
]);
}
}

View File

@ -30,7 +30,7 @@ trait Reference
$table->primary($fromColumn); $table->primary($fromColumn);
} }
$this->addReference($table, $fromColumn, $targetTable, $targetColumn ?: 'id'); $this->addReference($table, $fromColumn, $targetTable, $targetColumn);
return $col; return $col;
} }

View File

@ -1,59 +0,0 @@
INSERT INTO `Privileges` (`id`, `name`, `desc`) VALUES (40, 'view_rooms', 'User can view rooms');
INSERT INTO `GroupPrivileges` (`id`, `group_id`, `privilege_id`) VALUES (NULL, '-2', '40');
ALTER TABLE `UserAngelTypes` CHANGE `coordinator` `supporter` BOOLEAN;
ALTER TABLE `User` ADD COLUMN `email_by_human_allowed` BOOLEAN NOT NULL;
-- No Self Sign Up for some Angel Types
ALTER TABLE AngelTypes ADD no_self_signup TINYINT(1) NOT NULL;
ALTER TABLE `AngelTypes`
ADD `contact_user_id` INT NULL,
ADD `contact_name` VARCHAR(250) NULL,
ADD `contact_dect` VARCHAR(5) NULL,
ADD `contact_email` VARCHAR(250) NULL,
ADD INDEX (`contact_user_id`);
ALTER TABLE `AngelTypes`
ADD FOREIGN KEY (`contact_user_id`) REFERENCES `User`(`UID`) ON DELETE SET NULL ON UPDATE CASCADE;
INSERT INTO `Privileges` (`id`, `name`, `desc`) VALUES (NULL, 'shiftentry_edit_angeltype_supporter', 'If user with this privilege is angeltype supporter, he can put users in shifts for their angeltype');
-- DB Performance
ALTER TABLE `Shifts` ADD INDEX(`start`);
ALTER TABLE `NeededAngelTypes` ADD INDEX(`count`);
-- Security
UPDATE `Groups` SET UID = UID * 10;
INSERT INTO `Groups` (Name, UID) VALUES ('News Admin', -65);
INSERT INTO `Privileges` (id, name, `desc`) VALUES (42, 'admin_news_html', 'Use HTML in news');
INSERT INTO `GroupPrivileges` (group_id, privilege_id) VALUES (-65, 14), (-65, 42);
-- Add log level to LogEntries
ALTER TABLE `LogEntries` CHANGE COLUMN `nick` `level` VARCHAR(20) NOT NULL;
-- Angeltype contact update
ALTER TABLE `AngelTypes` DROP FOREIGN KEY angeltypes_ibfk_1;
ALTER TABLE `AngelTypes` DROP `contact_user_id`;
-- Room update
ALTER TABLE `Room` DROP `Number`;
ALTER TABLE `Room` DROP `show`;
ALTER TABLE `Room` DROP `Man`;
ALTER TABLE `Room` ADD `from_frab` BOOLEAN NOT NULL AFTER `FromPentabarf`;
UPDATE Room SET `from_frab` = (`FromPentabarf` = 'Y');
ALTER TABLE `Room` DROP `FromPentabarf`;
ALTER TABLE `Room` ADD `map_url` VARCHAR(300) NULL AFTER `from_frab`;
ALTER TABLE `Room` ADD `description` TEXT NULL AFTER `map_url`;
-- Dashboard
ALTER TABLE `AngelTypes` ADD `show_on_dashboard` BOOLEAN NOT NULL AFTER `contact_email`;
UPDATE `AngelTypes` SET `show_on_dashboard` = TRUE;
-- Work Log
CREATE TABLE `UserWorkLog` ( `id` INT NOT NULL AUTO_INCREMENT , `user_id` INT NOT NULL , `work_hours` DECIMAL NOT NULL , `comment` VARCHAR(200) NOT NULL , `created_user_id` INT NOT NULL , `created_timestamp` INT NOT NULL , PRIMARY KEY (`id`), INDEX (`user_id`), INDEX (`created_user_id`)) ENGINE = InnoDB;
ALTER TABLE `UserWorkLog` ADD FOREIGN KEY (`created_user_id`) REFERENCES `User`(`UID`) ON DELETE CASCADE ON UPDATE CASCADE; ALTER TABLE `UserWorkLog` ADD FOREIGN KEY (`user_id`) REFERENCES `User`(`UID`) ON DELETE CASCADE ON UPDATE CASCADE;
ALTER TABLE `UserWorkLog` ADD INDEX(`created_timestamp`);
INSERT INTO `Privileges` (`id`, `name`, `desc`) VALUES (NULL, 'admin_user_worklog', 'Manage user work log entries.');
ALTER TABLE `UserWorkLog` CHANGE `work_hours` `work_hours` DECIMAL(10,2) NOT NULL;
ALTER TABLE `UserWorkLog` ADD `work_timestamp` INT NOT NULL AFTER `user_id`;

View File

@ -8,7 +8,7 @@ RUN composer --no-ansi dump-autoload --optimize
FROM alpine AS translation FROM alpine AS translation
RUN apk add gettext RUN apk add gettext
COPY resources/lang/ /data COPY resources/lang/ /data
RUN find /data -type f -name '*.po' -exec sh -c 'file="{}"; msgfmt "${file%.*}.po" -o "${file%.*}.mo"' \; RUN find /data -type f -name '*.po' -exec sh -c 'msgfmt "${1%.*}.po" -o"${1%.*}.mo"' shell {} \;
# Build the themes # Build the themes
FROM node:20-alpine AS themes FROM node:20-alpine AS themes
@ -26,6 +26,7 @@ COPY config/ /app/config
COPY db/ /app/db COPY db/ /app/db
COPY includes/ /app/includes COPY includes/ /app/includes
COPY public/ /app/public COPY public/ /app/public
COPY resources/api /app/resources/api
COPY resources/views /app/resources/views COPY resources/views /app/resources/views
COPY src/ /app/src COPY src/ /app/src
COPY storage/ /app/storage COPY storage/ /app/storage

View File

@ -24,7 +24,7 @@ ENV TRUSTED_PROXIES 10.0.0.0/8,::ffff:10.0.0.0/8,\
# Engelsystem development workspace # Engelsystem development workspace
# Contains all tools required to build / manage the system # Contains all tools required to build / manage the system
FROM es_base AS es_workspace FROM es_base AS es_workspace
RUN echo 'memory_limit = 512M' > /usr/local/etc/php/conf.d/docker-php.ini RUN echo 'memory_limit = 1024M' > /usr/local/etc/php/conf.d/docker-php.ini
RUN apk add --no-cache gettext git nodejs npm yarn RUN apk add --no-cache gettext git nodejs npm yarn
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
ENTRYPOINT php -r 'sleep(PHP_INT_MAX);' ENTRYPOINT php -r 'sleep(PHP_INT_MAX);'

View File

@ -20,10 +20,7 @@ services:
APP_NAME: Engelsystem DEV APP_NAME: Engelsystem DEV
env_file: deployment.env env_file: deployment.env
ports: ports:
- "5080:80" - "127.0.0.1:5080:80"
networks:
- database
- internet
depends_on: depends_on:
- es_database - es_database
es_workspace: es_workspace:
@ -44,11 +41,17 @@ services:
ENVIRONMENT: development ENVIRONMENT: development
MAIL_DRIVER: log MAIL_DRIVER: log
APP_NAME: Engelsystem DEV APP_NAME: Engelsystem DEV
networks:
- database
- internet
depends_on: depends_on:
- es_database - es_database
es_phpmyadmin:
image: phpmyadmin
environment:
PMA_HOST: es_database
ports:
- "127.0.0.1:8888:80"
depends_on:
- es_database
profiles: [ dev ]
es_database: es_database:
image: mariadb:10.2 image: mariadb:10.2
environment: environment:
@ -59,12 +62,5 @@ services:
MYSQL_INITDB_SKIP_TZINFO: "yes" MYSQL_INITDB_SKIP_TZINFO: "yes"
volumes: volumes:
- db:/var/lib/mysql - db:/var/lib/mysql
networks:
- database
volumes: volumes:
db: {} db: {}
networks:
database:
internal: true
internet:

View File

@ -2,7 +2,7 @@
use Engelsystem\Helpers\Carbon; use Engelsystem\Helpers\Carbon;
use Engelsystem\Models\AngelType; use Engelsystem\Models\AngelType;
use Engelsystem\Models\Room; use Engelsystem\Models\Location;
use Engelsystem\Models\UserAngelType; use Engelsystem\Models\UserAngelType;
use Engelsystem\ShiftsFilter; use Engelsystem\ShiftsFilter;
use Engelsystem\ShiftsFilterRenderer; use Engelsystem\ShiftsFilterRenderer;
@ -17,7 +17,7 @@ use Illuminate\Support\Collection;
*/ */
function angeltypes_title() function angeltypes_title()
{ {
return __('Angeltypes'); return __('angeltypes.angeltypes');
} }
/** /**
@ -48,7 +48,7 @@ function angeltypes_controller()
function angeltype_link($angeltype_id, $params = []) function angeltype_link($angeltype_id, $params = [])
{ {
$params = array_merge(['action' => 'view', 'angeltype_id' => $angeltype_id], $params); $params = array_merge(['action' => 'view', 'angeltype_id' => $angeltype_id], $params);
return page_link_to('angeltypes', $params); return url('/angeltypes', $params);
} }
/** /**
@ -59,7 +59,7 @@ function angeltype_link($angeltype_id, $params = [])
function angeltype_delete_controller() function angeltype_delete_controller()
{ {
if (!auth()->can('admin_angel_types')) { if (!auth()->can('admin_angel_types')) {
throw_redirect(page_link_to('angeltypes')); throw_redirect(url('/angeltypes'));
} }
$angeltype = AngelType::findOrFail(request()->input('angeltype_id')); $angeltype = AngelType::findOrFail(request()->input('angeltype_id'));
@ -68,11 +68,11 @@ function angeltype_delete_controller()
$angeltype->delete(); $angeltype->delete();
engelsystem_log('Deleted angeltype: ' . AngelType_name_render($angeltype, true)); engelsystem_log('Deleted angeltype: ' . AngelType_name_render($angeltype, true));
success(sprintf(__('Angeltype %s deleted.'), $angeltype->name)); success(sprintf(__('Angeltype %s deleted.'), $angeltype->name));
throw_redirect(page_link_to('angeltypes')); throw_redirect(url('/angeltypes'));
} }
return [ return [
sprintf(__('Delete angeltype %s'), $angeltype->name), sprintf(__('Delete angeltype %s'), htmlspecialchars($angeltype->name)),
AngelType_delete_view($angeltype), AngelType_delete_view($angeltype),
]; ];
} }
@ -92,14 +92,14 @@ function angeltype_edit_controller()
// Edit existing angeltype // Edit existing angeltype
$angeltype = AngelType::findOrFail($request->input('angeltype_id')); $angeltype = AngelType::findOrFail($request->input('angeltype_id'));
if (!auth()->user()->isAngelTypeSupporter($angeltype) && !auth()->can('admin_user_angeltypes')) { if (!auth()->user()?->isAngelTypeSupporter($angeltype) && !auth()->can('admin_user_angeltypes')) {
throw_redirect(page_link_to('angeltypes')); throw_redirect(url('/angeltypes'));
} }
} else { } else {
// New angeltype // New angeltype
if ($supporter_mode) { if ($supporter_mode) {
// Supporters aren't allowed to create new angeltypes. // Supporters aren't allowed to create new angeltypes.
throw_redirect(page_link_to('angeltypes')); throw_redirect(url('/angeltypes'));
} }
$angeltype = new AngelType(); $angeltype = new AngelType();
} }
@ -118,11 +118,13 @@ function angeltype_edit_controller()
} }
$angeltype->restricted = $request->has('restricted'); $angeltype->restricted = $request->has('restricted');
$angeltype->no_self_signup = $request->has('no_self_signup'); $angeltype->shift_self_signup = $request->has('shift_self_signup');
$angeltype->show_on_dashboard = $request->has('show_on_dashboard'); $angeltype->show_on_dashboard = $request->has('show_on_dashboard');
$angeltype->hide_register = $request->has('hide_register'); $angeltype->hide_register = $request->has('hide_register');
$angeltype->hide_on_shift_view = $request->has('hide_on_shift_view');
$angeltype->requires_driver_license = $request->has('requires_driver_license'); $angeltype->requires_driver_license = $request->has('requires_driver_license');
$angeltype->requires_ifsg_certificate = $request->has('requires_ifsg_certificate');
} }
$angeltype->description = strip_request_item_nl('description', $angeltype->description); $angeltype->description = strip_request_item_nl('description', $angeltype->description);
@ -134,23 +136,29 @@ function angeltype_edit_controller()
if ($valid) { if ($valid) {
$angeltype->save(); $angeltype->save();
success('Angel type saved.'); success(__('Angel type saved.'));
engelsystem_log( engelsystem_log(
'Saved angeltype: ' . $angeltype->name . ($angeltype->restricted ? ', restricted' : '') 'Saved angeltype: ' . $angeltype->name . ($angeltype->restricted ? ', restricted' : '')
. ($angeltype->no_self_signup ? ', no_self_signup' : '') . ($angeltype->shift_self_signup ? ', shift_self_signup' : '')
. ($angeltype->requires_driver_license ? ', requires driver license' : '') . ', ' . (config('driving_license_enabled')
? (($angeltype->requires_driver_license ? ', requires driver license' : '') . ', ')
: '')
. (config('ifsg_enabled')
? (($angeltype->requires_ifsg_certificate ? ', requires ifsg certificate' : '') . ', ')
: '')
. $angeltype->contact_name . ', ' . $angeltype->contact_name . ', '
. $angeltype->contact_dect . ', ' . $angeltype->contact_dect . ', '
. $angeltype->contact_email . ', ' . $angeltype->contact_email . ', '
. $angeltype->show_on_dashboard . ', ' . $angeltype->show_on_dashboard . ', '
. $angeltype->hide_register . $angeltype->hide_register . ', '
. $angeltype->hide_on_shift_view
); );
throw_redirect(angeltype_link($angeltype->id)); throw_redirect(angeltype_link($angeltype->id));
} }
} }
return [ return [
sprintf(__('Edit %s'), $angeltype->name), sprintf(__('Edit %s'), htmlspecialchars((string) $angeltype->name)),
AngelType_edit_view($angeltype, $supporter_mode), AngelType_edit_view($angeltype, $supporter_mode),
]; ];
} }
@ -165,13 +173,15 @@ function angeltype_controller()
$user = auth()->user(); $user = auth()->user();
if (!auth()->can('angeltypes')) { if (!auth()->can('angeltypes')) {
throw_redirect(page_link_to('/')); throw_redirect(url('/'));
} }
$angeltype = AngelType::findOrFail(request()->input('angeltype_id')); $angeltype = AngelType::findOrFail(request()->input('angeltype_id'));
/** @var UserAngelType $user_angeltype */ /** @var UserAngelType $user_angeltype */
$user_angeltype = UserAngelType::whereUserId($user->id)->where('angel_type_id', $angeltype->id)->first(); $user_angeltype = UserAngelType::whereUserId($user->id)->where('angel_type_id', $angeltype->id)->first();
$members = $angeltype->userAngelTypes->sortBy('name', SORT_NATURAL | SORT_FLAG_CASE); $members = $angeltype->userAngelTypes
->sortBy('name', SORT_NATURAL | SORT_FLAG_CASE)
->load(['state', 'personalData', 'contact']);
$days = angeltype_controller_shiftsFilterDays($angeltype); $days = angeltype_controller_shiftsFilterDays($angeltype);
$shiftsFilter = angeltype_controller_shiftsFilter($angeltype, $days); $shiftsFilter = angeltype_controller_shiftsFilter($angeltype, $days);
if (request()->input('showFilledShifts')) { if (request()->input('showFilledShifts')) {
@ -191,7 +201,7 @@ function angeltype_controller()
$isSupporter = !is_null($user_angeltype) && $user_angeltype->supporter; $isSupporter = !is_null($user_angeltype) && $user_angeltype->supporter;
return [ return [
sprintf(__('Team %s'), $angeltype->name), sprintf(__('Team %s'), htmlspecialchars($angeltype->name)),
AngelType_view( AngelType_view(
$angeltype, $angeltype,
$members, $members,
@ -220,9 +230,8 @@ function angeltype_controller_shiftsFilterDays(AngelType $angeltype)
$days = []; $days = [];
foreach ($all_shifts as $shift) { foreach ($all_shifts as $shift) {
$day = Carbon::make($shift['start'])->format('Y-m-d'); $day = Carbon::make($shift['start'])->format('Y-m-d');
$dayFormatted = Carbon::make($shift['start'])->format(__('Y-m-d'));
if (!isset($days[$day])) { if (!isset($days[$day])) {
$days[$day] = $dayFormatted; $days[$day] = dateWithEventDay($day);
} }
} }
ksort($days); ksort($days);
@ -239,13 +248,13 @@ function angeltype_controller_shiftsFilterDays(AngelType $angeltype)
function angeltype_controller_shiftsFilter(AngelType $angeltype, $days) function angeltype_controller_shiftsFilter(AngelType $angeltype, $days)
{ {
$request = request(); $request = request();
$roomIds = Room::query() $locationIds = Location::query()
->select('id') ->select('id')
->pluck('id') ->pluck('id')
->toArray(); ->toArray();
$shiftsFilter = new ShiftsFilter( $shiftsFilter = new ShiftsFilter(
auth()->can('user_shifts_admin'), auth()->can('user_shifts_admin'),
$roomIds, $locationIds,
[$angeltype->id] [$angeltype->id]
); );
$selected_day = date('Y-m-d'); $selected_day = date('Y-m-d');
@ -269,59 +278,70 @@ function angeltype_controller_shiftsFilter(AngelType $angeltype, $days)
function angeltypes_list_controller() function angeltypes_list_controller()
{ {
$user = auth()->user(); $user = auth()->user();
$admin_angeltypes = auth()->can('admin_angel_types');
if (!auth()->can('angeltypes')) { if (!auth()->can('angeltypes')) {
throw_redirect(page_link_to('/')); throw_redirect(url('/'));
} }
$angeltypes = AngelTypes_with_user($user->id); $angeltypes = AngelTypes_with_user($user->id);
foreach ($angeltypes as $angeltype) { foreach ($angeltypes as $angeltype) {
$actions = [ $actions = [
button( button(
page_link_to('angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype->id]), url('/angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype->id]),
icon('eye') . __('view'), icon('eye') . ($admin_angeltypes ? '' : __('View')),
'btn-sm' 'btn-sm btn-info',
'',
($admin_angeltypes ? __('View') : '')
), ),
]; ];
if (auth()->can('admin_angel_types')) { if ($admin_angeltypes) {
$actions[] = button( $actions[] = button(
page_link_to('angeltypes', ['action' => 'edit', 'angeltype_id' => $angeltype->id]), url('/angeltypes', ['action' => 'edit', 'angeltype_id' => $angeltype->id]),
icon('pencil') . __('edit'), icon('pencil'),
'btn-sm' 'btn-sm',
'',
__('form.edit')
); );
$actions[] = button( $actions[] = button(
page_link_to('angeltypes', ['action' => 'delete', 'angeltype_id' => $angeltype->id]), url('/angeltypes', ['action' => 'delete', 'angeltype_id' => $angeltype->id]),
icon('trash') . __('delete'), icon('trash'),
'btn-sm' 'btn-sm btn-danger',
'',
__('form.delete')
); );
} }
$angeltype->membership = AngelType_render_membership($angeltype); $angeltype->membership = AngelType_render_membership($angeltype);
if (!empty($angeltype->user_angel_type_id)) { if (!empty($angeltype->user_angel_type_id)) {
$actions[] = button( $actions[] = button(
page_link_to( url(
'user_angeltypes', '/user-angeltypes',
['action' => 'delete', 'user_angeltype_id' => $angeltype->user_angel_type_id] ['action' => 'delete', 'user_angeltype_id' => $angeltype->user_angel_type_id]
), ),
icon('box-arrow-right') . __('leave'), icon('box-arrow-right') . ($admin_angeltypes ? '' : __('Leave')),
'btn-sm' 'btn-sm',
'',
($admin_angeltypes ? __('Leave') : '')
); );
} else { } else {
$actions[] = button( $actions[] = button(
page_link_to('user_angeltypes', ['action' => 'add', 'angeltype_id' => $angeltype->id]), url('/user_angeltypes', ['action' => 'add', 'angeltype_id' => $angeltype->id]),
icon('box-arrow-in-right') . __('join'), icon('box-arrow-in-right') . ($admin_angeltypes ? '' : __('Join')),
'btn-sm' 'btn-sm' . ($admin_angeltypes ? ' btn-success' : ''),
'',
($admin_angeltypes ? __('Join') : '')
); );
} }
$angeltype->is_restricted = $angeltype->restricted ? icon('mortarboard-fill') : ''; $angeltype->is_restricted = $angeltype->restricted ? icon('mortarboard-fill') : '';
$angeltype->no_self_signup_allowed = $angeltype->no_self_signup ? '' : icon('pencil-square'); $angeltype->shift_self_signup_allowed = $angeltype->shift_self_signup ? icon('pencil-square') : '';
$angeltype->name = '<a href="' $angeltype->name = '<a href="'
. page_link_to('angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype->id]) . url('/angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype->id])
. '">' . '">'
. $angeltype->name . htmlspecialchars($angeltype->name)
. '</a>'; . '</a>';
$angeltype->actions = table_buttons($actions); $angeltype->actions = table_buttons($actions);

View File

@ -17,7 +17,7 @@ function event_config_title()
function event_config_edit_controller() function event_config_edit_controller()
{ {
if (!auth()->can('admin_event_config')) { if (!auth()->can('admin_event_config')) {
throw_redirect(page_link_to('/')); throw_redirect(url('/'));
} }
$request = request(); $request = request();
@ -117,8 +117,8 @@ function event_config_edit_controller()
$teardown_end_date ? $teardown_end_date->format('Y-m-d H:i') : '' $teardown_end_date ? $teardown_end_date->format('Y-m-d H:i') : ''
) )
); );
success(__('Settings saved.')); success(__('settings.success'));
throw_redirect(page_link_to('admin_event_config')); throw_redirect(url('/admin_event_config'));
} }
} }

View File

@ -1,40 +1,40 @@
<?php <?php
use Engelsystem\Models\AngelType; use Engelsystem\Models\AngelType;
use Engelsystem\Models\Room; use Engelsystem\Models\Location;
use Engelsystem\ShiftsFilter; use Engelsystem\ShiftsFilter;
use Engelsystem\ShiftsFilterRenderer; use Engelsystem\ShiftsFilterRenderer;
/** /**
* Room controllers for managing everything room related. * Location controllers for managing everything location related.
*/ */
/** /**
* View a room with its shifts. * View a location with its shifts.
* *
* @return array * @return array
*/ */
function room_controller(): array function location_controller(): array
{ {
if (!auth()->can('view_rooms')) { if (!auth()->can('view_locations')) {
throw_redirect(page_link_to()); throw_redirect(url('/'));
} }
$request = request(); $request = request();
$room = load_room(); $location = load_location();
$all_shifts = $room->shifts->sortBy('start'); $all_shifts = $location->shifts->sortBy('start');
$days = []; $days = [];
foreach ($all_shifts as $shift) { foreach ($all_shifts as $shift) {
$day = $shift->start->format('Y-m-d'); $day = $shift->start->format('Y-m-d');
if (!isset($days[$day])) { if (!isset($days[$day])) {
$days[$day] = $shift->start->format(__('Y-m-d')); $days[$day] = dateWithEventDay($day);
} }
} }
$shiftsFilter = new ShiftsFilter( $shiftsFilter = new ShiftsFilter(
true, true,
[$room->id], [$location->id],
AngelType::query()->get('id')->pluck('id')->toArray() AngelType::query()->get('id')->pluck('id')->toArray()
); );
$selected_day = date('Y-m-d'); $selected_day = date('Y-m-d');
@ -53,17 +53,17 @@ function room_controller(): array
$shiftCalendarRenderer = shiftCalendarRendererByShiftFilter($shiftsFilter); $shiftCalendarRenderer = shiftCalendarRendererByShiftFilter($shiftsFilter);
return [ return [
$room->name, htmlspecialchars($location->name),
Room_view($room, $shiftsFilterRenderer, $shiftCalendarRenderer), location_view($location, $shiftsFilterRenderer, $shiftCalendarRenderer),
]; ];
} }
/** /**
* Dispatch different room actions. * Dispatch different location actions.
* *
* @return array * @return array
*/ */
function rooms_controller(): array function locations_controller(): array
{ {
$request = request(); $request = request();
$action = $request->input('action'); $action = $request->input('action');
@ -72,36 +72,36 @@ function rooms_controller(): array
} }
return match ($action) { return match ($action) {
'view' => room_controller(), 'view' => location_controller(),
'list' => throw_redirect(page_link_to('admin/rooms')), 'list' => throw_redirect(url('/admin/locations')),
default => throw_redirect(page_link_to('admin/rooms')), default => throw_redirect(url('/admin/locations')),
}; };
} }
/** /**
* @param Room $room * @param Location $location
* @return string * @return string
*/ */
function room_link(Room $room) function location_link(Location $location)
{ {
return page_link_to('rooms', ['action' => 'view', 'room_id' => $room->id]); return url('/locations', ['action' => 'view', 'location_id' => $location->id]);
} }
/** /**
* Loads room by request param room_id * Loads location by request param location_id
* *
* @return Room * @return Location
*/ */
function load_room() function load_location()
{ {
if (!test_request_int('room_id')) { if (!test_request_int('location_id')) {
throw_redirect(page_link_to()); throw_redirect(url('/'));
} }
$room = Room::find(request()->input('room_id')); $location = Location::find(request()->input('location_id'));
if (!$room) { if (!$location) {
throw_redirect(page_link_to()); throw_redirect(url('/'));
} }
return $room; return $location;
} }

View File

@ -1,8 +1,8 @@
<?php <?php
use Engelsystem\Models\AngelType; use Engelsystem\Models\AngelType;
use Engelsystem\Models\Location;
use Engelsystem\Models\News; use Engelsystem\Models\News;
use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\Shift; use Engelsystem\Models\Shifts\Shift;
use Engelsystem\ShiftsFilter; use Engelsystem\ShiftsFilter;
@ -15,22 +15,22 @@ function public_dashboard_controller()
{ {
$filter = null; $filter = null;
if (request()->get('filtered')) { if (request()->get('filtered')) {
$requestRooms = check_request_int_array('rooms'); $requestLocations = check_request_int_array('locations');
$requestAngelTypes = check_request_int_array('types'); $requestAngelTypes = check_request_int_array('types');
if (!$requestRooms && !$requestAngelTypes) { if (!$requestLocations && !$requestAngelTypes) {
$sessionFilter = collect(session()->get('shifts-filter', [])); $sessionFilter = collect(session()->get('shifts-filter', []));
$requestRooms = $sessionFilter->get('rooms', []); $requestLocations = $sessionFilter->get('locations', []);
$requestAngelTypes = $sessionFilter->get('types', []); $requestAngelTypes = $sessionFilter->get('types', []);
} }
$angelTypes = collect(unrestricted_angeltypes()); $angelTypes = collect(unrestricted_angeltypes());
$rooms = $requestRooms ?: Room::orderBy('name')->get()->pluck('id')->toArray(); $locations = $requestLocations ?: Location::orderBy('name')->get()->pluck('id')->toArray();
$angelTypes = $requestAngelTypes ?: $angelTypes->pluck('id')->toArray(); $angelTypes = $requestAngelTypes ?: $angelTypes->pluck('id')->toArray();
$filterValues = [ $filterValues = [
'userShiftsAdmin' => false, 'userShiftsAdmin' => false,
'filled' => [], 'filled' => [],
'rooms' => $rooms, 'locations' => $locations,
'types' => $angelTypes, 'types' => $angelTypes,
'startTime' => null, 'startTime' => null,
'endTime' => null, 'endTime' => null,
@ -57,14 +57,14 @@ function public_dashboard_controller()
} }
} }
$important_news = News::whereIsImportant(true) $highlighted_news = News::whereIsHighlighted(true)
->orderBy('updated_at') ->orderBy('updated_at')
->limit(1) ->limit(1)
->get(); ->get();
return [ return [
__('Public Dashboard'), __('Public Dashboard'),
public_dashboard_view($stats, $free_shifts, $important_news), public_dashboard_view($stats, $free_shifts, $highlighted_news),
]; ];
} }
@ -87,7 +87,7 @@ function public_dashboard_controller_free_shift(Shift $shift, ShiftsFilter $filt
'duration' => round(($shift->end->timestamp - $shift->start->timestamp) / 3600), 'duration' => round(($shift->end->timestamp - $shift->start->timestamp) / 3600),
'shifttype_name' => $shift->shiftType->name, 'shifttype_name' => $shift->shiftType->name,
'title' => $shift->title, 'title' => $shift->title,
'room_name' => $shift->room->name, 'location_name' => $shift->location->name,
'needed_angels' => public_dashboard_needed_angels($shift->neededAngels, $filter), 'needed_angels' => public_dashboard_needed_angels($shift->neededAngels, $filter),
]; ];
@ -136,5 +136,5 @@ function public_dashboard_needed_angels($needed_angels, ShiftsFilter $filter = n
*/ */
function public_dashboard_link(array $parameters = []): string function public_dashboard_link(array $parameters = []): string
{ {
return page_link_to('public-dashboard', $parameters); return url('/public-dashboard', $parameters);
} }

View File

@ -18,7 +18,7 @@ function shift_entries_controller(): array
{ {
$user = auth()->user(); $user = auth()->user();
if (!$user) { if (!$user) {
throw_redirect(page_link_to('login')); throw_redirect(url('/login'));
} }
$action = strip_request_item('action'); $action = strip_request_item('action');
@ -44,7 +44,7 @@ function shift_entry_create_controller(): array
$request = request(); $request = request();
if ($user->isFreeloader()) { if ($user->isFreeloader()) {
throw_redirect(page_link_to('user_myshifts')); throw_redirect(url('/user_myshifts'));
} }
$shift = Shift($request->input('shift_id')); $shift = Shift($request->input('shift_id'));
@ -113,17 +113,21 @@ function shift_entry_create_controller_admin(Shift $shift, ?AngelType $angeltype
} }
/** @var User[]|Collection $users */ /** @var User[]|Collection $users */
$users = User::query()->orderBy('name')->get(); $users = User::with('userAngelTypes')->orderBy('name')->get();
$users_select = []; $users_select = [];
foreach ($users as $user) { foreach ($users as $user) {
$users_select[$user->id] = $user->displayName; $name = $user->displayName;
if ($user->userAngelTypes->where('id', $angeltype->id)->isEmpty()) {
$name = __('%s (not "%s")', [$name, $angeltype->name]);
}
$users_select[$user->id] = $name;
} }
$angeltypes_select = $angeltypes->pluck('name', 'id')->toArray(); $angeltypes_select = $angeltypes->pluck('name', 'id')->toArray();
$room = $shift->room; $location = $shift->location;
return [ return [
ShiftEntry_create_title(), ShiftEntry_create_title(),
ShiftEntry_create_view_admin($shift, $room, $angeltype, $angeltypes_select, $signup_user, $users_select), ShiftEntry_create_view_admin($shift, $location, $angeltype, $angeltypes_select, $signup_user, $users_select),
]; ];
} }
@ -167,10 +171,10 @@ function shift_entry_create_controller_supporter(Shift $shift, AngelType $angelt
$users_select[$u->id] = $u->displayName; $users_select[$u->id] = $u->displayName;
} }
$room = $shift->room; $location = $shift->location;
return [ return [
ShiftEntry_create_title(), ShiftEntry_create_title(),
ShiftEntry_create_view_supporter($shift, $room, $angeltype, $signup_user, $users_select), ShiftEntry_create_view_supporter($shift, $location, $angeltype, $signup_user, $users_select),
]; ];
} }
@ -206,7 +210,7 @@ function shift_entry_create_controller_user(Shift $shift, AngelType $angeltype):
$request = request(); $request = request();
$signup_user = auth()->user(); $signup_user = auth()->user();
$needed_angeltype = (new AngelType())->forceFill(NeededAngeltype_by_Shift_and_Angeltype($shift, $angeltype)); $needed_angeltype = (new AngelType())->forceFill(NeededAngeltype_by_Shift_and_Angeltype($shift, $angeltype) ?: []);
$shift_entries = $shift->shiftEntries() $shift_entries = $shift->shiftEntries()
->where('angel_type_id', $angeltype->id) ->where('angel_type_id', $angeltype->id)
->get(); ->get();
@ -250,10 +254,10 @@ function shift_entry_create_controller_user(Shift $shift, AngelType $angeltype):
throw_redirect(shift_link($shift)); throw_redirect(shift_link($shift));
} }
$room = $shift->room; $location = $shift->location;
return [ return [
ShiftEntry_create_title(), ShiftEntry_create_title(),
ShiftEntry_create_view_user($shift, $room, $angeltype, $comment), ShiftEntry_create_view_user($shift, $location, $angeltype, $comment),
]; ];
} }
@ -272,7 +276,7 @@ function shift_entry_create_link(Shift $shift, AngelType $angeltype, $params = [
'shift_id' => $shift->id, 'shift_id' => $shift->id,
'angeltype_id' => $angeltype->id, 'angeltype_id' => $angeltype->id,
], $params); ], $params);
return page_link_to('shift_entries', $params); return url('/shift-entries', $params);
} }
/** /**
@ -288,7 +292,7 @@ function shift_entry_create_link_admin(Shift $shift, $params = [])
'action' => 'create', 'action' => 'create',
'shift_id' => $shift->id, 'shift_id' => $shift->id,
], $params); ], $params);
return page_link_to('shift_entries', $params); return url('/shift-entries', $params);
} }
/** /**
@ -301,11 +305,10 @@ function shift_entry_load()
$request = request(); $request = request();
if (!$request->has('shift_entry_id') || !test_request_int('shift_entry_id')) { if (!$request->has('shift_entry_id') || !test_request_int('shift_entry_id')) {
throw_redirect(page_link_to('user_shifts')); throw_redirect(url('/user-shifts'));
} }
$shiftEntry = ShiftEntry::findOrFail($request->input('shift_entry_id'));
return $shiftEntry; return ShiftEntry::findOrFail($request->input('shift_entry_id'));
} }
/** /**
@ -362,5 +365,5 @@ function shift_entry_delete_link($shiftEntry, $params = [])
'action' => 'delete', 'action' => 'delete',
'shift_entry_id' => $shiftEntry['shift_entry_id'] ?? $shiftEntry['id'], 'shift_entry_id' => $shiftEntry['shift_entry_id'] ?? $shiftEntry['id'],
], $params); ], $params);
return page_link_to('shift_entries', $params); return url('/shift-entries', $params);
} }

View File

@ -1,13 +1,17 @@
<?php <?php
use Engelsystem\Http\Exceptions\HttpForbidden;
use Engelsystem\Http\Exceptions\HttpNotFound;
use Engelsystem\Http\Redirector;
use Engelsystem\Models\AngelType; use Engelsystem\Models\AngelType;
use Engelsystem\Models\Location;
use Engelsystem\Models\Shifts\NeededAngelType; use Engelsystem\Models\Shifts\NeededAngelType;
use Engelsystem\Models\Room;
use Engelsystem\Models\Shifts\ScheduleShift; use Engelsystem\Models\Shifts\ScheduleShift;
use Engelsystem\Models\Shifts\Shift; use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftType; use Engelsystem\Models\Shifts\ShiftType;
use Engelsystem\Models\Shifts\ShiftSignupStatus; use Engelsystem\Models\Shifts\ShiftSignupStatus;
use Engelsystem\ShiftSignupState; use Engelsystem\ShiftSignupState;
use Illuminate\Support\Str;
/** /**
* @param array|Shift $shift * @param array|Shift $shift
@ -20,16 +24,7 @@ function shift_link($shift)
$parameters['shift_id'] = $shift['shift_id'] ?? $shift['id']; $parameters['shift_id'] = $shift['shift_id'] ?? $shift['id'];
} }
return page_link_to('shifts', $parameters); return url('/shifts', $parameters);
}
/**
* @param Shift $shift
* @return string
*/
function shift_delete_link(Shift $shift)
{
return page_link_to('user_shifts', ['delete_shift' => $shift->id]);
} }
/** /**
@ -38,7 +33,7 @@ function shift_delete_link(Shift $shift)
*/ */
function shift_edit_link(Shift $shift) function shift_edit_link(Shift $shift)
{ {
return page_link_to('user_shifts', ['edit_shift' => $shift->id]); return url('/user-shifts', ['edit_shift' => $shift->id]);
} }
/** /**
@ -52,11 +47,11 @@ function shift_edit_controller()
$request = request(); $request = request();
if (!auth()->can('admin_shifts')) { if (!auth()->can('admin_shifts')) {
throw_redirect(page_link_to('user_shifts')); throw_redirect(url('/user-shifts'));
} }
if (!$request->has('edit_shift') || !test_request_int('edit_shift')) { if (!$request->has('edit_shift') || !test_request_int('edit_shift')) {
throw_redirect(page_link_to('user_shifts')); throw_redirect(url('/user-shifts'));
} }
$shift_id = $request->input('edit_shift'); $shift_id = $request->input('edit_shift');
@ -67,14 +62,14 @@ function shift_edit_controller()
)); ));
} }
$rooms = []; $locations = [];
foreach (Room::orderBy('name')->get() as $room) { foreach (Location::orderBy('name')->get() as $location) {
$rooms[$room->id] = $room->name; $locations[$location->id] = $location->name;
} }
$angeltypes = AngelType::all()->pluck('name', 'id')->toArray(); $angeltypes = AngelType::all()->pluck('name', 'id')->toArray();
$shifttypes = ShiftType::all()->pluck('name', 'id')->toArray(); $shifttypes = ShiftType::all()->pluck('name', 'id')->toArray();
$needed_angel_types = collect(NeededAngelTypes_by_shift($shift_id))->pluck('count', 'angel_type_id')->toArray(); $needed_angel_types = collect(NeededAngelTypes_by_shift($shift))->pluck('count', 'angel_type_id')->toArray();
foreach (array_keys($angeltypes) as $angeltype_id) { foreach (array_keys($angeltypes) as $angeltype_id) {
if (!isset($needed_angel_types[$angeltype_id])) { if (!isset($needed_angel_types[$angeltype_id])) {
$needed_angel_types[$angeltype_id] = 0; $needed_angel_types[$angeltype_id] = 0;
@ -84,7 +79,7 @@ function shift_edit_controller()
$shifttype_id = $shift->shift_type_id; $shifttype_id = $shift->shift_type_id;
$title = $shift->title; $title = $shift->title;
$description = $shift->description; $description = $shift->description;
$rid = $shift->room_id; $rid = $shift->location_id;
$start = $shift->start; $start = $shift->start;
$end = $shift->end; $end = $shift->end;
@ -97,12 +92,12 @@ function shift_edit_controller()
if ( if (
$request->has('rid') $request->has('rid')
&& preg_match('/^\d+$/', $request->input('rid')) && preg_match('/^\d+$/', $request->input('rid'))
&& isset($rooms[$request->input('rid')]) && isset($locations[$request->input('rid')])
) { ) {
$rid = $request->input('rid'); $rid = $request->input('rid');
} else { } else {
$valid = false; $valid = false;
error(__('Please select a room.')); error(__('Please select a location.'));
} }
if ($request->has('shifttype_id') && isset($shifttypes[$request->input('shifttype_id')])) { if ($request->has('shifttype_id') && isset($shifttypes[$request->input('shifttype_id')])) {
@ -154,13 +149,16 @@ function shift_edit_controller()
$shift->shift_type_id = $shifttype_id; $shift->shift_type_id = $shifttype_id;
$shift->title = $title; $shift->title = $title;
$shift->description = $description; $shift->description = $description;
$shift->room_id = $rid; $shift->location_id = $rid;
$shift->start = $start; $shift->start = $start;
$shift->end = $end; $shift->end = $end;
$shift->updatedBy()->associate(auth()->user()); $shift->updatedBy()->associate(auth()->user());
$shift->save(); $shift->save();
mail_shift_change($oldShift, $shift); event('shift.updating', [
'shift' => $shift,
'oldShift' => $oldShift,
]);
NeededAngelType::whereShiftId($shift_id)->delete(); NeededAngelType::whereShiftId($shift_id)->delete();
$needed_angel_types_info = []; $needed_angel_types_info = [];
@ -194,13 +192,16 @@ function shift_edit_controller()
foreach ($angeltypes as $angeltype_id => $angeltype_name) { foreach ($angeltypes as $angeltype_id => $angeltype_name) {
$angel_types_spinner .= form_spinner( $angel_types_spinner .= form_spinner(
'angeltype_count_' . $angeltype_id, 'angeltype_count_' . $angeltype_id,
$angeltype_name, htmlspecialchars($angeltype_name),
$needed_angel_types[$angeltype_id] $needed_angel_types[$angeltype_id],
[],
(bool) ScheduleShift::whereShiftId($shift->id)->first(),
); );
} }
$link = button(url('/shifts', ['action' => 'view', 'shift_id' => $shift_id]), icon('chevron-left'), 'btn-sm', '', __('general.back'));
return page_with_title( return page_with_title(
shifts_title(), $link . ' ' . shifts_title(),
[ [
msg(), msg(),
'<noscript>' '<noscript>'
@ -208,56 +209,41 @@ function shift_edit_controller()
. '</noscript>', . '</noscript>',
form([ form([
form_select('shifttype_id', __('Shifttype'), $shifttypes, $shifttype_id), form_select('shifttype_id', __('Shifttype'), $shifttypes, $shifttype_id),
form_text('title', __('Title'), $title), form_text('title', __('title.title'), $title),
form_select('rid', __('Room:'), $rooms, $rid), form_select('rid', __('Location:'), $locations, $rid),
form_text('start', __('Start:'), $start->format('Y-m-d H:i')), form_text('start', __('Start:'), $start->format('Y-m-d H:i')),
form_text('end', __('End:'), $end->format('Y-m-d H:i')), form_text('end', __('End:'), $end->format('Y-m-d H:i')),
form_textarea('description', __('Additional description'), $description), form_textarea('description', __('Additional description'), $description),
form_info('', __('This description is for single shifts, otherwise please use the description in shift type.')), form_info(
'',
__('This description is for single shifts, otherwise please use the description in shift type.')
),
'<h2>' . __('Needed angels') . '</h2>', '<h2>' . __('Needed angels') . '</h2>',
$angel_types_spinner, $angel_types_spinner,
form_submit('submit', __('Save')), form_submit('submit', icon('save') . __('form.save')),
]), ]),
] ]
); );
} }
/** function shift_delete_controller(): void
* @return string
*/
function shift_delete_controller()
{ {
$request = request(); $request = request();
// Only accessible for admins / ShiCos with user_shifts_admin privileg
if (!auth()->can('user_shifts_admin')) { if (!auth()->can('user_shifts_admin')) {
throw_redirect(page_link_to('user_shifts')); throw new HttpForbidden();
} }
// Schicht komplett löschen (nur für admins/user mit user_shifts_admin privileg) // Must contain shift id and confirmation
if (!$request->has('delete_shift') || !preg_match('/^\d+$/', $request->input('delete_shift'))) { if (!$request->has('delete_shift') || !$request->hasPostData('delete')) {
throw_redirect(page_link_to('user_shifts')); throw new HttpNotFound();
} }
$shift_id = $request->input('delete_shift'); $shift_id = $request->input('delete_shift');
$shift = Shift::findOrFail($shift_id);
$shift = Shift($shift_id); event('shift.deleting', ['shift' => $shift]);
if (empty($shift)) {
throw_redirect(page_link_to('user_shifts'));
}
// Schicht löschen bestätigt
if ($request->hasPostData('delete')) {
foreach ($shift->shiftEntries as $entry) {
event('shift.entry.deleting', [
'user' => $entry->user,
'start' => $shift->start,
'end' => $shift->end,
'name' => $shift->shiftType->name,
'title' => $shift->title,
'type' => $entry->angelType->name,
'room' => $shift->room,
'freeloaded' => $entry->freeloaded,
]);
}
$shift->delete(); $shift->delete();
@ -267,21 +253,15 @@ function shift_delete_controller()
. ' to ' . $shift->end->format('Y-m-d H:i') . ' to ' . $shift->end->format('Y-m-d H:i')
); );
success(__('Shift deleted.')); success(__('Shift deleted.'));
throw_redirect(page_link_to('user_shifts'));
/** @var Redirector $redirect */
$redirect = app('redirect');
$old = $redirect->back()->getHeaderLine('location');
if (Str::contains($old, '/shifts') && Str::contains($old, 'action=view')) {
throw_redirect(url('/user-shifts'));
} }
return page_with_title(shifts_title(), [ throw_redirect($old);
error(sprintf(
__('Do you want to delete the shift %s from %s to %s?'),
$shift->shiftType->name,
$shift->start->format(__('Y-m-d H:i')),
$shift->end->format(__('H:i'))
), true),
form([
form_hidden('delete_shift', $shift->id),
form_submit('delete', __('delete')),
]),
]);
} }
/** /**
@ -293,21 +273,21 @@ function shift_controller()
$request = request(); $request = request();
if (!auth()->can('user_shifts')) { if (!auth()->can('user_shifts')) {
throw_redirect(page_link_to('/')); throw_redirect(url('/'));
} }
if (!$request->has('shift_id')) { if (!$request->has('shift_id')) {
throw_redirect(page_link_to('user_shifts')); throw_redirect(url('/user-shifts'));
} }
$shift = Shift($request->input('shift_id')); $shift = Shift($request->input('shift_id'));
if (empty($shift)) { if (empty($shift)) {
error(__('Shift could not be found.')); error(__('Shift could not be found.'));
throw_redirect(page_link_to('user_shifts')); throw_redirect(url('/user-shifts'));
} }
$shifttype = $shift->shiftType; $shifttype = $shift->shiftType;
$room = $shift->room; $location = $shift->location;
/** @var AngelType[] $angeltypes */ /** @var AngelType[] $angeltypes */
$angeltypes = AngelType::all(); $angeltypes = AngelType::all();
$user_shifts = Shifts_by_user($user->id); $user_shifts = Shifts_by_user($user->id);
@ -338,8 +318,8 @@ function shift_controller()
} }
return [ return [
$shift->shiftType->name, htmlspecialchars($shift->shiftType->name),
Shift_view($shift, $shifttype, $room, $angeltypes, $shift_signup_state), Shift_view($shift, $shifttype, $location, $angeltypes, $shift_signup_state),
]; ];
} }
@ -350,13 +330,13 @@ function shifts_controller()
{ {
$request = request(); $request = request();
if (!$request->has('action')) { if (!$request->has('action')) {
throw_redirect(page_link_to('user_shifts')); throw_redirect(url('/user-shifts'));
} }
return match ($request->input('action')) { return match ($request->input('action')) {
'view' => shift_controller(), 'view' => shift_controller(),
'next' => shift_next_controller(), // throws redirect 'next' => shift_next_controller(), // throws redirect
default => throw_redirect(page_link_to('/')), default => throw_redirect(url('/')),
}; };
} }
@ -366,7 +346,7 @@ function shifts_controller()
function shift_next_controller() function shift_next_controller()
{ {
if (!auth()->can('user_shifts')) { if (!auth()->can('user_shifts')) {
throw_redirect(page_link_to('/')); throw_redirect(url('/'));
} }
$upcoming_shifts = ShiftEntries_upcoming_for_user(auth()->user()); $upcoming_shifts = ShiftEntries_upcoming_for_user(auth()->user());
@ -375,5 +355,5 @@ function shift_next_controller()
throw_redirect(shift_link($upcoming_shifts[0]->shift)); throw_redirect(shift_link($upcoming_shifts[0]->shift));
} }
throw_redirect(page_link_to('user_shifts')); throw_redirect(url('/user-shifts'));
} }

View File

@ -1,163 +0,0 @@
<?php
use Engelsystem\Models\Shifts\ShiftType;
/**
* @param ShiftType $shifttype
* @return string
*/
function shifttype_link(ShiftType $shifttype)
{
return page_link_to('shifttypes', ['action' => 'view', 'shifttype_id' => $shifttype->id]);
}
/**
* Delete a shifttype.
*
* @return array
*/
function shifttype_delete_controller()
{
$request = request();
if (!$request->has('shifttype_id')) {
throw_redirect(page_link_to('shifttypes'));
}
$shifttype = ShiftType::findOrFail($request->input('shifttype_id'));
if ($request->hasPostData('delete')) {
engelsystem_log('Deleted shifttype ' . $shifttype->name);
success(sprintf(__('Shifttype %s deleted.'), $shifttype->name));
$shifttype->delete();
throw_redirect(page_link_to('shifttypes'));
}
return [
sprintf(__('Delete shifttype %s'), $shifttype->name),
ShiftType_delete_view($shifttype),
];
}
/**
* Edit or create shift type.
*
* @return array
*/
function shifttype_edit_controller()
{
$shifttype_id = null;
$name = '';
$description = '';
$request = request();
if ($request->has('shifttype_id')) {
$shifttype = ShiftType::findOrFail($request->input('shifttype_id'));
$shifttype_id = $shifttype->id;
$name = $shifttype->name;
$description = $shifttype->description;
}
if ($request->hasPostData('submit')) {
$valid = true;
if ($request->has('name') && $request->input('name') != '') {
$name = strip_request_item('name');
} else {
$valid = false;
error(__('Please enter a name.'));
}
if ($request->has('description')) {
$description = strip_request_item_nl('description');
}
if ($valid) {
$shiftType = ShiftType::findOrNew($shifttype_id);
$shiftType->name = $name;
$shiftType->description = $description;
$shiftType->save();
if ($shifttype_id) {
engelsystem_log('Updated shifttype ' . $name);
success(__('Updated shifttype.'));
} else {
$shifttype_id = $shiftType->id;
engelsystem_log('Created shifttype ' . $name);
success(__('Created shifttype.'));
}
throw_redirect(page_link_to('shifttypes', ['action' => 'view', 'shifttype_id' => $shifttype_id]));
}
}
return [
shifttypes_title(),
ShiftType_edit_view($name, $description, $shifttype_id),
];
}
/**
* @return array
*/
function shifttype_controller()
{
$request = request();
if (!$request->has('shifttype_id')) {
throw_redirect(page_link_to('shifttypes'));
}
$shifttype = ShiftType::findOrFail($request->input('shifttype_id'));
return [
$shifttype->name,
ShiftType_view($shifttype),
];
}
/**
* List all shift types.
*
* @return array
*/
function shifttypes_list_controller()
{
$shifttypes = ShiftType::all();
return [
shifttypes_title(),
ShiftTypes_list_view($shifttypes),
];
}
/**
* Text for shift type related links.
*
* @return string
*/
function shifttypes_title()
{
return __('Shifttypes');
}
/**
* Route shift type actions
*
* @return array
*/
function shifttypes_controller()
{
$request = request();
$action = 'list';
if ($request->has('action')) {
$action = $request->input('action');
}
return match ($action) {
'view' => shifttype_controller(),
'edit' => shifttype_edit_controller(),
'delete' => shifttype_delete_controller(),
'list' => shifttypes_list_controller(),
default => shifttypes_list_controller(),
};
}

View File

@ -5,8 +5,6 @@ use Engelsystem\Models\AngelType;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
use Engelsystem\Models\UserAngelType; use Engelsystem\Models\UserAngelType;
use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Collection;
use Psr\Log\LoggerInterface;
use Symfony\Component\Mailer\Exception\TransportException;
/** /**
* Display a hint for team/angeltype supporters if there are unconfirmed users for his angeltype. * Display a hint for team/angeltype supporters if there are unconfirmed users for his angeltype.
@ -37,9 +35,9 @@ function user_angeltypes_unconfirmed_hint()
$unconfirmed_links = []; $unconfirmed_links = [];
foreach ($unconfirmed_user_angeltypes as $user_angeltype) { foreach ($unconfirmed_user_angeltypes as $user_angeltype) {
$unconfirmed_links[] = '<a class="text-info" href="' $unconfirmed_links[] = '<a href="'
. page_link_to('angeltypes', ['action' => 'view', 'angeltype_id' => $user_angeltype->angel_type_id]) . url('/angeltypes', ['action' => 'view', 'angeltype_id' => $user_angeltype->angel_type_id])
. '">' . $user_angeltype->angelType->name . '">' . htmlspecialchars($user_angeltype->angelType->name)
. ' (+' . $user_angeltype->count . ')' . ' (+' . $user_angeltype->count . ')'
. '</a>'; . '</a>';
} }
@ -67,13 +65,13 @@ function user_angeltypes_delete_all_controller(): array
if (!$request->has('angeltype_id')) { if (!$request->has('angeltype_id')) {
error(__('Angeltype doesn\'t exist.')); error(__('Angeltype doesn\'t exist.'));
throw_redirect(page_link_to('angeltypes')); throw_redirect(url('/angeltypes'));
} }
$angeltype = AngelType::findOrFail($request->input('angeltype_id')); $angeltype = AngelType::findOrFail($request->input('angeltype_id'));
if (!auth()->user()->isAngelTypeSupporter($angeltype) && !auth()->can('admin_user_angeltypes')) { if (!auth()->user()->isAngelTypeSupporter($angeltype) && !auth()->can('admin_user_angeltypes')) {
error(__('You are not allowed to delete all users for this angeltype.')); error(__('You are not allowed to delete all users for this angeltype.'));
throw_redirect(page_link_to('angeltypes')); throw_redirect(url('/angeltypes'));
} }
if ($request->hasPostData('deny_all')) { if ($request->hasPostData('deny_all')) {
@ -83,7 +81,7 @@ function user_angeltypes_delete_all_controller(): array
engelsystem_log(sprintf('Denied all users for angeltype %s', AngelType_name_render($angeltype, true))); engelsystem_log(sprintf('Denied all users for angeltype %s', AngelType_name_render($angeltype, true)));
success(sprintf(__('Denied all users for angeltype %s.'), $angeltype->name)); success(sprintf(__('Denied all users for angeltype %s.'), $angeltype->name));
throw_redirect(page_link_to('angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype->id])); throw_redirect(url('/angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype->id]));
} }
return [ return [
@ -104,13 +102,13 @@ function user_angeltypes_confirm_all_controller(): array
if (!$request->has('angeltype_id')) { if (!$request->has('angeltype_id')) {
error(__('Angeltype doesn\'t exist.')); error(__('Angeltype doesn\'t exist.'));
throw_redirect(page_link_to('angeltypes')); throw_redirect(url('/angeltypes'));
} }
$angeltype = AngelType::findOrFail($request->input('angeltype_id')); $angeltype = AngelType::findOrFail($request->input('angeltype_id'));
if (!auth()->can('admin_user_angeltypes') && !$user->isAngelTypeSupporter($angeltype)) { if (!auth()->can('admin_user_angeltypes') && !$user->isAngelTypeSupporter($angeltype)) {
error(__('You are not allowed to confirm all users for this angeltype.')); error(__('You are not allowed to confirm all users for this angeltype.'));
throw_redirect(page_link_to('angeltypes')); throw_redirect(url('/angeltypes'));
} }
if ($request->hasPostData('confirm_all')) { if ($request->hasPostData('confirm_all')) {
@ -127,7 +125,7 @@ function user_angeltypes_confirm_all_controller(): array
user_angeltype_confirm_email($user, $angeltype); user_angeltype_confirm_email($user, $angeltype);
} }
throw_redirect(page_link_to('angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype->id])); throw_redirect(url('/angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype->id]));
} }
return [ return [
@ -148,7 +146,7 @@ function user_angeltype_confirm_controller(): array
if (!$request->has('user_angeltype_id')) { if (!$request->has('user_angeltype_id')) {
error(__('User angeltype doesn\'t exist.')); error(__('User angeltype doesn\'t exist.'));
throw_redirect(page_link_to('angeltypes')); throw_redirect(url('/angeltypes'));
} }
/** @var UserAngelType $user_angeltype */ /** @var UserAngelType $user_angeltype */
@ -156,7 +154,7 @@ function user_angeltype_confirm_controller(): array
$angeltype = $user_angeltype->angelType; $angeltype = $user_angeltype->angelType;
if (!$user->isAngelTypeSupporter($angeltype) && !auth()->can('admin_user_angeltypes')) { if (!$user->isAngelTypeSupporter($angeltype) && !auth()->can('admin_user_angeltypes')) {
error(__('You are not allowed to confirm this users angeltype.')); error(__('You are not allowed to confirm this users angeltype.'));
throw_redirect(page_link_to('angeltypes')); throw_redirect(url('/angeltypes'));
} }
$user_source = $user_angeltype->user; $user_source = $user_angeltype->user;
@ -173,7 +171,7 @@ function user_angeltype_confirm_controller(): array
user_angeltype_confirm_email($user_source, $angeltype); user_angeltype_confirm_email($user_source, $angeltype);
throw_redirect(page_link_to('angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype->id])); throw_redirect(url('/angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype->id]));
} }
return [ return [
@ -188,7 +186,6 @@ function user_angeltype_confirm_email(User $user, AngelType $angeltype): void
return; return;
} }
try {
/** @var EngelsystemMailer $mailer */ /** @var EngelsystemMailer $mailer */
$mailer = app(EngelsystemMailer::class); $mailer = app(EngelsystemMailer::class);
$mailer->sendViewTranslated( $mailer->sendViewTranslated(
@ -197,14 +194,6 @@ function user_angeltype_confirm_email(User $user, AngelType $angeltype): void
'emails/angeltype-confirmed', 'emails/angeltype-confirmed',
['name' => $angeltype->name, 'angeltype' => $angeltype, 'username' => $user->displayName] ['name' => $angeltype->name, 'angeltype' => $angeltype, 'username' => $user->displayName]
); );
} catch (TransportException $e) {
/** @var LoggerInterface $logger */
$logger = app('logger');
$logger->error(
'Unable to send email "{title}" to user {user} with {exception}',
['title' => __('notification.angeltype.confirmed'), 'user' => $user->name, 'exception' => $e]
);
}
} }
function user_angeltype_add_email(User $user, AngelType $angeltype): void function user_angeltype_add_email(User $user, AngelType $angeltype): void
@ -213,7 +202,6 @@ function user_angeltype_add_email(User $user, AngelType $angeltype): void
return; return;
} }
try {
/** @var EngelsystemMailer $mailer */ /** @var EngelsystemMailer $mailer */
$mailer = app(EngelsystemMailer::class); $mailer = app(EngelsystemMailer::class);
$mailer->sendViewTranslated( $mailer->sendViewTranslated(
@ -222,14 +210,6 @@ function user_angeltype_add_email(User $user, AngelType $angeltype): void
'emails/angeltype-added', 'emails/angeltype-added',
['name' => $angeltype->name, 'angeltype' => $angeltype, 'username' => $user->displayName] ['name' => $angeltype->name, 'angeltype' => $angeltype, 'username' => $user->displayName]
); );
} catch (TransportException $e) {
/** @var LoggerInterface $logger */
$logger = app('logger');
$logger->error(
'Unable to send email "{title}" to user {user} with {exception}',
['title' => __('notification.angeltype.added'), 'user' => $user->name, 'exception' => $e]
);
}
} }
/** /**
@ -244,34 +224,35 @@ function user_angeltype_delete_controller(): array
if (!$request->has('user_angeltype_id')) { if (!$request->has('user_angeltype_id')) {
error(__('User angeltype doesn\'t exist.')); error(__('User angeltype doesn\'t exist.'));
throw_redirect(page_link_to('angeltypes')); throw_redirect(url('/angeltypes'));
} }
/** @var UserAngelType $user_angeltype */ /** @var UserAngelType $user_angeltype */
$user_angeltype = UserAngelType::findOrFail($request->input('user_angeltype_id')); $user_angeltype = UserAngelType::findOrFail($request->input('user_angeltype_id'));
$angeltype = $user_angeltype->angelType; $angeltype = $user_angeltype->angelType;
$user_source = $user_angeltype->user; $user_source = $user_angeltype->user;
$isOwnAngelType = $user->id == $user_source->id;
if ( if (
$user->id != $user_angeltype->user_id !$isOwnAngelType
&& !$user->isAngelTypeSupporter($angeltype) && !$user->isAngelTypeSupporter($angeltype)
&& !auth()->can('admin_user_angeltypes') && !auth()->can('admin_user_angeltypes')
) { ) {
error(__('You are not allowed to delete this users angeltype.')); error(__('You are not allowed to delete this users angeltype.'));
throw_redirect(page_link_to('angeltypes')); throw_redirect(url('/angeltypes'));
} }
if ($request->hasPostData('delete')) { if ($request->hasPostData('delete')) {
$user_angeltype->delete(); $user_angeltype->delete();
engelsystem_log(sprintf('User %s removed from %s.', User_Nick_render($user_source, true), $angeltype->name)); engelsystem_log(sprintf('User "%s" removed from "%s".', User_Nick_render($user_source, true), $angeltype->name));
success(sprintf(__('User %s removed from %s.'), $user_source->displayName, $angeltype->name)); success(sprintf($isOwnAngelType ? __('You successfully left "%2$s".') : __('User "%s" removed from "%s".'), $user_source->displayName, $angeltype->name));
throw_redirect(page_link_to('angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype->id])); throw_redirect(url('/angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype->id]));
} }
return [ return [
__('Remove angeltype'), __('Leave angeltype'),
UserAngelType_delete_view($user_angeltype, $user_source, $angeltype), UserAngelType_delete_view($user_angeltype, $user_source, $angeltype, $isOwnAngelType),
]; ];
} }
@ -285,21 +266,21 @@ function user_angeltype_update_controller(): array
$supporter = false; $supporter = false;
$request = request(); $request = request();
if (!auth()->can('admin_angel_types')) { if (!auth()->can('admin_angel_types') && !config('supporters_can_promote')) {
error(__('You are not allowed to set supporter rights.')); error(__('You are not allowed to set supporter rights.'));
throw_redirect(page_link_to('angeltypes')); throw_redirect(url('/angeltypes'));
} }
if (!$request->has('user_angeltype_id')) { if (!$request->has('user_angeltype_id')) {
error(__('User angeltype doesn\'t exist.')); error(__('User angeltype doesn\'t exist.'));
throw_redirect(page_link_to('angeltypes')); throw_redirect(url('/angeltypes'));
} }
if ($request->has('supporter') && preg_match('/^[01]$/', $request->input('supporter'))) { if ($request->has('supporter') && preg_match('/^[01]$/', $request->input('supporter'))) {
$supporter = $request->input('supporter') == '1'; $supporter = $request->input('supporter') == '1';
} else { } else {
error(__('No supporter update given.')); error(__('No supporter update given.'));
throw_redirect(page_link_to('angeltypes')); throw_redirect(url('/angeltypes'));
} }
/** @var UserAngelType $user_angeltype */ /** @var UserAngelType $user_angeltype */
@ -321,7 +302,7 @@ function user_angeltype_update_controller(): array
)); ));
success(sprintf($msg, $angeltype->name, $user_source->displayName)); success(sprintf($msg, $angeltype->name, $user_source->displayName));
throw_redirect(page_link_to('angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype->id])); throw_redirect(url('/angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype->id]));
} }
return [ return [
@ -382,7 +363,7 @@ function user_angeltype_add_controller(): array
user_angeltype_add_email($user_source, $angeltype); user_angeltype_add_email($user_source, $angeltype);
throw_redirect(page_link_to('angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype->id])); throw_redirect(url('/angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype->id]));
} }
} }
@ -406,7 +387,7 @@ function user_angeltype_join_controller(AngelType $angeltype)
$user_angeltype = UserAngelType::whereUserId($user->id)->where('angel_type_id', $angeltype->id)->first(); $user_angeltype = UserAngelType::whereUserId($user->id)->where('angel_type_id', $angeltype->id)->first();
if (!empty($user_angeltype)) { if (!empty($user_angeltype)) {
error(sprintf(__('You are already a %s.'), $angeltype->name)); error(sprintf(__('You are already a %s.'), $angeltype->name));
throw_redirect(page_link_to('angeltypes')); throw_redirect(url('/angeltypes'));
} }
$request = request(); $request = request();
@ -434,11 +415,11 @@ function user_angeltype_join_controller(AngelType $angeltype)
)); ));
} }
throw_redirect(page_link_to('angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype->id])); throw_redirect(url('/angeltypes', ['action' => 'view', 'angeltype_id' => $angeltype->id]));
} }
return [ return [
sprintf(__('Become a %s'), $angeltype->name), sprintf(__('Become a %s'), htmlspecialchars($angeltype->name)),
UserAngelType_join_view($user, $angeltype), UserAngelType_join_view($user, $angeltype),
]; ];
} }
@ -452,7 +433,7 @@ function user_angeltypes_controller(): array
{ {
$request = request(); $request = request();
if (!$request->has('action')) { if (!$request->has('action')) {
throw_redirect(page_link_to('angeltypes')); throw_redirect(url('/angeltypes'));
} }
return match ($request->input('action')) { return match ($request->input('action')) {
@ -462,6 +443,6 @@ function user_angeltypes_controller(): array
'delete' => user_angeltype_delete_controller(), 'delete' => user_angeltype_delete_controller(),
'update' => user_angeltype_update_controller(), 'update' => user_angeltype_update_controller(),
'add' => user_angeltype_add_controller(), 'add' => user_angeltype_add_controller(),
default => throw_redirect(page_link_to('angeltyps')), default => throw_redirect(url('/angeltyps')),
}; };
} }

View File

@ -1,143 +0,0 @@
<?php
use Engelsystem\Models\User\User;
/**
* Generates a hint, if user joined angeltypes that require a driving license and the user has no driver license
* information provided.
*
* @return string|null
*/
function user_driver_license_required_hint()
{
$user = auth()->user();
// User has already entered data, no hint needed.
if ($user->license->wantsToDrive()) {
return null;
}
$angeltypes = $user->userAngelTypes;
foreach ($angeltypes as $angeltype) {
if ($angeltype->requires_driver_license) {
return sprintf(
__('You joined an angeltype which requires a driving license. Please edit your driving license information here: %s.'),
'<a href="' . user_driver_license_edit_link() . '" class="text-info">' . __('driving license information') . '</a>'
);
}
}
return null;
}
/**
* Route user driver licenses actions.
*
* @return array
*/
function user_driver_licenses_controller()
{
$user = auth()->user();
if (!$user) {
throw_redirect(page_link_to());
}
$action = strip_request_item('action', 'edit');
return match ($action) {
'edit' => user_driver_license_edit_controller(),
default => user_driver_license_edit_controller(),
};
}
/**
* Link to user driver license edit page for given user.
*
* @param User $user
* @return string
*/
function user_driver_license_edit_link($user = null)
{
if (!$user) {
return page_link_to('user_driver_licenses');
}
return page_link_to('user_driver_licenses', ['user_id' => $user->id]);
}
/**
* Loads the user for the driver license.
*
* @return User
*/
function user_driver_license_load_user()
{
$request = request();
$user_source = auth()->user();
if ($request->has('user_id')) {
$user_source = User::find($request->input('user_id'));
if (empty($user_source)) {
throw_redirect(user_driver_license_edit_link());
}
}
return $user_source;
}
/**
* Edit a users driver license information.
*
* @return array
*/
function user_driver_license_edit_controller()
{
$user = auth()->user();
$request = request();
$user_source = user_driver_license_load_user();
// only privilege admin_user can edit other users driver license information
if ($user->id != $user_source->id && !auth()->can('admin_user')) {
throw_redirect(user_driver_license_edit_link());
}
$driverLicense = $user_source->license;
if ($request->hasPostData('submit')) {
if ($request->has('wants_to_drive')) {
$driverLicense->has_car = $request->has('has_car');
$driverLicense->drive_car = $request->has('has_license_car');
$driverLicense->drive_3_5t = $request->has('has_license_3_5t_transporter');
$driverLicense->drive_7_5t = $request->has('has_license_7_5t_truck');
$driverLicense->drive_12t = $request->has('has_license_12t_truck');
$driverLicense->drive_forklift = $request->has('has_license_forklift');
if ($driverLicense->wantsToDrive()) {
$driverLicense->save();
engelsystem_log('Driver license information updated.');
success(__('Your driver license information has been saved.'));
throw_redirect(user_link($user_source->id));
} else {
error(__('Please select at least one driving license.'));
}
} else {
$driverLicense->has_car = false;
$driverLicense->drive_car = false;
$driverLicense->drive_3_5t = false;
$driverLicense->drive_7_5t = false;
$driverLicense->drive_12t = false;
$driverLicense->drive_forklift = false;
$driverLicense->save();
engelsystem_log('Driver license information removed.');
success(__('Your driver license information has been removed.'));
throw_redirect(user_link($user_source->id));
}
}
return [
sprintf(__('Edit %s driving license information'), $user_source->displayName),
UserDriverLicense_edit_view($user_source, $driverLicense),
];
}

View File

@ -1,12 +1,14 @@
<?php <?php
use Engelsystem\Database\Db; use Engelsystem\Database\Db;
use Engelsystem\Models\AngelType;
use Engelsystem\Models\Shifts\ShiftEntry; use Engelsystem\Models\Shifts\ShiftEntry;
use Engelsystem\Models\User\State; use Engelsystem\Models\User\State;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
use Engelsystem\ShiftCalendarRenderer; use Engelsystem\ShiftCalendarRenderer;
use Engelsystem\ShiftsFilter; use Engelsystem\ShiftsFilter;
use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Str; use Illuminate\Support\Str;
/** /**
@ -20,7 +22,7 @@ function users_controller()
$request = request(); $request = request();
if (!$user) { if (!$user) {
throw_redirect(page_link_to()); throw_redirect(url('/'));
} }
$action = 'list'; $action = 'list';
@ -55,7 +57,7 @@ function user_delete_controller()
} }
if (!auth()->can('admin_user')) { if (!auth()->can('admin_user')) {
throw_redirect(page_link_to()); throw_redirect(url('/'));
} }
// You cannot delete yourself // You cannot delete yourself
@ -91,7 +93,7 @@ function user_delete_controller()
} }
return [ return [
sprintf(__('Delete %s'), $user_source->displayName), sprintf(__('Delete %s'), htmlspecialchars($user_source->displayName)),
User_delete_view($user_source), User_delete_view($user_source),
]; ];
} }
@ -101,7 +103,7 @@ function user_delete_controller()
*/ */
function users_link() function users_link()
{ {
return page_link_to('users'); return url('/users');
} }
/** /**
@ -110,7 +112,7 @@ function users_link()
*/ */
function user_edit_link($userId) function user_edit_link($userId)
{ {
return page_link_to('admin_user', ['user_id' => $userId]); return url('/admin-user', ['user_id' => $userId]);
} }
/** /**
@ -119,7 +121,7 @@ function user_edit_link($userId)
*/ */
function user_delete_link($userId) function user_delete_link($userId)
{ {
return page_link_to('users', ['action' => 'delete', 'user_id' => $userId]); return url('/users', ['action' => 'delete', 'user_id' => $userId]);
} }
/** /**
@ -128,7 +130,7 @@ function user_delete_link($userId)
*/ */
function user_link($userId) function user_link($userId)
{ {
return page_link_to('users', ['action' => 'view', 'user_id' => $userId]); return url('/users', ['action' => 'view', 'user_id' => $userId]);
} }
/** /**
@ -149,7 +151,7 @@ function user_edit_vouchers_controller()
(!auth()->can('admin_user') && !auth()->can('voucher.edit')) (!auth()->can('admin_user') && !auth()->can('voucher.edit'))
|| !config('enable_voucher') || !config('enable_voucher')
) { ) {
throw_redirect(page_link_to()); throw_redirect(url('/'));
} }
if ($request->hasPostData('submit')) { if ($request->hasPostData('submit')) {
@ -182,7 +184,7 @@ function user_edit_vouchers_controller()
} }
return [ return [
sprintf(__('%s\'s vouchers'), $user_source->displayName), sprintf(__('%s\'s vouchers'), htmlspecialchars($user_source->displayName)),
User_edit_vouchers_view($user_source), User_edit_vouchers_view($user_source),
]; ];
} }
@ -200,11 +202,11 @@ function user_controller()
$user_source = User::find($request->input('user_id')); $user_source = User::find($request->input('user_id'));
if (!$user_source) { if (!$user_source) {
error(__('User not found.')); error(__('User not found.'));
throw_redirect(page_link_to('/')); throw_redirect(url('/'));
} }
} }
$shifts = Shifts_by_user($user_source->id, auth()->can('user_shifts_admin')); $shifts = Shifts_by_user($user_source->id, true);
foreach ($shifts as $shift) { foreach ($shifts as $shift) {
// TODO: Move queries to model // TODO: Move queries to model
$shift->needed_angeltypes = Db::select( $shift->needed_angeltypes = Db::select(
@ -234,17 +236,32 @@ function user_controller()
} }
if (empty($user_source->api_key)) { if (empty($user_source->api_key)) {
User_reset_api_key($user_source, false); auth()->resetApiKey($user_source);
} }
if ($user_source->state->force_active) { $goodie_score = sprintf('%.2f', User_goodie_score($user_source->id)) . '&nbsp;h';
$tshirt_score = __('Enough'); if ($user_source->state->force_active && config('enable_force_active')) {
} else { $goodie_score = '<span title="' . $goodie_score . '">' . __('Enough') . '</span>';
$tshirt_score = sprintf('%.2f', User_tshirt_score($user_source->id)) . '&nbsp;h';
} }
$worklogs = $user_source->worklogs()
->with(['user', 'creator'])
->get();
$is_ifsg_supporter = (bool) AngelType::whereRequiresIfsgCertificate(true)
->leftJoin('user_angel_type', 'user_angel_type.angel_type_id', 'angel_types.id')
->where('user_angel_type.user_id', $user->id)
->where('user_angel_type.supporter', true)
->count();
$is_drive_supporter = (bool) AngelType::whereRequiresDriverLicense(true)
->leftJoin('user_angel_type', 'user_angel_type.angel_type_id', 'angel_types.id')
->where('user_angel_type.user_id', $user->id)
->where('user_angel_type.supporter', true)
->count();
return [ return [
$user_source->displayName, htmlspecialchars($user_source->displayName),
User_view( User_view(
$user_source, $user_source,
auth()->can('admin_user'), auth()->can('admin_user'),
@ -253,10 +270,14 @@ function user_controller()
$user_source->groups, $user_source->groups,
$shifts, $shifts,
$user->id == $user_source->id, $user->id == $user_source->id,
$tshirt_score, $goodie_score,
auth()->can('admin_active'), auth()->can('user.goodie.edit'),
auth()->can('admin_user_worklog'), auth()->can('admin_user_worklog'),
UserWorkLogsForUser($user_source->id) $worklogs,
auth()->can('user.ifsg.edit')
|| $is_ifsg_supporter
|| auth()->can('user.drive.edit')
|| $is_drive_supporter,
), ),
]; ];
} }
@ -271,7 +292,7 @@ function users_list_controller()
$request = request(); $request = request();
if (!auth()->can('admin_user')) { if (!auth()->can('admin_user')) {
throw_redirect(page_link_to()); throw_redirect(url('/'));
} }
$order_by = 'name'; $order_by = 'name';
@ -286,7 +307,7 @@ function users_list_controller()
'freeloads', 'freeloads',
'active', 'active',
'force_active', 'force_active',
'got_shirt', 'got_goodie',
'shirt_size', 'shirt_size',
'planned_arrival_date', 'planned_arrival_date',
'planned_departure_date', 'planned_departure_date',
@ -297,13 +318,15 @@ function users_list_controller()
} }
/** @var User[]|Collection $users */ /** @var User[]|Collection $users */
$users = User::with(['contact', 'personalData', 'state']) $users = User::with(['contact', 'personalData', 'state', 'shiftEntries' => function (HasMany $query) {
$query->where('freeloaded', true);
}])
->orderBy('name') ->orderBy('name')
->get(); ->get();
foreach ($users as $user) { foreach ($users as $user) {
$user->setAttribute( $user->setAttribute(
'freeloads', 'freeloads',
$user->shiftEntries() $user->shiftEntries
->where('freeloaded', true) ->where('freeloaded', true)
->count() ->count()
); );
@ -328,7 +351,7 @@ function users_list_controller()
State::whereActive(true)->count(), State::whereActive(true)->count(),
State::whereForceActive(true)->count(), State::whereForceActive(true)->count(),
ShiftEntry::whereFreeloaded(true)->count(), ShiftEntry::whereFreeloaded(true)->count(),
State::whereGotShirt(true)->count(), State::whereGotGoodie(true)->count(),
State::query()->sum('got_voucher') State::query()->sum('got_voucher')
), ),
]; ];
@ -343,13 +366,13 @@ function load_user()
{ {
$request = request(); $request = request();
if (!$request->has('user_id')) { if (!$request->has('user_id')) {
throw_redirect(page_link_to()); throw_redirect(url('/'));
} }
$user = User::find($request->input('user_id')); $user = User::find($request->input('user_id'));
if (!$user) { if (!$user) {
error(__('User doesn\'t exist.')); error(__('User doesn\'t exist.'));
throw_redirect(page_link_to()); throw_redirect(url('/'));
} }
return $user; return $user;
@ -437,3 +460,57 @@ function shiftCalendarRendererByShiftFilter(ShiftsFilter $shiftsFilter)
return new ShiftCalendarRenderer($filtered_shifts, $needed_angeltypes, $shift_entries, $shiftsFilter); return new ShiftCalendarRenderer($filtered_shifts, $needed_angeltypes, $shift_entries, $shiftsFilter);
} }
/**
* Generates a hint, if user joined angeltypes that require a driving license and the user has no driver license
* information provided.
*
* @return string|null
*/
function user_driver_license_required_hint()
{
$user = auth()->user();
// User has already entered data, no hint needed.
if (!config('driving_license_enabled') || $user->license->wantsToDrive()) {
return null;
}
$angeltypes = $user->userAngelTypes;
foreach ($angeltypes as $angeltype) {
if ($angeltype->requires_driver_license) {
return sprintf(
__('angeltype.driving_license.required.info.here'),
'<a href="' . url('/settings/certificates') . '">' . __('driving_license.info') . '</a>'
);
}
}
return null;
}
function user_ifsg_certificate_required_hint()
{
$user = auth()->user();
// User has already entered data, no hint needed.
if (!config('ifsg_enabled') || $user->license->ifsg_light || $user->license->ifsg) {
return null;
}
$angeltypes = $user->userAngelTypes;
foreach ($angeltypes as $angeltype) {
if (
$angeltype->requires_ifsg_certificate && !(
$user->license->ifsg_certificate || $user->license->ifsg_certificate_light
)
) {
return sprintf(
__('angeltype.ifsg.required.info.here'),
'<a href="' . url('/settings/certificates') . '">' . __('ifsg.info') . '</a>'
);
}
}
return null;
}

View File

@ -4,6 +4,7 @@
* Bootstrap application * Bootstrap application
*/ */
use Engelsystem\Application;
use Engelsystem\Http\UrlGeneratorInterface; use Engelsystem\Http\UrlGeneratorInterface;
require __DIR__ . '/application.php'; require __DIR__ . '/application.php';
@ -18,12 +19,12 @@ require __DIR__ . '/includes.php';
/** /**
* Check for maintenance * Check for maintenance
*/ */
/** @var \Engelsystem\Application $app */ /** @var Application $app */
if ($app->get('config')->get('maintenance')) { if ($app->get('config')->get('maintenance')) {
http_response_code(503); http_response_code(503);
$url = $app->get(UrlGeneratorInterface::class); $url = $app->get(UrlGeneratorInterface::class);
$maintenance = file_get_contents(__DIR__ . '/../resources/views/layouts/maintenance.html'); $maintenance = file_get_contents(__DIR__ . '/../resources/views/layouts/maintenance.html');
$maintenance = str_replace('%APP_NAME%', $app->get('config')->get('app_name'), $maintenance); $maintenance = str_replace('%APP_NAME%', htmlspecialchars($app->get('config')->get('app_name')), $maintenance);
$maintenance = str_replace('%ASSETS_PATH%', $url->to(''), $maintenance); $maintenance = str_replace('%ASSETS_PATH%', $url->to(''), $maintenance);
echo $maintenance; echo $maintenance;
die(); die();

View File

@ -1,9 +1,7 @@
<?php <?php
use Engelsystem\Helpers\Translation\Translator;
use Engelsystem\Mail\EngelsystemMailer; use Engelsystem\Mail\EngelsystemMailer;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
use Psr\Log\LogLevel;
/** /**
* @param User $recipientUser * @param User $recipientUser
@ -18,38 +16,18 @@ function engelsystem_email_to_user($recipientUser, $title, $message, $notIfItsMe
return true; return true;
} }
/** @var Translator $translator */
$translator = app()->get('translator');
$locale = $translator->getLocale();
$status = true;
try {
/** @var EngelsystemMailer $mailer */ /** @var EngelsystemMailer $mailer */
$mailer = app('mailer'); $mailer = app('mailer');
$status = $mailer->sendViewTranslated(
$translator->setLocale($recipientUser->settings->language); $recipientUser,
$mailer->sendView(
$recipientUser->contact->email ?: $recipientUser->email,
$title, $title,
'emails/mail', 'emails/mail',
['username' => $recipientUser->displayName, 'message' => $message] ['username' => $recipientUser->displayName, 'message' => $message]
); );
} catch (Exception $e) {
$status = false;
engelsystem_log(sprintf(
'An exception occurred while sending a mail to %s in %s:%u: %s',
$recipientUser->name,
$e->getFile(),
$e->getLine(),
$e->getMessage()
), LogLevel::CRITICAL);
}
$translator->setLocale($locale);
if (!$status) { if (!$status) {
error(sprintf(__('User %s could not be notified by email due to an error.'), $recipientUser->displayName)); error(sprintf(__('User %s could not be notified by e-mail due to an error.'), $recipientUser->displayName));
engelsystem_log(sprintf('User %s could not be notified by email due to an error.', $recipientUser->name)); engelsystem_log(sprintf('User %s could not be notified by e-mail due to an error.', $recipientUser->name));
} }
return $status; return $status;

View File

@ -3,6 +3,8 @@
declare(strict_types=1); declare(strict_types=1);
use Engelsystem\Renderer\Twig\Extensions\Globals; use Engelsystem\Renderer\Twig\Extensions\Globals;
use Engelsystem\Helpers\Carbon;
use Engelsystem\Helpers\DayOfEvent;
function theme_id(): int function theme_id(): int
{ {
@ -12,9 +14,6 @@ function theme_id(): int
return $globals['themeId']; return $globals['themeId'];
} }
/**
* @return array
*/
function theme(): array function theme(): array
{ {
$theme_id = theme_id(); $theme_id = theme_id();
@ -25,3 +24,16 @@ function theme_type(): string
{ {
return theme()['type']; return theme()['type'];
} }
function dateWithEventDay(string $day): string
{
$date = Carbon::createFromFormat('Y-m-d', $day);
$dayOfEvent = DayOfEvent::get($date);
$dateFormatted = $date->format(__('general.date'));
if (!config('enable_show_day_of_event') || is_null($dayOfEvent)) {
return $dateFormatted;
}
return $dateFormatted . ' (' . $dayOfEvent . ')';
}

View File

@ -17,11 +17,12 @@ function msg()
* *
* @param string $msg * @param string $msg
* @param bool $immediately * @param bool $immediately
* @param bool $immediatelyRaw
* @return string * @return string
*/ */
function info($msg, $immediately = false) function info($msg, $immediately = false, $immediatelyRaw = false)
{ {
return alert(NotificationType::INFORMATION, $msg, $immediately); return alert(NotificationType::INFORMATION, $msg, $immediately, $immediatelyRaw);
} }
/** /**
@ -29,11 +30,12 @@ function info($msg, $immediately = false)
* *
* @param string $msg * @param string $msg
* @param bool $immediately * @param bool $immediately
* @param bool $immediatelyRaw
* @return string * @return string
*/ */
function warning($msg, $immediately = false) function warning($msg, $immediately = false, $immediatelyRaw = false)
{ {
return alert(NotificationType::WARNING, $msg, $immediately); return alert(NotificationType::WARNING, $msg, $immediately, $immediatelyRaw);
} }
/** /**
@ -41,11 +43,12 @@ function warning($msg, $immediately = false)
* *
* @param string $msg * @param string $msg
* @param bool $immediately * @param bool $immediately
* @param bool $immediatelyRaw
* @return string * @return string
*/ */
function error($msg, $immediately = false) function error($msg, $immediately = false, $immediatelyRaw = false)
{ {
return alert(NotificationType::ERROR, $msg, $immediately); return alert(NotificationType::ERROR, $msg, $immediately, $immediatelyRaw);
} }
/** /**
@ -53,24 +56,27 @@ function error($msg, $immediately = false)
* *
* @param string $msg * @param string $msg
* @param bool $immediately * @param bool $immediately
* @param bool $immediatelyRaw
* @return string * @return string
*/ */
function success($msg, $immediately = false) function success($msg, $immediately = false, $immediatelyRaw = false)
{ {
return alert(NotificationType::MESSAGE, $msg, $immediately); return alert(NotificationType::MESSAGE, $msg, $immediately, $immediatelyRaw);
} }
/** /**
* Renders an alert message with the given alert-* class or sets it in session * Renders an alert message with the given alert-* class or sets it in session
* *
* @see \Engelsystem\Controllers\HasUserNotifications
*
* @param NotificationType $type * @param NotificationType $type
* @param string $msg * @param string $msg
* @param bool $immediately * @param bool $immediately
* @param bool $immediatelyRaw
* @return string * @return string
*
* @see \Engelsystem\Controllers\HasUserNotifications
*
*/ */
function alert(NotificationType $type, $msg, $immediately = false) function alert(NotificationType $type, $msg, $immediately = false, $immediatelyRaw = false)
{ {
if (empty($msg)) { if (empty($msg)) {
return ''; return '';
@ -87,6 +93,7 @@ function alert(NotificationType $type, $msg, $immediately = false)
['danger', 'warning', 'info', 'success'], ['danger', 'warning', 'info', 'success'],
$type->value $type->value
); );
$msg = $immediatelyRaw ? $msg : htmlspecialchars($msg);
return '<div class="alert alert-' . $type . '" role="alert">' . $msg . '</div>'; return '<div class="alert alert-' . $type . '" role="alert">' . $msg . '</div>';
} }

View File

@ -1,97 +0,0 @@
<?php
namespace Engelsystem\Events\Listener;
use Carbon\Carbon;
use Engelsystem\Helpers\Shifts;
use Engelsystem\Mail\EngelsystemMailer;
use Engelsystem\Models\Room;
use Engelsystem\Models\User\User;
use Engelsystem\Models\Worklog;
use Psr\Log\LoggerInterface;
use Symfony\Component\Mailer\Exception\TransportException;
class Shift
{
public function __construct(
protected LoggerInterface $log,
protected EngelsystemMailer $mailer
) {
}
public function deletedEntryCreateWorklog(
User $user,
Carbon $start,
Carbon $end,
string $name,
string $title,
string $type,
Room $room,
bool $freeloaded
): void {
if ($freeloaded || $start > Carbon::now()) {
return;
}
$workLog = new Worklog();
$workLog->user()->associate($user);
$workLog->creator()->associate(auth()->user());
$workLog->worked_at = $start->copy()->startOfDay();
$workLog->hours =
(($end->timestamp - $start->timestamp) / 60 / 60)
* Shifts::getNightShiftMultiplier($start, $end);
$workLog->comment = sprintf(
__('%s (%s as %s) in %s, %s - %s'),
$name,
$title,
$type,
$room->name,
$start->format(__('Y-m-d H:i')),
$end->format(__('Y-m-d H:i'))
);
$workLog->save();
$this->log->info(
'Created worklog entry from shift for {user} ({uid}): {worklog})',
['user' => $workLog->user->name, 'uid' => $workLog->user->id, 'worklog' => $workLog->comment]
);
}
public function deletedEntrySendEmail(
User $user,
Carbon $start,
Carbon $end,
string $name,
string $title,
string $type,
Room $room,
bool $freeloaded
): void {
if (!$user->settings->email_shiftinfo) {
return;
}
$subject = 'notification.shift.deleted';
try {
$this->mailer->sendViewTranslated(
$user,
$subject,
'emails/worklog-from-shift',
[
'name' => $name,
'title' => $title,
'start' => $start,
'end' => $end,
'room' => $room,
'freeloaded' => $freeloaded,
'username' => $user->displayName,
]
);
} catch (TransportException $e) {
$this->log->error(
'Unable to send email "{title}" to user {user} with {exception}',
['title' => $subject, 'user' => $user->name, 'exception' => $e]
);
}
}
}

View File

@ -18,40 +18,34 @@ $includeFiles = [
__DIR__ . '/../includes/model/ShiftSignupState.php', __DIR__ . '/../includes/model/ShiftSignupState.php',
__DIR__ . '/../includes/model/Stats.php', __DIR__ . '/../includes/model/Stats.php',
__DIR__ . '/../includes/model/User_model.php', __DIR__ . '/../includes/model/User_model.php',
__DIR__ . '/../includes/model/UserWorkLog_model.php',
__DIR__ . '/../includes/model/ValidationResult.php', __DIR__ . '/../includes/model/ValidationResult.php',
__DIR__ . '/../includes/view/AngelTypes_view.php', __DIR__ . '/../includes/view/AngelTypes_view.php',
__DIR__ . '/../includes/view/EventConfig_view.php', __DIR__ . '/../includes/view/EventConfig_view.php',
__DIR__ . '/../includes/view/PublicDashboard_view.php', __DIR__ . '/../includes/view/PublicDashboard_view.php',
__DIR__ . '/../includes/view/Rooms_view.php', __DIR__ . '/../includes/view/Locations_view.php',
__DIR__ . '/../includes/view/ShiftCalendarLane.php', __DIR__ . '/../includes/view/ShiftCalendarLane.php',
__DIR__ . '/../includes/view/ShiftCalendarRenderer.php', __DIR__ . '/../includes/view/ShiftCalendarRenderer.php',
__DIR__ . '/../includes/view/ShiftCalendarShiftRenderer.php', __DIR__ . '/../includes/view/ShiftCalendarShiftRenderer.php',
__DIR__ . '/../includes/view/ShiftsFilterRenderer.php', __DIR__ . '/../includes/view/ShiftsFilterRenderer.php',
__DIR__ . '/../includes/view/Shifts_view.php', __DIR__ . '/../includes/view/Shifts_view.php',
__DIR__ . '/../includes/view/ShiftEntry_view.php', __DIR__ . '/../includes/view/ShiftEntry_view.php',
__DIR__ . '/../includes/view/ShiftTypes_view.php',
__DIR__ . '/../includes/view/UserAngelTypes_view.php', __DIR__ . '/../includes/view/UserAngelTypes_view.php',
__DIR__ . '/../includes/view/UserDriverLicenses_view.php',
__DIR__ . '/../includes/view/UserHintsRenderer.php', __DIR__ . '/../includes/view/UserHintsRenderer.php',
__DIR__ . '/../includes/view/User_view.php', __DIR__ . '/../includes/view/User_view.php',
__DIR__ . '/../includes/controller/angeltypes_controller.php', __DIR__ . '/../includes/controller/angeltypes_controller.php',
__DIR__ . '/../includes/controller/event_config_controller.php', __DIR__ . '/../includes/controller/event_config_controller.php',
__DIR__ . '/../includes/controller/public_dashboard_controller.php', __DIR__ . '/../includes/controller/public_dashboard_controller.php',
__DIR__ . '/../includes/controller/rooms_controller.php', __DIR__ . '/../includes/controller/locations_controller.php',
__DIR__ . '/../includes/controller/shift_entries_controller.php', __DIR__ . '/../includes/controller/shift_entries_controller.php',
__DIR__ . '/../includes/controller/shifts_controller.php', __DIR__ . '/../includes/controller/shifts_controller.php',
__DIR__ . '/../includes/controller/shifttypes_controller.php',
__DIR__ . '/../includes/controller/users_controller.php', __DIR__ . '/../includes/controller/users_controller.php',
__DIR__ . '/../includes/controller/user_angeltypes_controller.php', __DIR__ . '/../includes/controller/user_angeltypes_controller.php',
__DIR__ . '/../includes/controller/user_driver_licenses_controller.php',
__DIR__ . '/../includes/helper/legacy_helper.php', __DIR__ . '/../includes/helper/legacy_helper.php',
__DIR__ . '/../includes/helper/message_helper.php', __DIR__ . '/../includes/helper/message_helper.php',
__DIR__ . '/../includes/helper/email_helper.php', __DIR__ . '/../includes/helper/email_helper.php',
__DIR__ . '/../includes/helper/shift_helper.php',
__DIR__ . '/../includes/mailer/shifts_mailer.php', __DIR__ . '/../includes/mailer/shifts_mailer.php',
__DIR__ . '/../includes/mailer/users_mailer.php', __DIR__ . '/../includes/mailer/users_mailer.php',
@ -62,11 +56,8 @@ $includeFiles = [
__DIR__ . '/../includes/pages/admin_groups.php', __DIR__ . '/../includes/pages/admin_groups.php',
__DIR__ . '/../includes/pages/admin_shifts.php', __DIR__ . '/../includes/pages/admin_shifts.php',
__DIR__ . '/../includes/pages/admin_user.php', __DIR__ . '/../includes/pages/admin_user.php',
__DIR__ . '/../includes/pages/guest_login.php',
__DIR__ . '/../includes/pages/user_myshifts.php', __DIR__ . '/../includes/pages/user_myshifts.php',
__DIR__ . '/../includes/pages/user_shifts.php', __DIR__ . '/../includes/pages/user_shifts.php',
__DIR__ . '/../includes/pages/schedule/ImportSchedule.php',
]; ];
foreach ($includeFiles as $file) { foreach ($includeFiles as $file) {

View File

@ -1,87 +1,7 @@
<?php <?php
use Engelsystem\Models\Shifts\Shift; use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftEntry;
use Engelsystem\Models\User\User; use Engelsystem\Models\User\User;
use Illuminate\Database\Eloquent\Collection;
function mail_shift_change(Shift $old_shift, Shift $new_shift)
{
/** @var ShiftEntry[]|Collection $shiftEntries */
$shiftEntries = $old_shift->shiftEntries()
->with(['user', 'user.settings'])
->get();
$old_room = $old_shift->room;
$new_room = $new_shift->room;
$noticeable_changes = false;
$message = __('A Shift you are registered on has changed:');
$message .= "\n";
if ($old_shift->shift_type_id != $new_shift->shift_type_id) {
$message .= sprintf(
__('* Shift type changed from %s to %s'),
$old_shift->shiftType->name,
$new_shift->shiftType->name
) . "\n";
$noticeable_changes = true;
}
if ($old_shift->title != $new_shift->title) {
$message .= sprintf(__('* Shift title changed from %s to %s'), $old_shift->title, $new_shift->title) . "\n";
$noticeable_changes = true;
}
if ($old_shift->start->timestamp != $new_shift->start->timestamp) {
$message .= sprintf(
__('* Shift Start changed from %s to %s'),
$old_shift->start->format(__('Y-m-d H:i')),
$new_shift->start->format(__('Y-m-d H:i'))
) . "\n";
$noticeable_changes = true;
}
if ($old_shift->end->timestamp != $new_shift->end->timestamp) {
$message .= sprintf(
__('* Shift End changed from %s to %s'),
$old_shift->end->format(__('Y-m-d H:i')),
$new_shift->end->format(__('Y-m-d H:i'))
) . "\n";
$noticeable_changes = true;
}
if ($old_shift->room_id != $new_shift->room_id) {
$message .= sprintf(__('* Shift Location changed from %s to %s'), $old_room->name, $new_room->name) . "\n";
$noticeable_changes = true;
}
if (!$noticeable_changes) {
// There are no changes worth sending an E-Mail
return;
}
$message .= "\n";
$message .= __('The updated Shift:') . "\n";
$message .= $new_shift->shiftType->name . "\n";
$message .= $new_shift->title . "\n";
$message .= $new_shift->start->format(__('Y-m-d H:i')) . ' - ' . $new_shift->end->format(__('H:i')) . "\n";
$message .= $new_room->name . "\n\n";
$message .= url('/shifts', ['action' => 'view', 'shift_id' => $new_shift->id]) . "\n";
foreach ($shiftEntries as $shiftEntry) {
$user = $shiftEntry->user;
if ($user->settings->email_shiftinfo) {
engelsystem_email_to_user(
$user,
__('Your Shift has changed'),
$message,
true
);
}
}
}
function mail_shift_assign(User $user, Shift $shift) function mail_shift_assign(User $user, Shift $shift)
{ {
@ -89,13 +9,11 @@ function mail_shift_assign(User $user, Shift $shift)
return; return;
} }
$room = $shift->room;
$message = __('You have been assigned to a Shift:') . "\n"; $message = __('You have been assigned to a Shift:') . "\n";
$message .= $shift->shiftType->name . "\n"; $message .= $shift->shiftType->name . "\n";
$message .= $shift->title . "\n"; $message .= $shift->title . "\n";
$message .= $shift->start->format(__('Y-m-d H:i')) . ' - ' . $shift->end->format(__('H:i')) . "\n"; $message .= $shift->start->format(__('general.datetime')) . ' - ' . $shift->end->format(__('H:i')) . "\n";
$message .= $room->name . "\n\n"; $message .= $shift->location->name . "\n\n";
$message .= url('/shifts', ['action' => 'view', 'shift_id' => $shift->id]) . "\n"; $message .= url('/shifts', ['action' => 'view', 'shift_id' => $shift->id]) . "\n";
engelsystem_email_to_user($user, __('Assigned to Shift'), $message, true); engelsystem_email_to_user($user, __('Assigned to Shift'), $message, true);
@ -107,13 +25,11 @@ function mail_shift_removed(User $user, Shift $shift)
return; return;
} }
$room = $shift->room;
$message = __('You have been removed from a Shift:') . "\n"; $message = __('You have been removed from a Shift:') . "\n";
$message .= $shift->shiftType->name . "\n"; $message .= $shift->shiftType->name . "\n";
$message .= $shift->title . "\n"; $message .= $shift->title . "\n";
$message .= $shift->start->format(__('Y-m-d H:i')) . ' - ' . $shift->end->format(__('H:i')) . "\n"; $message .= $shift->start->format(__('general.datetime')) . ' - ' . $shift->end->format(__('H:i')) . "\n";
$message .= $room->name . "\n"; $message .= $shift->location->name . "\n";
engelsystem_email_to_user($user, __('Removed from Shift'), $message, true); engelsystem_email_to_user($user, __('Removed from Shift'), $message, true);
} }

View File

@ -10,7 +10,7 @@ function mail_user_delete($user)
{ {
return engelsystem_email_to_user( return engelsystem_email_to_user(
$user, $user,
__('Your account has been deleted'), __('Your account has been deleted.'),
__( __(
'Your %s account has been deleted. If you have any questions regarding your account deletion, please contact heaven.', 'Your %s account has been deleted. If you have any questions regarding your account deletion, please contact heaven.',
[config('app_name')] [config('app_name')]

View File

@ -1,47 +1,70 @@
<?php <?php
use Engelsystem\Database\Db; use Engelsystem\Database\Db;
use Engelsystem\Models\Shifts\Shift;
use Engelsystem\Models\Shifts\ShiftEntry; use Engelsystem\Models\Shifts\ShiftEntry;
use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Collection;
/** /**
* Returns all needed angeltypes and already taken needs. * Returns all needed angeltypes and already taken needs.
* *
* @param int $shiftId id of shift * @param Shift $shift
* @return array * @return array
*/ */
function NeededAngelTypes_by_shift($shiftId) function NeededAngelTypes_by_shift($shift)
{ {
$needed_angeltypes_source = [];
// Select from shift
if (!$shift->schedule) {
$needed_angeltypes_source = Db::select( $needed_angeltypes_source = Db::select(
' '
SELECT SELECT
`needed_angel_types`.*, `needed_angel_types`.*,
`angel_types`.`id`,
`angel_types`.`name`, `angel_types`.`name`,
`angel_types`.`restricted`, `angel_types`.`restricted`,
`angel_types`.`no_self_signup` `angel_types`.`shift_self_signup`
FROM `needed_angel_types` FROM `needed_angel_types`
JOIN `angel_types` ON `angel_types`.`id` = `needed_angel_types`.`angel_type_id` JOIN `angel_types` ON `angel_types`.`id` = `needed_angel_types`.`angel_type_id`
WHERE `shift_id` = ? WHERE `needed_angel_types`.`shift_id` = ?
ORDER BY `room_id` DESC', ORDER BY `location_id` DESC
[$shiftId] ',
[$shift->id]
); );
}
// Use settings from room // Get needed by shift type
if (count($needed_angeltypes_source) == 0) { if ($shift->schedule && $shift->schedule->needed_from_shift_type) {
$needed_angeltypes_source = Db::select(' $needed_angeltypes_source = Db::select('
SELECT `needed_angel_types`.*, `angel_types`.`name`, `angel_types`.`restricted` SELECT
`needed_angel_types`.*,
`angel_types`.`name`,
`angel_types`.`restricted`,
`angel_types`.`shift_self_signup`
FROM `needed_angel_types` FROM `needed_angel_types`
JOIN `angel_types` ON `angel_types`.`id` = `needed_angel_types`.`angel_type_id` JOIN `angel_types` ON `angel_types`.`id` = `needed_angel_types`.`angel_type_id`
JOIN `shifts` ON `shifts`.`room_id` = `needed_angel_types`.`room_id` WHERE `needed_angel_types`.`shift_type_id` = ?
WHERE `shifts`.`id` = ? ORDER BY `location_id` DESC
ORDER BY `room_id` DESC ', [$shift->shift_type_id]);
', [$shiftId]); }
// Load from room
if ($shift->schedule && !$shift->schedule->needed_from_shift_type) {
$needed_angeltypes_source = Db::select('
SELECT
`needed_angel_types`.*,
`angel_types`.`name`,
`angel_types`.`restricted`,
`angel_types`.`shift_self_signup`
FROM `needed_angel_types`
JOIN `angel_types` ON `angel_types`.`id` = `needed_angel_types`.`angel_type_id`
WHERE `needed_angel_types`.`location_id` = ?
ORDER BY `location_id` DESC
', [$shift->location_id]);
} }
/** @var ShiftEntry[]|Collection $shift_entries */ /** @var ShiftEntry[]|Collection $shift_entries */
$shift_entries = ShiftEntry::with('user', 'angelType') $shift_entries = ShiftEntry::with('user', 'angelType')
->where('shift_id', $shiftId) ->where('shift_id', $shift->id)
->get(); ->get();
$needed_angeltypes = []; $needed_angeltypes = [];
foreach ($needed_angeltypes_source as $angeltype) { foreach ($needed_angeltypes_source as $angeltype) {

View File

@ -15,7 +15,7 @@ function ShiftEntry_onCreate(ShiftEntry $shiftEntry): void
'User ' . User_Nick_render($shiftEntry->user, true) 'User ' . User_Nick_render($shiftEntry->user, true)
. ' signed up for shift ' . $shiftEntry->shift->title . ' signed up for shift ' . $shiftEntry->shift->title
. ' (' . $shift->shiftType->name . ')' . ' (' . $shift->shiftType->name . ')'
. ' at ' . $shift->room->name . ' at ' . $shift->location->name
. ' from ' . $shift->start->format('Y-m-d H:i') . ' from ' . $shift->start->format('Y-m-d H:i')
. ' to ' . $shift->end->format('Y-m-d H:i') . ' to ' . $shift->end->format('Y-m-d H:i')
. ' as ' . $shiftEntry->angelType->name . ' as ' . $shiftEntry->angelType->name
@ -33,14 +33,14 @@ function ShiftEntry_onDelete(ShiftEntry $shiftEntry)
$signout_user = $shiftEntry->user; $signout_user = $shiftEntry->user;
$shift = Shift($shiftEntry->shift); $shift = Shift($shiftEntry->shift);
$shifttype = $shift->shiftType; $shifttype = $shift->shiftType;
$room = $shift->room; $location = $shift->location;
$angeltype = $shiftEntry->angelType; $angeltype = $shiftEntry->angelType;
engelsystem_log( engelsystem_log(
'Shift signout: ' . User_Nick_render($signout_user, true) 'Shift signout: ' . User_Nick_render($signout_user, true)
. ' from shift ' . $shift->title . ' from shift ' . $shift->title
. ' (' . $shifttype->name . ')' . ' (' . $shifttype->name . ')'
. ' at ' . $room->name . ' at ' . $location->name
. ' from ' . $shift->start->format('Y-m-d H:i') . ' from ' . $shift->start->format('Y-m-d H:i')
. ' to ' . $shift->end->format('Y-m-d H:i') . ' to ' . $shift->end->format('Y-m-d H:i')
. ' as ' . $angeltype->name . ' as ' . $angeltype->name

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