From 9ebe306499244e962a735acc6088754b8e43dd71 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 5 Jan 2020 02:18:35 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=8E=81=20Add=20invitation=20emails=20?= =?UTF-8?q?=E2=98=98=20fix=20link=20to=20fav=20icon=20=E2=98=98=20fix=20de?= =?UTF-8?q?letion=20of=20reservation=20for=20list=20owner=20=F0=9F=9B=A0?= =?UTF-8?q?=EF=B8=8F=20Major=20refactoring=20to=20implement=20separation?= =?UTF-8?q?=20of=20concerns?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 9 + .swiftlint.yml | 18 +- CHANGELOG.md | 17 + Docker/build | 2 +- Docker/build.test | 15 + Docker/docker | 14 +- Docker/docker-compose | 9 + Docker/docker-compose.test | 45 + Docker/docker.test | 10 + Docker/run | 2 +- Docker/run.test | 14 + LinuxMain.swift | 21 + Package.resolved | 25 +- Package.swift | 93 +- Public/styles/wishlist.css | 7 +- Resources/Localizations/de.json | 14 +- Resources/Localizations/en.json | 15 +- Resources/Views/Errors/Authentication.leaf | 2 +- Resources/Views/Protected/Wishlist.leaf | 9 +- Resources/Views/User/Invitations.leaf | 8 + Resources/Views/User/Item.leaf | 10 +- Resources/Views/User/Items-Collection.leaf | 2 +- Resources/Views/User/Items-Table.leaf | 2 +- Resources/Views/User/Items.leaf | 2 +- Resources/Views/User/List.leaf | 6 +- Resources/Views/User/Welcome.leaf | 3 - .../[MySQL]/MySQLModelRepository.swift | 13 - .../Actors/AppAnnouncementsActor+Vapor.swift | 23 + .../Actors/AppEnrollmentActor+Vapor.swift | 27 + .../Actors/AppUserFavoritesActor+Vapor.swift | 28 + .../AppUserInvitationsActor+Vapor.swift | 26 + .../Actors/AppUserItemsActor+Vapor.swift | 27 + .../Actors/AppUserListsActor+Vapor.swift | 28 + .../AppUserNotificationsActor+Vapor.swift | 25 + .../Actors/AppUserProfileActor+Vapor.swift | 26 + .../AppUserReservationsActor+Vapor.swift | 26 + .../Actors/AppUserSettimgsActor+Vapor.swift | 25 + .../Actors/AppUserWelcomeActor+Vapor.swift | 26 + .../Actors/AppWishlistActor+Vapor.swift | 29 + Sources/App/Domain/Adapters/Actors/README.md | 6 + .../Model/EmailSpecification+Fluent.swift | 25 + .../Adapters/Model/Entity/Entity+Fluent.swift | 3 + .../Model/Entity/EntitySorting+Fluent.swift} | 2 + .../Adapters/Model}/Favorite+Fluent.swift | 18 +- .../Model/Identification+Fluent.swift | 33 + .../Adapters/Model}/Invitation+Fluent.swift | 14 +- .../Model}/InvitationCode+Fluent.swift | 11 +- .../Adapters/Model}/Item+Fluent.swift | 20 +- .../Adapters/Model}/List+Fluent.swift | 23 +- .../Adapters/Model}/Reservation+Fluent.swift | 12 +- .../Adapters/Model}/User+Fluent.swift | 17 +- .../Model/UserIdentifier+Fluent.swift | 30 + .../Model/UserIdentityProvider+Fluent.swift | 30 + .../Adapters/Model}/UserSettings+Fluent.swift | 10 +- .../Adapters/Model}/Visibility+Fluent.swift | 4 +- .../MySQLFavoriteRepository+Vapor.swift | 21 + .../MySQLFavoriteRepository.swift | 38 +- .../MySQLInvitationRepository+Vapor.swift | 21 + .../MySQLInvitationRepository.swift | 32 +- .../MySQLItemRepository+Vapor.swift | 21 + .../Repositories}/MySQLItemRepository.swift | 76 +- .../MySQLListRepository+Vapor.swift | 21 + .../Repositories}/MySQLListRepository.swift | 60 +- .../Repositories/MySQLModelRepository.swift | 31 + .../MySQLReservationRepository+Vapor.swift | 21 + .../MySQLReservationRepository.swift | 43 +- .../MySQLUserRepository+Vapor.swift | 21 + .../Repositories}/MySQLUserRepository.swift | 36 +- .../Mappers/Model/EmailSpecification+.swift | 11 + .../Domain/Mappers/Model/FavoriteID+.swift | 24 + .../Mappers/Model/Identification+.swift | 15 + .../Domain/Mappers/Model/InvitationID+.swift | 24 + .../App/Domain/Mappers/Model/ItemID+.swift | 24 + .../App/Domain/Mappers/Model/ListID+.swift | 24 + .../Domain/Mappers/Model/PushoverUser+.swift | 11 + Sources/App/Domain/Mappers/Model/README.md | 3 + .../Domain/Mappers/Model/ReservationID+.swift | 24 + .../App/Domain/Mappers/Model/UserID+.swift | 23 + Sources/App/Domain/Model/Favorite.swift | 27 - Sources/App/Domain/Model/Identification.swift | 8 - Sources/App/Domain/Model/Invitation.swift | 145 - Sources/App/Domain/Model/InvitationData.swift | 63 - Sources/App/Domain/Model/Item.swift | 163 - Sources/App/Domain/Model/List.swift | 93 - Sources/App/Domain/Model/Reservation.swift | 71 - .../Domain/Model/ReservationRepository.swift | 16 - Sources/App/Domain/Model/User.swift | 114 - Sources/App/Domain/Model/UserData.swift | 118 - Sources/App/Domain/Model/UserRepository.swift | 18 - Sources/App/Domain/Model/UserSettings.swift | 39 - Sources/App/Domain/Model/Visibility.swift | 25 - .../App/Domain/Model/[Entity]/Entity.swift | 20 - .../Domain/Model/[Entity]/EntityError.swift | 38 - .../Model/[Entity]/EntityReflectable.swift | 9 - .../Model/[Entity]/EntityRepository.swift | 5 - .../Domain/Model/[Entity]/EntitySorting.swift | 128 - .../App/Domain/Model/[Traits]/Imagable.swift | 38 - .../Providers/VaporEmailSendingProvider.swift | 20 + .../VaporEventRecordingProvider.swift | 29 + .../Providers/VaporImageStoreProvider.swift} | 69 +- .../VaporMessageLoggingProvider.swift | 34 + .../VaporNotificationSendingProvider.swift | 153 + Sources/App/Domain/README.md | 9 + Sources/App/Feature.swift | 39 +- Sources/App/Features.swift | 2 +- .../Announcements/LegalNoticeController.swift | 31 + .../LegalNoticePageContext.swift | 4 +- .../PrivacyPolicyController.swift | 35 + .../PrivacyPolicyPageContext.swift | 4 +- .../Favorites/Controller+Favorite.swift | 16 +- .../Scenes/Favorites/FavoriteContext.swift | 18 +- .../Favorites/FavoriteContextsBuilder.swift | 78 - .../Scenes/Favorites/FavoriteController.swift | 143 +- .../Favorites/FavoritesController.swift | 32 +- .../Favorites/FavoritesPageContext.swift | 40 +- .../Invitations/Controller+Invitation.swift | 38 +- .../Invitations/InvitationContext.swift | 45 +- .../InvitationController+Save.swift | 92 +- .../Invitations/InvitationController.swift | 213 +- .../Scenes/Invitations/InvitationEmail.swift | 76 + ...ext.swift => InvitationEmailContext.swift} | 16 +- ...Error.swift => InvitationEmailError.swift} | 2 +- .../Scenes/Invitations/InvitationMail.swift | 62 - .../Invitations/InvitationPageContext.swift | 28 +- .../Invitations/InvitationPageFormData.swift | 19 +- .../Invitations/InvitationsController.swift | 32 +- .../Invitations/InvitationsPageContext.swift | 61 + .../App/Scenes/Items/Controller+Item.swift | 44 +- Sources/App/Scenes/Items/ItemContext.swift | 67 +- .../Scenes/Items/ItemController+Save.swift | 88 +- Sources/App/Scenes/Items/ItemController.swift | 256 +- .../App/Scenes/Items/ItemPageContext.swift | 41 +- .../App/Scenes/Items/ItemPageFormData.swift | 29 +- .../App/Scenes/Items/ItemsController.swift | 42 +- .../App/Scenes/Items/ItemsPageContext.swift | 32 +- .../App/Scenes/Lists/Controller+List.swift | 49 +- Sources/App/Scenes/Lists/ListContext.swift | 49 +- .../Scenes/Lists/ListController+Save.swift | 82 +- Sources/App/Scenes/Lists/ListController.swift | 188 +- .../App/Scenes/Lists/ListPageContext.swift | 53 +- .../App/Scenes/Lists/ListPageFormData.swift | 41 +- .../App/Scenes/Lists/ListsController.swift | 33 +- .../Scenes/Lists/ListsImportController.swift | 128 +- .../App/Scenes/Lists/ListsPageContext.swift | 40 +- .../Scenes/Public/LegalNoticeController.swift | 16 - .../Public/PrivacyPolicyController.swift | 20 - .../Reservations/Controller+Reservation.swift | 48 +- .../Reservations/ReservationContoller.swift | 199 - .../ReservationController+Authorization.swift | 50 - .../ReservationController+Save.swift | 70 - .../Reservations/ReservationController.swift | 132 + .../ReservationControllerForOwner.swift | 125 - .../ReservationCreateNotification.swift | 26 +- .../ReservationDeleteNotification.swift | 26 +- .../Reservations/ReservationPageContext.swift | 95 +- .../AuthenticationController.swift | 161 +- .../Authentication/AuthenticationError.swift | 15 +- .../Authentication/AuthenticationState.swift | 2 + .../AuthenticationUserInfo.swift | 51 +- .../GoogleAuthenticationController.swift | 18 +- .../GoogleAuthenticationUserInfo.swift | 2 + .../NetIDAuthenticationController.swift | 22 +- .../NetIDAuthenticationUserInfo.swift | 2 + .../Request+Authentication.swift | 236 - .../Request+AuthenticationState.swift | 83 + .../Request+AuthenticationToken.swift | 12 + .../User/Authentication/Session+User.swift | 15 + .../Scenes/User/Login/LoginController.swift | 4 +- .../Scenes/User/Login/LoginPageContext.swift | 2 + .../Scenes/User/Logout/LogoutController.swift | 6 +- .../Scenes/User/NotificationsController.swift | 51 + .../User/NotificationsPageContext.swift | 43 + .../Scenes/User/ProfileController+Save.swift | 74 +- .../App/Scenes/User/ProfileController.swift | 72 +- .../App/Scenes/User/ProfilePageContext.swift | 22 +- .../App/Scenes/User/ProfilePageFormData.swift | 14 +- .../Scenes/User/SettingsController+Save.swift | 82 +- .../App/Scenes/User/SettingsController.swift | 71 +- .../SettingsNotificationsNotification.swift | 18 - .../SettingsNotificationsPageContext.swift | 53 - .../App/Scenes/User/SettingsPageContext.swift | 8 +- .../Scenes/User/SettingsPageFormData.swift | 16 +- .../App/Scenes/User/TestNotification.swift | 39 + .../App/Scenes/User/UserNotification.swift | 40 +- .../Scenes/User/UserNotificationManager.swift | 64 - Sources/App/Scenes/WelcomeController.swift | 54 +- Sources/App/Scenes/WelcomePageContext.swift | 40 +- Sources/App/Scenes/WishlistController.swift | 273 +- Sources/App/Scenes/WishlistPageContext.swift | 39 +- .../Future+Authorization.swift | 2 + .../ProtectedController+Authorization.swift | 31 - .../Request+Authorization.swift | 35 - .../ProtectedController+Identification.swift | 10 +- .../Request+Identification.swift | 33 +- Sources/App/Scenes/[Traits]/Sorting.swift | 2 + Sources/App/Site.swift | 2 +- Sources/App/SiteAccess.swift | 2 +- Sources/App/SiteRelease.swift | 2 +- .../App/[Framework]/Authenticatable+.swift | 21 + .../AuthenticatableController.swift | 34 + Sources/App/[Framework]/Controller.swift | 60 +- Sources/App/[Framework]/Future+.swift | 27 - Sources/App/[Framework]/Future+Error.swift | 20 + Sources/App/[Framework]/Future+Model.swift | 12 - Sources/App/[Framework]/Future+Values.swift | 51 +- Sources/App/[Framework]/Message.swift | 12 +- Sources/App/[Framework]/Outcome.swift | 8 +- .../App/[Framework]/ProtectedController.swift | 31 - .../App/[Framework]/SendMessageResult.swift | 2 +- Sources/App/[Framework]/Types/ID.swift | 27 +- Sources/App/[Library]/Character.swift | 7 - Sources/App/[Library]/Operators.swift | 6 + Sources/App/[Library]/Result.swift | 6 - .../Middlewares/ImageFileMiddleware.swift | 47 +- .../SecurityHeadersConfiguration.swift | 50 + .../SecurityHeadersMiddleware.swift | 10 +- .../Dispatching/DispatchingQueue.swift | 2 + .../[Server]/Providers/Dispatching/Job.swift | 6 +- .../Providers/Logging/BasicLogger.swift | 7 +- .../Providers/Logging/ConsoleLogger.swift | 21 +- .../Providers/Logging/StandardLogger.swift | 1 + .../Providers/Messaging/PushoverUser.swift | 2 +- .../[Server]/Services/RequestLanguage.swift | 2 + .../Services/RequestLanguageService.swift | 4 +- Sources/App/boot.swift | 2 +- Sources/App/configure.swift | 87 +- Sources/App/databases-migrations.swift | 216 +- Sources/App/databases.swift | 2 +- Sources/App/middlewares.swift | 2 +- Sources/App/routes.swift | 99 +- .../Announcements/AnnouncementsActor.swift | 53 + .../Announcements/PresentPublicly.swift | 59 + .../Enrollment/EnrollmentActor.swift | 87 + .../Enrollment/MaterialiseUser.swift | 247 + .../UserFavorites/CreateFavorite.swift | 75 + .../UserFavorites/DeleteFavorite.swift | 73 + .../UserFavorites/GetFavorites.swift | 71 + .../RequestFavoriteCreation.swift | 66 + .../RequestFavoriteDeletion.swift | 66 + .../UserFavorites/UserFavoritesActor.swift | 82 + .../UserInvitations/CreateInvitation.swift | 145 + .../UserInvitations/GetInvitations.swift | 65 + .../RequestInvitationCreation.swift | 55 + .../RequestInvitationRevocation.swift | 76 + .../UserInvitations/RevokeInvitation.swift | 75 + .../UserInvitations/SendInvitationEmail.swift | 120 + .../UserInvitationsActor+.swift | 1 + .../UserInvitationsActor.swift | 215 + .../UserItems/CreateItem.swift | 165 + .../UserItems/CreateOrUpdateItem.swift | 89 + .../UserItems/DeleteItem.swift | 83 + .../UserItems/GetItems.swift | 86 + .../UserItems/RequestItemDeletion.swift | 68 + .../UserItems/RequestItemEditing.swift | 81 + .../UserItems/UpdateItem.swift | 168 + .../UserItems/UserItemsActor.swift | 88 + .../UserLists/CreateList.swift | 128 + .../UserLists/CreateOrUpdateList.swift | 81 + .../UserLists/DeleteList.swift | 81 + .../UserLists/ExportListToJSON.swift | 147 + .../UserLists/GetLists.swift | 70 + .../UserLists/ImportListFromJSON.swift | 186 + .../UserLists/RequestListDeletion.swift | 58 + .../UserLists/RequestListEditing.swift | 69 + .../UserLists/RequestListImportFromJSON.swift | 53 + .../UserLists/UpdateList.swift | 150 + .../UserLists/UserListsActor.swift | 112 + .../UserNotifications/TestNotifications.swift | 72 + .../UserNotificationsActor.swift | 39 + .../UserNotificationsResult.swift | 7 + .../GetProfileAndInvitations.swift | 70 + .../UserProfile/RequestProfileEditing.swift | 54 + .../UserProfile/UpdateProfile.swift | 115 + .../UserProfile/UserProfileActor.swift | 59 + .../UserReservations/DeleteReservation.swift | 90 + .../RequestReservationDeletion.swift | 91 + .../UserReservationsActor.swift | 49 + .../UserSettings/RequestSettingsEditing.swift | 52 + .../UserSettings/UpdateSettings.swift | 115 + .../UserSettings/UserSettingsActor.swift | 47 + .../UserWelcome/GetListsAndFavorites.swift | 84 + .../UserWelcome/UserWelcomeActor.swift | 42 + .../Wishlist/AddReservationToItem.swift | 148 + .../Wishlist/PresentReservation.swift | 91 + .../Wishlist/PresentWishlist.swift | 95 + .../Wishlist/RemoveReservationFromItem.swift | 139 + .../Wishlist/WishlistActor.swift | 74 + .../Wishlist/WishlistSpecification.swift | 33 + .../Actors and Actions/[Action]/Action.swift | 3 + .../[Action]/ActionBoundaries.swift | 8 + .../[Action]/ActionError.swift | 3 + .../[Action]/ActionResult.swift | 3 + .../[Action]/ActionSpecification.swift | 3 + .../FavoriteRepresentation.swift | 13 + .../FavoriteRepresentationsBuilder.swift | 98 + .../InvitationRepresentation.swift | 34 + .../InvitationRepresentationsBuilder.swift} | 24 +- .../ItemRepresentation.swift | 55 + .../ItemRepresentationsBuilder.swift} | 28 +- .../ListRepresentation.swift | 48 + .../ListRepresentationsBuilder.swift} | 35 +- .../ReservationRepresentation.swift | 26 + .../UserRepresentation.swift | 66 + .../Model}/Authorization.swift | 4 +- .../Model}/AuthorizationError.swift | 13 +- Sources/Domain/Model/EmailSpecification.swift | 18 + Sources/{App => }/Domain/Model/Entity.md | 0 Sources/Domain/Model/Favorite.swift | 56 + Sources/Domain/Model/FavoriteID.swift | 11 + .../Domain/Model/FavoriteRepository.swift | 11 +- Sources/Domain/Model/Identification.swift | 17 + Sources/Domain/Model/Identifier.swift | 122 + .../Model/Invitation+Authorization.swift | 28 + Sources/Domain/Model/Invitation.swift | 130 + .../Domain/Model/InvitationCode.swift | 55 +- .../Domain/Model/InvitationError.swift | 0 Sources/Domain/Model/InvitationID.swift | 11 + .../Domain/Model/InvitationRepository.swift | 11 +- Sources/Domain/Model/InvitationValues.swift | 97 + Sources/Domain/Model/Item.swift | 156 + Sources/Domain/Model/ItemID.swift | 11 + .../Domain/Model/ItemRepository.swift | 20 +- .../Model/ItemValues.swift} | 126 +- Sources/Domain/Model/List+Authorization.swift | 29 + Sources/Domain/Model/List.swift | 102 + Sources/Domain/Model/ListID.swift | 11 + .../Domain/Model/ListRepository.swift | 19 +- .../Model/ListValues.swift} | 96 +- Sources/Domain/Model/PushoverKey.swift | 15 + Sources/Domain/Model/Reservation.swift | 69 + Sources/Domain/Model/ReservationID.swift | 11 + .../Domain/Model/ReservationRepository.swift | 20 + Sources/Domain/Model/User+Authorization.swift | 29 + Sources/Domain/Model/User.swift | 106 + Sources/Domain/Model/UserID.swift | 11 + Sources/Domain/Model/UserIdentity.swift | 16 + .../Domain/Model/UserIdentityProvider.swift | 16 + Sources/Domain/Model/UserRepository.swift | 21 + Sources/Domain/Model/UserSettings.swift | 63 + Sources/Domain/Model/UserValues.swift | 132 + Sources/Domain/Model/Visibility.swift | 40 + Sources/Domain/Model/[Entity]/Entity.swift | 29 + .../Model/[Entity]/EntityDetachable.swift | 28 + .../Domain/Model/[Entity]/EntityError.swift | 53 + .../Domain/Model/[Entity]/EntityKeyPath.swift | 36 + .../Model/[Entity]/EntityProperties.swift | 44 + .../Model/[Entity]/EntityReflectable.swift | 11 + .../Model/[Entity]/EntityRepository.swift | 12 + .../Domain/Model/[Entity]/EntitySorting.swift | 164 + .../[Traits]/Confidential+Authorization.swift | 22 + .../Domain/Model/[Traits]/Confidential.swift | 2 + Sources/Domain/Model/[Traits]/Imagable.swift | 22 + .../[Traits]/Viewable+Authorization.swift | 45 + .../Domain/Model/[Traits]/Viewable.swift | 2 - .../Domain/Model/[Values]/PartialValues.swift | 178 + .../Model/[Values]/Types/StringValue.swift | 78 + .../[Values]/Validation/OptionalValue.swift | 36 + .../Validation/Validators/AndValidator.swift | 72 + .../Validators/CharacterSetValidator.swift | 40 + .../Validators/CountValidator.swift | 65 + .../EmailSpecificationValidator.swift | 35 + .../Validators/EmptyValidator.swift | 21 + .../Validators/NilIgnoringValidator.swift | 37 + .../Validation/Validators/NilValidator.swift | 26 + .../Validation/Validators/NotValidator.swift | 33 + .../Validation/Validators/OrValidator.swift | 56 + .../Validators/PushoverKeyValidator.swift | 32 + .../Validation/Validators/URLValidator.swift | 27 + .../Validation/ValueValidatable.swift | 15 + .../Validation/ValueValidationError.swift | 13 + .../Validation/ValueValidationErrorType.swift | 19 + .../Validation/ValueValidationErrors.swift | 20 + .../Validation/ValueValidations.swift | 76 + .../[Values]/Validation/ValueValidator.swift | 23 + .../Validation/ValueValidatorType.swift | 19 + Sources/Domain/Model/[Values]/Values.swift | 5 + .../Domain/Model/[Values]/ValuesError.swift | 25 + .../Providers/EmailSendingProvider.swift | 12 + .../Providers/EventRecordingProvider.swift | 92 + .../Domain/Providers/ImageStoreProvider.swift | 14 + .../Providers/MessageLoggingProvider.swift | 155 + .../NotificationSendingProvider.swift | 69 + Sources/Domain/README.md | 19 + .../Domain/Services/InvitationService.swift | 55 + Sources/Domain/Services/ItemService.swift | 50 + .../Domain/Services/ReservationService.swift | 42 + .../Services/UserNotificationService.swift | 18 + Sources/Domain/Services/UserService.swift | 58 + Sources/Domain/[Worker]/EventLoop+.swift | 23 + .../[Worker]/EventLoopFuture+Error.swift | 20 + .../[Worker]/EventLoopFuture+Flatten.swift | 14 + .../[Worker]/EventLoopFuture+FutureType.swift | 9 + .../Domain/[Worker]/EventLoopFuture+Map.swift | 48 + .../EventLoopFuture+OptionalType.swift | 22 + .../[Worker]/EventLoopFuture+Transform.swift | 22 + .../[Worker]/EventLoopFuture+Values.swift | 95 + Sources/Domain/[Worker]/FutureType.swift | 10 + Sources/Domain/[Worker]/OptionalType.swift | 38 + .../Heap.swift | 2 +- .../Operators.swift} | 5 - .../Standard Library Extensions/Array+.swift} | 0 .../Standard Library Extensions/String+.swift | 21 + .../String+Slugify.swift | 0 .../UUID+Base62.swift | 13 +- Tests/AppTests/AppTests.swift | 15 - Tests/AppTests/Application+Testable.swift | 16 + .../Domain/DomainModelInvitationTests.swift | 24 + .../Domain/DomainModelItemTests.swift | 24 + .../Domain/DomainModelListTests.swift | 24 + .../Domain/DomainModelReservationTests.swift | 24 + .../Domain/DomainModelUserTests.swift | 24 + .../HasEntityTestSupport.swift} | 19 +- Tests/AppTests/Model/InvitationTests.swift | 14 - Tests/AppTests/Model/ItemTests.swift | 14 - Tests/AppTests/Model/ListTests.swift | 14 - Tests/AppTests/Model/ReservationTests.swift | 14 - Tests/AppTests/Model/UserTests.swift | 14 - .../RequestLanguageServiceTests.swift | 31 +- Tests/AppTests/XCTestCase+.swift | 47 + Tests/AppTests/XCTestManifests.swift | 23 - .../Actors/AnnouncementsActorTests.swift | 70 + .../Actors/EnrollmentActorTests.swift | 203 + Tests/DomainTests/LinuxMain.swift | 4 + Tests/DomainTests/Model/InvitationTests.swift | 52 + Tests/DomainTests/Model/UserSupport.swift | 24 + Tests/DomainTests/XCTestCase+.swift | 47 + .../TestingInvitationRepository.swift | 74 + .../[Testing]/TestingLoggingProvider.swift | 30 + .../[Testing]/TestingRecodingProvider.swift | 15 + .../TestingReservationRepository.swift | 69 + .../[Testing]/TestingUserRepository.swift | 78 + .../TestingUserRepositoryTests.swift | 46 + .../Library => LibraryTests}/UUIDTests.swift | 18 +- Tests/LibraryTests/XCTestCase+.swift | 47 + Tests/LinuxMain.swift | 8 - Wishlist.xcodeproj/DomainTests_Info.plist | 25 + Wishlist.xcodeproj/Domain_Info.plist | 25 + .../CCryptoOpenSSL/module.modulemap | 2 +- .../CNIOOpenSSL/module.modulemap | 2 +- Wishlist.xcodeproj/LibraryTests_Info.plist | 25 + Wishlist.xcodeproj/Library_Info.plist | 25 + Wishlist.xcodeproj/project.pbxproj | 33132 ++++++++++------ .../xcshareddata/xcschemes/Run.xcscheme | 20 + .../xcschemes/Wishlist-Package.xcscheme | 54 +- update.rb | 17 + update.sh | 2 +- 446 files changed, 35453 insertions(+), 17599 deletions(-) create mode 100644 .dockerignore create mode 100755 Docker/build.test create mode 100644 Docker/docker-compose.test create mode 100644 Docker/docker.test create mode 100755 Docker/run.test create mode 100644 LinuxMain.swift create mode 100644 Resources/Views/User/Invitations.leaf delete mode 100644 Sources/App/Adapter/[MySQL]/MySQLModelRepository.swift create mode 100644 Sources/App/Domain/Adapters/Actors/AppAnnouncementsActor+Vapor.swift create mode 100644 Sources/App/Domain/Adapters/Actors/AppEnrollmentActor+Vapor.swift create mode 100644 Sources/App/Domain/Adapters/Actors/AppUserFavoritesActor+Vapor.swift create mode 100644 Sources/App/Domain/Adapters/Actors/AppUserInvitationsActor+Vapor.swift create mode 100644 Sources/App/Domain/Adapters/Actors/AppUserItemsActor+Vapor.swift create mode 100644 Sources/App/Domain/Adapters/Actors/AppUserListsActor+Vapor.swift create mode 100644 Sources/App/Domain/Adapters/Actors/AppUserNotificationsActor+Vapor.swift create mode 100644 Sources/App/Domain/Adapters/Actors/AppUserProfileActor+Vapor.swift create mode 100644 Sources/App/Domain/Adapters/Actors/AppUserReservationsActor+Vapor.swift create mode 100644 Sources/App/Domain/Adapters/Actors/AppUserSettimgsActor+Vapor.swift create mode 100644 Sources/App/Domain/Adapters/Actors/AppUserWelcomeActor+Vapor.swift create mode 100644 Sources/App/Domain/Adapters/Actors/AppWishlistActor+Vapor.swift create mode 100644 Sources/App/Domain/Adapters/Actors/README.md create mode 100644 Sources/App/Domain/Adapters/Model/EmailSpecification+Fluent.swift create mode 100644 Sources/App/Domain/Adapters/Model/Entity/Entity+Fluent.swift rename Sources/App/{Adapter/[MySQL]/EntitySorting+MySQL.swift => Domain/Adapters/Model/Entity/EntitySorting+Fluent.swift} (98%) rename Sources/App/{Adapter/[MySQL] => Domain/Adapters/Model}/Favorite+Fluent.swift (62%) create mode 100644 Sources/App/Domain/Adapters/Model/Identification+Fluent.swift rename Sources/App/{Adapter/[MySQL] => Domain/Adapters/Model}/Invitation+Fluent.swift (75%) rename Sources/App/{Adapter/[MySQL] => Domain/Adapters/Model}/InvitationCode+Fluent.swift (67%) rename Sources/App/{Adapter/[MySQL] => Domain/Adapters/Model}/Item+Fluent.swift (74%) rename Sources/App/{Adapter/[MySQL] => Domain/Adapters/Model}/List+Fluent.swift (72%) rename Sources/App/{Adapter/[MySQL] => Domain/Adapters/Model}/Reservation+Fluent.swift (68%) rename Sources/App/{Adapter/[MySQL] => Domain/Adapters/Model}/User+Fluent.swift (73%) create mode 100644 Sources/App/Domain/Adapters/Model/UserIdentifier+Fluent.swift create mode 100644 Sources/App/Domain/Adapters/Model/UserIdentityProvider+Fluent.swift rename Sources/App/{Adapter/[MySQL] => Domain/Adapters/Model}/UserSettings+Fluent.swift (78%) rename Sources/App/{Adapter/[MySQL] => Domain/Adapters/Model}/Visibility+Fluent.swift (59%) create mode 100644 Sources/App/Domain/Adapters/Repositories/MySQLFavoriteRepository+Vapor.swift rename Sources/App/{Adapter/Repository => Domain/Adapters/Repositories}/MySQLFavoriteRepository.swift (78%) create mode 100644 Sources/App/Domain/Adapters/Repositories/MySQLInvitationRepository+Vapor.swift rename Sources/App/{Adapter/Repository => Domain/Adapters/Repositories}/MySQLInvitationRepository.swift (79%) create mode 100644 Sources/App/Domain/Adapters/Repositories/MySQLItemRepository+Vapor.swift rename Sources/App/{Adapter/Repository => Domain/Adapters/Repositories}/MySQLItemRepository.swift (77%) create mode 100644 Sources/App/Domain/Adapters/Repositories/MySQLListRepository+Vapor.swift rename Sources/App/{Adapter/Repository => Domain/Adapters/Repositories}/MySQLListRepository.swift (75%) create mode 100644 Sources/App/Domain/Adapters/Repositories/MySQLModelRepository.swift create mode 100644 Sources/App/Domain/Adapters/Repositories/MySQLReservationRepository+Vapor.swift rename Sources/App/{Adapter/Repository => Domain/Adapters/Repositories}/MySQLReservationRepository.swift (61%) create mode 100644 Sources/App/Domain/Adapters/Repositories/MySQLUserRepository+Vapor.swift rename Sources/App/{Adapter/Repository => Domain/Adapters/Repositories}/MySQLUserRepository.swift (69%) create mode 100644 Sources/App/Domain/Mappers/Model/EmailSpecification+.swift create mode 100644 Sources/App/Domain/Mappers/Model/FavoriteID+.swift create mode 100644 Sources/App/Domain/Mappers/Model/Identification+.swift create mode 100644 Sources/App/Domain/Mappers/Model/InvitationID+.swift create mode 100644 Sources/App/Domain/Mappers/Model/ItemID+.swift create mode 100644 Sources/App/Domain/Mappers/Model/ListID+.swift create mode 100644 Sources/App/Domain/Mappers/Model/PushoverUser+.swift create mode 100644 Sources/App/Domain/Mappers/Model/README.md create mode 100644 Sources/App/Domain/Mappers/Model/ReservationID+.swift create mode 100644 Sources/App/Domain/Mappers/Model/UserID+.swift delete mode 100644 Sources/App/Domain/Model/Favorite.swift delete mode 100644 Sources/App/Domain/Model/Identification.swift delete mode 100644 Sources/App/Domain/Model/Invitation.swift delete mode 100644 Sources/App/Domain/Model/InvitationData.swift delete mode 100644 Sources/App/Domain/Model/Item.swift delete mode 100644 Sources/App/Domain/Model/List.swift delete mode 100644 Sources/App/Domain/Model/Reservation.swift delete mode 100644 Sources/App/Domain/Model/ReservationRepository.swift delete mode 100644 Sources/App/Domain/Model/User.swift delete mode 100644 Sources/App/Domain/Model/UserData.swift delete mode 100644 Sources/App/Domain/Model/UserRepository.swift delete mode 100644 Sources/App/Domain/Model/UserSettings.swift delete mode 100644 Sources/App/Domain/Model/Visibility.swift delete mode 100644 Sources/App/Domain/Model/[Entity]/Entity.swift delete mode 100644 Sources/App/Domain/Model/[Entity]/EntityError.swift delete mode 100644 Sources/App/Domain/Model/[Entity]/EntityReflectable.swift delete mode 100644 Sources/App/Domain/Model/[Entity]/EntityRepository.swift delete mode 100644 Sources/App/Domain/Model/[Entity]/EntitySorting.swift delete mode 100644 Sources/App/Domain/Model/[Traits]/Imagable.swift create mode 100644 Sources/App/Domain/Providers/VaporEmailSendingProvider.swift create mode 100644 Sources/App/Domain/Providers/VaporEventRecordingProvider.swift rename Sources/App/{[Framework]/Imagable+ImageFileMiddleware.swift => Domain/Providers/VaporImageStoreProvider.swift} (52%) create mode 100644 Sources/App/Domain/Providers/VaporMessageLoggingProvider.swift create mode 100644 Sources/App/Domain/Providers/VaporNotificationSendingProvider.swift create mode 100644 Sources/App/Domain/README.md create mode 100644 Sources/App/Scenes/Announcements/LegalNoticeController.swift rename Sources/App/Scenes/{Public => Announcements}/LegalNoticePageContext.swift (69%) create mode 100644 Sources/App/Scenes/Announcements/PrivacyPolicyController.swift rename Sources/App/Scenes/{Public => Announcements}/PrivacyPolicyPageContext.swift (69%) delete mode 100644 Sources/App/Scenes/Favorites/FavoriteContextsBuilder.swift create mode 100644 Sources/App/Scenes/Invitations/InvitationEmail.swift rename Sources/App/Scenes/Invitations/{InvitationMailContext.swift => InvitationEmailContext.swift} (58%) rename Sources/App/Scenes/Invitations/{InvitationMailError.swift => InvitationEmailError.swift} (60%) delete mode 100644 Sources/App/Scenes/Invitations/InvitationMail.swift create mode 100644 Sources/App/Scenes/Invitations/InvitationsPageContext.swift delete mode 100644 Sources/App/Scenes/Public/LegalNoticeController.swift delete mode 100644 Sources/App/Scenes/Public/PrivacyPolicyController.swift delete mode 100644 Sources/App/Scenes/Reservations/ReservationContoller.swift delete mode 100644 Sources/App/Scenes/Reservations/ReservationController+Authorization.swift delete mode 100644 Sources/App/Scenes/Reservations/ReservationController+Save.swift create mode 100644 Sources/App/Scenes/Reservations/ReservationController.swift delete mode 100644 Sources/App/Scenes/Reservations/ReservationControllerForOwner.swift delete mode 100644 Sources/App/Scenes/User/Authentication/Request+Authentication.swift create mode 100644 Sources/App/Scenes/User/Authentication/Request+AuthenticationState.swift create mode 100644 Sources/App/Scenes/User/Authentication/Request+AuthenticationToken.swift create mode 100644 Sources/App/Scenes/User/Authentication/Session+User.swift create mode 100644 Sources/App/Scenes/User/NotificationsController.swift create mode 100644 Sources/App/Scenes/User/NotificationsPageContext.swift delete mode 100644 Sources/App/Scenes/User/SettingsNotificationsNotification.swift delete mode 100644 Sources/App/Scenes/User/SettingsNotificationsPageContext.swift create mode 100644 Sources/App/Scenes/User/TestNotification.swift delete mode 100644 Sources/App/Scenes/User/UserNotificationManager.swift delete mode 100644 Sources/App/Scenes/[Authorization]/ProtectedController+Authorization.swift delete mode 100644 Sources/App/Scenes/[Authorization]/Request+Authorization.swift create mode 100644 Sources/App/[Framework]/Authenticatable+.swift create mode 100644 Sources/App/[Framework]/AuthenticatableController.swift delete mode 100644 Sources/App/[Framework]/Future+.swift create mode 100644 Sources/App/[Framework]/Future+Error.swift delete mode 100644 Sources/App/[Framework]/Future+Model.swift delete mode 100644 Sources/App/[Framework]/ProtectedController.swift delete mode 100644 Sources/App/[Library]/Character.swift create mode 100644 Sources/App/[Library]/Operators.swift delete mode 100644 Sources/App/[Library]/Result.swift create mode 100644 Sources/Domain/Actors and Actions/Announcements/AnnouncementsActor.swift create mode 100644 Sources/Domain/Actors and Actions/Announcements/PresentPublicly.swift create mode 100644 Sources/Domain/Actors and Actions/Enrollment/EnrollmentActor.swift create mode 100644 Sources/Domain/Actors and Actions/Enrollment/MaterialiseUser.swift create mode 100644 Sources/Domain/Actors and Actions/UserFavorites/CreateFavorite.swift create mode 100644 Sources/Domain/Actors and Actions/UserFavorites/DeleteFavorite.swift create mode 100644 Sources/Domain/Actors and Actions/UserFavorites/GetFavorites.swift create mode 100644 Sources/Domain/Actors and Actions/UserFavorites/RequestFavoriteCreation.swift create mode 100644 Sources/Domain/Actors and Actions/UserFavorites/RequestFavoriteDeletion.swift create mode 100644 Sources/Domain/Actors and Actions/UserFavorites/UserFavoritesActor.swift create mode 100644 Sources/Domain/Actors and Actions/UserInvitations/CreateInvitation.swift create mode 100644 Sources/Domain/Actors and Actions/UserInvitations/GetInvitations.swift create mode 100644 Sources/Domain/Actors and Actions/UserInvitations/RequestInvitationCreation.swift create mode 100644 Sources/Domain/Actors and Actions/UserInvitations/RequestInvitationRevocation.swift create mode 100644 Sources/Domain/Actors and Actions/UserInvitations/RevokeInvitation.swift create mode 100644 Sources/Domain/Actors and Actions/UserInvitations/SendInvitationEmail.swift create mode 100644 Sources/Domain/Actors and Actions/UserInvitations/UserInvitationsActor+.swift create mode 100644 Sources/Domain/Actors and Actions/UserInvitations/UserInvitationsActor.swift create mode 100644 Sources/Domain/Actors and Actions/UserItems/CreateItem.swift create mode 100644 Sources/Domain/Actors and Actions/UserItems/CreateOrUpdateItem.swift create mode 100644 Sources/Domain/Actors and Actions/UserItems/DeleteItem.swift create mode 100644 Sources/Domain/Actors and Actions/UserItems/GetItems.swift create mode 100644 Sources/Domain/Actors and Actions/UserItems/RequestItemDeletion.swift create mode 100644 Sources/Domain/Actors and Actions/UserItems/RequestItemEditing.swift create mode 100644 Sources/Domain/Actors and Actions/UserItems/UpdateItem.swift create mode 100644 Sources/Domain/Actors and Actions/UserItems/UserItemsActor.swift create mode 100644 Sources/Domain/Actors and Actions/UserLists/CreateList.swift create mode 100644 Sources/Domain/Actors and Actions/UserLists/CreateOrUpdateList.swift create mode 100644 Sources/Domain/Actors and Actions/UserLists/DeleteList.swift create mode 100644 Sources/Domain/Actors and Actions/UserLists/ExportListToJSON.swift create mode 100644 Sources/Domain/Actors and Actions/UserLists/GetLists.swift create mode 100644 Sources/Domain/Actors and Actions/UserLists/ImportListFromJSON.swift create mode 100644 Sources/Domain/Actors and Actions/UserLists/RequestListDeletion.swift create mode 100644 Sources/Domain/Actors and Actions/UserLists/RequestListEditing.swift create mode 100644 Sources/Domain/Actors and Actions/UserLists/RequestListImportFromJSON.swift create mode 100644 Sources/Domain/Actors and Actions/UserLists/UpdateList.swift create mode 100644 Sources/Domain/Actors and Actions/UserLists/UserListsActor.swift create mode 100644 Sources/Domain/Actors and Actions/UserNotifications/TestNotifications.swift create mode 100644 Sources/Domain/Actors and Actions/UserNotifications/UserNotificationsActor.swift create mode 100644 Sources/Domain/Actors and Actions/UserNotifications/UserNotificationsResult.swift create mode 100644 Sources/Domain/Actors and Actions/UserProfile/GetProfileAndInvitations.swift create mode 100644 Sources/Domain/Actors and Actions/UserProfile/RequestProfileEditing.swift create mode 100644 Sources/Domain/Actors and Actions/UserProfile/UpdateProfile.swift create mode 100644 Sources/Domain/Actors and Actions/UserProfile/UserProfileActor.swift create mode 100644 Sources/Domain/Actors and Actions/UserReservations/DeleteReservation.swift create mode 100644 Sources/Domain/Actors and Actions/UserReservations/RequestReservationDeletion.swift create mode 100644 Sources/Domain/Actors and Actions/UserReservations/UserReservationsActor.swift create mode 100644 Sources/Domain/Actors and Actions/UserSettings/RequestSettingsEditing.swift create mode 100644 Sources/Domain/Actors and Actions/UserSettings/UpdateSettings.swift create mode 100644 Sources/Domain/Actors and Actions/UserSettings/UserSettingsActor.swift create mode 100644 Sources/Domain/Actors and Actions/UserWelcome/GetListsAndFavorites.swift create mode 100644 Sources/Domain/Actors and Actions/UserWelcome/UserWelcomeActor.swift create mode 100644 Sources/Domain/Actors and Actions/Wishlist/AddReservationToItem.swift create mode 100644 Sources/Domain/Actors and Actions/Wishlist/PresentReservation.swift create mode 100644 Sources/Domain/Actors and Actions/Wishlist/PresentWishlist.swift create mode 100644 Sources/Domain/Actors and Actions/Wishlist/RemoveReservationFromItem.swift create mode 100644 Sources/Domain/Actors and Actions/Wishlist/WishlistActor.swift create mode 100644 Sources/Domain/Actors and Actions/Wishlist/WishlistSpecification.swift create mode 100644 Sources/Domain/Actors and Actions/[Action]/Action.swift create mode 100644 Sources/Domain/Actors and Actions/[Action]/ActionBoundaries.swift create mode 100644 Sources/Domain/Actors and Actions/[Action]/ActionError.swift create mode 100644 Sources/Domain/Actors and Actions/[Action]/ActionResult.swift create mode 100644 Sources/Domain/Actors and Actions/[Action]/ActionSpecification.swift create mode 100644 Sources/Domain/Model Representation/FavoriteRepresentation.swift create mode 100644 Sources/Domain/Model Representation/FavoriteRepresentationsBuilder.swift create mode 100644 Sources/Domain/Model Representation/InvitationRepresentation.swift rename Sources/{App/Scenes/Invitations/InvitationContextsBuilder.swift => Domain/Model Representation/InvitationRepresentationsBuilder.swift} (52%) create mode 100644 Sources/Domain/Model Representation/ItemRepresentation.swift rename Sources/{App/Scenes/Items/ItemContextsBuilder.swift => Domain/Model Representation/ItemRepresentationsBuilder.swift} (70%) create mode 100644 Sources/Domain/Model Representation/ListRepresentation.swift rename Sources/{App/Scenes/Lists/ListContextsBuilder.swift => Domain/Model Representation/ListRepresentationsBuilder.swift} (73%) create mode 100644 Sources/Domain/Model Representation/ReservationRepresentation.swift create mode 100644 Sources/Domain/Model Representation/UserRepresentation.swift rename Sources/{App/Scenes/[Authorization] => Domain/Model}/Authorization.swift (65%) rename Sources/{App/Scenes/[Authorization] => Domain/Model}/AuthorizationError.swift (66%) create mode 100644 Sources/Domain/Model/EmailSpecification.swift rename Sources/{App => }/Domain/Model/Entity.md (100%) create mode 100644 Sources/Domain/Model/Favorite.swift create mode 100644 Sources/Domain/Model/FavoriteID.swift rename Sources/{App => }/Domain/Model/FavoriteRepository.swift (79%) create mode 100644 Sources/Domain/Model/Identification.swift create mode 100644 Sources/Domain/Model/Identifier.swift create mode 100644 Sources/Domain/Model/Invitation+Authorization.swift create mode 100644 Sources/Domain/Model/Invitation.swift rename Sources/{App => }/Domain/Model/InvitationCode.swift (57%) rename Sources/{App => }/Domain/Model/InvitationError.swift (100%) create mode 100644 Sources/Domain/Model/InvitationID.swift rename Sources/{App => }/Domain/Model/InvitationRepository.swift (66%) create mode 100644 Sources/Domain/Model/InvitationValues.swift create mode 100644 Sources/Domain/Model/Item.swift create mode 100644 Sources/Domain/Model/ItemID.swift rename Sources/{App => }/Domain/Model/ItemRepository.swift (50%) rename Sources/{App/Domain/Model/ItemData.swift => Domain/Model/ItemValues.swift} (52%) create mode 100644 Sources/Domain/Model/List+Authorization.swift create mode 100644 Sources/Domain/Model/List.swift create mode 100644 Sources/Domain/Model/ListID.swift rename Sources/{App => }/Domain/Model/ListRepository.swift (58%) rename Sources/{App/Domain/Model/ListData.swift => Domain/Model/ListValues.swift} (55%) create mode 100644 Sources/Domain/Model/PushoverKey.swift create mode 100644 Sources/Domain/Model/Reservation.swift create mode 100644 Sources/Domain/Model/ReservationID.swift create mode 100644 Sources/Domain/Model/ReservationRepository.swift create mode 100644 Sources/Domain/Model/User+Authorization.swift create mode 100644 Sources/Domain/Model/User.swift create mode 100644 Sources/Domain/Model/UserID.swift create mode 100644 Sources/Domain/Model/UserIdentity.swift create mode 100644 Sources/Domain/Model/UserIdentityProvider.swift create mode 100644 Sources/Domain/Model/UserRepository.swift create mode 100644 Sources/Domain/Model/UserSettings.swift create mode 100644 Sources/Domain/Model/UserValues.swift create mode 100644 Sources/Domain/Model/Visibility.swift create mode 100644 Sources/Domain/Model/[Entity]/Entity.swift create mode 100644 Sources/Domain/Model/[Entity]/EntityDetachable.swift create mode 100644 Sources/Domain/Model/[Entity]/EntityError.swift create mode 100644 Sources/Domain/Model/[Entity]/EntityKeyPath.swift create mode 100644 Sources/Domain/Model/[Entity]/EntityProperties.swift create mode 100644 Sources/Domain/Model/[Entity]/EntityReflectable.swift create mode 100644 Sources/Domain/Model/[Entity]/EntityRepository.swift create mode 100644 Sources/Domain/Model/[Entity]/EntitySorting.swift create mode 100644 Sources/Domain/Model/[Traits]/Confidential+Authorization.swift create mode 100644 Sources/Domain/Model/[Traits]/Confidential.swift create mode 100644 Sources/Domain/Model/[Traits]/Imagable.swift create mode 100644 Sources/Domain/Model/[Traits]/Viewable+Authorization.swift rename Sources/{App => }/Domain/Model/[Traits]/Viewable.swift (81%) create mode 100644 Sources/Domain/Model/[Values]/PartialValues.swift create mode 100644 Sources/Domain/Model/[Values]/Types/StringValue.swift create mode 100644 Sources/Domain/Model/[Values]/Validation/OptionalValue.swift create mode 100644 Sources/Domain/Model/[Values]/Validation/Validators/AndValidator.swift create mode 100644 Sources/Domain/Model/[Values]/Validation/Validators/CharacterSetValidator.swift create mode 100644 Sources/Domain/Model/[Values]/Validation/Validators/CountValidator.swift create mode 100644 Sources/Domain/Model/[Values]/Validation/Validators/EmailSpecificationValidator.swift create mode 100644 Sources/Domain/Model/[Values]/Validation/Validators/EmptyValidator.swift create mode 100644 Sources/Domain/Model/[Values]/Validation/Validators/NilIgnoringValidator.swift create mode 100644 Sources/Domain/Model/[Values]/Validation/Validators/NilValidator.swift create mode 100644 Sources/Domain/Model/[Values]/Validation/Validators/NotValidator.swift create mode 100644 Sources/Domain/Model/[Values]/Validation/Validators/OrValidator.swift create mode 100644 Sources/Domain/Model/[Values]/Validation/Validators/PushoverKeyValidator.swift create mode 100644 Sources/Domain/Model/[Values]/Validation/Validators/URLValidator.swift create mode 100644 Sources/Domain/Model/[Values]/Validation/ValueValidatable.swift create mode 100644 Sources/Domain/Model/[Values]/Validation/ValueValidationError.swift create mode 100644 Sources/Domain/Model/[Values]/Validation/ValueValidationErrorType.swift create mode 100644 Sources/Domain/Model/[Values]/Validation/ValueValidationErrors.swift create mode 100644 Sources/Domain/Model/[Values]/Validation/ValueValidations.swift create mode 100644 Sources/Domain/Model/[Values]/Validation/ValueValidator.swift create mode 100644 Sources/Domain/Model/[Values]/Validation/ValueValidatorType.swift create mode 100644 Sources/Domain/Model/[Values]/Values.swift create mode 100644 Sources/Domain/Model/[Values]/ValuesError.swift create mode 100644 Sources/Domain/Providers/EmailSendingProvider.swift create mode 100644 Sources/Domain/Providers/EventRecordingProvider.swift create mode 100644 Sources/Domain/Providers/ImageStoreProvider.swift create mode 100644 Sources/Domain/Providers/MessageLoggingProvider.swift create mode 100644 Sources/Domain/Providers/NotificationSendingProvider.swift create mode 100644 Sources/Domain/README.md create mode 100644 Sources/Domain/Services/InvitationService.swift create mode 100644 Sources/Domain/Services/ItemService.swift create mode 100644 Sources/Domain/Services/ReservationService.swift create mode 100644 Sources/Domain/Services/UserNotificationService.swift create mode 100644 Sources/Domain/Services/UserService.swift create mode 100644 Sources/Domain/[Worker]/EventLoop+.swift create mode 100644 Sources/Domain/[Worker]/EventLoopFuture+Error.swift create mode 100644 Sources/Domain/[Worker]/EventLoopFuture+Flatten.swift create mode 100644 Sources/Domain/[Worker]/EventLoopFuture+FutureType.swift create mode 100644 Sources/Domain/[Worker]/EventLoopFuture+Map.swift create mode 100644 Sources/Domain/[Worker]/EventLoopFuture+OptionalType.swift create mode 100644 Sources/Domain/[Worker]/EventLoopFuture+Transform.swift create mode 100644 Sources/Domain/[Worker]/EventLoopFuture+Values.swift create mode 100644 Sources/Domain/[Worker]/FutureType.swift create mode 100644 Sources/Domain/[Worker]/OptionalType.swift rename Sources/{App/[Library] => Library/Algorithms and Data Structures}/Heap.swift (99%) rename Sources/{App/[Library]/operators.swift => Library/Operators.swift} (74%) rename Sources/{App/[Library]/Array.swift => Library/Standard Library Extensions/Array+.swift} (100%) create mode 100644 Sources/Library/Standard Library Extensions/String+.swift rename Sources/{App/[Library] => Library/Standard Library Extensions}/String+Slugify.swift (100%) rename Sources/{App/[Library] => Library/Standard Library Extensions}/UUID+Base62.swift (93%) delete mode 100755 Tests/AppTests/AppTests.swift create mode 100644 Tests/AppTests/Application+Testable.swift create mode 100644 Tests/AppTests/Domain/DomainModelInvitationTests.swift create mode 100644 Tests/AppTests/Domain/DomainModelItemTests.swift create mode 100644 Tests/AppTests/Domain/DomainModelListTests.swift create mode 100644 Tests/AppTests/Domain/DomainModelReservationTests.swift create mode 100644 Tests/AppTests/Domain/DomainModelUserTests.swift rename Tests/AppTests/{Model/EntityTestsSupport.swift => Domain/HasEntityTestSupport.swift} (65%) delete mode 100644 Tests/AppTests/Model/InvitationTests.swift delete mode 100644 Tests/AppTests/Model/ItemTests.swift delete mode 100644 Tests/AppTests/Model/ListTests.swift delete mode 100644 Tests/AppTests/Model/ReservationTests.swift delete mode 100644 Tests/AppTests/Model/UserTests.swift create mode 100644 Tests/AppTests/XCTestCase+.swift delete mode 100644 Tests/AppTests/XCTestManifests.swift create mode 100644 Tests/DomainTests/Actors/AnnouncementsActorTests.swift create mode 100644 Tests/DomainTests/Actors/EnrollmentActorTests.swift create mode 100644 Tests/DomainTests/LinuxMain.swift create mode 100644 Tests/DomainTests/Model/InvitationTests.swift create mode 100644 Tests/DomainTests/Model/UserSupport.swift create mode 100644 Tests/DomainTests/XCTestCase+.swift create mode 100644 Tests/DomainTests/[Testing]/TestingInvitationRepository.swift create mode 100644 Tests/DomainTests/[Testing]/TestingLoggingProvider.swift create mode 100644 Tests/DomainTests/[Testing]/TestingRecodingProvider.swift create mode 100644 Tests/DomainTests/[Testing]/TestingReservationRepository.swift create mode 100644 Tests/DomainTests/[Testing]/TestingUserRepository.swift create mode 100644 Tests/DomainTests/[Testing]/TestingUserRepositoryTests.swift rename Tests/{AppTests/Library => LibraryTests}/UUIDTests.swift (77%) create mode 100644 Tests/LibraryTests/XCTestCase+.swift delete mode 100644 Tests/LinuxMain.swift create mode 100644 Wishlist.xcodeproj/DomainTests_Info.plist create mode 100644 Wishlist.xcodeproj/Domain_Info.plist create mode 100644 Wishlist.xcodeproj/LibraryTests_Info.plist create mode 100644 Wishlist.xcodeproj/Library_Info.plist diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..0f19457 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,9 @@ +.env* +Wishlist.xcodeproj +update.rb +update.sh +~nginx +.DS_Store +.dockerignore +.git +.gitignore diff --git a/.swiftlint.yml b/.swiftlint.yml index 383e1c9..d4e9662 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -1,8 +1,18 @@ line_length: 100 function_parameter_count: 6 +large_tuple: + warning: 3 + error: 3 +type_name: + min_length: + error: 4 + max_length: + warning: 48 + error: 60 cyclomatic_complexity: ignores_case_statements: true disabled_rules: + - static_operator - opening_brace - statement_position opt_in_rules: @@ -67,9 +77,12 @@ type_name: identifier_name: min_length: error: 4 + max_length: + error: 48 excluded: - id - uid + - lid - key - db - row @@ -89,5 +102,8 @@ identifier_name: - rhs - cwd - awd - + - map + - min + - max + - nil diff --git a/CHANGELOG.md b/CHANGELOG.md index fc3c2ea..7ac534c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,9 +9,26 @@ ## [..] 🎁 Add functionality to move items between wishlists + +## [1.4.0] 🎁 Add invitation emails ☘ fix link to fav icon ☘ fix deletion of reservation for list owner +🛠️ Major refactoring to implement separation of concerns + +``` +CLOC before refactoring: + +Language files blank comment code +------------------------------------------------------------------------------- +Swift 226 2684 1523 10227 + +CLOC after refactoring: + +Language files blank comment code +------------------------------------------------------------------------------- +Swift 383 4390 2500 16025 +``` ## [1.3.3] ☘ fix link to about page diff --git a/Docker/build b/Docker/build index 7c05602..1084cef 100755 --- a/Docker/build +++ b/Docker/build @@ -4,7 +4,7 @@ version=$1 if [[ ! -d "./Wishlist.xcodeproj" ]] ; then echo "Script must be run in project directory"; exit 1 fi -if [[ ! $version =~ ^[0-9]+(\.[0-9]+){2,2}$ ]]; +if [[ ! $version =~ ^[0-9]+(\.[0-9]+){2,2}(\-RC[0-9]+){0,1}$ ]]; then echo "Script must be run with version number as argument"; exit 1 fi diff --git a/Docker/build.test b/Docker/build.test new file mode 100755 index 0000000..920d656 --- /dev/null +++ b/Docker/build.test @@ -0,0 +1,15 @@ +#!/bin/bash +version=$1 + +if [[ ! -d "./Wishlist.xcodeproj" ]] ; then + echo "Script must be run in project directory"; exit 1 +fi +if [[ ! $version =~ ^[0-9]+(\.[0-9]+){2,2}(\-RC[0-9]+){0,1}$ ]]; +then + echo "Script must be run with version number as argument"; exit 1 +fi + +echo " #### Building Docker image for project Wishlist with version $version" + +docker build -f Docker/docker.test -t "wishlist:$version-test" . + diff --git a/Docker/docker b/Docker/docker index ee3ae87..5473774 100644 --- a/Docker/docker +++ b/Docker/docker @@ -1,15 +1,15 @@ -FROM swift:4.2.4 AS builder +FROM swift:5.1.3 AS builder RUN apt-get -qq update && apt-get -q -y install \ openssl libssl-dev zlib1g-dev tzdata \ && rm -r /var/lib/apt/lists/* WORKDIR /app COPY . . -RUN mkdir -p /build/lib && cp -R /usr/lib/swift/linux/*.so /build/lib +RUN mkdir -p /build/lib && cp -R /usr/lib/swift/linux/lib* /build/lib RUN swift build -Xswiftc -suppress-warnings --product "Wishlist" -c release && mv `swift build -c release --show-bin-path` /build/bin -FROM ubuntu:16.04 +FROM ubuntu:18.04 RUN apt-get -qq update && apt-get install -y \ - libicu55 libxml2 libbsd0 libcurl3 libatomic1 openssl libssl-dev \ + libicu60 libxml2 libbsd0 libcurl4 libatomic1 openssl libssl-dev \ tzdata \ && rm -r /var/lib/apt/lists/* WORKDIR /app @@ -17,6 +17,8 @@ COPY --from=builder /build/bin/Wishlist . COPY --from=builder /build/lib/* /usr/lib/ COPY --from=builder /app/Public ./Public COPY --from=builder /app/Resources ./Resources -EXPOSE 12345 +RUN groupadd -g 999 appuser && useradd -r -u 999 -g appuser appuser +RUN mkdir ./Public-Images && chown -R appuser:appuser ./Public && chown -R appuser:appuser ./Public-Images && chown -R appuser:appuser ./Resources +USER appuser ENTRYPOINT ["./Wishlist", "serve", "--hostname", "0.0.0.0", "--port", "12345"] - +EXPOSE 12345 diff --git a/Docker/docker-compose b/Docker/docker-compose index a2959b1..4369280 100644 --- a/Docker/docker-compose +++ b/Docker/docker-compose @@ -29,8 +29,17 @@ services: DBPASSWORD: "${DBPASSWORD}" GOOGLE_CLIENT_ID: "${GOOGLE_CLIENT_ID}" GOOGLE_CLIENT_SECRET: "${GOOGLE_CLIENT_SECRET}" + NETID_CLIENT_ID: "${NETID_CLIENT_ID}" + NETID_CLIENT_SECRET: "${NETID_CLIENT_SECRET}" CLOUDIMG_TOKEN: "${CLOUDIMG_TOKEN}" + EMAIL_SMTP_HOSTNAME: "${EMAIL_SMTP_HOSTNAME}" + EMAIL_SMTP_USERNAME: "${EMAIL_SMTP_USERNAME}" + EMAIL_SMTP_PASSWORD: "${EMAIL_SMTP_PASSWORD}" + EMAIL_SENDER_ADDRESS: "${EMAIL_SENDER_ADDRESS}" + EMAIL_SENDER_NAME: "${EMAIL_SENDER_NAME}" PUSHOVER_APPLICATION_TOKEN: "${PUSHOVER_APPLICATION_TOKEN}" + DEVELOPMENT_LOG_LEVEL: "${DEVELOPMENT_LOG_LEVEL}" + RELEASE_LOG_LEVEL: "${RELEASE_LOG_LEVEL}" volumes: - public:/app/Public - public-images:/app/Public-Images diff --git a/Docker/docker-compose.test b/Docker/docker-compose.test new file mode 100644 index 0000000..96a7570 --- /dev/null +++ b/Docker/docker-compose.test @@ -0,0 +1,45 @@ +version: "3.3" +services: + app-test: + container_name: wishlist-app-test + image: "wishlist:${APPVERSION}-test" + environment: + SITE_URL: "http://localhost:8080" + SITE_RELEASE: "beta" + SITE_ACCESS: "all" + DBHOST: "${DBHOST}" + DBPORT: "${DBPORT}" + DBNAME: "${DBNAME}" + DBUSERNAME: "${DBUSERNAME}" + DBPASSWORD: "${DBPASSWORD}" + GOOGLE_CLIENT_ID: "${GOOGLE_CLIENT_ID}" + GOOGLE_CLIENT_SECRET: "${GOOGLE_CLIENT_SECRET}" + NETID_CLIENT_ID: "${NETID_CLIENT_ID}" + NETID_CLIENT_SECRET: "${NETID_CLIENT_SECRET}" + CLOUDIMG_TOKEN: "${CLOUDIMG_TOKEN}" + EMAIL_SMTP_HOSTNAME: "${EMAIL_SMTP_HOSTNAME}" + EMAIL_SMTP_USERNAME: "${EMAIL_SMTP_USERNAME}" + EMAIL_SMTP_PASSWORD: "${EMAIL_SMTP_PASSWORD}" + EMAIL_SENDER_ADDRESS: "${EMAIL_SENDER_ADDRESS}" + EMAIL_SENDER_NAME: "${EMAIL_SENDER_NAME}" + PUSHOVER_APPLICATION_TOKEN: "${PUSHOVER_APPLICATION_TOKEN}" + DEVELOPMENT_LOG_LEVEL: "${DEVELOPMENT_LOG_LEVEL}" + RELEASE_LOG_LEVEL: "${RELEASE_LOG_LEVEL}" + links: + - db-test + depends_on: + - db-test + networks: + - web-test + db-test: + container_name: wishlist-db-test + image: mysql:5 + environment: + MYSQL_ROOT_PASSWORD: wishlist + MYSQL_USER: wishlist + MYSQL_PASSWORD: wishlist + MYSQL_DATABASE: wishlist + networks: + - web-test +networks: + web-test: diff --git a/Docker/docker.test b/Docker/docker.test new file mode 100644 index 0000000..40cad12 --- /dev/null +++ b/Docker/docker.test @@ -0,0 +1,10 @@ +FROM swift:5.1.3 +RUN apt-get -qq update && apt-get -q -y install \ + openssl libssl-dev zlib1g-dev tzdata \ + && rm -r /var/lib/apt/lists/* +WORKDIR /app +COPY . . +RUN mkdir -p /build/lib && cp -R /usr/lib/swift/linux/lib* /build/lib +RUN swift package resolve +RUN swift package clean +CMD ["swift", "test"] diff --git a/Docker/run b/Docker/run index dd797d6..3bc36dd 100755 --- a/Docker/run +++ b/Docker/run @@ -5,7 +5,7 @@ mode=$2 if [[ ! -d "./Wishlist.xcodeproj" ]] ; then echo "Script must be run in project directory"; exit 1 fi -if [[ ! $version =~ ^[0-9]+(\.[0-9]+){2,2}$ ]] ; then +if [[ ! $version =~ ^[0-9]+(\.[0-9]+){2,2}(\-RC[0-9]+){0,1}$ ]] ; then echo "Script must be run with version number as first argument"; exit 1 fi if [[ ! $mode =~ ^prod|dev$ ]] ; then diff --git a/Docker/run.test b/Docker/run.test new file mode 100755 index 0000000..60098fb --- /dev/null +++ b/Docker/run.test @@ -0,0 +1,14 @@ +#!/bin/bash +version=$1 + +if [[ ! -d "./Wishlist.xcodeproj" ]] ; then + echo "Script must be run in project directory"; exit 1 +fi +if [[ ! $version =~ ^[0-9]+(\.[0-9]+){2,2}(\-RC[0-9]+){0,1}$ ]]; +then + echo "Script must be run with version number as argument"; exit 1 +fi + +echo " #### Running Docker Compose for project Wishlist with version $version" + +APPVERSION=$version DBHOST=db-test docker-compose -p wishlist -f Docker/docker-compose.test up --abort-on-container-exit diff --git a/LinuxMain.swift b/LinuxMain.swift new file mode 100644 index 0000000..5485969 --- /dev/null +++ b/LinuxMain.swift @@ -0,0 +1,21 @@ +import XCTest +@testable import AppTests +@testable import DomainTests +@testable import LibraryTests + +XCTMain([ + // LibraryTests + testCase(UUIDTests.__allTests), + // DomainTests + testCase(AnnouncementsActorTests.__allTests), + testCase(EnrollmentActorTests.__allTests), + testCase(InvitationTests.__allTests), + testCase(TestingUserRepositoryTests.__allTests), + // AppTests + testCase(DomainModelInvitationTests.__allTests), + testCase(DomainModelItemTests.__allTests), + testCase(DomainModelListTests.__allTests), + testCase(DomainModelReservationTests.__allTests), + testCase(DomainModelUserTests.__allTests), + testCase(RequestLanguageServiceTests.__allTests) +]) diff --git a/Package.resolved b/Package.resolved index 96d20b0..311b9e5 100644 --- a/Package.resolved +++ b/Package.resolved @@ -24,8 +24,8 @@ "repositoryURL": "https://github.com/IBM-Swift/BlueSocket.git", "state": { "branch": null, - "revision": "f82e1401f04f62b2e8ce4670456d104466957810", - "version": "1.0.50" + "revision": "c46a3d41f5b2401d18bcb46d0101cdc5cd13e307", + "version": "1.0.52" } }, { @@ -33,8 +33,8 @@ "repositoryURL": "https://github.com/IBM-Swift/BlueSSLService.git", "state": { "branch": null, - "revision": "d6616def31a12088034f0a63ed4646772eff5bce", - "version": "1.0.50" + "revision": "ab2c2aa2fe574969f391c80ee87746be431dee0e", + "version": "1.0.52" } }, { @@ -51,8 +51,8 @@ "repositoryURL": "https://github.com/vapor/core.git", "state": { "branch": null, - "revision": "18f2436bf7a6bc2224372c0885db2e0159af1649", - "version": "3.9.2" + "revision": "10d33362a47fab03a067e78fb0791341d9c634fa", + "version": "3.9.3" } }, { @@ -113,8 +113,8 @@ "package": "Imperial", "repositoryURL": "https://github.com/edmw/Imperial.git", "state": { - "branch": "netid-fix-1", - "revision": "7370f7bd51fdaa9421dc7204dd0c3fc4e3997821", + "branch": "edmw", + "revision": "f8e1647dc750711de9156a98aaae8258325601a7", "version": null } }, @@ -253,15 +253,6 @@ "version": "5.1.0" } }, - { - "package": "SwiftDate", - "repositoryURL": "https://github.com/malcommac/SwiftDate.git", - "state": { - "branch": null, - "revision": "ed308a5afb97a3874300b761dad6e9966269e3e7", - "version": "5.1.0" - } - }, { "package": "TemplateKit", "repositoryURL": "https://github.com/vapor/template-kit.git", diff --git a/Package.swift b/Package.swift index d265f5f..2d48e8d 100755 --- a/Package.swift +++ b/Package.swift @@ -1,44 +1,89 @@ -// swift-tools-version:4.0 +// swift-tools-version:5.0 import PackageDescription let package = Package( name: "Wishlist", + platforms: [ + .macOS(.v10_12) + ], products: [ - .executable(name: "Wishlist", targets: ["Run"]) + .library(name: "WishlistLibrary", targets: [ "Library" ]), + .library(name: "WishlistDomain", targets: [ "Domain" ]), + .executable(name: "Wishlist", targets: [ "Run" ]) ], -// Swift 5: -// platforms: [ -// .macOS(.v10_12), -// ], dependencies: [ + .package(url: "https://github.com/apple/swift-nio.git", from: "1.13.0"), + .package(url: "https://github.com/apple/swift-nio-ssl.git", from: "1.0.1"), + .package(url: "https://github.com/apple/swift-nio-ssl-support.git", from: "1.0.0"), .package(url: "https://github.com/vapor/vapor.git", from: "3.1.0"), .package(url: "https://github.com/vapor/leaf.git", from: "3.0.0"), .package(url: "https://github.com/vapor/auth.git", from: "2.0.1"), .package(url: "https://github.com/vapor/crypto.git", from: "3.3.0"), .package(url: "https://github.com/vapor/fluent-sqlite.git", from: "3.0.0"), .package(url: "https://github.com/vapor/fluent-mysql.git", from: "3.0.1"), - //.package(url: "https://github.com/vapor-community/Imperial.git", from: "0.7.1"), - .package(url: "https://github.com/edmw/Imperial.git", .branch("netid-fix-1")), + .package(url: "https://github.com/edmw/Imperial.git", .branch("edmw")), .package(url: "https://github.com/miroslavkovac/Lingo.git", from: "3.0.5"), - .package(url: "https://github.com/malcommac/SwiftDate.git", from: "5.0.0"), .package(url: "https://github.com/LiveUI/VaporTestTools.git", from: "0.1.7"), .package(url: "https://github.com/IBM-Swift/LoggerAPI.git", .exact("1.8.0")), .package(url: "https://github.com/IBM-Swift/Swift-SMTP", .exact("5.1.0")) ], targets: [ - .target(name: "App", dependencies: [ - "Vapor", - "Leaf", - "Authentication", - "Crypto", - "FluentSQLite", - "FluentMySQL", - "Imperial", - "Lingo", - "SwiftDate", - "SwiftSMTP" - ]), - .target(name: "Run", dependencies: ["App"]), - .testTarget(name: "AppTests", dependencies: ["App", "VaporTestTools", "LoggerAPI"]) - ] + .target( + name: "Library", + dependencies: [] + ), + .target( + name: "Domain", + dependencies: [ + "Library", + "NIO" + ] + ), + .target( + name: "App", + dependencies: [ + "Library", + "Domain", + "Vapor", + "Leaf", + "Authentication", + "Crypto", + "FluentSQLite", + "FluentMySQL", + "Imperial", + "Lingo", + "SwiftSMTP" + ] + ), + .target( + name: "Run", + dependencies: [ + "App" + ] + ), + .testTarget( + name: "LibraryTests", + dependencies: [ + "Library" + ], + path: "Tests/LibraryTests" + ), + .testTarget( + name: "DomainTests", + dependencies: [ + "Domain" + ], + path: "Tests/DomainTests" + ), + .testTarget( + name: "AppTests", + dependencies: [ + "App", + "VaporTestTools", + "LoggerAPI" + ], + path: "Tests/AppTests" + ) + ], + swiftLanguageVersions: [.v5] ) diff --git a/Public/styles/wishlist.css b/Public/styles/wishlist.css index f3244d0..67e356d 100644 --- a/Public/styles/wishlist.css +++ b/Public/styles/wishlist.css @@ -47,7 +47,6 @@ header .content { display: flex; flex-direction: column; justify-content: space-between; - height: 132px; } @media (min-width: 768px) { header { @@ -155,9 +154,13 @@ main .content { main .content > .h2 { display: flex; justify-content: space-between; - align-items: center; + align-items: baseline; + margin-bottom: 0; } main .content > h2, main .content > .h2 > h2 { + display: flex; + justify-content: space-between; + align-items: baseline; font-family: 'Open Sans', sans-serif; font-size: 1.8rem; font-weight: 800; diff --git a/Resources/Localizations/de.json b/Resources/Localizations/de.json index 0ef5df7..d11487a 100644 --- a/Resources/Localizations/de.json +++ b/Resources/Localizations/de.json @@ -24,6 +24,7 @@ "wishlist": "Wunschzettel", "your-wishlists": "Deine Wunschzettel", "your-wishes": "Deine Wünsche", + "invitations": "Einladungen", "your-invitations": "Deine Einladungen", "want-to": "Möchtest du ...", @@ -38,6 +39,7 @@ "none": "Keine", "never": "Niemals", "edit": "Bearbeiten", + "view": "Anzeigen", "options": "Einstellungen", "profile": "Profil", @@ -116,6 +118,7 @@ "list-options": "Einstellungen des Zettels", "edit-list": "Zettel bearbeiten", "list-editing": "Zettel bearbeiten", + "view-list": "Zettel anzeigen", "list-field-title-placeholder": "Name des Wunschzettels eingeben", "list-field-title-description": "Wunschzettel können anhand ihres Namens von anderen Nutzern gefunden werden.", "list-field-title-invalid": "Ein eindeutiger Name wird benötigt. Gültige Namen sind mindestens 4 Zeichen lang und enthalten nur alphanumerische Zeichen, Leerzeichen und Satzzeichen.", @@ -135,6 +138,7 @@ "list-import-error": "Importfehler", "message-list-import-error": "Die ausgewählte Datei kann leider nicht in einen neuen Wunschzettel umgewandelt werden.", "edit-list-wishes": "Wünsche bearbeiten", + "favorites": "Favoriten", "list-your-favorites": "Deine Favoriten", "add-favorite": "Favorit hinzufügen", "to-add-favorite": ".... den Zettel %{value1} zu deinen Favoriten hinzufügen?", @@ -147,11 +151,11 @@ "item-title": "Name", "item-description": "Beschreibung", "item-preference": "Präferenz", - "item-preference-0": "Nicht so wichtig", - "item-preference-1": "Bin mir nicht sicher", - "item-preference-2": "Würde mich sehr freuen", - "item-preference-3": "Hätte ich sehr gerne", - "item-preference-4": "Muss ich haben", + "item-preference-lowest": "Nicht so wichtig", + "item-preference-low": "Bin mir nicht sicher", + "item-preference-normal": "Würde mich sehr freuen", + "item-preference-high": "Hätte ich sehr gerne", + "item-preference-highest": "Muss ich haben", "item-wanted": "gewünscht", "item-reserved": "reserviert", "item-state": "Status", diff --git a/Resources/Localizations/en.json b/Resources/Localizations/en.json index 96b2e54..a06f209 100644 --- a/Resources/Localizations/en.json +++ b/Resources/Localizations/en.json @@ -5,11 +5,16 @@ "private": "Private", "friends": "Friends", - "item-preference-0": "Not so important", - "item-preference-1": "Not so sure", - "item-preference-2": "Would be happy", - "item-preference-3": "Would love to have", - "item-preference-4": "Must have", + "visibility-public": "public", + "visibility-private": "only you", + "visibility-friends": "your friends", + "visibility-users": "all users", + + "item-preference-lowest": "Not so important", + "item-preference-low": "Not so sure", + "item-preference-normal": "Would be happy", + "item-preference-high": "Would love to have", + "item-preference-highest": "Must have", "invitation-mail-title": "🎁 Invitation", diff --git a/Resources/Views/Errors/Authentication.leaf b/Resources/Views/Errors/Authentication.leaf index ac13eac..bed66ad 100644 --- a/Resources/Views/Errors/Authentication.leaf +++ b/Resources/Views/Errors/Authentication.leaf @@ -4,7 +4,7 @@

