diff --git a/app-modules/api/composer.json b/app-modules/api/composer.json new file mode 100644 index 00000000..265c06a1 --- /dev/null +++ b/app-modules/api/composer.json @@ -0,0 +1,24 @@ +{ + "name": "hack-greenville/api", + "description": "", + "type": "library", + "version": "1.0", + "license": "proprietary", + "require": {}, + "autoload": { + "psr-4": { + "HackGreenville\\Api\\": "src/", + "HackGreenville\\Api\\Tests\\": "tests/", + "HackGreenville\\Api\\Database\\Factories\\": "database/factories/", + "HackGreenville\\Api\\Database\\Seeders\\": "database/seeders/" + } + }, + "minimum-stability": "stable", + "extra": { + "laravel": { + "providers": [ + "HackGreenville\\Api\\Providers\\ApiServiceProvider" + ] + } + } +} diff --git a/app-modules/api/routes/api-routes.php b/app-modules/api/routes/api-routes.php new file mode 100644 index 00000000..22dd4b08 --- /dev/null +++ b/app-modules/api/routes/api-routes.php @@ -0,0 +1,10 @@ +group(function () { + Route::get('v0/events', EventApiV0Controller::class)->name('api.v0.events.index'); + Route::get('v0/orgs', OrgsApiV0Controller::class)->name('api.v0.orgs.index'); + }); diff --git a/app-modules/api/src/Http/Controllers/EventApiV0Controller.php b/app-modules/api/src/Http/Controllers/EventApiV0Controller.php new file mode 100644 index 00000000..df9b56df --- /dev/null +++ b/app-modules/api/src/Http/Controllers/EventApiV0Controller.php @@ -0,0 +1,16 @@ +latest()->get() + ); + } +} diff --git a/app-modules/api/src/Http/Controllers/OrgsApiV0Controller.php b/app-modules/api/src/Http/Controllers/OrgsApiV0Controller.php new file mode 100644 index 00000000..fa055f16 --- /dev/null +++ b/app-modules/api/src/Http/Controllers/OrgsApiV0Controller.php @@ -0,0 +1,17 @@ +collection->toArray(); + } + + public function paginationInformation($request, $paginated, $default) + { + return []; + } +} diff --git a/app-modules/api/src/Resources/Events/V0/EventResource.php b/app-modules/api/src/Resources/Events/V0/EventResource.php new file mode 100644 index 00000000..21109072 --- /dev/null +++ b/app-modules/api/src/Resources/Events/V0/EventResource.php @@ -0,0 +1,35 @@ + $this->resource->event_name, + 'group_name' => $this->resource->group_name, + 'group_url' => 'TBD', // TODO + 'url' => $this->resource->url, + 'time' => $this->resource->active_at->toISOString(), + 'tags' => '1', // TODO, + 'nid' => '1', // TODO, + 'status' => $this->resource->getStatusAttribute(), + 'rsvp_count' => $this->resource->rsvp_count, + 'description' => $this->resource->description, + 'uuid' => $this->resource->event_uuid, + 'data_as_of' => now()->toISOString(), + 'service_id' => $this->resource->service_id, + 'service' => $this->resource->service, + 'venue' => new VenueResource($this->resource->venue), + 'created_at' => $this->resource->created_at->toISOString(), + ]; + } +} diff --git a/app-modules/api/src/Resources/Events/V0/VenueResource.php b/app-modules/api/src/Resources/Events/V0/VenueResource.php new file mode 100644 index 00000000..e96cc1fa --- /dev/null +++ b/app-modules/api/src/Resources/Events/V0/VenueResource.php @@ -0,0 +1,27 @@ + $this->resource->name, + 'address' => $this->resource->address, + 'city' => $this->resource->city, + 'state' => $this->resource->state->abbr, + 'zip' => $this->resource->zipcode, + 'country' => $this->resource->country, + 'lat' => $this->resource->lat, + 'lon' => $this->resource->lon, + ]; + } +} diff --git a/app-modules/api/src/Resources/Orgs/V0/OrgResource.php b/app-modules/api/src/Resources/Orgs/V0/OrgResource.php new file mode 100644 index 00000000..6627a8ff --- /dev/null +++ b/app-modules/api/src/Resources/Orgs/V0/OrgResource.php @@ -0,0 +1,33 @@ + $this->resource->title, + 'path' => $this->resource->path, + 'changed' => $this->resource->updated_at->toISOString(), + 'field_city' => $this->resource->city, + 'field_event_service' => $this->resource->service, + 'field_events_api_key' => $this->resource->service_api_key, + 'field_focus_area' => $this->resource->focus_area, + 'field_homepage' => $this->resource->home_page, + 'field_event_calendar_homepage' => $this->resource->event_calendar_uri, + 'field_primary_contact_person' => $this->resource->primary_contact_person, + 'field_org_status' => $this->resource->status, + 'field_organization_type' => $this->resource->organization_type, + 'field_year_established' => $this->resource->established_at->year, + 'uuid' => $this->resource->id, + ]; + } +} diff --git a/app-modules/api/src/Resources/Orgs/V0/OrganizationsCollection.php b/app-modules/api/src/Resources/Orgs/V0/OrganizationsCollection.php new file mode 100644 index 00000000..bdf7c500 --- /dev/null +++ b/app-modules/api/src/Resources/Orgs/V0/OrganizationsCollection.php @@ -0,0 +1,21 @@ +collection->toArray(); + } +} diff --git a/app-modules/api/tests/Feature/EventApiV0Test.php b/app-modules/api/tests/Feature/EventApiV0Test.php new file mode 100644 index 00000000..dc964ac7 --- /dev/null +++ b/app-modules/api/tests/Feature/EventApiV0Test.php @@ -0,0 +1,51 @@ +travelTo(now()); + + $event = Event::factory()->create([ + 'cancelled_at' => now(), + ]); + + $this->getJson(route('api.v0.events.index')) + ->assertSessionDoesntHaveErrors() + ->assertExactJson([ + [ + 'event_name' => $event->event_name, + 'group_name' => $event->group_name, + 'group_url' => 'TBD', //FIXME + 'url' => $event->url, + 'time' => $event->active_at->toISOString(), + 'tags' => '1', // TODO, + 'nid' => '1', // TODO, + 'rsvp_count' => $event->rsvp_count, + 'created_at' => $event->created_at->toISOString(), + 'description' => $event->description, + 'uuid' => $event->event_uuid, + 'data_as_of' => now()->toISOString(), + 'status' => 'cancelled', + 'service_id' => $event->service_id, + 'service' => $event->service->value, + 'venue' => [ + 'name' => $event->venue->name, + 'address' => $event->venue->address, + 'city' => $event->venue->city, + 'state' => $event->venue->state->abbr, + 'zip' => $event->venue->zipcode, + 'country' => $event->venue->country, + 'lat' => $event->venue->lat, + 'lon' => $event->venue->lon, + ], + ], + ]); + } +} diff --git a/app-modules/api/tests/Feature/OrganizationsApiV0Test.php b/app-modules/api/tests/Feature/OrganizationsApiV0Test.php new file mode 100644 index 00000000..a28a7c15 --- /dev/null +++ b/app-modules/api/tests/Feature/OrganizationsApiV0Test.php @@ -0,0 +1,42 @@ +travelTo(now()); + + $org = Org::factory()->create(); + + $this->getJson(route('api.v0.orgs.index')) + ->assertSessionDoesntHaveErrors() + ->assertExactJson([ + [ + 'title' => $org->title, + 'path' => $org->path, + 'changed' => $org->updated_at->toISOString(), + 'field_city' => $org->city, + 'field_event_service' => $org->service, + 'field_events_api_key' => $org->service, + 'field_focus_area' => $org->focus_area, + 'field_homepage' => $org->home_page, + 'field_org_status' => $org->status->value, + 'field_primary_contact_person' => $org->primary_contact_person, + 'field_organization_type' => $org->organization_type, + 'field_event_calendar_homepage' => $org->event_calendar_uri, + // 'field_regular_day_of_the_month' => '', // TBD + // 'field_regular_start_time' => '', // TBD + 'field_year_established' => $org->established_at->year, + // 'field_year_inactive' => '', // TBD + 'uuid' => $org->id, + // 'field_org_tags' => '', // TBD + ], + ]); + } +} diff --git a/app-modules/event-importer/composer.json b/app-modules/event-importer/composer.json new file mode 100644 index 00000000..64694281 --- /dev/null +++ b/app-modules/event-importer/composer.json @@ -0,0 +1,24 @@ +{ + "name": "hack-greenville/event-importer", + "description": "", + "type": "library", + "version": "1.0", + "license": "proprietary", + "require": {}, + "autoload": { + "psr-4": { + "HackGreenville\\EventImporter\\": "src/", + "HackGreenville\\EventImporter\\Tests\\": "tests/", + "HackGreenville\\EventImporter\\Database\\Factories\\": "database/factories/", + "HackGreenville\\EventImporter\\Database\\Seeders\\": "database/seeders/" + } + }, + "minimum-stability": "stable", + "extra": { + "laravel": { + "providers": [ + "HackGreenville\\EventImporter\\Providers\\EventImporterServiceProvider" + ] + } + } +} diff --git a/app-modules/event-importer/src/Console/Commands/ImportEventsCommand.php b/app-modules/event-importer/src/Console/Commands/ImportEventsCommand.php new file mode 100644 index 00000000..d6ec59ed --- /dev/null +++ b/app-modules/event-importer/src/Console/Commands/ImportEventsCommand.php @@ -0,0 +1,56 @@ +event_ids = []; + } + + public function query() + { + return Org::query() + ->where('status', OrganizationStatus::Active) + ->whereIn('service', config('event-import-handlers.active_services')) + ->whereNotNull('service') + ->whereNotNull('service_api_key'); + } + + public function handleRow(Org $org) + { + $handler = $org->getEventImporterHandler(); + + do { + $current_page = 1; + + [$last_page, $events] = $handler->getPaginatedData($current_page); + + $this->info("Processing Page <{$current_page}> from {$org->service->name} for {$org->title}"); + + /** @var EventData $event_data */ + foreach ($events as $event_data) { + $this->info("Importing event: {$event_data->name}"); + + $event = ImportEventForOrganization::process($event_data, $org); + + $this->event_ids[] = $event->id; + } + + } while ($current_page++ < $last_page); + } +} diff --git a/app-modules/event-importer/src/Data/EventData.php b/app-modules/event-importer/src/Data/EventData.php new file mode 100644 index 00000000..318b6d63 --- /dev/null +++ b/app-modules/event-importer/src/Data/EventData.php @@ -0,0 +1,33 @@ +venue instanceof VenueData; + } +} diff --git a/app-modules/event-importer/src/Data/VenueData.php b/app-modules/event-importer/src/Data/VenueData.php new file mode 100644 index 00000000..7fb965ac --- /dev/null +++ b/app-modules/event-importer/src/Data/VenueData.php @@ -0,0 +1,47 @@ + $this->name, + 'address' => $this->address, + 'zipcode' => $this->zip, + 'city' => $this->city, + 'country' => $this->country, + 'state_id' => $this->resolveState()->id, + 'lat' => $this->lat, + 'lng' => $this->lon, + ]); + } + + protected function resolveState(): State + { + return State::firstOrCreate([ + 'abbr' => $this->state, + 'name' => $this->state, + ]); + } +} diff --git a/app-modules/event-importer/src/Providers/EventImporterServiceProvider.php b/app-modules/event-importer/src/Providers/EventImporterServiceProvider.php new file mode 100644 index 00000000..c4e4673a --- /dev/null +++ b/app-modules/event-importer/src/Providers/EventImporterServiceProvider.php @@ -0,0 +1,16 @@ +} */ + public function getPaginatedData(int $page): array + { + return [ + $this->page_count, + $this->eventResults($page)->map(fn ($data) => $this->mapIntoEventData($data)), + ]; + } +} diff --git a/app-modules/event-importer/src/Services/EventBriteHandler.php b/app-modules/event-importer/src/Services/EventBriteHandler.php new file mode 100644 index 00000000..970af557 --- /dev/null +++ b/app-modules/event-importer/src/Services/EventBriteHandler.php @@ -0,0 +1,131 @@ + EventServices::EventBrite, + 'service_id' => $data['id'], + + 'name' => $data['name']['text'], + 'description' => $data['description']['text'], + 'url' => $data['url'], + 'starts_at' => Carbon::parse($data['start']['local']), + // Yes "canceled" is misspelled + 'cancelled_at' => 'canceled' === $data['status'] + ? now() + : null, + 'event_type' => match ($data['online_event']) { + true => EventType::Online, + false => EventType::Live, + default => throw new RuntimeException("Unable to determine event type {$data['eventType']}"), + }, + 'venue' => $this->mapIntoVenueData($data), + ]); + } + + protected function mapIntoVenueData(array $data): ?VenueData + { + if ( ! isset($data['venue_id'])) { + return null; + } + + $venue_id = $data['venue_id']; + + // Cache to prevent unnecessary api calls for same venue id + $venue = Cache::remember(__CLASS__ . __FUNCTION__ . $venue_id, now()->addHour(), function () use ($venue_id) { + return $this->client() + ->get("v3/venues/{$venue_id}") + ->object(); + }); + + return VenueData::from([ + 'id' => $venue->id, + 'name' => $venue->name, + 'address' => $venue->address->address_1, + 'city' => $venue->address->city, + 'state' => $venue->address->region, + 'zip' => $venue->address->postal_code, + 'country' => $venue->address->country, + 'lat' => $venue->latitude, + 'lon' => $venue->longitude, + ]); + } + + protected function eventResults(int $page): Collection + { + return $this->client() + ->get("v3/organizers/{$this->org->service_api_key}/events/", [ + 'status' => 'all', + 'order_by' => 'start_desc', + 'start_date.range_start' => now()->subMonths(1)->format('Y-m-d\TH:i:s'), + 'start_date.range_end' => now()->addMonths(3)->format('Y-m-d\TH:i:s'), + ]) + ->collect() + ->tap(function ($data) { + $this->page_count = data_get($data, 'pagination.page_count', 1); + }) + ->only('events') + ->collapse() + ->filter(fn ($data) => Arr::has($data, ['id', 'name', 'description', 'url'])) + ->map(function ($data) { + + // If the event is already over, we no longer need to look into the cancellation status + if (Carbon::parse($data['start']['local'])->isPast()) { + return $data; + } + + // Eventbrite API does not show events which were "cancelled" + // We need to visit the event page, and pull json-ld data from the page. + $response = Http::get($data['url']) + ->body(); + + $schemaReader = SchemaReader::forJsonLd(); + $things = $schemaReader->readHtml($response, $data['url']); + + /** @var SocialEvent|EducationEvent $social_event */ + $social_event = Arr::first($things, fn ($thing) => $thing instanceof SocialEvent || $thing instanceof EducationEvent); + + return [ + ...$data, + 'status' => $social_event->eventStatus->toString() === 'https://schema.org/EventCancelled' + ? 'canceled' + : 'upcoming', + ]; + }); + } + + protected function client() + { + $token = config('services.eventbrite.private_token'); + + if (empty($token)) { + throw new RuntimeException('Missing EventBright Private Token'); + } + + return Http::baseUrl('https://www.eventbriteapi.com/') + ->throw() + ->timeout(180) + ->withQueryParameters([ + 'token' => config('services.eventbrite.private_token'), + ]); + } +} diff --git a/app-modules/event-importer/src/Services/ImportEventForOrganization.php b/app-modules/event-importer/src/Services/ImportEventForOrganization.php new file mode 100644 index 00000000..daddfd0f --- /dev/null +++ b/app-modules/event-importer/src/Services/ImportEventForOrganization.php @@ -0,0 +1,35 @@ +uniqueIdentifier(), + values: [ + 'event_name' => $data->name, + 'group_name' => $org->title, + 'description' => $data->description, + 'rsvp_count' => $data->rsvp, + 'active_at' => $data->starts_at, + 'cancelled_at' => $data->cancelled_at, + 'uri' => $data->url, + 'venue_id' => $data->hasVenue() + ? $data->venue->resolveVenue($data)->id + : null, + 'cache' => [], + 'event_uuid' => $data->uniqueIdentifierHash(), + 'organization_id' => $org->id, + ] + ); + }); + } +} diff --git a/app-modules/event-importer/src/Services/MeetupRestHandler.php b/app-modules/event-importer/src/Services/MeetupRestHandler.php new file mode 100644 index 00000000..400e45fc --- /dev/null +++ b/app-modules/event-importer/src/Services/MeetupRestHandler.php @@ -0,0 +1,62 @@ + $data['id'], + 'name' => $data['name'], + 'description' => $data['description'], + 'url' => $data['link'], + 'starts_at' => Carbon::createFromTimestampMs($data['time']), + 'event_type' => match ($data['eventType']) { + 'ONLINE' => EventType::Online, + 'PHYSICAL' => EventType::Live, + default => throw new RuntimeException("Unable to determine event type {$data['eventType']}"), + }, + 'rsvp' => $data['yes_rsvp_count'], + 'service' => EventServices::MeetupRest, + 'service_id' => $data['id'], + 'venue' => $this->mapIntoVenueData($data), + ]); + } + + protected function mapIntoVenueData(array $data): ?VenueData + { + if ( ! isset($data['venue'])) { + return null; + } + + return VenueData::from([ + 'id' => $data['venue']['id'], + 'name' => $data['venue']['name'], + 'address' => $data['venue']['address_1'], + 'city' => $data['venue']['city'], + 'state' => $data['venue']['state'], + 'zip' => $data['venue']['zip'], + 'country' => $data['venue']['country'], + 'lat' => $data['venue']['lat'], + 'lon' => $data['venue']['lon'], + ]); + } + + protected function eventResults(int $page): Collection + { + return Http::baseUrl('https://api.meetup.com/') + ->get("{$this->org->service_api_key}/events?&sign=true&photo-host=public") + ->collect(); + } +} diff --git a/app-modules/event-importer/tests/Feature/EventBriteTest.php b/app-modules/event-importer/tests/Feature/EventBriteTest.php new file mode 100644 index 00000000..59ba57a6 --- /dev/null +++ b/app-modules/event-importer/tests/Feature/EventBriteTest.php @@ -0,0 +1,71 @@ + 'ABC']); + + Http::fake([ + 'https://www.eventbriteapi.com/v3/organizers/15516951616/events/?token=ABC&status=all&order_by=start_desc&start_date.range_start=2019-12-01T00%3A00%3A00&start_date.range_end=2020-04-01T00%3A00%3A00' => Http::response($this->apiResponse('live-event-in-past.json')), + 'https://www.eventbriteapi.com/v3/venues/21742454?token=ABC' => Http::response($this->apiResponse('live-event-in-past-venue.json')), + ]); + + $organization = Org::factory()->create([ + 'service' => EventServices::EventBrite->value, + 'service_api_key' => '15516951616', + ]); + + $this->artisan(ImportEventsCommand::class); + + $event = Event::query() + ->where([ + 'service' => EventServices::EventBrite->value, + 'service_id' => '39146789100', + ]) + ->firstOrFail(); + + $this->assertEquals('974c9c735567f0160b9a7df25e1837c9', $event->event_uuid); + $this->assertEquals('BSides Greenville 2018', $event->event_name); + $this->assertEquals($organization->title, $event->group_name); + $this->assertStringContainsString('Security BSides is coming to Greenville', $event->description); + + $this->assertEquals(null, $event->rsvp_count); + $this->assertEquals('2018-03-10 08:30:00', $event->active_at->toDateTimeString()); + $this->assertEquals('https://www.eventbrite.com/e/bsides-greenville-2018-tickets-39146789100', $event->url); + $this->assertNull($event->cancelled_at); + + $this->assertEquals('past', $event->status); + + $venue = Venue::query()->where([ + 'name' => 'Fluor Management Center', + "address" => "100 Fluor Daniel Drive", + "city" => "Greenville", + "zipcode" => "29607", + "country" => "US", + "lat" => "34.8442668", + "lng" => "-82.3340627", + ]) + ->firstOrFail(); + + $this->assertEquals('SC', $venue->state->abbr); + } + + protected function apiResponse(string $file): string + { + return file_get_contents(__DIR__ . '/../fixtures/eventbrite/' . $file); + } +} diff --git a/app-modules/event-importer/tests/Feature/MeetupRestTest.php b/app-modules/event-importer/tests/Feature/MeetupRestTest.php new file mode 100644 index 00000000..296c7c83 --- /dev/null +++ b/app-modules/event-importer/tests/Feature/MeetupRestTest.php @@ -0,0 +1,48 @@ + Http::response($this->apiResponse('online-event-in-future.json'), 200), + ]); + + $organization = Org::factory()->create([ + 'service' => EventServices::MeetupRest, + 'service_api_key' => 'code-for-the-carolinas-greenville', + ]); + + $this->artisan(ImportEventsCommand::class); + + $event = Event::query()->where('event_uuid', '11d0362255a5cc64693968c035892576')->firstOrFail(); + + $this->assertEquals('Civic Hacking for Affordable Housing', $event->event_name); + $this->assertEquals($organization->title, $event->group_name); + $this->assertStringContainsString(<<Join volunteers at Code for the Carolinas as we help the [National Zoning Atlas](https://www.zoningatlas.org/) (NZA) team include North Carolina and South Carolina in their nationwide zoning atlas. "The National Zoning Atlas aims to depict key aspects of zoning codes in an online, user-friendly map." The Atlas aims to provide data for better solutions in transportation, environmental, and especially affordable housing policy.

