From 564fcde1fb7e174335b39e834c596cfb6c89c8c5 Mon Sep 17 00:00:00 2001 From: eliqs Date: Thu, 14 Mar 2024 10:46:42 +0000 Subject: [PATCH 01/24] =?UTF-8?q?=F0=9F=8C=90=20Translated=20using=20Webla?= =?UTF-8?q?te=20(French)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 97.2% (739 of 760 strings) Translation: Träwelling/Träwelling Translate-URL: https://translate.codeberg.org/projects/trawelling/traewelling/fr/ --- lang/fr.json | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lang/fr.json b/lang/fr.json index bd5f49ed2..6a5eaa4d7 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -731,5 +731,13 @@ "export.generate": "Générer", "export.reason.business": "Voyage pour le travail", "export.period": "Quelle période voulez-vous exporter ?", - "export.format": "Dans quel format voulez-vous exporter ?" + "export.format": "Dans quel format voulez-vous exporter ?", + "checkin.conflict": "Un enregistrement parallèle existe déjà.", + "maintenance.try-later": "Veuillez réessayer ultérieurement.", + "checkin.points.full": "Vous auriez pu gagner :points points.", + "modals.tags.new": "Nouveau tag", + "notifications.show": "Afficher les notifications", + "maintenance.title": "Nous effectuons actuellement des mises à jour.", + "maintenance.subtitle": "Ne vous inquiétez pas, nous travaillons actuellement à l'amélioration du site.", + "maintenance.prolonged": "Ce message ne devrait pas s'afficher pendant plus de quelques minutes." } From badf7123bc032462208f4c1624cedce3a092edad Mon Sep 17 00:00:00 2001 From: 0AgentSmith <0AgentSmith@users.noreply.translate.codeberg.org> Date: Thu, 14 Mar 2024 16:27:09 +0000 Subject: [PATCH 02/24] =?UTF-8?q?=F0=9F=8C=90=20Translated=20using=20Webla?= =?UTF-8?q?te=20(English)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 100.0% (760 of 760 strings) Translation: Träwelling/Träwelling Translate-URL: https://translate.codeberg.org/projects/trawelling/traewelling/en/ --- lang/en.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lang/en.json b/lang/en.json index fe67928cc..49efdad86 100644 --- a/lang/en.json +++ b/lang/en.json @@ -5,8 +5,8 @@ "about.express": "InterCity, EuroCity", "about.events": "What are events?", "about.events.description1": "Träwelling brings people together who use the same public transport. Sometimes those people even ride to the same events, without knowing from another!", - "about.events.description2": "That’s why we’ve created the Events feature. Anyone can can suggest an event in a specific time frame; and once accepted, all users can connect their check-ins to that event.", - "about.events.description3": "If you want to create an event, please make sure that events should benefit the entire Träwelling community. Those events can be from the European railway community such as the last course of a train model, hacking events such as GPN, or from the LGBTQ scene such as Cologne Pride.", + "about.events.description2": "That’s why we’ve created the Events feature. Anyone can suggest an event in a specific time frame; and once accepted, all users can connect their check-ins to that event.", + "about.events.description3": "If you want to create an event, please be aware that events should benefit the entire Träwelling community. Those events can be from the European railway community such as the last course of a train model, hacking events such as GPN, or from the LGBTQ scene such as Cologne Pride.", "about.events.description4": "Events are something special. That’s why we decided to deny suggestions to smaller, local events such as Christmas Markets or city fairs. As always, there are exceptions to that rule.", "about.faq-heading": "Frequently asked questions", "about.feature-missing": "If you would like to suggest a feature or have found a bug, please report it directly to our GitHub repository.", From b8e875745585cfa3aeab1d7f1d622e483447bf15 Mon Sep 17 00:00:00 2001 From: 0AgentSmith <0AgentSmith@users.noreply.translate.codeberg.org> Date: Thu, 14 Mar 2024 15:36:41 +0000 Subject: [PATCH 03/24] =?UTF-8?q?=F0=9F=8C=90=20Translated=20using=20Webla?= =?UTF-8?q?te=20(German)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 100.0% (760 of 760 strings) Translation: Träwelling/Träwelling Translate-URL: https://translate.codeberg.org/projects/trawelling/traewelling/de/ --- lang/de.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/de.json b/lang/de.json index 7bbe320bf..ed9e55059 100644 --- a/lang/de.json +++ b/lang/de.json @@ -672,7 +672,7 @@ "edit": "Bearbeiten", "delete": "Löschen", "active-tokens-count": ":count aktive Token|:count aktive Tokens", - "user.blocked.text": "Du kannst die Check-Ins von :username nicht sehen, da du blockiert wurdest.", + "user.blocked.text": "Du kannst die Check-Ins von :username nicht sehen, da du den Nutzer blockiert hast.", "events.disclaimer.organizer": "Träwelling ist nicht der Veranstalter.", "events.disclaimer.source": "Dieser Termin wurde von einem Träwelling User vorgeschlagen und durch uns genehmigt.", "events.disclaimer.warranty": "Träwelling übernimmt keine Gewähr auf die Korrektheit oder Vollständigkeit der Daten.", From 5cb87f59ecb7cd7bdee85ca95df93ded4787b59f Mon Sep 17 00:00:00 2001 From: 0AgentSmith <0AgentSmith@users.noreply.translate.codeberg.org> Date: Thu, 14 Mar 2024 17:04:32 +0000 Subject: [PATCH 04/24] =?UTF-8?q?=F0=9F=8C=90=20Translated=20using=20Webla?= =?UTF-8?q?te=20(English)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 100.0% (760 of 760 strings) Translation: Träwelling/Träwelling Translate-URL: https://translate.codeberg.org/projects/trawelling/traewelling/en/ --- lang/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/en.json b/lang/en.json index 49efdad86..5bf840e97 100644 --- a/lang/en.json +++ b/lang/en.json @@ -15,7 +15,7 @@ "about.international": "InterCityExpress, TGV, RailJet", "about.name": "The name is an allusion to the well-known \"Senk ju for träwelling wis Deutsche Bahn\", which you should have heard in almost every long-distance train of the Deutsche Bahn.", "about.name-heading": "Where does the name come from?", - "about.no-train": "We use an interface of the Deutsche Bahn, where not all offers are displayed directly. Unfortunately, we can't do much with it if your train is not there.", + "about.no-train": "We use an interface of the Deutsche Bahn, where not all offers are displayed directly. Unfortunately, we can't do much if your train is not listed.", "about.no-train-heading": "Why isn't my train listed?", "about.points-heading": "How are points calculated?", "about.points1": "The points consist of the product class and the distance of your journey.", From 2a6133bc7e9e8c934a1f221dcedfc7394e0e76d0 Mon Sep 17 00:00:00 2001 From: 0AgentSmith <0AgentSmith@users.noreply.translate.codeberg.org> Date: Thu, 14 Mar 2024 17:04:33 +0000 Subject: [PATCH 05/24] =?UTF-8?q?=F0=9F=8C=90=20Translated=20using=20Webla?= =?UTF-8?q?te=20(Norwegian=20Bokm=C3=A5l)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 71.8% (546 of 760 strings) Translation: Träwelling/Träwelling Translate-URL: https://translate.codeberg.org/projects/trawelling/traewelling/nb_NO/ --- lang/nb_NO.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/nb_NO.json b/lang/nb_NO.json index 404bcad08..321ed508c 100644 --- a/lang/nb_NO.json +++ b/lang/nb_NO.json @@ -316,7 +316,7 @@ "notifications.socialNotShared.mastodon.429": "Det ser ut til at Träwelling har blitt midlertidig blokkert av Instance:n din. (429 Too Many Requests)", "pagination.previous": "« Forrige side", "pagination.back": "« Komme tilbake", - "notifications.userJoinedConnection.lead": "@:username er også i din forbindelse!", + "notifications.userJoinedConnection.lead": "@:username er også i din forbindelse!", "notifications.userJoinedConnection.notice": "@:username reiser med :linename fra :origin til :destination . |@:username reiser med linje :linename fra :origin til :destination.", "pagination.next": "Neste side »", "notifications.socialNotShared.mastodon.504": "Instance:n din var ikke tilgjengelig da vi prøvde å sende innsjekkingen din. (504 Bad Gateway)", From cae19b6eb89b6196bcb29096820a3b36d0a6e368 Mon Sep 17 00:00:00 2001 From: 0AgentSmith <0AgentSmith@users.noreply.translate.codeberg.org> Date: Thu, 14 Mar 2024 17:09:00 +0000 Subject: [PATCH 06/24] =?UTF-8?q?=F0=9F=8C=90=20Translated=20using=20Webla?= =?UTF-8?q?te=20(English)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 100.0% (760 of 760 strings) Translation: Träwelling/Träwelling Translate-URL: https://translate.codeberg.org/projects/trawelling/traewelling/en/ --- lang/en.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lang/en.json b/lang/en.json index 5bf840e97..f2fe2f20b 100644 --- a/lang/en.json +++ b/lang/en.json @@ -21,7 +21,7 @@ "about.points1": "The points consist of the product class and the distance of your journey.", "about.productclass": "product category", "about.regional": "Regional train/-express", - "about.suburban": "S-Bahn, Ferry", + "about.suburban": "Suburban rail, Ferry", "about.tram": "Tram / light rail, bus, subway", "about.who0": "Träwelling is an open source project.", "about.who1": "Since 2013, various people have been developing the project. Partly only bugfixes, partly major changes.", @@ -33,7 +33,7 @@ "login-required": "(Login required)", "admin.usage-board": "Usage Board", "admin.select-range": "Select range", - "admin.checkins": "Check-in's", + "admin.checkins": "Check-ins", "admin.registered-users": "Registered users", "admin.hafas-entries-by-type": "HAFAS-entries by transport type", "admin.hafas-entries-by-polylines": "HAFAS-entries and quantity of corresponding Polylines", From 57d5a5662c89476d2b2ddc1d76530c831048a928 Mon Sep 17 00:00:00 2001 From: Vistaus Date: Thu, 14 Mar 2024 17:59:06 +0000 Subject: [PATCH 07/24] =?UTF-8?q?=F0=9F=8C=90=20Translated=20using=20Webla?= =?UTF-8?q?te=20(Dutch)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 100.0% (760 of 760 strings) Translation: Träwelling/Träwelling Translate-URL: https://translate.codeberg.org/projects/trawelling/traewelling/nl/ --- lang/nl.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/nl.json b/lang/nl.json index 4403eeacd..54deb6314 100644 --- a/lang/nl.json +++ b/lang/nl.json @@ -16,7 +16,7 @@ "about.points1": "De punten zijn samengesteld uit de productklasse en de afstand van je reis.", "about.productclass": "Productklasse", "about.tram": "Tram, lightrail, bus, metro", - "about.suburban": "S-Bahn, Veerboot", + "about.suburban": "Voorstadstreinen, Veerboten", "admin.usage": "Gebruik", "admin.select-range": "Kies een gebied", "about.regional": "Regionale (snel)treinen", From 28b0f969069e97d0a05b490601001275d3959275 Mon Sep 17 00:00:00 2001 From: Kris Date: Fri, 15 Mar 2024 11:39:00 +0100 Subject: [PATCH 08/24] :technologist: fix translation output on 403 page (#2421) --- resources/views/errors/403.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/errors/403.blade.php b/resources/views/errors/403.blade.php index caaba79a1..27aab2480 100644 --- a/resources/views/errors/403.blade.php +++ b/resources/views/errors/403.blade.php @@ -3,4 +3,4 @@ @section('image', asset('images/covers/traffic_lights.jpg')) @section('title', __('error.403')) @section('code', '403') -@section('message', __($exception->getMessage() ?: 'error.403')) +@section('message', $exception->getMessage() ?: __('error.403')) From 326ad846ba9b7a98bbbe84dad801b6ff21075a62 Mon Sep 17 00:00:00 2001 From: Kris Date: Fri, 15 Mar 2024 11:40:45 +0100 Subject: [PATCH 09/24] :bug: accept Error as attribute for Referencable (#2420) --- app/Helpers/Helper.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Helpers/Helper.php b/app/Helpers/Helper.php index 819d36cbc..56a7dc6af 100644 --- a/app/Helpers/Helper.php +++ b/app/Helpers/Helper.php @@ -78,7 +78,7 @@ function hasStationBoardTimezoneOffsetToUser(Collection $departures, User $user) return false; } -function errorMessage(Exception $exception, ?string $text = null): array|null|string { +function errorMessage(Exception|Error $exception, ?string $text = null): array|null|string { $text = $text ?? __('messages.exception.general'); if (!$exception instanceof Referencable) { From 28bb3bde7c6ed067153e4e6806cce4badeeb3b0b Mon Sep 17 00:00:00 2001 From: Kris Date: Fri, 15 Mar 2024 14:08:11 +0100 Subject: [PATCH 10/24] :card_file_box: add database layout for IFOPT (#2413) Co-authored-by: Levin Herr --- app/Models/Station.php | 29 +++++++++++++++-- database/factories/StationFactory.php | 10 +++++- ...024_03_10_211526_add_ifopt_to_stations.php | 31 +++++++++++++++++++ resources/views/admin/stations/show.blade.php | 8 +++++ 4 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 database/migrations/2024_03_10_211526_add_ifopt_to_stations.php diff --git a/app/Models/Station.php b/app/Models/Station.php index 1027f888d..a82a35897 100644 --- a/app/Models/Station.php +++ b/app/Models/Station.php @@ -29,22 +29,45 @@ class Station extends Model use HasFactory, LogsActivity; protected $table = 'train_stations'; - protected $fillable = ['ibnr', 'wikidata_id', 'rilIdentifier', 'name', 'latitude', 'longitude', 'time_offset', 'shift_time']; + protected $fillable = [ + 'ibnr', 'wikidata_id', 'rilIdentifier', + 'ifopt_a', 'ifopt_b', 'ifopt_c', 'ifopt_d', 'ifopt_e', + 'name', 'latitude', 'longitude', 'time_offset', 'shift_time' + ]; protected $hidden = ['created_at', 'updated_at', 'time_offset', 'shift_time']; protected $casts = [ 'id' => 'integer', - 'rilIdentifier' => 'string', - 'name' => 'string', 'ibnr' => 'integer', 'wikidata_id' => 'string', + 'ifopt_a' => 'string', + 'ifopt_b' => 'integer', + 'ifopt_c' => 'integer', + 'ifopt_d' => 'integer', + 'ifopt_e' => 'integer', + 'rilIdentifier' => 'string', + 'name' => 'string', 'latitude' => 'float', 'longitude' => 'float', ]; + protected $appends = ['ifopt']; public function wikidataEntity(): BelongsTo { return $this->belongsTo(WikidataEntity::class, 'wikidata_id', 'id'); } + public function getIfoptAttribute(): ?string { + if (!$this->ifopt_a) { + return null; + } + $ifopt = $this->ifopt_a; + foreach (['b', 'c', 'd', 'e'] as $level) { + if ($this->{"ifopt_$level"}) { + $ifopt .= ':' . $this->{"ifopt_$level"}; + } + } + return $ifopt; + } + public function getActivitylogOptions(): LogOptions { return LogOptions::defaults() ->dontSubmitEmptyLogs() diff --git a/database/factories/StationFactory.php b/database/factories/StationFactory.php index 0b10ea849..41c266d2d 100644 --- a/database/factories/StationFactory.php +++ b/database/factories/StationFactory.php @@ -9,10 +9,18 @@ class StationFactory extends Factory public function definition(): array { return [ 'ibnr' => $this->faker->unique()->numberBetween(8000001, 8999999), + 'wikidata_id' => null, + 'ifopt_a' => $this->faker->randomElement(['de', 'at', 'ch', 'fr']), + 'ifopt_b' => $this->faker->numberBetween(10000, 99999), + 'ifopt_c' => $this->faker->numberBetween(1, 9999), + 'ifopt_d' => $this->faker->boolean() ? $this->faker->numberBetween(1, 9999) : null, + 'ifopt_e' => $this->faker->boolean() ? $this->faker->numberBetween(1, 9) : null, + 'rilIdentifier' => substr(str_shuffle("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 4), 'name' => $this->faker->unique()->city, 'latitude' => $this->faker->latitude, 'longitude' => $this->faker->longitude, - 'rilIdentifier' => substr(str_shuffle("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), 0, 4), + 'time_offset' => null, + 'shift_time' => null, ]; } } diff --git a/database/migrations/2024_03_10_211526_add_ifopt_to_stations.php b/database/migrations/2024_03_10_211526_add_ifopt_to_stations.php new file mode 100644 index 000000000..a4cfa6799 --- /dev/null +++ b/database/migrations/2024_03_10_211526_add_ifopt_to_stations.php @@ -0,0 +1,31 @@ +unsignedInteger('ifopt_e')->nullable()->comment('Stop Place Component (or unused)')->after('wikidata_id'); + $table->unsignedInteger('ifopt_d')->nullable()->comment('Stop Place or Stop Place Component')->after('wikidata_id'); + $table->unsignedInteger('ifopt_c')->nullable()->comment('Mode or Stop Place')->after('wikidata_id'); + $table->unsignedInteger('ifopt_b')->nullable()->comment('Administrative Area')->after('wikidata_id'); + $table->string('ifopt_a')->nullable()->comment('Country')->after('wikidata_id'); + + $table->index(['ifopt_a', 'ifopt_b', 'ifopt_c', 'ifopt_d', 'ifopt_e'], 'ifopt'); + }); + } + + public function down(): void { + Schema::table('train_stations', static function(Blueprint $table) { + $table->dropIndex('ifopt'); + $table->dropColumn('ifopt_e'); + $table->dropColumn('ifopt_d'); + $table->dropColumn('ifopt_c'); + $table->dropColumn('ifopt_b'); + $table->dropColumn('ifopt_a'); + }); + } +}; diff --git a/resources/views/admin/stations/show.blade.php b/resources/views/admin/stations/show.blade.php index 9a20b1f2f..064821764 100644 --- a/resources/views/admin/stations/show.blade.php +++ b/resources/views/admin/stations/show.blade.php @@ -17,6 +17,14 @@ Name {{ $station->name }} + + IBNR + {{ $station->ibnr }} + + + IFOPT + {{ $station->ifopt }} + From 34685d725b5fe999cc92187dc3e6fa799a158628 Mon Sep 17 00:00:00 2001 From: Kris Date: Fri, 15 Mar 2024 14:21:57 +0100 Subject: [PATCH 11/24] :recycle: migrate home station endpoint to use TRWL-ID (#2412) --- API_CHANGELOG.md | 9 ++- .../API/v1/TransportController.php | 64 +++++++++++----- .../Backend/Transport/HomeController.php | 3 +- routes/api.php | 9 ++- storage/api-docs/api-docs.json | 11 ++- tests/Feature/APIv1/TransportTest.php | 73 ++++++++++--------- 6 files changed, 105 insertions(+), 64 deletions(-) diff --git a/API_CHANGELOG.md b/API_CHANGELOG.md index 08a72b9d2..d8634d5d1 100644 --- a/API_CHANGELOG.md +++ b/API_CHANGELOG.md @@ -4,8 +4,15 @@ In this we try to keep track of changes to the API. Primarily this should document changes that are not backwards compatible or belongs to already documented endpoints. This is to help you keep track of the changes and to help you update your code accordingly. +## 2024-03-10 + +Replaced `PUT /trains/station/{name}/home` with `PUT /station/{id}/home`. +The old endpoint is marked as deprecated and will be removed after 2024-06. + +Please note, that the ID is the Träwelling internal ID and not the IBNR! + ## 2024-03-01 -> **warning** +> [!WARNING] > Possibly breaking change: The implementation of next/prev links on user/{username}/statuses endpoint has been changed to adhere to the documentation. ## 2024-01-21 diff --git a/app/Http/Controllers/API/v1/TransportController.php b/app/Http/Controllers/API/v1/TransportController.php index 26ee371c6..0b68b6adf 100644 --- a/app/Http/Controllers/API/v1/TransportController.php +++ b/app/Http/Controllers/API/v1/TransportController.php @@ -21,6 +21,7 @@ use App\Models\Event; use App\Models\Station; use Carbon\Carbon; +use Exception; use Illuminate\Database\Eloquent\ModelNotFoundException; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; @@ -420,18 +421,45 @@ public function create(Request $request): JsonResponse { } } + /** + * @param string $stationName + * + * @return JsonResponse + * @see All slashes (as well as encoded to %2F) in $name need to be replaced, preferrably by a space (%20) + * @deprecated Replaced by setHome (with "ID" instead of StationName and without "trains" in the path) + */ + public function setHomeLegacy(string $stationName): JsonResponse { //ToDo: Remove this endpoint after 2024-06 (replaced by id) + try { + $station = HafasController::getStations(query: $stationName, results: 1)->first(); + if ($station === null) { + return $this->sendError("Your query matches no station"); + } + + $station = HomeController::setHome(user: auth()->user(), station: $station); + + return $this->sendResponse( + data: new StationResource($station), + ); + } catch (HafasException) { + return $this->sendError("There has been an error with our data provider", 502); + } catch (ModelNotFoundException) { + return $this->sendError("Your query matches no station"); + } + } + + /** * @OA\Put( - * path="/trains/station/{name}/home", + * path="/station/{id}/home", * operationId="setHomeStation", * tags={"Checkin"}, * summary="Set a station as home station", * @OA\Parameter( - * name="name", + * name="id", * in="path", - * description="Name of the station", + * description="Träwelling-ID of the station", * required=true, - * example="Karlsruhe Hbf", + * example=1234, * ), * @OA\Response( * response=200, @@ -444,33 +472,29 @@ public function create(Request $request): JsonResponse { * @OA\Response(response=400, description="Bad request"), * @OA\Response(response=401, description="Unauthorized"), * @OA\Response(response=404, description="Station not found"), - * @OA\Response(response=502, description="Error with our data provider"), - * security={ - * {"passport": {"create-statuses"}}, {"token": {}} - * - * } + * @OA\Response(response=500, description="Unknown error"), + * security={{"passport": {"create-statuses"}}, {"token": {}}} * ) - * @param string $stationName + * @param int $stationId * * @return JsonResponse - * @see All slashes (as well as encoded to %2F) in $name need to be replaced, preferrably by a space (%20) */ - public function setHome(string $stationName): JsonResponse { + public function setHome(int $stationId): JsonResponse { try { - $station = HafasController::getStations(query: $stationName, results: 1)->first(); - if ($station === null) { - return $this->sendError("Your query matches no station"); - } + $station = Station::findOrFail($stationId); - $station = HomeController::setHome(user: auth()->user(), station: $station); + auth()->user()?->update([ + 'home_id' => $station->id + ]); return $this->sendResponse( data: new StationResource($station), ); - } catch (HafasException) { - return $this->sendError("There has been an error with our data provider", 502); } catch (ModelNotFoundException) { - return $this->sendError("Your query matches no station"); + return $this->sendError('The station could not be found'); + } catch (Exception $exception) { + report($exception); + return $this->sendError('Unknown error', 500); } } diff --git a/app/Http/Controllers/Backend/Transport/HomeController.php b/app/Http/Controllers/Backend/Transport/HomeController.php index a8ee56fcf..5b0b51a4a 100644 --- a/app/Http/Controllers/Backend/Transport/HomeController.php +++ b/app/Http/Controllers/Backend/Transport/HomeController.php @@ -13,7 +13,8 @@ abstract class HomeController extends Controller * @param Station $station * * @return Station - * @api v1 + * @api v1 + * @deprecated just use $user->update(...) directly...? */ public static function setHome(User $user, Station $station): Station { $user->update([ diff --git a/routes/api.php b/routes/api.php index 7cfcee586..b5f379336 100644 --- a/routes/api.php +++ b/routes/api.php @@ -87,12 +87,17 @@ Route::post('checkin', [TransportController::class, 'create']); Route::group(['prefix' => 'station'], static function() { Route::get('{name}/departures', [TransportController::class, 'departures']); - Route::put('{name}/home', [TransportController::class, 'setHome']); + Route::put('{name}/home', [TransportController::class, 'setHomeLegacy']); //ToDo: Remove this endpoint after 2024-06 (replaced by id) Route::get('nearby', [TransportController::class, 'getNextStationByCoordinates']); Route::get('autocomplete/{query}', [TransportController::class, 'getTrainStationAutocomplete']); Route::get('history', [TransportController::class, 'getTrainStationHistory']); }); }); + + Route::prefix('station')->middleware(['scope:write-statuses'])->group(static function() { + Route::put('{id}/home', [TransportController::class, 'setHome'])->whereNumber('id'); + }); + Route::group(['prefix' => 'statistics', 'middleware' => 'scope:read-statistics'], static function() { Route::get('/', [StatisticsController::class, 'getPersonalStatistics']); Route::get('/global', [StatisticsController::class, 'getGlobalStatistics']); @@ -165,7 +170,7 @@ Route::delete('/{webhookId}', [WebhookController::class, 'deleteWebhook']); }); - Route::apiResource('station', StationController::class); // currently admin/backend only + Route::apiResource('station', StationController::class); // currently admin/backend only Route::put('station/{oldStationId}/merge/{newStationId}', [StationController::class, 'merge']); // currently admin/backend only }); diff --git a/storage/api-docs/api-docs.json b/storage/api-docs/api-docs.json index 4a9a7763b..45266e2a6 100644 --- a/storage/api-docs/api-docs.json +++ b/storage/api-docs/api-docs.json @@ -3388,7 +3388,7 @@ ] } }, - "/trains/station/{name}/home": { + "/station/{id}/home": { "put": { "tags": [ "Checkin" @@ -3397,11 +3397,11 @@ "operationId": "setHomeStation", "parameters": [ { - "name": "name", + "name": "id", "in": "path", - "description": "Name of the station", + "description": "Träwelling-ID of the station", "required": true, - "example": "Karlsruhe Hbf" + "example": 1234 } ], "responses": { @@ -3429,6 +3429,9 @@ "404": { "description": "Station not found" }, + "500": { + "description": "Unknown error" + }, "502": { "description": "Error with our data provider" } diff --git a/tests/Feature/APIv1/TransportTest.php b/tests/Feature/APIv1/TransportTest.php index 6c3af2ceb..fc7f40d31 100644 --- a/tests/Feature/APIv1/TransportTest.php +++ b/tests/Feature/APIv1/TransportTest.php @@ -2,6 +2,7 @@ namespace Feature\APIv1; +use App\Models\Station; use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\Date; @@ -25,8 +26,8 @@ public function testGetDeparturesFetchTripAndCheckin(): void { $station = self::FRANKFURT_HBF['name']; $timestamp = Date::parse('next monday 8 am'); $this->actAsApiUserWithAllScopes(); - $response = $this->get( - uri: '/api/v1/trains/station/' . $station . '/departures?when=' . urlencode($timestamp->toIso8601String()), + $response = $this->get( + uri: '/api/v1/trains/station/' . $station . '/departures?when=' . urlencode($timestamp->toIso8601String()), ); $response->assertOk(); $response->assertJsonStructure([ @@ -63,18 +64,18 @@ public function testGetDeparturesFetchTripAndCheckin(): void { //Fetch trip with wrong origin / stopover $response = $this->get( - uri: '/api/v1/trains/trip' - . '?hafasTripId=' . $departure['tripId'] - . '&lineName=' . $departure['line']['name'] - . '&start=' . ($departure['stop']['id'] + 99999), + uri: '/api/v1/trains/trip' + . '?hafasTripId=' . $departure['tripId'] + . '&lineName=' . $departure['line']['name'] + . '&start=' . ($departure['stop']['id'] + 99999), ); $response->assertStatus(400); // Fetch correct trip $response = $this->get( - uri: '/api/v1/trains/trip' - . '?hafasTripId=' . $departure['tripId'] - . '&lineName=' . $departure['line']['name'] - . '&start=' . $departure['stop']['id'], + uri: '/api/v1/trains/trip' + . '?hafasTripId=' . $departure['tripId'] + . '&lineName=' . $departure['line']['name'] + . '&start=' . $departure['stop']['id'], ); $response->assertOk(); $response->assertJsonStructure([ @@ -105,16 +106,16 @@ public function testGetDeparturesFetchTripAndCheckin(): void { //Now checkin... $response = $this->postJson( - uri: '/api/v1/trains/checkin', - data: [ - 'tripId' => $departure['tripId'], - 'lineName' => $departure['line']['name'], - 'start' => $trip['stopovers'][0]['evaIdentifier'], - 'departure' => $trip['stopovers'][0]['departurePlanned'], - 'destination' => $trip['stopovers'][1]['evaIdentifier'], - 'arrival' => $trip['stopovers'][1]['arrivalPlanned'], - 'ibnr' => true, - ], + uri: '/api/v1/trains/checkin', + data: [ + 'tripId' => $departure['tripId'], + 'lineName' => $departure['line']['name'], + 'start' => $trip['stopovers'][0]['evaIdentifier'], + 'departure' => $trip['stopovers'][0]['departurePlanned'], + 'destination' => $trip['stopovers'][1]['evaIdentifier'], + 'arrival' => $trip['stopovers'][1]['arrivalPlanned'], + 'ibnr' => true, + ], ); $response->assertCreated(); $response->assertJsonStructure([ @@ -135,16 +136,16 @@ public function testGetDeparturesFetchTripAndCheckin(): void { //Do the same thing again! Should be a CheckInCollision $response = $this->postJson( - uri: '/api/v1/trains/checkin', - data: [ - 'tripId' => $departure['tripId'], - 'lineName' => $departure['line']['name'], - 'start' => $trip['stopovers'][0]['evaIdentifier'], - 'departure' => $trip['stopovers'][0]['departurePlanned'], - 'destination' => $trip['stopovers'][1]['evaIdentifier'], - 'arrival' => $trip['stopovers'][1]['arrivalPlanned'], - 'ibnr' => true, - ], + uri: '/api/v1/trains/checkin', + data: [ + 'tripId' => $departure['tripId'], + 'lineName' => $departure['line']['name'], + 'start' => $trip['stopovers'][0]['evaIdentifier'], + 'departure' => $trip['stopovers'][0]['departurePlanned'], + 'destination' => $trip['stopovers'][1]['evaIdentifier'], + 'arrival' => $trip['stopovers'][1]['arrivalPlanned'], + 'ibnr' => true, + ], ); $response->assertStatus(409); } @@ -180,21 +181,21 @@ public function testGetStationByCoordinatesIfNoStationIsNearby(): void { } public function testSetHome(): void { - $user = User::factory()->create(); + $user = User::factory()->create(); Passport::actingAs($user, ['*']); - $this->assertNull($user->home); + $station = Station::factory()->create(); - Http::fake(["*" => Http::response([self::HANNOVER_HBF])]); + $this->assertNull($user->home); - $response = $this->put('/api/v1/trains/station/Hannover Hbf/home'); + $response = $this->put('/api/v1/station/' . $station->id . '/home'); $response->assertOk(); $user->refresh(); - $this->assertEquals('Hannover Hbf', $user->home?->name); + $this->assertEquals($station->name, $user->home?->name); } public function testAutocompleteWithDs100(): void { - $user = User::factory()->create(); + $user = User::factory()->create(); Passport::actingAs($user, ['*']); Http::fake(["*/stations/" . self::HANNOVER_HBF['ril100'] => Http::response(self::HANNOVER_HBF)]); From 437996819a55286fd481e73a50aaa26521d2e5db Mon Sep 17 00:00:00 2001 From: Levin Herr Date: Fri, 15 Mar 2024 14:36:44 +0100 Subject: [PATCH 12/24] :sparkles: Add buttons to jump to the frontend (#2423) --- resources/views/admin/layout.blade.php | 15 ++++++++++++--- resources/views/admin/status/edit.blade.php | 10 +++++++++- resources/views/admin/users/show.blade.php | 7 +++++++ 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/resources/views/admin/layout.blade.php b/resources/views/admin/layout.blade.php index f81b9fef2..20046d61a 100644 --- a/resources/views/admin/layout.blade.php +++ b/resources/views/admin/layout.blade.php @@ -84,9 +84,18 @@ class="brand-image me-3" style="width: 30px; opacity: 0.8">
- @hasSection('title') -

