diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php b/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php
index 378b27d5ce..2b215ccf1e 100644
--- a/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php
+++ b/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php
@@ -1,199 +1,218 @@
 <?php
 
 final class PhabricatorCalendarEventEditController
   extends PhabricatorCalendarController {
 
   private $id;
 
   public function willProcessRequest(array $data) {
     $this->id = idx($data, 'id');
   }
 
   public function isCreate() {
     return !$this->id;
   }
 
   public function processRequest() {
     $request = $this->getRequest();
     $user = $request->getUser();
     $error_name = true;
     $validation_exception = null;
 
 
     $start_time = id(new AphrontFormDateControl())
       ->setUser($user)
       ->setName('start')
       ->setLabel(pht('Start'))
       ->setInitialTime(AphrontFormDateControl::TIME_START_OF_DAY);
 
     $end_time = id(new AphrontFormDateControl())
       ->setUser($user)
       ->setName('end')
       ->setLabel(pht('End'))
       ->setInitialTime(AphrontFormDateControl::TIME_END_OF_DAY);
 
     if ($this->isCreate()) {
       $event = PhabricatorCalendarEvent::initializeNewCalendarEvent($user);
       $end_value = $end_time->readValueFromRequest($request);
       $start_value = $start_time->readValueFromRequest($request);
       $submit_label = pht('Create');
       $filter = 'event/create/';
       $page_title = pht('Create Event');
       $redirect = 'created';
+      $subscribers = array();
     } else {
       $event = id(new PhabricatorCalendarEventQuery())
         ->setViewer($user)
         ->withIDs(array($this->id))
         ->requireCapabilities(
           array(
             PhabricatorPolicyCapability::CAN_VIEW,
             PhabricatorPolicyCapability::CAN_EDIT,
           ))
         ->executeOne();
       if (!$event) {
         return new Aphront404Response();
       }
 
       $end_time->setValue($event->getDateTo());
       $start_time->setValue($event->getDateFrom());
       $submit_label = pht('Update');
       $filter       = 'event/edit/'.$event->getID().'/';
       $page_title   = pht('Update Event');
       $redirect     = 'updated';
+
+      $subscribers = PhabricatorSubscribersQuery::loadSubscribersForPHID(
+        $event->getPHID());
     }
 
     $errors = array();
     if ($request->isFormPost()) {
       $xactions = array();
       $name = $request->getStr('name');
       $type = $request->getInt('status');
       $start_value = $start_time->readValueFromRequest($request);
       $end_value = $end_time->readValueFromRequest($request);
       $description = $request->getStr('description');
+      $subscribers = $request->getArr('subscribers');
 
       if ($start_time->getError()) {
         $errors[] = pht('Invalid start time; reset to default.');
       }
       if ($end_time->getError()) {
         $errors[] = pht('Invalid end time; reset to default.');
       }
       if (!$errors) {
         $xactions[] = id(new PhabricatorCalendarEventTransaction())
           ->setTransactionType(
             PhabricatorCalendarEventTransaction::TYPE_NAME)
           ->setNewValue($name);
 
         $xactions[] = id(new PhabricatorCalendarEventTransaction())
           ->setTransactionType(
             PhabricatorCalendarEventTransaction::TYPE_START_DATE)
           ->setNewValue($start_value);
 
         $xactions[] = id(new PhabricatorCalendarEventTransaction())
           ->setTransactionType(
             PhabricatorCalendarEventTransaction::TYPE_END_DATE)
           ->setNewValue($end_value);
 
         $xactions[] = id(new PhabricatorCalendarEventTransaction())
           ->setTransactionType(
             PhabricatorCalendarEventTransaction::TYPE_STATUS)
           ->setNewValue($type);
 
+        $xactions[] = id(new PhabricatorCalendarEventTransaction())
+          ->setTransactionType(
+            PhabricatorTransactions::TYPE_SUBSCRIBERS)
+          ->setNewValue(array('=' => array_fuse($subscribers)));
+
         $xactions[] = id(new PhabricatorCalendarEventTransaction())
           ->setTransactionType(
             PhabricatorCalendarEventTransaction::TYPE_DESCRIPTION)
           ->setNewValue($description);
 
         $editor = id(new PhabricatorCalendarEventEditor())
           ->setActor($user)
           ->setContentSourceFromRequest($request)
           ->setContinueOnNoEffect(true);
 
         try {
           $xactions = $editor->applyTransactions($event, $xactions);
           $response = id(new AphrontRedirectResponse());
           return $response->setURI('/E'.$event->getID());
         } catch (PhabricatorApplicationTransactionValidationException $ex) {
           $validation_exception = $ex;
           $error_name = $ex
             ->getShortMessage(PhabricatorCalendarEventTransaction::TYPE_NAME);
         }
       }
     }
 
     $error_view = null;
     if ($errors) {
       $error_view = id(new PHUIInfoView())
         ->setTitle(pht('Status can not be set!'))
         ->setErrors($errors);
     }
 
     $name = id(new AphrontFormTextControl())
       ->setLabel(pht('Name'))
       ->setName('name')
       ->setValue($event->getName())
       ->setError($error_name);
 
     $status_select = id(new AphrontFormSelectControl())
       ->setLabel(pht('Status'))
       ->setName('status')
       ->setValue($event->getStatus())
       ->setOptions($event->getStatusOptions());
 
     $description = id(new AphrontFormTextAreaControl())
       ->setLabel(pht('Description'))
       ->setName('description')
       ->setValue($event->getDescription());
 
+    $subscribers = id(new AphrontFormTokenizerControl())
+      ->setLabel(pht('Subscribers'))
+      ->setName('subscribers')
+      ->setValue($subscribers)
+      ->setUser($user)
+      ->setDatasource(new PhabricatorMetaMTAMailableDatasource());
+
+
     $form = id(new AphrontFormView())
       ->setUser($user)
       ->appendChild($name)
       ->appendChild($status_select)
       ->appendChild($start_time)
       ->appendChild($end_time)
+      ->appendControl($subscribers)
       ->appendChild($description);
 
     $submit = id(new AphrontFormSubmitControl())
       ->setValue($submit_label);
     if ($this->isCreate()) {
       $submit->addCancelButton($this->getApplicationURI());
     } else {
       $submit->addCancelButton('/E'.$event->getID());
     }
 
     $form->appendChild($submit);
 
     $form_box = id(new PHUIObjectBoxView())
       ->setHeaderText($page_title)
       ->setFormErrors($errors)
       ->setForm($form);
 
     $nav = $this->buildSideNavView($event);
     $nav->selectFilter($filter);
 
     $crumbs = $this->buildApplicationCrumbs();
 
     if (!$this->isCreate()) {
       $crumbs->addTextCrumb('E'.$event->getId(), '/E'.$event->getId());
     }
 
     $crumbs->addTextCrumb($page_title);
 
     $object_box = id(new PHUIObjectBoxView())
       ->setHeaderText($page_title)
       ->setValidationException($validation_exception)
       ->appendChild($form);
 
     $nav->appendChild(
       array(
         $crumbs,
         $object_box,
       ));
 
     return $this->buildApplicationPage(
       $nav,
       array(
         'title' => $page_title,
       ));
   }
 
 }
diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php
index 8531ab9236..e4b2cfcdb2 100644
--- a/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php
+++ b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php
@@ -1,121 +1,124 @@
 <?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();
 
     $event = id(new PhabricatorCalendarEventQuery())
       ->setViewer($viewer)
       ->withIDs(array($this->id))
       ->executeOne();
     if (!$event) {
       return new Aphront404Response();
     }
 
     $title = 'E'.$event->getID();
     $page_title = $title.' '.$event->getName();
     $crumbs = $this->buildApplicationCrumbs();
     $crumbs->addTextCrumb($title, '/E'.$event->getID());
 
     $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);
 
     return $this->buildApplicationPage(
       array(
         $crumbs,
         $box,
         $timeline,
       ),
       array(
         'title' => $page_title,
       ));
   }
 
   private function buildHeaderView(PhabricatorCalendarEvent $event) {
     $viewer = $this->getRequest()->getUser();
 
     return id(new PHUIHeaderView())
       ->setUser($viewer)
       ->setHeader($event->getName())
       ->setPolicyObject($event);
   }
 
   private function buildActionView(PhabricatorCalendarEvent $event) {
     $viewer = $this->getRequest()->getUser();
     $id = $event->getID();
 
     $actions = id(new PhabricatorActionListView())
       ->setObjectURI($this->getApplicationURI('event/'.$id.'/'))
-      ->setUser($viewer);
+      ->setUser($viewer)
+      ->setObject($event);
 
     $can_edit = PhabricatorPolicyFilter::hasCapability(
       $viewer,
       $event,
       PhabricatorPolicyCapability::CAN_EDIT);
 
     $actions->addAction(
       id(new PhabricatorActionView())
         ->setName(pht('Edit Event'))
         ->setIcon('fa-pencil')
         ->setHref($this->getApplicationURI("event/edit/{$id}/"))
         ->setDisabled(!$can_edit)
         ->setWorkflow(!$can_edit));
 
     $actions->addAction(
       id(new PhabricatorActionView())
         ->setName(pht('Cancel Event'))
         ->setIcon('fa-times')
         ->setHref($this->getApplicationURI("event/delete/{$id}/"))
         ->setDisabled(!$can_edit)
         ->setWorkflow(true));
 
     return $actions;
   }
 
   private function buildPropertyView(PhabricatorCalendarEvent $event) {
     $viewer = $this->getRequest()->getUser();
 
     $properties = id(new PHUIPropertyListView())
       ->setUser($viewer)
       ->setObject($event);
 
     $properties->addProperty(
       pht('Starts'),
       phabricator_datetime($event->getDateFrom(), $viewer));
 
     $properties->addProperty(
       pht('Ends'),
       phabricator_datetime($event->getDateTo(), $viewer));
 
+    $properties->invokeWillRenderEvent();
+
     $properties->addSectionHeader(
       pht('Description'),
       PHUIPropertyListView::ICON_SUMMARY);
     $properties->addTextContent($event->getDescription());
 
     return $properties;
   }
 
 }
