Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15456943
D12833.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
17 KB
Referenced Files
None
Subscribers
None
D12833.diff
View Options
diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -117,7 +117,7 @@
'rsrc/css/font/font-source-sans-pro.css' => '8906c07b',
'rsrc/css/font/phui-font-icon-base.css' => '3dad2ae3',
'rsrc/css/layout/phabricator-filetree-view.css' => 'fccf9f82',
- 'rsrc/css/layout/phabricator-hovercard-view.css' => '44394670',
+ 'rsrc/css/layout/phabricator-hovercard-view.css' => 'dd9121a9',
'rsrc/css/layout/phabricator-side-menu-view.css' => 'c1db9e9c',
'rsrc/css/layout/phabricator-source-code-view.css' => '2ceee894',
'rsrc/css/phui/calendar/phui-calendar-day.css' => '38891735',
@@ -713,7 +713,7 @@
'phabricator-filetree-view-css' => 'fccf9f82',
'phabricator-flag-css' => '5337623f',
'phabricator-hovercard' => '14ac66f5',
- 'phabricator-hovercard-view-css' => '44394670',
+ 'phabricator-hovercard-view-css' => 'dd9121a9',
'phabricator-keyboard-shortcut' => '1ae869f2',
'phabricator-keyboard-shortcut-manager' => 'c1700f6f',
'phabricator-main-menu-view' => '663e3810',
diff --git a/src/applications/calendar/storage/PhabricatorCalendarEvent.php b/src/applications/calendar/storage/PhabricatorCalendarEvent.php
--- a/src/applications/calendar/storage/PhabricatorCalendarEvent.php
+++ b/src/applications/calendar/storage/PhabricatorCalendarEvent.php
@@ -218,18 +218,6 @@
}
}
- 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 function getInvitees() {
return $this->assertAttached($this->invitees);
}
diff --git a/src/applications/calendar/storage/PhabricatorCalendarEventInvitee.php b/src/applications/calendar/storage/PhabricatorCalendarEventInvitee.php
--- a/src/applications/calendar/storage/PhabricatorCalendarEventInvitee.php
+++ b/src/applications/calendar/storage/PhabricatorCalendarEventInvitee.php
@@ -38,6 +38,10 @@
) + parent::getConfiguration();
}
+ public function isAttending() {
+ return ($this->getStatus() == self::STATUS_ATTENDING);
+ }
+
public function isUninvited() {
if ($this->getStatus() == self::STATUS_UNINVITED) {
return true;
diff --git a/src/applications/people/conduit/UserConduitAPIMethod.php b/src/applications/people/conduit/UserConduitAPIMethod.php
--- a/src/applications/people/conduit/UserConduitAPIMethod.php
+++ b/src/applications/people/conduit/UserConduitAPIMethod.php
@@ -6,9 +6,7 @@
return PhabricatorApplication::getByClass('PhabricatorPeopleApplication');
}
- protected function buildUserInformationDictionary(
- PhabricatorUser $user,
- PhabricatorCalendarEvent $current_status = null) {
+ protected function buildUserInformationDictionary(PhabricatorUser $user) {
$roles = array();
if ($user->getIsDisabled()) {
@@ -48,9 +46,12 @@
'roles' => $roles,
);
- if ($current_status) {
- $return['currentStatus'] = $current_status->getTextStatus();
- $return['currentStatusUntil'] = $current_status->getDateTo();
+ // TODO: Modernize this once we have a more long-term view of what the
+ // data looks like.
+ $until = $user->getAwayUntil();
+ if ($until) {
+ $return['currentStatus'] = 'away';
+ $return['currentStatusUntil'] = $until;
}
return $return;
diff --git a/src/applications/people/conduit/UserQueryConduitAPIMethod.php b/src/applications/people/conduit/UserQueryConduitAPIMethod.php
--- a/src/applications/people/conduit/UserQueryConduitAPIMethod.php
+++ b/src/applications/people/conduit/UserQueryConduitAPIMethod.php
@@ -43,7 +43,8 @@
$query = id(new PhabricatorPeopleQuery())
->setViewer($request->getUser())
- ->needProfileImage(true);
+ ->needProfileImage(true)
+ ->needAvailability(true);
if ($usernames) {
$query->withUsernames($usernames);
@@ -68,14 +69,9 @@
}
$users = $query->execute();
- $statuses = id(new PhabricatorCalendarEvent())->loadCurrentStatuses(
- mpull($users, 'getPHID'));
-
$results = array();
foreach ($users as $user) {
- $results[] = $this->buildUserInformationDictionary(
- $user,
- idx($statuses, $user->getPHID()));
+ $results[] = $this->buildUserInformationDictionary($user);
}
return $results;
}
diff --git a/src/applications/people/controller/PhabricatorPeopleProfileController.php b/src/applications/people/controller/PhabricatorPeopleProfileController.php
--- a/src/applications/people/controller/PhabricatorPeopleProfileController.php
+++ b/src/applications/people/controller/PhabricatorPeopleProfileController.php
@@ -20,6 +20,7 @@
->setViewer($viewer)
->withUsernames(array($this->username))
->needProfileImage(true)
+ ->needAvailability(true)
->executeOne();
if (!$user) {
return new Aphront404Response();
diff --git a/src/applications/people/customfield/PhabricatorUserStatusField.php b/src/applications/people/customfield/PhabricatorUserStatusField.php
--- a/src/applications/people/customfield/PhabricatorUserStatusField.php
+++ b/src/applications/people/customfield/PhabricatorUserStatusField.php
@@ -29,16 +29,7 @@
public function renderPropertyViewValue(array $handles) {
$user = $this->getObject();
$viewer = $this->requireViewer();
-
- $statuses = id(new PhabricatorCalendarEvent())
- ->loadCurrentStatuses(array($user->getPHID()));
- if (!$statuses) {
- return pht('Available');
- }
-
- $status = head($statuses);
-
- return $status->getTerseSummary($viewer);
+ return $user->getAvailabilityDescription($viewer);
}
}
diff --git a/src/applications/people/event/PhabricatorPeopleHovercardEventListener.php b/src/applications/people/event/PhabricatorPeopleHovercardEventListener.php
--- a/src/applications/people/event/PhabricatorPeopleHovercardEventListener.php
+++ b/src/applications/people/event/PhabricatorPeopleHovercardEventListener.php
@@ -26,12 +26,15 @@
return;
}
- $profile = $user->loadUserProfile();
+ // Reload to get availability.
+ $user = id(new PhabricatorPeopleQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($user->getID()))
+ ->needAvailability(true)
+ ->executeOne();
$hovercard->setTitle($user->getUsername());
- $hovercard->setDetail(pht('%s - %s.', $user->getRealname(),
- nonempty($profile->getTitle(),
- pht('No title was found befitting of this rare specimen'))));
+ $hovercard->setDetail($user->getRealName());
if ($user->getIsDisabled()) {
$hovercard->addField(pht('Account'), pht('Disabled'));
@@ -40,29 +43,14 @@
} else if (PhabricatorApplication::isClassInstalledForViewer(
'PhabricatorCalendarApplication',
$viewer)) {
- $statuses = id(new PhabricatorCalendarEvent())->loadCurrentStatuses(
- array($user->getPHID()));
- if ($statuses) {
- $current_status = reset($statuses);
- $dateto = phabricator_datetime($current_status->getDateTo(), $user);
- $hovercard->addField(pht('Status'),
- $current_status->getDescription());
- $hovercard->addField(pht('Until'),
- $dateto);
- } else {
- $hovercard->addField(pht('Status'), pht('Available'));
- }
+ $hovercard->addField(
+ pht('Status'),
+ $user->getAvailabilityDescription($viewer));
}
- $hovercard->addField(pht('User since'),
- phabricator_date($user->getDateCreated(), $user));
-
- if ($profile->getBlurb()) {
- $hovercard->addField(pht('Blurb'),
- id(new PhutilUTF8StringTruncator())
- ->setMaximumGlyphs(120)
- ->truncateString($profile->getBlurb()));
- }
+ $hovercard->addField(
+ pht('User Since'),
+ phabricator_date($user->getDateCreated(), $viewer));
$event->setValue('hovercard', $hovercard);
}
diff --git a/src/applications/people/markup/PhabricatorMentionRemarkupRule.php b/src/applications/people/markup/PhabricatorMentionRemarkupRule.php
--- a/src/applications/people/markup/PhabricatorMentionRemarkupRule.php
+++ b/src/applications/people/markup/PhabricatorMentionRemarkupRule.php
@@ -72,16 +72,9 @@
$users = id(new PhabricatorPeopleQuery())
->setViewer($this->getEngine()->getConfig('viewer'))
->withUsernames($usernames)
+ ->needAvailability(true)
->execute();
- if ($users) {
- $user_statuses = id(new PhabricatorCalendarEvent())
- ->loadCurrentStatuses(mpull($users, 'getPHID'));
- $user_statuses = mpull($user_statuses, null, 'getUserPHID');
- } else {
- $user_statuses = array();
- }
-
$actual_users = array();
$mentioned_key = self::KEY_MENTIONED;
@@ -156,14 +149,8 @@
if (!$user->isUserActivated()) {
$tag->setDotColor(PHUITagView::COLOR_GREY);
} else {
- $status = idx($user_statuses, $user->getPHID());
- if ($status) {
- $status = $status->getStatus();
- if ($status == PhabricatorCalendarEvent::STATUS_AWAY) {
- $tag->setDotColor(PHUITagView::COLOR_RED);
- } else if ($status == PhabricatorCalendarEvent::STATUS_AWAY) {
- $tag->setDotColor(PHUITagView::COLOR_ORANGE);
- }
+ if ($user->getAwayUntil()) {
+ $tag->setDotColor(PHUITagView::COLOR_RED);
}
}
}
diff --git a/src/applications/people/phid/PhabricatorPeopleUserPHIDType.php b/src/applications/people/phid/PhabricatorPeopleUserPHIDType.php
--- a/src/applications/people/phid/PhabricatorPeopleUserPHIDType.php
+++ b/src/applications/people/phid/PhabricatorPeopleUserPHIDType.php
@@ -27,7 +27,7 @@
return id(new PhabricatorPeopleQuery())
->withPHIDs($phids)
->needProfileImage(true)
- ->needStatus(true);
+ ->needAvailability(true);
}
public function loadHandles(
@@ -48,18 +48,9 @@
if (!$user->isUserActivated()) {
$availability = PhabricatorObjectHandle::AVAILABILITY_DISABLED;
} else {
- if ($user->hasStatus()) {
- // NOTE: This first call returns an event; then we get the event
- // status.
- $status = $user->getStatus()->getStatus();
- switch ($status) {
- case PhabricatorCalendarEvent::STATUS_AWAY:
- $availability = PhabricatorObjectHandle::AVAILABILITY_NONE;
- break;
- case PhabricatorCalendarEvent::STATUS_SPORADIC:
- $availability = PhabricatorObjectHandle::AVAILABILITY_PARTIAL;
- break;
- }
+ $until = $user->getAwayUntil();
+ if ($until) {
+ $availability = PhabricatorObjectHandle::AVAILABILITY_NONE;
}
}
diff --git a/src/applications/people/query/PhabricatorPeopleQuery.php b/src/applications/people/query/PhabricatorPeopleQuery.php
--- a/src/applications/people/query/PhabricatorPeopleQuery.php
+++ b/src/applications/people/query/PhabricatorPeopleQuery.php
@@ -20,7 +20,7 @@
private $needPrimaryEmail;
private $needProfile;
private $needProfileImage;
- private $needStatus;
+ private $needAvailability;
public function withIDs(array $ids) {
$this->ids = $ids;
@@ -102,8 +102,8 @@
return $this;
}
- public function needStatus($need) {
- $this->needStatus = $need;
+ public function needAvailability($need) {
+ $this->needAvailability = $need;
return $this;
}
@@ -200,15 +200,11 @@
}
}
- if ($this->needStatus) {
- $user_list = mpull($users, null, 'getPHID');
- $statuses = id(new PhabricatorCalendarEvent())->loadCurrentStatuses(
- array_keys($user_list));
- foreach ($user_list as $phid => $user) {
- $status = idx($statuses, $phid);
- if ($status) {
- $user->attachStatus($status);
- }
+ if ($this->needAvailability) {
+ // TODO: Add caching.
+ $rebuild = $users;
+ if ($rebuild) {
+ $this->rebuildAvailabilityCache($rebuild);
}
}
@@ -375,5 +371,82 @@
);
}
+ private function rebuildAvailabilityCache(array $rebuild) {
+ $rebuild = mpull($rebuild, null, 'getPHID');
+
+ // Limit the window we look at because far-future events are largely
+ // irrelevant and this makes the cache cheaper to build and allows it to
+ // self-heal over time.
+ $min_range = PhabricatorTime::getNow();
+ $max_range = $min_range + phutil_units('72 hours in seconds');
+
+ $events = id(new PhabricatorCalendarEventQuery())
+ ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->withInvitedPHIDs(array_keys($rebuild))
+ ->withIsCancelled(false)
+ ->withDateRange($min_range, $max_range)
+ ->execute();
+
+ // Group all the events by invited user. Only examine events that users
+ // are actually attending.
+ $map = array();
+ foreach ($events as $event) {
+ foreach ($event->getInvitees() as $invitee) {
+ if (!$invitee->isAttending()) {
+ continue;
+ }
+
+ $invitee_phid = $invitee->getInviteePHID();
+ if (!isset($rebuild[$invitee_phid])) {
+ continue;
+ }
+
+ $map[$invitee_phid][] = $event;
+ }
+ }
+
+ // Margin between meetings: pretend meetings start earlier than they do
+ // so we mark you away for the entire time if you have a series of
+ // back-to-back meetings, even if they don't strictly overlap.
+ $margin = phutil_units('15 minutes in seconds');
+
+ foreach ($rebuild as $phid => $user) {
+ $events = idx($map, $phid, array());
+
+ $cursor = $min_range;
+ if ($events) {
+ // Find the next time when the user has no meetings. If we move forward
+ // because of an event, we check again for events after that one ends.
+ while (true) {
+ foreach ($events as $event) {
+ $from = ($event->getDateFrom() - $margin);
+ $to = $event->getDateTo();
+ if (($from <= $cursor) && ($to > $cursor)) {
+ $cursor = $to;
+ continue 2;
+ }
+ }
+ break;
+ }
+ }
+
+ if ($cursor > $min_range) {
+ $availability = array(
+ 'until' => $cursor,
+ );
+ $availability_ttl = $cursor;
+ } else {
+ $availability = null;
+ $availability_ttl = $max_range;
+ }
+
+ // Never TTL the cache to longer than the maximum range we examined.
+ $availability_ttl = min($availability_ttl, $max_range);
+
+ // TODO: Write the cache.
+
+ $user->attachAvailability($availability);
+ }
+ }
}
diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php
--- a/src/applications/people/storage/PhabricatorUser.php
+++ b/src/applications/people/storage/PhabricatorUser.php
@@ -1,6 +1,7 @@
<?php
/**
+ * @task availability Availability
* @task image-cache Profile Image Cache
* @task factors Multi-Factor Authentication
* @task handles Managing Handles
@@ -45,7 +46,7 @@
private $profileImage = self::ATTACHABLE;
private $profile = null;
- private $status = self::ATTACHABLE;
+ private $availability = self::ATTACHABLE;
private $preferences = null;
private $omnipotent = false;
private $customFields = self::ATTACHABLE;
@@ -658,19 +659,6 @@
return celerity_get_resource_uri('/rsrc/image/avatar.png');
}
- public function attachStatus(PhabricatorCalendarEvent $status) {
- $this->status = $status;
- return $this;
- }
-
- public function getStatus() {
- return $this->assertAttached($this->status);
- }
-
- public function hasStatus() {
- return $this->status !== self::ATTACHABLE;
- }
-
public function attachProfileImageURI($uri) {
$this->profileImage = $uri;
return $this;
@@ -727,6 +715,53 @@
}
+/* -( Availability )------------------------------------------------------- */
+
+
+ /**
+ * @task availability
+ */
+ public function attachAvailability($availability) {
+ $this->availability = $availability;
+ return $this;
+ }
+
+
+ /**
+ * Get the timestamp the user is away until, if they are currently away.
+ *
+ * @return int|null Epoch timestamp, or `null` if the user is not away.
+ * @task availability
+ */
+ public function getAwayUntil() {
+ $availability = $this->availability;
+
+ $this->assertAttached($availability);
+ if (!$availability) {
+ return null;
+ }
+
+ return idx($availability, 'until');
+ }
+
+
+ /**
+ * Describe the user's availability.
+ *
+ * @param PhabricatorUser Viewing user.
+ * @return string Human-readable description of away status.
+ * @task availability
+ */
+ public function getAvailabilityDescription(PhabricatorUser $viewer) {
+ $until = $this->getAwayUntil();
+ if ($until) {
+ return pht('Away until %s', phabricator_datetime($until, $viewer));
+ } else {
+ return pht('Available');
+ }
+ }
+
+
/* -( Profile Image Cache )------------------------------------------------ */
diff --git a/webroot/rsrc/css/layout/phabricator-hovercard-view.css b/webroot/rsrc/css/layout/phabricator-hovercard-view.css
--- a/webroot/rsrc/css/layout/phabricator-hovercard-view.css
+++ b/webroot/rsrc/css/layout/phabricator-hovercard-view.css
@@ -77,6 +77,7 @@
height: 50px;
background-position: center;
background-repeat: no-repeat;
+ background-size: 100%;
}
.phabricator-hovercard-tail {
width: 396px;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Mar 31, 1:13 PM (4 d, 11 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7388655
Default Alt Text
D12833.diff (17 KB)
Attached To
Mode
D12833: Replace user "status" with "availability"
Attached
Detach File
Event Timeline
Log In to Comment