#L10N("sorry"){Sorry!}

#L10N("message-authentication"){It seems that your authentication could not proceed.}

#L10N("message-authentication-2"){Unfortunately we usually can not do much about it, please try again later ...}

-

#L10N("message-authentication-tips"){"Here are some useful links meanwhile:}

+

#L10N("message-authentication-tips"){Meanwhile here are some useful links:}

#embed("Errors/Useful") } #// diff --git a/Resources/Views/Protected/Wishlist.leaf b/Resources/Views/Protected/Wishlist.leaf index 755d023..44ab9e3 100644 --- a/Resources/Views/Protected/Wishlist.leaf +++ b/Resources/Views/Protected/Wishlist.leaf @@ -2,6 +2,7 @@ #set("title") {#L10N("wishlist"){Wishlist}} #set("subtitle") {#L10N("someones-wishlist","#(page.ownerName)"){#(page.ownerName)'s Wishlist}} #// +#set("location"){/list/#(page.listID)} #set("content") { #if(page.userID) {
@@ -16,7 +17,7 @@ you're viewing #(page.ownerName)'s #(page.listTitle) Wishlist. }
}} -

#(page.listTitle)#if(page.userID && page.userID == page.ownerID){#L10N("edit"){Edit}}

#if(page.userID != nil) {#if(page.userFavorsList){★}else{}}
+

#(page.listTitle)#if(page.userID && page.userID == page.ownerID){#L10N("edit"){Edit}}

#if(page.userID != nil) {#if(page.userFavorsList){★}else{}}
#if(request.parameter.m == "WAR") {