diff --git a/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php b/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php
index b865bb85ac..cc6e4410f9 100644
--- a/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php
+++ b/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php
@@ -1,145 +1,147 @@
 <?php
 
 final class PhabricatorCalendarEventEditor
   extends PhabricatorApplicationTransactionEditor {
 
   public function getEditorApplicationClass() {
     return 'PhabricatorCalendarApplication';
   }
 
   public function getEditorObjectsDescription() {
     return pht('Calendar');
   }
 
   public function getTransactionTypes() {
     $types = parent::getTransactionTypes();
 
     $types[] = PhabricatorCalendarEventTransaction::TYPE_NAME;
     $types[] = PhabricatorCalendarEventTransaction::TYPE_START_DATE;
     $types[] = PhabricatorCalendarEventTransaction::TYPE_END_DATE;
     $types[] = PhabricatorCalendarEventTransaction::TYPE_STATUS;
     $types[] = PhabricatorCalendarEventTransaction::TYPE_DESCRIPTION;
 
     $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
     $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
 
     return $types;
   }
 
   protected function getCustomTransactionOldValue(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
     switch ($xaction->getTransactionType()) {
       case PhabricatorCalendarEventTransaction::TYPE_NAME:
         return $object->getName();
       case PhabricatorCalendarEventTransaction::TYPE_START_DATE:
         return $object->getDateFrom();
       case PhabricatorCalendarEventTransaction::TYPE_END_DATE:
         return $object->getDateTo();
       case PhabricatorCalendarEventTransaction::TYPE_STATUS:
         $status = $object->getStatus();
         if ($status === null) {
           return null;
         }
         return (int)$status;
       case PhabricatorCalendarEventTransaction::TYPE_DESCRIPTION:
         return $object->getDescription();
     }
 
     return parent::getCustomTransactionOldValue($object, $xaction);
   }
 
   protected function getCustomTransactionNewValue(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     switch ($xaction->getTransactionType()) {
       case PhabricatorCalendarEventTransaction::TYPE_NAME:
       case PhabricatorCalendarEventTransaction::TYPE_START_DATE:
       case PhabricatorCalendarEventTransaction::TYPE_END_DATE:
       case PhabricatorCalendarEventTransaction::TYPE_DESCRIPTION:
         return $xaction->getNewValue();
       case PhabricatorCalendarEventTransaction::TYPE_STATUS:
         return (int)$xaction->getNewValue();
     }
 
     return parent::getCustomTransactionNewValue($object, $xaction);
   }
 
   protected function applyCustomInternalTransaction(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     switch ($xaction->getTransactionType()) {
       case PhabricatorCalendarEventTransaction::TYPE_NAME:
         $object->setName($xaction->getNewValue());
         return;
       case PhabricatorCalendarEventTransaction::TYPE_START_DATE:
         $object->setDateFrom($xaction->getNewValue());
         return;
       case PhabricatorCalendarEventTransaction::TYPE_END_DATE:
         $object->setDateTo($xaction->getNewValue());
         return;
       case PhabricatorCalendarEventTransaction::TYPE_STATUS:
         $object->setStatus($xaction->getNewValue());
         return;
       case PhabricatorCalendarEventTransaction::TYPE_DESCRIPTION:
         $object->setDescription($xaction->getNewValue());
         return;
       case PhabricatorTransactions::TYPE_VIEW_POLICY:
       case PhabricatorTransactions::TYPE_EDIT_POLICY:
       case PhabricatorTransactions::TYPE_EDGE:
+      case PhabricatorTransactions::TYPE_SUBSCRIBERS:
         return;
     }
 
     return parent::applyCustomInternalTransaction($object, $xaction);
   }
 
   protected function applyCustomExternalTransaction(
     PhabricatorLiskDAO $object,
     PhabricatorApplicationTransaction $xaction) {
 
     switch ($xaction->getTransactionType()) {
       case PhabricatorCalendarEventTransaction::TYPE_NAME:
       case PhabricatorCalendarEventTransaction::TYPE_START_DATE:
       case PhabricatorCalendarEventTransaction::TYPE_END_DATE:
       case PhabricatorCalendarEventTransaction::TYPE_STATUS:
       case PhabricatorCalendarEventTransaction::TYPE_DESCRIPTION:
       case PhabricatorTransactions::TYPE_VIEW_POLICY:
       case PhabricatorTransactions::TYPE_EDIT_POLICY:
       case PhabricatorTransactions::TYPE_EDGE:
+      case PhabricatorTransactions::TYPE_SUBSCRIBERS:
         return;
     }
 
     return parent::applyCustomExternalTransaction($object, $xaction);
   }
 
   protected function validateTransaction(
     PhabricatorLiskDAO $object,
     $type,
     array $xactions) {
 
     $errors = parent::validateTransaction($object, $type, $xactions);
 
     switch ($type) {
       case PhabricatorCalendarEventTransaction::TYPE_NAME:
         $missing = $this->validateIsEmptyTextField(
           $object->getName(),
           $xactions);
 
         if ($missing) {
           $error = new PhabricatorApplicationTransactionValidationError(
             $type,
             pht('Required'),
             pht('Event name is required.'),
             nonempty(last($xactions), null));
 
           $error->setIsMissingFieldError(true);
           $errors[] = $error;
         }
         break;
     }
 
     return $errors;
   }
 }
