From c12290f5fc10ed6dea4110482893c5d76ac30599 Mon Sep 17 00:00:00 2001 From: "smarcet@gmail.com" Date: Thu, 27 Jan 2022 15:37:26 -0600 Subject: [PATCH] Voteable Presentations GET api/v1/summits/{id}/presentations/voteable filter published == 1 order random custom_order cast vote POST api/v1/summits/{id}/presentations/{presentation_id}/attendee-vote required scope %s/presentations/attendee-vote uncast vote DElETE api/v1/summits/{id}/presentations/{presentation_id}/attendee-vote required scope %s/presentations/attendee-vote Signed-off-by: smarcet@gmail.com Change-Id: I652902fc746349eebf36c9067c57b46dd1c16dd6 --- .../EventTypeValidationRulesFactory.php | 25 +- ...ionCategoryGroupValidationRulesFactory.php | 3 + .../SummitEventValidationRulesFactory.php | 3 +- .../OAuth2PresentationApiController.php | 93 ++++++ .../OAuth2SummitEventsApiController.php | 204 ++++++++---- ...trieveAllPublishedSummitEventsStrategy.php | 4 +- ...etrieveAllSummitEventsBySummitStrategy.php | 13 +- .../RetrieveAllSummitEventsStrategy.php | 9 - ...RetrieveAllSummitPresentationsStrategy.php | 34 ++ ...AllSummitVoteablePresentationsStrategy.php | 35 ++ ...ePublishedSummitEventsBySummitStrategy.php | 2 + .../events/RetrieveSummitEventsStrategy.php | 18 + app/Http/Utils/Order.php | 14 + app/ModelSerializers/SerializerRegistry.php | 4 + .../PresentationAttendeeVoteSerializer.php | 26 ++ .../PresentationCategoryGroupSerializer.php | 3 + .../Presentation/PresentationSerializer.php | 4 + .../PresentationTypeSerializer.php | 2 + .../Registration/SummitAttendeeSerializer.php | 21 ++ .../Summit/SummitEventSerializer.php | 2 + .../Summit/SummitEventTypeSerializer.php | 23 +- .../Summit/Events/ISummitEventType.php | 1 + .../Events/Presentations/Presentation.php | 88 ++++- .../PresentationAttendeeVote.php | 86 +++++ .../PresentationCategoryGroup.php | 211 ++++++++++-- .../Events/Presentations/PresentationType.php | 47 +++ .../Foundation/Summit/Events/SummitEvent.php | 80 +++-- .../Summit/Events/SummitEventType.php | 62 +++- .../PresentationCategoryGroupFactory.php | 32 +- .../Summit/Factories/PresentationFactory.php | 4 + .../Factories/SummitEventTypeFactory.php | 14 + .../Registration/Attendees/SummitAttendee.php | 238 ++++++++----- .../Summit/DoctrineSummitEventRepository.php | 260 ++++++++------- app/Security/SummitScopes.php | 2 + app/Services/Model/IPresentationService.php | 20 ++ app/Services/Model/ISummitService.php | 6 +- .../Model/Imp/PresentationService.php | 68 ++++ .../SummitRegistrationInvitationService.php | 6 +- app/Services/Model/Imp/SummitService.php | 48 ++- config/doctrine.php | 4 +- .../model/Version20220127210145.php | 52 +++ .../model/Version20220127210146.php | 52 +++ .../model/Version20220128194504.php | 50 +++ .../model/Version20220128200351.php | 55 +++ .../model/Version20220131195047.php | 63 ++++ .../model/Version20220131201421.php | 42 +++ database/seeders/ApiEndpointsSeeder.php | 50 ++- database/seeders/ApiScopesSeeder.php | 5 + database/seeders/TestSeeder.php | 10 + routes/api_v1.php | 11 +- tests/InsertSummitTestData.php | 107 ++++-- tests/OAuth2EventTypesApiTest.php | 64 ++-- tests/OAuth2SummitEventsApiTest.php | 312 ++++++++++++++++-- tests/OAuth2TrackGroupsApiTest.php | 107 +++--- 54 files changed, 2258 insertions(+), 541 deletions(-) create mode 100644 app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrieveAllSummitPresentationsStrategy.php create mode 100644 app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrieveAllSummitVoteablePresentationsStrategy.php create mode 100644 app/ModelSerializers/Summit/Presentation/PresentationAttendeeVoteSerializer.php create mode 100644 app/Models/Foundation/Summit/Events/Presentations/PresentationAttendeeVote.php create mode 100644 database/migrations/model/Version20220127210145.php create mode 100644 database/migrations/model/Version20220127210146.php create mode 100644 database/migrations/model/Version20220128194504.php create mode 100644 database/migrations/model/Version20220128200351.php create mode 100644 database/migrations/model/Version20220131195047.php create mode 100644 database/migrations/model/Version20220131201421.php diff --git a/app/Http/Controllers/Apis/Protected/Summit/Factories/EventTypeValidationRulesFactory.php b/app/Http/Controllers/Apis/Protected/Summit/Factories/EventTypeValidationRulesFactory.php index 8f9f1a099..a188612f1 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/Factories/EventTypeValidationRulesFactory.php +++ b/app/Http/Controllers/Apis/Protected/Summit/Factories/EventTypeValidationRulesFactory.php @@ -49,15 +49,17 @@ public static function build(array $data, $update = false){ } $base_rules = [ - 'name' => $name_rule, - 'color' => 'sometimes|hex_color', - 'black_out_times' => 'sometimes|boolean', - 'use_sponsors' => 'sometimes|boolean', - 'are_sponsors_mandatory' => 'sometimes|boolean|required_with:use_sponsors', - 'allows_attachment' => 'sometimes|boolean', - 'allows_level' => 'sometimes|boolean', - 'is_private' => 'sometimes|boolean', - 'is_default' => 'sometimes|boolean', + 'name' => $name_rule, + 'color' => 'sometimes|hex_color', + 'black_out_times' => 'sometimes|boolean', + 'use_sponsors' => 'sometimes|boolean', + 'are_sponsors_mandatory' => 'sometimes|boolean|required_with:use_sponsors', + 'allows_attachment' => 'sometimes|boolean', + 'allows_level' => 'sometimes|boolean', + 'allows_location' => 'sometimes|boolean', + 'allows_publishing_dates' => 'sometimes|boolean', + 'is_private' => 'sometimes|boolean', + 'is_default' => 'sometimes|boolean', ]; $specific_rules = []; @@ -75,11 +77,12 @@ public static function build(array $data, $update = false){ 'min_moderators' => 'sometimes|integer|required_with:use_moderator', 'max_moderators' => 'sometimes|integer|required_with:use_moderator|greater_than_field:min_moderators', 'should_be_available_on_cfp' => 'sometimes|boolean', - 'moderator_label' => 'sometimes|string' + 'moderator_label' => 'sometimes|string', + 'allow_custom_ordering' => 'sometimes|boolean', + 'allow_attendee_vote' => 'sometimes|boolean', ]; } break; - } return array_merge($base_rules, $specific_rules); } diff --git a/app/Http/Controllers/Apis/Protected/Summit/Factories/PresentationCategoryGroupValidationRulesFactory.php b/app/Http/Controllers/Apis/Protected/Summit/Factories/PresentationCategoryGroupValidationRulesFactory.php index 792af56b3..ed15cb039 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/Factories/PresentationCategoryGroupValidationRulesFactory.php +++ b/app/Http/Controllers/Apis/Protected/Summit/Factories/PresentationCategoryGroupValidationRulesFactory.php @@ -47,6 +47,9 @@ public static function build(array $data, $update = false){ 'name' => 'required|string', 'description' => 'sometimes|string', 'color' => 'sometimes|hex_color', + 'max_attendee_votes' => 'sometimes|integer|min:0', + 'begin_attendee_voting_period_date' => 'sometimes|date_format:U', + 'end_attendee_voting_period_date' => 'sometimes|date_format:U|required_with:submission_begin_date|after:begin_attendee_voting_period_date', ]; if($update){ diff --git a/app/Http/Controllers/Apis/Protected/Summit/Factories/SummitEventValidationRulesFactory.php b/app/Http/Controllers/Apis/Protected/Summit/Factories/SummitEventValidationRulesFactory.php index 8b247c4aa..5c68f9d48 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/Factories/SummitEventValidationRulesFactory.php +++ b/app/Http/Controllers/Apis/Protected/Summit/Factories/SummitEventValidationRulesFactory.php @@ -12,7 +12,6 @@ * limitations under the License. **/ - /** * Class SummitEventValidationRulesFactory * @package App\Http\Controllers @@ -66,6 +65,7 @@ public static function build(array $data, bool $update = false): array 'disclaimer_accepted' => 'sometimes|boolean', 'created_by_id' => 'sometimes|integer', 'show_sponsors' => 'sometimes|boolean', + 'custom_order' => 'sometimes|integer', ]; } @@ -99,6 +99,7 @@ public static function build(array $data, bool $update = false): array 'moderator_speaker_id' => 'sometimes|integer', 'extra_questions' => 'sometimes|extra_question_dto_array', 'links' => 'sometimes|url_array', + 'custom_order' => 'sometimes|integer', // group event 'groups' => 'sometimes|int_array', 'selection_plan_id' => 'sometimes|integer', diff --git a/app/Http/Controllers/Apis/Protected/Summit/OAuth2PresentationApiController.php b/app/Http/Controllers/Apis/Protected/Summit/OAuth2PresentationApiController.php index 5592423f1..9a8d086f0 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/OAuth2PresentationApiController.php +++ b/app/Http/Controllers/Apis/Protected/Summit/OAuth2PresentationApiController.php @@ -1289,4 +1289,97 @@ public function importAssetsFromMUX($summit_id) } } + /** + * Attendees Votes + */ + + /** + * @param $summit_id + * @param $presentation_id + * @return \Illuminate\Http\JsonResponse|mixed + */ + public function getAttendeeVotes($summit_id, $presentation_id){ + try { + + $summit = SummitFinderStrategyFactory::build($this->summit_repository, $this->resource_server_context)->find($summit_id); + if (is_null($summit)) return $this->error404(); + + return $this->ok(); + } catch (EntityNotFoundException $ex1) { + Log::warning($ex1); + return $this->error404(); + } catch (ValidationException $ex2) { + Log::warning($ex2); + return $this->error412($ex2->getMessages()); + } catch (Exception $ex) { + Log::error($ex); + return $this->error500($ex); + } + } + + /** + * @param $summit_id + * @param $presentation_id + * @return \Illuminate\Http\JsonResponse|mixed + */ + public function castAttendeeVote($summit_id, $presentation_id){ + try { + + $summit = SummitFinderStrategyFactory::build($this->summit_repository, $this->resource_server_context)->find($summit_id); + if (is_null($summit)) return $this->error404(); + + $current_member = $this->resource_server_context->getCurrentUser(); + if (is_null($current_member)) return $this->error403(); + + $vote = $this->presentation_service->castAttendeeVote($summit, $current_member, intval($presentation_id)); + + return $this->created(SerializerRegistry::getInstance()->getSerializer + ($vote) + ->serialize + ( + self::getExpands(), + self::getFields(), + self::getRelations() + ) + ); + } catch (EntityNotFoundException $ex1) { + Log::warning($ex1); + return $this->error404(); + } catch (ValidationException $ex2) { + Log::warning($ex2); + return $this->error412($ex2->getMessages()); + } catch (Exception $ex) { + Log::error($ex); + return $this->error500($ex); + } + } + + /** + * @param $summit_id + * @param $presentation_id + * @return \Illuminate\Http\JsonResponse|mixed + */ + public function unCastAttendeeVote($summit_id, $presentation_id){ + try { + + $summit = SummitFinderStrategyFactory::build($this->summit_repository, $this->resource_server_context)->find($summit_id); + if (is_null($summit)) return $this->error404(); + + $current_member = $this->resource_server_context->getCurrentUser(); + if (is_null($current_member)) return $this->error403(); + + $this->presentation_service->unCastAttendeeVote($summit, $current_member, intval($presentation_id)); + + return $this->deleted(); + } catch (EntityNotFoundException $ex1) { + Log::warning($ex1); + return $this->error404(); + } catch (ValidationException $ex2) { + Log::warning($ex2); + return $this->error412($ex2->getMessages()); + } catch (Exception $ex) { + Log::error($ex); + return $this->error500($ex); + } + } } \ No newline at end of file diff --git a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitEventsApiController.php b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitEventsApiController.php index 9f5f73b40..eaee75cf0 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitEventsApiController.php +++ b/app/Http/Controllers/Apis/Protected/Summit/OAuth2SummitEventsApiController.php @@ -131,12 +131,12 @@ public function getEvents($summit_id) ( $response->toArray ( - Request::input('expand', ''), - [], - [], + self::getExpands(), + self::getFields(), + self::getRelations(), [ - 'current_user' => $this->resource_server_context->getCurrentUser(true)] - , + 'current_user' => $this->resource_server_context->getCurrentUser(true) + ], $this->getSerializerType() ) ); @@ -180,8 +180,8 @@ public function getEventsCSV($summit_id) $filename = "activities-" . date('Ymd'); $list = $response->toArray ( - null, - [], + self::getExpands(), + self::getFields(), ['none'], [ 'current_user' => $this->resource_server_context->getCurrentUser(true) @@ -230,12 +230,6 @@ public function getScheduledEvents($summit_id) { try { - $expand = Request::input('expand', ''); - $fields = Request::input('fields', ''); - $relations = Request::input('relations', ''); - $relations = !empty($relations) ? explode(',', $relations) :[]; - $fields = !empty($fields) ? explode(',', $fields) :[]; - $params = [ 'summit_id' => $summit_id, 'current_user' => $this->resource_server_context->getCurrentUser(true) @@ -245,9 +239,9 @@ public function getScheduledEvents($summit_id) $response = $strategy->getEvents($params); return $this->ok($response->toArray ( - $expand, - $fields, - $relations, + self::getExpands(), + self::getFields(), + self::getRelations(), $params, $this->getSerializerType() )); @@ -331,9 +325,91 @@ public function getAllEvents() ( $response->toArray ( - Request::input('expand', ''), - [], - [], + self::getExpands(), + self::getFields(), + self::getRelations(), + [ + 'current_user' => $this->resource_server_context->getCurrentUser(true) + ], + $this->getSerializerType() + ) + ); + } + catch (EntityNotFoundException $ex1) + { + Log::warning($ex1); + return $this->error404(); + } + catch (ValidationException $ex2) + { + Log::warning($ex2); + return $this->error412($ex2->getMessages()); + } + catch (Exception $ex) + { + Log::error($ex); + return $this->error500($ex); + } + } + + /** + * @return \Illuminate\Http\JsonResponse|mixed + */ + public function getAllPresentations($summit_id) + { + try + { + $strategy = new RetrieveAllSummitPresentationsStrategy($this->repository, $this->event_repository, $this->resource_server_context); + $response = $strategy->getEvents(['summit_id' => intval($summit_id)]); + + $response = $strategy->getEvents(); + return $this->ok + ( + $response->toArray + ( + self::getExpands(), + self::getFields(), + self::getRelations(), + [ + 'current_user' => $this->resource_server_context->getCurrentUser(true) + ], + $this->getSerializerType() + ) + ); + } + catch (EntityNotFoundException $ex1) + { + Log::warning($ex1); + return $this->error404(); + } + catch (ValidationException $ex2) + { + Log::warning($ex2); + return $this->error412($ex2->getMessages()); + } + catch (Exception $ex) + { + Log::error($ex); + return $this->error500($ex); + } + } + + /** + * @return \Illuminate\Http\JsonResponse|mixed + */ + public function getAllVoteablePresentations($summit_id) + { + try + { + $strategy = new RetrieveAllSummitVoteablePresentationsStrategy($this->repository, $this->event_repository, $this->resource_server_context); + $response = $strategy->getEvents(['summit_id' => intval($summit_id)]); + return $this->ok + ( + $response->toArray + ( + self::getExpands(), + self::getFields(), + self::getRelations(), [ 'current_user' => $this->resource_server_context->getCurrentUser(true) ], @@ -371,9 +447,9 @@ public function getAllScheduledEvents() ( $response->toArray ( - Request::input('expand', ''), - [], - [], + self::getExpands(), + self::getFields(), + self::getRelations(), [ 'current_user' => $this->resource_server_context->getCurrentUser(true) ], @@ -401,14 +477,11 @@ public function getAllScheduledEvents() /** * @param $summit_id * @param $event_id - * @param string $expand - * @param string $fields - * @param string $relations * @param bool $published * @return array * @throws EntityNotFoundException */ - private function _getSummitEvent($summit_id, $event_id, $expand = '', $fields = '', $relations = '', $published = true) + private function _getSummitEvent($summit_id, $event_id, $published = true) { $summit = SummitFinderStrategyFactory::build($this->repository, $this->resource_server_context)->find($summit_id); if (is_null($summit)) throw new EntityNotFoundException; @@ -416,14 +489,12 @@ private function _getSummitEvent($summit_id, $event_id, $expand = '', $fields = $event = $published ? $summit->getScheduleEvent(intval($event_id)) : $summit->getEvent(intval($event_id)); if (is_null($event)) throw new EntityNotFoundException; - $relations = !empty($relations) ? explode(',', $relations) : array(); - $fields = !empty($fields) ? explode(',', $fields) : array(); return SerializerRegistry::getInstance()->getSerializer($event, $this->getSerializerType())->serialize ( - $expand, - $fields, - $relations, + self::getExpands(), + self::getFields(), + self::getRelations(), [ 'current_user' => $this->resource_server_context->getCurrentUser(true) ] @@ -437,12 +508,7 @@ private function _getSummitEvent($summit_id, $event_id, $expand = '', $fields = public function getEvent($summit_id, $event_id) { try { - - $expand = Request::input('expand', ''); - $fields = Request::input('fields', ''); - $relations = Request::input('relations', ''); - - return $this->ok($this->_getSummitEvent($summit_id, $event_id, $expand, $fields, $relations, false)); + return $this->ok($this->_getSummitEvent($summit_id, $event_id, false)); } catch (EntityNotFoundException $ex1) { Log::warning($ex1); @@ -463,11 +529,7 @@ public function getScheduledEvent($summit_id, $event_id) { try { - $expand = Request::input('expand', ''); - $fields = Request::input('fields', ''); - $relations = Request::input('relations', ''); - - return $this->ok($this->_getSummitEvent($summit_id, $event_id, $expand, $fields, $relations, true)); + return $this->ok($this->_getSummitEvent($summit_id, $event_id)); } catch (EntityNotFoundException $ex1) { Log::warning($ex1); @@ -528,7 +590,6 @@ public function addEvent($summit_id) if(!Request::isJson()) return $this->error400(); $data = Request::json(); - $current_member = $this->resource_server_context->getCurrentUser(); $payload = $data->all(); // Creates a Validator instance and validates the data. @@ -557,9 +618,9 @@ public function addEvent($summit_id) $this->getSerializerType() )->serialize ( - Request::input('expand', ''), - [], - [], + self::getExpands(), + self::getFields(), + self::getRelations(), [ 'current_user' => $this->resource_server_context->getCurrentUser(true) ] @@ -619,9 +680,9 @@ public function updateEvent($summit_id, $event_id) $event = $this->service->updateEvent($summit, $event_id, HTMLCleaner::cleanData($payload, $fields)); return $this->ok(SerializerRegistry::getInstance()->getSerializer($event, $this->getSerializerType())->serialize( - Request::input('expand', ''), - [], - [], + self::getExpands(), + self::getFields(), + self::getRelations(), [ 'current_user' => $this->resource_server_context->getCurrentUser(true) ] @@ -658,12 +719,12 @@ public function publishEvent($summit_id, $event_id) if(!Request::isJson()) return $this->error400(); $data = Request::json(); - $rules = array - ( + $rules = [ + 'location_id' => 'sometimes|required|integer', 'start_date' => 'sometimes|required|date_format:U', 'end_date' => 'sometimes|required_with:start_date|date_format:U|after:start_date', - ); + ]; // Creates a Validator instance and validates the data. $validation = Validator::make($data->all(), $rules); @@ -677,9 +738,18 @@ public function publishEvent($summit_id, $event_id) ); } - $this->service->publishEvent($summit, $event_id, $data->all()); + $event = $this->service->publishEvent($summit, $event_id, $data->all()); - return $this->updated(); + return $this->updated( + SerializerRegistry::getInstance()->getSerializer($event, $this->getSerializerType())->serialize( + self::getExpands(), + self::getFields(), + self::getRelations(), + [ + 'current_user' => $this->resource_server_context->getCurrentUser(true) + ] + ) + ); } catch (ValidationException $ex1) { @@ -710,7 +780,6 @@ public function unPublishEvent($summit_id, $event_id) if(!Request::isJson()) return $this->error400(); - $this->service->unPublishEvent($summit, $event_id); return $this->deleted(); @@ -824,7 +893,9 @@ public function getEventFeedback($summit_id, $event_id) $response = $this->event_feedback_repository->getByEvent($event, new PagingInfo($page, $per_page), $filter, $order); - return $this->ok($response->toArray(Request::input('expand', ''))); + return $this->ok($response->toArray( + self::getExpands() + )); } catch(FilterParserException $ex1){ @@ -887,7 +958,7 @@ private function _addMyEventFeedback($summit_id, $event_id, $returnId = false){ return $this->created(SerializerRegistry::getInstance()->getSerializer($feedback)->serialize ( - Request::input('expand', '') + self::getExpands() )); } catch (EntityNotFoundException $ex) { @@ -956,7 +1027,7 @@ private function _updateMyEventFeedback($summit_id, $event_id, $returnId = false return $this->updated(SerializerRegistry::getInstance()->getSerializer($feedback)->serialize ( - Request::input('expand', '') + self::getExpands() )); } catch (EntityNotFoundException $ex) { @@ -995,7 +1066,7 @@ public function getMyEventFeedback($summit_id, $event_id){ return $this->ok(SerializerRegistry::getInstance()->getSerializer($feedback)->serialize ( - Request::input('expand', '') + self::getExpands(), )); } catch (EntityNotFoundException $ex) { @@ -1107,7 +1178,13 @@ public function getUnpublishedEvents($summit_id){ } $response = $strategy->getEvents(['summit_id' => $summit_id]); - return $this->ok($response->toArray(Request::input('expand', ''),[],[],[], $serializer_type)); + return $this->ok($response->toArray + ( + + self::getExpands(), + self::getFields(), + self::getRelations(),[], $serializer_type + )); } catch (EntityNotFoundException $ex1) { @@ -1504,7 +1581,6 @@ public function updateEventLiveInfo($summit_id, $event_id) ); } - $event = $this->service->updateEvent($summit, $event_id, [ 'streaming_url' => $payload['streaming_url'], @@ -1512,9 +1588,9 @@ public function updateEventLiveInfo($summit_id, $event_id) ]); return $this->ok(SerializerRegistry::getInstance()->getSerializer($event, $this->getSerializerType())->serialize( - Request::input('expand', ''), - [], - [], + self::getExpands(), + self::getFields(), + self::getRelations(), [ 'current_user' => $this->resource_server_context->getCurrentUser(true) ] diff --git a/app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrieveAllPublishedSummitEventsStrategy.php b/app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrieveAllPublishedSummitEventsStrategy.php index 16bfec6f5..e5157c12e 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrieveAllPublishedSummitEventsStrategy.php +++ b/app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrieveAllPublishedSummitEventsStrategy.php @@ -27,7 +27,7 @@ final class RetrieveAllPublishedSummitEventsStrategy extends RetrieveAllSummitEv protected function getValidFilters() { $valid_filters = parent::getValidFilters(); - $valid_filters['published'] = array('=='); + $valid_filters['published'] = ['==']; return $valid_filters; } @@ -38,6 +38,8 @@ protected function buildFilter() { $filter = parent::buildFilter(); $filter->addFilterCondition(FilterParser::buildFilter('published','==','1')); + $filter->addFilterCondition(FilterParser::buildFilter('type_allows_publishing_dates','==','1')); + $filter->addFilterCondition(FilterParser::buildFilter('type_allows_location','==','1')); return $filter; } } \ No newline at end of file diff --git a/app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrieveAllSummitEventsBySummitStrategy.php b/app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrieveAllSummitEventsBySummitStrategy.php index 8f96a9f43..33868882d 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrieveAllSummitEventsBySummitStrategy.php +++ b/app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrieveAllSummitEventsBySummitStrategy.php @@ -84,17 +84,6 @@ public function getEvents(array $params = []) return parent::getEvents($params); } - /** - * @return array - */ - protected function getValidFilters() - { - $valid_filters = parent::getValidFilters(); - $valid_filters['summit_id'] = ['==']; - $valid_filters['published'] = ['==']; - return $valid_filters; - } - /** * @return null|Filter */ @@ -116,7 +105,7 @@ protected function buildFilter(){ * @param Order|null $order * @return PagingResponse */ - public function retrieveEventsFromSource(PagingInfo $paging_info, Filter $filter = null, Order $order = null) + public function retrieveEventsFromSource(PagingInfo $paging_info, Filter $filter = null, Order $order = null): PagingResponse { return $this->events_repository->getAllByPage($paging_info, $filter, $order); } diff --git a/app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrieveAllSummitEventsStrategy.php b/app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrieveAllSummitEventsStrategy.php index cddd59160..3d73bf208 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrieveAllSummitEventsStrategy.php +++ b/app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrieveAllSummitEventsStrategy.php @@ -51,13 +51,4 @@ public function retrieveEventsFromSource(PagingInfo $paging_info, Filter $filter return $this->event_repository->getAllByPage($paging_info, $filter, $order); } - /** - * @return array - */ - protected function getValidFilters() - { - $valid_filters = parent::getValidFilters(); - $valid_filters['summit_id'] = array('=='); - return $valid_filters; - } } \ No newline at end of file diff --git a/app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrieveAllSummitPresentationsStrategy.php b/app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrieveAllSummitPresentationsStrategy.php new file mode 100644 index 000000000..8799639b4 --- /dev/null +++ b/app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrieveAllSummitPresentationsStrategy.php @@ -0,0 +1,34 @@ +addFilterCondition(FilterParser::buildFilter('class_name','==',Presentation::ClassName)); + return $filter; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrieveAllSummitVoteablePresentationsStrategy.php b/app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrieveAllSummitVoteablePresentationsStrategy.php new file mode 100644 index 000000000..5dd372817 --- /dev/null +++ b/app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrieveAllSummitVoteablePresentationsStrategy.php @@ -0,0 +1,35 @@ +addFilterCondition(FilterParser::buildFilter('type_allows_attendee_vote','==',1)); + return $filter; + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrievePublishedSummitEventsBySummitStrategy.php b/app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrievePublishedSummitEventsBySummitStrategy.php index fe010107c..3cd64bef2 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrievePublishedSummitEventsBySummitStrategy.php +++ b/app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrievePublishedSummitEventsBySummitStrategy.php @@ -37,6 +37,8 @@ protected function buildFilter() { $filter = parent::buildFilter(); $filter->addFilterCondition(FilterParser::buildFilter('published','==','1')); + $filter->addFilterCondition(FilterParser::buildFilter('type_allows_publishing_dates','==','1')); + $filter->addFilterCondition(FilterParser::buildFilter('type_allows_location','==','1')); return $filter; } } \ No newline at end of file diff --git a/app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrieveSummitEventsStrategy.php b/app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrieveSummitEventsStrategy.php index 116061d5f..dc7e3780b 100644 --- a/app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrieveSummitEventsStrategy.php +++ b/app/Http/Controllers/Apis/Protected/Summit/Strategies/events/RetrieveSummitEventsStrategy.php @@ -12,6 +12,8 @@ * limitations under the License. **/ use models\exceptions\ValidationException; +use models\summit\Presentation; +use models\summit\SummitEvent; use utils\Filter; use utils\Order; use utils\OrderParser; @@ -106,6 +108,8 @@ protected function buildOrder(){ 'id', 'created', 'track', + 'random', + 'custom_order', ]); } return $order; @@ -137,6 +141,7 @@ protected function getValidFilters() 'track_id' => ['=='], 'speaker_id' => ['=='], 'sponsor_id' => ['=='], + 'summit_id' => ['=='], 'sponsor' => ['=@', '=='], 'location_id' => ['=='], 'speaker' => ['=@', '=='], @@ -148,6 +153,12 @@ protected function getValidFilters() 'selection_plan_id' => ['=='], 'created_by_fullname' => ['=@', '=='], 'created_by_email' => ['=@', '=='], + 'type_allows_publishing_dates' => ['=='], + 'type_allows_location' => ['=='], + 'type_allows_attendee_vote' => ['=='], + 'type_allows_custom_ordering' => ['=='], + 'published' => ['=='], + 'class_name' => ['=='], ]; } @@ -170,12 +181,19 @@ protected function getFilterValidatorRules():array{ 'summit_type_id' => 'sometimes|integer', 'event_type_id' => 'sometimes|integer', 'track_id' => 'sometimes|integer', + 'summit_id' => 'sometimes|integer', 'speaker_id' => 'sometimes|integer', 'location_id' => 'sometimes|integer', 'id' => 'sometimes|integer', 'selection_plan_id' => 'sometimes|integer', 'created_by_fullname' => 'sometimes|string', 'created_by_email' => 'sometimes|string', + 'type_allows_publishing_dates' => 'sometimes|boolean', + 'type_allows_location' => 'sometimes|boolean', + 'type_allows_attendee_vote' => 'sometimes|boolean', + 'type_allows_custom_ordering' => 'sometimes|boolean', + 'published' => 'sometimes|boolean', + 'class_name' => 'sometimes|string|in:'.implode(',',[ Presentation::ClassName, SummitEvent::ClassName]) ]; } } \ No newline at end of file diff --git a/app/Http/Utils/Order.php b/app/Http/Utils/Order.php index a40d660d9..f87cd75d3 100644 --- a/app/Http/Utils/Order.php +++ b/app/Http/Utils/Order.php @@ -44,6 +44,20 @@ public function hasOrder(string $field):bool{ return false; } + /** + * @param string $field + * @return bool + */ + public function removeOrder(string $field):bool{ + foreach ($this->ordering as $index => $order){ + if ($order instanceof OrderElement && $order->getField() == $field) { + unset($this->ordering[$index]); + return true; + } + } + return false; + } + /** * @param QueryBuilder $query * @param array $mappings diff --git a/app/ModelSerializers/SerializerRegistry.php b/app/ModelSerializers/SerializerRegistry.php index 07fc1f097..c9878b3e8 100644 --- a/app/ModelSerializers/SerializerRegistry.php +++ b/app/ModelSerializers/SerializerRegistry.php @@ -217,6 +217,10 @@ private function __construct() IPresentationSerializerTypes::TrackChairs_CSV => TrackChairPresentationCSVSerializer::class ]; + $this->registry['PresentationAttendeeVoteSerializer'] = [ + self::SerializerType_Public => PresentationAttendeeVoteSerializer::class, + ]; + $this->registry['SummitGroupEvent'] = SummitGroupEventSerializer::class; $this->registry['TrackTagGroup'] = TrackTagGroupSerializer::class; diff --git a/app/ModelSerializers/Summit/Presentation/PresentationAttendeeVoteSerializer.php b/app/ModelSerializers/Summit/Presentation/PresentationAttendeeVoteSerializer.php new file mode 100644 index 000000000..b75e7a3c4 --- /dev/null +++ b/app/ModelSerializers/Summit/Presentation/PresentationAttendeeVoteSerializer.php @@ -0,0 +1,26 @@ + 'voter_id:json_int', + 'PresentationId' => 'presentation_id:json_int', + ]; + +} \ No newline at end of file diff --git a/app/ModelSerializers/Summit/Presentation/PresentationCategoryGroupSerializer.php b/app/ModelSerializers/Summit/Presentation/PresentationCategoryGroupSerializer.php index 22704fcf9..214bf3497 100644 --- a/app/ModelSerializers/Summit/Presentation/PresentationCategoryGroupSerializer.php +++ b/app/ModelSerializers/Summit/Presentation/PresentationCategoryGroupSerializer.php @@ -24,6 +24,9 @@ class PresentationCategoryGroupSerializer extends SilverStripeSerializer 'Description' => 'description:json_string', 'ClassName' => 'class_name:json_string', 'SummitId' => 'summit_id:json_int', + 'BeginAttendeeVotingPeriodDate' => 'begin_attendee_voting_period_date:datetime_epoch', + 'EndAttendeeVotingPeriodDate' => 'end_attendee_voting_period_date:datetime_epoch', + 'MaxAttendeeVotes' => 'max_attendee_votes:json_int' ]; /** diff --git a/app/ModelSerializers/Summit/Presentation/PresentationSerializer.php b/app/ModelSerializers/Summit/Presentation/PresentationSerializer.php index 11860807f..89d46878c 100644 --- a/app/ModelSerializers/Summit/Presentation/PresentationSerializer.php +++ b/app/ModelSerializers/Summit/Presentation/PresentationSerializer.php @@ -33,6 +33,8 @@ class PresentationSerializer extends SummitEventSerializer 'SelectionStatus' => 'selection_status:string', 'DisclaimerAcceptedDate' => 'disclaimer_accepted_date:datetime_epoch', 'DisclaimerAccepted' => 'disclaimer_accepted:json_boolean', + 'CustomOrder' => 'custom_order:json_int', + 'AttendeeVotesCount' => 'attendee_votes_count:json_int', ]; protected static $allowed_fields = [ @@ -49,6 +51,8 @@ class PresentationSerializer extends SummitEventSerializer 'selection_status', 'disclaimer_accepted_date', 'disclaimer_accepted', + 'custom_order', + 'attendee_votes_count', ]; protected static $allowed_relations = [ diff --git a/app/ModelSerializers/Summit/Presentation/PresentationTypeSerializer.php b/app/ModelSerializers/Summit/Presentation/PresentationTypeSerializer.php index ffd307b46..986da76bc 100644 --- a/app/ModelSerializers/Summit/Presentation/PresentationTypeSerializer.php +++ b/app/ModelSerializers/Summit/Presentation/PresentationTypeSerializer.php @@ -30,6 +30,8 @@ final class PresentationTypeSerializer extends SummitEventTypeSerializer 'ModeratorMandatory' => 'is_moderator_mandatory:json_boolean', 'ModeratorLabel' => 'moderator_label:json_string', 'ShouldBeAvailableOnCfp' => 'should_be_available_on_cfp:json_boolean', + 'AllowCustomOrdering' => 'allow_custom_ordering:json_boolean', + 'AllowAttendeeVote' => 'allow_attendee_vote:json_boolean', ]; /** diff --git a/app/ModelSerializers/Summit/Registration/SummitAttendeeSerializer.php b/app/ModelSerializers/Summit/Registration/SummitAttendeeSerializer.php index e3e2bfc32..b7e73ba93 100644 --- a/app/ModelSerializers/Summit/Registration/SummitAttendeeSerializer.php +++ b/app/ModelSerializers/Summit/Registration/SummitAttendeeSerializer.php @@ -38,6 +38,7 @@ class SummitAttendeeSerializer extends SilverStripeSerializer protected static $allowed_relations = [ 'extra_questions', 'tickets', + 'presentation_votes' ]; /** @@ -83,6 +84,15 @@ public function serialize($expand = null, array $fields = array(), array $relati $values['extra_questions'] = $extra_question_answers; } + if (in_array('presentation_votes', $relations)) { + $presentation_votes = []; + + foreach ($attendee->getPresentationVotes() as $vote) { + $presentation_votes[] = $vote->getId(); + } + $values['presentation_votes'] = $presentation_votes; + } + if($attendee->hasMember()) { $member = $attendee->getMember(); @@ -121,6 +131,17 @@ public function serialize($expand = null, array $fields = array(), array $relati $values['extra_questions'] = $extra_question_answers; } break; + case 'presentation_votes': { + if (!in_array('presentation_votes', $relations)) break; + unset($values['presentation_votes']); + $presentation_votes = []; + foreach($attendee->getPresentationVotes() as $vote) + { + $presentation_votes[] = SerializerRegistry::getInstance()->getSerializer($vote)->serialize(AbstractSerializer::getExpandForPrefix('presentation_votes', $expand)); + } + $values['presentation_votes'] = $presentation_votes; + } + break; case 'speaker': { if (!is_null($speaker)) { diff --git a/app/ModelSerializers/Summit/SummitEventSerializer.php b/app/ModelSerializers/Summit/SummitEventSerializer.php index bb36d7780..8e91bcd7e 100644 --- a/app/ModelSerializers/Summit/SummitEventSerializer.php +++ b/app/ModelSerializers/Summit/SummitEventSerializer.php @@ -37,6 +37,7 @@ class SummitEventSerializer extends SilverStripeSerializer 'AllowFeedBack' => 'allow_feedback:json_boolean', 'AvgFeedbackRate' => 'avg_feedback_rate:json_float', 'Published' => 'is_published:json_boolean', + 'PublishedDate' => 'published_date:datetime_epoch', 'HeadCount' => 'head_count:json_int', 'RSVPLink' => 'rsvp_link:json_string', 'RSVPTemplateId' => 'rsvp_template_id:json_int', @@ -92,6 +93,7 @@ class SummitEventSerializer extends SilverStripeSerializer 'created_by_id', 'updated_by_id', 'show_sponsors', + 'published_date', ]; protected static $allowed_relations = [ diff --git a/app/ModelSerializers/Summit/SummitEventTypeSerializer.php b/app/ModelSerializers/Summit/SummitEventTypeSerializer.php index e09ff08ff..e036072ed 100644 --- a/app/ModelSerializers/Summit/SummitEventTypeSerializer.php +++ b/app/ModelSerializers/Summit/SummitEventTypeSerializer.php @@ -23,16 +23,18 @@ class SummitEventTypeSerializer extends SilverStripeSerializer { protected static $array_mappings = [ - 'Type' => 'name:json_string', - 'ClassName' => 'class_name:json_string', - 'Color' => 'color:json_color', - 'BlackoutTimes' => 'black_out_times:json_boolean', - 'UseSponsors' => 'use_sponsors:json_boolean', - 'AreSponsorsMandatory' => 'are_sponsors_mandatory:json_boolean', - 'AllowsAttachment' => 'allows_attachment:json_boolean', - 'AllowsLevel' => 'allows_level:json_boolean', - 'Default' => 'is_default:json_boolean', - 'SummitId' => 'summit_id:json_int', + 'Type' => 'name:json_string', + 'ClassName' => 'class_name:json_string', + 'Color' => 'color:json_color', + 'BlackoutTimes' => 'black_out_times:json_boolean', + 'UseSponsors' => 'use_sponsors:json_boolean', + 'AreSponsorsMandatory' => 'are_sponsors_mandatory:json_boolean', + 'AllowsAttachment' => 'allows_attachment:json_boolean', + 'AllowsLevel' => 'allows_level:json_boolean', + 'AllowsPublishingDates' => 'allows_publishing_dates:json_boolean', + 'AllowsLocation' => 'allows_location:json_boolean', + 'Default' => 'is_default:json_boolean', + 'SummitId' => 'summit_id:json_int', ]; /** @@ -76,6 +78,7 @@ public function serialize($expand = null, array $fields = array(), array $relati } } } + return $values; } } \ No newline at end of file diff --git a/app/Models/Foundation/Summit/Events/ISummitEventType.php b/app/Models/Foundation/Summit/Events/ISummitEventType.php index 52b862793..f3683c6d2 100644 --- a/app/Models/Foundation/Summit/Events/ISummitEventType.php +++ b/app/Models/Foundation/Summit/Events/ISummitEventType.php @@ -28,4 +28,5 @@ interface ISummitEventType const Breaks = 'Breaks'; const Breakfast = 'Breakfast'; const MarketplaceHours = 'Marketplace Hours'; + } \ No newline at end of file diff --git a/app/Models/Foundation/Summit/Events/Presentations/Presentation.php b/app/Models/Foundation/Summit/Events/Presentations/Presentation.php index fd27fd202..ec62188b6 100644 --- a/app/Models/Foundation/Summit/Events/Presentations/Presentation.php +++ b/app/Models/Foundation/Summit/Events/Presentations/Presentation.php @@ -36,6 +36,8 @@ */ class Presentation extends SummitEvent { + const ClassName = 'Presentation'; + /** * SELECTION STATUS (TRACK CHAIRS LIST) */ @@ -138,6 +140,12 @@ class Presentation extends SummitEvent */ protected $disclaimer_accepted_date; + /** + * @ORM\Column(name="CustomOrder", type="integer") + * @var integer + */ + protected $custom_order; + /** * @ORM\ManyToOne(targetEntity="PresentationSpeaker", inversedBy="moderated_presentations", fetch="EXTRA_LAZY") * @ORM\JoinColumn(name="ModeratorID", referencedColumnName="ID", onDelete="SET NULL") @@ -191,19 +199,25 @@ class Presentation extends SummitEvent protected $answers; /** - * @ORM\OneToMany(targetEntity="models\summit\PresentationTrackChairView", mappedBy="presentation", cascade={"persist","remove"}, orphanRemoval=true) + * @ORM\OneToMany(targetEntity="models\summit\PresentationTrackChairView", mappedBy="presentation", cascade={"persist","remove"}, orphanRemoval=true, fetch="EXTRA_LAZY") * @var PresentationTrackChairView[] */ private $track_chair_views; /** - * @ORM\OneToMany(targetEntity="models\summit\PresentationVote", mappedBy="presentation", cascade={"persist","remove"}, orphanRemoval=true) + * @ORM\OneToMany(targetEntity="models\summit\PresentationVote", mappedBy="presentation", cascade={"persist","remove"}, orphanRemoval=true, fetch="EXTRA_LAZY") * @var PresentationVote[] */ private $votes; /** - * @ORM\OneToMany(targetEntity="models\summit\SummitCategoryChange", mappedBy="presentation", cascade={"persist","remove"}, orphanRemoval=true) + * @ORM\OneToMany(targetEntity="models\summit\PresentationAttendeeVote", mappedBy="presentation", cascade={"persist","remove"}, orphanRemoval=true, fetch="EXTRA_LAZY") + * @var PresentationAttendeeVote[] + */ + private $attendees_votes; + + /** + * @ORM\OneToMany(targetEntity="models\summit\SummitCategoryChange", mappedBy="presentation", cascade={"persist","remove"}, orphanRemoval=true, fetch="EXTRA_LAZY") * @var SummitCategoryChange[] */ private $category_changes_requests; @@ -215,7 +229,7 @@ class Presentation extends SummitEvent private $actions; /** - * @ORM\OneToMany(targetEntity="models\summit\PresentationExtraQuestionAnswer", mappedBy="presentation", cascade={"persist","remove"}, orphanRemoval=true) + * @ORM\OneToMany(targetEntity="models\summit\PresentationExtraQuestionAnswer", mappedBy="presentation", cascade={"persist","remove"}, orphanRemoval=true, fetch="EXTRA_LAZY") * @var PresentationExtraQuestionAnswer[] */ private $extra_question_answers; @@ -261,10 +275,12 @@ public function __construct() $this->selected_presentations = new ArrayCollection(); $this->actions = new ArrayCollection(); $this->extra_question_answers = new ArrayCollection(); + $this->attendees_votes = new ArrayCollection(); $this->to_record = false; $this->attending_media = false; $this->will_all_speakers_attend = false; $this->disclaimer_accepted_date = null; + $this->custom_order = 0; } /** @@ -1605,7 +1621,7 @@ public function getExtraQuestionAnswers() */ public function getExtraQuestionAnswerByQuestion(SummitSelectionPlanExtraQuestionType $question):?PresentationExtraQuestionAnswer{ $answer = $this->extra_question_answers->matching( - $criteria = Criteria::create() + Criteria::create() ->where(Criteria::expr()->eq("question", $question)) )->first(); return $answer ? $answer : null; @@ -1699,7 +1715,67 @@ public function clearMediaUploads():void{ Log::debug(sprintf("Presentation::clearMediaUploads as deleted path ( public ) %s.", $path)); $strategy->markAsDeleted($path); } - } } + + /** + * @return int + */ + public function getCustomOrder(): int + { + return $this->custom_order; + } + + /** + * @param int $custom_order + */ + public function setCustomOrder(int $custom_order): void + { + $this->custom_order = $custom_order; + } + + /** + * @return int + */ + public function getAttendeeVotesCount():int{ + return $this->attendees_votes->count(); + } + + /** + * @param SummitAttendee $attendee + * @return PresentationAttendeeVote + * @throws ValidationException + */ + public function castAttendeeVote(SummitAttendee $attendee):PresentationAttendeeVote{ + + // check that member did not vote yet... + if($this->attendees_votes->matching(Criteria::create() + ->where(Criteria::expr()->eq("voter", $attendee)))->count() > 0) + throw new ValidationException(sprintf("Attendee %s already vote on presentation %s", $attendee->getEmail(), $this->id)); + + $vote = new PresentationAttendeeVote($attendee, $this); + + $this->attendees_votes->add($vote); + $attendee->addPresentationVote($vote); + + return $vote; + } + + /** + * @param SummitAttendee $attendee + * @throws ValidationException + */ + public function unCastAttendeeVote(SummitAttendee $attendee):void{ + + $vote = $this->attendees_votes->matching(Criteria::create() + ->where(Criteria::expr()->eq("voter", $attendee)))->first(); + + if(!$vote) + throw new ValidationException(sprintf("Vote not found.")); + + $this->attendees_votes->removeElement($vote); + $attendee->removePresentationVote($vote); + $vote->clearVoter(); + $vote->clearPresentation(); + } } diff --git a/app/Models/Foundation/Summit/Events/Presentations/PresentationAttendeeVote.php b/app/Models/Foundation/Summit/Events/Presentations/PresentationAttendeeVote.php new file mode 100644 index 000000000..b6969a615 --- /dev/null +++ b/app/Models/Foundation/Summit/Events/Presentations/PresentationAttendeeVote.php @@ -0,0 +1,86 @@ + 'voter', + 'getPresentationId' => 'presentation', + ]; + + protected $hasPropertyMappings = [ + 'hasVoter' => 'voter', + 'hasPresentation' => 'presentation', + ]; + + /** + * @ORM\ManyToOne(targetEntity="models\summit\SummitAttendee", inversedBy="presentation_votes") + * @ORM\JoinColumn(name="SummitAttendeeID", referencedColumnName="ID", onDelete="SET NULL") + * @var SummitAttendee + */ + private $voter; + + /** + * @ORM\ManyToOne(targetEntity="models\summit\Presentation", inversedBy="attendees_votes") + * @ORM\JoinColumn(name="PresentationID", referencedColumnName="ID", onDelete="SET NULL") + * @var Presentation + */ + private $presentation; + + /** + * @return SummitAttendee + */ + public function getVoter(): SummitAttendee + { + return $this->voter; + } + + /** + * @return Presentation + */ + public function getPresentation(): Presentation + { + return $this->presentation; + } + + /** + * @param SummitAttendee $voter + * @param Presentation $presentation + */ + public function __construct(SummitAttendee $voter, Presentation $presentation) + { + parent::__construct(); + $this->voter = $voter; + $this->presentation = $presentation; + } + + public function clearVoter():void{ + $this->voter = null; + } + + public function clearPresentation():void{ + $this->presentation = null; + } + +} \ No newline at end of file diff --git a/app/Models/Foundation/Summit/Events/Presentations/PresentationCategoryGroup.php b/app/Models/Foundation/Summit/Events/Presentations/PresentationCategoryGroup.php index 936b7802d..8ec1dc582 100644 --- a/app/Models/Foundation/Summit/Events/Presentations/PresentationCategoryGroup.php +++ b/app/Models/Foundation/Summit/Events/Presentations/PresentationCategoryGroup.php @@ -11,10 +11,14 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ + use Doctrine\Common\Collections\Criteria; +use Illuminate\Support\Facades\Cache; +use Illuminate\Support\Facades\Log; use models\utils\SilverstripeBaseModel; -use Doctrine\ORM\Mapping AS ORM; +use Doctrine\ORM\Mapping as ORM; use Doctrine\Common\Collections\ArrayCollection; + /** * Class PresentationCategoryGroup * @ORM\Entity(repositoryClass="App\Repositories\Summit\DoctrinePresentationCategoryGroupRepository") @@ -49,17 +53,46 @@ class PresentationCategoryGroup extends SilverstripeBaseModel protected $description; /** - * @return mixed + * @ORM\Column(name="BeginAttendeeVotingPeriodDate", type="datetime") + * @var \DateTime + */ + protected $begin_attendee_voting_period_date; + + /** + * @ORM\Column(name="EndAttendeeVotingPeriodDate", type="datetime") + * @var \DateTime + */ + protected $end_attendee_voting_period_date; + + /** + * @ORM\Column(name="MaxUniqueAttendeeVotes", type="integer") + * @var int + */ + protected $max_attendee_votes; + + + public function __construct() + { + parent::__construct(); + $this->begin_attendee_voting_period_date = null; + $this->end_attendee_voting_period_date = null; + $this->max_attendee_votes = 0; + $this->categories = new ArrayCollection; + } + + + /** + * @return string */ - public function getName() + public function getName(): string { return $this->name; } /** - * @param mixed $name + * @param string $name */ - public function setName($name) + public function setName(string $name) { $this->name = $name; } @@ -83,7 +116,7 @@ public function setColor($color) /** * @return string */ - public function getDescription() + public function getDescription(): ?string { return $this->description; } @@ -91,7 +124,7 @@ public function getDescription() /** * @param string $description */ - public function setDescription($description) + public function setDescription(string $description) { $this->description = $description; } @@ -103,37 +136,34 @@ public function setDescription($description) */ protected $summit; - public function setSummit($summit){ + public function setSummit($summit) + { $this->summit = $summit; } /** * @return int */ - public function getSummitId(){ + public function getSummitId() + { try { return is_null($this->summit) ? 0 : $this->summit->getId(); - } - catch(\Exception $ex){ + } catch (\Exception $ex) { return 0; } } - public function clearSummit(){ + public function clearSummit() + { $this->summit = null; } /** * @return Summit */ - public function getSummit(){ - return $this->summit; - } - - public function __construct() + public function getSummit() { - parent::__construct(); - $this->categories = new ArrayCollection; + return $this->summit; } /** @@ -158,7 +188,8 @@ public function getCategories() /** * @param PresentationCategory $track */ - public function addCategory(PresentationCategory $track){ + public function addCategory(PresentationCategory $track) + { $track->addToGroup($this); $this->categories[] = $track; } @@ -166,7 +197,8 @@ public function addCategory(PresentationCategory $track){ /** * @param PresentationCategory $track */ - public function removeCategory(PresentationCategory $track){ + public function removeCategory(PresentationCategory $track) + { $track->removeFromGroup($this); $this->categories->removeElement($track); } @@ -175,12 +207,13 @@ public function removeCategory(PresentationCategory $track){ * @param int $category_id * @return PresentationCategory|null */ - public function getCategoryById($category_id){ + public function getCategoryById($category_id) + { /*$criteria = Criteria::create(); $criteria->where(Criteria::expr()->eq('id', intval($category_id))); $res = $this->categories->matching($criteria)->first(); return $res === false ? null : $res;*/ - $res = $this->categories->filter(function(PresentationCategory $t) use($category_id){ + $res = $this->categories->filter(function (PresentationCategory $t) use ($category_id) { return $t->getId() == $category_id; })->first(); @@ -191,7 +224,8 @@ public function getCategoryById($category_id){ * @param int $category_id * @return bool */ - public function hasCategory($category_id){ + public function hasCategory($category_id) + { return $this->getCategoryById($category_id) != null; } @@ -200,25 +234,138 @@ public function hasCategory($category_id){ /** * @return string */ - public function getClassName(){ + public function getClassName() + { return self::ClassName; } public static $metadata = [ - 'class_name' => self::ClassName, - 'id' => 'integer', - 'summit_id' => 'integer', - 'name' => 'string', - 'color' => 'string', + 'class_name' => self::ClassName, + 'id' => 'integer', + 'summit_id' => 'integer', + 'name' => 'string', + 'color' => 'string', 'description' => 'string', - 'categories' => 'array' + 'categories' => 'array', + 'begin_attendee_voting_period_date' => 'datetime', + 'end_attendee_voting_period_date' => 'datetime', + 'max_attendee_votes' => 'integer', ]; /** * @return array */ - public static function getMetadata(){ + public static function getMetadata() + { return self::$metadata; } + /** + * @return \DateTime + */ + public function getBeginAttendeeVotingPeriodDate(): ?\DateTime + { + return $this->begin_attendee_voting_period_date; + } + + /** + * @param \DateTime $begin_attendee_voting_period_date + */ + public function setBeginAttendeeVotingPeriodDate(?\DateTime $begin_attendee_voting_period_date): void + { + $this->begin_attendee_voting_period_date = $begin_attendee_voting_period_date; + } + + /** + * @return \DateTime + */ + public function getEndAttendeeVotingPeriodDate(): ?\DateTime + { + return $this->end_attendee_voting_period_date; + } + + /** + * @param \DateTime $end_attendee_voting_period_date + */ + public function setEndAttendeeVotingPeriodDate(?\DateTime $end_attendee_voting_period_date): void + { + $this->end_attendee_voting_period_date = $end_attendee_voting_period_date; + } + + /** + * @return int + */ + public function getMaxAttendeeVotes(): int + { + return $this->max_attendee_votes; + } + + /** + * @param int $max_attendee_votes + */ + public function setMaxAttendeeVotes(int $max_attendee_votes): void + { + $this->max_attendee_votes = $max_attendee_votes; + } + + /** + * @throws \Exception + */ + public function isAttendeeVotingPeriodOpen(): bool + { + $now = new \DateTime('now', new \DateTimeZone('UTC')); + if (!is_null($this->begin_attendee_voting_period_date) && !is_null($this->end_attendee_voting_period_date)) { + return $now >= $this->begin_attendee_voting_period_date && $now <= $this->end_attendee_voting_period_date; + } + return true; + } + + public function isNotLimitedAttendeeVotingCount(): bool + { + return $this->max_attendee_votes > 0; + } + + /** + * @param SummitAttendee $attendee + * @return bool + */ + public function canEmitAttendeeVote(SummitAttendee $attendee): bool + { + if ($this->isNotLimitedAttendeeVotingCount()) return true; + try { + $sql = <<prepareRawSQL($sql); + $stmt->execute + ([ + 'id' => $this->id, + 'attendee_id' => $attendee->getId(), + ]); + $res = $stmt->fetchAll(\PDO::FETCH_COLUMN); + $res = count($res) > 0 ? $res[0] : 0; + $res = !is_null($res) ? $res : 0; + Log::debug + ( + sprintf + ( + "PresentationCategoryGroup::canEmitAttendeeVote group %s attendee %s votes %s max vote %s", + $this->id, + $attendee->getId(), + $res, + $this->max_attendee_votes + ) + ); + return ($res + 1) < $this->max_attendee_votes; + } catch (\Exception $ex) { + Log::warning($ex); + } + return true; + } + } \ No newline at end of file diff --git a/app/Models/Foundation/Summit/Events/Presentations/PresentationType.php b/app/Models/Foundation/Summit/Events/Presentations/PresentationType.php index 61cd547ef..1d942bb68 100644 --- a/app/Models/Foundation/Summit/Events/Presentations/PresentationType.php +++ b/app/Models/Foundation/Summit/Events/Presentations/PresentationType.php @@ -87,6 +87,18 @@ class PresentationType extends SummitEventType */ protected $allowed_media_upload_types; + /** + * @ORM\Column(name="AllowAttendeeVote", type="boolean") + * @var bool + */ + protected $allow_attendee_vote; + + /** + * @ORM\Column(name="AllowCustomOrdering", type="boolean") + * @var bool + */ + protected $allow_custom_ordering; + /** * @param Summit $summit * @param string $type @@ -302,6 +314,8 @@ public function __construct() $this->max_speakers = 0; $this->min_moderators = 0; $this->min_speakers = 0; + $this->allow_attendee_vote = false; + $this->allow_custom_ordering = false; } public function addAllowedMediaUploadType(SummitMediaUploadType $type) @@ -325,4 +339,37 @@ public function getAllowedMediaUploadTypes() { return $this->allowed_media_upload_types; } + + /** + * @return bool + */ + public function isAllowAttendeeVote(): bool + { + return $this->allow_attendee_vote; + } + + /** + * @param bool $allow_attendee_vote + */ + public function setAllowAttendeeVote(bool $allow_attendee_vote): void + { + $this->allow_attendee_vote = $allow_attendee_vote; + } + + /** + * @return bool + */ + public function isAllowCustomOrdering(): bool + { + return $this->allow_custom_ordering; + } + + /** + * @param bool $allow_custom_ordering + */ + public function setAllowCustomOrdering(bool $allow_custom_ordering): void + { + $this->allow_custom_ordering = $allow_custom_ordering; + } + } \ No newline at end of file diff --git a/app/Models/Foundation/Summit/Events/SummitEvent.php b/app/Models/Foundation/Summit/Events/SummitEvent.php index af660f289..5b40a5e2a 100644 --- a/app/Models/Foundation/Summit/Events/SummitEvent.php +++ b/app/Models/Foundation/Summit/Events/SummitEvent.php @@ -19,6 +19,7 @@ use App\Models\Utils\Traits\HasImageTrait; use Doctrine\Common\Collections\Criteria; use Doctrine\ORM\Event\PreUpdateEventArgs; +use Google\Service\AdMob\Date; use models\exceptions\ValidationException; use models\main\Company; use models\main\File; @@ -58,6 +59,8 @@ class SummitEvent extends SilverstripeBaseModel { use One2ManyPropertyTrait; + const ClassName = 'SummitEvent'; + protected $getIdMappings = [ 'getCreatedById' => 'created_by', 'getUpdatedById' => 'updated_by', @@ -443,9 +446,9 @@ public function setSocialSummary($social_summary) } /** - * @return DateTime + * @return DateTime|null */ - public function getPublishedDate() + public function getPublishedDate():?DateTime { return $this->published_date; } @@ -604,10 +607,15 @@ public function clearLocation() return $this; } + public function clearPublishingDates():void{ + $this->start_date = null; + $this->end_date = null; + } + /** - * @return SummitAbstractLocation + * @return SummitAbstractLocation|null */ - public function getLocation() + public function getLocation():?SummitAbstractLocation { return $this->location; } @@ -615,9 +623,12 @@ public function getLocation() /** * @param SummitAbstractLocation $location * @return $this + * @throws ValidationException */ public function setLocation(SummitAbstractLocation $location) { + if(!$this->type->isAllowsLocation()) + throw new ValidationException("Event Type does not allows Location."); $this->location = $location; return $this; } @@ -698,27 +709,30 @@ public function clearTags() public function publish() { if ($this->isPublished()) - throw new ValidationException('Already published Summit Event'); - - $start_date = $this->getStartDate(); - $end_date = $this->getEndDate(); - - if ((is_null($start_date) || is_null($end_date))) - throw new ValidationException('To publish this event you must define a start/end datetime!'); + throw new ValidationException('Already published Summit Event.'); $summit = $this->getSummit(); if (is_null($summit)) - throw new ValidationException('To publish you must assign a summit'); + throw new ValidationException('To publish you must assign a summit.'); - $timezone = $summit->getTimeZoneId(); + if($this->type->isAllowsPublishingDates()) { - if (empty($timezone)) { - throw new ValidationException('Invalid Summit TimeZone!'); - } + $start_date = $this->getStartDate(); + $end_date = $this->getEndDate(); + + if ((is_null($start_date) || is_null($end_date))) + throw new ValidationException('To publish this event you must define a start/end datetime.'); - if ($end_date < $start_date) - throw new ValidationException('start datetime must be greather or equal than end datetime!'); + $timezone = $summit->getTimeZoneId(); + + if (empty($timezone)) { + throw new ValidationException('Invalid Summit TimeZone.'); + } + + if ($end_date < $start_date) + throw new ValidationException('start datetime must be greater or equal than end datetime.'); + } $this->published = true; $this->published_date = new DateTime(); @@ -727,7 +741,7 @@ public function publish() /** * @return bool */ - public function isPublished() + public function isPublished():bool { return $this->getPublished(); } @@ -743,17 +757,22 @@ public function getPublished() /** * @return \DateTime|null */ - public function getStartDate() + public function getStartDate():?DateTime { - return $this->start_date; + $type = $this->type; + return !is_null($type) && $type->isAllowsPublishingDates() ? $this->start_date: null; } /** * @param DateTime $value * @return $this + * @throws ValidationException */ public function setStartDate(DateTime $value) { + if(!$this->type->isAllowsPublishingDates()){ + throw new ValidationException("Type does not allows Publishing Period."); + } $summit = $this->getSummit(); if (!is_null($summit)) { $value = $summit->convertDateFromTimeZone2UTC($value); @@ -762,24 +781,36 @@ public function setStartDate(DateTime $value) return $this; } + /** + * @param DateTime $value + * @throws ValidationException + */ public function setRawStartDate(DateTime $value){ + if(!$this->type->isAllowsPublishingDates()){ + throw new ValidationException("Type does not allows Publishing Period."); + } $this->start_date = $value; } /** * @return \DateTime|null */ - public function getEndDate() + public function getEndDate():?DateTime { - return $this->end_date; + $type = $this->type; + return !is_null($type) && $type->isAllowsPublishingDates() ? $this->end_date: null; } /** * @param DateTime $value * @return $this + * @throws ValidationException */ public function setEndDate(DateTime $value) { + if(!$this->type->isAllowsPublishingDates()){ + throw new ValidationException("Type does not allows Publishing Period."); + } $summit = $this->getSummit(); if (!is_null($summit)) { $value = $summit->convertDateFromTimeZone2UTC($value); @@ -789,6 +820,9 @@ public function setEndDate(DateTime $value) } public function setRawEndDate(DateTime $value){ + if(!$this->type->isAllowsPublishingDates()){ + throw new ValidationException("Type does not allows Publishing Period."); + } $this->end_date = $value; } /** diff --git a/app/Models/Foundation/Summit/Events/SummitEventType.php b/app/Models/Foundation/Summit/Events/SummitEventType.php index e337d7b3c..cbe921fda 100644 --- a/app/Models/Foundation/Summit/Events/SummitEventType.php +++ b/app/Models/Foundation/Summit/Events/SummitEventType.php @@ -80,6 +80,18 @@ class SummitEventType extends SilverstripeBaseModel */ protected $is_default; + /** + * @ORM\Column(name="AllowsPublishingDates", type="boolean") + * @var bool + */ + protected $allows_publishing_dates; + + /** + * @ORM\Column(name="AllowsLocation", type="boolean") + * @var bool + */ + protected $allows_location; + /** * @ORM\Column(name="IsPrivate", type="boolean") * @var bool @@ -234,14 +246,16 @@ public function setAllowsAttachment($allows_attachment) public function __construct() { parent::__construct(); - $this->is_default = false; - $this->use_sponsors = false; - $this->blackout_times = false; - $this->are_sponsors_mandatory = false; - $this->allows_attachment = false; - $this->is_private = false; - $this->allows_level = false; - $this->summit_documents = new ArrayCollection(); + $this->is_default = false; + $this->use_sponsors = false; + $this->blackout_times = false; + $this->are_sponsors_mandatory = false; + $this->allows_attachment = false; + $this->is_private = false; + $this->allows_level = false; + $this->allows_location = true; + $this->allows_publishing_dates = true; + $this->summit_documents = new ArrayCollection(); } /** @@ -340,4 +354,36 @@ public function setAllowsLevel(bool $allows_level): void $this->allows_level = $allows_level; } + /** + * @return bool + */ + public function isAllowsPublishingDates(): bool + { + return $this->allows_publishing_dates; + } + + /** + * @param bool $allows_publishing_dates + */ + public function setAllowsPublishingDates(bool $allows_publishing_dates): void + { + $this->allows_publishing_dates = $allows_publishing_dates; + } + + /** + * @return bool + */ + public function isAllowsLocation(): bool + { + return $this->allows_location; + } + + /** + * @param bool $allows_location + */ + public function setAllowsLocation(bool $allows_location): void + { + $this->allows_location = $allows_location; + } + } diff --git a/app/Models/Foundation/Summit/Factories/PresentationCategoryGroupFactory.php b/app/Models/Foundation/Summit/Factories/PresentationCategoryGroupFactory.php index 03aff7010..67cbf0a77 100644 --- a/app/Models/Foundation/Summit/Factories/PresentationCategoryGroupFactory.php +++ b/app/Models/Foundation/Summit/Factories/PresentationCategoryGroupFactory.php @@ -34,7 +34,7 @@ public static function build(Summit $summit, array $data){ $track_group = null; switch($data['class_name']){ case PresentationCategoryGroup::ClassName :{ - $track_group = self::populatePresentationCategoryGroup(new PresentationCategoryGroup, $data); + $track_group = self::populatePresentationCategoryGroup($summit, new PresentationCategoryGroup, $data); } break; case PrivatePresentationCategoryGroup::ClassName :{ @@ -46,11 +46,18 @@ public static function build(Summit $summit, array $data){ /** + * @param Summit $summit * @param PresentationCategoryGroup $track_group * @param array $data * @return PresentationCategoryGroup */ - private static function populatePresentationCategoryGroup(PresentationCategoryGroup $track_group, array $data){ + private static function populatePresentationCategoryGroup + ( + Summit $summit, + PresentationCategoryGroup $track_group, + array $data + ) + { if(isset($data['name'])) $track_group->setName(trim($data['name'])); @@ -60,6 +67,23 @@ private static function populatePresentationCategoryGroup(PresentationCategoryGr if(isset($data['color'])) $track_group->setColor(trim($data['color'])); + if(isset($data['begin_attendee_voting_period_date'])) { + $start_datetime = intval($data['begin_attendee_voting_period_date']); + $start_datetime = new \DateTime("@$start_datetime"); + $start_datetime->setTimezone($summit->getTimeZone()); + $track_group->setBeginAttendeeVotingPeriodDate($start_datetime); + } + + if(isset($data['end_attendee_voting_period_date'])) { + $end_datetime = intval($data['end_attendee_voting_period_date']); + $end_datetime = new \DateTime("@$end_datetime"); + $end_datetime->setTimezone($summit->getTimeZone()); + $track_group->setEndAttendeeVotingPeriodDate($end_datetime); + } + + if(isset($data['max_attendee_votes'])) + $track_group->setMaxAttendeeVotes(intval($data['max_attendee_votes'])); + return $track_group; } @@ -96,7 +120,7 @@ private static function populatePrivatePresentationCategoryGroup if(isset($data['max_submission_allowed_per_user'])) $track_group->setMaxSubmissionAllowedPerUser(intval($data['max_submission_allowed_per_user'])); - return self::populatePresentationCategoryGroup($track_group, $data); + return self::populatePresentationCategoryGroup($summit, $track_group, $data); } /** @@ -110,7 +134,7 @@ public static function populate(Summit $summit, PresentationCategoryGroup $track return self::populatePrivatePresentationCategoryGroup($summit, $track_group, $data); } else if($track_group instanceof PresentationCategoryGroup){ - return self::populatePresentationCategoryGroup($track_group, $data); + return self::populatePresentationCategoryGroup($summit, $track_group, $data); } return $track_group; } diff --git a/app/Models/Foundation/Summit/Factories/PresentationFactory.php b/app/Models/Foundation/Summit/Factories/PresentationFactory.php index 1aa229d7c..2922cd8c7 100644 --- a/app/Models/Foundation/Summit/Factories/PresentationFactory.php +++ b/app/Models/Foundation/Summit/Factories/PresentationFactory.php @@ -76,6 +76,10 @@ public static function populate(Presentation $presentation, array $payload, $onl } } + if(isset($payload['custom_order'])){ + $presentation->setCustomOrder(intval($payload['custom_order'])); + } + // links if (isset($payload['links'])) { diff --git a/app/Models/Foundation/Summit/Factories/SummitEventTypeFactory.php b/app/Models/Foundation/Summit/Factories/SummitEventTypeFactory.php index 9cbb3f6c1..ff11a6bcc 100644 --- a/app/Models/Foundation/Summit/Factories/SummitEventTypeFactory.php +++ b/app/Models/Foundation/Summit/Factories/SummitEventTypeFactory.php @@ -94,6 +94,14 @@ public static function populate(SummitEventType $event_type, Summit $summit, arr if(isset($data['should_be_available_on_cfp'])) { $event_type->setShouldBeAvailableOnCfp(boolval($data['should_be_available_on_cfp'])); } + + if(isset($data['allow_custom_ordering'])) { + $event_type->setAllowCustomOrdering(boolval($data['allow_custom_ordering'])); + } + + if(isset($data['allow_attendee_vote'])) { + $event_type->setAllowAttendeeVote(boolval($data['allow_attendee_vote'])); + } } } break; @@ -126,6 +134,12 @@ public static function populate(SummitEventType $event_type, Summit $summit, arr if(isset($data['allows_level'])) $event_type->setAllowsLevel(boolval($data['allows_level'])); + if(isset($data['allows_publishing_dates'])) + $event_type->setAllowsPublishingDates(boolval($data['allows_publishing_dates'])); + + if(isset($data['allows_location'])) + $event_type->setAllowsLocation(boolval($data['allows_location'])); + $summit->addEventType($event_type); return $event_type; } diff --git a/app/Models/Foundation/Summit/Registration/Attendees/SummitAttendee.php b/app/Models/Foundation/Summit/Registration/Attendees/SummitAttendee.php index 3d7e297e0..ebe497b5c 100644 --- a/app/Models/Foundation/Summit/Registration/Attendees/SummitAttendee.php +++ b/app/Models/Foundation/Summit/Registration/Attendees/SummitAttendee.php @@ -11,6 +11,7 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ + use App\Jobs\Emails\InviteAttendeeTicketEditionMail; use App\Jobs\Emails\RevocationTicketEmail; use App\Jobs\Emails\SummitAttendeeTicketEmail; @@ -23,7 +24,8 @@ use models\main\Member; use models\main\SummitMemberSchedule; use models\utils\SilverstripeBaseModel; -use Doctrine\ORM\Mapping AS ORM; +use Doctrine\ORM\Mapping as ORM; + /** * @ORM\Entity(repositoryClass="App\Repositories\Summit\DoctrineSummitAttendeeRepository") * @ORM\AssociationOverrides({ @@ -40,7 +42,7 @@ class SummitAttendee extends SilverstripeBaseModel { const StatusIncomplete = 'Incomplete'; - const StatusComplete = 'Complete'; + const StatusComplete = 'Complete'; /** * @ORM\Column(name="FirstName", type="string") * @var string @@ -126,6 +128,12 @@ class SummitAttendee extends SilverstripeBaseModel */ private $extra_question_answers; + /** + * @ORM\OneToMany(targetEntity="models\summit\PresentationAttendeeVote", mappedBy="voter", cascade={"persist","remove"}, orphanRemoval=true, fetch="EXTRA_LAZY") + * @var PresentationAttendeeVote[] + */ + private $presentation_votes; + /** * @ORM\Column(name="Company", type="string") * @var string @@ -154,28 +162,33 @@ class SummitAttendee extends SilverstripeBaseModel /** * @return \DateTime|null */ - public function getSummitHallCheckedInDate():?\DateTime{ + public function getSummitHallCheckedInDate(): ?\DateTime + { return $this->summit_hall_checked_in_date; } /** * @return bool */ - public function getSummitHallCheckedIn(){ + public function getSummitHallCheckedIn() + { return (bool)$this->summit_hall_checked_in; } /** * @param bool $summit_hall_checked_in */ - public function setSummitHallCheckedIn(bool $summit_hall_checked_in):void{ + public function setSummitHallCheckedIn(bool $summit_hall_checked_in): void + { $this->summit_hall_checked_in = $summit_hall_checked_in; - $this->summit_hall_checked_in_date = $summit_hall_checked_in? new \DateTime('now', new \DateTimeZone('UTC')):null; + $this->summit_hall_checked_in_date = $summit_hall_checked_in ? new \DateTime('now', new \DateTimeZone('UTC')) : null; } - public function hasCheckedIn():bool{ + public function hasCheckedIn(): bool + { return (bool)$this->summit_hall_checked_in; } + /** * @return boolean */ @@ -195,11 +208,11 @@ public function setShareContactInfo($share_contact_info) /** * @return int */ - public function getMemberId(){ + public function getMemberId() + { try { return is_null($this->member) ? 0 : $this->member->getId(); - } - catch(\Exception $ex){ + } catch (\Exception $ex) { return 0; } } @@ -207,7 +220,8 @@ public function getMemberId(){ /** * @return bool */ - public function hasMember(){ + public function hasMember() + { return $this->getMemberId() > 0; } @@ -220,15 +234,17 @@ public function hasMember(){ /** * @return SummitAttendeeTicket[] */ - public function getTickets(){ + public function getTickets() + { return $this->tickets; } /** * @param SummitAttendeeTicket $ticket */ - public function addTicket(SummitAttendeeTicket $ticket){ - if($this->tickets->contains($ticket)) return; + public function addTicket(SummitAttendeeTicket $ticket) + { + if ($this->tickets->contains($ticket)) return; $this->tickets->add($ticket); $ticket->setOwner($this); } @@ -236,18 +252,21 @@ public function addTicket(SummitAttendeeTicket $ticket){ /** * @return Member */ - public function getMember():?Member{ + public function getMember(): ?Member + { return $this->member; } /** * @param Member $member */ - public function setMember(Member $member){ + public function setMember(Member $member) + { $this->member = $member; } - public function clearMember(){ + public function clearMember() + { $this->member = null; } @@ -256,21 +275,23 @@ public function clearMember(){ public function __construct() { parent::__construct(); - $this->share_contact_info = false; - $this->summit_hall_checked_in = false; - $this->tickets = new ArrayCollection(); - $this->extra_question_answers = new ArrayCollection(); + $this->share_contact_info = false; + $this->summit_hall_checked_in = false; + $this->tickets = new ArrayCollection(); + $this->extra_question_answers = new ArrayCollection(); $this->disclaimer_accepted_date = null; $this->summit_virtual_checked_in_date = null; $this->invitation_email_sent_date = null; $this->public_edition_email_sent_date = null; - $this->status = self::StatusIncomplete; + $this->status = self::StatusIncomplete; + $this->presentation_votes = new ArrayCollection(); } /** * @return SummitEventFeedback[] */ - public function getEmittedFeedback(){ + public function getEmittedFeedback() + { return $this->member->getFeedback()->matching ( @@ -295,7 +316,7 @@ public function add2Schedule(SummitEvent $event) */ public function removeFromSchedule(SummitEvent $event) { - $this->member->removeFromSchedule($event); + $this->member->removeFromSchedule($event); } /** @@ -313,7 +334,8 @@ public function isOnSchedule(SummitEvent $event) * @return null| SummitMemberSchedule * @deprecated use Member::getScheduleByEvent instead */ - public function getScheduleByEvent(SummitEvent $event){ + public function getScheduleByEvent(SummitEvent $event) + { return $this->member->getScheduleByEvent($event); } @@ -321,7 +343,8 @@ public function getScheduleByEvent(SummitEvent $event){ * @return SummitMemberSchedule[] * @deprecated use Member::getScheduleBySummit instead */ - public function getSchedule(){ + public function getSchedule() + { return $this->member->getScheduleBySummit($this->summit); } @@ -329,7 +352,8 @@ public function getSchedule(){ * @return int[] * @deprecated use Member::getScheduledEventsIds instead */ - public function getScheduledEventsIds(){ + public function getScheduledEventsIds() + { return $this->member->getScheduledEventsIds($this->summit); } @@ -338,15 +362,17 @@ public function getScheduledEventsIds(){ * @return null|RSVP * @deprecated use Member::getRsvpByEvent instead */ - public function getRsvpByEvent($event_id){ - return $this->member->getRsvpByEvent($event_id); + public function getRsvpByEvent($event_id) + { + return $this->member->getRsvpByEvent($event_id); } /** * @param int $ticket_id * @return SummitAttendeeTicket */ - public function getTicketById($ticket_id){ + public function getTicketById($ticket_id) + { $ticket = $this->tickets->matching( $criteria = Criteria::create() ->where(Criteria::expr()->eq("id", $ticket_id)) @@ -358,7 +384,8 @@ public function getTicketById($ticket_id){ * @param SummitAttendeeTicket $ticket * @return $this */ - public function removeTicket(SummitAttendeeTicket $ticket){ + public function removeTicket(SummitAttendeeTicket $ticket) + { $this->tickets->removeElement($ticket); $ticket->clearOwner(); return $this; @@ -367,13 +394,14 @@ public function removeTicket(SummitAttendeeTicket $ticket){ /** * @param SummitAttendeeTicket $ticket */ - public function sendRevocationTicketEmail(SummitAttendeeTicket $ticket){ - if(!$ticket->hasOwner()) return; + public function sendRevocationTicketEmail(SummitAttendeeTicket $ticket) + { + if (!$ticket->hasOwner()) return; - if($ticket->getOwner()->getId() != $this->getId()) return; + if ($ticket->getOwner()->getId() != $this->getId()) return; $email = $this->getEmail(); $key = md5($email); - if(Cache::add(sprintf("%s_revoke_ticket", $key),true, 600 )) { + if (Cache::add(sprintf("%s_revoke_ticket", $key), true, 600)) { RevocationTicketEmail::dispatch($this, $ticket); } } @@ -382,30 +410,30 @@ public function sendRevocationTicketEmail(SummitAttendeeTicket $ticket){ * @param SummitAttendeeTicket $ticket * @param bool $overrideTicketOwnerIsSameAsOrderOwnerRule */ - public function sendInvitationEmail(SummitAttendeeTicket $ticket, bool $overrideTicketOwnerIsSameAsOrderOwnerRule = false){ + public function sendInvitationEmail(SummitAttendeeTicket $ticket, bool $overrideTicketOwnerIsSameAsOrderOwnerRule = false) + { $email = $this->getEmail(); $key = md5($email); Log::debug(sprintf("SummitAttendee::sendInvitationEmail attendee %s", $email)); - if($ticket->getOwnerEmail() != $this->getEmail()) return; - if(!$ticket->isPaid()){ + if ($ticket->getOwnerEmail() != $this->getEmail()) return; + if (!$ticket->isPaid()) { Log::warning(sprintf("SummitAttendee::sendInvitationEmail attendee %s ticket is not paid", $email)); return; } - if(!$ticket->isActive()){ + if (!$ticket->isActive()) { Log::warning(sprintf("SummitAttendee::sendInvitationEmail attendee %s ticket is not active", $email)); return; } $this->updateStatus(); $ticket->generateHash(); - if($this->isComplete()) { + if ($this->isComplete()) { Log::debug(sprintf("SummitAttendee::sendInvitationEmail attendee %s is complete", $email)); // adds a threshold of 10 minutes to avoid duplicates emails - if(Cache::add(sprintf("%s_emit_ticket", $key),true, 10 )) - { + if (Cache::add(sprintf("%s_emit_ticket", $key), true, 10)) { Log::debug(sprintf("SummitAttendee::sendInvitationEmail attendee %s sending SummitAttendeeTicketEmail", $email)); SummitAttendeeTicketEmail::dispatch($ticket); $ticket->getOwner()->markInvitationEmailSentDate(); @@ -418,10 +446,10 @@ public function sendInvitationEmail(SummitAttendeeTicket $ticket, bool $override // buyer is presented the option to fill in the details during the checkout process. Second, buyer will // receive daily reminder emails. So, I think that makes this email not really needed as the buyer already knows // they bought a ticket for themselves. - if($order->getOwnerEmail() !== $ticket->getOwnerEmail() || $overrideTicketOwnerIsSameAsOrderOwnerRule) { + if ($order->getOwnerEmail() !== $ticket->getOwnerEmail() || $overrideTicketOwnerIsSameAsOrderOwnerRule) { // no delay // adds a threshold of 10 minutes to avoid duplicates emails - if(Cache::add(sprintf("%s_edit_ticket", $key),true, 10 )) { + if (Cache::add(sprintf("%s_edit_ticket", $key), true, 10)) { Log::debug(sprintf("SummitAttendee::sendInvitationEmail attendee %s sending InviteAttendeeTicketEditionMail", $email)); InviteAttendeeTicketEditionMail::dispatch($ticket); $ticket->getOwner()->markInvitationEmailSentDate(); @@ -432,7 +460,8 @@ public function sendInvitationEmail(SummitAttendeeTicket $ticket, bool $override /** * @return bool */ - public function hasTickets(){ + public function hasTickets() + { return $this->tickets->count() > 0; } @@ -442,10 +471,10 @@ public function hasTickets(){ public function getFirstName(): ?string { $res = null; - if($this->hasMember()){ + if ($this->hasMember()) { $res = $this->member->getFirstName(); } - if(empty($res)) + if (empty($res)) $res = $this->first_name; return $res; } @@ -464,10 +493,10 @@ public function setFirstName(string $first_name): void public function getSurname(): ?string { $res = null; - if($this->hasMember()){ + if ($this->hasMember()) { $res = $this->member->getLastName(); } - if(empty($res)) + if (empty($res)) $res = $this->surname; return $res; } @@ -485,25 +514,26 @@ public function setSurname(string $surname): void */ public function getEmail(): string { - if($this->hasMember()){ + if ($this->hasMember()) { return $this->member->getEmail(); } return $this->email; } - public function getFullName():?string{ + public function getFullName(): ?string + { Log::debug(sprintf("SummitAttendee::getFullName id %s", $this->id)); - if($this->hasMember()){ + if ($this->hasMember()) { Log::debug(sprintf("SummitAttendee::getFullName id %s hasMember", $this->id)); - $fullname = $this->member->getFullName(); + $fullname = $this->member->getFullName(); Log::debug(sprintf("SummitAttendee::getFullName id %s Member Full Name %s", $this->id, $fullname)); - if(!empty($fullname)) + if (!empty($fullname)) return $fullname; } $fullname = $this->first_name; - if(!empty($this->surname)){ - if(!empty($fullname)) $fullname .= ' '; + if (!empty($this->surname)) { + if (!empty($fullname)) $fullname .= ' '; $fullname .= $this->surname; } @@ -530,7 +560,8 @@ public function getDisclaimerAcceptedDate(): ?\DateTime /** * @return bool */ - public function isDisclaimerAccepted():bool{ + public function isDisclaimerAccepted(): bool + { return !is_null($this->disclaimer_accepted_date); } @@ -542,7 +573,8 @@ public function setDisclaimerAcceptedDate(\DateTime $disclaimer_accepted_date): $this->disclaimer_accepted_date = $disclaimer_accepted_date; } - public function clearDisclaimerAcceptedDate():void{ + public function clearDisclaimerAcceptedDate(): void + { $this->disclaimer_accepted_date = null; } @@ -558,7 +590,8 @@ public function getExtraQuestionAnswers() * @param SummitOrderExtraQuestionType $question * @return SummitOrderExtraQuestionAnswer|null */ - public function getExtraQuestionAnswerByQuestion(SummitOrderExtraQuestionType $question):?SummitOrderExtraQuestionAnswer{ + public function getExtraQuestionAnswerByQuestion(SummitOrderExtraQuestionType $question): ?SummitOrderExtraQuestionAnswer + { $answer = $this->extra_question_answers->matching( $criteria = Criteria::create() ->where(Criteria::expr()->eq("question", $question)) @@ -570,7 +603,8 @@ public function getExtraQuestionAnswerByQuestion(SummitOrderExtraQuestionType $q * @param SummitOrderExtraQuestionType $question * @return string|null */ - public function getExtraQuestionAnswerValueByQuestion(SummitOrderExtraQuestionType $question):?string{ + public function getExtraQuestionAnswerValueByQuestion(SummitOrderExtraQuestionType $question): ?string + { try { $sql = <<extra_question_answers->contains($answer)) return; + public function addExtraQuestionAnswer(SummitOrderExtraQuestionAnswer $answer) + { + if ($this->extra_question_answers->contains($answer)) return; $this->extra_question_answers->add($answer); $answer->setAttendee($this); } @@ -610,8 +645,9 @@ public function addExtraQuestionAnswer(SummitOrderExtraQuestionAnswer $answer){ /** * @param SummitOrderExtraQuestionAnswer $answer */ - public function removeExtraQuestionAnswer(SummitOrderExtraQuestionAnswer $answer){ - if(!$this->extra_question_answers->contains($answer)) return; + public function removeExtraQuestionAnswer(SummitOrderExtraQuestionAnswer $answer) + { + if (!$this->extra_question_answers->contains($answer)) return; $this->extra_question_answers->removeElement($answer); $answer->clearAttendee(); } @@ -651,49 +687,53 @@ public function setCompany(Company $company): void /** * @return bool */ - public function needToFillDetails():bool { + public function needToFillDetails(): bool + { return $this->getStatus() == self::StatusIncomplete; } /** * @return bool */ - public function isComplete():bool{ + public function isComplete(): bool + { return $this->getStatus() == self::StatusComplete; } /** * @return string */ - public function getStatus():?string{ + public function getStatus(): ?string + { return $this->status; } - public function updateStatus():string { + public function updateStatus(): string + { Log::debug(sprintf("SummitAttendee::updateStatus original status %s", $this->status)); $is_disclaimer_mandatory = $this->summit->isRegistrationDisclaimerMandatory(); // mandatory fields - if($is_disclaimer_mandatory && !$this->isDisclaimerAccepted()){ + if ($is_disclaimer_mandatory && !$this->isDisclaimerAccepted()) { $this->status = self::StatusIncomplete; Log::debug(sprintf("SummitAttendee::updateStatus StatusIncomplete for attendee %s (disclaimer mandatory)", $this->id)); return $this->status; } - if(empty($this->getFirstName())){ + if (empty($this->getFirstName())) { $this->status = self::StatusIncomplete; Log::debug(sprintf("SummitAttendee::updateStatus StatusIncomplete for attendee %s (first name empty)", $this->id)); return $this->status; } - if(empty($this->getSurname())){ + if (empty($this->getSurname())) { $this->status = self::StatusIncomplete; Log::debug(sprintf("SummitAttendee::updateStatus StatusIncomplete for attendee %s (last name empty)", $this->id)); return $this->status; } - if(empty($this->getEmail())){ + if (empty($this->getEmail())) { $this->status = self::StatusIncomplete; Log::debug(sprintf("SummitAttendee::updateStatus StatusIncomplete for attendee %s (email empty)", $this->id)); return $this->status; @@ -702,20 +742,20 @@ public function updateStatus():string { // check mandatory questions // get mandatory question ids - $extra_questions_mandatory_questions = $this->summit->getMandatoryOrderExtraQuestionsByUsage(SummitOrderExtraQuestionTypeConstants::TicketQuestionUsage); + $extra_questions_mandatory_questions = $this->summit->getMandatoryOrderExtraQuestionsByUsage(SummitOrderExtraQuestionTypeConstants::TicketQuestionUsage); $extra_questions_mandatory_questions_ids = []; - foreach($extra_questions_mandatory_questions as $extra_mandatory_question){ + foreach ($extra_questions_mandatory_questions as $extra_mandatory_question) { $extra_questions_mandatory_questions_ids[] = $extra_mandatory_question->getId(); } // now check the answers - foreach($this->extra_question_answers as $extra_question_answer){ - if(!$extra_question_answer->hasQuestion()) continue; + foreach ($this->extra_question_answers as $extra_question_answer) { + if (!$extra_question_answer->hasQuestion()) continue; $question_type = $extra_question_answer->getQuestion(); - if(in_array($question_type->getId(), $extra_questions_mandatory_questions_ids)) { + if (in_array($question_type->getId(), $extra_questions_mandatory_questions_ids)) { // is mandatory now check if we have value set - if(!$extra_question_answer->hasValue()) { + if (!$extra_question_answer->hasValue()) { $this->status = self::StatusIncomplete; Log::debug(sprintf("SummitAttendee::updateStatus StatusIncomplete for attendee %s ( mandatory extra question missing value )", $this->id)); return $this->status; @@ -728,7 +768,7 @@ public function updateStatus():string { } // if we have mandatory questions without answer ... - if(count($extra_questions_mandatory_questions_ids) > 0 ){ + if (count($extra_questions_mandatory_questions_ids) > 0) { $this->status = self::StatusIncomplete; Log::debug(sprintf("SummitAttendee::updateStatus StatusIncomplete for attendee %s ( mandatory extra questions )", $this->id)); return $this->status; @@ -802,12 +842,13 @@ public function getSummitVirtualCheckedInDate(): ?\DateTime * @param string $access_level * @return bool */ - public function hasAccessLevel(string $access_level):bool{ - foreach($this->tickets as $ticket){ - if(!$ticket->isActive()) continue; - if(!$ticket->hasBadge()) continue; + public function hasAccessLevel(string $access_level): bool + { + foreach ($this->tickets as $ticket) { + if (!$ticket->isActive()) continue; + if (!$ticket->hasBadge()) continue; $al = $ticket->getBadge()->getType()->getAccessLevelByName($access_level); - if(!is_null($al)) return true; + if (!is_null($al)) return true; } return false; } @@ -815,13 +856,14 @@ public function hasAccessLevel(string $access_level):bool{ /** * @throws \Exception */ - public function doVirtualChecking():void{ + public function doVirtualChecking(): void + { $now = new \DateTime('now', new \DateTimeZone('UTC')); - if(is_null($this->summit_virtual_checked_in_date)){ - if(!$this->summit->isOpen()){ + if (is_null($this->summit_virtual_checked_in_date)) { + if (!$this->summit->isOpen()) { throw new ValidationException("Is not show time yet."); } - if(!$this->hasAccessLevel(SummitAccessLevelType::VIRTUAL)){ + if (!$this->hasAccessLevel(SummitAccessLevelType::VIRTUAL)) { throw new ValidationException("Attendee does not posses VIRTUAL access level."); } $this->summit_virtual_checked_in_date = $now; @@ -832,7 +874,8 @@ public function doVirtualChecking():void{ * @return \DateTime * @throws \Exception */ - public function markInvitationEmailSentDate():\DateTime{ + public function markInvitationEmailSentDate(): \DateTime + { $now = new \DateTime('now', new \DateTimeZone('UTC')); $this->invitation_email_sent_date = $now; return $now; @@ -842,10 +885,25 @@ public function markInvitationEmailSentDate():\DateTime{ * @return \DateTime * @throws \Exception */ - public function markPublicEditionEmailSentDate():\DateTime{ + public function markPublicEditionEmailSentDate(): \DateTime + { $now = new \DateTime('now', new \DateTimeZone('UTC')); $this->public_edition_email_sent_date = $now; return $now; } + public function getPresentationVotes(){ + return $this->presentation_votes; + } + + public function addPresentationVote(PresentationAttendeeVote $vote){ + if($this->presentation_votes->contains($vote)) return; + $this->addPresentationVote($vote); + } + + public function removePresentationVote(PresentationAttendeeVote $vote){ + if(!$this->presentation_votes->contains($vote)) return; + $this->presentation_votes->removeElement($vote); + } + } \ No newline at end of file diff --git a/app/Repositories/Summit/DoctrineSummitEventRepository.php b/app/Repositories/Summit/DoctrineSummitEventRepository.php index 7834de6ab..1ea07caa7 100644 --- a/app/Repositories/Summit/DoctrineSummitEventRepository.php +++ b/app/Repositories/Summit/DoctrineSummitEventRepository.php @@ -15,18 +15,21 @@ use App\Models\Foundation\Main\IGroup; use App\Repositories\SilverStripeDoctrineRepository; use Doctrine\ORM\Query\Expr\Join; +use Doctrine\ORM\QueryBuilder; use Doctrine\ORM\Tools\Pagination\Paginator; use Illuminate\Support\Facades\Log; use models\main\Tag; use models\summit\ISummitCategoryChangeStatus; use models\summit\ISummitEventRepository; use models\summit\Presentation; +use models\summit\PresentationType; use models\summit\Summit; use models\summit\SummitEvent; use models\summit\SummitGroupEvent; use utils\DoctrineCaseFilterMapping; use utils\DoctrineCollectionFieldsFilterMapping; use utils\DoctrineFilterMapping; +use utils\DoctrineInstanceOfFilterMapping; use utils\DoctrineJoinFilterMapping; use utils\DoctrineLeftJoinFilterMapping; use utils\DoctrineSwitchFilterMapping; @@ -34,7 +37,6 @@ use utils\Order; use utils\PagingInfo; use utils\PagingResponse; - /** * Class DoctrineSummitEventRepository * @package App\Repositories\Summit @@ -55,11 +57,11 @@ final class DoctrineSummitEventRepository */ public function getPublishedOnSameTimeFrame(SummitEvent $event) { - $summit = $event->getSummit(); - $end_date = $event->getEndDate(); + $summit = $event->getSummit(); + $end_date = $event->getEndDate(); $start_date = $event->getStartDate(); - $query = $this->getEntityManager()->createQueryBuilder() + $query = $this->getEntityManager()->createQueryBuilder() ->select("e") ->from(\models\summit\SummitEvent::class, "e") ->join('e.summit', 's', Join::WITH, " s.id = :summit_id") @@ -70,50 +72,65 @@ public function getPublishedOnSameTimeFrame(SummitEvent $event) ->setParameter('start_date', $start_date) ->setParameter('end_date', $end_date); - $idx = 1; - foreach(self::$forbidden_classes as $forbidden_class){ + $idx = 1; + foreach (self::$forbidden_classes as $forbidden_class) { $query = $query - ->andWhere("not e INSTANCE OF :forbidden_class".$idx); - $query->setParameter("forbidden_class".$idx, $forbidden_class); + ->andWhere("not e INSTANCE OF :forbidden_class" . $idx); + $query->setParameter("forbidden_class" . $idx, $forbidden_class); $idx++; } return $query->getQuery()->getResult(); } + /** + * @param QueryBuilder $query + * @return QueryBuilder + */ + protected function applyExtraJoins(QueryBuilder $query) + { + $query = $query->innerJoin("e.type", "et", Join::ON); + $query = $query->leftJoin(PresentationType::class, 'et2', 'WITH', 'et.id = et2.id'); + return $query; + } + /** * @param int $current_member_id * @param int $current_track_id * @return array */ - protected function getCustomFilterMappings(int $current_member_id , int $current_track_id) + protected function getCustomFilterMappings(int $current_member_id, int $current_track_id) { return [ - 'id' => 'e.id:json_int', - 'title' => 'e.title:json_string', - 'abstract' => 'e.abstract:json_string', - 'level' => 'e.level:json_string', - 'status' => 'p.status:json_string', - 'progress' => 'p.progress:json_int', - 'is_chair_visible' => new DoctrineJoinFilterMapping + 'id' => 'e.id:json_int', + 'title' => 'e.title:json_string', + 'abstract' => 'e.abstract:json_string', + 'level' => 'e.level:json_string', + 'status' => 'p.status:json_string', + 'progress' => 'p.progress:json_int', + 'is_chair_visible' => new DoctrineJoinFilterMapping ( 'e.category', 'c', "c.chair_visible :operator :value" ), - 'is_voting_visible' => new DoctrineJoinFilterMapping + 'is_voting_visible' => new DoctrineJoinFilterMapping ( 'e.category', 'c', "c.voting_visible :operator :value" ), 'social_summary' => 'e.social_summary:json_string', - 'published' => 'e.published', - 'start_date' => 'e.start_date:datetime_epoch', - 'end_date' => 'e.end_date:datetime_epoch', - 'created' => 'e.created:datetime_epoch', - 'last_edited' => 'e.last_edited:datetime_epoch', - 'tags' => new DoctrineLeftJoinFilterMapping + 'published' => 'e.published', + 'type_allows_publishing_dates' => 'et.allows_publishing_dates', + 'type_allows_location' => 'et.allows_location', + 'type_allows_attendee_vote' => 'et2.allow_attendee_vote', + 'type_allows_custom_ordering' => 'et2.allow_custom_ordering', + 'start_date' => 'e.start_date:datetime_epoch', + 'end_date' => 'e.end_date:datetime_epoch', + 'created' => 'e.created:datetime_epoch', + 'last_edited' => 'e.last_edited:datetime_epoch', + 'tags' => new DoctrineLeftJoinFilterMapping ( 'e.tags', 't', @@ -125,12 +142,7 @@ protected function getCustomFilterMappings(int $current_member_id , int $current 's', "s.id :operator :value" ), - 'event_type_id' => new DoctrineJoinFilterMapping - ( - 'e.type', - 'et', - "et.id :operator :value" - ), + 'event_type_id' => "et.id :operator :value", 'track_id' => new DoctrineJoinFilterMapping ( 'e.category', @@ -149,14 +161,14 @@ protected function getCustomFilterMappings(int $current_member_id , int $current ), 'speaker' => new DoctrineFilterMapping ( - "( concat(sp.first_name, ' ', sp.last_name) :operator :value ". - "OR concat(spm.first_name, ' ', spm.last_name) :operator :value ". - "OR concat(spmm.first_name, ' ', spmm.last_name) :operator :value ". - "OR sp.first_name :operator :value ". - "OR sp.last_name :operator :value ". - "OR spm.first_name :operator :value ". - "OR spm.last_name :operator :value ". - "OR spmm.first_name :operator :value ". + "( concat(sp.first_name, ' ', sp.last_name) :operator :value " . + "OR concat(spm.first_name, ' ', spm.last_name) :operator :value " . + "OR concat(spmm.first_name, ' ', spmm.last_name) :operator :value " . + "OR sp.first_name :operator :value " . + "OR sp.last_name :operator :value " . + "OR spm.first_name :operator :value " . + "OR spm.last_name :operator :value " . + "OR spmm.first_name :operator :value " . "OR spmm.last_name :operator :value) " ), 'speaker_email' => new DoctrineFilterMapping @@ -184,29 +196,29 @@ protected function getCustomFilterMappings(int $current_member_id , int $current "(sprs.name :operator :value)" ), 'selection_status' => new DoctrineSwitchFilterMapping([ - 'selected' => new DoctrineCaseFilterMapping( - 'selected', - "ssp.order is not null and sspl.list_type = 'Group' and sspl.category = e.category" - ), - 'accepted' => new DoctrineCaseFilterMapping( - 'accepted', - "ssp.order is not null and ssp.order <= cc.session_count and sspl.list_type = 'Group' and sspl.list_class = 'Session' and sspl.category = e.category" - ), - 'alternate' => new DoctrineCaseFilterMapping( + 'selected' => new DoctrineCaseFilterMapping( + 'selected', + "ssp.order is not null and sspl.list_type = 'Group' and sspl.category = e.category" + ), + 'accepted' => new DoctrineCaseFilterMapping( + 'accepted', + "ssp.order is not null and ssp.order <= cc.session_count and sspl.list_type = 'Group' and sspl.list_class = 'Session' and sspl.category = e.category" + ), + 'alternate' => new DoctrineCaseFilterMapping( 'alternate', "ssp.order is not null and ssp.order > cc.session_count and sspl.list_type = 'Group' and sspl.list_class = 'Session' and sspl.category = e.category" - ), - 'lightning-accepted' => new DoctrineCaseFilterMapping( + ), + 'lightning-accepted' => new DoctrineCaseFilterMapping( 'lightning-accepted', "ssp.order is not null and ssp.order <= cc.lightning_count and sspl.list_type = 'Group' and sspl.list_class = 'Lightning' and sspl.category = e.category" - ), - 'lightning-alternate' => new DoctrineCaseFilterMapping( + ), + 'lightning-alternate' => new DoctrineCaseFilterMapping( 'lightning-alternate', "ssp.order is not null and ssp.order > cc.lightning_count and sspl.list_type = 'Group' and sspl.list_class = 'Lightning' and sspl.category = e.category" - ), - ] + ), + ] ), - 'track_chairs_status' => new DoctrineSwitchFilterMapping + 'track_chairs_status' => new DoctrineSwitchFilterMapping ( [ 'voted' => new DoctrineCaseFilterMapping( @@ -223,19 +235,19 @@ protected function getCustomFilterMappings(int $current_member_id , int $current ), 'selected' => new DoctrineCaseFilterMapping( 'selected', - "sspl.list_type = 'Individual' and sspl.list_class = 'Session' and ssp.collection = 'selected' and ssp_member.id = ".$current_member_id + "sspl.list_type = 'Individual' and sspl.list_class = 'Session' and ssp.collection = 'selected' and ssp_member.id = " . $current_member_id ), 'maybe' => new DoctrineCaseFilterMapping( 'maybe', - "sspl.list_type = 'Individual' and sspl.list_class = 'Session' and ssp.collection = 'maybe' and ssp_member.id = ".$current_member_id + "sspl.list_type = 'Individual' and sspl.list_class = 'Session' and ssp.collection = 'maybe' and ssp_member.id = " . $current_member_id ), 'pass' => new DoctrineCaseFilterMapping( 'selected', - "sspl.list_type = 'Individual' and sspl.list_class = 'Session' and ssp.collection = 'pass' and ssp_member.id = ".$current_member_id + "sspl.list_type = 'Individual' and sspl.list_class = 'Session' and ssp.collection = 'pass' and ssp_member.id = " . $current_member_id ), ] ), - 'viewed_status' => new DoctrineSwitchFilterMapping + 'viewed_status' => new DoctrineSwitchFilterMapping ( [ 'seen' => new DoctrineCaseFilterMapping( @@ -284,10 +296,17 @@ protected function getCustomFilterMappings(int $current_member_id , int $current ] ), 'created_by_fullname' => new DoctrineFilterMapping - ( + ( "concat(cb.first_name, ' ', cb.last_name) :operator :value " - ), + ), 'created_by_email' => 'cb.email', + 'class_name' => new DoctrineInstanceOfFilterMapping( + "e", + [ + SummitEvent::ClassName => SummitEvent::class, + Presentation::ClassName => Presentation::class, + ] + ) ]; } @@ -297,15 +316,17 @@ protected function getCustomFilterMappings(int $current_member_id , int $current protected function getOrderMappings() { return [ - 'id' => 'e.id', - 'title' => 'e.title', - 'start_date' => 'e.start_date', - 'end_date' => 'e.end_date', - 'created' => 'e.created', - 'track' => 'cc.title', - 'location' => 'l.name', + 'id' => 'e.id', + 'title' => 'e.title', + 'start_date' => 'e.start_date', + 'end_date' => 'e.end_date', + 'created' => 'e.created', + 'track' => 'cc.title', + 'location' => 'l.name', 'trackchairsel' => 'ssp.order', - 'last_edited' => 'e.last_edited', + 'last_edited' => 'e.last_edited', + 'random' => 'RAND()', + 'custom_order' => 'e.custom_order', ]; } @@ -318,23 +339,23 @@ protected function getOrderMappings() public function getAllByPage(PagingInfo $paging_info, Filter $filter = null, Order $order = null) { - $current_track_id = 0; + $current_track_id = 0; $current_member_id = 0; - if(!is_null($filter)){ + if (!is_null($filter)) { Log::debug(sprintf("DoctrineSummitEventRepository::getAllByPage filter %s", $filter)); // check for dependant filtering - $track_id_filter = $filter->getUniqueFilter('track_id'); + $track_id_filter = $filter->getUniqueFilter('track_id'); if (!is_null($track_id_filter)) { $current_track_id = intval($track_id_filter->getValue()); } - $current_member_id_filter = $filter->getUniqueFilter('current_member_id'); + $current_member_id_filter = $filter->getUniqueFilter('current_member_id'); if (!is_null($current_member_id_filter)) { $current_member_id = intval($current_member_id_filter->getValue()); } } - $query = $this->getEntityManager()->createQueryBuilder() + $query = $this->getEntityManager()->createQueryBuilder() ->select("e") ->from($this->getBaseEntity(), "e") ->leftJoin(Presentation::class, 'p', 'WITH', 'e.id = p.id') @@ -353,30 +374,36 @@ public function getAllByPage(PagingInfo $paging_info, Filter $filter = null, Ord ->leftJoin('sp.registration_request', "sprr", Join::LEFT_JOIN) ->leftJoin('spm.registration_request', "sprr2", Join::LEFT_JOIN); - if(!is_null($filter)){ + $query = $this->applyExtraJoins($query); + + if (!is_null($filter)) { $filter->apply2Query($query, $this->getCustomFilterMappings($current_member_id, $current_track_id)); } - + $shouldPerformRandomOrderingByPage = false; if (!is_null($order)) { + if($order->hasOrder("random")){ + $shouldPerformRandomOrderingByPage = true; + $order->removeOrder("random"); + } $order->apply2Query($query, $this->getOrderMappings()); - if(!$order->hasOrder('id')) { + if (!$order->hasOrder('id')) { $query = $query->addOrderBy("e.id", 'ASC'); } } else { //default order - $query = $query->addOrderBy("e.start_date",'ASC'); + $query = $query->addOrderBy("e.start_date", 'ASC'); $query = $query->addOrderBy("e.end_date", 'ASC'); $query = $query->addOrderBy("e.id", 'ASC'); } $can_view_private_events = self::isCurrentMemberOnGroup(IGroup::SummitAdministrators); - if(!$can_view_private_events){ - $idx = 1; - foreach(self::$forbidden_classes as $forbidden_class){ + if (!$can_view_private_events) { + $idx = 1; + foreach (self::$forbidden_classes as $forbidden_class) { $query = $query - ->andWhere("not e INSTANCE OF :forbidden_class".$idx); - $query->setParameter("forbidden_class".$idx, $forbidden_class); + ->andWhere("not e INSTANCE OF :forbidden_class" . $idx); + $query->setParameter("forbidden_class" . $idx, $forbidden_class); $idx++; } } @@ -386,11 +413,14 @@ public function getAllByPage(PagingInfo $paging_info, Filter $filter = null, Ord ->setMaxResults($paging_info->getPerPage()); $paginator = new Paginator($query, $fetchJoinCollection = true); - $total = $paginator->count(); - $data = []; + $total = $paginator->count(); + $data = []; - foreach($paginator as $entity) - $data[]= $entity; + foreach ($paginator as $entity) + $data[] = $entity; + + if($shouldPerformRandomOrderingByPage) + shuffle($data); return new PagingResponse ( @@ -405,7 +435,8 @@ public function getAllByPage(PagingInfo $paging_info, Filter $filter = null, Ord /** * @param int $event_id */ - public function cleanupScheduleAndFavoritesForEvent(int $event_id):void{ + public function cleanupScheduleAndFavoritesForEvent(int $event_id): void + { $query = "DELETE Member_Schedule FROM Member_Schedule WHERE SummitEventID = {$event_id};"; $this->getEntityManager()->getConnection()->executeStatement($query); @@ -431,22 +462,22 @@ protected function getBaseEntity() public function getAllByPageLocationTBD(PagingInfo $paging_info, Filter $filter = null, Order $order = null) { - $current_track_id = 0; + $current_track_id = 0; $current_member_id = 0; - if(!is_null($filter)){ + if (!is_null($filter)) { // check for dependant filtering - $track_id_filter = $filter->getUniqueFilter('track_id'); + $track_id_filter = $filter->getUniqueFilter('track_id'); if (!is_null($track_id_filter)) { $current_track_id = intval($track_id_filter->getValue()); } - $current_member_id_filter = $filter->getUniqueFilter('current_member_id'); + $current_member_id_filter = $filter->getUniqueFilter('current_member_id'); if (!is_null($current_member_id_filter)) { $current_member_id = intval($current_member_id_filter->getValue()); } } - $query = $this->getEntityManager()->createQueryBuilder() + $query = $this->getEntityManager()->createQueryBuilder() ->select("e") ->from($this->getBaseEntity(), "e") ->leftJoin(Presentation::class, 'p', 'WITH', 'e.id = p.id') @@ -459,7 +490,7 @@ public function getAllByPageLocationTBD(PagingInfo $paging_info, Filter $filter ->leftJoin('sp.registration_request', "sprr", Join::LEFT_JOIN) ->where("l.id is null or l.id = 0"); - if(!is_null($filter)){ + if (!is_null($filter)) { $filter->apply2Query($query, $this->getCustomFilterMappings($current_member_id, $current_track_id)); } @@ -467,18 +498,18 @@ public function getAllByPageLocationTBD(PagingInfo $paging_info, Filter $filter $order->apply2Query($query, $this->getOrderMappings()); } else { //default order - $query = $query->addOrderBy("e.start_date",'ASC'); + $query = $query->addOrderBy("e.start_date", 'ASC'); $query = $query->addOrderBy("e.end_date", 'ASC'); } $can_view_private_events = self::isCurrentMemberOnGroup(IGroup::SummitAdministrators); - if(!$can_view_private_events){ - $idx = 1; - foreach(self::$forbidden_classes as $forbidden_class){ + if (!$can_view_private_events) { + $idx = 1; + foreach (self::$forbidden_classes as $forbidden_class) { $query = $query - ->andWhere("not e INSTANCE OF :forbidden_class".$idx); - $query->setParameter("forbidden_class".$idx, $forbidden_class); + ->andWhere("not e INSTANCE OF :forbidden_class" . $idx); + $query->setParameter("forbidden_class" . $idx, $forbidden_class); $idx++; } } @@ -488,11 +519,11 @@ public function getAllByPageLocationTBD(PagingInfo $paging_info, Filter $filter ->setMaxResults($paging_info->getPerPage()); $paginator = new Paginator($query, $fetchJoinCollection = true); - $total = $paginator->count(); - $data = []; + $total = $paginator->count(); + $data = []; - foreach($paginator as $entity) - $data[]= $entity; + foreach ($paginator as $entity) + $data[] = $entity; return new PagingResponse ( @@ -505,16 +536,17 @@ public function getAllByPageLocationTBD(PagingInfo $paging_info, Filter $filter } /** - * @param Summit $summit, + * @param Summit $summit , * @param array $external_ids * @return mixed */ public function getPublishedEventsBySummitNotInExternalIds(Summit $summit, array $external_ids) { - $query = $this->getEntityManager()->createQueryBuilder() + $query = $this->getEntityManager()->createQueryBuilder() ->select("e") ->from($this->getBaseEntity(), "e") ->join('e.summit', 's', Join::WITH, " s.id = :summit_id") + ->join('e.summit', 's', Join::WITH, " s.id = :summit_id") ->where('e.published = 1') ->andWhere('e.external_id not in (:external_ids)') ->setParameter('summit_id', $summit->getId()) @@ -524,12 +556,12 @@ public function getPublishedEventsBySummitNotInExternalIds(Summit $summit, array } /** - * @param int $summit_id, + * @param int $summit_id , * @return array */ - public function getPublishedEventsIdsBySummit(int $summit_id):array + public function getPublishedEventsIdsBySummit(int $summit_id): array { - $query = $this->getEntityManager() + $query = $this->getEntityManager() ->createQueryBuilder() ->select("e.id") ->from($this->getBaseEntity(), "e") @@ -550,16 +582,16 @@ public function getPublishedEventsIdsBySummit(int $summit_id):array public function getAllPublishedTagsByPage(PagingInfo $paging_info, Filter $filter = null, Order $order = null): PagingResponse { - $query = $this->getEntityManager()->createQueryBuilder() + $query = $this->getEntityManager()->createQueryBuilder() ->select("distinct t") ->from(Tag::class, "t") ->join("t.events", 'e') ->leftJoin(Presentation::class, 'p', 'WITH', 'e.id = p.id'); - if(!is_null($filter)){ + if (!is_null($filter)) { $filter->apply2Query($query, [ 'tag' => 't.tag:json_string', - 'summit_id'=> new DoctrineJoinFilterMapping + 'summit_id' => new DoctrineJoinFilterMapping ( 'e.summit', 's', @@ -581,11 +613,11 @@ public function getAllPublishedTagsByPage(PagingInfo $paging_info, Filter $filte ->setMaxResults($paging_info->getPerPage()); $paginator = new Paginator($query, $fetchJoinCollection = true); - $total = $paginator->count(); - $data = []; + $total = $paginator->count(); + $data = []; - foreach($paginator as $entity) - $data[]= $entity; + foreach ($paginator as $entity) + $data[] = $entity; return new PagingResponse ( diff --git a/app/Security/SummitScopes.php b/app/Security/SummitScopes.php index 72f1995bf..0763fbde2 100644 --- a/app/Security/SummitScopes.php +++ b/app/Security/SummitScopes.php @@ -107,4 +107,6 @@ final class SummitScopes const ReadSummitMediaFileTypes = '%s/summit-media-file-types/read'; const WriteSummitMediaFileTypes = '%s/summit-media-file-types/write'; + + const Allow2PresentationAttendeeVote = '%s/presentations/attendee-vote'; } \ No newline at end of file diff --git a/app/Services/Model/IPresentationService.php b/app/Services/Model/IPresentationService.php index 017944de7..bcf4fd7d9 100644 --- a/app/Services/Model/IPresentationService.php +++ b/app/Services/Model/IPresentationService.php @@ -15,6 +15,7 @@ use models\exceptions\ValidationException; use models\main\Member; use models\summit\Presentation; +use models\summit\PresentationAttendeeVote; use models\summit\PresentationLink; use models\summit\PresentationMediaUpload; use models\summit\PresentationSlide; @@ -197,4 +198,23 @@ public function updateMediaUploadFrom * @throws EntityNotFoundException */ public function deleteMediaUpload(Summit $summit, int $presentation_id, int $media_upload_id): void; + + /** + * @param Summit $summit + * @param Member $member + * @param int $presentation_id + * @throws EntityNotFoundException + * @throws ValidationException + */ + public function castAttendeeVote(Summit $summit, Member $member, int $presentation_id):PresentationAttendeeVote; + + /** + * @param Summit $summit + * @param Member $member + * @param int $presentation_id + * @throws EntityNotFoundException + * @throws ValidationException + */ + public function unCastAttendeeVote(Summit $summit, Member $member, int $presentation_id):void; + } \ No newline at end of file diff --git a/app/Services/Model/ISummitService.php b/app/Services/Model/ISummitService.php index 4a24acb4f..c6d1f9955 100644 --- a/app/Services/Model/ISummitService.php +++ b/app/Services/Model/ISummitService.php @@ -53,11 +53,11 @@ public function updateEvent(Summit $summit, $event_id, array $data); /** * @param Summit $summit - * @param $event_id + * @param int $event_id * @param array $data - * @return mixed + * @return SummitEvent */ - public function publishEvent(Summit $summit, $event_id, array $data); + public function publishEvent(Summit $summit, $event_id, array $data):SummitEvent; /** * @param Summit $summit diff --git a/app/Services/Model/Imp/PresentationService.php b/app/Services/Model/Imp/PresentationService.php index 2cbf9943b..27ddc50b5 100644 --- a/app/Services/Model/Imp/PresentationService.php +++ b/app/Services/Model/Imp/PresentationService.php @@ -38,9 +38,11 @@ use models\exceptions\ValidationException; use models\main\IFolderRepository; use models\main\ITagRepository; +use models\main\Member; use models\summit\ISpeakerRepository; use models\summit\ISummitEventRepository; use models\summit\Presentation; +use models\summit\PresentationAttendeeVote; use models\summit\PresentationLink; use models\summit\PresentationMediaUpload; use models\summit\PresentationSlide; @@ -1164,4 +1166,70 @@ public function deleteMediaUpload(Summit $summit, int $presentation_id, int $med $presentation->removeMediaUpload($mediaUpload); }); } + + /** + * @param Summit $summit + * @param Member $member + * @param int $presentation_id + * @throws EntityNotFoundException + * @throws ValidationException + */ + public function castAttendeeVote(Summit $summit, Member $member, int $presentation_id): PresentationAttendeeVote + { + return $this->tx_service->transaction(function () use($summit, $member, $presentation_id){ + $presentation = $this->presentation_repository->getById($presentation_id); + if(is_null($presentation) || !$presentation instanceof Presentation) + throw new EntityNotFoundException("Presentation not found."); + + if($presentation->getSummitId() !== $summit->getId()) + throw new EntityNotFoundException("Presentation not found."); + + $attendee = $summit->getAttendeeByMember($member); + + if(is_null($attendee)) + throw new ValidationException(sprintf("Current Member is not an attendee at Summit %s.", $summit->getId())); + + $currentTrack = $presentation->getCategory(); + + foreach($currentTrack->getGroups() as $currentTrackGroup){ + // check voting period + if(!$currentTrackGroup->isAttendeeVotingPeriodOpen()) + throw new ValidationException("Attendee Voting Period for track group %s is closed.", $currentTrackGroup->getName()); + + // check voting count + + if(!$currentTrackGroup->canEmitAttendeeVote($attendee)){ + throw new ValidationException("You Reached the Max. allowed votes for Track Group (%s)", $currentTrackGroup->getMaxAttendeeVotes()); + } + } + + $presentation->castAttendeeVote($attendee); + }); + } + + /** + * @param Summit $summit + * @param Member $member + * @param int $presentation_id + * @throws EntityNotFoundException + * @throws ValidationException + */ + public function unCastAttendeeVote(Summit $summit, Member $member, int $presentation_id): void + { + $this->tx_service->transaction(function () use($summit, $member, $presentation_id){ + $presentation = $this->presentation_repository->getById($presentation_id); + if(is_null($presentation) || !$presentation instanceof Presentation) + throw new EntityNotFoundException("Presentation not found."); + + if($presentation->getSummitId() !== $summit->getId()) + throw new EntityNotFoundException("Presentation not found."); + + $attendee = $summit->getAttendeeByMember($member); + + if(is_null($attendee)) + throw new ValidationException(sprintf("Current Member is not an attendee at Summit %s.", $summit->getId())); + + $presentation->unCastAttendeeVote($attendee); + }); + } } \ No newline at end of file diff --git a/app/Services/Model/Imp/SummitRegistrationInvitationService.php b/app/Services/Model/Imp/SummitRegistrationInvitationService.php index a5dda878e..07bc8228b 100644 --- a/app/Services/Model/Imp/SummitRegistrationInvitationService.php +++ b/app/Services/Model/Imp/SummitRegistrationInvitationService.php @@ -414,7 +414,7 @@ public function send(int $summit_id, array $payload, Filter $filter = null): voi do{ - Log::debug(sprintf("SummitRegistrationInvitationService::send summit id %s flow_event %s filter %s processing page %s", $summit_id, $flow_event, $filter->__toString(), $page)); + Log::debug(sprintf("SummitRegistrationInvitationService::send summit id %s flow_event %s filter %s processing page %s", $summit_id, $flow_event, is_null($filter) ? '' : $filter->__toString(), $page)); $ids = $this->tx_service->transaction(function () use ($summit_id, $payload, $filter, $page) { if (isset($payload['invitations_ids'])) { @@ -430,7 +430,7 @@ public function send(int $summit_id, array $payload, Filter $filter = null): voi return $this->invitation_repository->getAllIdsByPage(new PagingInfo($page, 100), $filter); }); - Log::debug(sprintf("SummitRegistrationInvitationService::send summit id %s flow_event %s filter %s page %s got %s records", $summit_id, $flow_event, $filter->__toString(), $page, count($ids))); + Log::debug(sprintf("SummitRegistrationInvitationService::send summit id %s flow_event %s filter %s page %s got %s records", $summit_id, $flow_event, is_null($filter) ? '' : $filter->__toString(), $page, count($ids))); if (!count($ids)) { // if we are processing a page , then break it Log::debug(sprintf("SummitRegistrationInvitationService::send summit id %s page is empty, ending processing.", $summit_id)); @@ -474,6 +474,6 @@ public function send(int $summit_id, array $payload, Filter $filter = null): voi }while(!$done); - Log::debug(sprintf("SummitRegistrationInvitationService::send summit id %s flow_event %s filter %s had processed %s records", $summit_id, $flow_event, $filter->__toString(), $count)); + Log::debug(sprintf("SummitRegistrationInvitationService::send summit id %s flow_event %s filter %s had processed %s records", $summit_id, $flow_event, is_null($filter) ? '' : $filter->__toString(), $count)); } } \ No newline at end of file diff --git a/app/Services/Model/Imp/SummitService.php b/app/Services/Model/Imp/SummitService.php index 4e9c5a971..e8effe019 100644 --- a/app/Services/Model/Imp/SummitService.php +++ b/app/Services/Model/Imp/SummitService.php @@ -624,6 +624,14 @@ private function updateEventDates(array $data, Summit $summit, SummitEvent $even { if (isset($data['start_date']) && isset($data['end_date'])) { + if(!$event->hasType()){ + throw new ValidationException("To be able to set schedule dates event type must be set First."); + } + + $type = $event->getType(); + if(!$type->isAllowsPublishingDates()) + throw new ValidationException("Event Type does not allow schedule dates."); + $event->setSummit($summit); $start_datetime = intval($data['start_date']); $start_datetime = new \DateTime("@$start_datetime"); @@ -754,7 +762,6 @@ private function saveOrUpdateEvent(Summit $summit, array $data, $event_id = null if (is_null($event_type)) $event_type = $old_event_type; } - if (is_null($event_id) && is_null($event_type)) { // is event is new one and we dont provide an event type ... throw new ValidationException('type_id is mandatory!'); @@ -804,8 +811,14 @@ private function saveOrUpdateEvent(Summit $summit, array $data, $event_id = null $event->setCategory($track); } - if (!is_null($location)) + if (!is_null($location)) { + if(!$event->hasType()){ + throw new ValidationException("To be able to set a location, event type must be set First."); + } + if(!$event_type->isAllowsLocation()) + throw new ValidationException("Event Type does not allow location."); $event->setLocation($location); + } if (is_null($location) && isset($data['location_id'])) { // clear location @@ -844,6 +857,11 @@ private function saveOrUpdateEvent(Summit $summit, array $data, $event_id = null $this->saveOrUpdatePresentationData($event, $event_type, $data); $this->saveOrUpdateSummitGroupEventData($event, $event_type, $data); + if(!$event_type->isAllowsLocation()) + $event->clearLocation(); + if(!$event_type->isAllowsPublishingDates()) + $event->clearPublishingDates(); + if ($event->isPublished()) { $this->validateBlackOutTimesAndTimes($event); $event->unPublish(); @@ -968,9 +986,8 @@ private function saveOrUpdatePresentationData(SummitEvent $event, SummitEventTyp * @param array $data * @return SummitEvent */ - public function publishEvent(Summit $summit, $event_id, array $data) + public function publishEvent(Summit $summit, $event_id, array $data):SummitEvent { - return $this->tx_service->transaction(function () use ($summit, $data, $event_id) { $event = $this->event_repository->getById($event_id); @@ -978,9 +995,11 @@ public function publishEvent(Summit $summit, $event_id, array $data) if (is_null($event) || !$event instanceof SummitEvent) throw new EntityNotFoundException(sprintf("event id %s does not exists!", $event_id)); - if (is_null($event->getType())) + if (!$event->hasType()) throw new EntityNotFoundException(sprintf("event type its not assigned to event id %s!", $event_id)); + $type = $event->getType(); + if (is_null($event->getSummit())) throw new EntityNotFoundException(sprintf("summit its not assigned to event id %s!", $event_id)); @@ -989,16 +1008,18 @@ public function publishEvent(Summit $summit, $event_id, array $data) $this->updateEventDates($data, $summit, $event); - $start_datetime = $event->getStartDate(); - $end_datetime = $event->getEndDate(); + if($type->isAllowsPublishingDates()) { + $start_datetime = $event->getStartDate(); + $end_datetime = $event->getEndDate(); - if (is_null($start_datetime)) - throw new ValidationException(sprintf("start_date its not assigned to event id %s!", $event_id)); + if (is_null($start_datetime)) + throw new ValidationException(sprintf("start_date its not assigned to event id %s!", $event_id)); - if (is_null($end_datetime)) - throw new ValidationException(sprintf("end_date its not assigned to event id %s!", $event_id)); + if (is_null($end_datetime)) + throw new ValidationException(sprintf("end_date its not assigned to event id %s!", $event_id)); + } - if (isset($data['location_id'])) { + if (isset($data['location_id']) && $type->isAllowsLocation()) { $location_id = intval($data['location_id']); $event->clearLocation(); if ($location_id > 0) { @@ -1021,7 +1042,7 @@ public function publishEvent(Summit $summit, $event_id, array $data) private function validateBlackOutTimesAndTimes(SummitEvent $event) { $current_event_location = $event->getLocation(); - + if(!$event->getType()->isAllowsPublishingDates()) return; // validate blackout times $conflict_events = $this->event_repository->getPublishedOnSameTimeFrame($event); if (!is_null($conflict_events)) { @@ -1069,7 +1090,6 @@ private function validateBlackOutTimesAndTimes(SummitEvent $event) } } } - } } } diff --git a/config/doctrine.php b/config/doctrine.php index 8f60b3d43..c13ac167a 100644 --- a/config/doctrine.php +++ b/config/doctrine.php @@ -182,7 +182,9 @@ | DQL custom numeric functions |-------------------------------------------------------------------------- */ - 'custom_numeric_functions' => [], + 'custom_numeric_functions' => [ + "rand" => DoctrineExtensions\Query\Mysql\Rand::class, + ], /* |-------------------------------------------------------------------------- | DQL custom string functions diff --git a/database/migrations/model/Version20220127210145.php b/database/migrations/model/Version20220127210145.php new file mode 100644 index 000000000..fab54fe30 --- /dev/null +++ b/database/migrations/model/Version20220127210145.php @@ -0,0 +1,52 @@ +hasTable("SummitEventType") && !$builder->hasColumn("SummitEventType", "AllowsPublishingDates")) { + $builder->table('SummitEventType', function (Table $table) { + $table->boolean('AllowsPublishingDates')->setNotnull(true)->setDefault(true); + $table->boolean('AllowsLocation')->setNotnull(true)->setDefault(true); + }); + } + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema): void + { + $builder = new Builder($schema); + if($schema->hasTable("SummitEventType") && $builder->hasColumn("SummitEventType", "AllowsPublishingDates")) { + $builder->table('SummitEventType', function (Table $table) { + $table->dropColumn('AllowsPublishingDates'); + $table->dropColumn('AllowsLocation'); + }); + } + } +} diff --git a/database/migrations/model/Version20220127210146.php b/database/migrations/model/Version20220127210146.php new file mode 100644 index 000000000..4cc0a50dc --- /dev/null +++ b/database/migrations/model/Version20220127210146.php @@ -0,0 +1,52 @@ +hasTable("PresentationType") && !$builder->hasColumn("PresentationType", "AllowAttendeeVote")) { + $builder->table('PresentationType', function (Table $table) { + $table->boolean('AllowAttendeeVote')->setNotnull(true)->setDefault(false); + $table->boolean('AllowCustomOrdering')->setNotnull(true)->setDefault(false); + }); + } + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema): void + { + $builder = new Builder($schema); + if($schema->hasTable("PresentationType") && $builder->hasColumn("PresentationType", "AllowAttendeeVote")) { + $builder->table('PresentationType', function (Table $table) { + $table->dropColumn('AllowAttendeeVote'); + $table->dropColumn('AllowCustomOrdering'); + }); + } + } +} diff --git a/database/migrations/model/Version20220128194504.php b/database/migrations/model/Version20220128194504.php new file mode 100644 index 000000000..75128c361 --- /dev/null +++ b/database/migrations/model/Version20220128194504.php @@ -0,0 +1,50 @@ +hasTable("Presentation") && !$builder->hasColumn("Presentation", "CustomOrder")) { + $builder->table('Presentation', function (Table $table) { + $table->integer('CustomOrder')->setNotnull(true)->setDefault(0); + }); + } + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema): void + { + $builder = new Builder($schema); + if($schema->hasTable("Presentation") && $builder->hasColumn("Presentation", "CustomOrder")) { + $builder->table('Presentation', function (Table $table) { + $table->dropColumn('CustomOrder'); + }); + } + } +} diff --git a/database/migrations/model/Version20220128200351.php b/database/migrations/model/Version20220128200351.php new file mode 100644 index 000000000..2f36c7dbb --- /dev/null +++ b/database/migrations/model/Version20220128200351.php @@ -0,0 +1,55 @@ +hasTable("PresentationCategoryGroup") && !$builder->hasColumn("PresentationCategoryGroup", "MaxUniqueAttendeeVotes")) { + $builder->table('PresentationCategoryGroup', function (Table $table) { + $table->integer('MaxUniqueAttendeeVotes')->setNotnull(true)->setDefault(0); + $table->dateTime('BeginAttendeeVotingPeriodDate')->setNotnull(false)->setDefault(null); + $table->dateTime('EndAttendeeVotingPeriodDate')->setNotnull(false)->setDefault(null); + }); + } + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema): void + { + $builder = new Builder($schema); + if($schema->hasTable("PresentationCategoryGroup") && $builder->hasColumn("PresentationCategoryGroup", "MaxUniqueAttendeeVotes")) { + $builder->table('PresentationCategoryGroup', function (Table $table) { + $table->dropColumn('MaxUniqueAttendeeVotes'); + $table->dropColumn('BeginAttendeeVotingPeriodDate'); + $table->dropColumn('EndAttendeeVotingPeriodDate'); + }); + } + } +} diff --git a/database/migrations/model/Version20220131195047.php b/database/migrations/model/Version20220131195047.php new file mode 100644 index 000000000..e81541096 --- /dev/null +++ b/database/migrations/model/Version20220131195047.php @@ -0,0 +1,63 @@ +hasTable("PresentationAttendeeVote")) { + $builder->create("PresentationAttendeeVote", function (Table $table) { + + $table->integer("ID", true, false); + $table->primary("ID"); + $table->timestamp('Created'); + $table->timestamp('LastEdited'); + $table->string('ClassName'); + + $table->integer("PresentationID", false, false)->setNotnull(false)->setDefault('NULL'); + $table->index("PresentationID", "PresentationID"); + $table->foreign("Presentation", "PresentationID", "ID", ["onDelete" => "CASCADE"]); + + $table->integer("SummitAttendeeID", false, false)->setNotnull(false)->setDefault('NULL'); + $table->index("SummitAttendeeID", "SummitAttendeeID"); + $table->foreign("SummitAttendee", "SummitAttendeeID", "ID", ["onDelete" => "CASCADE"]); + + $table->unique(['PresentationID', 'SummitAttendeeID']); + }); + } + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema): void + { + $builder = new Builder($schema); + + $builder->dropIfExists("PresentationAttendeeVote"); + } +} diff --git a/database/migrations/model/Version20220131201421.php b/database/migrations/model/Version20220131201421.php new file mode 100644 index 000000000..3883d572e --- /dev/null +++ b/database/migrations/model/Version20220131201421.php @@ -0,0 +1,42 @@ +addSql($sql); + } + + /** + * @param Schema $schema + */ + public function down(Schema $schema): void + { + + } +} diff --git a/database/seeders/ApiEndpointsSeeder.php b/database/seeders/ApiEndpointsSeeder.php index 4f9405de7..793aec4f8 100644 --- a/database/seeders/ApiEndpointsSeeder.php +++ b/database/seeders/ApiEndpointsSeeder.php @@ -2344,7 +2344,7 @@ private function seedSummitEndpoints() 'scopes' => [sprintf(SummitScopes::WriteSummitData, $current_realm)], ), // events - array( + [ 'name' => 'get-events', 'route' => '/api/v1/summits/{id}/events', 'http_method' => 'GET', @@ -2357,7 +2357,7 @@ private function seedSummitEndpoints() IGroup::Administrators, IGroup::SummitAdministrators, ] - ), + ], [ 'name' => 'get-events-csv', 'route' => '/api/v1/summits/{id}/events/csv', @@ -4355,6 +4355,52 @@ private function seedSummitEndpoints() IGroup::SummitAdministrators, ] ], + // presentations + [ + 'name' => 'get-presentations', + 'route' => '/api/v1/summits/{id}/presentations', + 'http_method' => 'GET', + 'scopes' => [ + sprintf(SummitScopes::ReadSummitData, $current_realm), + sprintf(SummitScopes::ReadAllSummitData, $current_realm) + ], + ], + // attendees votes + [ + 'name' => 'cast-attendee-vote', + 'route' => '/api/v1/summits/{id}/presentations/{id}/attendee-votes', + 'http_method' => 'POST', + 'scopes' => [ + sprintf(SummitScopes::Allow2PresentationAttendeeVote, $current_realm), + ], + ], + [ + 'name' => 'uncast-attendee-vote', + 'route' => '/api/v1/summits/{id}/presentations/{id}/attendee-votes', + 'http_method' => 'DELETE', + 'scopes' => [ + sprintf(SummitScopes::Allow2PresentationAttendeeVote, $current_realm), + ], + ], + [ + 'name' => 'get-attendees-votes', + 'route' => '/api/v1/summits/{id}/presentations/{id}/attendee-votes', + 'http_method' => 'GET', + 'scopes' => [ + sprintf(SummitScopes::ReadSummitData, $current_realm), + sprintf(SummitScopes::ReadAllSummitData, $current_realm) + ], + ], + // voteable presentations + [ + 'name' => 'get-voteable-presentations', + 'route' => '/api/v1/summits/{id}/presentations/voteable', + 'http_method' => 'GET', + 'scopes' => [ + sprintf(SummitScopes::ReadSummitData, $current_realm), + sprintf(SummitScopes::ReadAllSummitData, $current_realm) + ], + ], // presentation submissions [ 'name' => 'submit-presentation', diff --git a/database/seeders/ApiScopesSeeder.php b/database/seeders/ApiScopesSeeder.php index 390385b2c..0a4012a8a 100644 --- a/database/seeders/ApiScopesSeeder.php +++ b/database/seeders/ApiScopesSeeder.php @@ -375,6 +375,11 @@ private function seedSummitScopes() 'short_description' => 'Allow virtual Check In', 'description' => 'Allow virtual Check In', ], + [ + 'name' => sprintf(SummitScopes::Allow2PresentationAttendeeVote, $current_realm), + 'short_description' => 'Allow Attendee Vote on Presentation', + 'description' => 'Allow Attendee Vote on Presentation', + ], ]; foreach ($scopes as $scope_info) { diff --git a/database/seeders/TestSeeder.php b/database/seeders/TestSeeder.php index 436c42f6b..710a7086d 100644 --- a/database/seeders/TestSeeder.php +++ b/database/seeders/TestSeeder.php @@ -24,6 +24,16 @@ final class TestSeeder extends Seeder public function run() { Model::unguard(); + DB::setDefaultConnection("model"); + DB::table('Presentation')->delete(); + DB::table('SummitEvent')->delete(); + DB::table('Summit')->delete(); + DB::table('SummitEventType')->delete(); + DB::table('PresentationType')->delete(); + DB::table('SummitAbstractLocation')->delete(); + DB::table('SummitGeoLocatedLocation')->delete(); + DB::table('SummitVenue')->delete(); + DB::setDefaultConnection("config"); $this->call(ApiSeeder::class); $this->call(ApiScopesSeeder::class); diff --git a/routes/api_v1.php b/routes/api_v1.php index 38347e907..e1a0a6135 100644 --- a/routes/api_v1.php +++ b/routes/api_v1.php @@ -377,7 +377,6 @@ Route::get('', ['middleware' => 'auth.user', 'uses' => 'OAuth2SummitEventsApiController@getEvents']); - Route::group(['prefix' => 'csv'], function () { Route::get('', ['middleware' => 'auth.user', 'uses' => 'OAuth2SummitEventsApiController@getEventsCSV']); Route::post('', ['middleware' => 'auth.user', 'uses' => 'OAuth2SummitEventsApiController@importEventData']); @@ -429,6 +428,8 @@ // presentations Route::group(['prefix' => 'presentations'], function () { + Route::get('', [ 'uses' => 'OAuth2SummitEventsApiController@getAllPresentations']); + Route::get('voteable', [ 'uses' => 'OAuth2SummitEventsApiController@getAllVoteablePresentations']); // opened without role CFP - valid selection plan on CFP status Route::post('', 'OAuth2PresentationApiController@submitPresentation'); // import from mux @@ -487,6 +488,14 @@ Route::delete('', 'OAuth2PresentationApiController@deletePresentationMediaUpload'); }); }); + + // attendees votes + + Route::group(['prefix' => 'attendee-votes'], function(){ + Route::get('', ['middleware' => 'auth.user', 'uses' => 'OAuth2PresentationApiController@getAttendeeVotes']); + Route::post('', ['middleware' => 'auth.user', 'uses' => 'OAuth2PresentationApiController@castAttendeeVote']); + Route::delete('', ['middleware' => 'auth.user', 'uses' => 'OAuth2PresentationApiController@unCastAttendeeVote']); + }); }); }); diff --git a/tests/InsertSummitTestData.php b/tests/InsertSummitTestData.php index 211602b42..2adc135d5 100644 --- a/tests/InsertSummitTestData.php +++ b/tests/InsertSummitTestData.php @@ -16,7 +16,9 @@ use LaravelDoctrine\ORM\Facades\EntityManager; use LaravelDoctrine\ORM\Facades\Registry; use models\main\Tag; +use models\summit\ISummitEventType; use models\summit\PresentationCategoryGroup; +use models\summit\SummitEvent; use models\utils\SilverstripeBaseModel; use models\summit\SummitVenue; use models\summit\Summit; @@ -70,6 +72,16 @@ trait InsertSummitTestData */ static $defaultTrack; + /** + * @var PresentationType + */ + static $defaultPresentationType; + + /** + * @var PresentationType + */ + static $allow2VotePresentationType; + /** * @var PresentationCategory */ @@ -139,19 +151,42 @@ protected static function insertTestData(){ self::$summit->setRegistrationEndDate((clone $begin_date)->add(new DateInterval("P30D"))); self::$summit->setName("TEST SUMMIT"); - $presentation_type = new PresentationType(); - $presentation_type->setType('TEST PRESENTATION TYPE'); - $presentation_type->setMinSpeakers(1); - $presentation_type->setMaxSpeakers(3); - $presentation_type->setMinModerators(0); - $presentation_type->setMaxModerators(0); - $presentation_type->setUseSpeakers(true); - $presentation_type->setShouldBeAvailableOnCfp(true); - $presentation_type->setAreSpeakersMandatory(false); - $presentation_type->setUseModerator(false); - $presentation_type->setIsModeratorMandatory(false); + self::$defaultPresentationType = new PresentationType(); + self::$defaultPresentationType->setType('TEST PRESENTATION TYPE'); + self::$defaultPresentationType->setAsDefault(); + self::$defaultPresentationType->setMinSpeakers(1); + self::$defaultPresentationType->setMaxSpeakers(3); + self::$defaultPresentationType->setMinModerators(0); + self::$defaultPresentationType->setMaxModerators(0); + self::$defaultPresentationType->setUseSpeakers(true); + self::$defaultPresentationType->setShouldBeAvailableOnCfp(true); + self::$defaultPresentationType->setAreSpeakersMandatory(false); + self::$defaultPresentationType->setUseModerator(false); + self::$defaultPresentationType->setIsModeratorMandatory(false); + + self::$summit->addEventType(self::$defaultPresentationType); + + self::$allow2VotePresentationType = new PresentationType(); + self::$allow2VotePresentationType->setType('TEST PRESENTATION TYPE VOTABLE'); + self::$allow2VotePresentationType->setMinSpeakers(1); + self::$allow2VotePresentationType->setMaxSpeakers(3); + self::$allow2VotePresentationType->setMinModerators(0); + self::$allow2VotePresentationType->setMaxModerators(0); + self::$allow2VotePresentationType->setUseSpeakers(true); + self::$allow2VotePresentationType->setShouldBeAvailableOnCfp(true); + self::$allow2VotePresentationType->setAreSpeakersMandatory(false); + self::$allow2VotePresentationType->setUseModerator(false); + self::$allow2VotePresentationType->setIsModeratorMandatory(false); + self::$allow2VotePresentationType->setAllowAttendeeVote(true); + self::$allow2VotePresentationType->setAllowCustomOrdering(true); + self::$allow2VotePresentationType->setAllowsLocation(false); + self::$allow2VotePresentationType->setAllowsPublishingDates(false); + + self::$defaultEventType = new SummitEventType(); + self::$defaultEventType->setType(ISummitEventType::Breaks); + self::$summit->addEventType(self::$defaultEventType); - self::$summit->addEventType($presentation_type); + self::$summit->addEventType(self::$allow2VotePresentationType); self::$summit2 = new Summit(); self::$summit2->setActive(true); @@ -212,19 +247,6 @@ protected static function insertTestData(){ self::$summit->addPresentationCategory(self::$secondaryTrack); self::$summit->addCategoryGroup(self::$defaultTrackGroup); - self::$defaultEventType = new PresentationType(); - self::$defaultEventType->setType(IPresentationType::Presentation); - self::$defaultEventType->setMinSpeakers(1); - self::$defaultEventType->setMaxSpeakers(3); - self::$defaultEventType->setMinModerators(0); - self::$defaultEventType->setMaxModerators(0); - self::$defaultEventType->setUseSpeakers(true); - self::$defaultEventType->setShouldBeAvailableOnCfp(true); - self::$defaultEventType->setAreSpeakersMandatory(false); - self::$defaultEventType->setUseModerator(false); - self::$defaultEventType->setIsModeratorMandatory(false); - self::$summit->addEventType(self::$defaultEventType); - self::$default_selection_plan = new SelectionPlan(); self::$default_selection_plan->setName("TEST_SELECTION_PLAN"); $submission_begin_date = new DateTime('now', self::$summit->getTimeZone()); @@ -247,6 +269,9 @@ protected static function insertTestData(){ self::$presentations = []; + $start_date = clone($begin_date); + $end_date = clone($start_date); + $end_date = $end_date->add(new DateInterval("P1D")); for($i = 0 ; $i < 20; $i++){ $presentation = new Presentation(); $presentation->setTitle(sprintf("Presentation Title %s %s", $i, str_random(16))); @@ -254,10 +279,40 @@ protected static function insertTestData(){ $presentation->setCategory(self::$defaultTrack); $presentation->setProgress(Presentation::PHASE_COMPLETE); $presentation->setStatus(Presentation::STATUS_RECEIVED); - $presentation->setType( self::$defaultEventType ); + $presentation->setType( self::$defaultPresentationType ); + $presentation->setStartDate($start_date); + $presentation->setEndDate($end_date); self::$default_selection_plan->addPresentation($presentation); self::$summit->addEvent($presentation); self::$presentations[] = $presentation; + $presentation->publish(); + $start_date = clone($start_date); + $start_date = $start_date->add(new DateInterval("P1D")); + $end_date = clone($start_date); + $end_date = $end_date->add(new DateInterval("P1D")); + } + + for($i = 0 ; $i < 20; $i++){ + $presentation = new Presentation(); + $presentation->setTitle(sprintf("Presentation Title %s %s Votable", $i, str_random(16))); + $presentation->setAbstract(sprintf("Presentation Abstract %s %s Votable", $i, str_random(16))); + $presentation->setCategory(self::$defaultTrack); + $presentation->setProgress(Presentation::PHASE_COMPLETE); + $presentation->setStatus(Presentation::STATUS_RECEIVED); + $presentation->setType( self::$allow2VotePresentationType ); + self::$summit->addEvent($presentation); + self::$presentations[] = $presentation; + $presentation->publish(); + } + + for($i = 0 ; $i < 20; $i++){ + $event = new SummitEvent(); + $event->setTitle(sprintf("Raw Event Title %s %s", $i, str_random(16))); + $event->setAbstract(sprintf("Raw Event Abstract %s %s", $i, str_random(16))); + $event->setCategory(self::$defaultTrack); + $event->setType( self::$defaultEventType ); + self::$summit->addEvent($event); + self::$presentations[] = $event; } self::$summit_permission_group = new SummitAdministratorPermissionGroup(); diff --git a/tests/OAuth2EventTypesApiTest.php b/tests/OAuth2EventTypesApiTest.php index 776963b69..b28c13d1f 100644 --- a/tests/OAuth2EventTypesApiTest.php +++ b/tests/OAuth2EventTypesApiTest.php @@ -17,10 +17,23 @@ */ final class OAuth2EventTypesApiTest extends ProtectedApiTest { + use InsertSummitTestData; + + protected function setUp(): void + { + parent::setUp(); + self::insertTestData(); + } + + protected function tearDown(): void + { + self::clearTestData(); + parent::tearDown(); + } + public function testGetEventTypesByClassName(){ $params = [ - - 'id' => 23, + 'id' => self::$summit->getId(), 'page' => 1, 'per_page' => 10, 'filter' => 'class_name==EVENT_TYPE', @@ -51,8 +64,7 @@ public function testGetEventTypesByClassName(){ public function testGetEventTypesByClassNameCSV(){ $params = [ - - 'id' => 23, + 'id' => self::$summit->getId(), 'page' => 1, 'per_page' => 10, 'filter' => 'class_name==EVENT_TYPE', @@ -81,8 +93,7 @@ public function testGetEventTypesByClassNameCSV(){ public function testGetEventTypesDefaultOnes(){ $params = [ - - 'id' => 23, + 'id' => self::$summit->getId(), 'page' => 1, 'per_page' => 10, 'filter' => 'is_default==1', @@ -113,8 +124,7 @@ public function testGetEventTypesDefaultOnes(){ public function testGetEventTypesNonDefaultOnes(){ $params = [ - - 'id' => 23, + 'id' => self::$summit->getId(), 'page' => 1, 'per_page' => 10, 'filter' => 'is_default==0', @@ -145,7 +155,7 @@ public function testGetEventTypesNonDefaultOnes(){ public function testGetEventTypeAll(){ $params = [ - 'id' => 23, + 'id' => self::$summit->getId(), 'page' => 1, 'per_page' => 10, 'order' => '+name' @@ -176,7 +186,7 @@ public function testGetEventTypeAll(){ public function testGetEventTypesByClassNamePresentationType(){ $params = [ - 'id' => 23, + 'id' => self::$summit->getId(), 'page' => 1, 'per_page' => 10, 'filter' => 'class_name==PRESENTATION_TYPE', @@ -204,15 +214,17 @@ public function testGetEventTypesByClassNamePresentationType(){ $this->assertTrue(!is_null($event_types)); } - public function testAddEventType($summit_id = 23){ + public function testAddEventType(){ $params = [ - 'id' => $summit_id, + 'id' => self::$summit->getId(), ]; $name = str_random(16).'_eventtype'; $data = [ 'name' => $name, 'class_name' => \models\summit\SummitEventType::ClassName, + 'allows_publishing_dates' => false, + 'allows_location' => false, ]; $headers = [ @@ -235,15 +247,17 @@ public function testAddEventType($summit_id = 23){ $this->assertResponseStatus(201); $event_type = json_decode($content); $this->assertTrue(!is_null($event_type)); + $this->assertTrue($event_type->allows_location == false); + $this->assertTrue($event_type->allows_location == false); return $event_type; } - public function testUpdateEventType($summit_id = 23){ + public function testUpdateEventType(){ - $new_event_type = $this->testAddEventType($summit_id); + $new_event_type = $this->testAddEventType(); $params = [ - 'id' => $summit_id, + 'id' => self::$summit->getId(), 'event_type_id' => $new_event_type->id, ]; @@ -276,12 +290,12 @@ public function testUpdateEventType($summit_id = 23){ return $event_type; } - public function testDeleteDefaultOne($summit_id = 23){ + public function testDeleteDefaultOne(){ - $event_types = $this->testGetEventTypesDefaultOnes($summit_id); + $event_types = $this->testGetEventTypesDefaultOnes(); $params = [ - 'id' => $summit_id, + 'id' => self::$summit->getId(), 'event_type_id' => $event_types->data[0]->id, ]; @@ -301,16 +315,16 @@ public function testDeleteDefaultOne($summit_id = 23){ ); $content = $response->getContent(); - $this->assertResponseStatus(!empty($content)); + $this->assertTrue(!empty($content)); $this->assertResponseStatus(412); } - public function testDeleteNonDefaultOne($summit_id = 23){ + public function testDeleteNonDefaultOne(){ - $event_types = $this->testGetEventTypesNonDefaultOnes($summit_id); + $event_types = $this->testGetEventTypesNonDefaultOnes(); $params = [ - 'id' => $summit_id, + 'id' => self::$summit->getId(), 'event_type_id' => $event_types->data[0]->id, ]; @@ -330,13 +344,13 @@ public function testDeleteNonDefaultOne($summit_id = 23){ ); $content = $response->getContent(); - $this->assertResponseStatus(empty($content)); + $this->assertTrue(empty($content)); $this->assertResponseStatus(204); } - public function testSeedDefaultEventTYpes($summit_id = 23){ + public function testSeedDefaultEventTypes(){ $params = [ - 'id' => $summit_id, + 'id' => self::$summit->getId(), ]; $headers = [ diff --git a/tests/OAuth2SummitEventsApiTest.php b/tests/OAuth2SummitEventsApiTest.php index ffbaa199f..6ac3e672a 100644 --- a/tests/OAuth2SummitEventsApiTest.php +++ b/tests/OAuth2SummitEventsApiTest.php @@ -28,12 +28,11 @@ protected function tearDown():void parent::tearDown(); } - public function testPostEvent($summit_id = 23, $location_id = 0, $type_id = 0, $track_id = 0, $start_date = 1477645200, $end_date = 1477647600) + public function testAddPublishableEvent($start_date = 1477645200, $end_date = 1477647600, $location_id = 0) { - $params = array - ( - 'id' => $summit_id, - ); + $params = [ + 'id' => self::$summit->getId(), + ]; $headers = array ( @@ -46,9 +45,9 @@ public function testPostEvent($summit_id = 23, $location_id = 0, $type_id = 0, $ 'title' => 'Neutron: tbd', 'description' => 'TBD', 'allow_feedback' => true, - 'type_id' => $type_id, + 'type_id' => self::$defaultPresentationType->getId(), 'tags' => ['Neutron'], - 'track_id' => $track_id + 'track_id' => self::$defaultTrack->getId() ); if($start_date > 0){ @@ -82,12 +81,11 @@ public function testPostEvent($summit_id = 23, $location_id = 0, $type_id = 0, $ return $event; } - public function testPostEventRSVPTemplateUnExistent($summit_id = 23, $location_id = 0, $type_id = 124, $track_id = 208, $start_date = 1477645200, $end_date = 1477647600) + public function testAddNonPublishableEventWithScheduledDates($start_date = 1477645200, $end_date = 1477647600, $location_id = 0) { - $params = array - ( - 'id' => $summit_id, - ); + $params = [ + 'id' => self::$summit->getId(), + ]; $headers = array ( @@ -97,13 +95,12 @@ public function testPostEventRSVPTemplateUnExistent($summit_id = 23, $location_i $data = array ( - 'title' => 'Neutron: tbd', - 'description' => 'TBD', - 'allow_feedback' => true, - 'type_id' => $type_id, - 'tags' => ['Neutron'], - 'track_id' => $track_id, - 'rsvp_template_id' => 1, + 'title' => 'Neutron: tbd', + 'description' => 'TBD', + 'allow_feedback' => true, + 'type_id' => self::$allow2VotePresentationType->getId(), + 'tags' => ['Neutron'], + 'track_id' => self::$defaultTrack->getId() ); if($start_date > 0){ @@ -118,6 +115,167 @@ public function testPostEventRSVPTemplateUnExistent($summit_id = 23, $location_i $data['location_id'] = $location_id; } + $response = $this->action + ( + "POST", + "OAuth2SummitEventsApiController@addEvent", + $params, + array(), + array(), + array(), + $headers, + json_encode($data) + ); + + $content = $response->getContent(); + $this->assertResponseStatus(412); + $error = json_decode($content); + } + + public function testAddNonPublishableEventWithScheduledDatesAndLocation() + { + $params = [ + 'id' => self::$summit->getId(), + ]; + + $headers = array + ( + "HTTP_Authorization" => " Bearer " . $this->access_token, + "CONTENT_TYPE" => "application/json" + ); + + $start_date = clone(self::$summit->getBeginDate()); + $end_date = clone($start_date); + $end_date = $end_date->add(new \DateInterval("P1D")); + $data = array + ( + 'title' => 'Neutron: tbd', + 'description' => 'TBD', + 'allow_feedback' => true, + 'type_id' => self::$allow2VotePresentationType->getId(), + 'tags' => ['Neutron'], + 'track_id' => self::$defaultTrack->getId(), + 'start_date' => $start_date->getTimestamp(), + 'end_date' => $end_date->getTimestamp(), + 'location_id' => self::$mainVenue->getId() + ); + + $response = $this->action + ( + "POST", + "OAuth2SummitEventsApiController@addEvent", + $params, + array(), + array(), + array(), + $headers, + json_encode($data) + ); + + $content = $response->getContent(); + $this->assertResponseStatus(412); + $error = json_decode($content); + } + + public function testAddNonPublishableEvent() + { + $params = [ + 'id' => self::$summit->getId(), + ]; + + $headers = array + ( + "HTTP_Authorization" => " Bearer " . $this->access_token, + "CONTENT_TYPE" => "application/json" + ); + + $start_date = clone(self::$summit->getBeginDate()); + $end_date = clone($start_date); + $end_date = $end_date->add(new \DateInterval("P1D")); + $data = array + ( + 'title' => 'Neutron: tbd', + 'description' => 'TBD', + 'allow_feedback' => true, + 'type_id' => self::$allow2VotePresentationType->getId(), + 'tags' => ['Neutron'], + 'track_id' => self::$defaultTrack->getId(), + ); + + $response = $this->action + ( + "POST", + "OAuth2SummitEventsApiController@addEvent", + $params, + array(), + array(), + array(), + $headers, + json_encode($data) + ); + + $content = $response->getContent(); + $this->assertResponseStatus(201); + $event = json_decode($content); + $this->assertTrue($event->id > 0); + + $params = array + ( + 'id' => self::$summit->getId(), + 'event_id' => $event->id, + ); + + $headers = array + ( + "HTTP_Authorization" => " Bearer " . $this->access_token, + "CONTENT_TYPE" => "application/json" + ); + + $response = $this->action + ( + "PUT", + "OAuth2SummitEventsApiController@publishEvent", + $params, + array(), + array(), + array(), + $headers + ); + + $this->assertResponseStatus(201); + $content = $response->getContent(); + + $event = json_decode($content); + $this->assertTrue($event->id > 0); + $this->assertTrue(is_null($event->start_date)); + $this->assertTrue($event->is_published == true); + } + + public function testPostEventRSVPTemplateUnExistent() + { + $params = array + ( + 'id' => self::$summit->getId(), + ); + + $headers = array + ( + "HTTP_Authorization" => " Bearer " . $this->access_token, + "CONTENT_TYPE" => "application/json" + ); + + $data = array + ( + 'title' => 'Neutron: tbd', + 'description' => 'TBD', + 'allow_feedback' => true, + 'type_id' => self::$allow2VotePresentationType->getId(), + 'tags' => ['Neutron'], + 'track_id' => self::$defaultTrack->getId(), + 'rsvp_template_id' => 1, + ); + + $response = $this->action ( "POST", @@ -1069,16 +1227,119 @@ public function testGetUnpublishedEventBySummitOrderByTrack($summit_id=26) $this->assertTrue(!is_null($events)); } + public function testGetAllEvents(){ + $params = array + ( + 'id' => self::$summit->getId(), + 'page' => 1, + 'per_page' => 5, + 'filter' => [ + 'published==1', + 'type_allows_attendee_vote==1', + ], + 'order' => 'random' + ); + + $headers = array + ( + "HTTP_Authorization" => " Bearer " . $this->access_token, + "CONTENT_TYPE" => "application/json" + ); + + $response = $this->action + ( + "GET", + "OAuth2SummitEventsApiController@getEvents", + $params, + array(), + array(), + array(), + $headers + ); + + $content = $response->getContent(); + $this->assertResponseStatus(200); + + $events = json_decode($content); + $this->assertTrue(!is_null($events)); + } + + public function testGetAllPresentations(){ + $params = array + ( + 'id' => self::$summit->getId(), + 'page' => 1, + 'per_page' => 5, + 'order' => 'random' + ); + + $headers = array + ( + "HTTP_Authorization" => " Bearer " . $this->access_token, + "CONTENT_TYPE" => "application/json" + ); + + $response = $this->action + ( + "GET", + "OAuth2SummitEventsApiController@getAllPresentations", + $params, + array(), + array(), + array(), + $headers + ); + + $content = $response->getContent(); + $this->assertResponseStatus(200); + + $events = json_decode($content); + $this->assertTrue(!is_null($events)); + } + + public function testGetAllVoteablePresentations(){ + $params = array + ( + 'id' => self::$summit->getId(), + 'page' => 1, + 'per_page' => 5, + 'filter' => ['published==1'], + 'order' => 'random' + ); + + $headers = array + ( + "HTTP_Authorization" => " Bearer " . $this->access_token, + "CONTENT_TYPE" => "application/json" + ); + + $response = $this->action + ( + "GET", + "OAuth2SummitEventsApiController@getAllVoteablePresentations", + $params, + array(), + array(), + array(), + $headers + ); + + $content = $response->getContent(); + $this->assertResponseStatus(200); + + $events = json_decode($content); + $this->assertTrue(!is_null($events)); + } + public function testGetAllScheduledEventsUsingOrder() { $params = array ( - 'id' => 7, + 'id' => self::$summit->getId(), 'page' => 1, 'per_page' => 5, - 'filter' => '', - 'order' => '+title' + 'order' => 'random' ); $headers = array @@ -1110,13 +1371,9 @@ public function testGetAllScheduledEvents() $params = array ( - 'id' => 7, + 'id' => self::$summit->getId(), 'page' => 1, 'per_page' => 10, - 'filter' => array - ( - 'title=@Lightning', - ), ); $headers = array @@ -1161,7 +1418,6 @@ public function testCurrentSummitEventsByEventTypeExpandLocation($summit_id = 7) "CONTENT_TYPE" => "application/json" ); - $response = $this->action ( "GET", diff --git a/tests/OAuth2TrackGroupsApiTest.php b/tests/OAuth2TrackGroupsApiTest.php index f8863a5f0..983993ae0 100644 --- a/tests/OAuth2TrackGroupsApiTest.php +++ b/tests/OAuth2TrackGroupsApiTest.php @@ -11,18 +11,27 @@ * See the License for the specific language governing permissions and * limitations under the License. **/ - final class OAuth2TrackGroupsApiTest extends ProtectedApiTest { - /** - * @param int $summit_id - * @return mixed - */ - public function testGetTrackGroups($summit_id = 23) + use InsertSummitTestData; + + protected function setUp(): void + { + parent::setUp(); + self::insertTestData(); + } + + protected function tearDown(): void + { + self::clearTestData(); + parent::tearDown(); + } + + public function testGetTrackGroups() { $params = [ - 'id' => $summit_id, + 'id' => self::$summit->getId(), 'expand' => 'tracks', ]; @@ -44,15 +53,11 @@ public function testGetTrackGroups($summit_id = 23) return $track_groups; } - /** - * @param int $summit_id - * @return mixed - */ - public function testGetTrackGroupsMetadata($summit_id = 23) + public function testGetTrackGroupsMetadata() { $params = [ - 'id' => $summit_id, + 'id' => self::$summit->getId(), ]; $headers = ["HTTP_Authorization" => " Bearer " . $this->access_token]; @@ -73,25 +78,15 @@ public function testGetTrackGroupsMetadata($summit_id = 23) return $metadata; } - - /** - * @param int $summit_id - */ - public function testGetTrackGroupById($summit_id = 24){ - $track_groups_response = $this->testGetTrackGroups($summit_id); + public function testGetTrackGroupById(){ + $track_groups_response = $this->testGetTrackGroups(); $track_groups = $track_groups_response->data; - $track_group = null; - foreach($track_groups as $track_group_aux){ - if($track_group_aux->class_name == \models\summit\PrivatePresentationCategoryGroup::ClassName){ - $track_group = $track_group_aux; - break; - } - } + $track_group = $track_groups[0]; $params = [ - 'id' => $summit_id, + 'id' => self::$summit->getId(), 'track_group_id' => $track_group->id, 'expand' => 'tracks,allowed_groups', ]; @@ -143,20 +138,22 @@ public function testGetTrackGroupsPrivate($summit_id = 23) $this->assertResponseStatus(200); } - /** - * @param int $summit_id - * @return mixed - */ - public function testAddTrackGroup($summit_id = 24){ + public function testAddTrackGroup(){ $params = [ - 'id' => $summit_id, + 'id' => self::$summit->getId(), ]; + $start_date = clone(self::$summit->getBeginDate()); + $end_date = clone($start_date); + $end_date = $end_date->add(new \DateInterval("P1D")); $name = str_random(16).'_track_group'; $data = [ 'name' => $name, 'description' => 'test desc track group', - 'class_name' => \models\summit\PrivatePresentationCategoryGroup::ClassName + 'class_name' => \models\summit\PresentationCategoryGroup::ClassName, + "begin_attendee_voting_period_date" => $start_date->getTimestamp(), + "end_attendee_voting_period_date" => $end_date->getTimestamp(), + "max_attendee_votes" => 10, ]; $headers = [ @@ -179,25 +176,28 @@ public function testAddTrackGroup($summit_id = 24){ $this->assertResponseStatus(201); $track_group = json_decode($content); $this->assertTrue(!is_null($track_group)); + $this->assertTrue($track_group->max_attendee_votes == 10); + $this->assertTrue($track_group->begin_attendee_voting_period_date == $start_date->getTimestamp()); + $this->assertTrue($track_group->end_attendee_voting_period_date == $end_date->getTimestamp()); return $track_group; } - /** - * @param int $summit_id - * @return mixed - */ - public function testUpdateTrackGroup($summit_id = 24){ + public function testUpdateTrackGroup(){ - $track_group = $this->testAddTrackGroup($summit_id); + $track_groups_response = $this->testGetTrackGroups(); + + $track_groups = $track_groups_response->data; + + $track_group = $track_groups[0]; $params = [ - 'id' => $summit_id, + 'id' => self::$summit->getId(), 'track_group_id' => $track_group->id ]; $data = [ 'description' => 'test desc track group update', - 'class_name' => \models\summit\PrivatePresentationCategoryGroup::ClassName + 'class_name' => \models\summit\PresentationCategoryGroup::ClassName ]; $headers = [ @@ -256,14 +256,18 @@ public function testAssociateTrack2TrackGroup412($summit_id = 24){ } - public function testAssociateTrack2TrackGroup($summit_id = 24){ + public function testAssociateTrack2TrackGroup(){ - $track_group = $this->testAddTrackGroup($summit_id); + $track_groups_response = $this->testGetTrackGroups(); + + $track_groups = $track_groups_response->data; + + $track_group = $track_groups[0]; $params = [ - 'id' => $summit_id, + 'id' => self::$summit->getId(), 'track_group_id' => $track_group->id, - 'track_id' => 211 + 'track_id' => self::$defaultTrack->getId() ]; $headers = [ @@ -286,15 +290,12 @@ public function testAssociateTrack2TrackGroup($summit_id = 24){ $this->assertResponseStatus(201); } - /** - * @param int $summit_id - * @param int $track_group_id - */ - public function testDeleteExistentTrackGroup($summit_id = 24, $track_group_id = 85){ + + public function testDeleteExistentTrackGroup(){ $params = [ - 'id' => $summit_id, - 'track_group_id' => $track_group_id + 'id' => self::$summit->getId(), + 'track_group_id' => self::$defaultTrackGroup->getId() ]; $headers = [