diff --git a/app/Console/Commands/ImportMRES.php b/app/Console/Commands/ImportMRES.php new file mode 100644 index 000000000..193ca3c28 --- /dev/null +++ b/app/Console/Commands/ImportMRES.php @@ -0,0 +1,78 @@ +argument('input'); + + $inputFile = fopen($input, 'r'); + + // First three lines are headers. + fgetcsv($inputFile); + fgetcsv($inputFile); + fgetcsv($inputFile); + + while (!feof($inputFile)) + { + $fields = fgetcsv($inputFile); + + if ($fields) { + $fields = array_map("utf8_encode", $fields); + $groupname = trim($fields[0]); + $email = trim($fields[1]); + + if ($email) { + // Find group with name + $group = \App\Group::where('name', 'like', $groupname)->first(); + + if ($group) { + $this->info("Set email for $groupname to $email"); + $group->email = $email; + $group->save(); + } else { + $this->error("No group found for $groupname"); + } + } + } + } + } +} diff --git a/app/Group.php b/app/Group.php index 1cb8c8d8f..1c1d1ef3c 100644 --- a/app/Group.php +++ b/app/Group.php @@ -46,6 +46,7 @@ class Group extends Model implements Auditable 'timezone', 'phone', 'network_data', + 'email', ]; protected $appends = ['ShareableLink', 'auto_approve']; diff --git a/app/Http/Controllers/API/GroupController.php b/app/Http/Controllers/API/GroupController.php index f7bc88f70..86a3f665d 100644 --- a/app/Http/Controllers/API/GroupController.php +++ b/app/Http/Controllers/API/GroupController.php @@ -555,6 +555,10 @@ public function moderateGroupsv2(Request $request) { * ref="#/components/schemas/Group/properties/website" * ), * @OA\Property( + * property="email", + * ref="#/components/schemas/Group/properties/email" + * ), + * @OA\Property( * property="description", * ref="#/components/schemas/Group/properties/description", * ), @@ -592,7 +596,7 @@ public function createGroupv2(Request $request) { $user = $this->getUser(); $user->convertToHost(); - list($name, $area, $postcode, $location, $phone, $website, $description, $timezone, $latitude, $longitude, $country, $network_data) = $this->validateGroupParams( + list($name, $area, $postcode, $location, $phone, $website, $description, $timezone, $latitude, $longitude, $country, $network_data, $email) = $this->validateGroupParams( $request, true ); @@ -611,6 +615,7 @@ public function createGroupv2(Request $request) { 'timezone' => $timezone, 'phone' => $phone, 'network_data' => $network_data, + 'email' => $email, ]; $group = Group::create($data); @@ -692,6 +697,10 @@ public function createGroupv2(Request $request) { * ref="#/components/schemas/Group/properties/website" * ), * @OA\Property( + * property="email", + * ref="#/components/schemas/Group/properties/email" + * ), + * @OA\Property( * property="description", * ref="#/components/schemas/Group/properties/description", * ), @@ -728,7 +737,7 @@ public function createGroupv2(Request $request) { public function updateGroupv2(Request $request, $idGroup) { $user = $this->getUser(); - list($name, $area, $postcode, $location, $phone, $website, $description, $timezone, $latitude, $longitude, $country, $network_data) = $this->validateGroupParams( + list($name, $area, $postcode, $location, $phone, $website, $description, $timezone, $latitude, $longitude, $country, $network_data, $email) = $this->validateGroupParams( $request, false ); @@ -754,6 +763,7 @@ public function updateGroupv2(Request $request, $idGroup) { 'timezone' => $timezone, 'phone' => $phone, 'network_data' => $network_data, + 'email' => $email, ]; if ($user->hasRole('Administrator') || $user->hasRole('NetworkCoordinator')) { @@ -871,6 +881,7 @@ private function validateGroupParams(Request $request, $create): array { $description = $request->input('description'); $timezone = $request->input('timezone'); $network_data = $request->input('network_data'); + $email = $request->input('email'); $latitude = null; $longitude = null; @@ -910,6 +921,7 @@ private function validateGroupParams(Request $request, $create): array { $longitude, $country_code, $network_data, + $email, ); } } diff --git a/app/Http/Resources/Group.php b/app/Http/Resources/Group.php index 09a44a8f7..93619503a 100644 --- a/app/Http/Resources/Group.php +++ b/app/Http/Resources/Group.php @@ -54,6 +54,13 @@ * example="https://therestartproject.org" * ), * @OA\Property( + * property="email", + * title="email", + * description="Any email contact address for the group.", + * format="string", + * example="info@therestartproject.org" + * ), + * @OA\Property( * property="description", * title="description", * description="HTML description of the group.", @@ -280,7 +287,8 @@ public function toArray($request) 'timezone' => $this->timezone, 'approved' => $this->approved ? true : false, 'network_data' => gettype($this->network_data) == 'string' ? json_decode($this->network_data, true) : $this->network_data, - 'full' => true + 'full' => true, + 'email' => $this->email, ]; $ret['hosts'] = $this->resource->all_confirmed_hosts_count; diff --git a/database/migrations/2023_08_14_112539_group_email.php b/database/migrations/2023_08_14_112539_group_email.php new file mode 100644 index 000000000..8fed6d948 --- /dev/null +++ b/database/migrations/2023_08_14_112539_group_email.php @@ -0,0 +1,32 @@ +string('email', 255)->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('groups', function (Blueprint $table) { + $table->dropColumn('email'); + }); + } +}; diff --git a/lang/en/groups.php b/lang/en/groups.php index 6ea751255..d2978e47e 100644 --- a/lang/en/groups.php +++ b/lang/en/groups.php @@ -20,7 +20,9 @@ 'groups_about_group' => 'Tell us about your group', 'groups_website' => 'Your website', 'groups_website_small' => 'Don\'t have a website? Feel free to add a Facebook group or similar', - 'groups_group_small' => 'A couple of examples include \'Restarters Torino\' or \'Nottingham Fixers\'', + 'groups_email' => 'Email address', + 'groups_email_small' => 'A public contact email address for your group (optional)', + 'groups_group_small' => 'e.g. \'Restarters Torino\' or \'Nottingham Fixers\'', 'groups_location' => 'Location', 'location' => 'Group location', 'area' => 'Area', diff --git a/lang/fr-BE/groups.php b/lang/fr-BE/groups.php index 6120fa2a7..ddc0b126b 100644 --- a/lang/fr-BE/groups.php +++ b/lang/fr-BE/groups.php @@ -16,7 +16,9 @@ 'groups_about_group' => 'Parlez-nous de votre Repair Café', 'groups_website' => 'Votre site web', 'groups_website_small' => 'Vous n\'avez pas de site web? Vous pouvez aussi ajouter une page Facebook ou autre', - 'groups_group_small' => 'Quelques exemples tels que \'Restarters Torino\' ou \'Réparateurs de Paris\'', + 'groups_email' => 'Adresse électronique', + 'groups_email_small' => 'Une adresse électronique de contact public pour votre Repair Café (facultatif)', + 'groups_group_small' => 'Par exemple \'Restarters Torino\' ou \'Réparateurs de Paris\'', 'groups_location' => 'Lieu', 'location' => 'Localisation du Repair Café', 'groups_approval_text' => 'L\'ajout de repair cafés doit être approuvé par un administrateur', diff --git a/lang/fr/groups.php b/lang/fr/groups.php index 37c4bbf3e..9fc5deaab 100644 --- a/lang/fr/groups.php +++ b/lang/fr/groups.php @@ -16,7 +16,9 @@ 'groups_about_group' => 'Parlez-nous de votre Repair Café', 'groups_website' => 'Votre site web', 'groups_website_small' => 'Vous n\'avez pas de site web? Vous pouvez aussi ajouter une page Facebook ou autre', - 'groups_group_small' => 'Quelques exemples tels que \'Restarters Torino\' ou \'Réparateurs de Paris\'', + 'groups_email' => 'Adresse électronique', + 'groups_email_small' => 'Une adresse électronique de contact public pour votre Repair Café (facultatif)', + 'groups_group_small' => 'Par exemple \'Restarters Torino\' ou \'Réparateurs de Paris\'', 'groups_location' => 'Lieu', 'location' => 'Localisation du Repair Café', 'groups_approval_text' => 'L\'ajout de repair cafés doit être approuvé par un administrateur', diff --git a/resources/js/components/GroupAddEdit.vue b/resources/js/components/GroupAddEdit.vue index 53de255c5..7245cb040 100644 --- a/resources/js/components/GroupAddEdit.vue +++ b/resources/js/components/GroupAddEdit.vue @@ -25,6 +25,11 @@ :website.sync="website" :has-error="$v.website.$error" ref="website"/> +
@@ -170,10 +175,11 @@ import group from '../mixins/group' import auth from '../mixins/auth' import RichTextEditor from './RichTextEditor' -import { required, url, helpers } from 'vuelidate/lib/validators' +import { required, url, email, helpers } from 'vuelidate/lib/validators' import validationHelpers from '../mixins/validationHelpers' import GroupName from './GroupName' import GroupWebsite from './GroupWebsite' +import GroupEmail from './GroupEmail' import GroupLocation from './GroupLocation' import GroupLocationMap from './GroupLocationMap' import GroupTimeZone from './GroupTimeZone' @@ -192,6 +198,7 @@ export default { RichTextEditor, GroupName, GroupWebsite, + GroupEmail, GroupLocation, GroupLocationMap, GroupPhone, @@ -221,6 +228,7 @@ export default { location: null, phone: null, website: null, + email: null, timezone: null, timezoneValid: true, description: null, @@ -255,6 +263,9 @@ export default { }, website: { url + }, + email: { + email } }, computed: { @@ -328,6 +339,7 @@ export default { this.area = group.location.area this.phone = group.phone this.website = group.website + this.email = group.email this.timezone = group.timezone this.description = group.description this.lat = parseFloat(group.location.lat) @@ -368,6 +380,7 @@ export default { const id = await this.$store.dispatch('groups/create', { name: this.name, website: this.website, + email: this.email, description: this.description, location: this.location, postcode: this.postcode, @@ -392,6 +405,7 @@ export default { id: this.idgroups, name: this.name, website: this.website, + email: this.email, description: this.description, location: this.location, postcode: this.postcode, @@ -436,12 +450,12 @@ export default { .layout { display: grid; - grid-column-gap: 40px; grid-template-columns: 1fr; @include media-breakpoint-up(lg) { - grid-template-columns: 2fr 1.5fr 1fr; + grid-template-columns: 1fr 1fr; + grid-column-gap: 20px; } .group-name { @@ -452,77 +466,64 @@ export default { .group-website { grid-row: 2 / 3; grid-column: 1 / 2; - margin-right: 2px; } - .group-description { + .group-email { grid-row: 3 / 4; grid-column: 1 / 2; - - @include media-breakpoint-up(lg) { - grid-row: 3 / 7; - } } - .group-location { + .group-phone { grid-row: 4 / 5; grid-column: 1 / 2; - - ::v-deep(.btn) { - font-size: 16px; - } - - @include media-breakpoint-up(lg) { - grid-row: 1 / 2; - grid-column: 2 / 3; - } } - .group-locationmap { + .group-description { grid-row: 5 / 6; grid-column: 1 / 2; - - @include media-breakpoint-up(lg) { - grid-row: 1 / 4; - grid-column: 3 / 4; - } } - .group-timezone { + .group-image { grid-row: 6 / 7; grid-column: 1 / 2; - margin-right: 2px; + } + + .group-location { + grid-row: 7 / 8; + grid-column: 1 / 2; + + ::v-deep(.btn) { + font-size: 16px; + } @include media-breakpoint-up(lg) { - grid-row: 2 / 3; + grid-row: 1 / 4; grid-column: 2 / 3; } } - .group-phone { - grid-row: 7 / 8; + .group-locationmap { + grid-row: 8 / 9; grid-column: 1 / 2; - margin-right: 2px; @include media-breakpoint-up(lg) { - grid-row: 3 / 4; + grid-row: 3 / 5; grid-column: 2 / 3; } } - .group-image { - grid-row: 8 / 9; + .group-timezone { + grid-row: 9 / 10; grid-column: 1 / 2; - margin-right: 2px; @include media-breakpoint-up(lg) { - grid-row: 4 / 5; + grid-row: 5 / 6; grid-column: 2 / 3; } } .group-admin { - grid-row: 9 / 10; + grid-row: 10 / 11; grid-column: 1 / 2; ::v-deep(.btn) { @@ -530,13 +531,13 @@ export default { } @include media-breakpoint-up(lg) { - grid-row: 5 / 6; - grid-column: 2 / 4; + grid-row: 7 / 8; + grid-column: 1 / 3; } } .group-buttons { - grid-row: 9 / 10; + grid-row: 11 / 12; grid-column: 1 / 2; ::v-deep(.btn) { @@ -544,8 +545,8 @@ export default { } @include media-breakpoint-up(lg) { - grid-row: 7 / 8; - grid-column: 1 / 4; + grid-row: 8 / 9; + grid-column: 1 / 3; } } } diff --git a/resources/js/components/GroupDescription.vue b/resources/js/components/GroupDescription.vue index 6804a7a07..d1c4562f9 100644 --- a/resources/js/components/GroupDescription.vue +++ b/resources/js/components/GroupDescription.vue @@ -17,6 +17,14 @@ {{ group.phone }}

+
+
+ +
+
+ {{ group.email }} +
+
diff --git a/resources/js/components/GroupEmail.vue b/resources/js/components/GroupEmail.vue new file mode 100644 index 000000000..eb9e5b421 --- /dev/null +++ b/resources/js/components/GroupEmail.vue @@ -0,0 +1,36 @@ + + \ No newline at end of file diff --git a/tests/Feature/Groups/APIv2GroupTest.php b/tests/Feature/Groups/APIv2GroupTest.php index acb7ba09f..078ccb888 100644 --- a/tests/Feature/Groups/APIv2GroupTest.php +++ b/tests/Feature/Groups/APIv2GroupTest.php @@ -35,7 +35,8 @@ public function testGetGroup($approve) { 'London', 'Some text.', true, - $approve + $approve, + 'info@test.com' ); // Test invalid group id. @@ -52,6 +53,7 @@ public function testGetGroup($approve) { $response->assertSuccessful(); $json = json_decode($response->getContent(), true); $this->assertEquals($idgroups, $json['data']['id']); + $this->assertEquals('info@test.com', $json['data']['email']); $this->assertTrue(array_key_exists('description', $json['data'])); $this->assertTrue(array_key_exists('stats', $json['data'])); $this->assertTrue(array_key_exists('updated_at', $json['data'])); @@ -153,7 +155,8 @@ public function testCreateGroupLoggedOutWithToken() 'name' => 'Test Group', 'location' => 'London', 'description' => 'Some text.', - 'timezone' => 'Europe/Brussels' + 'timezone' => 'Europe/Brussels', + 'email' => 'info@test.com' ] ); @@ -169,6 +172,7 @@ public function testCreateGroupLoggedOutWithToken() $this->assertEquals('Some text.', $group->free_text); $this->assertStringContainsString('.jpg', $group->groupImage->image->path); $this->assertEquals('Europe/Brussels', $group->timezone); + $this->assertEquals('info@test.com', $group->email); // Group should now appear in the list of groups. $response = $this->get('/api/v2/groups/names'); diff --git a/tests/Feature/Groups/GroupEditTest.php b/tests/Feature/Groups/GroupEditTest.php index 9196c8773..eba23f12c 100644 --- a/tests/Feature/Groups/GroupEditTest.php +++ b/tests/Feature/Groups/GroupEditTest.php @@ -155,4 +155,38 @@ public function can_edit_timezone() { self::assertTrue($found); } + + /** @test */ + public function edit_email() + { + $this->withoutExceptionHandling(); + + $group = Group::factory()->create(); + + $host = User::factory()->host()->create(); + $group->addVolunteer($host); + $group->makeMemberAHost($host); + + $this->actingAs($host); + + $response = $this->get('/group/edit/' . $group->idgroups); + $response->assertStatus(200); + + $response = $this->patch('/api/v2/groups/' . $group->idgroups, [ + 'description' => 'Test', + 'location' => 'London', + 'name' => 'Test', + 'website' => 'https://therestartproject.org', + 'free_text' => 'HQ', + 'network_data' => [ + 'no_dummy' => 'no_dummy' + ], + 'email' => 'info@test.com' + ]); + + $response->assertSuccessful(); + + $group->refresh(); + $this->assertEquals('info@test.com', $group->email); + } } diff --git a/tests/TestCase.php b/tests/TestCase.php index 4712dcdc4..f132046d4 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -157,7 +157,7 @@ public function loginAsTestUser($role = Role::RESTARTER) return Auth::user(); } - public function createGroup($name = 'Test Group', $website = 'https://therestartproject.org', $location = 'London', $text = 'Some text.', $assert = true, $approve = true) + public function createGroup($name = 'Test Group', $website = 'https://therestartproject.org', $location = 'London', $text = 'Some text.', $assert = true, $approve = true, $email = null) { $idgroups = null; @@ -175,7 +175,8 @@ public function createGroup($name = 'Test Group', $website = 'https://therestart 'timezone' => 'Europe/London', 'network_data' => [ 'dummy' => 'dummy', - ] + ], + 'email' => $email, ]); if ($assert) {