diff --git a/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php b/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php
index 936bd023e1..820cd3d621 100644
--- a/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php
+++ b/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php
@@ -1,211 +1,211 @@
 <?php
 
 final class PhabricatorCalendarEventSearchEngine
   extends PhabricatorApplicationSearchEngine {
 
   public function getResultTypeDescription() {
     return pht('Calendar Events');
   }
 
   public function getApplicationClassName() {
     return 'PhabricatorCalendarApplication';
   }
 
   public function buildSavedQueryFromRequest(AphrontRequest $request) {
     $saved = new PhabricatorSavedQuery();
 
     $saved->setParameter(
       'rangeStart',
       $this->readDateFromRequest($request, 'rangeStart'));
 
     $saved->setParameter(
       'rangeEnd',
       $this->readDateFromRequest($request, 'rangeEnd'));
 
     $saved->setParameter(
       'upcoming',
       $this->readBoolFromRequest($request, 'upcoming'));
 
     $saved->setParameter(
       'invitedPHIDs',
       $this->readUsersFromRequest($request, 'invited'));
 
     $saved->setParameter(
       'creatorPHIDs',
       $this->readUsersFromRequest($request, 'creators'));
 
     return $saved;
   }
 
   public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
     $query = id(new PhabricatorCalendarEventQuery());
 
     $min_range = null;
     $max_range = null;
 
     if ($saved->getParameter('rangeStart')) {
       $min_range = $saved->getParameter('rangeStart');
     }
 
     if ($saved->getParameter('rangeEnd')) {
       $max_range = $saved->getParameter('rangeEnd');
     }
 
     if ($saved->getParameter('upcoming')) {
       if ($min_range) {
         $min_range = max(time(), $min_range);
       } else {
         $min_range = time();
       }
     }
 
     if ($min_range || $max_range) {
       $query->withDateRange($min_range, $max_range);
     }
 
     $invited_phids = $saved->getParameter('invitedPHIDs');
     if ($invited_phids) {
       $query->withInvitedPHIDs($invited_phids);
     }
 
     $creator_phids = $saved->getParameter('creatorPHIDs');
     if ($creator_phids) {
       $query->withCreatorPHIDs($creator_phids);
     }
 
     return $query;
   }
 
   public function buildSearchForm(
     AphrontFormView $form,
     PhabricatorSavedQuery $saved) {
 
     $range_start = $saved->getParameter('rangeStart');
     $range_end = $saved->getParameter('rangeEnd');
     $upcoming = $saved->getParameter('upcoming');
 
     $invited_phids = $saved->getParameter('invitedPHIDs', array());
     $creator_phids = $saved->getParameter('creatorPHIDs', array());
 
     $form
       ->appendControl(
         id(new AphrontFormTokenizerControl())
           ->setDatasource(new PhabricatorPeopleDatasource())
           ->setName('creators')
           ->setLabel(pht('Created By'))
           ->setValue($creator_phids))
       ->appendControl(
         id(new AphrontFormTokenizerControl())
           ->setDatasource(new PhabricatorPeopleDatasource())
           ->setName('invited')
           ->setLabel(pht('Invited'))
           ->setValue($invited_phids))
       ->appendChild(
         id(new AphrontFormDateControl())
           ->setLabel(pht('Occurs After'))
           ->setUser($this->requireViewer())
           ->setName('rangeStart')
           ->setAllowNull(true)
           ->setValue($range_start))
       ->appendChild(
         id(new AphrontFormDateControl())
           ->setLabel(pht('Occurs Before'))
           ->setUser($this->requireViewer())
           ->setName('rangeEnd')
           ->setAllowNull(true)
           ->setValue($range_end))
       ->appendChild(
         id(new AphrontFormCheckboxControl())
           ->addCheckbox(
             'upcoming',
             1,
             pht('Show only upcoming events.'),
             $upcoming));
   }
 
   protected function getURI($path) {
     return '/calendar/event/'.$path;
   }
 
   protected function getBuiltinQueryNames() {
     $names = array(
       'upcoming' => pht('Upcoming Events'),
       'all'      => pht('All Events'),
     );
 
     return $names;
   }
 
   public function buildSavedQueryFromBuiltin($query_key) {
     $query = $this->newSavedQuery();
     $query->setQueryKey($query_key);
 
     switch ($query_key) {
       case 'upcoming':
         return $query->setParameter('upcoming', true);
       case 'all':
         return $query;
     }
 
     return parent::buildSavedQueryFromBuiltin($query_key);
   }
 
   protected function getRequiredHandlePHIDsForResultList(
     array $objects,
     PhabricatorSavedQuery $query) {
     $phids = array();
     foreach ($objects as $event) {
       $phids[$event->getUserPHID()] = 1;
     }
     return array_keys($phids);
   }
 
   protected function renderResultList(
     array $events,
     PhabricatorSavedQuery $query,
     array $handles) {
     assert_instances_of($events, 'PhabricatorCalendarEvent');
 
     $viewer = $this->requireViewer();
 
     $list = new PHUIObjectItemListView();
     foreach ($events as $event) {
       if ($event->getUserPHID() == $viewer->getPHID()) {
-        $href = $this->getApplicationURI('/event/edit/'.$event->getID().'/');
+        $href = '/E'.$event->getID();
       } else {
         $from  = $event->getDateFrom();
         $month = phabricator_format_local_time($from, $viewer, 'm');
         $year  = phabricator_format_local_time($from, $viewer, 'Y');
         $uri   = new PhutilURI($this->getApplicationURI());
         $uri->setQueryParams(
           array(
             'month' => $month,
             'year'  => $year,
           ));
         $href = (string) $uri;
       }
       $from = phabricator_datetime($event->getDateFrom(), $viewer);
       $to   = phabricator_datetime($event->getDateTo(), $viewer);
       $creator_handle = $handles[$event->getUserPHID()];
 
       $color = ($event->getStatus() == PhabricatorCalendarEvent::STATUS_AWAY)
         ? 'red'
         : 'yellow';
 
       $item = id(new PHUIObjectItemView())
         ->setHeader($event->getTerseSummary($viewer))
         ->setHref($href)
         ->setBarColor($color)
         ->addByline(pht('Creator: %s', $creator_handle->renderLink()))
         ->addAttribute(pht('From %s to %s', $from, $to))
         ->addAttribute(id(new PhutilUTF8StringTruncator())
           ->setMaximumGlyphs(64)
           ->truncateString($event->getDescription()));
 
       $list->addItem($item);
     }
 
     return $list;
   }
 
 }
