Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php
index 390736dc35..16e057ebcc 100644
--- a/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php
+++ b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php
@@ -1,392 +1,389 @@
<?php
final class PhabricatorCalendarEventViewController
extends PhabricatorCalendarController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$event = $this->loadEvent();
if (!$event) {
return new Aphront404Response();
}
// If we looked up or generated a stub event, redirect to that event's
// canonical URI.
$id = $request->getURIData('id');
if ($event->getID() != $id) {
$uri = $event->getURI();
return id(new AphrontRedirectResponse())->setURI($uri);
}
$monogram = $event->getMonogram();
$page_title = $monogram.' '.$event->getName();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($monogram);
$crumbs->setBorder(true);
$timeline = $this->buildTransactionTimeline(
$event,
new PhabricatorCalendarEventTransactionQuery());
$header = $this->buildHeaderView($event);
+ $subheader = $this->buildSubheaderView($event);
$curtain = $this->buildCurtain($event);
$details = $this->buildPropertySection($event);
$description = $this->buildDescriptionView($event);
$comment_view = id(new PhabricatorCalendarEventEditEngine())
->setViewer($viewer)
->buildEditEngineCommentView($event);
$timeline->setQuoteRef($monogram);
$comment_view->setTransactionTimeline($timeline);
$view = id(new PHUITwoColumnView())
->setHeader($header)
+ ->setSubheader($subheader)
->setMainColumn(
array(
$timeline,
$comment_view,
))
->setCurtain($curtain)
->addPropertySection(pht('Details'), $details)
->addPropertySection(pht('Description'), $description);
return $this->newPage()
->setTitle($page_title)
->setCrumbs($crumbs)
->setPageObjectPHIDs(array($event->getPHID()))
->appendChild($view);
}
private function buildHeaderView(
PhabricatorCalendarEvent $event) {
$viewer = $this->getViewer();
$id = $event->getID();
if ($event->isCancelledEvent()) {
$icon = 'fa-ban';
$color = 'red';
$status = pht('Cancelled');
} else {
$icon = 'fa-check';
$color = 'bluegrey';
$status = pht('Active');
}
$invite_status = $event->getUserInviteStatus($viewer->getPHID());
$status_invited = PhabricatorCalendarEventInvitee::STATUS_INVITED;
$is_invite_pending = ($invite_status == $status_invited);
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader($event->getName())
->setStatus($icon, $color, $status)
->setPolicyObject($event)
->setHeaderIcon($event->getIcon());
if ($is_invite_pending) {
$decline_button = id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-times grey')
->setHref($this->getApplicationURI("/event/decline/{$id}/"))
->setWorkflow(true)
->setText(pht('Decline'));
$accept_button = id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-check green')
->setHref($this->getApplicationURI("/event/accept/{$id}/"))
->setWorkflow(true)
->setText(pht('Accept'));
$header->addActionLink($decline_button)
->addActionLink($accept_button);
}
return $header;
}
private function buildCurtain(PhabricatorCalendarEvent $event) {
$viewer = $this->getRequest()->getUser();
$id = $event->getID();
$is_attending = $event->getIsUserAttending($viewer->getPHID());
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$event,
PhabricatorPolicyCapability::CAN_EDIT);
$edit_uri = "event/edit/{$id}/";
if ($event->isChildEvent()) {
$edit_label = pht('Edit This Instance');
} else {
$edit_label = pht('Edit Event');
}
$curtain = $this->newCurtainView($event);
if ($edit_label && $edit_uri) {
$curtain->addAction(
id(new PhabricatorActionView())
->setName($edit_label)
->setIcon('fa-pencil')
->setHref($this->getApplicationURI($edit_uri))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
}
if ($is_attending) {
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Decline Event'))
->setIcon('fa-user-times')
->setHref($this->getApplicationURI("event/join/{$id}/"))
->setWorkflow(true));
} else {
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Join Event'))
->setIcon('fa-user-plus')
->setHref($this->getApplicationURI("event/join/{$id}/"))
->setWorkflow(true));
}
$cancel_uri = $this->getApplicationURI("event/cancel/{$id}/");
$cancel_disabled = !$can_edit;
if ($event->isChildEvent()) {
$cancel_label = pht('Cancel This Instance');
$reinstate_label = pht('Reinstate This Instance');
if ($event->getParentEvent()->getIsCancelled()) {
$cancel_disabled = true;
}
} else if ($event->isParentEvent()) {
$cancel_label = pht('Cancel All');
$reinstate_label = pht('Reinstate All');
} else {
$cancel_label = pht('Cancel Event');
$reinstate_label = pht('Reinstate Event');
}
if ($event->isCancelledEvent()) {
$curtain->addAction(
id(new PhabricatorActionView())
->setName($reinstate_label)
->setIcon('fa-plus')
->setHref($cancel_uri)
->setDisabled($cancel_disabled)
->setWorkflow(true));
} else {
$curtain->addAction(
id(new PhabricatorActionView())
->setName($cancel_label)
->setIcon('fa-times')
->setHref($cancel_uri)
->setDisabled($cancel_disabled)
->setWorkflow(true));
}
return $curtain;
}
private function buildPropertySection(
PhabricatorCalendarEvent $event) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView())
->setUser($viewer);
- if ($event->getIsAllDay()) {
- $date_start = phabricator_date($event->getViewerDateFrom(), $viewer);
- $date_end = phabricator_date($event->getViewerDateTo(), $viewer);
-
- if ($date_start == $date_end) {
- $properties->addProperty(
- pht('Time'),
- phabricator_date($event->getViewerDateFrom(), $viewer));
- } else {
- $properties->addProperty(
- pht('Starts'),
- phabricator_date($event->getViewerDateFrom(), $viewer));
- $properties->addProperty(
- pht('Ends'),
- phabricator_date($event->getViewerDateTo(), $viewer));
- }
- } else {
- $properties->addProperty(
- pht('Starts'),
- phabricator_datetime($event->getViewerDateFrom(), $viewer));
-
- $properties->addProperty(
- pht('Ends'),
- phabricator_datetime($event->getViewerDateTo(), $viewer));
- }
-
if ($event->getIsRecurring()) {
$properties->addProperty(
pht('Recurs'),
ucwords(idx($event->getRecurrenceFrequency(), 'rule')));
if ($event->getRecurrenceEndDate()) {
$properties->addProperty(
pht('Recurrence Ends'),
phabricator_datetime($event->getRecurrenceEndDate(), $viewer));
}
if ($event->getInstanceOfEventPHID()) {
$properties->addProperty(
pht('Recurrence of Event'),
pht('%s of %s',
$event->getSequenceIndex(),
$viewer->renderHandle($event->getInstanceOfEventPHID())->render()));
}
}
- $properties->addProperty(
- pht('Host'),
- $viewer->renderHandle($event->getHostPHID()));
-
$invitees = $event->getInvitees();
foreach ($invitees as $key => $invitee) {
if ($invitee->isUninvited()) {
unset($invitees[$key]);
}
}
if ($invitees) {
$invitee_list = new PHUIStatusListView();
$icon_invited = PHUIStatusItemView::ICON_OPEN;
$icon_attending = PHUIStatusItemView::ICON_ACCEPT;
$icon_declined = PHUIStatusItemView::ICON_REJECT;
$status_invited = PhabricatorCalendarEventInvitee::STATUS_INVITED;
$status_attending = PhabricatorCalendarEventInvitee::STATUS_ATTENDING;
$status_declined = PhabricatorCalendarEventInvitee::STATUS_DECLINED;
$icon_map = array(
$status_invited => $icon_invited,
$status_attending => $icon_attending,
$status_declined => $icon_declined,
);
$icon_color_map = array(
$status_invited => null,
$status_attending => 'green',
$status_declined => 'red',
);
foreach ($invitees as $invitee) {
$item = new PHUIStatusItemView();
$invitee_phid = $invitee->getInviteePHID();
$status = $invitee->getStatus();
$target = $viewer->renderHandle($invitee_phid);
$icon = $icon_map[$status];
$icon_color = $icon_color_map[$status];
$item->setIcon($icon, $icon_color)
->setTarget($target);
$invitee_list->addItem($item);
}
} else {
$invitee_list = phutil_tag(
'em',
array(),
pht('None'));
}
$properties->addProperty(
pht('Invitees'),
$invitee_list);
$properties->invokeWillRenderEvent();
return $properties;
}
private function buildDescriptionView(
PhabricatorCalendarEvent $event) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView())
->setUser($viewer);
if (strlen($event->getDescription())) {
$description = new PHUIRemarkupView($viewer, $event->getDescription());
$properties->addTextContent($description);
return $properties;
}
return null;
}
private function loadEvent() {
$request = $this->getRequest();
$viewer = $this->getViewer();
$id = $request->getURIData('id');
$sequence = $request->getURIData('sequence');
// We're going to figure out which event you're trying to look at. Most of
// the time this is simple, but you may be looking at an instance of a
// recurring event which we haven't generated an object for.
// If you are, we're going to generate a "stub" event so we have a real
// ID and PHID to work with, since the rest of the infrastructure relies
// on these identifiers existing.
// Load the event identified by ID first.
$event = id(new PhabricatorCalendarEventQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$event) {
return null;
}
// If we aren't looking at an instance of this event, this is a completely
// normal request and we can just return this event.
if (!$sequence) {
return $event;
}
// When you view "E123/999", E123 is normally the parent event. However,
// you might visit a different instance first instead and then fiddle
// with the URI. If the event we're looking at is a child, we are going
// to act on the parent instead.
if ($event->isChildEvent()) {
$event = $event->getParentEvent();
}
// Try to load the instance. If it already exists, we're all done and
// can just return it.
$instance = id(new PhabricatorCalendarEventQuery())
->setViewer($viewer)
->withInstanceSequencePairs(
array(
array($event->getPHID(), $sequence),
))
->executeOne();
if ($instance) {
return $instance;
}
if (!$viewer->isLoggedIn()) {
throw new Exception(
pht(
'This event instance has not been created yet. Log in to create '.
'it.'));
}
$instance = $event->newStub($viewer, $sequence);
return $instance;
}
+ private function buildSubheaderView(PhabricatorCalendarEvent $event) {
+ $viewer = $this->getViewer();
+
+ $host_phid = $event->getHostPHID();
+
+ $handles = $viewer->loadHandles(array($host_phid));
+ $handle = $handles[$host_phid];
+
+ $host = $viewer->renderHandle($host_phid);
+ $host = phutil_tag('strong', array(), $host);
+
+ $image_uri = $handles[$host_phid]->getImageURI();
+ $image_href = $handles[$host_phid]->getURI();
+
+ $date = $event->renderEventDate($viewer, true);
+
+ $content = pht('Hosted by %s on %s.', $host, $date);
+
+ return id(new PHUIHeadThingView())
+ ->setImage($image_uri)
+ ->setImageHref($image_href)
+ ->setContent($content);
+ }
+
+
}
diff --git a/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php b/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php
index b88b99dd39..d0c4559902 100644
--- a/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php
+++ b/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php
@@ -1,581 +1,537 @@
<?php
final class PhabricatorCalendarEventSearchEngine
extends PhabricatorApplicationSearchEngine {
private $calendarYear;
private $calendarMonth;
private $calendarDay;
public function getResultTypeDescription() {
return pht('Calendar Events');
}
public function getApplicationClassName() {
return 'PhabricatorCalendarApplication';
}
public function newQuery() {
return new PhabricatorCalendarEventQuery();
}
protected function shouldShowOrderField() {
return false;
}
protected function buildCustomSearchFields() {
return array(
id(new PhabricatorSearchDatasourceField())
->setLabel(pht('Created By'))
->setKey('creatorPHIDs')
->setDatasource(new PhabricatorPeopleUserFunctionDatasource()),
id(new PhabricatorSearchDatasourceField())
->setLabel(pht('Invited'))
->setKey('invitedPHIDs')
->setDatasource(new PhabricatorPeopleUserFunctionDatasource()),
id(new PhabricatorSearchDateControlField())
->setLabel(pht('Occurs After'))
->setKey('rangeStart'),
id(new PhabricatorSearchDateControlField())
->setLabel(pht('Occurs Before'))
->setKey('rangeEnd')
->setAliases(array('rangeEnd')),
id(new PhabricatorSearchCheckboxesField())
->setKey('upcoming')
->setOptions(array(
'upcoming' => pht('Show only upcoming events.'),
)),
id(new PhabricatorSearchSelectField())
->setLabel(pht('Cancelled Events'))
->setKey('isCancelled')
->setOptions($this->getCancelledOptions())
->setDefault('active'),
id(new PhabricatorSearchSelectField())
->setLabel(pht('Display Options'))
->setKey('display')
->setOptions($this->getViewOptions())
->setDefault('month'),
);
}
private function getCancelledOptions() {
return array(
'active' => pht('Active Events Only'),
'cancelled' => pht('Cancelled Events Only'),
'both' => pht('Both Cancelled and Active Events'),
);
}
private function getViewOptions() {
return array(
'month' => pht('Month View'),
'day' => pht('Day View'),
'list' => pht('List View'),
);
}
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
$viewer = $this->requireViewer();
if ($map['creatorPHIDs']) {
$query->withCreatorPHIDs($map['creatorPHIDs']);
}
if ($map['invitedPHIDs']) {
$query->withInvitedPHIDs($map['invitedPHIDs']);
}
$range_start = $map['rangeStart'];
$range_end = $map['rangeEnd'];
$display = $map['display'];
if ($map['upcoming'] && $map['upcoming'][0] == 'upcoming') {
$upcoming = true;
} else {
$upcoming = false;
}
list($range_start, $range_end) = $this->getQueryDateRange(
$range_start,
$range_end,
$display,
$upcoming);
$query->withDateRange($range_start, $range_end);
switch ($map['isCancelled']) {
case 'active':
$query->withIsCancelled(false);
break;
case 'cancelled':
$query->withIsCancelled(true);
break;
}
// Generate ghosts (and ignore stub events) if we aren't querying for
// specific events.
if (!$map['ids'] && !$map['phids']) {
$query
->withIsStub(false)
->setGenerateGhosts(true);
}
return $query;
}
private function getQueryDateRange(
$start_date_wild,
$end_date_wild,
$display,
$upcoming) {
$start_date_value = $this->getSafeDate($start_date_wild);
$end_date_value = $this->getSafeDate($end_date_wild);
$viewer = $this->requireViewer();
$timezone = new DateTimeZone($viewer->getTimezoneIdentifier());
$min_range = null;
$max_range = null;
$min_range = $start_date_value->getEpoch();
$max_range = $end_date_value->getEpoch();
if ($display == 'month' || $display == 'day') {
list($start_year, $start_month, $start_day) =
$this->getDisplayYearAndMonthAndDay($min_range, $max_range, $display);
$start_day = new DateTime(
"{$start_year}-{$start_month}-{$start_day}",
$timezone);
$next = clone $start_day;
if ($display == 'month') {
$next->modify('+1 month');
} else if ($display == 'day') {
$next->modify('+7 day');
}
$display_start = $start_day->format('U');
$display_end = $next->format('U');
$start_of_week = $viewer->getUserSetting(
PhabricatorWeekStartDaySetting::SETTINGKEY);
$end_of_week = ($start_of_week + 6) % 7;
$first_of_month = $start_day->format('w');
$last_of_month = id(clone $next)->modify('-1 day')->format('w');
if (!$min_range || ($min_range < $display_start)) {
$min_range = $display_start;
if ($display == 'month' &&
$first_of_month !== $start_of_week) {
$interim_day_num = ($first_of_month + 7 - $start_of_week) % 7;
$min_range = id(clone $start_day)
->modify('-'.$interim_day_num.' days')
->format('U');
}
}
if (!$max_range || ($max_range > $display_end)) {
$max_range = $display_end;
if ($display == 'month' &&
$last_of_month !== $end_of_week) {
$interim_day_num = ($end_of_week + 7 - $last_of_month) % 7;
$max_range = id(clone $next)
->modify('+'.$interim_day_num.' days')
->format('U');
}
}
}
if ($upcoming) {
$now = PhabricatorTime::getNow();
if ($min_range) {
$min_range = max($now, $min_range);
} else {
$min_range = $now;
}
}
return array($min_range, $max_range);
}
protected function getURI($path) {
return '/calendar/'.$path;
}
protected function getBuiltinQueryNames() {
$names = array(
'month' => pht('Month View'),
'day' => pht('Day View'),
'upcoming' => pht('Upcoming Events'),
'all' => pht('All Events'),
);
return $names;
}
public function setCalendarYearAndMonthAndDay($year, $month, $day = null) {
$this->calendarYear = $year;
$this->calendarMonth = $month;
$this->calendarDay = $day;
return $this;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'month':
return $query->setParameter('display', 'month');
case 'day':
return $query->setParameter('display', 'day');
case 'upcoming':
return $query
->setParameter('display', 'list')
->setParameter('upcoming', array(
0 => 'upcoming',
));
case 'all':
return $query;
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function renderResultList(
array $events,
PhabricatorSavedQuery $query,
array $handles) {
if ($this->isMonthView($query)) {
return $this->buildCalendarView($events, $query, $handles);
} else if ($this->isDayView($query)) {
return $this->buildCalendarDayView($events, $query, $handles);
}
assert_instances_of($events, 'PhabricatorCalendarEvent');
$viewer = $this->requireViewer();
$list = new PHUIObjectItemListView();
foreach ($events as $event) {
- $event_date_info = $this->getEventDateLabel($event);
$attendees = array();
foreach ($event->getInvitees() as $invitee) {
$status_attending = PhabricatorCalendarEventInvitee::STATUS_ATTENDING;
if ($invitee->getStatus() === $status_attending) {
$attendees[] = $invitee->getInviteePHID();
}
}
if ($event->getIsGhostEvent()) {
$monogram = $event->getParentEvent()->getMonogram();
$index = $event->getSequenceIndex();
$monogram = "{$monogram}/{$index}";
} else {
$monogram = $event->getMonogram();
}
$item = id(new PHUIObjectItemView())
->setUser($viewer)
->setObject($event)
->setObjectName($monogram)
->setHeader($event->getName())
->setHref($event->getURI())
- ->addAttribute($event_date_info);
+ ->addAttribute($event->renderEventDate($viewer, false));
if ($attendees) {
$attending = pht(
'Attending: %s',
$viewer->renderHandleList($attendees)
->setAsInline(1)
->render());
$item->addAttribute($attending);
}
- if ($event->getDuration()) {
- $duration = pht(
- 'Duration: %s',
- $event->getDisplayDuration());
- $item->addIcon('none', $duration);
- }
-
$list->addItem($item);
}
$result = new PhabricatorApplicationSearchResultView();
$result->setObjectList($list);
$result->setNoDataString(pht('No events found.'));
return $result;
}
private function buildCalendarView(
array $statuses,
PhabricatorSavedQuery $query,
array $handles) {
$viewer = $this->requireViewer();
$now = time();
list($start_year, $start_month) =
$this->getDisplayYearAndMonthAndDay(
$this->getQueryDateFrom($query)->getEpoch(),
$this->getQueryDateTo($query)->getEpoch(),
$query->getParameter('display'));
$now_year = phabricator_format_local_time($now, $viewer, 'Y');
$now_month = phabricator_format_local_time($now, $viewer, 'm');
$now_day = phabricator_format_local_time($now, $viewer, 'j');
if ($start_month == $now_month && $start_year == $now_year) {
$month_view = new PHUICalendarMonthView(
$this->getQueryDateFrom($query),
$this->getQueryDateTo($query),
$start_month,
$start_year,
$now_day);
} else {
$month_view = new PHUICalendarMonthView(
$this->getQueryDateFrom($query),
$this->getQueryDateTo($query),
$start_month,
$start_year);
}
$month_view->setUser($viewer);
$phids = mpull($statuses, 'getHostPHID');
$handles = $viewer->loadHandles($phids);
foreach ($statuses as $status) {
$viewer_is_invited = $status->getIsUserInvited($viewer->getPHID());
$event = new AphrontCalendarEventView();
$event->setEpochRange(
$status->getViewerDateFrom(),
$status->getViewerDateTo());
$event->setIsAllDay($status->getIsAllDay());
$event->setIcon($status->getIcon());
$name_text = $handles[$status->getHostPHID()]->getName();
$status_text = $status->getName();
$event->setHostPHID($status->getHostPHID());
$event->setDescription(pht('%s (%s)', $name_text, $status_text));
$event->setName($status_text);
$event->setURI($status->getURI());
$event->setViewerIsInvited($viewer_is_invited);
$month_view->addEvent($event);
}
$month_view->setBrowseURI(
$this->getURI('query/'.$query->getQueryKey().'/'));
// TODO redesign-2015 : Move buttons out of PHUICalendarView?
$result = new PhabricatorApplicationSearchResultView();
$result->setContent($month_view);
return $result;
}
private function buildCalendarDayView(
array $statuses,
PhabricatorSavedQuery $query,
array $handles) {
$viewer = $this->requireViewer();
list($start_year, $start_month, $start_day) =
$this->getDisplayYearAndMonthAndDay(
$this->getQueryDateFrom($query)->getEpoch(),
$this->getQueryDateTo($query)->getEpoch(),
$query->getParameter('display'));
$day_view = id(new PHUICalendarDayView(
$this->getQueryDateFrom($query)->getEpoch(),
$this->getQueryDateTo($query)->getEpoch(),
$start_year,
$start_month,
$start_day))
->setQuery($query->getQueryKey());
$day_view->setUser($viewer);
$phids = mpull($statuses, 'getHostPHID');
foreach ($statuses as $status) {
if ($status->getIsCancelled()) {
continue;
}
$viewer_is_invited = $status->getIsUserInvited($viewer->getPHID());
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$status,
PhabricatorPolicyCapability::CAN_EDIT);
$event = new AphrontCalendarEventView();
$event->setCanEdit($can_edit);
$event->setEventID($status->getID());
$event->setEpochRange(
$status->getViewerDateFrom(),
$status->getViewerDateTo());
$event->setIsAllDay($status->getIsAllDay());
$event->setIcon($status->getIcon());
$event->setViewerIsInvited($viewer_is_invited);
$event->setName($status->getName());
$event->setURI($status->getURI());
$day_view->addEvent($event);
}
$day_view->setBrowseURI(
$this->getURI('query/'.$query->getQueryKey().'/'));
$result = new PhabricatorApplicationSearchResultView();
$result->setContent($day_view);
return $result;
}
private function getDisplayYearAndMonthAndDay(
$range_start,
$range_end,
$display) {
$viewer = $this->requireViewer();
$epoch = null;
if ($this->calendarYear && $this->calendarMonth) {
$start_year = $this->calendarYear;
$start_month = $this->calendarMonth;
$start_day = $this->calendarDay ? $this->calendarDay : 1;
} else {
if ($range_start) {
$epoch = $range_start;
} else if ($range_end) {
$epoch = $range_end;
} else {
$epoch = time();
}
if ($display == 'month') {
$day = 1;
} else {
$day = phabricator_format_local_time($epoch, $viewer, 'd');
}
$start_year = phabricator_format_local_time($epoch, $viewer, 'Y');
$start_month = phabricator_format_local_time($epoch, $viewer, 'm');
$start_day = $day;
}
return array($start_year, $start_month, $start_day);
}
public function getPageSize(PhabricatorSavedQuery $saved) {
if ($this->isMonthView($saved) || $this->isDayView($saved)) {
return $saved->getParameter('limit', 1000);
} else {
return $saved->getParameter('limit', 100);
}
}
private function getQueryDateFrom(PhabricatorSavedQuery $saved) {
return $this->getQueryDate($saved, 'rangeStart');
}
private function getQueryDateTo(PhabricatorSavedQuery $saved) {
return $this->getQueryDate($saved, 'rangeEnd');
}
private function getQueryDate(PhabricatorSavedQuery $saved, $key) {
$viewer = $this->requireViewer();
$wild = $saved->getParameter($key);
return $this->getSafeDate($wild);
}
private function getSafeDate($value) {
$viewer = $this->requireViewer();
if ($value) {
// ideally this would be consistent and always pass in the same type
if ($value instanceof AphrontFormDateControlValue) {
return $value;
} else {
$value = AphrontFormDateControlValue::newFromWild($viewer, $value);
}
} else {
$value = AphrontFormDateControlValue::newFromEpoch(
$viewer,
PhabricatorTime::getTodayMidnightDateTime($viewer)->format('U'));
$value->setEnabled(false);
}
$value->setOptional(true);
return $value;
}
private function isMonthView(PhabricatorSavedQuery $query) {
if ($this->isDayView($query)) {
return false;
}
if ($query->getParameter('display') == 'month') {
return true;
}
}
private function isDayView(PhabricatorSavedQuery $query) {
if ($query->getParameter('display') == 'day') {
return true;
}
if ($this->calendarDay) {
return true;
}
return false;
}
- private function getEventDateLabel($event) {
- $viewer = $this->requireViewer();
-
- $from_datetime = PhabricatorTime::getDateTimeFromEpoch(
- $event->getViewerDateFrom(),
- $viewer);
- $to_datetime = PhabricatorTime::getDateTimeFromEpoch(
- $event->getViewerDateTo(),
- $viewer);
-
- $from_date_formatted = $from_datetime->format('Y m d');
- $to_date_formatted = $to_datetime->format('Y m d');
-
- if ($event->getIsAllDay()) {
- if ($from_date_formatted == $to_date_formatted) {
- return pht(
- '%s, All Day',
- phabricator_date($event->getViewerDateFrom(), $viewer));
- } else {
- return pht(
- '%s - %s, All Day',
- phabricator_date($event->getViewerDateFrom(), $viewer),
- phabricator_date($event->getViewerDateTo(), $viewer));
- }
- } else if ($from_date_formatted == $to_date_formatted) {
- return pht(
- '%s - %s',
- phabricator_datetime($event->getViewerDateFrom(), $viewer),
- phabricator_time($event->getViewerDateTo(), $viewer));
- } else {
- return pht(
- '%s - %s',
- phabricator_datetime($event->getViewerDateFrom(), $viewer),
- phabricator_datetime($event->getViewerDateTo(), $viewer));
- }
- }
}
diff --git a/src/applications/calendar/storage/PhabricatorCalendarEvent.php b/src/applications/calendar/storage/PhabricatorCalendarEvent.php
index 8afac00247..53261ed49b 100644
--- a/src/applications/calendar/storage/PhabricatorCalendarEvent.php
+++ b/src/applications/calendar/storage/PhabricatorCalendarEvent.php
@@ -1,666 +1,696 @@
<?php
final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
implements
PhabricatorPolicyInterface,
PhabricatorProjectInterface,
PhabricatorMarkupInterface,
PhabricatorApplicationTransactionInterface,
PhabricatorSubscribableInterface,
PhabricatorTokenReceiverInterface,
PhabricatorDestructibleInterface,
PhabricatorMentionableInterface,
PhabricatorFlaggableInterface,
PhabricatorSpacesInterface,
PhabricatorFulltextInterface,
PhabricatorConduitResultInterface {
protected $name;
protected $hostPHID;
protected $dateFrom;
protected $dateTo;
protected $description;
protected $isCancelled;
protected $isAllDay;
protected $icon;
protected $mailKey;
protected $isStub;
protected $isRecurring = 0;
protected $recurrenceFrequency = array();
protected $recurrenceEndDate;
private $isGhostEvent = false;
protected $instanceOfEventPHID;
protected $sequenceIndex;
protected $viewPolicy;
protected $editPolicy;
protected $spacePHID;
private $parentEvent = self::ATTACHABLE;
private $invitees = self::ATTACHABLE;
private $viewerDateFrom;
private $viewerDateTo;
// Frequency Constants
const FREQUENCY_DAILY = 'daily';
const FREQUENCY_WEEKLY = 'weekly';
const FREQUENCY_MONTHLY = 'monthly';
const FREQUENCY_YEARLY = 'yearly';
public static function initializeNewCalendarEvent(PhabricatorUser $actor) {
$app = id(new PhabricatorApplicationQuery())
->setViewer($actor)
->withClasses(array('PhabricatorCalendarApplication'))
->executeOne();
$view_default = PhabricatorCalendarEventDefaultViewCapability::CAPABILITY;
$edit_default = PhabricatorCalendarEventDefaultEditCapability::CAPABILITY;
$view_policy = $app->getPolicy($view_default);
$edit_policy = $app->getPolicy($edit_default);
$start = new DateTime('@'.PhabricatorTime::getNow());
$start->setTimeZone($actor->getTimeZone());
$start->setTime($start->format('H'), 0, 0);
$start->modify('+1 hour');
$end = id(clone $start)->modify('+1 hour');
$epoch_min = $start->format('U');
$epoch_max = $end->format('U');
$default_icon = 'fa-calendar';
return id(new PhabricatorCalendarEvent())
->setHostPHID($actor->getPHID())
->setIsCancelled(0)
->setIsAllDay(0)
->setIsStub(0)
->setIsRecurring(0)
->setRecurrenceFrequency(
array(
'rule' => self::FREQUENCY_WEEKLY,
))
->setIcon($default_icon)
->setViewPolicy($view_policy)
->setEditPolicy($edit_policy)
->setSpacePHID($actor->getDefaultSpacePHID())
->attachInvitees(array())
->setDateFrom($epoch_min)
->setDateTo($epoch_max)
->applyViewerTimezone($actor);
}
private function newChild(PhabricatorUser $actor, $sequence) {
if (!$this->isParentEvent()) {
throw new Exception(
pht(
'Unable to generate a new child event for an event which is not '.
'a recurring parent event!'));
}
$child = id(new self())
->setIsCancelled(0)
->setIsStub(0)
->setInstanceOfEventPHID($this->getPHID())
->setSequenceIndex($sequence)
->setIsRecurring(true)
->setRecurrenceFrequency($this->getRecurrenceFrequency())
->attachParentEvent($this);
return $child->copyFromParent($actor);
}
protected function readField($field) {
static $inherit = array(
'hostPHID' => true,
'isAllDay' => true,
'icon' => true,
'spacePHID' => true,
'viewPolicy' => true,
'editPolicy' => true,
'name' => true,
'description' => true,
);
// Read these fields from the parent event instead of this event. For
// example, we want any changes to the parent event's name to
if (isset($inherit[$field])) {
if ($this->getIsStub()) {
// TODO: This should be unconditional, but the execution order of
// CalendarEventQuery and applyViewerTimezone() are currently odd.
if ($this->parentEvent !== self::ATTACHABLE) {
return $this->getParentEvent()->readField($field);
}
}
}
return parent::readField($field);
}
public function copyFromParent(PhabricatorUser $actor) {
if (!$this->isChildEvent()) {
throw new Exception(
pht(
'Unable to copy from parent event: this is not a child event.'));
}
$parent = $this->getParentEvent();
$this
->setHostPHID($parent->getHostPHID())
->setIsAllDay($parent->getIsAllDay())
->setIcon($parent->getIcon())
->setSpacePHID($parent->getSpacePHID())
->setViewPolicy($parent->getViewPolicy())
->setEditPolicy($parent->getEditPolicy())
->setName($parent->getName())
->setDescription($parent->getDescription());
$frequency = $parent->getFrequencyUnit();
$modify_key = '+'.$this->getSequenceIndex().' '.$frequency;
$date = $parent->getDateFrom();
$date_time = PhabricatorTime::getDateTimeFromEpoch($date, $actor);
$date_time->modify($modify_key);
$date = $date_time->format('U');
$duration = $this->getDuration();
$this
->setDateFrom($date)
->setDateTo($date + $duration);
return $this;
}
public function newStub(PhabricatorUser $actor, $sequence) {
$stub = $this->newChild($actor, $sequence);
$stub->setIsStub(1);
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
$stub->save();
unset($unguarded);
$stub->applyViewerTimezone($actor);
return $stub;
}
public function newGhost(PhabricatorUser $actor, $sequence) {
$ghost = $this->newChild($actor, $sequence);
$ghost
->setIsGhostEvent(true)
->makeEphemeral();
$ghost->applyViewerTimezone($actor);
return $ghost;
}
public function getViewerDateFrom() {
if ($this->viewerDateFrom === null) {
throw new PhutilInvalidStateException('applyViewerTimezone');
}
return $this->viewerDateFrom;
}
public function getViewerDateTo() {
if ($this->viewerDateTo === null) {
throw new PhutilInvalidStateException('applyViewerTimezone');
}
return $this->viewerDateTo;
}
public function applyViewerTimezone(PhabricatorUser $viewer) {
if (!$this->getIsAllDay()) {
$this->viewerDateFrom = $this->getDateFrom();
$this->viewerDateTo = $this->getDateTo();
} else {
$zone = $viewer->getTimeZone();
$this->viewerDateFrom = $this->getDateEpochForTimeZone(
$this->getDateFrom(),
new DateTimeZone('UTC'),
'Y-m-d',
null,
$zone);
$this->viewerDateTo = $this->getDateEpochForTimeZone(
$this->getDateTo(),
new DateTimeZone('UTC'),
'Y-m-d 23:59:00',
null,
$zone);
}
return $this;
}
public function getDuration() {
return $this->getDateTo() - $this->getDateFrom();
}
private function getDateEpochForTimeZone(
$epoch,
$src_zone,
$format,
$adjust,
$dst_zone) {
$src = new DateTime('@'.$epoch);
$src->setTimeZone($src_zone);
if (strlen($adjust)) {
$adjust = ' '.$adjust;
}
$dst = new DateTime($src->format($format).$adjust, $dst_zone);
return $dst->format('U');
}
public function save() {
if (!$this->mailKey) {
$this->mailKey = Filesystem::readRandomCharacters(20);
}
return parent::save();
}
/**
* Get the event start epoch for evaluating invitee availability.
*
* When assessing availability, we pretend events start earlier than they
* really do. This allows us to mark users away for the entire duration of a
* series of back-to-back meetings, even if they don't strictly overlap.
*
* @return int Event start date for availability caches.
*/
public function getDateFromForCache() {
return ($this->getViewerDateFrom() - phutil_units('15 minutes in seconds'));
}
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_COLUMN_SCHEMA => array(
'name' => 'text',
'dateFrom' => 'epoch',
'dateTo' => 'epoch',
'description' => 'text',
'isCancelled' => 'bool',
'isAllDay' => 'bool',
'icon' => 'text32',
'mailKey' => 'bytes20',
'isRecurring' => 'bool',
'recurrenceEndDate' => 'epoch?',
'instanceOfEventPHID' => 'phid?',
'sequenceIndex' => 'uint32?',
'isStub' => 'bool',
),
self::CONFIG_KEY_SCHEMA => array(
'key_date' => array(
'columns' => array('dateFrom', 'dateTo'),
),
'key_instance' => array(
'columns' => array('instanceOfEventPHID', 'sequenceIndex'),
'unique' => true,
),
),
self::CONFIG_SERIALIZATION => array(
'recurrenceFrequency' => self::SERIALIZATION_JSON,
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhabricatorCalendarEventPHIDType::TYPECONST);
}
public function getMonogram() {
return 'E'.$this->getID();
}
public function getInvitees() {
return $this->assertAttached($this->invitees);
}
public function attachInvitees(array $invitees) {
$this->invitees = $invitees;
return $this;
}
public function getInviteePHIDsForEdit() {
$invitees = array();
foreach ($this->getInvitees() as $invitee) {
if ($invitee->isUninvited()) {
continue;
}
$invitees[] = $invitee->getInviteePHID();
}
return $invitees;
}
public function getUserInviteStatus($phid) {
$invitees = $this->getInvitees();
$invitees = mpull($invitees, null, 'getInviteePHID');
$invited = idx($invitees, $phid);
if (!$invited) {
return PhabricatorCalendarEventInvitee::STATUS_UNINVITED;
}
$invited = $invited->getStatus();
return $invited;
}
public function getIsUserAttending($phid) {
$attending_status = PhabricatorCalendarEventInvitee::STATUS_ATTENDING;
$old_status = $this->getUserInviteStatus($phid);
$is_attending = ($old_status == $attending_status);
return $is_attending;
}
public function getIsUserInvited($phid) {
$uninvited_status = PhabricatorCalendarEventInvitee::STATUS_UNINVITED;
$declined_status = PhabricatorCalendarEventInvitee::STATUS_DECLINED;
$status = $this->getUserInviteStatus($phid);
if ($status == $uninvited_status || $status == $declined_status) {
return false;
}
return true;
}
public function getIsGhostEvent() {
return $this->isGhostEvent;
}
public function setIsGhostEvent($is_ghost_event) {
$this->isGhostEvent = $is_ghost_event;
return $this;
}
public function getFrequencyRule() {
return idx($this->recurrenceFrequency, 'rule');
}
public function getFrequencyUnit() {
$frequency = $this->getFrequencyRule();
switch ($frequency) {
case 'daily':
return 'day';
case 'weekly':
return 'week';
case 'monthly':
return 'month';
case 'yearly':
return 'year';
default:
return 'day';
}
}
public function getURI() {
if ($this->getIsGhostEvent()) {
$base = $this->getParentEvent()->getURI();
$sequence = $this->getSequenceIndex();
return "{$base}/{$sequence}/";
}
return '/'.$this->getMonogram();
}
public function getParentEvent() {
return $this->assertAttached($this->parentEvent);
}
public function attachParentEvent($event) {
$this->parentEvent = $event;
return $this;
}
public function isParentEvent() {
return ($this->isRecurring && !$this->instanceOfEventPHID);
}
public function isChildEvent() {
return ($this->instanceOfEventPHID !== null);
}
public function isCancelledEvent() {
if ($this->getIsCancelled()) {
return true;
}
if ($this->isChildEvent()) {
if ($this->getParentEvent()->getIsCancelled()) {
return true;
}
}
return false;
}
- public function getDisplayDuration() {
- $seconds = $this->getDuration();
- $minutes = round($seconds / 60, 1);
- $hours = round($minutes / 60, 3);
- $days = round($hours / 24, 2);
+ public function renderEventDate(
+ PhabricatorUser $viewer,
+ $show_end) {
- $duration = '';
+ if ($show_end) {
+ $min_date = PhabricatorTime::getDateTimeFromEpoch(
+ $this->getViewerDateFrom(),
+ $viewer);
- if ($days >= 1) {
+ $max_date = PhabricatorTime::getDateTimeFromEpoch(
+ $this->getViewerDateTo(),
+ $viewer);
+
+ $min_day = $min_date->format('Y m d');
+ $max_day = $max_date->format('Y m d');
+
+ $show_end_date = ($min_day != $max_day);
+ } else {
+ $show_end_date = false;
+ }
+
+ $min_epoch = $this->getViewerDateFrom();
+ $max_epoch = $this->getViewerDateTo();
+
+ if ($this->getIsAllDay()) {
+ if ($show_end_date) {
+ return pht(
+ '%s - %s, All Day',
+ phabricator_date($min_epoch, $viewer),
+ phabricator_date($max_epoch, $viewer));
+ } else {
+ return pht(
+ '%s, All Day',
+ phabricator_date($min_epoch, $viewer));
+ }
+ } else if ($show_end_date) {
return pht(
- '%s day(s)',
- round($days, 1));
- } else if ($hours >= 1) {
+ '%s - %s',
+ phabricator_datetime($min_epoch, $viewer),
+ phabricator_datetime($max_epoch, $viewer));
+ } else if ($show_end) {
return pht(
- '%s hour(s)',
- round($hours, 1));
- } else if ($minutes >= 1) {
+ '%s - %s',
+ phabricator_datetime($min_epoch, $viewer),
+ phabricator_time($max_epoch, $viewer));
+ } else {
return pht(
- '%s minute(s)',
- round($minutes, 0));
+ '%s',
+ phabricator_datetime($min_epoch, $viewer));
}
}
+
/* -( Markup Interface )--------------------------------------------------- */
/**
* @task markup
*/
public function getMarkupFieldKey($field) {
$hash = PhabricatorHash::digest($this->getMarkupText($field));
$id = $this->getID();
return "calendar:T{$id}:{$field}:{$hash}";
}
/**
* @task markup
*/
public function getMarkupText($field) {
return $this->getDescription();
}
/**
* @task markup
*/
public function newMarkupEngine($field) {
return PhabricatorMarkupEngine::newCalendarMarkupEngine();
}
/**
* @task markup
*/
public function didMarkupText(
$field,
$output,
PhutilMarkupEngine $engine) {
return $output;
}
/**
* @task markup
*/
public function shouldUseMarkupCache($field) {
return (bool)$this->getID();
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return $this->getViewPolicy();
case PhabricatorPolicyCapability::CAN_EDIT:
return $this->getEditPolicy();
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
// The host of an event can always view and edit it.
$user_phid = $this->getHostPHID();
if ($user_phid) {
$viewer_phid = $viewer->getPHID();
if ($viewer_phid == $user_phid) {
return true;
}
}
if ($capability == PhabricatorPolicyCapability::CAN_VIEW) {
$status = $this->getUserInviteStatus($viewer->getPHID());
if ($status == PhabricatorCalendarEventInvitee::STATUS_INVITED ||
$status == PhabricatorCalendarEventInvitee::STATUS_ATTENDING ||
$status == PhabricatorCalendarEventInvitee::STATUS_DECLINED) {
return true;
}
}
return false;
}
public function describeAutomaticCapability($capability) {
return pht(
'The host of an event can always view and edit it. Users who are '.
'invited to an event can always view it.');
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new PhabricatorCalendarEventEditor();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new PhabricatorCalendarEventTransaction();
}
public function willRenderTimeline(
PhabricatorApplicationTransactionView $timeline,
AphrontRequest $request) {
return $timeline;
}
/* -( PhabricatorSubscribableInterface )----------------------------------- */
public function isAutomaticallySubscribed($phid) {
return ($phid == $this->getHostPHID());
}
/* -( PhabricatorTokenReceiverInterface )---------------------------------- */
public function getUsersToNotifyOfTokenGiven() {
return array($this->getHostPHID());
}
/* -( PhabricatorDestructibleInterface )----------------------------------- */
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {
$this->openTransaction();
$this->delete();
$this->saveTransaction();
}
/* -( PhabricatorSpacesInterface )----------------------------------------- */
public function getSpacePHID() {
return $this->spacePHID;
}
/* -( PhabricatorFulltextInterface )--------------------------------------- */
public function newFulltextEngine() {
return new PhabricatorCalendarEventFulltextEngine();
}
/* -( PhabricatorConduitResultInterface )---------------------------------- */
public function getFieldSpecificationsForConduit() {
return array(
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('name')
->setType('string')
->setDescription(pht('The name of the event.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('description')
->setType('string')
->setDescription(pht('The event description.')),
);
}
public function getFieldValuesForConduit() {
return array(
'name' => $this->getName(),
'description' => $this->getDescription(),
);
}
public function getConduitSearchAttachments() {
return array();
}
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Jul 2, 6:01 AM (1 d, 14 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
164723
Default Alt Text
(49 KB)

Event Timeline