This is a multi-dimensional project grounded in data science with an end-goal of contributing to a nationally influential dataset. Our current work session activities focus on internet search and integrating data from multiple sources. The project as a whole has occasional opportunities for U/X and outreach and opportunities to learn about machine learning and natural language processing through the work of the NZA team. Volunteering on this project will provide excellent exposure to the foundations of civic data science, with a focus on GIS data.

Even if you're new to this project and/or civic tech volunteering, you're welcome to join the meeting. Visitors are always welcome and no technical skills are needed. You can learn more about the project on our [Slack Workspace](https://join.slack.com/t/codeforthecarolinas/shared_invite/zt-1kxuwu05x-3KxOpkOYjAuN5yuOAH8ROg) in the #project-zoning-atlas channel.

Meetings start with brief introductions. New volunteers are offered a personal orientation, often in a breakout room, while experienced volunteers get right to work. By the end of the session, both groups of volunteers are working together. We also contribute to the project through asynchronous work and informal work sessions.

Meetings are on the Jitsi platform and the link is provided before each meeting. If you will be joining on mobile, install the Jitsi app in advance [https://jitsi.org/downloads/](https://jitsi.org/downloads/) .
Learn more at [codeforthecarolinas.org](http://codeforthecarolinas.org/) or reach out to [masked]

Meetings follow the [Open Collective Community Guidelines](https://docs.opencollective.com/help/about/the-open-collective-way/community-guidelines) as a Code of Conduct.

+HTML, $event->description); + + $this->assertEquals(1, $event->rsvp_count); + $this->assertEquals(1702944000, $event->active_at->utc()->unix()); + $this->assertEquals('https://www.meetup.com/code-for-the-carolinas-greenville/events/lvgwftyfcqbxb/', $event->uri); + $this->assertNull($event->cancelled_at); + $this->assertNull($event->venue_id); + $this->assertEquals('upcoming', $event->status); + + } + + protected function apiResponse(string $file): string + { + return file_get_contents(__DIR__ . '/../fixtures/meetup-rest/' . $file); + } +} diff --git a/app-modules/event-importer/tests/fixtures/eventbrite/live-event-in-past-venue.json b/app-modules/event-importer/tests/fixtures/eventbrite/live-event-in-past-venue.json new file mode 100644 index 00000000..d7becfe0 --- /dev/null +++ b/app-modules/event-importer/tests/fixtures/eventbrite/live-event-in-past-venue.json @@ -0,0 +1,25 @@ +{ + "address": { + "address_1": "100 Fluor Daniel Drive", + "address_2": "", + "city": "Greenville", + "region": "SC", + "postal_code": "29607", + "country": "US", + "latitude": "34.8442668", + "longitude": "-82.3340627", + "localized_address_display": "100 Fluor Daniel Drive, Greenville, SC 29607", + "localized_area_display": "Greenville, SC", + "localized_multi_line_address_display": [ + "100 Fluor Daniel Drive", + "Greenville, SC 29607" + ] + }, + "resource_uri": "https://www.eventbriteapi.com/v3/venues/21742454/", + "id": "21742454", + "age_restriction": null, + "capacity": null, + "name": "Fluor Management Center", + "latitude": "34.8442668", + "longitude": "-82.3340627" +} diff --git a/app-modules/event-importer/tests/fixtures/eventbrite/live-event-in-past.json b/app-modules/event-importer/tests/fixtures/eventbrite/live-event-in-past.json new file mode 100644 index 00000000..434fcba9 --- /dev/null +++ b/app-modules/event-importer/tests/fixtures/eventbrite/live-event-in-past.json @@ -0,0 +1,90 @@ +{ + "pagination": { + "object_count": 3, + "page_number": 1, + "page_size": 50, + "page_count": 1, + "has_more_items": false + }, + "events": [ + { + "name": { + "text": "BSides Greenville 2018", + "html": "BSides Greenville 2018" + }, + "description": { + "text": "Security BSides is coming to Greenville?\nThat's right - the BSides Security conference is coming on December 9th to Greenville, SC!\nThis year we are proud to have the BSides Security conference come to Greenville to help further the conversations around cyber security in not only the greater region, but the growing community in Greenville and the surrounding area, in a relaxed and casual environment.\nEach BSides is a community-driven framework for building events for and by information security community members. The goal is to expand the spectrum of conversation beyond the traditional confines of space and time. It creates opportunities for individuals to both present and participate in an intimate atmosphere that encourages collaboration. It is an intense event with discussions, demos, and interaction from participants. It is where conversations for the next-big-thing are happening. \nThis will be an all day event which will kick off with our opening keynote being delivered by Tim Tomes (@LaNMaSteR53), Upstate native and author of the extremely popular recon-ng open source tool (amongst others).\nThe main event page can be found at https://goo.gl/HSKBt4.\n\n", + "html": "

Security BSides is coming to Greenville?
<\/P>\n

That's right - the BSides Security conference is coming on December 9th to Greenville, SC!<\/P>\n

This year we are proud to have the BSides Security conference come to Greenville to help further the conversations around cyber security in not only the greater region, but the growing community in Greenville and the surrounding area, in a relaxed and casual environment.
<\/P>\n

Each BSides is a community-driven framework for building events for and by information security community members. The goal is to expand the spectrum of conversation beyond the traditional confines of space and time. It creates opportunities for individuals to both present and participate in an intimate atmosphere that encourages collaboration. It is an intense event with discussions, demos, and interaction from participants. It is where conversations for the next-big-thing are happening.
<\/P>\n

This will be an all day event which will kick off with our opening keynote being delivered by Tim Tomes (@LaNMaSteR53), Upstate native and author of the extremely popular recon-ng open source tool (amongst others).<\/P>\n

The main event page can be found at https://goo.gl/HSKBt4.<\/P>\n


<\/P>\n


<\/P>" + }, + "url": "https://www.eventbrite.com/e/bsides-greenville-2018-tickets-39146789100", + "start": { + "timezone": "America/New_York", + "local": "2018-03-10T08:30:00", + "utc": "2018-03-10T13:30:00Z" + }, + "end": { + "timezone": "America/New_York", + "local": "2018-03-10T17:30:00", + "utc": "2018-03-10T22:30:00Z" + }, + "organization_id": "220384806859", + "created": "2017-10-21T16:08:38Z", + "changed": "2018-03-15T10:33:56Z", + "published": "2017-10-21T16:11:09Z", + "capacity": null, + "capacity_is_custom": null, + "status": "completed", + "currency": "USD", + "listed": true, + "shareable": true, + "online_event": false, + "tx_time_limit": 480, + "hide_start_date": false, + "hide_end_date": false, + "locale": "en_US", + "is_locked": false, + "privacy_setting": "unlocked", + "is_series": false, + "is_series_parent": false, + "inventory_type": "limited", + "is_reserved_seating": false, + "show_pick_a_seat": false, + "show_seatmap_thumbnail": false, + "show_colors_in_seatmap_thumbnail": false, + "source": "create_2.0", + "is_free": false, + "version": null, + "summary": "Security BSides is coming to Greenville?\nThat's right - the BSides Security conference is coming on December 9th to Greenville, SC!\nThis yea", + "facebook_event_id": null, + "logo_id": "37564000", + "organizer_id": "15516951616", + "venue_id": "21742454", + "category_id": "102", + "subcategory_id": "2999", + "format_id": "1", + "id": "39146789100", + "resource_uri": "https://www.eventbriteapi.com/v3/events/39146789100/", + "is_externally_ticketed": false, + "logo": { + "crop_mask": { + "top_left": { + "x": 134, + "y": 84 + }, + "width": 466, + "height": 233 + }, + "original": { + "url": "https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.com%2Fimages%2F37564000%2F220384806859%2F1%2Foriginal.jpg?auto=format%2Ccompress&q=75&sharp=10&s=bad9a5b5e72324339c9763d215ae59a4", + "width": 721, + "height": 385 + }, + "id": "37564000", + "url": "https://img.evbuc.com/https%3A%2F%2Fcdn.evbuc.com%2Fimages%2F37564000%2F220384806859%2F1%2Foriginal.jpg?h=200&w=450&auto=format%2Ccompress&q=75&sharp=10&rect=134%2C84%2C466%2C233&s=cd3d4667d8f4192392af436d892195f0", + "aspect_ratio": "2", + "edge_color": null, + "edge_color_set": true + } + } + ] +} diff --git a/app-modules/event-importer/tests/fixtures/meetup-rest/online-event-in-future.json b/app-modules/event-importer/tests/fixtures/meetup-rest/online-event-in-future.json new file mode 100644 index 00000000..31ac086d --- /dev/null +++ b/app-modules/event-importer/tests/fixtures/meetup-rest/online-event-in-future.json @@ -0,0 +1,38 @@ +[ + { + "created": 1688863105000, + "duration": 3600000, + "id": "lvgwftyfcqbxb", + "name": "Civic Hacking for Affordable Housing", + "date_in_series_pattern": false, + "status": "upcoming", + "time": 1702944000000, + "local_date": "2023-12-18", + "local_time": "19:00", + "updated": 1688863105000, + "utc_offset": -18000000, + "waitlist_count": 0, + "yes_rsvp_count": 1, + "is_online_event": true, + "eventType": "ONLINE", + "group": { + "created": 1398967208000, + "name": "Code for the Carolinas - Greenville", + "id": 14215742, + "join_mode": "open", + "lat": 34.849998474121094, + "lon": -82.4000015258789, + "urlname": "code-for-the-carolinas-greenville", + "who": "Civic Tech Volunteers", + "localized_location": "Greenville, SC", + "state": "SC", + "country": "us", + "region": "en_US", + "timezone": "US/Eastern" + }, + "link": "https://www.meetup.com/code-for-the-carolinas-greenville/events/lvgwftyfcqbxb/", + "description": "

Join volunteers at Code for the Carolinas as we help the [National Zoning Atlas](https://www.zoningatlas.org/) (NZA) team include North Carolina and South Carolina in their nationwide zoning atlas. \"The National Zoning Atlas aims to depict key aspects of zoning codes in an online, user-friendly map.\" The Atlas aims to provide data for better solutions in transportation, environmental, and especially affordable housing policy.

This is a multi-dimensional project grounded in data science with an end-goal of contributing to a nationally influential dataset. Our current work session activities focus on internet search and integrating data from multiple sources. The project as a whole has occasional opportunities for U/X and outreach and opportunities to learn about machine learning and natural language processing through the work of the NZA team. Volunteering on this project will provide excellent exposure to the foundations of civic data science, with a focus on GIS data.

Even if you're new to this project and/or civic tech volunteering, you're welcome to join the meeting. Visitors are always welcome and no technical skills are needed. You can learn more about the project on our [Slack Workspace](https://join.slack.com/t/codeforthecarolinas/shared_invite/zt-1kxuwu05x-3KxOpkOYjAuN5yuOAH8ROg) in the #project-zoning-atlas channel.

Meetings start with brief introductions. New volunteers are offered a personal orientation, often in a breakout room, while experienced volunteers get right to work. By the end of the session, both groups of volunteers are working together. We also contribute to the project through asynchronous work and informal work sessions.

Meetings are on the Jitsi platform and the link is provided before each meeting. If you will be joining on mobile, install the Jitsi app in advance [https://jitsi.org/downloads/](https://jitsi.org/downloads/) .
Learn more at [codeforthecarolinas.org](http://codeforthecarolinas.org/) or reach out to [masked]

Meetings follow the [Open Collective Community Guidelines](https://docs.opencollective.com/help/about/the-open-collective-way/community-guidelines) as a Code of Conduct.

", + "visibility": "public", + "member_pay_fee": false + } +] diff --git a/app/Console/Commands/PullEventsCommand.php b/app/Console/Commands/PullEventsCommand.php index 6bf8a93c..b9eadb7f 100644 --- a/app/Console/Commands/PullEventsCommand.php +++ b/app/Console/Commands/PullEventsCommand.php @@ -33,7 +33,7 @@ public function handleRow(EventDataTransformer $data) $this->progressSubMessage($data->event_name); Event::updateOrCreate($data->uniqueIdentifier(), [ - 'event_uuid' => $data->uuid, + 'event_uuid' => $data->uniqueIdentifierHash(), 'event_name' => $data->event_name, 'group_name' => $data->group_name, 'description' => $data->description, diff --git a/app/Console/Commands/PullOrgsCommand.php b/app/Console/Commands/PullOrgsCommand.php index e6767d3d..6afa28cd 100644 --- a/app/Console/Commands/PullOrgsCommand.php +++ b/app/Console/Commands/PullOrgsCommand.php @@ -41,8 +41,12 @@ public function handleRow(OrganizationData $data) 'uri' => $data->field_homepage, 'primary_contact_person' => $data->field_primary_contact_person, 'organization_type' => $data->field_organization_type, + 'established_at' => $data->established_at, 'event_calendar_uri' => $data->field_event_calendar_homepage, 'cache' => '', + 'status' => $data->mapStatus(), + 'service' => $data->mapService(), + 'service_api_key' => $data->field_events_api_key, ]); } } diff --git a/app/Data/OrganizationData.php b/app/Data/OrganizationData.php index 5837f446..72d9642c 100644 --- a/app/Data/OrganizationData.php +++ b/app/Data/OrganizationData.php @@ -2,11 +2,19 @@ namespace App\Data; +use App\Enums\EventServices; +use App\Enums\OrganizationStatus; use App\Models\Category; +use Carbon\Carbon; +use RuntimeException; +use Spatie\LaravelData\Attributes\Computed; use Spatie\LaravelData\Data; class OrganizationData extends Data { + #[Computed] + public Carbon $established_at; + public function __construct( public string $title, public string $path, @@ -16,8 +24,12 @@ public function __construct( public string $field_homepage, public string $field_primary_contact_person, public string $field_organization_type, + public string $field_year_established, public string $field_event_calendar_homepage, + public string $field_event_service, + public string $field_events_api_key, ) { + $this->established_at = Carbon::create($field_year_established); } public function isOrganizationInactive(): bool @@ -33,4 +45,25 @@ public function resolveCategory(): Category return Category::firstOrCreate(['label' => $this->field_organization_type]); } + + public function mapStatus() + { + return match ($this->field_org_status) { + 'Inactive' => OrganizationStatus::InActive, + 'Active' => OrganizationStatus::Active, + default => throw new RuntimeException("Invalid status {$this->field_org_status}") + }; + } + + public function mapService() + { + return match ($this->field_event_service) { + 'Meetup.com' => EventServices::MeetupRest, + 'Eventbrite.com' => EventServices::EventBrite, + 'Nvite.com' => EventServices::Nvite, + 'GetTogether.community' => EventServices::GetTogether, + 'Unknown', '' => null, + default => throw new RuntimeException("Invalid organization service {$this->field_event_service}") + }; + } } diff --git a/app/Enums/EventServices.php b/app/Enums/EventServices.php new file mode 100644 index 00000000..e1930370 --- /dev/null +++ b/app/Enums/EventServices.php @@ -0,0 +1,14 @@ + 'json', - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'deleted_at' => 'datetime', - 'active_at' => 'datetime', - 'expire_at' => 'datetime', - ]; - - protected $appends - = [ - 'short_description', - 'title', - 'active_at_ftm', - ]; + protected $casts = [ + 'cache' => 'json', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'active_at' => 'datetime', + 'expire_at' => 'datetime', + 'cancelled_at' => 'datetime', + 'service_id' => 'int', + 'service' => EventServices::class, + ]; + + protected $appends = [ + 'short_description', + 'title', + 'active_at_ftm', + 'status', + ]; public function getUniqueIdentifierAttribute(): bool|string { - $service = $this->service; + $service = $this->service; $service_id = $this->service_id; return json_encode(compact('service', 'service_id')); @@ -83,6 +55,11 @@ public function venue(): BelongsTo return $this->belongsTo(Venue::class); } + public function organization(): BelongsTo + { + return $this->belongsTo(Org::class, 'organization_id'); + } + public function scopeGetActive(Builder $query): Builder { return $query @@ -125,6 +102,7 @@ public function scopeSearch(Builder $query) /** * accessor url to uri + * * @return string */ public function getUrlAttribute(): string @@ -138,12 +116,30 @@ public function getStateAttribute(): string return 'passed'; } - return 'upcoming'; } + public function getStatusAttribute(): string + { + if ($this->cancelled_at) { + return 'cancelled'; + } + + if ($this->active_at->isPast()) { + return 'past'; + } + + if ($this->active_at->isFuture()) { + return 'upcoming'; + } + + throw new RuntimeException('Unable to determine status'); + + } + /** * build out the link that adds this event to the users personal calendar + * * @return string */ public function getGCalUrlAttribute(): string @@ -179,11 +175,6 @@ public function getLocalActiveAtAttribute(): Carbon|string return $this->active_at->tz(config('app.timezone')); } - public function getDescriptionAttribute(): array|string - { - return str_replace('attributes['description']); - } - public function getShortDescriptionAttribute(): string { return str_limit($this->description); diff --git a/app/Models/Org.php b/app/Models/Org.php index 46ecfa7f..0040ca87 100644 --- a/app/Models/Org.php +++ b/app/Models/Org.php @@ -2,38 +2,26 @@ namespace App\Models; +use App\Enums\EventServices; +use App\Enums\OrganizationStatus; +use HackGreenville\EventImporter\Services\Concerns\AbstractEventHandler; use Illuminate\Database\Eloquent\Factories\HasFactory; -use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; /** * @property string uri */ -class Org extends Model +class Org extends BaseModel { use HasFactory; use SoftDeletes; - protected $table = 'orgs'; - - protected $fillable - = [ - 'category_id', - 'title', - 'path', - 'city', - 'focus_area', - 'uri', - 'primary_contact_person', - 'organization_type', - 'event_calendar_uri', - 'cache', - ]; - - protected $casts - = [ - 'cache' => 'json', - ]; + protected $casts = [ + 'cache' => 'json', + 'status' => OrganizationStatus::class, + 'service' => EventServices::class, + 'established_at' => 'datetime', + ]; public function category() { @@ -49,4 +37,13 @@ public function getHomePageAttribute() { return $this->uri ?: $this->path; } + + public function getEventImporterHandler(): AbstractEventHandler + { + /** @var AbstractEventHandler $handler */ + $handler = collect(config('event-import-handlers.handlers')) + ->firstOrFail(fn ($handler, $service) => $this->service->value === $service); + + return new $handler($this); + } } diff --git a/app/Models/Venue.php b/app/Models/Venue.php index 2c005b16..299cd103 100644 --- a/app/Models/Venue.php +++ b/app/Models/Venue.php @@ -17,17 +17,19 @@ class Venue extends Model protected $table = 'venues'; protected $fillable - = [ - 'slug', - 'name', - 'address', - 'zipcode', - 'phone', - 'city', - 'state_id', - 'lat', - 'lng', - ]; + = [ + 'unique_venue_id', + 'slug', + 'name', + 'address', + 'zipcode', + 'phone', + 'city', + 'state_id', + 'country', + 'lat', + 'lng', + ]; public function __toString() { diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index cc1c6a17..e87656a6 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -5,7 +5,6 @@ use App\Contracts\CalendarContract; use App\Http\Clients\GoogleCalendar; use App\Http\Clients\UpstateClient; -use Illuminate\Http\Resources\Json\JsonResource; use Illuminate\Pagination\Paginator; use Illuminate\Support\ServiceProvider; @@ -28,8 +27,6 @@ public function boot() */ public function register() { - JsonResource::withoutWrapping(); - $this->app->singleton( CalendarContract::class, fn () => new GoogleCalendar diff --git a/app/Traits/HasUniqueIdentifier.php b/app/Traits/HasUniqueIdentifier.php new file mode 100644 index 00000000..f9632aef --- /dev/null +++ b/app/Traits/HasUniqueIdentifier.php @@ -0,0 +1,22 @@ + $this->service->value, + 'service_id' => $this->service_id, + ]; + } + + public function uniqueIdentifierHash(): string + { + return hash( + algo: 'md5', + data: implode('', $this->uniqueIdentifier()) + ); + } +} diff --git a/composer.json b/composer.json index 88d66eac..2acf67cb 100644 --- a/composer.json +++ b/composer.json @@ -11,8 +11,12 @@ "require": { "php": "^8.1", "ext-json": "*", + "brick/schema": "^0.1.1", "glhd/conveyor-belt": "*", "guzzlehttp/guzzle": "^7.0.1", + "hack-greenville/api": "*", + "hack-greenville/event-importer": "*", + "internachi/modular": "^2.0", "intervention/image": ">=2.5 <3.0.0", "laravel/framework": "^10.0", "laravel/helpers": "^1.2", @@ -100,5 +104,14 @@ } }, "minimum-stability": "stable", - "prefer-stable": true + "prefer-stable": true, + "repositories": [ + { + "type": "path", + "url": "app-modules/*", + "options": { + "symlink": true + } + } + ] } diff --git a/composer.lock b/composer.lock index ba798dbc..7449ed2d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "d9db6cf0af5d41fccae074d3bfce2c78", + "content-hash": "989e4e73361b2b140c490b076d852185", "packages": [ { "name": "brick/math", @@ -61,6 +61,734 @@ ], "time": "2023-01-15T23:15:59+00:00" }, + { + "name": "brick/schema", + "version": "0.1.1", + "source": { + "type": "git", + "url": "https://github.com/brick/schema.git", + "reference": "935362907ef1b6e3706dac3d799be42f3c754f93" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/brick/schema/zipball/935362907ef1b6e3706dac3d799be42f3c754f93", + "reference": "935362907ef1b6e3706dac3d799be42f3c754f93", + "shasum": "" + }, + "require": { + "brick/structured-data": "^0.1.0", + "ext-dom": "*", + "php": ">=7.2" + }, + "require-dev": { + "brick/varexporter": "^0.2.1 || ^0.3.0", + "phpunit/phpunit": "^8.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Brick\\Schema\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Schema.org library for PHP", + "keywords": [ + "JSON-LD", + "brick", + "microdata", + "rdfa lite", + "schema", + "schema.org", + "structured data" + ], + "support": { + "issues": "https://github.com/brick/schema/issues", + "source": "https://github.com/brick/schema/tree/0.1.1" + }, + "time": "2020-03-27T14:36:17+00:00" + }, + { + "name": "brick/structured-data", + "version": "0.1.1", + "source": { + "type": "git", + "url": "https://github.com/brick/structured-data.git", + "reference": "1e1e1c8c87c392e65bb9c91e0f3cd732b4574fe9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/brick/structured-data/zipball/1e1e1c8c87c392e65bb9c91e0f3cd732b4574fe9", + "reference": "1e1e1c8c87c392e65bb9c91e0f3cd732b4574fe9", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "php": "^7.2 || ^8.0", + "sabre/uri": "^2.1" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.0", + "phpunit/phpunit": "^8.0 || ^9.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Brick\\StructuredData\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Microdata, RDFa Lite & JSON-LD structured data reader", + "keywords": [ + "JSON-LD", + "brick", + "microdata", + "rdfa", + "structured data" + ], + "support": { + "issues": "https://github.com/brick/structured-data/issues", + "source": "https://github.com/brick/structured-data/tree/0.1.1" + }, + "time": "2020-12-06T00:36:03+00:00" + }, + { + "name": "composer/ca-bundle", + "version": "1.3.7", + "source": { + "type": "git", + "url": "https://github.com/composer/ca-bundle.git", + "reference": "76e46335014860eec1aa5a724799a00a2e47cc85" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/ca-bundle/zipball/76e46335014860eec1aa5a724799a00a2e47cc85", + "reference": "76e46335014860eec1aa5a724799a00a2e47cc85", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "ext-pcre": "*", + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.55", + "psr/log": "^1.0", + "symfony/phpunit-bridge": "^4.2 || ^5", + "symfony/process": "^2.5 || ^3.0 || ^4.0 || ^5.0 || ^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\CaBundle\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Lets you find a path to the system CA bundle, and includes a fallback to the Mozilla CA bundle.", + "keywords": [ + "cabundle", + "cacert", + "certificate", + "ssl", + "tls" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/ca-bundle/issues", + "source": "https://github.com/composer/ca-bundle/tree/1.3.7" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2023-08-30T09:31:38+00:00" + }, + { + "name": "composer/class-map-generator", + "version": "1.1.0", + "source": { + "type": "git", + "url": "https://github.com/composer/class-map-generator.git", + "reference": "953cc4ea32e0c31f2185549c7d216d7921f03da9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/class-map-generator/zipball/953cc4ea32e0c31f2185549c7d216d7921f03da9", + "reference": "953cc4ea32e0c31f2185549c7d216d7921f03da9", + "shasum": "" + }, + "require": { + "composer/pcre": "^2.1 || ^3.1", + "php": "^7.2 || ^8.0", + "symfony/finder": "^4.4 || ^5.3 || ^6 || ^7" + }, + "require-dev": { + "phpstan/phpstan": "^1.6", + "phpstan/phpstan-deprecation-rules": "^1", + "phpstan/phpstan-phpunit": "^1", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/filesystem": "^5.4 || ^6", + "symfony/phpunit-bridge": "^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\ClassMapGenerator\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Utilities to scan PHP code and generate class maps.", + "keywords": [ + "classmap" + ], + "support": { + "issues": "https://github.com/composer/class-map-generator/issues", + "source": "https://github.com/composer/class-map-generator/tree/1.1.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2023-06-30T13:58:57+00:00" + }, + { + "name": "composer/composer", + "version": "2.6.5", + "source": { + "type": "git", + "url": "https://github.com/composer/composer.git", + "reference": "4b0fe89db9e65b1e64df633a992e70a7a215ab33" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/composer/zipball/4b0fe89db9e65b1e64df633a992e70a7a215ab33", + "reference": "4b0fe89db9e65b1e64df633a992e70a7a215ab33", + "shasum": "" + }, + "require": { + "composer/ca-bundle": "^1.0", + "composer/class-map-generator": "^1.0", + "composer/metadata-minifier": "^1.0", + "composer/pcre": "^2.1 || ^3.1", + "composer/semver": "^3.2.5", + "composer/spdx-licenses": "^1.5.7", + "composer/xdebug-handler": "^2.0.2 || ^3.0.3", + "justinrainbow/json-schema": "^5.2.11", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1.0 || ^2.0 || ^3.0", + "react/promise": "^2.8 || ^3", + "seld/jsonlint": "^1.4", + "seld/phar-utils": "^1.2", + "seld/signal-handler": "^2.0", + "symfony/console": "^5.4.11 || ^6.0.11 || ^7", + "symfony/filesystem": "^5.4 || ^6.0 || ^7", + "symfony/finder": "^5.4 || ^6.0 || ^7", + "symfony/polyfill-php73": "^1.24", + "symfony/polyfill-php80": "^1.24", + "symfony/polyfill-php81": "^1.24", + "symfony/process": "^5.4 || ^6.0 || ^7" + }, + "require-dev": { + "phpstan/phpstan": "^1.9.3", + "phpstan/phpstan-deprecation-rules": "^1", + "phpstan/phpstan-phpunit": "^1.0", + "phpstan/phpstan-strict-rules": "^1", + "phpstan/phpstan-symfony": "^1.2.10", + "symfony/phpunit-bridge": "^6.0 || ^7" + }, + "suggest": { + "ext-openssl": "Enabling the openssl extension allows you to access https URLs for repositories and packages", + "ext-zip": "Enabling the zip extension allows you to unzip archives", + "ext-zlib": "Allow gzip compression of HTTP requests" + }, + "bin": [ + "bin/composer" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.6-dev" + }, + "phpstan": { + "includes": [ + "phpstan/rules.neon" + ] + } + }, + "autoload": { + "psr-4": { + "Composer\\": "src/Composer/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "https://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "Composer helps you declare, manage and install dependencies of PHP projects. It ensures you have the right stack everywhere.", + "homepage": "https://getcomposer.org/", + "keywords": [ + "autoload", + "dependency", + "package" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/composer/issues", + "security": "https://github.com/composer/composer/security/policy", + "source": "https://github.com/composer/composer/tree/2.6.5" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2023-10-06T08:11:52+00:00" + }, + { + "name": "composer/metadata-minifier", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/composer/metadata-minifier.git", + "reference": "c549d23829536f0d0e984aaabbf02af91f443207" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/metadata-minifier/zipball/c549d23829536f0d0e984aaabbf02af91f443207", + "reference": "c549d23829536f0d0e984aaabbf02af91f443207", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "composer/composer": "^2", + "phpstan/phpstan": "^0.12.55", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\MetadataMinifier\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "Small utility library that handles metadata minification and expansion.", + "keywords": [ + "composer", + "compression" + ], + "support": { + "issues": "https://github.com/composer/metadata-minifier/issues", + "source": "https://github.com/composer/metadata-minifier/tree/1.0.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2021-04-07T13:37:33+00:00" + }, + { + "name": "composer/pcre", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/composer/pcre.git", + "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/pcre/zipball/4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", + "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.3", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Pcre\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "keywords": [ + "PCRE", + "preg", + "regex", + "regular expression" + ], + "support": { + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.1.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-11-17T09:50:14+00:00" + }, + { + "name": "composer/semver", + "version": "3.4.0", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/35e8d0af4486141bc745f23a29cc2091eb624a32", + "reference": "35e8d0af4486141bc745f23a29cc2091eb624a32", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.4", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Semver\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "Semver library that offers utilities, version constraint parsing and validation.", + "keywords": [ + "semantic", + "semver", + "validation", + "versioning" + ], + "support": { + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2023-08-31T09:50:34+00:00" + }, + { + "name": "composer/spdx-licenses", + "version": "1.5.7", + "source": { + "type": "git", + "url": "https://github.com/composer/spdx-licenses.git", + "reference": "c848241796da2abf65837d51dce1fae55a960149" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/spdx-licenses/zipball/c848241796da2abf65837d51dce1fae55a960149", + "reference": "c848241796da2abf65837d51dce1fae55a960149", + "shasum": "" + }, + "require": { + "php": "^5.3.2 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^0.12.55", + "symfony/phpunit-bridge": "^4.2 || ^5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Composer\\Spdx\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" + } + ], + "description": "SPDX licenses list and validation library.", + "keywords": [ + "license", + "spdx", + "validator" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/spdx-licenses/issues", + "source": "https://github.com/composer/spdx-licenses/tree/1.5.7" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-05-23T07:37:50+00:00" + }, + { + "name": "composer/xdebug-handler", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "ced299686f41dce890debac69273b47ffe98a40c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/ced299686f41dce890debac69273b47ffe98a40c", + "reference": "ced299686f41dce890debac69273b47ffe98a40c", + "shasum": "" + }, + "require": { + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" + }, + "require-dev": { + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "symfony/phpunit-bridge": "^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Composer\\XdebugHandler\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" + } + ], + "description": "Restarts a process without Xdebug.", + "keywords": [ + "Xdebug", + "performance" + ], + "support": { + "irc": "irc://irc.freenode.org/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.3" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2022-02-25T21:32:43+00:00" + }, { "name": "dflydev/dot-access-data", "version": "v3.0.2", @@ -1321,6 +2049,70 @@ ], "time": "2021-10-07T12:57:01+00:00" }, + { + "name": "hack-greenville/api", + "version": "1.0", + "dist": { + "type": "path", + "url": "app-modules/api", + "reference": "8f20a1858b1f9c8644834da8aa13c6b020223a05" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "HackGreenville\\Api\\Providers\\ApiServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "HackGreenville\\Api\\": "src/", + "HackGreenville\\Api\\Tests\\": "tests/", + "HackGreenville\\Api\\Database\\Factories\\": "database/factories/", + "HackGreenville\\Api\\Database\\Seeders\\": "database/seeders/" + } + }, + "license": [ + "proprietary" + ], + "transport-options": { + "symlink": true, + "relative": true + } + }, + { + "name": "hack-greenville/event-importer", + "version": "1.0", + "dist": { + "type": "path", + "url": "app-modules/event-importer", + "reference": "17a8265a21f116671906ebb24426538e2ba1117f" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "HackGreenville\\EventImporter\\Providers\\EventImporterServiceProvider" + ] + } + }, + "autoload": { + "psr-4": { + "HackGreenville\\EventImporter\\": "src/", + "HackGreenville\\EventImporter\\Tests\\": "tests/", + "HackGreenville\\EventImporter\\Database\\Factories\\": "database/factories/", + "HackGreenville\\EventImporter\\Database\\Seeders\\": "database/seeders/" + } + }, + "license": [ + "proprietary" + ], + "transport-options": { + "symlink": true, + "relative": true + } + }, { "name": "halaxa/json-machine", "version": "1.1.3", @@ -1379,6 +2171,75 @@ ], "time": "2022-10-12T11:40:33+00:00" }, + { + "name": "internachi/modular", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/InterNACHI/modular.git", + "reference": "2bcb0eeaae553a92315f0bab1cbde61e1614234b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/InterNACHI/modular/zipball/2bcb0eeaae553a92315f0bab1cbde61e1614234b", + "reference": "2bcb0eeaae553a92315f0bab1cbde61e1614234b", + "shasum": "" + }, + "require": { + "composer/composer": "^2.1", + "ext-dom": "*", + "ext-simplexml": "*", + "illuminate/support": "^9|^10|10.x-dev|11.x-dev|dev-master", + "php": ">=8.0" + }, + "require-dev": { + "ext-json": "*", + "friendsofphp/php-cs-fixer": "^3.14", + "livewire/livewire": "^2.5", + "mockery/mockery": "^1.5", + "orchestra/testbench": "^7.10|^8|8.x-dev|9.x-dev|dev-master", + "phpunit/phpunit": "^9.5" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "InterNACHI\\Modular\\Support\\ModularServiceProvider", + "InterNACHI\\Modular\\Support\\ModularizedCommandsServiceProvider" + ], + "aliases": { + "Modules": "InterNACHI\\Modular\\Support\\Facades\\Modules" + } + } + }, + "autoload": { + "psr-4": { + "InterNACHI\\Modular\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Chris Morrell", + "homepage": "http://www.cmorrell.com" + } + ], + "description": "Modularize your Laravel apps", + "keywords": [ + "laravel", + "modular", + "module", + "modules" + ], + "support": { + "issues": "https://github.com/InterNACHI/modular/issues", + "source": "https://github.com/InterNACHI/modular/tree/2.0.0" + }, + "time": "2023-05-19T13:59:41+00:00" + }, { "name": "intervention/image", "version": "2.7.2", @@ -1473,26 +2334,85 @@ }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/jdorn/sql-formatter/zipball/64990d96e0959dff8e059dfcdc1af130728d92bc", - "reference": "64990d96e0959dff8e059dfcdc1af130728d92bc", + "url": "https://api.github.com/repos/jdorn/sql-formatter/zipball/64990d96e0959dff8e059dfcdc1af130728d92bc", + "reference": "64990d96e0959dff8e059dfcdc1af130728d92bc", + "shasum": "" + }, + "require": { + "php": ">=5.2.4" + }, + "require-dev": { + "phpunit/phpunit": "3.7.*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.3.x-dev" + } + }, + "autoload": { + "classmap": [ + "lib" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jeremy Dorn", + "email": "jeremy@jeremydorn.com", + "homepage": "http://jeremydorn.com/" + } + ], + "description": "a PHP SQL highlighting library", + "homepage": "https://github.com/jdorn/sql-formatter/", + "keywords": [ + "highlight", + "sql" + ], + "support": { + "issues": "https://github.com/jdorn/sql-formatter/issues", + "source": "https://github.com/jdorn/sql-formatter/tree/v1.2.17" + }, + "time": "2014-01-12T16:20:24+00:00" + }, + { + "name": "justinrainbow/json-schema", + "version": "v5.2.13", + "source": { + "type": "git", + "url": "https://github.com/justinrainbow/json-schema.git", + "reference": "fbbe7e5d79f618997bc3332a6f49246036c45793" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/fbbe7e5d79f618997bc3332a6f49246036c45793", + "reference": "fbbe7e5d79f618997bc3332a6f49246036c45793", "shasum": "" }, "require": { - "php": ">=5.2.4" + "php": ">=5.3.3" }, "require-dev": { - "phpunit/phpunit": "3.7.*" + "friendsofphp/php-cs-fixer": "~2.2.20||~2.15.1", + "json-schema/json-schema-test-suite": "1.2.0", + "phpunit/phpunit": "^4.8.35" }, + "bin": [ + "bin/validate-json" + ], "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3.x-dev" + "dev-master": "5.0.x-dev" } }, "autoload": { - "classmap": [ - "lib" - ] + "psr-4": { + "JsonSchema\\": "src/JsonSchema/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1500,22 +2420,33 @@ ], "authors": [ { - "name": "Jeremy Dorn", - "email": "jeremy@jeremydorn.com", - "homepage": "http://jeremydorn.com/" + "name": "Bruno Prieto Reis", + "email": "bruno.p.reis@gmail.com" + }, + { + "name": "Justin Rainbow", + "email": "justin.rainbow@gmail.com" + }, + { + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" + }, + { + "name": "Robert Schönthal", + "email": "seroscho@googlemail.com" } ], - "description": "a PHP SQL highlighting library", - "homepage": "https://github.com/jdorn/sql-formatter/", + "description": "A library to validate a json schema.", + "homepage": "https://github.com/justinrainbow/json-schema", "keywords": [ - "highlight", - "sql" + "json", + "schema" ], "support": { - "issues": "https://github.com/jdorn/sql-formatter/issues", - "source": "https://github.com/jdorn/sql-formatter/tree/v1.2.17" + "issues": "https://github.com/justinrainbow/json-schema/issues", + "source": "https://github.com/justinrainbow/json-schema/tree/v5.2.13" }, - "time": "2014-01-12T16:20:24+00:00" + "time": "2023-09-26T02:20:38+00:00" }, { "name": "laravel/framework", @@ -4323,41 +5254,344 @@ ], "time": "2023-04-15T23:01:58+00:00" }, + { + "name": "react/promise", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "c86753c76fd3be465d93b308f18d189f01a22be4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/c86753c76fd3be465d93b308f18d189f01a22be4", + "reference": "c86753c76fd3be465d93b308f18d189f01a22be4", + "shasum": "" + }, + "require": { + "php": ">=7.1.0" + }, + "require-dev": { + "phpstan/phpstan": "1.10.20 || 1.4.10", + "phpunit/phpunit": "^9.5 || ^7.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "React\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "keywords": [ + "promise", + "promises" + ], + "support": { + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v3.0.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2023-07-11T16:12:49+00:00" + }, + { + "name": "sabre/uri", + "version": "2.3.3", + "source": { + "type": "git", + "url": "https://github.com/sabre-io/uri.git", + "reference": "7e0e7dfd0b7e14346a27eabd66e843a6e7f1812b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sabre-io/uri/zipball/7e0e7dfd0b7e14346a27eabd66e843a6e7f1812b", + "reference": "7e0e7dfd0b7e14346a27eabd66e843a6e7f1812b", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.17", + "phpstan/extension-installer": "^1.3", + "phpstan/phpstan": "^1.10", + "phpstan/phpstan-phpunit": "^1.3", + "phpstan/phpstan-strict-rules": "^1.5", + "phpunit/phpunit": "^9.6" + }, + "type": "library", + "autoload": { + "files": [ + "lib/functions.php" + ], + "psr-4": { + "Sabre\\Uri\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Evert Pot", + "email": "me@evertpot.com", + "homepage": "http://evertpot.com/", + "role": "Developer" + } + ], + "description": "Functions for making sense out of URIs.", + "homepage": "http://sabre.io/uri/", + "keywords": [ + "rfc3986", + "uri", + "url" + ], + "support": { + "forum": "https://groups.google.com/group/sabredav-discuss", + "issues": "https://github.com/sabre-io/uri/issues", + "source": "https://github.com/fruux/sabre-uri" + }, + "time": "2023-06-09T06:54:04+00:00" + }, { "name": "scyllaly/hcaptcha", "version": "4.4.5", "source": { "type": "git", - "url": "https://github.com/Scyllaly/hcaptcha.git", - "reference": "3c133dfe684d34570e911de11098ebaa0d2c369d" + "url": "https://github.com/Scyllaly/hcaptcha.git", + "reference": "3c133dfe684d34570e911de11098ebaa0d2c369d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Scyllaly/hcaptcha/zipball/3c133dfe684d34570e911de11098ebaa0d2c369d", + "reference": "3c133dfe684d34570e911de11098ebaa0d2c369d", + "shasum": "" + }, + "require": { + "illuminate/support": "5.*|6.*|7.*|8.*|^9.0|10.*", + "php": ">=5.5.5" + }, + "require-dev": { + "phpunit/phpunit": "~4.8|^9.5.10|^10.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Scyllaly\\HCaptcha\\HCaptchaServiceProvider" + ], + "aliases": { + "HCaptcha": "Scyllaly\\HCaptcha\\Facades\\HCaptcha" + } + } + }, + "autoload": { + "psr-4": { + "Scyllaly\\HCaptcha\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "scyllaly", + "email": "scyllaly@github.com" + } + ], + "description": "hCaptcha for Laravel", + "keywords": [ + "captcha", + "hcaptcha", + "laravel" + ], + "support": { + "issues": "https://github.com/Scyllaly/hcaptcha/issues", + "source": "https://github.com/Scyllaly/hcaptcha/tree/4.4.5" + }, + "time": "2023-03-14T16:36:21+00:00" + }, + { + "name": "seld/jsonlint", + "version": "1.10.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/jsonlint.git", + "reference": "594fd6462aad8ecee0b45ca5045acea4776667f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/594fd6462aad8ecee0b45ca5045acea4776667f1", + "reference": "594fd6462aad8ecee0b45ca5045acea4776667f1", + "shasum": "" + }, + "require": { + "php": "^5.3 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.5", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^8.5.13" + }, + "bin": [ + "bin/jsonlint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Seld\\JsonLint\\": "src/Seld/JsonLint/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + } + ], + "description": "JSON Linter", + "keywords": [ + "json", + "linter", + "parser", + "validator" + ], + "support": { + "issues": "https://github.com/Seldaek/jsonlint/issues", + "source": "https://github.com/Seldaek/jsonlint/tree/1.10.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint", + "type": "tidelift" + } + ], + "time": "2023-05-11T13:16:46+00:00" + }, + { + "name": "seld/phar-utils", + "version": "1.2.1", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/phar-utils.git", + "reference": "ea2f4014f163c1be4c601b9b7bd6af81ba8d701c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/phar-utils/zipball/ea2f4014f163c1be4c601b9b7bd6af81ba8d701c", + "reference": "ea2f4014f163c1be4c601b9b7bd6af81ba8d701c", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Seld\\PharUtils\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be" + } + ], + "description": "PHAR file format utilities, for when PHP phars you up", + "keywords": [ + "phar" + ], + "support": { + "issues": "https://github.com/Seldaek/phar-utils/issues", + "source": "https://github.com/Seldaek/phar-utils/tree/1.2.1" + }, + "time": "2022-08-31T10:31:18+00:00" + }, + { + "name": "seld/signal-handler", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/signal-handler.git", + "reference": "04a6112e883ad76c0ada8e4a9f7520bbfdb6bb98" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Scyllaly/hcaptcha/zipball/3c133dfe684d34570e911de11098ebaa0d2c369d", - "reference": "3c133dfe684d34570e911de11098ebaa0d2c369d", + "url": "https://api.github.com/repos/Seldaek/signal-handler/zipball/04a6112e883ad76c0ada8e4a9f7520bbfdb6bb98", + "reference": "04a6112e883ad76c0ada8e4a9f7520bbfdb6bb98", "shasum": "" }, "require": { - "illuminate/support": "5.*|6.*|7.*|8.*|^9.0|10.*", - "php": ">=5.5.5" + "php": ">=7.2.0" }, "require-dev": { - "phpunit/phpunit": "~4.8|^9.5.10|^10.0" + "phpstan/phpstan": "^1", + "phpstan/phpstan-deprecation-rules": "^1.0", + "phpstan/phpstan-phpunit": "^1", + "phpstan/phpstan-strict-rules": "^1.3", + "phpunit/phpunit": "^7.5.20 || ^8.5.23", + "psr/log": "^1 || ^2 || ^3" }, "type": "library", "extra": { - "laravel": { - "providers": [ - "Scyllaly\\HCaptcha\\HCaptchaServiceProvider" - ], - "aliases": { - "HCaptcha": "Scyllaly\\HCaptcha\\Facades\\HCaptcha" - } + "branch-alias": { + "dev-main": "2.x-dev" } }, "autoload": { "psr-4": { - "Scyllaly\\HCaptcha\\": "src/" + "Seld\\Signal\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4366,21 +5600,24 @@ ], "authors": [ { - "name": "scyllaly", - "email": "scyllaly@github.com" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" } ], - "description": "hCaptcha for Laravel", + "description": "Simple unix signal handler that silently fails where signals are not supported for easy cross-platform development", "keywords": [ - "captcha", - "hcaptcha", - "laravel" + "posix", + "sigint", + "signal", + "sigterm", + "unix" ], "support": { - "issues": "https://github.com/Scyllaly/hcaptcha/issues", - "source": "https://github.com/Scyllaly/hcaptcha/tree/4.4.5" + "issues": "https://github.com/Seldaek/signal-handler/issues", + "source": "https://github.com/Seldaek/signal-handler/tree/2.0.2" }, - "time": "2023-03-14T16:36:21+00:00" + "time": "2023-09-03T09:24:00+00:00" }, { "name": "spatie/laravel-data", @@ -5051,6 +6288,69 @@ ], "time": "2023-05-23T14:45:45+00:00" }, + { + "name": "symfony/filesystem", + "version": "v6.3.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/edd36776956f2a6fcf577edb5b05eb0e3bdc52ae", + "reference": "edd36776956f2a6fcf577edb5b05eb0e3bdc52ae", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v6.3.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2023-06-01T08:30:39+00:00" + }, { "name": "symfony/finder", "version": "v6.3.0", @@ -5651,7 +6951,179 @@ "symfony/polyfill-php72": "^1.10" }, "suggest": { - "ext-intl": "For best performance" + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Idn\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Laurent Bassin", + "email": "laurent@bassin.info" + }, + { + "name": "Trevor Rowbotham", + "email": "trevor.rowbotham@pm.me" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "idn", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.27-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-11-03T14:55:06+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.27.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" }, "type": "library", "extra": { @@ -5668,7 +7140,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Intl\\Idn\\": "" + "Symfony\\Polyfill\\Mbstring\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -5677,30 +7149,25 @@ ], "authors": [ { - "name": "Laurent Bassin", - "email": "laurent@bassin.info" - }, - { - "name": "Trevor Rowbotham", - "email": "trevor.rowbotham@pm.me" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions", + "description": "Symfony polyfill for the Mbstring extension", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "idn", - "intl", + "mbstring", "polyfill", "portable", "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" }, "funding": [ { @@ -5719,25 +7186,22 @@ "time": "2022-11-03T14:55:06+00:00" }, { - "name": "symfony/polyfill-intl-normalizer", + "name": "symfony/polyfill-php72", "version": "v1.27.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6" + "url": "https://github.com/symfony/polyfill-php72.git", + "reference": "869329b1e9894268a8a61dabb69153029b7a8c97" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/19bd1e4fcd5b91116f14d8533c57831ed00571b6", - "reference": "19bd1e4fcd5b91116f14d8533c57831ed00571b6", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/869329b1e9894268a8a61dabb69153029b7a8c97", + "reference": "869329b1e9894268a8a61dabb69153029b7a8c97", "shasum": "" }, "require": { "php": ">=7.1" }, - "suggest": { - "ext-intl": "For best performance" - }, "type": "library", "extra": { "branch-alias": { @@ -5753,11 +7217,8 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" - }, - "classmap": [ - "Resources/stubs" - ] + "Symfony\\Polyfill\\Php72\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -5773,18 +7234,16 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", + "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "intl", - "normalizer", "polyfill", "portable", "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-php72/tree/v1.27.0" }, "funding": [ { @@ -5803,32 +7262,26 @@ "time": "2022-11-03T14:55:06+00:00" }, { - "name": "symfony/polyfill-mbstring", - "version": "v1.27.0", + "name": "symfony/polyfill-php73", + "version": "v1.28.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534" + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "fe2f306d1d9d346a7fee353d0d5012e401e984b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/8ad114f6b39e2c98a8b0e3bd907732c207c2b534", - "reference": "8ad114f6b39e2c98a8b0e3bd907732c207c2b534", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/fe2f306d1d9d346a7fee353d0d5012e401e984b5", + "reference": "fe2f306d1d9d346a7fee353d0d5012e401e984b5", "shasum": "" }, "require": { "php": ">=7.1" }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -5840,8 +7293,11 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } + "Symfony\\Polyfill\\Php73\\": "" + }, + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -5857,17 +7313,16 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for the Mbstring extension", + "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "mbstring", "polyfill", "portable", "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-php73/tree/v1.28.0" }, "funding": [ { @@ -5883,20 +7338,20 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { - "name": "symfony/polyfill-php72", + "name": "symfony/polyfill-php80", "version": "v1.27.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "869329b1e9894268a8a61dabb69153029b7a8c97" + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/869329b1e9894268a8a61dabb69153029b7a8c97", - "reference": "869329b1e9894268a8a61dabb69153029b7a8c97", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", "shasum": "" }, "require": { @@ -5917,14 +7372,21 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php72\\": "" - } + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, { "name": "Nicolas Grekas", "email": "p@tchwork.com" @@ -5934,7 +7396,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 7.2+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -5943,7 +7405,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php72/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" }, "funding": [ { @@ -5962,17 +7424,17 @@ "time": "2022-11-03T14:55:06+00:00" }, { - "name": "symfony/polyfill-php80", - "version": "v1.27.0", + "name": "symfony/polyfill-php81", + "version": "v1.28.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936" + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "7581cd600fa9fd681b797d00b02f068e2f13263b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", - "reference": "7a6ff3f1959bb01aefccb463a0f2cd3d3d2fd936", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/7581cd600fa9fd681b797d00b02f068e2f13263b", + "reference": "7581cd600fa9fd681b797d00b02f068e2f13263b", "shasum": "" }, "require": { @@ -5981,7 +7443,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "1.27-dev" + "dev-main": "1.28-dev" }, "thanks": { "name": "symfony/polyfill", @@ -5993,7 +7455,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" + "Symfony\\Polyfill\\Php81\\": "" }, "classmap": [ "Resources/stubs" @@ -6004,10 +7466,6 @@ "MIT" ], "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, { "name": "Nicolas Grekas", "email": "p@tchwork.com" @@ -6017,7 +7475,7 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", @@ -6026,7 +7484,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.27.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.28.0" }, "funding": [ { @@ -6042,7 +7500,7 @@ "type": "tidelift" } ], - "time": "2022-11-03T14:55:06+00:00" + "time": "2023-01-26T09:26:14+00:00" }, { "name": "symfony/polyfill-php83", @@ -7438,150 +8896,6 @@ ], "time": "2023-06-22T14:22:36+00:00" }, - { - "name": "composer/class-map-generator", - "version": "1.1.0", - "source": { - "type": "git", - "url": "https://github.com/composer/class-map-generator.git", - "reference": "953cc4ea32e0c31f2185549c7d216d7921f03da9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/class-map-generator/zipball/953cc4ea32e0c31f2185549c7d216d7921f03da9", - "reference": "953cc4ea32e0c31f2185549c7d216d7921f03da9", - "shasum": "" - }, - "require": { - "composer/pcre": "^2.1 || ^3.1", - "php": "^7.2 || ^8.0", - "symfony/finder": "^4.4 || ^5.3 || ^6 || ^7" - }, - "require-dev": { - "phpstan/phpstan": "^1.6", - "phpstan/phpstan-deprecation-rules": "^1", - "phpstan/phpstan-phpunit": "^1", - "phpstan/phpstan-strict-rules": "^1.1", - "symfony/filesystem": "^5.4 || ^6", - "symfony/phpunit-bridge": "^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\ClassMapGenerator\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "https://seld.be" - } - ], - "description": "Utilities to scan PHP code and generate class maps.", - "keywords": [ - "classmap" - ], - "support": { - "issues": "https://github.com/composer/class-map-generator/issues", - "source": "https://github.com/composer/class-map-generator/tree/1.1.0" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2023-06-30T13:58:57+00:00" - }, - { - "name": "composer/pcre", - "version": "3.1.0", - "source": { - "type": "git", - "url": "https://github.com/composer/pcre.git", - "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", - "reference": "4bff79ddd77851fe3cdd11616ed3f92841ba5bd2", - "shasum": "" - }, - "require": { - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.3", - "phpstan/phpstan-strict-rules": "^1.1", - "symfony/phpunit-bridge": "^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Pcre\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "PCRE wrapping library that offers type-safe preg_* replacements.", - "keywords": [ - "PCRE", - "preg", - "regex", - "regular expression" - ], - "support": { - "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.1.0" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2022-11-17T09:50:14+00:00" - }, { "name": "doctrine/cache", "version": "2.2.0", diff --git a/config/app-modules.php b/config/app-modules.php new file mode 100644 index 00000000..eebdffa8 --- /dev/null +++ b/config/app-modules.php @@ -0,0 +1,81 @@ + 'HackGreenville', + + /* + |-------------------------------------------------------------------------- + | Composer "Vendor" Name + |-------------------------------------------------------------------------- + | + | This is the prefix used for your composer.json file. This should be the + | kebab-case version of your module namespace (if left null, we will + | generate the kebab-case version for you). + | + */ + + 'modules_vendor' => null, + + /* + |-------------------------------------------------------------------------- + | Modules Directory + |-------------------------------------------------------------------------- + | + | If you want to install modules in a custom directory, you can do so here. + | Keeping the default `app-modules/` directory is highly recommended, + | though, as it keeps your modules near the rest of your application code + | in an alpha-sorted directory listing. + | + */ + + 'modules_directory' => 'app-modules', + + /* + |-------------------------------------------------------------------------- + | Base Test Case + |-------------------------------------------------------------------------- + | + | This is the base TestCase class name that auto-generated Tests should + | extend. By default it assumes the default \Tests\TestCase exists. + | + */ + + 'tests_base' => 'Tests\TestCase', + + /* + |-------------------------------------------------------------------------- + | Custom Stubs + |-------------------------------------------------------------------------- + | + | If you would like to use your own custom stubs for new modules, you can + | configure those here. This should be an array where the key is the path + | relative to the module and the value is the absolute path to the stub + | stub file. Destination paths and contents support placeholders. See the + | README.md file for more information. + | + | For example: + | + | 'stubs' => [ + | 'src/Providers/StubClassNamePrefixServiceProvider.php' => base_path('stubs/app-modules/ServiceProvider.php'), + | ], + */ + + 'stubs' => null, +]; diff --git a/config/event-import-handlers.php b/config/event-import-handlers.php new file mode 100644 index 00000000..2abeccea --- /dev/null +++ b/config/event-import-handlers.php @@ -0,0 +1,16 @@ + [ + EventServices::EventBrite->value => EventBriteHandler::class, + EventServices::MeetupRest->value => MeetupRestHandler::class, + ], + 'active_services' => [ + EventServices::EventBrite->value, + EventServices::MeetupRest->value, + ], +]; diff --git a/config/services.php b/config/services.php index 7c56507e..2ef2d915 100644 --- a/config/services.php +++ b/config/services.php @@ -22,7 +22,7 @@ ], 'ses' => [ - 'key' => env('SES_KEY'), + 'key' => env('SES_KEY'), 'secret' => env('SES_SECRET'), 'region' => 'us-east-1', ], @@ -32,21 +32,25 @@ ], 'stripe' => [ - 'model' => User::class, - 'key' => env('STRIPE_KEY'), + 'model' => User::class, + 'key' => env('STRIPE_KEY'), 'secret' => env('STRIPE_SECRET'), ], 'google' => [ 'tagmanager' => [ - 'id' => env('GOOGLE_TAG_MANAGER') + 'id' => env('GOOGLE_TAG_MANAGER'), ], ], 'slack' => [ 'contact' => [ - 'webhook' => env('SLACK_CONTACT_WEBHOOK') + 'webhook' => env('SLACK_CONTACT_WEBHOOK'), ], ], + 'eventbrite' => [ + 'private_token' => env('EVENTBRITE_PRIVATE_TOKEN'), + ], + ]; diff --git a/database/factories/CategoryFactory.php b/database/factories/CategoryFactory.php new file mode 100644 index 00000000..468672eb --- /dev/null +++ b/database/factories/CategoryFactory.php @@ -0,0 +1,22 @@ + $this->faker->slug(), + 'label' => $this->faker->word(), + 'created_at' => Carbon::now(), + 'updated_at' => Carbon::now(), + ]; + } +} diff --git a/database/factories/EventFactory.php b/database/factories/EventFactory.php index ba15bd19..d7496a3a 100644 --- a/database/factories/EventFactory.php +++ b/database/factories/EventFactory.php @@ -2,7 +2,9 @@ namespace Database\Factories; +use App\Enums\EventServices; use App\Models\Event; +use App\Models\Org; use App\Models\Venue; use Illuminate\Database\Eloquent\Factories\Factory; @@ -25,33 +27,36 @@ public function definition() $venue = Venue::factory(); return [ - 'event_name' => $event_name = $this->faker->name . ' tech talk', - 'group_name' => $group_name = $this->faker->name . ' tech group', - 'description' => $description = $this->faker->text(100), - 'rsvp_count' => $rsvp_count = $this->faker->randomNumber(2), - 'active_at' => $active_at = $this->faker->dateTime('+2 hours'), - 'uri' => $url = $this->faker->url, - 'venue_id' => $venue, - 'event_uuid' => $uuid = $this->faker->uuid, - 'expire_at' => $expire_at = $this->faker->dateTimeBetween($active_at, '+2 days'), + 'event_name' => $event_name = $this->faker->name . ' tech talk', + 'group_name' => $group_name = $this->faker->name . ' tech group', + 'description' => $description = $this->faker->text(100), + 'rsvp_count' => $rsvp_count = $this->faker->randomNumber(2), + 'active_at' => $active_at = $this->faker->dateTime('+2 hours'), + 'uri' => $url = $this->faker->url, + 'venue_id' => $venue, + 'event_uuid' => $uuid = $this->faker->uuid, + 'expire_at' => $expire_at = $this->faker->dateTimeBetween($active_at, '+2 days'), 'cancelled_at' => null, + 'service' => EventServices::EventBrite->value, + 'service_id' => $this->faker->randomDigit(), + 'organization_id' => Org::factory(), 'cache' => [ [ - "created_at" => "2019-12-09T21:24:53Z", - "data_as_of" => "2020-09-09T18:40:10Z", + "created_at" => "2019-12-09T21:24:53Z", + "data_as_of" => "2020-09-09T18:40:10Z", "description" => $description, - "event_name" => $event_name, - "group_name" => $group_name, - "nid" => $this->faker->randomNumber(2), - "rsvp_count" => $rsvp_count, - "status" => "upcoming", - "tags" => "", - "time" => "2021-08-18T16:00:00Z", - "url" => $url, - "uuid" => $uuid, - "venue" => $venue, - "localtime" => "2021-08-18T16:00:00.000000Z", + "event_name" => $event_name, + "group_name" => $group_name, + "nid" => $this->faker->randomNumber(2), + "rsvp_count" => $rsvp_count, + "status" => "upcoming", + "tags" => "", + "time" => "2021-08-18T16:00:00Z", + "url" => $url, + "uuid" => $uuid, + "venue" => $venue, + "localtime" => "2021-08-18T16:00:00.000000Z", ], ], ]; diff --git a/database/factories/OrgFactory.php b/database/factories/OrgFactory.php new file mode 100644 index 00000000..ad5d4ded --- /dev/null +++ b/database/factories/OrgFactory.php @@ -0,0 +1,35 @@ + $this->faker->word(), + 'title' => $this->faker->word(), + 'path' => $this->faker->url(), + 'city' => $this->faker->city(), + 'focus_area' => $this->faker->word(), + 'primary_contact_person' => $this->faker->word(), + 'organization_type' => $this->faker->word(), + 'event_calendar_uri' => $this->faker->url(), + 'established_at' => now(), + 'cache' => $this->faker->words(), + 'status' => OrganizationStatus::Active, + 'created_at' => Carbon::now(), + 'updated_at' => Carbon::now(), + + 'category_id' => Category::factory(), + ]; + } +} diff --git a/database/factories/VenueFactory.php b/database/factories/VenueFactory.php index 116b19b1..71003459 100644 --- a/database/factories/VenueFactory.php +++ b/database/factories/VenueFactory.php @@ -9,30 +9,21 @@ class VenueFactory extends Factory { - /** - * The name of the factory's corresponding model. - * - * @var string - */ protected $model = Venue::class; - /** - * Define the model's default state. - * - * @return array - */ public function definition() { return [ - 'name' => $name = implode(' ', $this->faker->words(5)), - 'slug' => Str::slug($name), - 'address' => $this->faker->address, - 'zipcode' => $this->faker->postcode, - 'phone' => $this->faker->phoneNumber, - 'city' => $this->faker->city, + 'name' => $name = implode(' ', $this->faker->words(5)), + 'slug' => Str::slug($name), + 'address' => $this->faker->address, + 'zipcode' => $this->faker->postcode, + 'country' => $this->faker->countryCode(), + 'phone' => $this->faker->phoneNumber, + 'city' => $this->faker->city, 'state_id' => State::factory(), - 'lat' => $this->faker->latitude, - 'lng' => $this->faker->longitude, + 'lat' => $this->faker->latitude, + 'lng' => $this->faker->longitude, ]; } } diff --git a/database/migrations/2023_10_27_214140_add_services_to_orgs_table.php b/database/migrations/2023_10_27_214140_add_services_to_orgs_table.php new file mode 100644 index 00000000..ed123e19 --- /dev/null +++ b/database/migrations/2023_10_27_214140_add_services_to_orgs_table.php @@ -0,0 +1,23 @@ +string('service_api_key')->nullable()->after('event_calendar_uri'); + $table->string('service')->nullable()->after('event_calendar_uri'); + }); + } + + public function down(): void + { + Schema::table('orgs', function (Blueprint $table) { + $table->dropColumn('service'); + $table->dropColumn('service_api_key'); + }); + } +}; diff --git a/database/migrations/2023_10_27_214544_add_status_to_orgs_table.php b/database/migrations/2023_10_27_214544_add_status_to_orgs_table.php new file mode 100644 index 00000000..861af0e9 --- /dev/null +++ b/database/migrations/2023_10_27_214544_add_status_to_orgs_table.php @@ -0,0 +1,21 @@ +string('status')->after('service'); + }); + } + + public function down(): void + { + Schema::table('orgs', function (Blueprint $table) { + $table->dropColumn('status'); + }); + } +}; diff --git a/database/migrations/2023_10_28_011020_add_country_to_venue_table.php b/database/migrations/2023_10_28_011020_add_country_to_venue_table.php new file mode 100644 index 00000000..cc539669 --- /dev/null +++ b/database/migrations/2023_10_28_011020_add_country_to_venue_table.php @@ -0,0 +1,21 @@ +string('country', 2)->nullable(); + }); + } + + public function down(): void + { + Schema::table('venues', function (Blueprint $table) { + $table->dropColumn('country'); + }); + } +}; diff --git a/database/migrations/2023_10_28_172914_add_organization_id_to_events_table.php b/database/migrations/2023_10_28_172914_add_organization_id_to_events_table.php new file mode 100644 index 00000000..49ba04d5 --- /dev/null +++ b/database/migrations/2023_10_28_172914_add_organization_id_to_events_table.php @@ -0,0 +1,21 @@ +integer('organization_id')->unsigned()->after('id'); + }); + } + + public function down(): void + { + Schema::table('events', function (Blueprint $table) { + $table->dropColumn('organization_id'); + }); + } +}; diff --git a/database/migrations/2023_10_29_184929_add_year_established_at_to_orgs_table.php b/database/migrations/2023_10_29_184929_add_year_established_at_to_orgs_table.php new file mode 100644 index 00000000..15a09291 --- /dev/null +++ b/database/migrations/2023_10_29_184929_add_year_established_at_to_orgs_table.php @@ -0,0 +1,21 @@ +timestamp('established_at')->nullable()->after('cache'); + }); + } + + public function down(): void + { + Schema::table('orgs', function (Blueprint $table) { + $table->dropColumn('established_at'); + }); + } +}; diff --git a/tests/DatabaseTestCase.php b/tests/DatabaseTestCase.php new file mode 100644 index 00000000..84db6913 --- /dev/null +++ b/tests/DatabaseTestCase.php @@ -0,0 +1,10 @@ +