diff --git a/src/applications/calendar/storage/PhabricatorCalendarEvent.php b/src/applications/calendar/storage/PhabricatorCalendarEvent.php
index 8d6ab918d2..cb44fdbb22 100644
--- a/src/applications/calendar/storage/PhabricatorCalendarEvent.php
+++ b/src/applications/calendar/storage/PhabricatorCalendarEvent.php
@@ -1,222 +1,237 @@
 <?php
 
 final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
   implements PhabricatorPolicyInterface,
   PhabricatorMarkupInterface,
-  PhabricatorApplicationTransactionInterface {
+  PhabricatorApplicationTransactionInterface,
+  PhabricatorSubscribableInterface {
 
   protected $name;
   protected $userPHID;
   protected $dateFrom;
   protected $dateTo;
   protected $status;
   protected $description;
 
   const STATUS_AWAY = 1;
   const STATUS_SPORADIC = 2;
 
   public static function initializeNewCalendarEvent(PhabricatorUser $actor) {
     $app = id(new PhabricatorApplicationQuery())
       ->setViewer($actor)
       ->withClasses(array('PhabricatorCalendarApplication'))
       ->executeOne();
 
     return id(new PhabricatorCalendarEvent())
       ->setUserPHID($actor->getPHID());
   }
 
   private static $statusTexts = array(
     self::STATUS_AWAY => 'away',
     self::STATUS_SPORADIC => 'sporadic',
   );
 
+  public function setTextStatus($status) {
+    $statuses = array_flip(self::$statusTexts);
+    return $this->setStatus($statuses[$status]);
+  }
+
   public function getTextStatus() {
     return self::$statusTexts[$this->status];
   }
 
   public function getStatusOptions() {
     return array(
       self::STATUS_AWAY     => pht('Away'),
       self::STATUS_SPORADIC => pht('Sporadic'),
     );
   }
 
   public function getHumanStatus() {
     $options = $this->getStatusOptions();
     return $options[$this->status];
   }
 
   protected function getConfiguration() {
     return array(
       self::CONFIG_AUX_PHID => true,
       self::CONFIG_COLUMN_SCHEMA => array(
         'name' => 'text',
         'dateFrom' => 'epoch',
         'dateTo' => 'epoch',
         'status' => 'uint32',
         'description' => 'text',
       ),
       self::CONFIG_KEY_SCHEMA => array(
         'userPHID_dateFrom' => array(
           'columns' => array('userPHID', 'dateTo'),
         ),
       ),
     ) + parent::getConfiguration();
   }
 
   public function generatePHID() {
     return PhabricatorPHID::generateNewPHID(
       PhabricatorCalendarEventPHIDType::TYPECONST);
   }
 
   public function getMonogram() {
     return 'E'.$this->getID();
   }
 
   public function getTerseSummary(PhabricatorUser $viewer) {
     $until = phabricator_date($this->dateTo, $viewer);
     if ($this->status == PhabricatorCalendarEvent::STATUS_SPORADIC) {
       return pht('Sporadic until %s', $until);
     } else {
       return pht('Away until %s', $until);
     }
   }
 
-  public function setTextStatus($status) {
-    $statuses = array_flip(self::$statusTexts);
-    return $this->setStatus($statuses[$status]);
+  public static function getNameForStatus($value) {
+    switch ($value) {
+      case self::STATUS_AWAY:
+        return pht('Away');
+      case self::STATUS_SPORADIC:
+        return pht('Sporadic');
+      default:
+        return pht('Unknown');
+    }
   }
 
   public function loadCurrentStatuses($user_phids) {
     if (!$user_phids) {
       return array();
     }
 
     $statuses = $this->loadAllWhere(
       'userPHID IN (%Ls) AND UNIX_TIMESTAMP() BETWEEN dateFrom AND dateTo',
       $user_phids);
 
     return mpull($statuses, null, 'getUserPHID');
   }
 
-  public static function getNameForStatus($value) {
-    switch ($value) {
-      case self::STATUS_AWAY:
-        return pht('Away');
-      case self::STATUS_SPORADIC:
-        return pht('Sporadic');
-      default:
-        return pht('Unknown');
-    }
-  }
-
   /**
    * Validates data and throws exceptions for non-sensical status
    * windows
    */
   public function save() {
 
     if ($this->getDateTo() <= $this->getDateFrom()) {
       throw new PhabricatorCalendarEventInvalidEpochException();
     }
 
     return parent::save();
   }
 
 /* -(  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 PhabricatorPolicies::getMostOpenPolicy();
       case PhabricatorPolicyCapability::CAN_EDIT:
         return $this->getUserPHID();
     }
   }
 
   public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
     return false;
   }
 
   public function describeAutomaticCapability($capability) {
     return null;
   }
 
 /* -(  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->getUserPHID());
+  }
+
+  public function shouldShowSubscribersProperty() {
+    return true;
+  }
+
+  public function shouldAllowSubscription($phid) {
+    return true;
+  }
 }