421 lines
16 KiB
PHP
421 lines
16 KiB
PHP
<?php
|
|
|
|
class Application_Service_CalendarService
|
|
{
|
|
private $currentUser;
|
|
private $ccShowInstance;
|
|
private $ccShow;
|
|
|
|
public function __construct($instanceId = null)
|
|
{
|
|
if (!is_null($instanceId)) {
|
|
$this->ccShowInstance = CcShowInstancesQuery::create()->findPk($instanceId);
|
|
if (is_null($this->ccShowInstance)) {
|
|
throw new Exception('Instance does not exist');
|
|
}
|
|
$this->ccShow = $this->ccShowInstance->getCcShow();
|
|
}
|
|
|
|
$service_user = new Application_Service_UserService();
|
|
$this->currentUser = $service_user->getCurrentUser();
|
|
}
|
|
|
|
/**
|
|
* Enter description here ...
|
|
*/
|
|
public function makeContextMenu()
|
|
{
|
|
$menu = [];
|
|
$now = time();
|
|
$baseUrl = Config::getBasePath();
|
|
$isAdminOrPM = $this->currentUser->isAdminOrPM();
|
|
$isHostOfShow = $this->currentUser->isHostOfShow($this->ccShow->getDbId());
|
|
|
|
// DateTime objects in UTC
|
|
$startDT = $this->ccShowInstance->getDbStarts(null);
|
|
$endDT = $this->ccShowInstance->getDbEnds(null);
|
|
|
|
// timestamps
|
|
$start = $startDT->getTimestamp();
|
|
$end = $endDT->getTimestamp();
|
|
|
|
// show has ended
|
|
if ($now > $end) {
|
|
if ($this->ccShowInstance->isRecorded()) {
|
|
$ccFile = $this->ccShowInstance->getCcFiles();
|
|
if (!isset($ccFile)) {
|
|
$menu['error when recording'] = [
|
|
'name' => _("Record file doesn't exist"),
|
|
'icon' => 'error',
|
|
];
|
|
} else {
|
|
$menu['view_recorded'] = [
|
|
'name' => _('View Recorded File Metadata'),
|
|
'icon' => 'overview',
|
|
'url' => $baseUrl . 'library/edit-file-md/id/' . $ccFile->getDbId(),
|
|
];
|
|
}
|
|
} else {
|
|
$menu['content'] = [
|
|
// "name"=> _("Show Content"),
|
|
'name' => _('View'),
|
|
'icon' => 'overview',
|
|
'url' => $baseUrl . 'schedule/show-content-dialog',
|
|
];
|
|
}
|
|
} else {
|
|
// Show content can be modified from the calendar if:
|
|
// the user is admin or hosting the show,
|
|
// the show is not recorded
|
|
$currentShow = Application_Model_Show::getCurrentShow();
|
|
$currentShowId = count($currentShow) == 1 ? $currentShow[0]['id'] : null;
|
|
$showIsLinked = $this->ccShow->isLinked();
|
|
|
|
// user can add/remove content if the show has not ended
|
|
if ($now < $end && ($isAdminOrPM || $isHostOfShow) && !$this->ccShowInstance->isRecorded()) {
|
|
// if the show is not linked OR if the show is linked AND not the current playing show
|
|
// the user can add/remove content
|
|
if (!$showIsLinked || ($showIsLinked && $currentShowId != $this->ccShow->getDbId())) {
|
|
$menu['schedule'] = [
|
|
// "name"=> _("Add / Remove Content"),
|
|
'name' => _('Schedule Tracks'),
|
|
'icon' => 'add-remove-content',
|
|
'url' => $baseUrl . 'showbuilder/builder-dialog/',
|
|
];
|
|
}
|
|
}
|
|
|
|
// "Show Content" should be a menu item at all times except when
|
|
// the show is recorded
|
|
if (!$this->ccShowInstance->isRecorded()) {
|
|
$menu['content'] = [
|
|
// "name"=> _("Show Content"),
|
|
'name' => _('View'),
|
|
'icon' => 'overview',
|
|
'url' => $baseUrl . 'schedule/show-content-dialog',
|
|
];
|
|
}
|
|
|
|
// user can remove all content if the show has not started
|
|
if ($now < $start && ($isAdminOrPM || $isHostOfShow) && !$this->ccShowInstance->isRecorded()) {
|
|
// if the show is not linked OR if the show is linked AND not the current playing show
|
|
// the user can remove all content
|
|
if (!$showIsLinked || ($showIsLinked && $currentShowId != $this->ccShow->getDbId())) {
|
|
$menu['clear'] = [
|
|
// "name"=> _("Remove All Content"),
|
|
'name' => _('Clear Show'),
|
|
'icon' => 'remove-all-content',
|
|
'url' => $baseUrl . 'schedule/clear-show',
|
|
];
|
|
}
|
|
}
|
|
|
|
// show is currently playing and user is admin
|
|
if ($start <= $now && $now < $end && $isAdminOrPM) {
|
|
// Menu separator
|
|
$menu['sep1'] = '-----------';
|
|
|
|
if ($this->ccShowInstance->isRecorded()) {
|
|
$menu['cancel_recorded'] = [
|
|
// "name"=> _("Cancel Current Show"),
|
|
'name' => _('Cancel Show'),
|
|
'icon' => 'delete',
|
|
];
|
|
} else {
|
|
$menu['cancel'] = [
|
|
// "name"=> _("Cancel Current Show"),
|
|
'name' => _('Cancel Show'),
|
|
'icon' => 'delete',
|
|
];
|
|
}
|
|
}
|
|
|
|
$excludeIds = $this->ccShow->getEditedRepeatingInstanceIds();
|
|
|
|
$isRepeating = $this->ccShow->isRepeating();
|
|
$populateInstance = false;
|
|
if ($isRepeating && in_array($this->ccShowInstance->getDbId(), $excludeIds)) {
|
|
$populateInstance = true;
|
|
}
|
|
|
|
if (!$this->ccShowInstance->isRebroadcast() && $isAdminOrPM) {
|
|
// Menu separator
|
|
$menu['sep2'] = '-----------';
|
|
|
|
if ($isRepeating) {
|
|
if ($populateInstance) {
|
|
$menu['edit'] = [
|
|
// "name" => _("Edit This Instance"),
|
|
'name' => _('Edit Instance'),
|
|
'icon' => 'edit',
|
|
'url' => $baseUrl . 'Schedule/populate-repeating-show-instance-form',
|
|
];
|
|
} else {
|
|
$menu['edit'] = [
|
|
'name' => _('Edit'),
|
|
'icon' => 'edit',
|
|
'items' => [],
|
|
];
|
|
|
|
$menu['edit']['items']['all'] = [
|
|
'name' => _('Edit Show'),
|
|
'icon' => 'edit',
|
|
'url' => $baseUrl . 'Schedule/populate-show-form',
|
|
];
|
|
|
|
$menu['edit']['items']['instance'] = [
|
|
// "name" => _("Edit This Instance"),
|
|
'name' => _('Edit Instance'),
|
|
'icon' => 'edit',
|
|
'url' => $baseUrl . 'Schedule/populate-repeating-show-instance-form',
|
|
];
|
|
}
|
|
} else {
|
|
$menu['edit'] = [
|
|
'name' => _('Edit Show'),
|
|
'icon' => 'edit',
|
|
'_type' => 'all',
|
|
'url' => $baseUrl . 'Schedule/populate-show-form',
|
|
];
|
|
}
|
|
}
|
|
|
|
// show hasn't started yet and user is admin
|
|
if ($now < $start && $isAdminOrPM) {
|
|
// Menu separator
|
|
$menu['sep3'] = '-----------';
|
|
|
|
// show is repeating so give user the option to delete all
|
|
// repeating instances or just the one
|
|
if ($isRepeating) {
|
|
$menu['del'] = [
|
|
'name' => _('Delete'),
|
|
'icon' => 'delete',
|
|
'items' => [],
|
|
];
|
|
|
|
$menu['del']['items']['single'] = [
|
|
// "name"=> _("Delete This Instance"),
|
|
'name' => _('Delete Instance'),
|
|
'icon' => 'delete',
|
|
'url' => $baseUrl . 'schedule/delete-show-instance',
|
|
];
|
|
|
|
$menu['del']['items']['following'] = [
|
|
// "name"=> _("Delete This Instance and All Following"),
|
|
'name' => _('Delete Instance and All Following'),
|
|
'icon' => 'delete',
|
|
'url' => $baseUrl . 'schedule/delete-show',
|
|
];
|
|
} elseif ($populateInstance) {
|
|
$menu['del'] = [
|
|
'name' => _('Delete'),
|
|
'icon' => 'delete',
|
|
'url' => $baseUrl . 'schedule/delete-show-instance',
|
|
];
|
|
} else {
|
|
$menu['del'] = [
|
|
'name' => _('Delete'),
|
|
'icon' => 'delete',
|
|
'url' => $baseUrl . 'schedule/delete-show',
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
return $menu;
|
|
}
|
|
|
|
/**
|
|
* Enter description here ...
|
|
*
|
|
* @param DateTime $dateTime object to add deltas to
|
|
* @param int $deltaDay delta days show moved
|
|
* @param int $deltaMin delta minutes show moved
|
|
*/
|
|
public static function addDeltas($dateTime, $deltaDay, $deltaMin)
|
|
{
|
|
$newDateTime = clone $dateTime;
|
|
|
|
$days = abs($deltaDay);
|
|
$mins = abs($deltaMin);
|
|
|
|
$dayInterval = new DateInterval("P{$days}D");
|
|
$minInterval = new DateInterval("PT{$mins}M");
|
|
|
|
if ($deltaDay > 0) {
|
|
$newDateTime->add($dayInterval);
|
|
} elseif ($deltaDay < 0) {
|
|
$newDateTime->sub($dayInterval);
|
|
}
|
|
|
|
if ($deltaMin > 0) {
|
|
$newDateTime->add($minInterval);
|
|
} elseif ($deltaMin < 0) {
|
|
$newDateTime->sub($minInterval);
|
|
}
|
|
|
|
return $newDateTime;
|
|
}
|
|
|
|
private function validateShowMove($deltaDay, $deltaMin)
|
|
{
|
|
if (!$this->currentUser->isAdminOrPM()) {
|
|
throw new Exception(_('Permission denied'));
|
|
}
|
|
|
|
if ($this->ccShow->isRepeating()) {
|
|
throw new Exception(_("Can't drag and drop repeating shows"));
|
|
}
|
|
|
|
$today_timestamp = time();
|
|
|
|
$startsDateTime = $this->ccShowInstance->getDbStarts(null);
|
|
$endsDateTime = $this->ccShowInstance->getDbEnds(null);
|
|
|
|
if ($today_timestamp > $startsDateTime->getTimestamp()) {
|
|
throw new Exception(_("Can't move a past show"));
|
|
}
|
|
|
|
// the user is moving the show on the calendar from the perspective of local time.
|
|
// incase a show is moved across a time change border offsets should be added to the localtime
|
|
// stamp and then converted back to UTC to avoid show time changes!
|
|
$showTimezone = $this->ccShow->getFirstCcShowDay()->getDbTimezone();
|
|
$startsDateTime->setTimezone(new DateTimeZone($showTimezone));
|
|
$endsDateTime->setTimezone(new DateTimeZone($showTimezone));
|
|
|
|
$duration = $startsDateTime->diff($endsDateTime);
|
|
|
|
$newStartsDateTime = self::addDeltas($startsDateTime, $deltaDay, $deltaMin);
|
|
/* WARNING: Do not separately add a time delta to the start and end times because
|
|
that does not preserve the duration across a DST time change.
|
|
For example, 5am - 3 hours = 3am when DST occurs at 2am.
|
|
BUT, 6am - 3 hours = 3am also!
|
|
So when a DST change occurs, adding the deltas like this
|
|
separately does not conserve the duration of a show.
|
|
Since that's what we want (otherwise we'll get a zero length show),
|
|
we calculate the show duration FIRST, then we just add that on
|
|
to the start time to calculate the end time.
|
|
This is a safer approach.
|
|
The key lesson here is that in general: duration != end - start
|
|
... so be careful!
|
|
*/
|
|
// $newEndsDateTime = self::addDeltas($endsDateTime, $deltaDay, $deltaMin); <--- Wrong, don't do it.
|
|
$newEndsDateTime = clone $newStartsDateTime;
|
|
$newEndsDateTime = $newEndsDateTime->add($duration);
|
|
|
|
// convert our new starts/ends to UTC.
|
|
$newStartsDateTime->setTimezone(new DateTimeZone('UTC'));
|
|
$newEndsDateTime->setTimezone(new DateTimeZone('UTC'));
|
|
|
|
if ($today_timestamp > $newStartsDateTime->getTimestamp()) {
|
|
throw new Exception(_("Can't move show into past"));
|
|
}
|
|
|
|
// check if show is overlapping
|
|
$overlapping = Application_Model_Schedule::checkOverlappingShows(
|
|
$newStartsDateTime,
|
|
$newEndsDateTime,
|
|
true,
|
|
$this->ccShowInstance->getDbId()
|
|
);
|
|
if ($overlapping) {
|
|
throw new Exception(_('Cannot schedule overlapping shows'));
|
|
}
|
|
|
|
if ($this->ccShow->isRecorded()) {
|
|
// rebroadcasts should start at max 1 hour after a recorded show has ended.
|
|
$minRebroadcastStart = self::addDeltas($newEndsDateTime, 0, 60);
|
|
// check if we are moving a recorded show less than 1 hour before any of its own rebroadcasts.
|
|
$rebroadcasts = CcShowInstancesQuery::create()
|
|
->filterByDbOriginalShow($this->ccShow->getDbId())
|
|
->filterByDbStarts($minRebroadcastStart->format(DEFAULT_TIMESTAMP_FORMAT), Criteria::LESS_THAN)
|
|
->find();
|
|
|
|
if (count($rebroadcasts) > 0) {
|
|
throw new Exception(_("Can't move a recorded show less than 1 hour before its rebroadcasts."));
|
|
}
|
|
}
|
|
|
|
if ($this->ccShow->isRebroadcast()) {
|
|
$recordedShow = CcShowInstancesQuery::create()
|
|
->filterByCcShow($this->ccShowInstance->getDbOriginalShow())
|
|
->findOne();
|
|
if (is_null($recordedShow)) {
|
|
$this->ccShowInstance->delete();
|
|
|
|
throw new Exception(_('Show was deleted because recorded show does not exist!'));
|
|
}
|
|
|
|
$recordEndDateTime = new DateTime($recordedShow->getDbEnds(), new DateTimeZone('UTC'));
|
|
$newRecordEndDateTime = self::addDeltas($recordEndDateTime, 0, 60);
|
|
|
|
if ($newStartsDateTime->getTimestamp() < $newRecordEndDateTime->getTimestamp()) {
|
|
throw new Exception(_('Must wait 1 hour to rebroadcast.'));
|
|
}
|
|
}
|
|
|
|
return [$newStartsDateTime, $newEndsDateTime];
|
|
}
|
|
|
|
public function moveShow($deltaDay, $deltaMin)
|
|
{
|
|
try {
|
|
$con = Propel::getConnection();
|
|
$con->beginTransaction();
|
|
|
|
// new starts,ends are in UTC
|
|
[$newStartsDateTime, $newEndsDateTime] = $this->validateShowMove(
|
|
$deltaDay,
|
|
$deltaMin
|
|
);
|
|
|
|
$oldStartDateTime = $this->ccShowInstance->getDbStarts(null);
|
|
|
|
$this->ccShowInstance
|
|
->setDbStarts($newStartsDateTime)
|
|
->setDbEnds($newEndsDateTime)
|
|
->save($con);
|
|
|
|
if (!$this->ccShowInstance->getCcShow()->isRebroadcast()) {
|
|
// we can get the first show day because we know the show is
|
|
// not repeating, and therefore will only have one show day entry
|
|
$ccShowDay = $this->ccShow->getFirstCcShowDay();
|
|
$showTimezone = new DateTimeZone($ccShowDay->getDbTimezone());
|
|
$ccShowDay
|
|
->setDbFirstShow($newStartsDateTime->setTimezone($showTimezone)->format('Y-m-d'))
|
|
->setDbStartTime($newStartsDateTime->format('H:i'))
|
|
->save($con);
|
|
}
|
|
|
|
$diff = $newStartsDateTime->getTimestamp() - $oldStartDateTime->getTimestamp();
|
|
|
|
Application_Service_SchedulerService::updateScheduleStartTime(
|
|
[$this->ccShowInstance->getDbId()],
|
|
$diff
|
|
);
|
|
|
|
$con->commit();
|
|
Application_Model_RabbitMq::PushSchedule();
|
|
} catch (Exception $e) {
|
|
$con->rollback();
|
|
|
|
return $e->getMessage();
|
|
}
|
|
}
|
|
|
|
// TODO move the method resizeShow from Application_Model_Show here.
|
|
public function resizeShow($deltaDay, $deltaMin)
|
|
{
|
|
try {
|
|
$con = Propel::getConnection();
|
|
$con->beginTransaction();
|
|
|
|
$con->commit();
|
|
Application_Model_RabbitMq::PushSchedule();
|
|
} catch (Exception $e) {
|
|
return $e->getMessage();
|
|
}
|
|
}
|
|
}
|