Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2890750
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
19 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/applications/calendar/application/PhabricatorCalendarApplication.php b/src/applications/calendar/application/PhabricatorCalendarApplication.php
index 20e89425df..b5bdfaa1eb 100644
--- a/src/applications/calendar/application/PhabricatorCalendarApplication.php
+++ b/src/applications/calendar/application/PhabricatorCalendarApplication.php
@@ -1,97 +1,97 @@
<?php
final class PhabricatorCalendarApplication extends PhabricatorApplication {
public function getName() {
return pht('Calendar');
}
public function getShortDescription() {
return pht('Upcoming Events');
}
public function getFlavorText() {
return pht('Never miss an episode ever again.');
}
public function getBaseURI() {
return '/calendar/';
}
public function getFontIcon() {
return 'fa-calendar';
}
public function getTitleGlyph() {
// Unicode has a calendar character but it's in some distant code plane,
// use "keyboard" since it looks vaguely similar.
return "\xE2\x8C\xA8";
}
public function isPrototype() {
return true;
}
public function getRemarkupRules() {
return array(
new PhabricatorCalendarRemarkupRule(),
);
}
public function getRoutes() {
return array(
'/E(?P<id>[1-9]\d*)(?:/(?P<sequence>\d+))?'
=> 'PhabricatorCalendarEventViewController',
'/calendar/' => array(
'(?:query/(?P<queryKey>[^/]+)/(?:(?P<year>\d+)/'.
'(?P<month>\d+)/)?(?:(?P<day>\d+)/)?)?'
=> 'PhabricatorCalendarEventListController',
'icon/(?P<id>[1-9]\d*)/'
=> 'PhabricatorCalendarEventEditIconController',
'icon/'
=> 'PhabricatorCalendarEventEditIconController',
'event/' => array(
'create/'
=> 'PhabricatorCalendarEventEditController',
'edit/(?P<id>[1-9]\d*)/(?:(?P<sequence>\d+)/)?'
=> 'PhabricatorCalendarEventEditController',
'drag/(?P<id>[1-9]\d*)/'
=> 'PhabricatorCalendarEventDragController',
'cancel/(?P<id>[1-9]\d*)/(?:(?P<sequence>\d+)/)?'
=> 'PhabricatorCalendarEventCancelController',
'(?P<action>join|decline|accept)/(?P<id>[1-9]\d*)/'
=> 'PhabricatorCalendarEventJoinController',
- 'comment/(?P<id>[1-9]\d*)/'
+ 'comment/(?P<id>[1-9]\d*)/(?:(?P<sequence>\d+)/)?'
=> 'PhabricatorCalendarEventCommentController',
),
),
);
}
public function getQuickCreateItems(PhabricatorUser $viewer) {
$items = array();
$item = id(new PHUIListItemView())
->setName(pht('Calendar Event'))
->setIcon('fa-calendar')
->setHref($this->getBaseURI().'event/create/');
$items[] = $item;
return $items;
}
public function getMailCommandObjects() {
return array(
'event' => array(
'name' => pht('Email Commands: Events'),
'header' => pht('Interacting with Calendar Events'),
'object' => new PhabricatorCalendarEvent(),
'summary' => pht(
'This page documents the commands you can use to interact with '.
'events in Calendar. These commands work when creating new tasks '.
'via email and when replying to existing tasks.'),
),
);
}
}
diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventCommentController.php b/src/applications/calendar/controller/PhabricatorCalendarEventCommentController.php
index 4d31b020c4..a735ee5caa 100644
--- a/src/applications/calendar/controller/PhabricatorCalendarEventCommentController.php
+++ b/src/applications/calendar/controller/PhabricatorCalendarEventCommentController.php
@@ -1,69 +1,86 @@
<?php
final class PhabricatorCalendarEventCommentController
extends PhabricatorCalendarController {
private $id;
public function willProcessRequest(array $data) {
$this->id = idx($data, 'id');
}
public function handleRequest(AphrontRequest $request) {
- $user = $request->getUser();
-
if (!$request->isFormPost()) {
return new Aphront400Response();
}
+ $user = $request->getUser();
+ $is_preview = $request->isPreviewRequest();
+ $draft = PhabricatorDraft::buildFromRequest($request);
+
$event = id(new PhabricatorCalendarEventQuery())
->setViewer($user)
->withIDs(array($this->id))
->executeOne();
if (!$event) {
return new Aphront404Response();
}
- $is_preview = $request->isPreviewRequest();
- $draft = PhabricatorDraft::buildFromRequest($request);
+ $index = $request->getURIData('sequence');
+ if ($index && !$is_preview) {
+ $result = $this->getEventAtIndexForGhostPHID(
+ $user,
+ $event->getPHID(),
+ $index);
+
+ if ($result) {
+ $event = $result;
+ } else {
+ $event = $this->createEventFromGhost(
+ $user,
+ $event,
+ $index);
+ $event->applyViewerTimezone($user);
+ }
+ }
$view_uri = '/'.$event->getMonogram();
$xactions = array();
$xactions[] = id(new PhabricatorCalendarEventTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
->attachComment(
id(new PhabricatorCalendarEventTransactionComment())
->setContent($request->getStr('comment')));
$editor = id(new PhabricatorCalendarEventEditor())
->setActor($user)
->setContinueOnNoEffect($request->isContinueRequest())
->setContentSourceFromRequest($request)
->setIsPreview($is_preview);
try {
$xactions = $editor->applyTransactions($event, $xactions);
} catch (PhabricatorApplicationTransactionNoEffectException $ex) {
return id(new PhabricatorApplicationTransactionNoEffectResponse())
->setCancelURI($view_uri)
->setException($ex);
}
if ($draft) {
$draft->replaceOrDelete();
}
if ($request->isAjax() && $is_preview) {
return id(new PhabricatorApplicationTransactionResponse())
->setViewer($user)
->setTransactions($xactions)
->setIsPreview($is_preview);
} else {
return id(new AphrontRedirectResponse())
->setURI($view_uri);
}
}
}
diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php
index 0aea0c5607..dcc717d474 100644
--- a/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php
+++ b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php
@@ -1,382 +1,391 @@
<?php
final class PhabricatorCalendarEventViewController
extends PhabricatorCalendarController {
private $id;
public function shouldAllowPublic() {
return true;
}
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
-
$sequence = $request->getURIData('sequence');
+ $timeline = null;
+
$event = id(new PhabricatorCalendarEventQuery())
->setViewer($viewer)
->withIDs(array($this->id))
->executeOne();
if (!$event) {
return new Aphront404Response();
}
if ($sequence) {
$result = $this->getEventAtIndexForGhostPHID(
$viewer,
$event->getPHID(),
$sequence);
if ($result) {
$parent_event = $event;
$event = $result;
$event->attachParentEvent($parent_event);
return id(new AphrontRedirectResponse())
->setURI('/E'.$result->getID());
} else if ($sequence && $event->getIsRecurring()) {
$parent_event = $event;
$event = $event->generateNthGhost($sequence, $viewer);
$event->attachParentEvent($parent_event);
} else if ($sequence) {
return new Aphront404Response();
}
$title = $event->getMonogram().' ('.$sequence.')';
$page_title = $title.' '.$event->getName();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($title, '/'.$event->getMonogram().'/'.$sequence);
} else {
$title = 'E'.$event->getID();
$page_title = $title.' '.$event->getName();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($title, '/E'.$event->getID());
}
- $timeline = $this->buildTransactionTimeline(
- $event,
- new PhabricatorCalendarEventTransactionQuery());
+ if (!$event->getIsGhostEvent()) {
+ $timeline = $this->buildTransactionTimeline(
+ $event,
+ new PhabricatorCalendarEventTransactionQuery());
+ }
$header = $this->buildHeaderView($event);
$actions = $this->buildActionView($event);
$properties = $this->buildPropertyView($event);
$properties->setActionList($actions);
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$add_comment_header = $is_serious
? pht('Add Comment')
: pht('Add To Plate');
$draft = PhabricatorDraft::newFromUserAndKey($viewer, $event->getPHID());
+ if ($sequence) {
+ $comment_uri = $this->getApplicationURI(
+ '/event/comment/'.$event->getID().'/'.$sequence.'/');
+ } else {
+ $comment_uri = $this->getApplicationURI(
+ '/event/comment/'.$event->getID().'/');
+ }
$add_comment_form = id(new PhabricatorApplicationTransactionCommentView())
->setUser($viewer)
->setObjectPHID($event->getPHID())
->setDraft($draft)
->setHeaderText($add_comment_header)
- ->setAction(
- $this->getApplicationURI('/event/comment/'.$event->getID().'/'))
+ ->setAction($comment_uri)
->setSubmitButtonName(pht('Add Comment'));
return $this->buildApplicationPage(
array(
$crumbs,
$box,
$timeline,
$add_comment_form,
),
array(
'title' => $page_title,
));
}
private function buildHeaderView(PhabricatorCalendarEvent $event) {
$viewer = $this->getRequest()->getUser();
$id = $event->getID();
$is_cancelled = $event->getIsCancelled();
$icon = $is_cancelled ? ('fa-times') : ('fa-calendar');
$color = $is_cancelled ? ('grey') : ('green');
$status = $is_cancelled ? pht('Cancelled') : 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);
if ($is_invite_pending) {
$decline_button = id(new PHUIButtonView())
->setTag('a')
->setIcon(id(new PHUIIconView())
->setIconFont('fa-times grey'))
->setHref($this->getApplicationURI("/event/decline/{$id}/"))
->setWorkflow(true)
->setText(pht('Decline'));
$accept_button = id(new PHUIButtonView())
->setTag('a')
->setIcon(id(new PHUIIconView())
->setIconFont('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 buildActionView(PhabricatorCalendarEvent $event) {
$viewer = $this->getRequest()->getUser();
$id = $event->getID();
$is_cancelled = $event->getIsCancelled();
$is_attending = $event->getIsUserAttending($viewer->getPHID());
$actions = id(new PhabricatorActionListView())
->setObjectURI($this->getApplicationURI('event/'.$id.'/'))
->setUser($viewer)
->setObject($event);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$event,
PhabricatorPolicyCapability::CAN_EDIT);
$edit_label = false;
$edit_uri = false;
if ($event->getIsGhostEvent()) {
$index = $event->getSequenceIndex();
$edit_label = pht('Edit This Instance');
$edit_uri = "event/edit/{$id}/{$index}/";
} else if ($event->getIsRecurrenceException()) {
$edit_label = pht('Edit This Instance');
$edit_uri = "event/edit/{$id}/";
} else {
$edit_label = pht('Edit');
$edit_uri = "event/edit/{$id}/";
}
if ($edit_label && $edit_uri) {
$actions->addAction(
id(new PhabricatorActionView())
->setName($edit_label)
->setIcon('fa-pencil')
->setHref($this->getApplicationURI($edit_uri))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
}
if ($is_attending) {
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Decline Event'))
->setIcon('fa-user-times')
->setHref($this->getApplicationURI("event/join/{$id}/"))
->setWorkflow(true));
} else {
$actions->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}/");
if ($event->getIsGhostEvent()) {
$index = $event->getSequenceIndex();
$can_reinstate = $event->getIsParentCancelled();
$cancel_label = pht('Cancel This Instance');
$reinstate_label = pht('Reinstate This Instance');
$cancel_disabled = (!$can_edit || $can_reinstate);
$cancel_uri = $this->getApplicationURI("event/cancel/{$id}/{$index}/");
} else if ($event->getIsRecurrenceException()) {
$can_reinstate = $event->getIsParentCancelled();
$cancel_label = pht('Cancel This Instance');
$reinstate_label = pht('Reinstate This Instance');
$cancel_disabled = (!$can_edit || $can_reinstate);
} else if ($event->getIsRecurrenceParent()) {
$cancel_label = pht('Cancel Recurrence');
$reinstate_label = pht('Reinstate Recurrence');
$cancel_disabled = !$can_edit;
} else {
$cancel_label = pht('Cancel Event');
$reinstate_label = pht('Reinstate Event');
$cancel_disabled = !$can_edit;
}
if ($is_cancelled) {
$actions->addAction(
id(new PhabricatorActionView())
->setName($reinstate_label)
->setIcon('fa-plus')
->setHref($cancel_uri)
->setDisabled($cancel_disabled)
->setWorkflow(true));
} else {
$actions->addAction(
id(new PhabricatorActionView())
->setName($cancel_label)
->setIcon('fa-times')
->setHref($cancel_uri)
->setDisabled($cancel_disabled)
->setWorkflow(true));
}
return $actions;
}
private function buildPropertyView(PhabricatorCalendarEvent $event) {
$viewer = $this->getRequest()->getUser();
$properties = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($event);
if ($event->getIsAllDay()) {
$date_start = phabricator_date($event->getDateFrom(), $viewer);
$date_end = phabricator_date($event->getDateTo(), $viewer);
if ($date_start == $date_end) {
$properties->addProperty(
pht('Time'),
phabricator_date($event->getDateFrom(), $viewer));
} else {
$properties->addProperty(
pht('Starts'),
phabricator_date($event->getDateFrom(), $viewer));
$properties->addProperty(
pht('Ends'),
phabricator_date($event->getDateTo(), $viewer));
}
} else {
$properties->addProperty(
pht('Starts'),
phabricator_datetime($event->getDateFrom(), $viewer));
$properties->addProperty(
pht('Ends'),
phabricator_datetime($event->getDateTo(), $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->getUserPHID()));
$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();
$icon_display = PhabricatorCalendarIcon::renderIconForChooser(
$event->getIcon());
$properties->addProperty(
pht('Icon'),
$icon_display);
if (strlen($event->getDescription())) {
$description = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($event->getDescription()),
'default',
$viewer);
$properties->addSectionHeader(
pht('Description'),
PHUIPropertyListView::ICON_SUMMARY);
$properties->addTextContent($description);
}
return $properties;
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Jan 19, 14:02 (3 w, 2 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1125326
Default Alt Text
(19 KB)
Attached To
Mode
rP Phorge
Attached
Detach File
Event Timeline
Log In to Comment