From 80cccebca292899f2188370afa4f889a1602c554 Mon Sep 17 00:00:00 2001 From: Chad Little Date: Fri, 24 Feb 2017 13:15:30 -0800 Subject: [PATCH] Build a Badges page for Profiles Summary: Ref T12270. Moves badges into their own page and menu item. Capable of displaying hundreds of useful tokens of appreciation and dedication. Test Plan: Test blank state, mobile, awards badges. {F3284139} Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin Maniphest Tasks: T12270 Differential Revision: https://secure.phabricator.com/D17410 --- resources/celerity/map.php | 8 +- src/__phutil_library_map__.php | 4 + .../PhabricatorBadgesAwardController.php | 2 +- .../PhabricatorPeopleApplication.php | 2 + ...abricatorPeopleProfileBadgesController.php | 137 ++++++++++++++++++ .../PhabricatorPeopleProfileController.php | 7 +- ...abricatorPeopleProfileManageController.php | 2 +- ...bricatorPeopleProfilePictureController.php | 6 +- ...PhabricatorPeopleProfileViewController.php | 104 ------------- .../PhabricatorPeopleProfileMenuEngine.php | 10 ++ ...PhabricatorPeopleBadgesProfileMenuItem.php | 59 ++++++++ .../css/application/project/project-view.css | 4 - webroot/rsrc/css/phui/phui-badge.css | 5 +- 13 files changed, 228 insertions(+), 122 deletions(-) create mode 100644 src/applications/people/controller/PhabricatorPeopleProfileBadgesController.php create mode 100644 src/applications/people/menuitem/PhabricatorPeopleBadgesProfileMenuItem.php diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 7122e68f39..f2726a4568 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -97,7 +97,7 @@ return array( 'rsrc/css/application/policy/policy.css' => '957ea14c', 'rsrc/css/application/ponder/ponder-view.css' => 'fbd45f96', 'rsrc/css/application/project/project-card-view.css' => '77219296', - 'rsrc/css/application/project/project-view.css' => '9f6ce0e1', + 'rsrc/css/application/project/project-view.css' => '792c9057', 'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733', 'rsrc/css/application/releeph/releeph-preview-branch.css' => 'b7a6f4a5', 'rsrc/css/application/releeph/releeph-request-differential-create-dialog.css' => '8d8b92cd', @@ -130,7 +130,7 @@ return array( 'rsrc/css/phui/object-item/phui-oi-simple-ui.css' => 'a8beebea', 'rsrc/css/phui/phui-action-list.css' => 'f980c059', 'rsrc/css/phui/phui-action-panel.css' => '91c7b835', - 'rsrc/css/phui/phui-badge.css' => '22fe77f8', + 'rsrc/css/phui/phui-badge.css' => '22c0cf4f', 'rsrc/css/phui/phui-basic-nav-view.css' => 'a0705f53', 'rsrc/css/phui/phui-big-info-view.css' => 'bd903741', 'rsrc/css/phui/phui-box.css' => '269cbc99', @@ -837,7 +837,7 @@ return array( 'phrequent-css' => 'ffc185ad', 'phriction-document-css' => '4282e4ad', 'phui-action-panel-css' => '91c7b835', - 'phui-badge-view-css' => '22fe77f8', + 'phui-badge-view-css' => '22c0cf4f', 'phui-basic-nav-view-css' => 'a0705f53', 'phui-big-info-view-css' => 'bd903741', 'phui-box-css' => '269cbc99', @@ -906,7 +906,7 @@ return array( 'policy-transaction-detail-css' => '82100a43', 'ponder-view-css' => 'fbd45f96', 'project-card-view-css' => '77219296', - 'project-view-css' => '9f6ce0e1', + 'project-view-css' => '792c9057', 'releeph-core' => '9b3c5733', 'releeph-preview-branch' => 'b7a6f4a5', 'releeph-request-differential-create-dialog' => '8d8b92cd', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index bdb8cb120f..0fd7dd8418 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3316,6 +3316,7 @@ phutil_register_library_map(array( 'PhabricatorPeopleAnyOwnerDatasource' => 'applications/people/typeahead/PhabricatorPeopleAnyOwnerDatasource.php', 'PhabricatorPeopleApplication' => 'applications/people/application/PhabricatorPeopleApplication.php', 'PhabricatorPeopleApproveController' => 'applications/people/controller/PhabricatorPeopleApproveController.php', + 'PhabricatorPeopleBadgesProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeopleBadgesProfileMenuItem.php', 'PhabricatorPeopleController' => 'applications/people/controller/PhabricatorPeopleController.php', 'PhabricatorPeopleCreateController' => 'applications/people/controller/PhabricatorPeopleCreateController.php', 'PhabricatorPeopleCreateGuidanceContext' => 'applications/people/guidance/PhabricatorPeopleCreateGuidanceContext.php', @@ -3339,6 +3340,7 @@ phutil_register_library_map(array( 'PhabricatorPeopleNoOwnerDatasource' => 'applications/people/typeahead/PhabricatorPeopleNoOwnerDatasource.php', 'PhabricatorPeopleOwnerDatasource' => 'applications/people/typeahead/PhabricatorPeopleOwnerDatasource.php', 'PhabricatorPeoplePictureProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeoplePictureProfileMenuItem.php', + 'PhabricatorPeopleProfileBadgesController' => 'applications/people/controller/PhabricatorPeopleProfileBadgesController.php', 'PhabricatorPeopleProfileController' => 'applications/people/controller/PhabricatorPeopleProfileController.php', 'PhabricatorPeopleProfileEditController' => 'applications/people/controller/PhabricatorPeopleProfileEditController.php', 'PhabricatorPeopleProfileManageController' => 'applications/people/controller/PhabricatorPeopleProfileManageController.php', @@ -8473,6 +8475,7 @@ phutil_register_library_map(array( 'PhabricatorPeopleAnyOwnerDatasource' => 'PhabricatorTypeaheadDatasource', 'PhabricatorPeopleApplication' => 'PhabricatorApplication', 'PhabricatorPeopleApproveController' => 'PhabricatorPeopleController', + 'PhabricatorPeopleBadgesProfileMenuItem' => 'PhabricatorProfileMenuItem', 'PhabricatorPeopleController' => 'PhabricatorController', 'PhabricatorPeopleCreateController' => 'PhabricatorPeopleController', 'PhabricatorPeopleCreateGuidanceContext' => 'PhabricatorGuidanceContext', @@ -8496,6 +8499,7 @@ phutil_register_library_map(array( 'PhabricatorPeopleNoOwnerDatasource' => 'PhabricatorTypeaheadDatasource', 'PhabricatorPeopleOwnerDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 'PhabricatorPeoplePictureProfileMenuItem' => 'PhabricatorProfileMenuItem', + 'PhabricatorPeopleProfileBadgesController' => 'PhabricatorPeopleProfileController', 'PhabricatorPeopleProfileController' => 'PhabricatorPeopleController', 'PhabricatorPeopleProfileEditController' => 'PhabricatorPeopleProfileController', 'PhabricatorPeopleProfileManageController' => 'PhabricatorPeopleProfileController', diff --git a/src/applications/badges/controller/PhabricatorBadgesAwardController.php b/src/applications/badges/controller/PhabricatorBadgesAwardController.php index ad409f8297..3fa474018e 100644 --- a/src/applications/badges/controller/PhabricatorBadgesAwardController.php +++ b/src/applications/badges/controller/PhabricatorBadgesAwardController.php @@ -15,7 +15,7 @@ final class PhabricatorBadgesAwardController return new Aphront404Response(); } - $view_uri = '/p/'.$user->getUsername(); + $view_uri = '/people/badges/'.$user->getID().'/'; if ($request->isFormPost()) { $badge_phids = $request->getArr('badgePHIDs'); diff --git a/src/applications/people/application/PhabricatorPeopleApplication.php b/src/applications/people/application/PhabricatorPeopleApplication.php index 5278c2e801..d295319b71 100644 --- a/src/applications/people/application/PhabricatorPeopleApplication.php +++ b/src/applications/people/application/PhabricatorPeopleApplication.php @@ -64,6 +64,8 @@ final class PhabricatorPeopleApplication extends PhabricatorApplication { 'ldap/' => 'PhabricatorPeopleLdapController', 'editprofile/(?P[1-9]\d*)/' => 'PhabricatorPeopleProfileEditController', + 'badges/(?P[1-9]\d*)/' => + 'PhabricatorPeopleProfileBadgesController', 'picture/(?P[1-9]\d*)/' => 'PhabricatorPeopleProfilePictureController', 'manage/(?P[1-9]\d*)/' => diff --git a/src/applications/people/controller/PhabricatorPeopleProfileBadgesController.php b/src/applications/people/controller/PhabricatorPeopleProfileBadgesController.php new file mode 100644 index 0000000000..1b22452430 --- /dev/null +++ b/src/applications/people/controller/PhabricatorPeopleProfileBadgesController.php @@ -0,0 +1,137 @@ +getViewer(); + $id = $request->getURIData('id'); + + $user = id(new PhabricatorPeopleQuery()) + ->setViewer($viewer) + ->withIDs(array($id)) + ->needProfile(true) + ->needProfileImage(true) + ->needAvailability(true) + ->needBadges(true) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + )) + ->executeOne(); + if (!$user) { + return new Aphront404Response(); + } + + $class = 'PhabricatorBadgesApplication'; + if (!PhabricatorApplication::isClassInstalledForViewer($class, $viewer)) { + return new Aphront404Response(); + } + + $this->setUser($user); + $title = array(pht('Badges'), $user->getUsername()); + $header = $this->buildProfileHeader(); + $badges = $this->buildBadgesView($user); + + $crumbs = $this->buildApplicationCrumbs(); + $crumbs->addTextCrumb(pht('Badges')); + $crumbs->setBorder(true); + + $nav = $this->getProfileMenu(); + $nav->selectFilter(PhabricatorPeopleProfileMenuEngine::ITEM_BADGES); + + // Best option? + $badges = id(new PhabricatorBadgesQuery()) + ->setViewer($viewer) + ->withStatuses(array( + PhabricatorBadgesBadge::STATUS_ACTIVE, + )) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->execute(); + + $button = id(new PHUIButtonView()) + ->setTag('a') + ->setIcon('fa-plus') + ->setText(pht('Award Badge')) + ->setWorkflow(true) + ->setHref('/badges/award/'.$user->getID().'/'); + + if (count($badges)) { + $header->addActionLink($button); + } + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->addClass('project-view-home') + ->addClass('project-view-people-home') + ->setFooter(array( + $this->buildBadgesView($user) + )); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->setNavigation($nav) + ->appendChild($view); + } + + private function buildBadgesView(PhabricatorUser $user) { + $viewer = $this->getViewer(); + + $awards = array(); + $badges = array(); + if ($user->getBadgePHIDs()) { + $awards = id(new PhabricatorBadgesAwardQuery()) + ->setViewer($viewer) + ->withRecipientPHIDs(array($user->getPHID())) + ->execute(); + $awards = mpull($awards, null, 'getBadgePHID'); + + $badges = array(); + foreach ($awards as $award) { + $badge = $award->getBadge(); + if ($badge->getStatus() == PhabricatorBadgesBadge::STATUS_ACTIVE) { + $badges[$award->getBadgePHID()] = $badge; + } + } + } + + if (count($badges)) { + $flex = new PHUIBadgeBoxView(); + + foreach ($badges as $badge) { + if ($badge) { + $awarder_info = array(); + + $award = idx($awards, $badge->getPHID(), null); + $awarder_phid = $award->getAwarderPHID(); + $awarder_handle = $viewer->renderHandle($awarder_phid); + + $awarder_info = pht( + 'Awarded by %s', + $awarder_handle->render()); + + $item = id(new PHUIBadgeView()) + ->setIcon($badge->getIcon()) + ->setHeader($badge->getName()) + ->setSubhead($badge->getFlavor()) + ->setQuality($badge->getQuality()) + ->setHref($badge->getViewURI()) + ->addByLine($awarder_info); + + $flex->addItem($item); + } + } + } else { + $flex = id(new PHUIInfoView()) + ->setSeverity(PHUIInfoView::SEVERITY_NOTICE) + ->appendChild(pht('User has not been awarded any badges.')); + } + + return $flex; + } +} diff --git a/src/applications/people/controller/PhabricatorPeopleProfileController.php b/src/applications/people/controller/PhabricatorPeopleProfileController.php index 7393df8455..902b21efcc 100644 --- a/src/applications/people/controller/PhabricatorPeopleProfileController.php +++ b/src/applications/people/controller/PhabricatorPeopleProfileController.php @@ -86,6 +86,9 @@ abstract class PhabricatorPeopleProfileController if ($user->getIsMailingList()) { $roles[] = pht('Mailing List'); } + if (!$user->getIsEmailVerified()) { + $roles[] = pht('Email Not Verified'); + } $tag = null; if ($roles) { @@ -101,10 +104,10 @@ abstract class PhabricatorPeopleProfileController ->setProfileHeader(true) ->addClass('people-profile-header'); + require_celerity_resource('project-view-css'); + if ($user->getIsDisabled()) { $header->setStatus('fa-ban', 'red', pht('Disabled')); - } else if (!$user->getIsEmailVerified()) { - $header->setStatus('fa-envelope', 'red', pht('Email Not Verified')); } else { $header->setStatus($profile_icon, 'bluegrey', $profile_title); } diff --git a/src/applications/people/controller/PhabricatorPeopleProfileManageController.php b/src/applications/people/controller/PhabricatorPeopleProfileManageController.php index 84acfe1af9..2ac3e6de89 100644 --- a/src/applications/people/controller/PhabricatorPeopleProfileManageController.php +++ b/src/applications/people/controller/PhabricatorPeopleProfileManageController.php @@ -39,9 +39,9 @@ final class PhabricatorPeopleProfileManageController $manage = id(new PHUITwoColumnView()) ->setHeader($header) ->addClass('project-view-home') + ->addClass('project-view-people-home') ->setCurtain($curtain) ->addPropertySection(pht('Details'), $properties); - require_celerity_resource('project-view-css'); return $this->newPage() ->setTitle( diff --git a/src/applications/people/controller/PhabricatorPeopleProfilePictureController.php b/src/applications/people/controller/PhabricatorPeopleProfilePictureController.php index 226646bc70..2c9a4bf10c 100644 --- a/src/applications/people/controller/PhabricatorPeopleProfilePictureController.php +++ b/src/applications/people/controller/PhabricatorPeopleProfilePictureController.php @@ -258,12 +258,12 @@ final class PhabricatorPeopleProfilePictureController $nav = $this->getProfileMenu(); $nav->selectFilter(PhabricatorPeopleProfileMenuEngine::ITEM_MANAGE); - $header = id(new PHUIHeaderView()) - ->setHeader(pht('Edit Profile Picture')) - ->setHeaderIcon('fa-camera'); + $header = $this->buildProfileHeader(); $view = id(new PHUITwoColumnView()) ->setHeader($header) + ->addClass('project-view-home') + ->addClass('project-view-people-home') ->setFooter(array( $form_box, $upload_box, diff --git a/src/applications/people/controller/PhabricatorPeopleProfileViewController.php b/src/applications/people/controller/PhabricatorPeopleProfileViewController.php index 889ecd2969..9db106081d 100644 --- a/src/applications/people/controller/PhabricatorPeopleProfileViewController.php +++ b/src/applications/people/controller/PhabricatorPeopleProfileViewController.php @@ -14,7 +14,6 @@ final class PhabricatorPeopleProfileViewController $user = id(new PhabricatorPeopleQuery()) ->setViewer($viewer) ->withUsernames(array($username)) - ->needBadges(true) ->needProfileImage(true) ->needAvailability(true) ->executeOne(); @@ -36,8 +35,6 @@ final class PhabricatorPeopleProfileViewController $projects = $this->buildProjectsView($user); $calendar = $this->buildCalendarDayView($user); - $badges = $this->buildBadgesView($user); - require_celerity_resource('project-view-css'); $home = id(new PHUITwoColumnView()) ->setHeader($header) @@ -52,7 +49,6 @@ final class PhabricatorPeopleProfileViewController array( $projects, $calendar, - $badges, )); $nav = $this->getProfileMenu(); @@ -228,106 +224,6 @@ final class PhabricatorPeopleProfileViewController return $box; } - private function buildBadgesView(PhabricatorUser $user) { - - $viewer = $this->getViewer(); - $class = 'PhabricatorBadgesApplication'; - - if (!PhabricatorApplication::isClassInstalledForViewer($class, $viewer)) { - return null; - } - - $awards = array(); - $badges = array(); - if ($user->getBadgePHIDs()) { - $awards = id(new PhabricatorBadgesAwardQuery()) - ->setViewer($viewer) - ->withRecipientPHIDs(array($user->getPHID())) - ->execute(); - $awards = mpull($awards, null, 'getBadgePHID'); - - $badges = array(); - foreach ($awards as $award) { - $badge = $award->getBadge(); - if ($badge->getStatus() == PhabricatorBadgesBadge::STATUS_ACTIVE) { - $badges[$award->getBadgePHID()] = $badge; - } - } - } - - if (count($badges)) { - $flex = new PHUIBadgeBoxView(); - - foreach ($badges as $badge) { - if ($badge) { - $awarder_info = array(); - - $award = idx($awards, $badge->getPHID(), null); - $awarder_phid = $award->getAwarderPHID(); - $awarder_handle = $viewer->renderHandle($awarder_phid); - - $awarder_info = pht( - 'Awarded by %s', - $awarder_handle->render()); - - $item = id(new PHUIBadgeView()) - ->setIcon($badge->getIcon()) - ->setHeader($badge->getName()) - ->setSubhead($badge->getFlavor()) - ->setQuality($badge->getQuality()) - ->setHref($badge->getViewURI()) - ->addByLine($awarder_info); - - $flex->addItem($item); - } - } - } else { - $flex = id(new PHUIInfoView()) - ->setSeverity(PHUIInfoView::SEVERITY_NODATA) - ->appendChild(pht('User does not have any badges.')); - } - - // Best option? - $badges = id(new PhabricatorBadgesQuery()) - ->setViewer($viewer) - ->withStatuses(array( - PhabricatorBadgesBadge::STATUS_ACTIVE, - )) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->execute(); - - $button = id(new PHUIButtonView()) - ->setTag('a') - ->setIcon('fa-plus') - ->setText(pht('Award')) - ->setWorkflow(true) - ->setHref('/badges/award/'.$user->getID().'/'); - - $can_award = false; - if (count($badges)) { - $can_award = true; - } - - $header = id(new PHUIHeaderView()) - ->setHeader(pht('Badges')); - - if (count($badges)) { - $header->addActionLink($button); - } - - $box = id(new PHUIObjectBoxView()) - ->setHeader($header) - ->addClass('project-view-badges') - ->appendChild($flex) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY); - - return $box; - } - private function buildPeopleFeed( PhabricatorUser $user, $viewer) { diff --git a/src/applications/people/engine/PhabricatorPeopleProfileMenuEngine.php b/src/applications/people/engine/PhabricatorPeopleProfileMenuEngine.php index 2fd2ec3bd2..9e4bbba874 100644 --- a/src/applications/people/engine/PhabricatorPeopleProfileMenuEngine.php +++ b/src/applications/people/engine/PhabricatorPeopleProfileMenuEngine.php @@ -6,6 +6,7 @@ final class PhabricatorPeopleProfileMenuEngine const ITEM_PROFILE = 'people.profile'; const ITEM_MANAGE = 'people.manage'; const ITEM_PICTURE = 'people.picture'; + const ITEM_BADGES = 'people.badges'; protected function isMenuEngineConfigurable() { return false; @@ -31,6 +32,15 @@ final class PhabricatorPeopleProfileMenuEngine ->setBuiltinKey(self::ITEM_PROFILE) ->setMenuItemKey(PhabricatorPeopleDetailsProfileMenuItem::MENUITEMKEY); + $have_badges = PhabricatorApplication::isClassInstalledForViewer( + 'PhabricatorBadgesApplication', + $viewer); + if ($have_badges) { + $items[] = $this->newItem() + ->setBuiltinKey(self::ITEM_BADGES) + ->setMenuItemKey(PhabricatorPeopleBadgesProfileMenuItem::MENUITEMKEY); + } + $have_maniphest = PhabricatorApplication::isClassInstalledForViewer( 'PhabricatorManiphestApplication', $viewer); diff --git a/src/applications/people/menuitem/PhabricatorPeopleBadgesProfileMenuItem.php b/src/applications/people/menuitem/PhabricatorPeopleBadgesProfileMenuItem.php new file mode 100644 index 0000000000..0e4da29b61 --- /dev/null +++ b/src/applications/people/menuitem/PhabricatorPeopleBadgesProfileMenuItem.php @@ -0,0 +1,59 @@ +getMenuItemProperty('name'); + + if (strlen($name)) { + return $name; + } + + return $this->getDefaultName(); + } + + public function buildEditEngineFields( + PhabricatorProfileMenuItemConfiguration $config) { + return array( + id(new PhabricatorTextEditField()) + ->setKey('name') + ->setLabel(pht('Name')) + ->setPlaceholder($this->getDefaultName()) + ->setValue($config->getMenuItemProperty('name')), + ); + } + + protected function newNavigationMenuItems( + PhabricatorProfileMenuItemConfiguration $config) { + + $user = $config->getProfileObject(); + $id = $user->getID(); + + $item = $this->newItem() + ->setHref("/people/badges/{$id}/") + ->setName($this->getDisplayName($config)) + ->setIcon('fa-trophy'); + + return array( + $item, + ); + } + +} diff --git a/webroot/rsrc/css/application/project/project-view.css b/webroot/rsrc/css/application/project/project-view.css index cc8d484426..82c5a2e13b 100644 --- a/webroot/rsrc/css/application/project/project-view.css +++ b/webroot/rsrc/css/application/project/project-view.css @@ -67,10 +67,6 @@ text-align: center; } -.project-view-badges .phui-badge-flex-view { - background-color: #fff; -} - .project-view-home .phui-box-grey .phui-oi-attribute .phui-icon-view { color: {$lightgreytext}; } diff --git a/webroot/rsrc/css/phui/phui-badge.css b/webroot/rsrc/css/phui/phui-badge.css index a38dff9154..1538e5de4d 100644 --- a/webroot/rsrc/css/phui/phui-badge.css +++ b/webroot/rsrc/css/phui/phui-badge.css @@ -3,14 +3,13 @@ */ .phui-badge-flex-view { - padding: 12px 4px 8px; + padding: 0; overflow: hidden; - text-align: center; } .phui-badge-flex-item { display: inline-block; - padding: 4px 8px; + padding: 4px 12px 4px 0; } .phui-badge-flex-view.flex-view-collapsed {