@yield('title')

- @endif +
+ @hasSection('title') +
+

@yield('title')

+
+ @endif + @hasSection('actions') +
+ @yield('actions') +
+ @endif +
@if ($errors->any())
diff --git a/resources/views/admin/status/edit.blade.php b/resources/views/admin/status/edit.blade.php index 79f073fc1..7c71876f9 100644 --- a/resources/views/admin/status/edit.blade.php +++ b/resources/views/admin/status/edit.blade.php @@ -1,11 +1,19 @@ @extends('admin.layout') +@section('title', 'Status: ' . $status->id) + +@section('actions') + + + Frontend + +@endsection + @section('content')
-

Status bearbeiten

diff --git a/resources/views/admin/users/show.blade.php b/resources/views/admin/users/show.blade.php index 9d0bd0ad9..ce1cadb0a 100644 --- a/resources/views/admin/users/show.blade.php +++ b/resources/views/admin/users/show.blade.php @@ -2,6 +2,13 @@ @section('title', 'User: ' . $user->username) +@section('actions') + + + Frontend + +@endsection + @section('content')
From 3dd43e9236580098cf6b6e2d483ff31eacd1fa44 Mon Sep 17 00:00:00 2001 From: Kris Date: Fri, 15 Mar 2024 14:56:53 +0100 Subject: [PATCH 13/24] :bug: fix StatusTagPolicy for unauthenticated users (#2408) --- .../Backend/Transport/StatusTagController.php | 3 +- app/Policies/StatusTagPolicy.php | 32 ++++++---- database/factories/StatusTagFactory.php | 24 +++---- .../Feature/Privacy/StatusTagPrivacyTest.php | 64 +++++++++++++++++++ 4 files changed, 98 insertions(+), 25 deletions(-) create mode 100644 tests/Feature/Privacy/StatusTagPrivacyTest.php diff --git a/app/Http/Controllers/Backend/Transport/StatusTagController.php b/app/Http/Controllers/Backend/Transport/StatusTagController.php index 3e981578d..19f5f75aa 100644 --- a/app/Http/Controllers/Backend/Transport/StatusTagController.php +++ b/app/Http/Controllers/Backend/Transport/StatusTagController.php @@ -6,11 +6,12 @@ use App\Models\Status; use App\Models\StatusTag; use App\Models\User; +use Illuminate\Support\Collection; use Illuminate\Support\Facades\Gate; abstract class StatusTagController extends Controller { - public static function getVisibleTagsForUser(Status $status, User $user = null) { + public static function getVisibleTagsForUser(Status $status, User $user = null): Collection { return $status->tags->filter(function(StatusTag $tag) use ($user) { return Gate::forUser($user)->allows('view', $tag); }); diff --git a/app/Policies/StatusTagPolicy.php b/app/Policies/StatusTagPolicy.php index 1725b4027..11486536b 100644 --- a/app/Policies/StatusTagPolicy.php +++ b/app/Policies/StatusTagPolicy.php @@ -7,40 +7,46 @@ use App\Models\User; use Illuminate\Auth\Access\HandlesAuthorization; use Illuminate\Auth\Access\Response; +use Illuminate\Support\Facades\Gate; class StatusTagPolicy { use HandlesAuthorization; - public function view(User $user, StatusTag $statusTag) { + public function view(?User $user, StatusTag $statusTag): Response|bool { // Case 1: User is owner of this status - if ($user->id === $statusTag->status->user_id) { + if ($user?->id === $statusTag->status->user_id) { return Response::allow('User is owner of this status'); } - // Case 2: Status is NOT visible for user - if (!$user->can('view', $statusTag->status)) { + // Case 2: StatusTag is public + if ($statusTag->visibility === StatusVisibility::PUBLIC) { + return Response::allow('StatusTag is public'); + } + + // Case 3: Status is NOT visible for user + if (Gate::forUser($user)->denies('view', $statusTag->status)) { return Response::deny('Status is not visible for user'); } - // Case 3: StatusTag is private (and the StatusTag doesn't belong to the user) + // Case 4: StatusTag is private (and the StatusTag doesn't belong to the user) if ($statusTag->visibility === StatusVisibility::PRIVATE) { return Response::deny('StatusTag is private'); } - // Case 4: Status is followers only - if ($statusTag->visibility === StatusVisibility::FOLLOWERS) { + // Case 5: Status is followers only + if ($statusTag->visibility === StatusVisibility::FOLLOWERS && $user !== null) { return $user->follows->contains('id', $statusTag->status->user_id); } - // Case 5: Status is unlisted - // This isn't checked here. This is done in the query from the (global/private) dashboard. - - // Case 6: Status is public or authenticated - if ($statusTag->visibility === StatusVisibility::PUBLIC || $statusTag->visibility === StatusVisibility::AUTHENTICATED) { - return Response::allow('Status is public or authenticated'); + // Case 6: StatusTag is for authenticated users only + if ($user !== null && $statusTag->visibility === StatusVisibility::AUTHENTICATED) { + return Response::allow('StatusTag is for authenticated users'); } + // Case x: Status is unlisted + // This isn't checked here. This is done in the query from the (global/private) dashboard. + //In any edge case it should be false. Each case should be treated here. return Response::deny('Congratulations! You\'ve found an edge-case!'); } diff --git a/database/factories/StatusTagFactory.php b/database/factories/StatusTagFactory.php index 86827fcda..bf8159d52 100644 --- a/database/factories/StatusTagFactory.php +++ b/database/factories/StatusTagFactory.php @@ -3,6 +3,7 @@ namespace Database\Factories; use App\Enum\StatusTagKey; +use App\Enum\StatusVisibility; use App\Models\Status; use Illuminate\Database\Eloquent\Factories\Factory; @@ -11,17 +12,18 @@ class StatusTagFactory extends Factory public function definition(): array { return [ - 'status_id' => Status::factory(), - 'key' => $this->faker->randomElement([ - StatusTagKey::ROLE->value, - StatusTagKey::SEAT->value, - StatusTagKey::TICKET->value, - StatusTagKey::TRAVEL_CLASS->value, - StatusTagKey::WAGON->value, - StatusTagKey::WAGON_CLASS->value, - StatusTagKey::LOCOMOTIVE_CLASS->value, - ]), - 'value' => $this->faker->word, + 'status_id' => Status::factory(), + 'key' => $this->faker->randomElement([ + StatusTagKey::ROLE->value, + StatusTagKey::SEAT->value, + StatusTagKey::TICKET->value, + StatusTagKey::TRAVEL_CLASS->value, + StatusTagKey::WAGON->value, + StatusTagKey::WAGON_CLASS->value, + StatusTagKey::LOCOMOTIVE_CLASS->value, + ]), + 'value' => $this->faker->word, + 'visibility' => StatusVisibility::PUBLIC->value, ]; } } diff --git a/tests/Feature/Privacy/StatusTagPrivacyTest.php b/tests/Feature/Privacy/StatusTagPrivacyTest.php new file mode 100644 index 000000000..7a1a5d0c0 --- /dev/null +++ b/tests/Feature/Privacy/StatusTagPrivacyTest.php @@ -0,0 +1,64 @@ + StatusVisibility::PUBLIC->value])->create(); + $randomUser = User::factory()->create(); + $this->assertTrue($randomUser->can('view', $statusTag)); + } + + public function testUserViewPrivateStatusTag(): void { + $statusTag = StatusTag::factory(['visibility' => StatusVisibility::PRIVATE])->create(); + $randomUser = User::factory()->create(); + $this->assertFalse($randomUser->can('view', $statusTag)); + } + + public function testUserViewFollowersOnlyStatusTag(): void { + $statusTag = StatusTag::factory(['visibility' => StatusVisibility::FOLLOWERS])->create(); + $randomUser = User::factory()->create(); + $this->assertFalse($randomUser->can('view', $statusTag)); + } + + public function testFollowerViewFollowersOnlyStatusTag(): void { + $user = User::factory()->create(); + $follower = User::factory()->create(); + + $status = Status::factory(['user_id' => $user->id, 'visibility' => StatusVisibility::FOLLOWERS])->create(); + $statusTag = StatusTag::factory([ + 'status_id' => $status->id, + 'visibility' => StatusVisibility::FOLLOWERS, + ])->create(); + + FollowController::createOrRequestFollow($user, $follower); + FollowController::createOrRequestFollow($follower, $user); + $this->assertTrue($follower->refresh()->can('view', $statusTag)); + } + + public function testFollowerCantViewPrivateStatusTag(): void { + $statusTag = StatusTag::factory(['visibility' => StatusVisibility::PRIVATE])->create(); + $user = User::factory()->create(); + $follower = User::factory()->create(); + Follow::factory(['user_id' => $user->id, 'follow_id' => $follower->id])->create(); + $this->assertFalse($follower->can('view', $statusTag)); + } + + public function testOwnerCanViewPrivateStatusTag(): void { + $statusTag = StatusTag::factory(['visibility' => StatusVisibility::PRIVATE])->create(); + $this->assertTrue($statusTag->status->user->can('view', $statusTag)); + } +} From c52aa7b68416f02e936eefd7dbfb0dcf53f5459c Mon Sep 17 00:00:00 2001 From: 0AgentSmith <0AgentSmith@users.noreply.translate.codeberg.org> Date: Fri, 15 Mar 2024 15:49:45 +0000 Subject: [PATCH 14/24] =?UTF-8?q?=F0=9F=8C=90=20Translated=20using=20Webla?= =?UTF-8?q?te=20(English)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 100.0% (760 of 760 strings) Translation: Träwelling/Träwelling Translate-URL: https://translate.codeberg.org/projects/trawelling/traewelling/en/ --- lang/en.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lang/en.json b/lang/en.json index f2fe2f20b..d2b55c3b2 100644 --- a/lang/en.json +++ b/lang/en.json @@ -47,9 +47,9 @@ "user.email.new": "New email address", "user.email.change": "Change email address", "user.email-verify": "Verify your email address", - "user.forgot-password": "Forgot Your Password?", + "user.forgot-password": "Forgot your password?", "user.fresh-link": "A fresh verification link has been sent to your email address.", - "user.header-reset-pw": "Reset Password", + "user.header-reset-pw": "Reset password", "user.login": "Login", "user.login-credentials": "Email address or username", "user.mastodon-instance-url": "Instance URL", From 6ac09ec34170985548348b922527adde996dc2df Mon Sep 17 00:00:00 2001 From: 0AgentSmith <0AgentSmith@users.noreply.translate.codeberg.org> Date: Fri, 15 Mar 2024 16:06:26 +0000 Subject: [PATCH 15/24] =?UTF-8?q?=F0=9F=8C=90=20Translated=20using=20Webla?= =?UTF-8?q?te=20(English)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 100.0% (760 of 760 strings) Translation: Träwelling/Träwelling Translate-URL: https://translate.codeberg.org/projects/trawelling/traewelling/en/ --- lang/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/en.json b/lang/en.json index d2b55c3b2..c6b6fb6b2 100644 --- a/lang/en.json +++ b/lang/en.json @@ -59,7 +59,7 @@ "user.please-check": "Before proceeding, please check your email for a verification link.", "user.register": "Register", "user.remember-me": "Remember me", - "user.reset-pw-link": "Send Password Reset Link", + "user.reset-pw-link": "Send password reset link", "user.username": "Username", "user.profile-picture": "Profile picture of @:username", "user.you": "You", From 57aeb897dd3ba7360e69cb2f303e01ea43917d7a Mon Sep 17 00:00:00 2001 From: Kris Date: Fri, 15 Mar 2024 17:49:39 +0100 Subject: [PATCH 16/24] :bug: fix accepting events when end date is changed manually (#2424) --- resources/views/admin/events/suggestion-create.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/views/admin/events/suggestion-create.blade.php b/resources/views/admin/events/suggestion-create.blade.php index c6ed8601f..c8cbcde6f 100644 --- a/resources/views/admin/events/suggestion-create.blade.php +++ b/resources/views/admin/events/suggestion-create.blade.php @@ -84,7 +84,7 @@ class="form-control" placeholder="{{ __('stationboard.station-placeholder') }}"
From a551fcb549a14cfe27dfa6186b0cf96988ac8af4 Mon Sep 17 00:00:00 2001 From: 0AgentSmith <0AgentSmith@users.noreply.translate.codeberg.org> Date: Fri, 15 Mar 2024 16:19:56 +0000 Subject: [PATCH 17/24] =?UTF-8?q?=F0=9F=8C=90=20Translated=20using=20Webla?= =?UTF-8?q?te=20(German)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 100.0% (760 of 760 strings) Translation: Träwelling/Träwelling Translate-URL: https://translate.codeberg.org/projects/trawelling/traewelling/de/ --- lang/de.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/de.json b/lang/de.json index ed9e55059..04cde789f 100644 --- a/lang/de.json +++ b/lang/de.json @@ -45,7 +45,7 @@ "controller.status.status-not-found": "Status nicht gefunden", "controller.status.create-success": "Status erstellt.", "controller.status.delete-ok": "Status gelöscht.", - "controller.status.email-resend-mail": "Bestätigungslink senden", + "controller.status.email-resend-mail": "Bestätigungslink erneut senden", "welcome": "Willkommen bei Träwelling!", "email.change": "Bitte Aktualisiere deine E-Mail indem du die neue Adresse und dein aktuelles Passwort eingibst. Dir wird dann eine Bestätigungsmail zugesandt, mit der du diese Änderung bestätigen kannst.", "email.validation": "E-Mail Adresse bestätigen", From 00a272a032eb5c124035d28af5f4370e8831f80e Mon Sep 17 00:00:00 2001 From: Kris Date: Fri, 15 Mar 2024 21:05:27 +0100 Subject: [PATCH 18/24] :globe_with_meridians: remove outdated translations (#2425) --- lang/de_by.json | 1 - lang/fr.json | 1 - lang/it.json | 1 - lang/nb_NO.json | 1 - lang/nl.json | 1 - lang/pl.json | 1 - lang/sv.json | 1 - 7 files changed, 7 deletions(-) diff --git a/lang/de_by.json b/lang/de_by.json index 472a9d661..f7e24cb6e 100644 --- a/lang/de_by.json +++ b/lang/de_by.json @@ -517,7 +517,6 @@ "generic.error": "Feahler", "request-time": "agfrogat um :time", "support.privacy": "Dataschutzhinweis", - "checkin.points.earned": "Du kriagsch", "export.error.time": "Du kasch nur Fahrta über an Zeitraum vu maximal 365 Täg exportiera.", "export.error.amount": "Du hosch mehr als 2000 Fahrta agfrogt. Bitte versuach, den Zeitraum einzumschränka.", "notifications.eventSuggestionProcessed.lead": "Dei Feschtlavorschlag :name isch bearbeitet wora.", diff --git a/lang/fr.json b/lang/fr.json index 6a5eaa4d7..6647717fa 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -494,7 +494,6 @@ "christmas-mode.disable": "Désactiver le mode Noël pour cette séance", "merry-christmas": "On te souhaite joyeux Noël !", "sr.dropdown.toggle": "Basculer le menu déroulant", - "checkin.points.earned": "Tu gagnes", "checkin.conflict.question": "Tu veux encore t'enregistrer ? Tu ne vas recevoir aucun point pour ça mais tes statistiques personnelles vont mettre à jour quand même.", "christmas-mode.enable": "Activer le mode Noël pour cette séance", "settings.visibility.hide": "Cacher automatiquement les enregistrements après", diff --git a/lang/it.json b/lang/it.json index 82f9a12e8..00b491087 100644 --- a/lang/it.json +++ b/lang/it.json @@ -466,7 +466,6 @@ "status.visibility.2.detail": "Visibile solo per i follower (approvati)", "stationboard.no-departures": "Siamo spiacenti, al momento non ci sono partenze per questo orario/filtro.", "time-format": "HH:mm", - "checkin.points.earned": "Ottieni", "notifications.userJoinedConnection.notice": "@:username viaggia con :linename da :origin a :destination.|@:username viaggia con la linea :linename da :origin a :destination.", "time-format.with-day": "HH:mm (DD/MM/YYYY)", "checkin.points.could-have": "Avresti potuto ottenere più punti se avessi fatto il check-in più vicino all'orario di partenza effettivo!", diff --git a/lang/nb_NO.json b/lang/nb_NO.json index 321ed508c..d5c06cd72 100644 --- a/lang/nb_NO.json +++ b/lang/nb_NO.json @@ -512,7 +512,6 @@ "generic.why": "Hvorfor?", "menu.legal-notice": "Juridiske opplysninger", "menu.settings.followings": "jeg følger", - "checkin.points.earned": "Du får", "checkin.points.could-have": "Du kunne ha fått flere poeng hvis du sjekket inn nærmere det faktiske avgangstidspunktet!", "merry-christmas": "Vi ønsker deg en riktig god jul!", "overlapping-checkin.description2": "Vil du tvinge innsjekk nå?", diff --git a/lang/nl.json b/lang/nl.json index 54deb6314..2cdc88c55 100644 --- a/lang/nl.json +++ b/lang/nl.json @@ -501,7 +501,6 @@ "stats.stations": "Stationskaart", "time.years": "jaar", "generic.why": "Waarom?", - "checkin.points.earned": "Je ontvangt", "warning-alternative-station": "Je staat op het punt om in te checken voor vertrek op station :newStation, omdat het dichtbij :searchedStation is.", "stationboard.to": "naar", "status.visibility.0.detail": "Zichtbaar voor iedereen op het dashboard, bij evenementen, etc.", diff --git a/lang/pl.json b/lang/pl.json index 722dc2049..c13251b2c 100644 --- a/lang/pl.json +++ b/lang/pl.json @@ -445,7 +445,6 @@ "support": "Wsparcie", "generic.error": "Błąd", "support.privacy": "Polityka prywatności", - "checkin.points.earned": "Zyskano", "christmas-mode": "Tryb świąteczny", "other": "Inne", "exit": "Wyjście", diff --git a/lang/sv.json b/lang/sv.json index cd42503a9..f449bb73e 100644 --- a/lang/sv.json +++ b/lang/sv.json @@ -489,7 +489,6 @@ "support.answer": "Vi svarar dig så snart som möjligt. Du får svaret till din e-postadress: adress.", "support": "Support", "generic.why": "Varför?", - "checkin.points.earned": "Du får", "checkin.points.could-have": "Du kunde ha fått fler poäng om du checkade in närmare den verkliga avgångstiden!", "checkin.points.forced": "Du fick inga poäng för denna incheckning eftersom du tvingade fram den.", "checkin.conflict.question": "Vill du checka in ändå? Du får inga poäng för detta, men din personliga statistik kommer fortfarande att uppdateras.", From 8d68fc47d0d88e163ae44aa6b575cef1f3c7f410 Mon Sep 17 00:00:00 2001 From: Vistaus Date: Sat, 16 Mar 2024 11:03:55 +0000 Subject: [PATCH 19/24] =?UTF-8?q?=F0=9F=8C=90=20Translated=20using=20Webla?= =?UTF-8?q?te=20(Dutch)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 100.0% (760 of 760 strings) Translation: Träwelling/Träwelling Translate-URL: https://translate.codeberg.org/projects/trawelling/traewelling/nl/ --- lang/nl.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lang/nl.json b/lang/nl.json index 2cdc88c55..38ecbe445 100644 --- a/lang/nl.json +++ b/lang/nl.json @@ -775,5 +775,6 @@ "export.or-choose": "of kies je eigen", "export.all": "Alle velden", "export.nominal": "Nominale gegevens", - "export.nominal-tags": "Nominale gegevens + labels" + "export.nominal-tags": "Nominale gegevens + labels", + "checkin.points.earned": "Je hebt :points punt verdiend|Je hebt :points punten verdiend!" } From 2ebc8b3b273d3fea38ccab734ae6819edf7275e2 Mon Sep 17 00:00:00 2001 From: Kris Date: Sat, 16 Mar 2024 14:14:04 +0100 Subject: [PATCH 20/24] :recycle: migrate departures endpoint to use TRWL-ID (#2410) --- API_CHANGELOG.md | 11 +- .../API/v1/TransportController.php | 104 ++++++++----- app/Http/Controllers/TransportController.php | 7 +- resources/vue/components/Stationboard.vue | 2 +- routes/api.php | 5 +- storage/api-docs/api-docs.json | 19 +-- tests/Feature/APIv1/TransportTest.php | 137 +++++++++++++++++- 7 files changed, 232 insertions(+), 53 deletions(-) diff --git a/API_CHANGELOG.md b/API_CHANGELOG.md index d8634d5d1..e6ec326c0 100644 --- a/API_CHANGELOG.md +++ b/API_CHANGELOG.md @@ -4,6 +4,13 @@ In this we try to keep track of changes to the API. Primarily this should document changes that are not backwards compatible or belongs to already documented endpoints. This is to help you keep track of the changes and to help you update your code accordingly. +## 2024-03-16 + +Replaced `GET /trains/station/{name}/departures` with `GET /station/{id}/departures`. +The old endpoint is marked as deprecated and will be removed after 2024-06. + +Please note, that the ID is the Träwelling internal ID and not the IBNR! + ## 2024-03-10 Replaced `PUT /trains/station/{name}/home` with `PUT /station/{id}/home`. @@ -12,8 +19,10 @@ The old endpoint is marked as deprecated and will be removed after 2024-06. Please note, that the ID is the Träwelling internal ID and not the IBNR! ## 2024-03-01 + > [!WARNING] -> Possibly breaking change: The implementation of next/prev links on user/{username}/statuses endpoint has been changed to adhere to the documentation. +> Possibly breaking change: The implementation of next/prev links on user/{username}/statuses endpoint has been changed +> to adhere to the documentation. ## 2024-01-21 diff --git a/app/Http/Controllers/API/v1/TransportController.php b/app/Http/Controllers/API/v1/TransportController.php index 0b68b6adf..469d3557d 100644 --- a/app/Http/Controllers/API/v1/TransportController.php +++ b/app/Http/Controllers/API/v1/TransportController.php @@ -31,17 +31,49 @@ class TransportController extends Controller { + /** + * @see All slashes (as well as encoded to %2F) in $name need to be replaced, preferrably by a space (%20) + */ + public function getLegacyDepartures(Request $request, string $name): JsonResponse { //TODO: remove endpoint after 2024-06 + $validated = $request->validate([ + 'when' => ['nullable', 'date'], + 'travelType' => ['nullable', new Enum(TravelType::class)], + ]); + + try { + $trainStationboardResponse = TransportBackend::getDepartures( + stationQuery: $name, + when: isset($validated['when']) ? Carbon::parse($validated['when']) : null, + travelType: TravelType::tryFrom($validated['travelType'] ?? null), + localtime: isset($validated['when']) && !preg_match('(\+|Z)', $validated['when']) + ); + } catch (HafasException) { + return $this->sendError(__('messages.exception.generalHafas', [], 'en'), 502); + } catch (ModelNotFoundException) { + return $this->sendError(__('controller.transport.no-station-found', [], 'en')); + } catch (Exception $exception) { + report($exception); + return $this->sendError('An unknown error occurred.', 500); + } + return $this->sendResponse( + data: $trainStationboardResponse['departures'], + additional: ["meta" => ['station' => StationDto::fromModel($trainStationboardResponse['station']), + 'times' => $trainStationboardResponse['times'], + ]] + ); + } + /** * @OA\Get( - * path="/trains/station/{name}/departures", + * path="/station/{id}/departures", * operationId="getDepartures", * tags={"Checkin"}, * summary="Get departures from a station", * description="Get departures from a station", * @OA\Parameter( - * name="name", + * name="id", * in="path", - * description="Name of the station (replace slashes with spaces)", + * description="Träwelling-ID of the station (station needs to be looked up first)", * required=true, * ), * @OA\Parameter( @@ -131,56 +163,58 @@ class TransportController extends Controller * ) * ) * ), - * @OA\Response( - * response=404, - * description="Station not found", - * ), - * @OA\Response( - * response=502, - * description="Error with our data provider", - * ), - * @OA\Response( - * response=422, - * description="Invalid input", - * ), - * @OA\Response(response=401, description="Unauthorized"), - * security={ - * {"passport": {"create-statuses"}}, {"token": {}} - * - * } + * @OA\Response(response=401, description="Unauthorized"), + * @OA\Response(response=404, description="Station not found"), + * @OA\Response(response=422, description="Invalid input"), + * @OA\Response(response=502, description="Error with our data provider"), + * security={{"passport": {"create-statuses"}}, {"token": {}}} * ) * * @param Request $request - * @param string $name + * @param int $stationId * * @return JsonResponse - * @see All slashes (as well as encoded to %2F) in $name need to be replaced, preferrably by a space (%20) */ - public function departures(Request $request, string $name): JsonResponse { + public function getDepartures(Request $request, int $stationId): JsonResponse { $validated = $request->validate([ 'when' => ['nullable', 'date'], 'travelType' => ['nullable', new Enum(TravelType::class)], ]); + $timestamp = isset($validated['when']) ? Carbon::parse($validated['when']) : now(); + $station = Station::findOrFail($stationId); + try { - $trainStationboardResponse = TransportBackend::getDepartures( - stationQuery: $name, - when: isset($validated['when']) ? Carbon::parse($validated['when']) : null, - travelType: TravelType::tryFrom($validated['travelType'] ?? null), - localtime: isset($validated['when']) && !preg_match('(\+|Z)', $validated['when']) + $departures = HafasController::getDepartures( + station: $station, + when: $timestamp, + type: TravelType::tryFrom($validated['travelType'] ?? null), + localtime: isset($validated['when']) && !preg_match('(\+|Z)', $validated['when']) + )->sortBy(function($departure) { + return $departure->when ?? $departure->plannedWhen; + }); + + return $this->sendResponse( + data: $departures->values(), + additional: [ + 'meta' => [ + 'station' => StationDto::fromModel($station), + 'times' => [ + 'now' => $timestamp, + 'prev' => $timestamp->clone()->subMinutes(15), + 'next' => $timestamp->clone()->addMinutes(15) + ], + ] + ] ); } catch (HafasException) { return $this->sendError(__('messages.exception.generalHafas', [], 'en'), 502); } catch (ModelNotFoundException) { return $this->sendError(__('controller.transport.no-station-found', [], 'en')); + } catch (Exception $exception) { + report($exception); + return $this->sendError('An unknown error occurred.', 500); } - - return $this->sendResponse( - data: $trainStationboardResponse['departures'], - additional: ["meta" => ['station' => StationDto::fromModel($trainStationboardResponse['station']), - 'times' => $trainStationboardResponse['times'], - ]] - ); } /** diff --git a/app/Http/Controllers/TransportController.php b/app/Http/Controllers/TransportController.php index 680c23935..5d5477016 100644 --- a/app/Http/Controllers/TransportController.php +++ b/app/Http/Controllers/TransportController.php @@ -5,8 +5,8 @@ use App\Enum\TravelType; use App\Exceptions\HafasException; use App\Http\Controllers\Backend\Transport\StationController; -use App\Models\PolyLine; use App\Models\Checkin; +use App\Models\PolyLine; use App\Models\Station; use App\Models\User; use Carbon\Carbon; @@ -49,10 +49,13 @@ public static function getTrainStationAutocomplete(string $query): Collection { * @param string|int $stationQuery * @param Carbon|null $when * @param TravelType|null $travelType + * @param bool $localtime * * @return array * @throws HafasException - * @api v1 + * @deprecated use HafasController::getDepartures(...) directly instead (-> less overhead) + * + * @api v1 */ #[ArrayShape([ 'station' => Station::class, diff --git a/resources/vue/components/Stationboard.vue b/resources/vue/components/Stationboard.vue index 1e33c5845..57d780d9d 100644 --- a/resources/vue/components/Stationboard.vue +++ b/resources/vue/components/Stationboard.vue @@ -72,7 +72,7 @@ export default { let query = this.stationString.replace(/%2F/, " ").replace(/\//, " "); let travelType = this.travelType ? this.travelType : ""; - fetch(`/api/v1/trains/station/${query}/departures?when=${time}&travelType=${travelType}`) + fetch(`/api/v1/trains/station/${query}/departures?when=${time}&travelType=${travelType}`) //TODO: change this to the new endpoint "/station/{id}/departures" .then((response) => { this.loading = false; this.now = DateTime.now(); diff --git a/routes/api.php b/routes/api.php index b5f379336..720c69f78 100644 --- a/routes/api.php +++ b/routes/api.php @@ -86,7 +86,7 @@ Route::post('trip', [TripController::class, 'createTrip']); Route::post('checkin', [TransportController::class, 'create']); Route::group(['prefix' => 'station'], static function() { - Route::get('{name}/departures', [TransportController::class, 'departures']); + Route::get('{name}/departures', [TransportController::class, 'getLegacyDepartures']); //ToDo: Remove this endpoint after 2024-06 (replaced by id) Route::put('{name}/home', [TransportController::class, 'setHomeLegacy']); //ToDo: Remove this endpoint after 2024-06 (replaced by id) Route::get('nearby', [TransportController::class, 'getNextStationByCoordinates']); Route::get('autocomplete/{query}', [TransportController::class, 'getTrainStationAutocomplete']); @@ -95,7 +95,8 @@ }); Route::prefix('station')->middleware(['scope:write-statuses'])->group(static function() { - Route::put('{id}/home', [TransportController::class, 'setHome'])->whereNumber('id'); + Route::put('/{id}/home', [TransportController::class, 'setHome'])->whereNumber('id'); + Route::get('/{id}/departures', [TransportController::class, 'getDepartures'])->whereNumber('id'); }); Route::group(['prefix' => 'statistics', 'middleware' => 'scope:read-statistics'], static function() { diff --git a/storage/api-docs/api-docs.json b/storage/api-docs/api-docs.json index 45266e2a6..1e5a9078a 100644 --- a/storage/api-docs/api-docs.json +++ b/storage/api-docs/api-docs.json @@ -2969,7 +2969,7 @@ ] } }, - "/trains/station/{name}/departures": { + "/station/{id}/departures": { "get": { "tags": [ "Checkin" @@ -2979,9 +2979,9 @@ "operationId": "getDepartures", "parameters": [ { - "name": "name", + "name": "id", "in": "path", - "description": "Name of the station (replace slashes with spaces)", + "description": "Träwelling-ID of the station (station needs to be looked up first)", "required": true }, { @@ -3141,17 +3141,17 @@ } } }, + "401": { + "description": "Unauthorized" + }, "404": { "description": "Station not found" }, - "502": { - "description": "Error with our data provider" - }, "422": { "description": "Invalid input" }, - "401": { - "description": "Unauthorized" + "502": { + "description": "Error with our data provider" } }, "security": [ @@ -3431,9 +3431,6 @@ }, "500": { "description": "Unknown error" - }, - "502": { - "description": "Error with our data provider" } }, "security": [ diff --git a/tests/Feature/APIv1/TransportTest.php b/tests/Feature/APIv1/TransportTest.php index fc7f40d31..f5717325a 100644 --- a/tests/Feature/APIv1/TransportTest.php +++ b/tests/Feature/APIv1/TransportTest.php @@ -15,7 +15,7 @@ class TransportTest extends ApiTestCase use RefreshDatabase; - public function testGetDeparturesFetchTripAndCheckin(): void { + public function testGetLegacyDeparturesFetchTripAndCheckin(): void { //TODO: remove after 2024-06 (after API Endpoint is removed) Http::fake([ '/locations*' => Http::response([self::FRANKFURT_HBF]), '/stops/8000105/departures*' => Http::response([self::ICE802]), @@ -150,6 +150,141 @@ public function testGetDeparturesFetchTripAndCheckin(): void { $response->assertStatus(409); } + public function testGetDeparturesFetchTripAndCheckin(): void { + Http::fake([ + '/locations*' => Http::response([self::FRANKFURT_HBF]), + '/stops/8000105/departures*' => Http::response([self::ICE802]), + '/trips/' . urlencode(self::TRIP_ID) . '*' => Http::response(self::TRIP_INFO), + ]); + + //Test departures + $station = Station::factory(['ibnr' => self::FRANKFURT_HBF['id'], 'name' => self::FRANKFURT_HBF['name']])->create(); + $timestamp = Date::parse('next monday 8 am'); + $this->actAsApiUserWithAllScopes(); + $response = $this->get( + uri: '/api/v1/station/' . $station->id . '/departures?when=' . urlencode($timestamp->toIso8601String()), + ); + $response->assertOk(); + $response->assertJsonStructure([ + 'data' => [ + '*' => [ + 'tripId', + 'stop', + 'when', + 'plannedWhen', + //and more... + ] + ], + 'meta' => [ + 'station' => [ + 'id', + 'ibnr', + 'name', + 'latitude', + 'longitude', + 'rilIdentifier' + ], + 'times' => [ + 'now', + 'prev', + 'next', + ] + ] + ]); + + $this->assertEquals($station->name, $response->json('meta.station.name')); + $this->assertGreaterThan(0, $response->json('data')); + + $departure = $response->json('data')[0]; + + //Fetch trip with wrong origin / stopover + $response = $this->get( + uri: '/api/v1/trains/trip' + . '?hafasTripId=' . $departure['tripId'] + . '&lineName=' . $departure['line']['name'] + . '&start=' . ($departure['stop']['id'] + 99999), + ); + $response->assertStatus(400); + // Fetch correct trip + $response = $this->get( + uri: '/api/v1/trains/trip' + . '?hafasTripId=' . $departure['tripId'] + . '&lineName=' . $departure['line']['name'] + . '&start=' . $departure['stop']['id'], + ); + $response->assertOk(); + $response->assertJsonStructure([ + 'data' => [ + 'id', + 'category', + 'number', + 'lineName', + 'origin' => [ + 'id', //and more... + ], + 'destination' => [ + 'id', //and more... + ], + 'stopovers' => [ + '*' => [ + 'id', + 'name', + 'arrivalPlanned', + 'arrivalReal', + 'departurePlanned', + 'departureReal', + ] + ] + ] + ]); + $trip = $response->json('data'); + + //Now checkin... + $response = $this->postJson( + uri: '/api/v1/trains/checkin', + data: [ + 'tripId' => $departure['tripId'], + 'lineName' => $departure['line']['name'], + 'start' => $trip['stopovers'][0]['evaIdentifier'], + 'departure' => $trip['stopovers'][0]['departurePlanned'], + 'destination' => $trip['stopovers'][1]['evaIdentifier'], + 'arrival' => $trip['stopovers'][1]['arrivalPlanned'], + 'ibnr' => true, + ], + ); + $response->assertCreated(); + $response->assertJsonStructure([ + 'data' => [ + 'status' => [ + 'id', 'body', 'type', 'user', //and more... + ], + 'points' => [ + 'points', //TODO: should be renamed... this sounds weird duplicated. + 'calculation' => [ + 'base', 'distance', 'factor', 'reason', + ], + 'additional' + ], + 'alsoOnThisConnection', + ] + ]); + + //Do the same thing again! Should be a CheckInCollision + $response = $this->postJson( + uri: '/api/v1/trains/checkin', + data: [ + 'tripId' => $departure['tripId'], + 'lineName' => $departure['line']['name'], + 'start' => $trip['stopovers'][0]['evaIdentifier'], + 'departure' => $trip['stopovers'][0]['departurePlanned'], + 'destination' => $trip['stopovers'][1]['evaIdentifier'], + 'arrival' => $trip['stopovers'][1]['arrivalPlanned'], + 'ibnr' => true, + ], + ); + $response->assertStatus(409); + } + public function testGetStationByCoordinates(): void { Http::fake(["*/stops/nearby*" => Http::response([array_merge( self::HANNOVER_HBF, From b5bef6cc846b3e66d454c96e18c9af2ab67fe488 Mon Sep 17 00:00:00 2001 From: Levin Herr Date: Sat, 16 Mar 2024 14:43:36 +0100 Subject: [PATCH 21/24] =?UTF-8?q?=E2=9C=A8=20Adapt=20vue=20stationboard=20?= =?UTF-8?q?to=20new=20id=20based=20endpoint=20(#2426)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- resources/views/stationboard.blade.php | 6 +++++- resources/vue/components/Stationboard.vue | 22 ++++++++++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/resources/views/stationboard.blade.php b/resources/views/stationboard.blade.php index 9d62d86a1..cac6ba1e5 100644 --- a/resources/views/stationboard.blade.php +++ b/resources/views/stationboard.blade.php @@ -7,7 +7,11 @@
@if(auth()->user()->hasRole('open-beta')) - + @else @include('includes.station-autocomplete')
diff --git a/resources/vue/components/Stationboard.vue b/resources/vue/components/Stationboard.vue index 57d780d9d..fd08c260b 100644 --- a/resources/vue/components/Stationboard.vue +++ b/resources/vue/components/Stationboard.vue @@ -15,6 +15,11 @@ export default { type: String, required: true, default: "Karlsruhe Hbf" + }, + stationId: { + type: Number, + required: true, + default: 0 } }, data() { @@ -27,7 +32,8 @@ export default { selectedTrain: null, selectedDestination: null, loading: false, - stationString: null, + stationName: null, + trwlStationId: null }; }, methods: { @@ -39,7 +45,8 @@ export default { this.$refs.modal.show(); }, updateStation(station) { - this.stationString = station.name; + this.stationName = station.name; + this.trwlStationId = station.id; this.data = []; this.fetchData(); }, @@ -69,10 +76,9 @@ export default { time = this.fetchTime.minus({minutes: 5}).toString(); } - let query = this.stationString.replace(/%2F/, " ").replace(/\//, " "); let travelType = this.travelType ? this.travelType : ""; - fetch(`/api/v1/trains/station/${query}/departures?when=${time}&travelType=${travelType}`) //TODO: change this to the new endpoint "/station/{id}/departures" + fetch(`/api/v1/station/${this.trwlStationId}/departures?when=${time}&travelType=${travelType}`) .then((response) => { this.loading = false; this.now = DateTime.now(); @@ -105,7 +111,11 @@ export default { }, mounted() { this.fetchTime = DateTime.now().setZone("UTC"); - this.stationString = this.$props.station; + + // These are needed for the communication with blade templates + this.stationName = this.$props.station; + this.trwlStationId = this.$props.stationId; + this.fetchData(); }, computed: { @@ -123,7 +133,7 @@ export default { v-on:update:time="updateTime" v-on:update:station="updateStation" v-on:update:travel-type="updateTravelType" - :station="{name: stationString}" + :station="{name: stationName}" :time="now" />
From 5dde97eadb3d95d9645b7e18bfc791948015b058 Mon Sep 17 00:00:00 2001 From: 0AgentSmith <0AgentSmith@users.noreply.translate.codeberg.org> Date: Sat, 16 Mar 2024 15:55:12 +0000 Subject: [PATCH 22/24] =?UTF-8?q?=F0=9F=8C=90=20Translated=20using=20Webla?= =?UTF-8?q?te=20(English)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 99.8% (759 of 760 strings) Translation: Träwelling/Träwelling Translate-URL: https://translate.codeberg.org/projects/trawelling/traewelling/en/ --- lang/en.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lang/en.json b/lang/en.json index c6b6fb6b2..00a541ee1 100644 --- a/lang/en.json +++ b/lang/en.json @@ -92,7 +92,7 @@ "controller.status.like-not-found": "Like not found.", "controller.status.like-ok": "Like created!", "controller.status.not-permitted": "You're not permitted to do this.", - "controller.social.already-connected-error": "This Account is already connected to another user.", + "controller.social.already-connected-error": "This account is already connected to another user.", "controller.social.create-error": "There has been an error creating your account.", "controller.social.delete-never-connected": "Your user does not have a Social Login provider.", "controller.social.delete-set-email": "Before you delete an SSO provider, you need to set an email address so that you don't get locked out.", @@ -100,7 +100,7 @@ "controller.social.deleted": "Social Login Provider has been deleted.", "controller.user.follow-404": "This follow does not exist.", "controller.user.follow-delete-not-permitted": "This action is not permitted.", - "controller.user.follow-destroyed": "This follow has been destroyed.", + "controller.user.follow-destroyed": "This follow has been undone.", "controller.user.follow-error": "This follow did not work.", "controller.user.follow-ok": "Followed user.", "controller.user.follow-request-already-exists": "You've already requested to follow this person.", @@ -137,7 +137,7 @@ "events.hashtag": "Hashtag", "events.website": "Website", "events.host": "Organizer", - "events.url": "External Website", + "events.url": "External website", "events.begin": "Begins at", "events.end": "Ends at", "events.closestStation": "Closest Träwelling Station", @@ -228,7 +228,7 @@ "modals.editStatus-label": "Edit the status", "modals.editStatus-title": "Edit status", "modals.deleteEvent-title": "Delete event", - "modals.deleteEvent-body": "Are you sure, that you want to delete the event :name? Check-ins en route will be updated to NULL.", + "modals.deleteEvent-body": "Are you sure that you want to delete the event :name? Check-ins en route will be updated to NULL.", "modals.setHome-title": "Set home station", "modals.setHome-body": "Do you want to set :stationName as your home station?", "modals.tags.new": "New tag", From 218f4d201969cb2744f8b15007fe37c57dfd881e Mon Sep 17 00:00:00 2001 From: 0AgentSmith <0AgentSmith@users.noreply.translate.codeberg.org> Date: Sat, 16 Mar 2024 15:45:33 +0000 Subject: [PATCH 23/24] =?UTF-8?q?=F0=9F=8C=90=20Translated=20using=20Webla?= =?UTF-8?q?te=20(German)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently translated at 99.6% (757 of 760 strings) Translation: Träwelling/Träwelling Translate-URL: https://translate.codeberg.org/projects/trawelling/traewelling/de/ --- lang/de.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lang/de.json b/lang/de.json index 04cde789f..ff6bf8220 100644 --- a/lang/de.json +++ b/lang/de.json @@ -70,12 +70,12 @@ "controller.transport.social-post-with-event": "Ich bin gerade in :lineName nach :destination für #:hashtag! #NowTräwelling | Ich bin gerade in Linie :lineName nach #destination für #:hashtag! #NowTräwelling ", "controller.transport.social-post-for": "für #:hashtag", "controller.transport.no-station-found": "Für diese Suche wurde keine Haltestelle gefunden.", - "controller.user.follow-404": "Dieser Follow existierte gar nicht.", + "controller.user.follow-404": "Dieser Follow existiert nicht.", "controller.user.follow-delete-not-permitted": "Diese Aktion ist nicht erlaubt.", "controller.user.follow-destroyed": "Du folgst dieser Person nicht mehr.", "controller.user.follow-error": "Du kannst dieser Person nicht folgen.", "controller.user.follow-ok": "Benutzer gefolgt.", - "controller.user.follow-request-already-exists": "Du hast dieser Person bereits angefragt zu folgen.", + "controller.user.follow-request-already-exists": "Du hast bereits angefragt, dieser Person zu folgen.", "controller.user.follow-request-ok": "Folgen angefragt.", "controller.user.password-changed-ok": "Passwort geändert.", "controller.user.password-wrong": "Altes Passwort ist falsch.", @@ -233,12 +233,12 @@ "messages.cookie-notice": "Wir nutzen Cookies für unser Login-System.", "messages.cookie-notice-button": "Okay", "messages.cookie-notice-learn": "Mehr erfahren", - "messages.exception.general": "Es ist ein Fehler aufgetreten. Bitte probiere es erneut.", + "messages.exception.general": "Ein unbekannter Fehler ist aufgetreten. Bitte probiere es erneut.", "messages.exception.reference": "Fehler-Referenz: :reference", "messages.exception.generalHafas": "Es gab ein Problem mit der Schnittstelle zu den Fahrplandaten. Bitte probiere es erneut.", "messages.exception.hafas.502": "Der Fahrplan konnte nicht geladen werden. Bitte versuche es gleich erneut.", "messages.exception.already-checkedin": "Es existiert bereits ein Checkin in dieser Verbindung.", - "messages.exception.maybe-too-fast": "Vielleicht hast du deine Anfrage ausversehen mehrfach abgesendet?", + "messages.exception.maybe-too-fast": "Vielleicht hast du deine Anfrage aus Versehen mehrfach abgesendet?", "messages.account.please-confirm": "Bitte gib :delete ein um die Löschung zu bestätigen.", "modals.delete-confirm": "Löschen", "modals.deleteStatus-title": "Möchtest Du wirklich diesen Status löschen?", From 914f8a3bd73e61f38f1a4df24e54d3912ea46b6f Mon Sep 17 00:00:00 2001 From: Fabian Reinders Date: Sat, 16 Mar 2024 20:45:46 +0100 Subject: [PATCH 24/24] =?UTF-8?q?=F0=9F=90=9B=20Fix=20issues=20related=20t?= =?UTF-8?q?o=20checking=20into=20international=20trains=20(#2407)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/Http/Controllers/FrontendTransportController.php | 5 +++-- resources/vue/components/CheckinInterface.vue | 2 +- resources/vue/components/Stationboard.vue | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/Http/Controllers/FrontendTransportController.php b/app/Http/Controllers/FrontendTransportController.php index bfe1a310d..07d466ad1 100644 --- a/app/Http/Controllers/FrontendTransportController.php +++ b/app/Http/Controllers/FrontendTransportController.php @@ -127,6 +127,7 @@ public function TrainTrip(Request $request): Renderable|RedirectResponse { // in long term to support multiple data providers we only support IDs here - no IBNRs. $startStation = Station::findOrFail($validated['start']); } + $departure = Carbon::parse($validated['departure']); $trip = TrainCheckinController::getHafasTrip( @@ -137,9 +138,9 @@ public function TrainTrip(Request $request): Renderable|RedirectResponse { $encounteredStart = false; $stopovers = $trip->stopovers - ->filter(function(Stopover $stopover) use ($departure, $startStation, &$encounteredStart): bool { + ->filter(function(Stopover $stopover) use ($startStation, &$encounteredStart): bool { if (!$encounteredStart) { // this assumes stopovers being ordered correctly - $encounteredStart = $stopover->departure_planned == $departure && $stopover->station->is($startStation); + $encounteredStart = $stopover->station->is($startStation); return false; } return true; diff --git a/resources/vue/components/CheckinInterface.vue b/resources/vue/components/CheckinInterface.vue index a799bc258..01d6a62fb 100644 --- a/resources/vue/components/CheckinInterface.vue +++ b/resources/vue/components/CheckinInterface.vue @@ -38,7 +38,7 @@ export default { business: this.business, ibnr: true, tripId: this.selectedTrain.tripId, - lineName: this.selectedTrain.line.name, + lineName: this.selectedTrain.line.name ?? this.selectedTrain.line.fahrtNr, start: this.selectedTrain.stop.id, destination: this.selectedDestination.evaIdentifier, departure: DateTime.fromISO(this.selectedTrain.plannedWhen).setZone("UTC").toISO(), diff --git a/resources/vue/components/Stationboard.vue b/resources/vue/components/Stationboard.vue index fd08c260b..ef121b3b3 100644 --- a/resources/vue/components/Stationboard.vue +++ b/resources/vue/components/Stationboard.vue @@ -145,7 +145,7 @@ export default {
- +