openstackid-resources/app/Services/Model/Imp/SummitService.php

3185 lines
128 KiB
PHP

<?php namespace services\model;
/**
* Copyright 2015 OpenStack Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
use App\Events\MyFavoritesAdd;
use App\Events\MyFavoritesRemove;
use App\Events\MyScheduleAdd;
use App\Events\MyScheduleRemove;
use App\Events\RSVPCreated;
use App\Events\RSVPUpdated;
use App\Events\SummitDeleted;
use App\Events\SummitUpdated;
use App\Facades\ResourceServerContext;
use App\Http\Utils\IFileUploader;
use App\Jobs\Emails\PresentationSubmissions\ImportEventSpeakerEmail;
use App\Jobs\Emails\Schedule\ShareEventEmail;
use App\Jobs\ProcessEventDataImport;
use App\Models\Foundation\Summit\Factories\PresentationFactory;
use App\Models\Foundation\Summit\Factories\SummitEventFeedbackFactory;
use App\Models\Foundation\Summit\Factories\SummitFactory;
use App\Models\Foundation\Summit\Factories\SummitRSVPFactory;
use App\Models\Foundation\Summit\Repositories\IDefaultSummitEventTypeRepository;
use App\Models\Utils\IntervalParser;
use App\Permissions\IPermissionsManager;
use App\Services\Model\AbstractService;
use App\Services\Model\IFolderService;
use App\Services\Model\IMemberService;
use CalDAVClient\Facade\Utils\ICalTimeZoneBuilder;
use DateInterval;
use DateTime;
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use Exception;
use GuzzleHttp\Exception\ClientException;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use League\Csv\Reader;
use libs\utils\ITransactionService;
use models\exceptions\EntityNotFoundException;
use models\exceptions\ValidationException;
use Models\foundation\summit\EntityEvents\EntityEventTypeFactory;
use Models\foundation\summit\EntityEvents\SummitEntityEventProcessContext;
use models\main\File;
use models\main\ICompanyRepository;
use models\main\IGroupRepository;
use models\main\IMemberRepository;
use models\main\ITagRepository;
use models\main\Member;
use models\main\PersonalCalendarShareInfo;
use models\main\Tag;
use models\oauth2\IResourceServerContext;
use models\summit\CalendarSync\WorkQueue\AbstractCalendarSyncWorkRequest;
use models\summit\CalendarSync\WorkQueue\MemberEventScheduleSummitActionSyncWorkRequest;
use models\summit\ConfirmationExternalOrderRequest;
use models\summit\IAbstractCalendarSyncWorkRequestRepository;
use models\summit\IRSVPRepository;
use models\summit\ISpeakerRepository;
use models\summit\ISummitAttendeeRepository;
use models\summit\ISummitAttendeeTicketRepository;
use models\summit\ISummitEntityEventRepository;
use models\summit\ISummitEventRepository;
use models\summit\ISummitRepository;
use models\summit\Presentation;
use models\summit\PresentationSpeaker;
use models\summit\PresentationType;
use models\summit\RSVP;
use models\summit\Summit;
use models\summit\SummitAttendee;
use models\summit\SummitAttendeeTicket;
use models\summit\SummitBookableVenueRoomAttributeType;
use models\summit\SummitBookableVenueRoomAttributeValue;
use models\summit\SummitEvent;
use models\summit\SummitEventFactory;
use models\summit\SummitEventFeedback;
use models\summit\SummitEventType;
use models\summit\SummitEventWithFile;
use models\summit\SummitGeoLocatedLocation;
use models\summit\SummitGroupEvent;
use models\summit\SummitScheduleEmptySpot;
use services\apis\IEventbriteAPI;
use utils\Filter;
use utils\FilterElement;
use utils\FilterParser;
use utils\Order;
use utils\OrderElement;
use utils\PagingInfo;
/**
* Class SummitService
* @package services\model
*/
final class SummitService extends AbstractService implements ISummitService
{
/**
* minimun number of minutes that an event must last
*/
const MIN_EVENT_MINUTES = 1;
/**
* @var ISummitEventRepository
*/
private $event_repository;
/**
* @var IEventbriteAPI
*/
private $eventbrite_api;
/**
* @var ISpeakerRepository
*/
private $speaker_repository;
/**
* @var ISummitEntityEventRepository
*/
private $entity_events_repository;
/**
* @var ISummitAttendeeTicketRepository
*/
private $ticket_repository;
/**
* @var IMemberRepository
*/
private $member_repository;
/**
* @var ISummitAttendeeRepository
*/
private $attendee_repository;
/**
* @var ITagRepository
*/
private $tag_repository;
/**
* @var IRSVPRepository
*/
private $rsvp_repository;
/**
* @var IAbstractCalendarSyncWorkRequestRepository
*/
private $calendar_sync_work_request_repository;
/**
* @var IFolderService
*/
private $folder_service;
/**
* @var ICompanyRepository
*/
private $company_repository;
/**
* @var IGroupRepository
*/
private $group_repository;
/**
* @var ISummitRepository
*/
private $summit_repository;
/**
* @var IDefaultSummitEventTypeRepository
*/
private $default_event_types_repository;
/**
* @var IPermissionsManager
*/
private $permissions_manager;
/**
* @var IFileUploader
*/
private $file_uploader;
/**
* @var IResourceServerContext
*/
private $resource_server_context;
/**
* @var ISpeakerService ISpeakerService
*/
private $speaker_service;
/**
* @var IMemberService
*/
private $member_service;
/**
* SummitService constructor.
* @param ISummitRepository $summit_repository
* @param ISummitEventRepository $event_repository
* @param ISpeakerRepository $speaker_repository
* @param ISummitEntityEventRepository $entity_events_repository
* @param ISummitAttendeeTicketRepository $ticket_repository
* @param ISummitAttendeeRepository $attendee_repository
* @param IMemberRepository $member_repository
* @param ITagRepository $tag_repository
* @param IRSVPRepository $rsvp_repository
* @param IAbstractCalendarSyncWorkRequestRepository $calendar_sync_work_request_repository
* @param IEventbriteAPI $eventbrite_api
* @param IFolderService $folder_service
* @param ICompanyRepository $company_repository
* @param IGroupRepository $group_repository
* @param IDefaultSummitEventTypeRepository $default_event_types_repository
* @param IPermissionsManager $permissions_manager
* @param IFileUploader $file_uploader
* @param IResourceServerContext $resource_server_context
* @param ISpeakerService $speaker_service
* @param IMemberService $member_service
* @param ITransactionService $tx_service
*/
public function __construct
(
ISummitRepository $summit_repository,
ISummitEventRepository $event_repository,
ISpeakerRepository $speaker_repository,
ISummitEntityEventRepository $entity_events_repository,
ISummitAttendeeTicketRepository $ticket_repository,
ISummitAttendeeRepository $attendee_repository,
IMemberRepository $member_repository,
ITagRepository $tag_repository,
IRSVPRepository $rsvp_repository,
IAbstractCalendarSyncWorkRequestRepository $calendar_sync_work_request_repository,
IEventbriteAPI $eventbrite_api,
IFolderService $folder_service,
ICompanyRepository $company_repository,
IGroupRepository $group_repository,
IDefaultSummitEventTypeRepository $default_event_types_repository,
IPermissionsManager $permissions_manager,
IFileUploader $file_uploader,
IResourceServerContext $resource_server_context,
ISpeakerService $speaker_service,
IMemberService $member_service,
ITransactionService $tx_service
)
{
parent::__construct($tx_service);
$this->summit_repository = $summit_repository;
$this->event_repository = $event_repository;
$this->speaker_repository = $speaker_repository;
$this->entity_events_repository = $entity_events_repository;
$this->ticket_repository = $ticket_repository;
$this->member_repository = $member_repository;
$this->attendee_repository = $attendee_repository;
$this->tag_repository = $tag_repository;
$this->rsvp_repository = $rsvp_repository;
$this->calendar_sync_work_request_repository = $calendar_sync_work_request_repository;
$this->eventbrite_api = $eventbrite_api;
$this->folder_service = $folder_service;
$this->company_repository = $company_repository;
$this->group_repository = $group_repository;
$this->default_event_types_repository = $default_event_types_repository;
$this->permissions_manager = $permissions_manager;
$this->file_uploader = $file_uploader;
$this->resource_server_context = $resource_server_context;
$this->speaker_service = $speaker_service;
$this->member_service = $member_service;
}
/**
* @param Summit $summit
* @param Member $member
* @param int $event_id
* @param bool $check_rsvp
* @return void
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function addEventToMemberSchedule(Summit $summit, Member $member, $event_id, $check_rsvp = true)
{
try {
$this->tx_service->transaction(function () use ($summit, $member, $event_id, $check_rsvp) {
$event = $summit->getScheduleEvent($event_id);
if (is_null($event)) {
throw new EntityNotFoundException('event not found on summit!');
}
if (!Summit::allowToSee($event, $member))
throw new EntityNotFoundException('event not found on summit!');
if ($check_rsvp && $event->hasRSVP() && !$event->isExternalRSVP())
throw new ValidationException("event has rsvp set on it!");
$member->add2Schedule($event);
if ($member->hasSyncInfoFor($summit)) {
Log::info(sprintf("synching externally event id %s", $event_id));
$sync_info = $member->getSyncInfoBy($summit);
$request = new MemberEventScheduleSummitActionSyncWorkRequest();
$request->setType(AbstractCalendarSyncWorkRequest::TypeAdd);
$request->setSummitEvent($event);
$request->setOwner($member);
$request->setCalendarSyncInfo($sync_info);
$this->calendar_sync_work_request_repository->add($request);
}
});
Event::fire(new MyScheduleAdd($member, $summit, $event_id));
} catch (UniqueConstraintViolationException $ex) {
throw new ValidationException
(
sprintf('Event %s already belongs to member %s schedule.', $event_id, $member->getId())
);
}
}
/**
* @param Summit $summit
* @param Member $member
* @param int $event_id
* @param boolean $check_rsvp
* @return void
* @throws \Exception
*/
public function removeEventFromMemberSchedule(Summit $summit, Member $member, $event_id, $check_rsvp = true)
{
$this->tx_service->transaction(function () use ($summit, $member, $event_id, $check_rsvp) {
$event = $summit->getScheduleEvent($event_id);
if (is_null($event))
throw new EntityNotFoundException('event not found on summit!');
if ($check_rsvp && $event->hasRSVP() && !$event->isExternalRSVP())
throw new ValidationException("event has rsvp set on it!");
$member->removeFromSchedule($event);
if ($member->hasSyncInfoFor($summit)) {
Log::info(sprintf("unsynching externally event id %s", $event_id));
$sync_info = $member->getSyncInfoBy($summit);
$request = new MemberEventScheduleSummitActionSyncWorkRequest();
$request->setType(AbstractCalendarSyncWorkRequest::TypeRemove);
$request->setSummitEvent($event);
$request->setOwner($member);
$request->setCalendarSyncInfo($sync_info);
$this->calendar_sync_work_request_repository->add($request);
}
});
Event::fire(new MyScheduleRemove($member, $summit, $event_id));
}
/**
* @param Summit $summit
* @param Member $member
* @param int $event_id
* @throws ValidationException
* @throws EntityNotFoundException
*/
public function addEventToMemberFavorites(Summit $summit, Member $member, $event_id)
{
try {
$this->tx_service->transaction(function () use ($summit, $member, $event_id) {
$event = $summit->getScheduleEvent($event_id);
if (is_null($event)) {
throw new EntityNotFoundException('event not found on summit!');
}
if (!Summit::allowToSee($event, $member))
throw new EntityNotFoundException('event not found on summit!');
$member->addFavoriteSummitEvent($event);
});
Event::fire(new MyFavoritesAdd($member, $summit, $event_id));
} catch (UniqueConstraintViolationException $ex) {
throw new ValidationException
(
sprintf('Event %s already belongs to member %s favorites.', $event_id, $member->getId())
);
}
}
/**
* @param Summit $summit
* @param Member $member
* @param int $event_id
* @throws EntityNotFoundException
*/
public function removeEventFromMemberFavorites(Summit $summit, Member $member, $event_id)
{
$this->tx_service->transaction(function () use ($summit, $member, $event_id) {
$event = $summit->getScheduleEvent($event_id);
if (is_null($event))
throw new EntityNotFoundException('event not found on summit!');
$member->removeFavoriteSummitEvent($event);
});
Event::fire(new MyFavoritesRemove($member, $summit, $event_id));
}
/**
* @param Member $member
* @param Summit $summit
* @param int $event_id
* @param array $payload
* @return SummitEventFeedback
* @throws Exception
*/
public function addMyEventFeedback(Member $member, Summit $summit, int $event_id, array $payload): SummitEventFeedback
{
return $this->tx_service->transaction(function () use ($member, $summit, $event_id, $payload) {
$event = $summit->getScheduleEvent($event_id);
if (is_null($event))
throw new EntityNotFoundException("Event not found.");
if (!Summit::allowToSee($event, $member))
throw new EntityNotFoundException("Event not found.");
if (!$event->isAllowFeedback())
throw new ValidationException(sprintf("Event id %s does not allow feedback.", $event->getIdentifier()));
// check older feedback
$former_feedback = $member->getFeedbackByEvent($event);
if (!is_null($former_feedback))
throw new ValidationException(sprintf("You already sent feedback for event id %s!.", $event->getIdentifier()));
$newFeedback = SummitEventFeedbackFactory::build($payload);
$newFeedback->setOwner($member);
$event->addFeedBack($newFeedback);
return $newFeedback;
});
}
/**
* @param Member $member
* @param Summit $summit
* @param int $event_id
* @param array $payload
* @return SummitEventFeedback
* @throws Exception
*/
public function updateMyEventFeedback(Member $member, Summit $summit, int $event_id, array $payload): SummitEventFeedback
{
return $this->tx_service->transaction(function () use ($member, $summit, $event_id, $payload) {
$event = $summit->getScheduleEvent($event_id);
if (is_null($event))
throw new EntityNotFoundException("Event not found.");
if (!Summit::allowToSee($event, $member))
throw new EntityNotFoundException("Event not found.");
if (!$event->isAllowFeedback())
throw new ValidationException(sprintf("Event id %s does not allow feedback.", $event->getIdentifier()));
// check older feedback
$feedback = $member->getFeedbackByEvent($event);
if (is_null($feedback))
throw new ValidationException(sprintf("you dont have feedback for event id %s!.", $event->getIdentifier()));
return SummitEventFeedbackFactory::populate($feedback, $payload);
});
}
/**
* @param Member $member
* @param Summit $summit
* @param int $event_id
* @return SummitEventFeedback
* @throws Exception
*/
public function getMyEventFeedback(Member $member, Summit $summit, int $event_id): SummitEventFeedback
{
return $this->tx_service->transaction(function () use ($member, $summit, $event_id) {
$event = $summit->getScheduleEvent($event_id);
if (is_null($event))
throw new EntityNotFoundException("Event not found.");
if (!Summit::allowToSee($event, $member))
throw new EntityNotFoundException("Event not found.");
if (!$event->isAllowFeedback())
throw new ValidationException(sprintf("Event id %s does not allow feedback.", $event->getIdentifier()));
// check older feedback
$feedback = $member->getFeedbackByEvent($event);
if (is_null($feedback))
throw new ValidationException(sprintf("you dont have feedback for event id %s!.", $event->getIdentifier()));
return $feedback;
});
}
/**
* @param Member $member
* @param Summit $summit
* @param int $event_id
* @throws Exception
*/
public function deleteMyEventFeedback(Member $member, Summit $summit, int $event_id): void
{
$this->tx_service->transaction(function () use ($member, $summit, $event_id) {
$event = $summit->getScheduleEvent($event_id);
if (is_null($event))
throw new EntityNotFoundException("Event not found.");
if (!Summit::allowToSee($event, $member))
throw new EntityNotFoundException("Event not found.");
if (!$event->isAllowFeedback())
throw new ValidationException(sprintf("Event id %s does not allow feedback.", $event->getIdentifier()));
// check older feedback
$feedback = $member->getFeedbackByEvent($event);
$member->removeFeedback($feedback);
});
}
/**
* @param Summit $summit
* @param null|int $member_id
* @param null|\DateTime $from_date
* @param null|int $from_id
* @param int $limit
* @return array
*/
public function getSummitEntityEvents(Summit $summit, $member_id = null, DateTime $from_date = null, $from_id = null, $limit = 25)
{
return $this->tx_service->transaction(function () use ($summit, $member_id, $from_date, $from_id, $limit) {
$global_last_id = $this->entity_events_repository->getLastEntityEventId($summit);
$from_id = !is_null($from_id) ? intval($from_id) : null;
$member = !is_null($member_id) && $member_id > 0 ? $this->member_repository->getById($member_id) : null;
$ctx = new SummitEntityEventProcessContext($member);
do {
$last_event_id = 0;
$last_event_date = 0;
// if we got a from id and its greater than the last one, then break
if (!is_null($from_id) && $global_last_id <= $from_id) break;
$events = $this->entity_events_repository->getEntityEvents
(
$summit,
$member_id,
$from_id,
$from_date,
$limit
);
foreach ($events as $e) {
if ($ctx->getListSize() === $limit) break;
$last_event_id = $e->getId();
$last_event_date = $e->getCreated();
try {
$entity_event_type_processor = EntityEventTypeFactory::getInstance()->build($e, $ctx);
$entity_event_type_processor->process();
} catch (\InvalidArgumentException $ex1) {
Log::info($ex1);
} catch (\Exception $ex) {
Log::error($ex);
}
}
// reset if we do not get any data so far, to get next batch
$from_id = $last_event_id;
$from_date = null;
//post process for summit events , we should send only te last one
$ctx->postProcessList();
// we do not have any any to process
if ($last_event_id == 0 || $global_last_id <= $last_event_id) break;
} while ($ctx->getListSize() < $limit);
return array($last_event_id, $last_event_date, $ctx->getListValues());
});
}
/**
* @param Summit $summit
* @param array $data
* @return SummitEvent
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function addEvent(Summit $summit, array $data)
{
return $this->saveOrUpdateEvent($summit, $data, null);
}
/**
* @param Summit $summit
* @param int $event_id
* @param array $data
* @return SummitEvent
*/
public function updateEvent(Summit $summit, $event_id, array $data)
{
return $this->saveOrUpdateEvent($summit, $data, $event_id);
}
/**
* @param array $data
* @param Summit $summit
* @param SummitEvent $event
* @return SummitEvent
* @throws ValidationException
*/
private function updateEventDates(array $data, Summit $summit, SummitEvent $event)
{
if (isset($data['start_date']) && isset($data['end_date'])) {
$event->setSummit($summit);
$start_datetime = intval($data['start_date']);
$start_datetime = new \DateTime("@$start_datetime");
$start_datetime->setTimezone($summit->getTimeZone());
$end_datetime = intval($data['end_date']);
$end_datetime = new \DateTime("@$end_datetime");
$end_datetime->setTimezone($summit->getTimeZone());
$interval_seconds = $end_datetime->getTimestamp() - $start_datetime->getTimestamp();
$minutes = $interval_seconds / 60;
if ($minutes < self::MIN_EVENT_MINUTES)
throw new ValidationException
(
sprintf
(
"event should last at least %s minutes - current duration %s",
self::MIN_EVENT_MINUTES,
$minutes
)
);
// set local time from UTC
$event->setStartDate($start_datetime);
$event->setEndDate($end_datetime);
}
return $event;
}
/**
* @param SummitEventType $old_event_type
* @param SummitEventType $event_type
* @return bool
*/
private function canPerformEventTypeTransition(SummitEventType $old_event_type, SummitEventType $event_type)
{
if ($old_event_type->getId() == $event_type->getId()) return true;
// cant upgrade from raw event to presentation and vice versa
if ($old_event_type->getClassName() != $event_type->getClassName()) {
return false;
}
$old_is_private = $old_event_type->isPrivate();
$new_is_private = $event_type->isPrivate();
if ((!$old_is_private && $new_is_private) || ($old_is_private && !$new_is_private))
return false;
$old_allow_attach = $old_event_type->isAllowsAttachment();
$new_allow_attach = $event_type->isAllowsAttachment();
if ((!$old_allow_attach && $new_allow_attach) || ($old_allow_attach && !$new_allow_attach))
return false;
return true;
}
/**
* @param Summit $summit
* @param array $data
* @param null|int $event_id
* @param Member|null $current_member
* @return SummitEvent
* @throws EntityNotFoundException
* @throws ValidationException
* @throws Exception
*/
private function saveOrUpdateEvent(Summit $summit, array $data, $event_id = null)
{
return $this->tx_service->transaction(function () use ($summit, $data, $event_id) {
$current_member = ResourceServerContext::getCurrentUser(false);
if (!is_null($current_member) && !$this->permissions_manager->canEditFields($current_member, 'SummitEvent', $data)) {
throw new ValidationException(sprintf("user %s cant set requested summit event fields", $current_member->getEmail()));
}
$event_type = null;
if (isset($data['type_id'])) {
$event_type = $summit->getEventType(intval($data['type_id']));
if (is_null($event_type)) {
throw new EntityNotFoundException(sprintf("event type id %s does not exists!", $data['type_id']));
}
}
$track = null;
if (isset($data['track_id'])) {
$track = $summit->getPresentationCategory(intval($data['track_id']));
if (is_null($track)) {
throw new EntityNotFoundException(sprintf("track id %s does not exists!", $data['track_id']));
}
}
$location = null;
if (isset($data['location_id'])) {
$location = $summit->getLocation(intval($data['location_id']));
if (is_null($location) && intval($data['location_id']) > 0) {
throw new EntityNotFoundException(sprintf("location id %s does not exists!", $data['location_id']));
}
}
$event = null;
// existing event
if (!is_null($event_id) && intval($event_id) > 0) {
$event = $this->event_repository->getById($event_id);
if (is_null($event))
throw new ValidationException(sprintf("event id %s does not exists!", $event_id));
$old_event_type = $event->getType();
// check event type transition ...
if (!is_null($event_type) && !$this->canPerformEventTypeTransition($old_event_type, $event_type)) {
throw new ValidationException
(
sprintf
(
"invalid event type transition for event id %s ( from %s to %s)",
$event_id,
$old_event_type->getType(),
$event_type->getType()
)
);
}
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!');
}
// new event
if (is_null($event)) {
$event = SummitEventFactory::build($event_type, $summit, $data);
$event->setCreatedBy($current_member);
}
else{
$event->setSummit($summit);
if (!is_null($event_type))
$event->setType($event_type);
SummitEventFactory::populate($event, $data);
}
$event->setUpdatedBy($current_member);
if (isset($data['rsvp_template_id'])) {
$rsvp_template = $summit->getRSVPTemplateById(intval($data['rsvp_template_id']));
if (is_null($rsvp_template))
throw new EntityNotFoundException(sprintf('rsvp template id %s does not belongs to summit id %s', $data['rsvp_template_id'], $summit->getId()));
if (!$rsvp_template->isEnabled())
throw new ValidationException(sprintf('rsvp template id %s is not enabled', $data['rsvp_template_id']));
$event->setRSVPTemplate($rsvp_template);
$event->setRSVPMaxUserNumber(intval($data['rsvp_max_user_number']));
$event->setRSVPMaxUserWaitListNumber(intval($data['rsvp_max_user_wait_list_number']));
}
if (!is_null($track)) {
$event->setCategory($track);
}
if (!is_null($location))
$event->setLocation($location);
if (is_null($location) && isset($data['location_id'])) {
// clear location
$event->clearLocation();
}
$this->updateEventDates($data, $summit, $event);
if (isset($data['tags'])) {
$event->clearTags();
foreach ($data['tags'] as $str_tag) {
$tag = $this->tag_repository->getByTag($str_tag);
if ($tag == null) $tag = new Tag($str_tag);
$event->addTag($tag);
}
}
// sponsors
$sponsors = ($event_type->isUseSponsors() && isset($data['sponsors'])) ?
$data['sponsors'] : [];
if ($event_type->isAreSponsorsMandatory() && count($sponsors) == 0) {
throw new ValidationException('sponsors are mandatory!');
}
if (isset($data['sponsors'])) {
$event->clearSponsors();
foreach ($sponsors as $sponsor_id) {
$sponsor = $this->company_repository->getById(intval($sponsor_id));
if (is_null($sponsor)) throw new EntityNotFoundException(sprintf('sponsor id %s', $sponsor_id));
$event->addSponsor($sponsor);
}
}
$this->saveOrUpdatePresentationData($event, $event_type, $data);
$this->saveOrUpdateSummitGroupEventData($event, $event_type, $data);
if ($event->isPublished()) {
$this->validateBlackOutTimesAndTimes($event);
$event->unPublish();
$event->publish();
}
$this->event_repository->add($event);
return $event;
});
}
private function saveOrUpdateSummitGroupEventData(SummitEvent $event, SummitEventType $event_type, array $data)
{
if (!$event instanceof SummitGroupEvent) return;
if (!isset($data['groups']) || count($data['groups']) == 0)
throw new ValidationException('groups is required');
$event->clearGroups();
foreach ($data['groups'] as $group_id) {
$group = $this->group_repository->getById(intval($group_id));
if (is_null($group)) throw new EntityNotFoundException(sprintf('group id %s', $group_id));
$event->addGroup($group);
}
}
/**
* @param SummitEvent $event
* @param SummitEventType $event_type
* @param array $data
* @throws EntityNotFoundException
* @throws ValidationException
*/
private function saveOrUpdatePresentationData(SummitEvent $event, SummitEventType $event_type, array $data)
{
if (!$event instanceof Presentation) return;
// if we are creating the presentation from admin, then
// we should mark it as received and complete
$event->setStatus(Presentation::STATUS_RECEIVED);
$event->setProgress(Presentation::PHASE_COMPLETE);
// speakers
if ($event_type instanceof PresentationType && $event_type->isUseSpeakers()) {
$shouldClearSpeakers = isset($data['speakers']) && count($data['speakers']) == 0;
$speakers = $data['speakers'] ?? [];
if ($event_type->isAreSpeakersMandatory() && count($speakers) == 0) {
throw new ValidationException('Speakers are mandatory.');
}
if($shouldClearSpeakers){
$event->clearSpeakers();
}
if (count($speakers) > 0 && $event instanceof Presentation) {
$event->clearSpeakers();
foreach ($speakers as $speaker_id) {
$speaker = $this->speaker_repository->getById(intval($speaker_id));
if (is_null($speaker) || !$speaker instanceof PresentationSpeaker)
throw new EntityNotFoundException(sprintf('Speaker id %s.', $speaker_id));
$event->addSpeaker($speaker);
}
}
}
// moderator
if ($event_type instanceof PresentationType && $event_type->isUseModerator()) {
$moderator_id = isset($data['moderator_speaker_id']) ? intval($data['moderator_speaker_id']) : 0;
if ($event_type->isModeratorMandatory() && $moderator_id == 0) {
throw new ValidationException('moderator_speaker_id is mandatory.');
}
if ($moderator_id > 0) {
$speaker_id = intval($data['moderator_speaker_id']);
if ($speaker_id === 0) $event->unsetModerator();
else {
$moderator = $this->speaker_repository->getById($speaker_id);
if (is_null($moderator) || !$moderator instanceof PresentationSpeaker)
throw new EntityNotFoundException(sprintf('speaker id %s', $speaker_id));
$event->setModerator($moderator);
}
}
}
// selection plan
if (isset($data['selection_plan_id'])) {
$selection_plan_id = intval($data['selection_plan_id']);
$selection_plan = $event->getSummit()->getSelectionPlanById($selection_plan_id);
if (!is_null($selection_plan)) {
$track = $event->getCategory();
if (!$selection_plan->hasTrack($track)) {
throw new ValidationException
(
sprintf
(
"Track %s (%s) does not belongs to Selection Plan %s (%s).",
$track->getTitle(),
$track->getId(),
$selection_plan->getName(),
$selection_plan->getId()
)
);
}
$event->setSelectionPlan($selection_plan);
}
}
PresentationFactory::populate($event, $data, true);
}
/**
* @param Summit $summit
* @param int $event_id
* @param array $data
* @return SummitEvent
*/
public function publishEvent(Summit $summit, $event_id, array $data)
{
return $this->tx_service->transaction(function () use ($summit, $data, $event_id) {
$event = $this->event_repository->getById($event_id);
if (is_null($event) || !$event instanceof SummitEvent)
throw new EntityNotFoundException(sprintf("event id %s does not exists!", $event_id));
if (is_null($event->getType()))
throw new EntityNotFoundException(sprintf("event type its not assigned to event id %s!", $event_id));
if (is_null($event->getSummit()))
throw new EntityNotFoundException(sprintf("summit its not assigned to event id %s!", $event_id));
if ($event->getSummit()->getIdentifier() !== $summit->getIdentifier())
throw new ValidationException(sprintf("event %s does not belongs to summit id %s", $event_id, $summit->getIdentifier()));
$this->updateEventDates($data, $summit, $event);
$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($end_datetime))
throw new ValidationException(sprintf("end_date its not assigned to event id %s!", $event_id));
if (isset($data['location_id'])) {
$location_id = intval($data['location_id']);
$event->clearLocation();
if ($location_id > 0) {
$location = $summit->getLocation($location_id);
if (is_null($location))
throw new EntityNotFoundException(sprintf("location id %s does not exists!", $data['location_id']));
$event->setLocation($location);
}
}
$this->validateBlackOutTimesAndTimes($event);
$event->unPublish();
$event->publish();
$event->setUpdatedBy(ResourceServerContext::getCurrentUser(false));
$this->event_repository->add($event);
return $event;
});
}
private function validateBlackOutTimesAndTimes(SummitEvent $event)
{
$current_event_location = $event->getLocation();
// validate blackout times
$conflict_events = $this->event_repository->getPublishedOnSameTimeFrame($event);
if (!is_null($conflict_events)) {
foreach ($conflict_events as $c_event) {
// if the published event is BlackoutTime or if there is a BlackoutTime event in this timeframe
if ((!is_null($current_event_location) && !$current_event_location->isOverrideBlackouts()) && ($event->getType()->isBlackoutTimes() || $c_event->getType()->isBlackoutTimes()) && $event->getId() != $c_event->getId()) {
throw new ValidationException
(
sprintf
(
"You can't publish on this time frame, it conflicts with event id %s",
$c_event->getId()
)
);
}
// if trying to publish an event on a slot occupied by another event
if (!is_null($current_event_location) && !is_null($c_event->getLocation()) && $current_event_location->getId() == $c_event->getLocation()->getId() && $event->getId() != $c_event->getId()) {
throw new ValidationException
(
sprintf
(
"You can't publish on this time frame, it conflicts with event id %s",
$c_event->getId()
)
);
}
// check speakers collisions
if ($event instanceof Presentation && $c_event instanceof Presentation && $event->getId() != $c_event->getId()) {
foreach ($event->getSpeakers() as $current_speaker) {
foreach ($c_event->getSpeakers() as $c_speaker) {
if (intval($c_speaker->getId()) === intval($current_speaker->getId())) {
throw new ValidationException
(
sprintf
(
"You can't publish Event %s (%s) on this timeframe, speaker %s its presention in room %s at this time.",
$event->getTitle(),
$event->getId(),
$current_speaker->getFullName(),
$c_event->getLocationName()
)
);
}
}
}
}
}
}
}
/**
* @param Summit $summit
* @param int $event_id
* @return mixed
*/
public function unPublishEvent(Summit $summit, $event_id)
{
return $this->tx_service->transaction(function () use ($summit, $event_id) {
$event = $this->event_repository->getById($event_id);
if (is_null($event))
throw new EntityNotFoundException(sprintf("event id %s does not exists!", $event_id));
if ($event->getSummit()->getIdentifier() !== $summit->getIdentifier())
throw new ValidationException(sprintf("event %s does not belongs to summit id %s", $event_id, $summit->getIdentifier()));
$event->unPublish();
$event->setUpdatedBy(ResourceServerContext::getCurrentUser(false));
return $event;
});
}
/**
* @param Summit $summit
* @param int $event_id
* @return mixed
*/
public function deleteEvent(Summit $summit, $event_id)
{
return $this->tx_service->transaction(function () use ($summit, $event_id) {
$event = $this->event_repository->getById($event_id);
if (is_null($event))
throw new EntityNotFoundException(sprintf("event id %s does not exists!", $event_id));
if ($event->getSummit()->getIdentifier() !== $summit->getIdentifier())
throw new ValidationException(sprintf("event %s does not belongs to summit id %s", $event_id, $summit->getIdentifier()));
$this->event_repository->delete($event);
return true;
});
}
/**
* @param Summit $summit
* @param $external_order_id
* @return array
* @throws ValidationException
* @throws EntityNotFoundException
* @throws Exception
*/
public function getExternalOrder(Summit $summit, $external_order_id)
{
try {
$external_order = $this->eventbrite_api->getOrder($external_order_id);
if (isset($external_order['attendees'])) {
$status = $external_order['status'];
$summit_external_id = $external_order['event_id'];
if (intval($summit->getSummitExternalId()) !== intval($summit_external_id))
throw new ValidationException('order %s does not belongs to current summit!', $external_order_id);
if ($status !== 'placed')
throw new ValidationException(sprintf('invalid order status %s for order %s', $status, $external_order_id));
$attendees = array();
foreach ($external_order['attendees'] as $a) {
$ticket_external_id = intval($a['ticket_class_id']);
$ticket_type = $summit->getTicketTypeByExternalId($ticket_external_id);
$external_attendee_id = $a['id'];
if (is_null($ticket_type))
throw new EntityNotFoundException(sprintf('external ticket type %s not found!', $ticket_external_id));
$old_ticket = $this->ticket_repository->getByExternalOrderIdAndExternalAttendeeId
(
trim($external_order_id), $external_attendee_id
);
if (!is_null($old_ticket)) continue;
$attendees[] = [
'external_id' => intval($a['id']),
'first_name' => $a['profile']['first_name'],
'last_name' => $a['profile']['last_name'],
'email' => $a['profile']['email'],
'company' => isset($a['profile']['company']) ? $a['profile']['company'] : null,
'job_title' => isset($a['profile']['job_title']) ? $a['profile']['job_title'] : null,
'status' => $a['status'],
'ticket_type' => [
'id' => intval($ticket_type->getId()),
'name' => $ticket_type->getName(),
'external_id' => $ticket_external_id,
]
];
}
if (count($attendees) === 0)
throw new ValidationException(sprintf('order %s already redeem!', $external_order_id));
return array('id' => intval($external_order_id), 'attendees' => $attendees);
}
} catch (ClientException $ex1) {
if ($ex1->getCode() === 400)
throw new EntityNotFoundException('external order does not exists!');
if ($ex1->getCode() === 403)
throw new EntityNotFoundException('external order does not exists!');
throw $ex1;
} catch (Exception $ex) {
throw $ex;
}
}
/**
* @param ConfirmationExternalOrderRequest $request
* @return SummitAttendee
*/
public function confirmExternalOrderAttendee(ConfirmationExternalOrderRequest $request)
{
return $this->tx_service->transaction(function () use ($request) {
try {
$external_order = $this->eventbrite_api->getOrder($request->getExternalOrderId());
if (isset($external_order['attendees'])) {
$summit_external_id = $external_order['event_id'];
if (intval($request->getSummit()->getSummitExternalId()) !== intval($summit_external_id))
throw new ValidationException('order %s does not belongs to current summit!', $request->getExternalOrderId());
$external_attendee = null;
foreach ($external_order['attendees'] as $a) {
if (intval($a['id']) === intval($request->getExternalAttendeeId())) {
$external_attendee = $a;
break;
}
}
if (is_null($external_attendee))
throw new EntityNotFoundException(sprintf('attendee %s not found!', $request->getExternalAttendeeId()));
$ticket_external_id = intval($external_attendee['ticket_class_id']);
$ticket_type = $request->getSummit()->getTicketTypeByExternalId($ticket_external_id);
if (is_null($ticket_type))
throw new EntityNotFoundException(sprintf('ticket type %s not found!', $ticket_external_id));;
$status = $external_order['status'];
if ($status !== 'placed')
throw new ValidationException(sprintf('invalid order status %s for order %s', $status, $request->getExternalOrderId()));
$old_attendee = $request->getSummit()->getAttendeeByMemberId($request->getMemberId());
if (!is_null($old_attendee))
throw new ValidationException
(
'attendee already exists for current summit!'
);
$old_ticket = $this->ticket_repository->getByExternalOrderIdAndExternalAttendeeId(
$request->getExternalOrderId(),
$request->getExternalAttendeeId()
);
if (!is_null($old_ticket))
throw new ValidationException
(
sprintf
(
'order %s already redeem for attendee id %s !',
$request->getExternalOrderId(),
$request->getExternalAttendeeId()
)
);
$ticket = new SummitAttendeeTicket;
$ticket->setExternalOrderId($request->getExternalOrderId());
$ticket->setExternalAttendeeId($request->getExternalAttendeeId());
$ticket->setBoughtDate(new DateTime($external_attendee['created']));
$ticket->setChangedDate(new DateTime($external_attendee['changed']));
$ticket->setTicketType($ticket_type);
$attendee = new SummitAttendee;
$attendee->setMember($this->member_repository->getById($request->getMemberId()));
$attendee->setSummit($request->getSummit());
$attendee->addTicket($ticket);
$attendee->updateStatus();
$this->attendee_repository->add($attendee);
return $attendee;
}
} catch (ClientException $ex1) {
if ($ex1->getCode() === 400)
throw new EntityNotFoundException('external order does not exists!');
if ($ex1->getCode() === 403)
throw new EntityNotFoundException('external order does not exists!');
throw $ex1;
} catch (Exception $ex) {
throw $ex;
}
});
}
/**
* @param Summit $summit
* @param int $event_id
* @param UploadedFile $file
* @param int $max_file_size
* @return File
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function addEventAttachment(Summit $summit, $event_id, UploadedFile $file, $max_file_size = 10485760)
{
return $this->tx_service->transaction(function () use ($summit, $event_id, $file, $max_file_size) {
$allowed_extensions = ['png', 'jpg', 'jpeg', 'gif', 'pdf'];
$event = $summit->getEvent($event_id);
if (is_null($event)) {
throw new EntityNotFoundException('event not found on summit!');
}
if (!$event instanceof SummitEventWithFile) {
throw new ValidationException(sprintf("event id %s does not allow attachments!", $event_id));
}
if (!in_array($file->extension(), $allowed_extensions)) {
throw new ValidationException("file does not has a valid extension ('png','jpg','jpeg','gif','pdf').");
}
if ($file->getSize() > $max_file_size) {
throw new ValidationException(sprintf("file exceeds max_file_size (%s MB).", ($max_file_size / 1024) / 1024));
}
$attachment = $this->file_uploader->build($file, 'summit-event-attachments', true);
$event->setAttachment($attachment);
return $attachment;
});
}
/**
* @param Summit $summit
* @param Filter $filter
* @return SummitScheduleEmptySpot[]
*/
public function getSummitScheduleEmptySpots
(
Summit $summit,
Filter $filter
)
{
return $this->tx_service->transaction(function () use (
$summit,
$filter
) {
$gaps = [];
$order = new Order([
OrderElement::buildAscFor("location_id"),
OrderElement::buildAscFor("start_date"),
]);
// parse locations ids
if (!$filter->hasFilter('location_id'))
throw new ValidationException("missing required filter location_id");
$location_ids = $filter->getFilterCollectionByField('location_id');
// parse start_date filter
$start_datetime_filter = $filter->getFilter('start_date');
if (is_null($start_datetime_filter))
throw new ValidationException("missing required filter start_date");
$start_datetime_unix = intval($start_datetime_filter[0]->getValue());
$start_datetime = new \DateTime("@$start_datetime_unix");
// parse end_date filter
$end_datetime_filter = $filter->getFilter('end_date');
if (is_null($end_datetime_filter))
throw new ValidationException("missing required filter end_date");
$end_datetime_unix = intval($end_datetime_filter[0]->getValue());
$end_datetime = new \DateTime("@$end_datetime_unix");
// gap size filter
$gap_size_filter = $filter->getFilter('gap');
if (is_null($end_datetime_filter))
throw new ValidationException("missing required filter gap");
$gap_size = $gap_size_filter[0];
$summit_time_zone = $summit->getTimeZone();
$start_datetime->setTimezone($summit_time_zone);
$end_datetime->setTimezone($summit_time_zone);
$intervals = IntervalParser::getInterval($start_datetime, $end_datetime);
foreach ($location_ids as $location_id) {
foreach ($intervals as $interval) {
$events_filter = new Filter();
$events_filter->addFilterCondition(FilterParser::buildFilter('published', '==', '1'));
$events_filter->addFilterCondition(FilterParser::buildFilter('summit_id', '==', $summit->getId()));
$events_filter->addFilterCondition(FilterParser::buildFilter('location_id', '==', intval($location_id)));
$events_filter->addFilterCondition(FilterParser::buildFilter('start_date', '<', $interval[1]->getTimestamp()));
$events_filter->addFilterCondition(FilterParser::buildFilter('end_date', '>', $interval[0]->getTimestamp()));
$paging_response = $this->event_repository->getAllByPage
(
new PagingInfo(1, PHP_INT_MAX),
$events_filter,
$order
);
$gap_start_date = $interval[0];
$gap_end_date = clone $gap_start_date;
// check published items
foreach ($paging_response->getItems() as $event) {
while
(
(
$gap_end_date->getTimestamp() + (self::MIN_EVENT_MINUTES * 60)
)
<= $event->getLocalStartDate()->getTimestamp()
) {
$max_gap_end_date = clone $gap_end_date;
$max_gap_end_date->setTime(23, 59, 59);
if ($gap_end_date->getTimestamp() + (self::MIN_EVENT_MINUTES * 60) > $max_gap_end_date->getTimestamp()) break;
$gap_end_date->add(new DateInterval('PT' . self::MIN_EVENT_MINUTES . 'M'));
}
if ($gap_start_date->getTimestamp() == $gap_end_date->getTimestamp()) {
// no gap!
$gap_start_date = $event->getLocalEndDate();
$gap_end_date = clone $gap_start_date;
continue;
}
// check min gap ...
if (self::checkGapCriteria($gap_size, $gap_end_date->diff($gap_start_date)))
$gaps[] = new SummitScheduleEmptySpot($location_id, $gap_start_date, $gap_end_date);
$gap_start_date = $event->getLocalEndDate();
$gap_end_date = clone $gap_start_date;
}
// check last possible gap ( from last $gap_start_date till $interval[1]
if ($gap_start_date < $interval[1]) {
// last possible gap
if (self::checkGapCriteria($gap_size, $interval[1]->diff($gap_start_date)))
$gaps[] = new SummitScheduleEmptySpot($location_id, $gap_start_date, $interval[1]);
}
}
}
return $gaps;
});
}
/**
* @param FilterElement $gap_size_criteria
* @param DateInterval $interval
* @return bool
*/
private static function checkGapCriteria
(
FilterElement $gap_size_criteria,
DateInterval $interval
)
{
$total_minutes = $interval->days * 24 * 60;
$total_minutes += $interval->h * 60;
$total_minutes += $interval->i;
switch ($gap_size_criteria->getOperator()) {
case '=':
{
return intval($gap_size_criteria->getValue()) == $total_minutes;
}
break;
case '<':
{
return $total_minutes < intval($gap_size_criteria->getValue());
}
break;
case '>':
{
return $total_minutes > intval($gap_size_criteria->getValue());
}
break;
case '<=':
{
return $total_minutes <= intval($gap_size_criteria->getValue());
}
break;
case '>=':
{
return $total_minutes >= intval($gap_size_criteria->getValue());
}
break;
}
return false;
}
/**
* @param Summit $summit
* @param array $data
* @return bool
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function unPublishEvents(Summit $summit, array $data)
{
return $this->tx_service->transaction(function () use (
$summit,
$data
) {
foreach ($data['events'] as $event_id) {
$this->unPublishEvent($summit, intval($event_id));
}
return true;
});
}
/**
* @param Summit $summit
* @param array $data
* @return bool
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function updateAndPublishEvents(Summit $summit, array $data)
{
return $this->tx_service->transaction(function () use (
$summit,
$data
) {
foreach ($data['events'] as $event_data) {
$this->updateEvent($summit, intval($event_data['id']), $event_data);
$this->publishEvent($summit, intval($event_data['id']), $event_data);
}
return true;
});
}
/**
* @param Summit $summit
* @param array $data
* @return bool
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function updateEvents(Summit $summit, array $data)
{
return $this->tx_service->transaction(function () use (
$summit,
$data
) {
foreach ($data['events'] as $event_data) {
$this->updateEvent($summit, intval($event_data['id']), $event_data);
}
return true;
});
}
/**
* @param array $data
* @return Summit
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function addSummit(array $data)
{
return $this->tx_service->transaction(function () use ($data) {
$name = trim($data['name']);
$former_summit = $this->summit_repository->getByName($name);
if (!is_null($former_summit)) {
throw new ValidationException
(
trans
(
'validation_errors.SummitService.AddSummit.NameAlreadyExists',
['name' => $name]
)
);
}
$slug = $data['slug'] ?? null;
if (!empty($slug)) {
// check if exist another summit with that slug
$old_summit = $this->summit_repository->getBySlug(trim($slug));
if (!is_null($old_summit)) {
throw new ValidationException(sprintf("slug %s already belongs to another summit", $slug));
}
}
$summit = SummitFactory::build($data);
// seed default event types
foreach ($this->default_event_types_repository->getAll() as $default_event_type) {
$summit->addEventType($default_event_type->buildType($summit));
}
$summit->seedDefaultEmailFlowEvents();
$summit->seedDefaultAccessLevelTypes();
$this->summit_repository->add($summit);
return $summit;
});
}
/**
* @param int $summit_id
* @param array $data
* @return Summit
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function updateSummit($summit_id, array $data)
{
return $this->tx_service->transaction(function () use ($summit_id, $data) {
if (isset($data['name'])) {
$former_summit = $this->summit_repository->getByName(trim($data['name']));
if (!is_null($former_summit) && $former_summit->getId() != $summit_id) {
throw new ValidationException
(
trans
(
'validation_errors.SummitService.updateSummit.NameAlreadyExists',
['name' => $data['name']]
)
);
}
}
if (isset($data['active'])) {
$active = boolval($data['active']);
$active_summit = $this->summit_repository->getActive();
if ($active && !is_null($active_summit) && $active_summit->getId() != $summit_id) {
throw new ValidationException
(
trans
(
'validation_errors.SummitService.updateSummit.SummitAlreadyActive',
['active_summit_id' => $active_summit->getId()]
)
);
}
}
$slug = $data['slug'] ?? null;
if (!empty($slug)) {
// check if exist another summit with that slug
$old_summit = $this->summit_repository->getBySlug(trim($slug));
if (!is_null($old_summit) && $summit_id != $old_summit->getId()) {
throw new ValidationException(sprintf("slug %s already belongs to another summit", $slug));
}
}
$summit = $this->summit_repository->getById($summit_id);
if (is_null($summit)) {
throw new EntityNotFoundException
(
trans
(
'not_found_errors.SummitService.updateSummit.SummitNotFound',
['summit_id' => $summit_id]
)
);
}
$summit = SummitFactory::populate($summit, $data);
Event::fire(new SummitUpdated($summit_id));
return $summit;
});
}
/**
* @param int $summit_id
* @return void
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function deleteSummit($summit_id)
{
return $this->tx_service->transaction(function () use ($summit_id) {
$summit = $this->summit_repository->getById($summit_id);
if (is_null($summit)) {
throw new EntityNotFoundException
(
trans
(
'not_found_errors.SummitService.deleteSummit.SummitNotFound',
['summit_id' => $summit_id]
)
);
}
$summit->markAsDeleted();
$this->summit_repository->delete($summit);
Event::fire(new SummitDeleted($summit_id));
});
}
/**
* @param int $current_member_id
* @param int $speaker_id
* @param int $presentation_id
* @return void
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function addSpeaker2Presentation($current_member_id, $speaker_id, $presentation_id)
{
return $this->tx_service->transaction(function () use ($current_member_id, $speaker_id, $presentation_id) {
$current_member = $this->member_repository->getById($current_member_id);
if (is_null($current_member))
throw new EntityNotFoundException(sprintf("member %s not found", $current_member_id));
$current_speaker = $this->speaker_repository->getByMember($current_member);
if (is_null($current_speaker))
throw new EntityNotFoundException(sprintf("member %s does not has a speaker profile", $current_member_id));
$presentation = $this->event_repository->getById($presentation_id);
if (is_null($presentation))
throw new EntityNotFoundException(sprintf("presentation %s not found", $presentation_id));
if (!$presentation instanceof Presentation)
throw new EntityNotFoundException(sprintf("presentation %s not found", $presentation_id));
if (!$presentation->canEdit($current_speaker))
throw new ValidationException(sprintf("member %s can not edit presentation %s",
$current_member_id,
$presentation_id
));
$speaker = $this->speaker_repository->getById(intval($speaker_id));
if (is_null($speaker))
throw new EntityNotFoundException(sprintf('speaker %s not found', $speaker_id));
if (!$presentation->isCompleted())
$presentation->setProgress(Presentation::PHASE_SPEAKERS);
$presentation->addSpeaker($speaker);
});
}
/**
* @param int $current_member_id
* @param int $speaker_id
* @param int $presentation_id
* @return void
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function removeSpeakerFromPresentation($current_member_id, $speaker_id, $presentation_id)
{
return $this->tx_service->transaction(function () use ($current_member_id, $speaker_id, $presentation_id) {
$current_member = $this->member_repository->getById($current_member_id);
if (is_null($current_member))
throw new EntityNotFoundException(sprintf("member %s not found", $current_member_id));
$current_speaker = $this->speaker_repository->getByMember($current_member);
if (is_null($current_speaker))
throw new EntityNotFoundException(sprintf("member %s does not has a speaker profile", $current_member_id));
$presentation = $this->event_repository->getById($presentation_id);
if (is_null($presentation))
throw new EntityNotFoundException(sprintf("presentation %s not found", $presentation_id));
if (!$presentation instanceof Presentation)
throw new EntityNotFoundException(sprintf("presentation %s not found", $presentation_id));
if (!$presentation->canEdit($current_speaker))
throw new ValidationException(sprintf("member %s can not edit presentation %s",
$current_member_id,
$presentation_id
));
$speaker = $this->speaker_repository->getById(intval($speaker_id));
if (is_null($speaker))
throw new EntityNotFoundException(sprintf('speaker %s not found', $speaker_id));
if (!$presentation->isCompleted())
$presentation->setProgress(Presentation::PHASE_SPEAKERS);
$presentation->removeSpeaker($speaker);
});
}
/**
* @param int $current_member_id
* @param int $speaker_id
* @param int $presentation_id
* @return void
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function addModerator2Presentation($current_member_id, $speaker_id, $presentation_id)
{
return $this->tx_service->transaction(function () use ($current_member_id, $speaker_id, $presentation_id) {
$current_member = $this->member_repository->getById($current_member_id);
if (is_null($current_member))
throw new EntityNotFoundException(sprintf("member %s not found", $current_member_id));
$current_speaker = $this->speaker_repository->getByMember($current_member);
if (is_null($current_speaker))
throw new EntityNotFoundException(sprintf("member %s does not has a speaker profile", $current_member_id));
$presentation = $this->event_repository->getById($presentation_id);
if (is_null($presentation))
throw new EntityNotFoundException(sprintf("presentation %s not found", $presentation_id));
if (!$presentation instanceof Presentation)
throw new EntityNotFoundException(sprintf("presentation %s not found", $presentation_id));
if (!$presentation->canEdit($current_speaker))
throw new ValidationException(sprintf("member %s can not edit presentation %s",
$current_member_id,
$presentation_id
));
$speaker = $this->speaker_repository->getById(intval($speaker_id));
if (is_null($speaker))
throw new EntityNotFoundException(sprintf('speaker %s not found', $speaker_id));
if (!$presentation->isCompleted())
$presentation->setProgress(Presentation::PHASE_SPEAKERS);
$presentation->setModerator($speaker);
});
}
/**
* @param int $current_member_id
* @param int $speaker_id
* @param int $presentation_id
* @return void
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function removeModeratorFromPresentation($current_member_id, $speaker_id, $presentation_id)
{
return $this->tx_service->transaction(function () use ($current_member_id, $speaker_id, $presentation_id) {
$current_member = $this->member_repository->getById($current_member_id);
if (is_null($current_member))
throw new EntityNotFoundException(sprintf("member %s not found", $current_member_id));
$current_speaker = $this->speaker_repository->getByMember($current_member);
if (is_null($current_speaker))
throw new EntityNotFoundException(sprintf("member %s does not has a speaker profile", $current_member_id));
$presentation = $this->event_repository->getById($presentation_id);
if (is_null($presentation))
throw new EntityNotFoundException(sprintf("presentation %s not found", $presentation_id));
if (!$presentation instanceof Presentation)
throw new EntityNotFoundException(sprintf("presentation %s not found", $presentation_id));
if (!$presentation->canEdit($current_speaker))
throw new ValidationException(sprintf("member %s can not edit presentation %s",
$current_member_id,
$presentation_id
));
$speaker = $this->speaker_repository->getById(intval($speaker_id));
if (is_null($speaker))
throw new EntityNotFoundException(sprintf('speaker %s not found', $speaker_id));
if (!$presentation->isCompleted())
$presentation->setProgress(Presentation::PHASE_SPEAKERS);
$presentation->unsetModerator();
});
}
/**
* @param Summit $summit
* @param int $event_id
* @return SummitEvent
* @throws ValidationException
* @throws EntityNotFoundException
*/
public function cloneEvent(Summit $summit, int $event_id): SummitEvent
{
return $this->tx_service->transaction(function () use ($summit, $event_id) {
$event = $this->event_repository->getById($event_id);
if (is_null($event))
throw new EntityNotFoundException(sprintf("event %s not found!", $event_id));
if ($event instanceof Presentation)
throw new ValidationException(sprintf("event %s is not allowed to be cloned!", $event_id));
$eventClone = SummitEventFactory::build($event->getType(), $summit);
$eventClone->setTitle($event->getTitle());
$eventClone->setAbstract($event->getAbstract());
$eventClone->setLocation($event->getLocation());
$eventClone->setAllowFeedBack($event->getAllowFeedback());
$eventClone->setSocialSummary($event->getSocialSummary());
$eventClone->setStartDate($event->getLocalStartDate());
$eventClone->setEndDate($event->getLocalEndDate());
$eventClone->setCategory($event->getCategory());
$eventClone->setEtherpadLink($event->getEtherpadLink());
$eventClone->setStreamingUrl($event->getStreamingUrl());
$eventClone->setCreatedBy(ResourceServerContext::getCurrentUser(false));
$eventClone->setUpdatedBy(ResourceServerContext::getCurrentUser(false));
if ($event->hasRSVPTemplate()) {
$eventClone->setRSVPTemplate($event->getRSVPTemplate());
}
if ($event->isExternalRSVP()) {
$eventClone->setRSVPLink($event->getRSVPLink());
}
foreach ($event->getSponsors() as $sponsor) {
$eventClone->addSponsor($sponsor);
}
foreach ($event->getTags() as $tag) {
$eventClone->addTag($tag);
}
// check if SummitEventWithFile
if ($event instanceof SummitEventWithFile && $event->hasAttachment()) {
$eventClone->setAttachment($event->getAttachment());
}
$this->event_repository->add($eventClone);
return $eventClone;
});
}
/**
* @param Summit $summit
* @param array $payload
* @return SummitBookableVenueRoomAttributeType
* @throws ValidationException
* @throws EntityNotFoundException
*/
public function addBookableRoomAttribute(Summit $summit, array $payload): SummitBookableVenueRoomAttributeType
{
return $this->tx_service->transaction(function () use ($summit, $payload) {
$type_name = trim($payload['type']);
$former_type = $summit->getBookableAttributeTypeByTypeName($type_name);
if (!is_null($former_type))
throw new ValidationException(sprintf("bookable room attr type %s already exists on summit %s", $type_name, $summit->getId()));
$type = new SummitBookableVenueRoomAttributeType();
$type->setType($type_name);
$summit->addMeetingBookingRoomAllowedAttribute($type);
return $type;
});
}
/**
* @param Summit $summit
* @param int $type_id
* @param array $payload
* @return SummitBookableVenueRoomAttributeType
* @throws ValidationException
* @throws EntityNotFoundException
*/
public function updateBookableRoomAttribute(Summit $summit, int $type_id, array $payload): SummitBookableVenueRoomAttributeType
{
return $this->tx_service->transaction(function () use ($summit, $type_id, $payload) {
$type = $summit->getBookableAttributeTypeById($type_id);
if (is_null($type))
throw new EntityNotFoundException();
$type_name = trim($payload['type']);
$former_type = $summit->getBookableAttributeTypeByTypeName($type_name);
if (!is_null($former_type) && $type_id != $former_type->getId())
throw new ValidationException(sprintf("bookable room attr type %s already exists on summit %s", $type_name, $summit->getId()));
$type->setType($type_name);
return $type;
});
}
/**
* @param Summit $summit
* @param int $type_id
* @return void
* @throws ValidationException
* @throws EntityNotFoundException
*/
public function deleteBookableRoomAttribute(Summit $summit, int $type_id): void
{
$this->tx_service->transaction(function () use ($summit, $type_id) {
$type = $summit->getBookableAttributeTypeById($type_id);
if (is_null($type))
throw new EntityNotFoundException();
$summit->removeMeetingBookingRoomAllowedAttribute($type);
});
}
/**
* @param Summit $summit
* @param int $type_id
* @param array $payload
* @return SummitBookableVenueRoomAttributeValue
* @throws ValidationException
* @throws EntityNotFoundException
*/
public function addBookableRoomAttributeValue(Summit $summit, int $type_id, array $payload): SummitBookableVenueRoomAttributeValue
{
return $this->tx_service->transaction(function () use ($summit, $type_id, $payload) {
$type = $summit->getBookableAttributeTypeById($type_id);
if (is_null($type))
throw new EntityNotFoundException();
$value_name = trim($payload['value']);
$former_value = $type->getValueByValue($value_name);
if (!is_null($former_value))
throw new ValidationException(sprintf("bookable room attr value %s already exists on summit %s", $value_name, $summit->getId()));
$value = new SummitBookableVenueRoomAttributeValue();
$value->setValue($value_name);
$type->addValue($value);
return $value;
});
}
/**
* @param Summit $summit
* @param int $type_id
* @param int $value_id
* @param array $payload
* @return SummitBookableVenueRoomAttributeValue
* @throws ValidationException
* @throws EntityNotFoundException
*/
public function updateBookableRoomAttributeValue(Summit $summit, int $type_id, int $value_id, array $payload): SummitBookableVenueRoomAttributeValue
{
return $this->tx_service->transaction(function () use ($summit, $type_id, $value_id, $payload) {
$type = $summit->getBookableAttributeTypeById($type_id);
if (is_null($type))
throw new EntityNotFoundException();
$value = $type->getValueById($value_id);
if (is_null($value))
throw new EntityNotFoundException();
$value_name = trim($payload['value']);
$former_value = $type->getValueByValue($value_name);
if (!is_null($former_value) && $value_id != $former_value->getId())
throw new ValidationException(sprintf("bookable room attr value %s already exists on summit %s", $value_name, $summit->getId()));
$value->setValue($value_name);
return $value;
});
}
/**
* @param Summit $summit
* @param int $type_id
* @param int $value_id
* @return void
* @throws ValidationException
* @throws EntityNotFoundException
*/
public function deleteBookableRoomAttributeValue(Summit $summit, int $type_id, int $value_id): void
{
$this->tx_service->transaction(function () use ($summit, $type_id, $value_id) {
$type = $summit->getBookableAttributeTypeById($type_id);
if (is_null($type))
throw new EntityNotFoundException();
$value = $type->getValueById($value_id);
if (is_null($value))
throw new EntityNotFoundException();
$type->removeValue($value);
});
}
/**
* @param int $summit_id
* @param UploadedFile $file
* @param int $max_file_size
* @return File
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function addSummitLogo(int $summit_id, UploadedFile $file, $max_file_size = 10485760)
{
return $this->tx_service->transaction(function () use ($summit_id, $file, $max_file_size) {
$allowed_extensions = ['png', 'jpg', 'jpeg', 'gif', 'svg', 'jfif'];
$summit = $this->summit_repository->getById($summit_id);
if (is_null($summit) || !$summit instanceof Summit) {
throw new EntityNotFoundException('summit not found!');
}
if (!in_array($file->extension(), $allowed_extensions)) {
throw new ValidationException(sprintf("file does not has a valid extension (%s).", implode(",", $allowed_extensions)));
}
if ($file->getSize() > $max_file_size) {
throw new ValidationException(sprintf("file exceeds max_file_size (%s MB).", ($max_file_size / 1024) / 1024));
}
$photo = $this->file_uploader->build($file, sprintf('summits/%s', $summit->getId()), true);
$summit->setLogo($photo);
return $photo;
});
}
/**
* @param int $summit_id
* @throws ValidationException
* @throws EntityNotFoundException
*/
public function deleteSummitLogo(int $summit_id): void
{
$this->tx_service->transaction(function () use ($summit_id) {
$summit = $this->summit_repository->getById($summit_id);
if (is_null($summit) || !$summit instanceof Summit) {
throw new EntityNotFoundException('summit not found!');
}
$summit->clearLogo();
});
}
/**
* @param Summit $summit
* @param Member $member
* @param int $event_id
* @param array $data
* @return RSVP
* @throws Exception
*/
public function addRSVP(Summit $summit, Member $member, int $event_id, array $data): RSVP
{
$rsvp = $this->tx_service->transaction(function () use ($summit, $member, $event_id, $data) {
$event = $this->event_repository->getByIdExclusiveLock($event_id);
if (is_null($event) || !$event instanceof SummitEvent) {
throw new EntityNotFoundException('Event not found on summit.');
}
if ($event->getSummitId() != $summit->getId()) {
throw new EntityNotFoundException('Event not found on summit.');
}
if (!Summit::allowToSee($event, $member))
throw new EntityNotFoundException('Event not found on summit.');
if (!$event->hasRSVPTemplate()) {
throw new EntityNotFoundException('Event not found on summit.');
}
// add to schedule the RSVP event
if (!$member->isOnSchedule($event)) {
$this->addEventToMemberSchedule($summit, $member, $event_id, false);
}
$old_rsvp = $member->getRsvpByEvent($event_id);
if (!is_null($old_rsvp))
throw new ValidationException
(
sprintf
(
"Member %s already submitted an rsvp for event %s on summit %s.",
$member->getId(),
$event_id,
$summit->getId()
)
);
// create RSVP
return SummitRSVPFactory::build($event, $member, $data);
});
Event::fire(new RSVPCreated($rsvp));
return $rsvp;
}
/**
* @param Summit $summit
* @param Member $member
* @param int $event_id
* @param array $data
* @return RSVP
* @throws Exception
*/
public function updateRSVP(Summit $summit, Member $member, int $event_id, array $data): RSVP
{
return $this->tx_service->transaction(function () use ($summit, $member, $event_id, $data) {
$event = $this->event_repository->getByIdExclusiveLock($event_id);
if (is_null($event) || !$event instanceof SummitEvent) {
throw new EntityNotFoundException('Event not found on summit.');
}
if ($event->getSummitId() != $summit->getId()) {
throw new EntityNotFoundException('Event not found on summit.');
}
if (!Summit::allowToSee($event, $member))
throw new EntityNotFoundException('Event not found on summit.');
if (!$event->hasRSVPTemplate()) {
throw new EntityNotFoundException('Event not found on summit.');
}
// add to schedule the RSVP event
if (!$member->isOnSchedule($event)) {
throw new EntityNotFoundException('Event not found on summit.');
}
$rsvp = $member->getRsvpByEvent($event->getId());
if (is_null($rsvp))
throw new ValidationException
(
sprintf
(
"Member %s did not submitted an rsvp for event %s on summit %s.",
$member->getId(),
$event_id,
$summit->getId()
)
);
// update RSVP
$rsvp = SummitRSVPFactory::populate($rsvp, $event, $member, $data);
Event::fire(new RSVPUpdated($rsvp));
return $rsvp;
});
}
/**
* @param Summit $summit
* @param Member $member
* @param int $event_id
* @return bool|mixed
* @throws Exception
*/
public function unRSVPEvent(Summit $summit, Member $member, int $event_id)
{
return $this->tx_service->transaction(function () use ($summit, $member, $event_id) {
$event = $this->event_repository->getByIdExclusiveLock($event_id);
if (is_null($event) || !$event instanceof SummitEvent) {
throw new EntityNotFoundException('Event not found on summit.');
}
if ($event->getSummitId() != $summit->getId()) {
throw new EntityNotFoundException('Event not found on summit.');
}
if (!Summit::allowToSee($event, $member))
throw new EntityNotFoundException('Event not found on summit.');
$rsvp = $member->getRsvpByEvent($event_id);
if (is_null($rsvp))
throw new ValidationException(sprintf("RSVP for event id %s does not exist for your member.", $event_id));
$this->rsvp_repository->delete($rsvp);
$this->removeEventFromMemberSchedule($summit, $member, $event_id, false);
return true;
});
}
/**
* @param Summit $summit
* @param Member $member
* @return PersonalCalendarShareInfo|null
* @throws Exception
*/
public function createScheduleShareableLink(Summit $summit, Member $member): ?PersonalCalendarShareInfo
{
return $this->tx_service->transaction(function () use ($summit, $member) {
return $member->createScheduleShareableLink($summit);
});
}
/**
* @param Summit $summit
* @param Member $member
* @return PersonalCalendarShareInfo|null
* @throws Exception
*/
public function revokeScheduleShareableLink(Summit $summit, Member $member): ?PersonalCalendarShareInfo
{
return $this->tx_service->transaction(function () use ($summit, $member) {
$link = $member->getScheduleShareableLinkBy($summit);
if (is_null($link)) {
throw new EntityNotFoundException("Schedule shareable link not found for member.");
}
$link->revoke();
return $link;
});
}
/**
* @param Summit $summit
* @param string $cid
* @return string
* @throws Exception
*/
public function buildICSFeed(Summit $summit, string $cid): string
{
return $this->tx_service->transaction(function () use ($summit, $cid) {
$link = $summit->getScheduleShareableLinkById($cid);
if (is_null($link)) {
throw new EntityNotFoundException("Schedule shareable link not found for member.");
}
$owner = $link->getOwner();
$timeZone = $summit->getTimeZone();
$vCalendar = ICalTimeZoneBuilder::build($timeZone, $summit->getName(), true);
foreach ($owner->getScheduleBySummit($summit) as $scheduled) {
$summitEvent = $scheduled->getEvent();
$local_start_time = new DateTime($summitEvent->getStartDateNice(), $timeZone);
$local_end_time = new DateTime($summitEvent->getEndDateNice(), $timeZone);
$vEvent = new \Eluceo\iCal\Component\Event($summitEvent->getId());
$vEvent
->setCreated(new DateTime())
->setDtStart($local_start_time)
->setDtEnd($local_end_time)
->setNoTime(false)
->setSummary($summitEvent->getTitle())
->setDescription(strip_tags($summitEvent->getAbstract()))
->setDescriptionHTML($summitEvent->getAbstract());
if ($timeZone->getName() == 'UTC') {
$vEvent->setUseUtc(true)
->setUseTimezone(false);
} else {
$vEvent->setUseUtc(false)
->setUseTimezone(true);
}
if ($summitEvent->hasLocation()) {
$location = $summitEvent;
$geo = null;
if ($location instanceof SummitGeoLocatedLocation) {
$geo = sprintf("%s;%s", $location->getLat(), $location->getLng());
}
$vEvent->setLocation($location->getTitle(), $location->getTitle(), $geo);
}
$vCalendar->addComponent($vEvent);
}
return $vCalendar->render();
});
}
/**
* @param Summit $summit
* @param int $event_id
* @param array $data
* @return void`
* @throws EntityNotFoundException
* @throws ValidationException
*/
public function shareEventByEmail(Summit $summit, int $event_id, array $data): void
{
$this->tx_service->transaction(function () use ($summit, $event_id, $data) {
$event = $summit->getScheduleEvent($event_id);
if (is_null($event)) {
throw new EntityNotFoundException(sprintf("Event %s not found.", $event_id));
}
$event_uri = $data['event_uri'] ?? null;
if (empty($event_uri)) {
Log::debug("event_uri not set on payload. trying to get from default one (summit)");
$default_event_uri = $summit->getScheduleDefaultEventDetailUrl();
if (!empty($default_event_uri)) {
Log::debug("default_event_uri set at summit level using it.");
$event_uri = str_replace(":event_id", $event_id, $default_event_uri);
}
}
if (empty($event_uri)) {
throw new ValidationException(sprintf("Property event_url is empty."));
}
ShareEventEmail::dispatch(
trim($data['from']),
trim($data['to']),
$event_uri,
$event
);
});
}
public function calculateFeedbackAverageForOngoingSummits(): void
{
$ongoing_summits = $this->tx_service->transaction(function () {
return $this->summit_repository->getOnGoing();
});
foreach ($ongoing_summits as $summit) {
Log::debug(sprintf("SummitService::calculateFeedbackAverageForOngoingSummits processing summit %s", $summit->getId()));
$event_ids = $this->tx_service->transaction(function () use ($summit) {
return $summit->getScheduleEventsIds();
});
foreach ($event_ids as $event_id) {
$event_id = $event_id['id'];
$this->tx_service->transaction(function () use ($event_id) {
try {
Log::debug(sprintf("SummitService::calculateFeedbackAverageForOngoingSummits processing event %s", $event_id));
$event = $this->event_repository->getById($event_id);
if (is_null($event) || !$event instanceof SummitEvent) {
Log::debug(sprintf("SummitService::calculateFeedbackAverageForOngoingSummits event %s not found", $event_id));
return;
}
$rate_sum = 0;
$rate_count = 0;
foreach ($event->getFeedback() as $feedback) {
$rate_count++;
$rate_sum = $rate_sum + $feedback->getRate();
}
$avg_rate = ($rate_count > 0) ? ($rate_sum / $rate_count) : 0;
$avg_rate = round($avg_rate, 2);
$old_avg_rate = $event->getAvgFeedbackRate();
Log::debug(sprintf("SummitService::calculateFeedbackAverageForOngoingSummits new avg rate %s - old avg rate %s - for event id %s", $avg_rate, $old_avg_rate, $event->getId()));
$event->setAvgFeedbackRate($avg_rate);
} catch (Exception $ex) {
Log::error($ex);
}
});
}
}
}
/**
* @inheritDoc
*/
public function addEventImage(Summit $summit, $event_id, UploadedFile $file, $max_file_size = 10485760)
{
return $this->tx_service->transaction(function () use ($summit, $event_id, $file, $max_file_size) {
$allowed_extensions = ['png', 'jpg', 'jpeg', 'gif', 'pdf'];
$event = $summit->getEvent($event_id);
if (is_null($event) || !$event instanceof SummitEvent) {
throw new EntityNotFoundException('event not found on summit!');
}
if (!in_array($file->extension(), $allowed_extensions)) {
throw new ValidationException("file does not has a valid extension ('png','jpg','jpeg','gif','pdf').");
}
if ($file->getSize() > $max_file_size) {
throw new ValidationException(sprintf("file exceeds max_file_size (%s MB).", ($max_file_size / 1024) / 1024));
}
$file = $this->file_uploader->build($file, 'summit-event-images', true);
$event->setImage($file);
return $file;
});
}
/**
* @inheritDoc
*/
public function removeEventImage(Summit $summit, $event_id): void
{
$this->tx_service->transaction(function () use ($summit, $event_id) {
$event = $summit->getEvent($event_id);
if (is_null($event) || !$event instanceof SummitEvent) {
throw new EntityNotFoundException('event not found on summit!');
}
$event->clearImage();
});
}
/**
* @param int $summit_id
* @param int $days
* @param bool $negative
* @param bool $check_summit_ends
* @throws Exception
*/
public function advanceSummit(int $summit_id, int $days, bool $negative = false, $check_summit_ends = true): void
{
$interval = new DateInterval(sprintf("P%sD", $days));
Log::debug(sprintf("SummitService::advanceSummit summit_id %s days %s negative %s check_summit_ends %s", $summit_id, $days, $negative, $check_summit_ends));
$events_ids = $this->tx_service->transaction(function () use ($summit_id, $interval, $negative, $check_summit_ends) {
$summit = $this->summit_repository->getByIdExclusiveLock($summit_id);
if (is_null($summit) || !$summit instanceof Summit)
throw new EntityNotFoundException("Summit not found");
if ($check_summit_ends && !$summit->isEnded()) {
Log::debug(sprintf("SummitService::advanceSummit summit %s has not ended !.", $summit_id));
return [];
}
// summit period
$summitBeginDate = $summit->getBeginDate();
$summitEndDate = $summit->getEndDate();
if (!is_null($summitBeginDate)) {
Log::debug(sprintf("SummitService::advanceSummit Current Summit begin date for summit %s is %s", $summit_id, $summitBeginDate->format("Ymd His")));
if ($negative) {
$summit->setRawBeginDate(clone $summitBeginDate->sub($interval));
} else {
$summit->setRawBeginDate(clone $summitBeginDate->add($interval));
}
Log::debug(sprintf("SummitService::advanceSummit New Summit begin date for summit %s is %s", $summit_id, $summit->getBeginDate()->format("Ymd His")));
}
if (!is_null($summitEndDate)) {
Log::debug(sprintf("SummitService::advanceSummit Current Summit end date for summit %s is %s", $summit_id, $summitEndDate->format("Ymd His")));
if ($negative) {
$summit->setRawEndDate(clone $summitEndDate->sub($interval));
} else {
$summit->setRawEndDate(clone $summitEndDate->add($interval));
}
Log::debug(sprintf("SummitService::advanceSummit New Summit end date for summit %s is %s", $summit_id, $summit->getEndDate()->format("Ymd His")));
}
// registration period
$summitRegistrationBeginDate = $summit->getRegistrationBeginDate();
$summitRegistrationEndDate = $summit->getRegistrationEndDate();
if (!is_null($summitRegistrationBeginDate)) {
Log::debug(sprintf("SummitService::advanceSummit Current Summit registration begin date for summit %s is %s", $summit_id, $summitRegistrationBeginDate->format("Ymd His")));
if ($negative) {
$summit->setRawRegistrationBeginDate(clone $summitRegistrationBeginDate->add($interval));
} else {
$summit->setRawRegistrationBeginDate(clone $summitRegistrationBeginDate->sub($interval));
}
Log::debug(sprintf("SummitService::advanceSummit New Summit registration begin date for summit %s is %s", $summit_id, $summit->getRegistrationBeginDate()->format("Ymd His")));
}
if (!is_null($summitRegistrationEndDate)) {
Log::debug(sprintf("SummitService::advanceSummit Current Summit registration end date for summit %s is %s", $summit_id, $summitRegistrationEndDate->format("Ymd His")));
$summit->setRawRegistrationEndDate(clone $summitRegistrationEndDate->add($interval));
Log::debug(sprintf("SummitService::advanceSummit New Summit registration end date for summit %s is %s", $summit_id, $summit->getRegistrationEndDate()->format("Ymd His")));
}
// random dates
$summitReassignTicketTillDate = $summit->getReassignTicketTillDate();
if (!is_null($summitReassignTicketTillDate)) {
if ($negative) {
$summit->setRawReassignTicketTillDate(clone $summitReassignTicketTillDate->sub($interval));
} else {
$summit->setRawReassignTicketTillDate(clone $summitReassignTicketTillDate->add($interval));
}
}
$summitStartShowingVenuesDate = $summit->getStartShowingVenuesDate();
if (!is_null($summitStartShowingVenuesDate)) {
if ($negative) {
$summit->setRawStartShowingVenuesDate(clone $summitStartShowingVenuesDate->sub($interval));
} else {
$summit->setRawStartShowingVenuesDate(clone $summitStartShowingVenuesDate->add($interval));
}
}
$summitScheduleDefaultStartDate = $summit->getScheduleDefaultStartDate();
if (!is_null($summitScheduleDefaultStartDate)) {
if ($negative) {
$summit->setRawScheduleDefaultStartDate(clone $summitScheduleDefaultStartDate->sub($interval));
} else {
$summit->setRawScheduleDefaultStartDate(clone $summitScheduleDefaultStartDate->add($interval));
}
}
$summitBeginAllowBookingDate = $summit->getBeginAllowBookingDate();
if (!is_null($summitBeginAllowBookingDate)) {
if ($negative) {
$summit->setRawBeginAllowBookingDate(clone $summitBeginAllowBookingDate->sub($interval));
} else {
$summit->setRawBeginAllowBookingDate(clone $summitBeginAllowBookingDate->add($interval));
}
}
$summitEndAllowBookingDate = $summit->getEndAllowBookingDate();
if (!is_null($summitEndAllowBookingDate)) {
if ($negative) {
$summit->setRawEndAllowBookingDate(clone $summitEndAllowBookingDate->sub($interval));
} else {
$summit->setRawEndAllowBookingDate(clone $summitEndAllowBookingDate->add($interval));
}
}
// schedule
$event_ids = [];
foreach ($summit->getPublishedEvents() as $event) {
if (!$event instanceof SummitEvent) continue;
$event_ids[] = $event->getId();
}
return $event_ids;
});
foreach ($events_ids as $event_id) {
$this->tx_service->transaction(function () use ($summit_id, $event_id, $interval, $negative) {
$event = $this->event_repository->getByIdExclusiveLock($event_id);
$eventBeginDate = $event->getStartDate();
$eventEndDate = $event->getEndDate();
if (is_null($eventBeginDate) || is_null($eventEndDate)) {
Log::debug(sprintf("SummitService::advanceSummit summit id %s event id %s ( has not set dates but is published!), skipping it", $summit_id, $event->getId()));
return;
}
Log::debug(sprintf("SummitService::advanceSummit summit id %s event id %s current start date %s", $summit_id, $event->getId(), $eventBeginDate->format("Ymd His")));
if ($negative) {
$event->setRawStartDate(clone $eventBeginDate->sub($interval));
} else {
$event->setRawStartDate(clone $eventBeginDate->add($interval));
}
Log::debug(sprintf("SummitService::advanceSummit summit id %s event id %s new start date %s", $summit_id, $event->getId(), $event->getStartDate()->format("Ymd His")));
Log::debug(sprintf("SummitService::advanceSummit summit id %s event id %s current end date %s", $summit_id, $event->getId(), $eventEndDate->format("Ymd His")));
if ($negative) {
$event->setRawEndDate(clone $eventEndDate->sub($interval));
} else {
$event->setRawEndDate(clone $eventEndDate->add($interval));
}
Log::debug(sprintf("SummitService::advanceSummit summit id %s event id %s new end date %s", $summit_id, $event->getId(), $event->getEndDate()->format("Ymd His")));
});
}
}
/**
* @param Summit $summit
* @param UploadedFile $csv_file
* @param array $payload
* @throws ValidationException
*/
public function importEventData(Summit $summit, UploadedFile $csv_file, array $payload): void
{
Log::debug(sprintf("SummitService::importEventData - summit %s", $summit->getId()));
$allowed_extensions = ['txt'];
if (!in_array($csv_file->extension(), $allowed_extensions)) {
throw new ValidationException("file does not has a valid extension ('csv').");
}
$real_path = $csv_file->getRealPath();
$filename = pathinfo($real_path);
$filename = $filename['filename'] ?? sprintf("file%s", time());
$basename = sprintf("%s_%s.csv", $filename, time());
$filename = $csv_file->storeAs(sprintf("%s/events_imports", sys_get_temp_dir()), $basename);
$csv_data = \Illuminate\Support\Facades\File::get($real_path);
if (empty($csv_data))
throw new ValidationException("file content is empty!");
$csv = Reader::createFromString($csv_data);
$csv->setHeaderOffset(0);
$header = $csv->getHeader(); //returns the CSV header record
// check needed columns (headers names)
/*
columns ( min)
* title
* abstract
* type_id (int) or type (string type name)
* track_id (int) or track ( string track name)
*/
if(!in_array("id", $header)) {
// validate format with col names
if (!in_array("title", $header))
throw new ValidationException('title column missing');
if (!in_array("abstract", $header))
throw new ValidationException('abstract column missing');
$type_data_present = in_array("type_id", $header) ||
in_array("type", $header);
if (!$type_data_present) {
throw new ValidationException('type_id / type column missing');
}
$track_present = in_array("track_id", $header) ||
in_array("track", $header);
if (!$track_present) {
throw new ValidationException('track_id / track column missing');
}
}
ProcessEventDataImport::dispatch($summit->getId(), $filename, $payload);
}
/**
* @param int $summit_id
* @param string $filename
* @param bool $send_speaker_email
* @throws ValidationException
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
*/
public function processEventData(int $summit_id, string $filename, bool $send_speaker_email): void
{
Log::debug(sprintf("SummitService::processEventData summit %s filename %s", $summit_id, $filename));
if (!Storage::disk('local')->exists($filename)) {
throw new ValidationException(sprintf("file %s does not exists.", $filename));
}
$csv_data = Storage::disk('local')->get($filename);
$summit = $this->tx_service->transaction(function () use ($summit_id) {
$summit = $this->summit_repository->getById($summit_id);
if (is_null($summit) || !$summit instanceof Summit)
throw new EntityNotFoundException(sprintf("summit %s does not exists.", $summit_id));
return $summit;
});
$csv = Reader::createFromString($csv_data);
$csv->setHeaderOffset(0);
$header = $csv->getHeader(); //returns the CSV header record
$records = $csv->getRecords();
foreach ($records as $idx => $row) {
try {
// variable to store only added speakers to event
$new_speakers = [];
$event = $this->tx_service->transaction(function () use ($summit, $row, &$new_speakers) {
Log::debug(sprintf("SummitService::processEventData processing row %s", json_encode($row)));
// event type
$event_type = null;
if (isset($row['type_id']))
$event_type = $summit->getEventType(intval($row['type_id']));
if (isset($row['type']))
$event_type = $summit->getEventTypeByType($row['type']);
// track
$track = null;
if (isset($row['track_id']))
$track = $summit->getPresentationCategory(intval($row['track_id']));
if (isset($row['track']))
$track = $summit->getPresentationCategoryByTitle($row['track']);
if (is_null($event_type) && !isset($row['id']))
throw new EntityNotFoundException("event type not found.");
if (is_null($track) && !isset($row['id']))
throw new EntityNotFoundException("track not found.");
$event = null;
if (isset($row['id']) && !empty($row['id'])) {
Log::debug(sprintf("SummitService::processEventData trying to get event %s", $row['id']));
$event = $summit->getEventById(intval($row['id']));
if(is_null($event)){
throw new EntityNotFoundException(sprintf("event %s not found.", $row['id']));
}
if (is_null($event_type)) {
$event_type = $event->getType();
}
if (is_null($track)) {
$track = $event->getCategory();
}
}
if (is_null($event)) // new event
$event = SummitEventFactory::build($event_type, $summit);
// main data
if (isset($row['title'])) {
$title = trim($row['title']);
Log::debug(sprintf("SummitService::processEventData setting title %s", $title));
$event->setTitle(html_entity_decode($title));
}
if (isset($row['abstract'])) {
$abstract = trim($row['abstract']);
Log::debug(sprintf("SummitService::processEventData setting abstract %s", $abstract));
$event->setAbstract(html_entity_decode($abstract));
}
if (isset($row['level']))
$event->setLevel($row['level']);
if (isset($row['social_summary']))
$event->setSocialSummary($row['social_summary']);
if (isset($row['allow_feedback']))
$event->setAllowFeedBack(boolval($row['allow_feedback']));
if (!is_null($event_type))
$event->setType($event_type);
if (!is_null($track))
$event->setCategory($track);
if (isset($row['location']) && !empty($row['location'])) {
Log::debug(sprintf("SummitService::processEventData processing location %s", $row['location']));
$location = $summit->getLocation(intval($row['location']));
if (is_null($location))
$location = $summit->getLocationByName(trim($row['location']));
if (is_null($location))
throw new EntityNotFoundException("location not found.");
Log::debug(sprintf("SummitService::processEventData setting location %s", $location));
$event->setLocation($location);
}
if (isset($row['start_date']) && !empty($row['start_date']) && isset($row['end_date']) && !empty($row['end_date'])) {
Log::debug
(
sprintf
(
"SummitService::processEventData publishing event start_date %s end_date %s",
$row['start_date'],
$row['end_date']
)
);
$start_date = DateTime::createFromFormat('Y-m-d H:i:s', $row['start_date'], $summit->getTimeZone());
$end_date = DateTime::createFromFormat('Y-m-d H:i:s', $row['end_date'], $summit->getTimeZone());
// set local time from UTC
$event->setStartDate($start_date);
$event->setEndDate($end_date);
}
// tags
if (isset($row['tags'])) {
Log::debug(sprintf("SummitService::processEventData processing tags %s", $row['tags']));
$tags = explode('|', $row['tags']);
$event->clearTags();
foreach ($tags as $val) {
$tag = $this->tag_repository->getByTag($val);
if ($tag == null) {
Log::debug(sprintf("SummitService::processEventData creating tag %s", $val));
$tag = new Tag($val);
}
$event->addTag($tag);
}
}
// sponsors
if (!is_null($event_type)) {
$sponsors = ($event_type->isUseSponsors() && isset($row['sponsors'])) ?
$row['sponsors'] : '';
$sponsors = explode('|', $sponsors);
if ($event_type->isAreSponsorsMandatory() && count($sponsors) == 0) {
throw new ValidationException('sponsors are mandatory!');
}
if (isset($row['sponsors'])) {
$event->clearSponsors();
foreach ($sponsors as $sponsor_name) {
$sponsor = $this->company_repository->getByName(trim($sponsor_name));
if (is_null($sponsor)) throw new EntityNotFoundException(sprintf('sponsor %s', $sponsor_name));
$event->addSponsor($sponsor);
}
}
}
if ($event instanceof Presentation) {
Log::debug(sprintf("SummitService::processEventData event %s is a presentation", $event->getId()));
if (isset($row['to_record']))
$event->setToRecord(boolval($row['to_record']));
if (isset($row['attendees_expected_learnt']))
$event->setAttendeesExpectedLearnt($row['attendees_expected_learnt']);
if (isset($row['problem_addressed']))
$event->setProblemAddressed($row['problem_addressed']);
// speakers
if (!is_null($event_type) && $event_type instanceof PresentationType && $event_type->isUseSpeakers()) {
$speakers = isset($row['speakers']) ? $row['speakers'] : '';
Log::debug(sprintf("SummitService::processEventData event %s processing speakers %s", $event->getId(), $row['speakers']));
$speakers = explode('|', $speakers);
$speakers_names = [];
if (isset($row["speakers_names"])) {
$speakers_names = isset($row['speakers_names']) ?
$row['speakers_names'] : '';
Log::debug(sprintf("SummitService::processEventData event %s processing speakers_names %s", $event->getId(), $row['speakers_names']));
$speakers_names = explode('|', $speakers_names);
}
$speakers_companies = [];
if (isset($row["speakers_companies"])) {
$speakers_companies = isset($row['speakers_companies']) ?
$row['speakers_companies'] : '';
Log::debug(sprintf("SummitService::processEventData event %s processing speakers_companies %s", $event->getId(), $row['speakers_companies']));
$speakers_companies = explode('|', $speakers_companies);
}
$speakers_titles = [];
if (isset($row["speakers_titles"])) {
$speakers_titles = isset($row['speakers_titles']) ?
$row['speakers_titles'] : '';
Log::debug(sprintf("SummitService::processEventData event %s processing speakers_titles %s", $event->getId(), $row['speakers_titles']));
$speakers_titles = explode('|', $speakers_titles);
}
if (count($speakers_names) == 0) {
$speakers_names = $speakers;
}
if (count($speakers_names) != count($speakers))
throw new ValidationException("count of speakers and speakers_name should match.");
if ($event_type->isAreSpeakersMandatory() && count($speakers) == 0) {
throw new ValidationException('speakers are mandatory!');
}
if (count($speakers) > 0) {
foreach ($speakers as $idx => $speaker_email) {
$speaker_full_name = $speakers_names[$idx];
$speaker_full_name_comps = explode(" ", $speaker_full_name, 2);
$speaker_first_name = trim($speaker_full_name_comps[0]);
$speaker_last_name = null;
if (count($speaker_full_name_comps) > 1) {
$speaker_last_name = trim($speaker_full_name_comps[1]);
}
if (empty($speaker_last_name))
$speaker_last_name = $speaker_first_name;
Log::debug(sprintf("SummitService::processEventData processing speaker email %s speaker fullname %s", $speaker_email, $speaker_full_name));
$speaker = $this->speaker_repository->getByEmail(trim($speaker_email));
if (is_null($speaker)) {
Log::debug(sprintf("SummitService::processEventData speaker %s fname %s lname %s does not exists", $speaker_email, $speaker_first_name, $speaker_last_name));
$payload = [
'first_name' => $speaker_first_name,
'last_name' => $speaker_last_name,
'email' => $speaker_email
];
if(array_key_exists($idx ,$speakers_companies)){
$payload['company'] = $speakers_companies[$idx];
}
if(array_key_exists($idx, $speakers_titles)){
$payload['title'] = $speakers_titles[$idx];
}
Log::debug(sprintf("SummitService::processEventData adding speaker %s", json_encode($payload)));
$speaker = $this->speaker_service->addSpeaker($payload, null, false);
} else {
Log::debug(sprintf("SummitService::processEventData speaker %s already exists, updating ", $speaker_email));
$payload = [
'first_name' => $speaker_first_name,
'last_name' => $speaker_last_name,
'email' => $speaker_email
];
if(array_key_exists($idx ,$speakers_companies)){
$payload['company'] = $speakers_companies[$idx];
}
if(array_key_exists($idx, $speakers_titles)){
$payload['title'] = $speakers_titles[$idx];
}
Log::debug(sprintf("SummitService::processEventData updating speaker %s", json_encode($payload)));
$this->speaker_service->updateSpeaker($speaker, $payload);
}
if(!$event->isSpeaker($speaker)) {
$new_speakers[] = $speaker;
Log::debug(sprintf("SummitService::processEventData adding speaker %s to event %s", $speaker->getEmail(), $event->getTitle()));
$event->addSpeaker($speaker);
}
}
}
}
// moderator
if (!is_null($event_type) && $event_type instanceof PresentationType && $event_type->isUseModerator() && isset($row['moderator'])) {
$moderator_email = trim($row['moderator']);
if ($event_type->isModeratorMandatory() && !$event->hasModerator() && empty($moderator_email)) {
throw new ValidationException('moderator is mandatory!');
}
if(!empty($moderator_email)) {
Log::debug(sprintf("SummitService::processEventData processing moderator %s", $moderator_email));
$moderator = $this->speaker_repository->getByEmail($moderator_email);
if (is_null($moderator)) {
Log::debug(sprintf("SummitService::processEventData moderator %s does not exists", $moderator_email));
$moderator = $this->speaker_service->addSpeaker(['email' => $moderator_email], null, false);
}
$event->setModerator($moderator);
}
}
// selection plan
if (isset($row['selection_plan'])) {
$selection_plan = $summit->getSelectionPlanByName($row['selection_plan']);
if (!is_null($selection_plan)) {
Log::debug(sprintf("SummitService::processEventData processing selection plan %s", $row['selection_plan']));
$track = $event->getCategory();
if (!$selection_plan->hasTrack($track)) {
throw new ValidationException(sprintf("Track %s (%s) does not belongs to Selection Plan %s (%s)", $track->getTitle(), $track->getId(), $selection_plan->getName(), $selection_plan->getId()));
}
$event->setSelectionPlan($selection_plan);
}
}
}
if (isset($row['is_published'])) {
$is_published = boolval($row['is_published']);
if ($is_published) {
if (!isset($row['start_date'])) throw new ValidationException("start_date is required.");
if (!isset($row['end_date'])) throw new ValidationException("end_date is required.");
if (!$event->isPublished())
$event->publish();
} else {
$event->unPublish();
}
}
$summit->addEvent($event);
return $event;
});
if ($send_speaker_email && $event instanceof Presentation) {
// only send emails to added speakers
foreach ($new_speakers as $speaker)
$this->tx_service->transaction(function () use ($speaker, $event) {
$setPasswordLink = null;
if ($speaker instanceof PresentationSpeaker) {
if (!$speaker->hasMember()) {
Log::debug(sprintf("SummitService::processEventData speaker %s has not member set, checking at idp", $speaker->getEmail()));
$user = $this->member_service->checkExternalUser($speaker->getEmail());
if (is_null($user)) {
// user does not exist at idp so we need to generate a registration request
// and create the magic links to complete the registration request
Log::debug(sprintf("SummitService::processEventData speaker %s user not found at idp, creating registration request", $speaker->getEmail()));
$userRegistrationRequest = $this->member_service->emitRegistrationRequest
(
$speaker->getEmail(),
$speaker->getFirstName(),
$speaker->getLastName()
);
$setPasswordLink = $userRegistrationRequest['set_password_link'];
$speaker_management_base_url = Config::get('cfp.base_url');
$setPasswordLink = sprintf(
"%s?client_id=%s&redirect_uri=%s",
$setPasswordLink,
Config::get("cfp.client_id"),
sprintf("%s/app/profile", $speaker_management_base_url)
);
}
}
}
ImportEventSpeakerEmail::dispatch($event, $speaker, $setPasswordLink);
});
}
}
catch(Exception $ex){
Log::warning($ex);
}
}
}
/**
* @inheritDoc
*/
public function addFeaturedSpeaker(int $summit_id, int $speaker_id): void
{
$this->tx_service->transaction(function() use($summit_id, $speaker_id){
$summit = $this->summit_repository->getById($summit_id);
if(is_null($summit) || !$summit instanceof Summit)
throw new EntityNotFoundException("summit not found");
$speaker = $this->speaker_repository->getById($speaker_id);
if(is_null($speaker) || !$speaker instanceof PresentationSpeaker)
throw new EntityNotFoundException("speaker not found");
// validate it
if(!$this->speaker_repository->speakerBelongsToSummitSchedule($speaker_id, $summit_id)){
throw new ValidationException(sprintf("Speaker %s does not belongs to Summit %s schedule.", $speaker_id, $summit_id));
}
$summit->addFeaturedSpeaker($speaker);
});
}
/**
* @inheritDoc
*/
public function removeFeaturedSpeaker(int $summit_id, int $speaker_id): void
{
$this->tx_service->transaction(function() use($summit_id, $speaker_id){
$summit = $this->summit_repository->getById($summit_id);
if(is_null($summit) || !$summit instanceof Summit)
throw new EntityNotFoundException("summit not found");
$speaker = $this->speaker_repository->getById($speaker_id);
if(is_null($speaker) || !$speaker instanceof PresentationSpeaker)
throw new EntityNotFoundException("speaker not found");
$summit->removeFeaturedSpeaker($speaker);
});
}
}