@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+228 -122
+4 -4
resources/celerity/map.php
··· 97 97 'rsrc/css/application/policy/policy.css' => '957ea14c', 98 98 'rsrc/css/application/ponder/ponder-view.css' => 'fbd45f96', 99 99 'rsrc/css/application/project/project-card-view.css' => '77219296', 100 - 'rsrc/css/application/project/project-view.css' => '9f6ce0e1', 100 + 'rsrc/css/application/project/project-view.css' => '792c9057', 101 101 'rsrc/css/application/releeph/releeph-core.css' => '9b3c5733', 102 102 'rsrc/css/application/releeph/releeph-preview-branch.css' => 'b7a6f4a5', 103 103 'rsrc/css/application/releeph/releeph-request-differential-create-dialog.css' => '8d8b92cd', ··· 130 130 'rsrc/css/phui/object-item/phui-oi-simple-ui.css' => 'a8beebea', 131 131 'rsrc/css/phui/phui-action-list.css' => 'f980c059', 132 132 'rsrc/css/phui/phui-action-panel.css' => '91c7b835', 133 - 'rsrc/css/phui/phui-badge.css' => '22fe77f8', 133 + 'rsrc/css/phui/phui-badge.css' => '22c0cf4f', 134 134 'rsrc/css/phui/phui-basic-nav-view.css' => 'a0705f53', 135 135 'rsrc/css/phui/phui-big-info-view.css' => 'bd903741', 136 136 'rsrc/css/phui/phui-box.css' => '269cbc99', ··· 837 837 'phrequent-css' => 'ffc185ad', 838 838 'phriction-document-css' => '4282e4ad', 839 839 'phui-action-panel-css' => '91c7b835', 840 - 'phui-badge-view-css' => '22fe77f8', 840 + 'phui-badge-view-css' => '22c0cf4f', 841 841 'phui-basic-nav-view-css' => 'a0705f53', 842 842 'phui-big-info-view-css' => 'bd903741', 843 843 'phui-box-css' => '269cbc99', ··· 906 906 'policy-transaction-detail-css' => '82100a43', 907 907 'ponder-view-css' => 'fbd45f96', 908 908 'project-card-view-css' => '77219296', 909 - 'project-view-css' => '9f6ce0e1', 909 + 'project-view-css' => '792c9057', 910 910 'releeph-core' => '9b3c5733', 911 911 'releeph-preview-branch' => 'b7a6f4a5', 912 912 'releeph-request-differential-create-dialog' => '8d8b92cd',
+4
src/__phutil_library_map__.php
··· 3316 3316 'PhabricatorPeopleAnyOwnerDatasource' => 'applications/people/typeahead/PhabricatorPeopleAnyOwnerDatasource.php', 3317 3317 'PhabricatorPeopleApplication' => 'applications/people/application/PhabricatorPeopleApplication.php', 3318 3318 'PhabricatorPeopleApproveController' => 'applications/people/controller/PhabricatorPeopleApproveController.php', 3319 + 'PhabricatorPeopleBadgesProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeopleBadgesProfileMenuItem.php', 3319 3320 'PhabricatorPeopleController' => 'applications/people/controller/PhabricatorPeopleController.php', 3320 3321 'PhabricatorPeopleCreateController' => 'applications/people/controller/PhabricatorPeopleCreateController.php', 3321 3322 'PhabricatorPeopleCreateGuidanceContext' => 'applications/people/guidance/PhabricatorPeopleCreateGuidanceContext.php', ··· 3339 3340 'PhabricatorPeopleNoOwnerDatasource' => 'applications/people/typeahead/PhabricatorPeopleNoOwnerDatasource.php', 3340 3341 'PhabricatorPeopleOwnerDatasource' => 'applications/people/typeahead/PhabricatorPeopleOwnerDatasource.php', 3341 3342 'PhabricatorPeoplePictureProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeoplePictureProfileMenuItem.php', 3343 + 'PhabricatorPeopleProfileBadgesController' => 'applications/people/controller/PhabricatorPeopleProfileBadgesController.php', 3342 3344 'PhabricatorPeopleProfileController' => 'applications/people/controller/PhabricatorPeopleProfileController.php', 3343 3345 'PhabricatorPeopleProfileEditController' => 'applications/people/controller/PhabricatorPeopleProfileEditController.php', 3344 3346 'PhabricatorPeopleProfileManageController' => 'applications/people/controller/PhabricatorPeopleProfileManageController.php', ··· 8473 8475 'PhabricatorPeopleAnyOwnerDatasource' => 'PhabricatorTypeaheadDatasource', 8474 8476 'PhabricatorPeopleApplication' => 'PhabricatorApplication', 8475 8477 'PhabricatorPeopleApproveController' => 'PhabricatorPeopleController', 8478 + 'PhabricatorPeopleBadgesProfileMenuItem' => 'PhabricatorProfileMenuItem', 8476 8479 'PhabricatorPeopleController' => 'PhabricatorController', 8477 8480 'PhabricatorPeopleCreateController' => 'PhabricatorPeopleController', 8478 8481 'PhabricatorPeopleCreateGuidanceContext' => 'PhabricatorGuidanceContext', ··· 8496 8499 'PhabricatorPeopleNoOwnerDatasource' => 'PhabricatorTypeaheadDatasource', 8497 8500 'PhabricatorPeopleOwnerDatasource' => 'PhabricatorTypeaheadCompositeDatasource', 8498 8501 'PhabricatorPeoplePictureProfileMenuItem' => 'PhabricatorProfileMenuItem', 8502 + 'PhabricatorPeopleProfileBadgesController' => 'PhabricatorPeopleProfileController', 8499 8503 'PhabricatorPeopleProfileController' => 'PhabricatorPeopleController', 8500 8504 'PhabricatorPeopleProfileEditController' => 'PhabricatorPeopleProfileController', 8501 8505 'PhabricatorPeopleProfileManageController' => 'PhabricatorPeopleProfileController',
+1 -1
src/applications/badges/controller/PhabricatorBadgesAwardController.php
··· 15 15 return new Aphront404Response(); 16 16 } 17 17 18 - $view_uri = '/p/'.$user->getUsername(); 18 + $view_uri = '/people/badges/'.$user->getID().'/'; 19 19 20 20 if ($request->isFormPost()) { 21 21 $badge_phids = $request->getArr('badgePHIDs');
+2
src/applications/people/application/PhabricatorPeopleApplication.php
··· 64 64 'ldap/' => 'PhabricatorPeopleLdapController', 65 65 'editprofile/(?P<id>[1-9]\d*)/' => 66 66 'PhabricatorPeopleProfileEditController', 67 + 'badges/(?P<id>[1-9]\d*)/' => 68 + 'PhabricatorPeopleProfileBadgesController', 67 69 'picture/(?P<id>[1-9]\d*)/' => 68 70 'PhabricatorPeopleProfilePictureController', 69 71 'manage/(?P<id>[1-9]\d*)/' =>
+137
src/applications/people/controller/PhabricatorPeopleProfileBadgesController.php
··· 1 + <?php 2 + 3 + final class PhabricatorPeopleProfileBadgesController 4 + extends PhabricatorPeopleProfileController { 5 + 6 + public function handleRequest(AphrontRequest $request) { 7 + $viewer = $this->getViewer(); 8 + $id = $request->getURIData('id'); 9 + 10 + $user = id(new PhabricatorPeopleQuery()) 11 + ->setViewer($viewer) 12 + ->withIDs(array($id)) 13 + ->needProfile(true) 14 + ->needProfileImage(true) 15 + ->needAvailability(true) 16 + ->needBadges(true) 17 + ->requireCapabilities( 18 + array( 19 + PhabricatorPolicyCapability::CAN_VIEW, 20 + )) 21 + ->executeOne(); 22 + if (!$user) { 23 + return new Aphront404Response(); 24 + } 25 + 26 + $class = 'PhabricatorBadgesApplication'; 27 + if (!PhabricatorApplication::isClassInstalledForViewer($class, $viewer)) { 28 + return new Aphront404Response(); 29 + } 30 + 31 + $this->setUser($user); 32 + $title = array(pht('Badges'), $user->getUsername()); 33 + $header = $this->buildProfileHeader(); 34 + $badges = $this->buildBadgesView($user); 35 + 36 + $crumbs = $this->buildApplicationCrumbs(); 37 + $crumbs->addTextCrumb(pht('Badges')); 38 + $crumbs->setBorder(true); 39 + 40 + $nav = $this->getProfileMenu(); 41 + $nav->selectFilter(PhabricatorPeopleProfileMenuEngine::ITEM_BADGES); 42 + 43 + // Best option? 44 + $badges = id(new PhabricatorBadgesQuery()) 45 + ->setViewer($viewer) 46 + ->withStatuses(array( 47 + PhabricatorBadgesBadge::STATUS_ACTIVE, 48 + )) 49 + ->requireCapabilities( 50 + array( 51 + PhabricatorPolicyCapability::CAN_VIEW, 52 + PhabricatorPolicyCapability::CAN_EDIT, 53 + )) 54 + ->execute(); 55 + 56 + $button = id(new PHUIButtonView()) 57 + ->setTag('a') 58 + ->setIcon('fa-plus') 59 + ->setText(pht('Award Badge')) 60 + ->setWorkflow(true) 61 + ->setHref('/badges/award/'.$user->getID().'/'); 62 + 63 + if (count($badges)) { 64 + $header->addActionLink($button); 65 + } 66 + 67 + $view = id(new PHUITwoColumnView()) 68 + ->setHeader($header) 69 + ->addClass('project-view-home') 70 + ->addClass('project-view-people-home') 71 + ->setFooter(array( 72 + $this->buildBadgesView($user) 73 + )); 74 + 75 + return $this->newPage() 76 + ->setTitle($title) 77 + ->setCrumbs($crumbs) 78 + ->setNavigation($nav) 79 + ->appendChild($view); 80 + } 81 + 82 + private function buildBadgesView(PhabricatorUser $user) { 83 + $viewer = $this->getViewer(); 84 + 85 + $awards = array(); 86 + $badges = array(); 87 + if ($user->getBadgePHIDs()) { 88 + $awards = id(new PhabricatorBadgesAwardQuery()) 89 + ->setViewer($viewer) 90 + ->withRecipientPHIDs(array($user->getPHID())) 91 + ->execute(); 92 + $awards = mpull($awards, null, 'getBadgePHID'); 93 + 94 + $badges = array(); 95 + foreach ($awards as $award) { 96 + $badge = $award->getBadge(); 97 + if ($badge->getStatus() == PhabricatorBadgesBadge::STATUS_ACTIVE) { 98 + $badges[$award->getBadgePHID()] = $badge; 99 + } 100 + } 101 + } 102 + 103 + if (count($badges)) { 104 + $flex = new PHUIBadgeBoxView(); 105 + 106 + foreach ($badges as $badge) { 107 + if ($badge) { 108 + $awarder_info = array(); 109 + 110 + $award = idx($awards, $badge->getPHID(), null); 111 + $awarder_phid = $award->getAwarderPHID(); 112 + $awarder_handle = $viewer->renderHandle($awarder_phid); 113 + 114 + $awarder_info = pht( 115 + 'Awarded by %s', 116 + $awarder_handle->render()); 117 + 118 + $item = id(new PHUIBadgeView()) 119 + ->setIcon($badge->getIcon()) 120 + ->setHeader($badge->getName()) 121 + ->setSubhead($badge->getFlavor()) 122 + ->setQuality($badge->getQuality()) 123 + ->setHref($badge->getViewURI()) 124 + ->addByLine($awarder_info); 125 + 126 + $flex->addItem($item); 127 + } 128 + } 129 + } else { 130 + $flex = id(new PHUIInfoView()) 131 + ->setSeverity(PHUIInfoView::SEVERITY_NOTICE) 132 + ->appendChild(pht('User has not been awarded any badges.')); 133 + } 134 + 135 + return $flex; 136 + } 137 + }
+5 -2
src/applications/people/controller/PhabricatorPeopleProfileController.php
··· 86 86 if ($user->getIsMailingList()) { 87 87 $roles[] = pht('Mailing List'); 88 88 } 89 + if (!$user->getIsEmailVerified()) { 90 + $roles[] = pht('Email Not Verified'); 91 + } 89 92 90 93 $tag = null; 91 94 if ($roles) { ··· 101 104 ->setProfileHeader(true) 102 105 ->addClass('people-profile-header'); 103 106 107 + require_celerity_resource('project-view-css'); 108 + 104 109 if ($user->getIsDisabled()) { 105 110 $header->setStatus('fa-ban', 'red', pht('Disabled')); 106 - } else if (!$user->getIsEmailVerified()) { 107 - $header->setStatus('fa-envelope', 'red', pht('Email Not Verified')); 108 111 } else { 109 112 $header->setStatus($profile_icon, 'bluegrey', $profile_title); 110 113 }
+1 -1
src/applications/people/controller/PhabricatorPeopleProfileManageController.php
··· 39 39 $manage = id(new PHUITwoColumnView()) 40 40 ->setHeader($header) 41 41 ->addClass('project-view-home') 42 + ->addClass('project-view-people-home') 42 43 ->setCurtain($curtain) 43 44 ->addPropertySection(pht('Details'), $properties); 44 - require_celerity_resource('project-view-css'); 45 45 46 46 return $this->newPage() 47 47 ->setTitle(
+3 -3
src/applications/people/controller/PhabricatorPeopleProfilePictureController.php
··· 258 258 $nav = $this->getProfileMenu(); 259 259 $nav->selectFilter(PhabricatorPeopleProfileMenuEngine::ITEM_MANAGE); 260 260 261 - $header = id(new PHUIHeaderView()) 262 - ->setHeader(pht('Edit Profile Picture')) 263 - ->setHeaderIcon('fa-camera'); 261 + $header = $this->buildProfileHeader(); 264 262 265 263 $view = id(new PHUITwoColumnView()) 266 264 ->setHeader($header) 265 + ->addClass('project-view-home') 266 + ->addClass('project-view-people-home') 267 267 ->setFooter(array( 268 268 $form_box, 269 269 $upload_box,
-104
src/applications/people/controller/PhabricatorPeopleProfileViewController.php
··· 14 14 $user = id(new PhabricatorPeopleQuery()) 15 15 ->setViewer($viewer) 16 16 ->withUsernames(array($username)) 17 - ->needBadges(true) 18 17 ->needProfileImage(true) 19 18 ->needAvailability(true) 20 19 ->executeOne(); ··· 36 35 37 36 $projects = $this->buildProjectsView($user); 38 37 $calendar = $this->buildCalendarDayView($user); 39 - $badges = $this->buildBadgesView($user); 40 - require_celerity_resource('project-view-css'); 41 38 42 39 $home = id(new PHUITwoColumnView()) 43 40 ->setHeader($header) ··· 52 49 array( 53 50 $projects, 54 51 $calendar, 55 - $badges, 56 52 )); 57 53 58 54 $nav = $this->getProfileMenu(); ··· 223 219 ->setHeader($header) 224 220 ->appendChild($day_view) 225 221 ->addClass('calendar-profile-box') 226 - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY); 227 - 228 - return $box; 229 - } 230 - 231 - private function buildBadgesView(PhabricatorUser $user) { 232 - 233 - $viewer = $this->getViewer(); 234 - $class = 'PhabricatorBadgesApplication'; 235 - 236 - if (!PhabricatorApplication::isClassInstalledForViewer($class, $viewer)) { 237 - return null; 238 - } 239 - 240 - $awards = array(); 241 - $badges = array(); 242 - if ($user->getBadgePHIDs()) { 243 - $awards = id(new PhabricatorBadgesAwardQuery()) 244 - ->setViewer($viewer) 245 - ->withRecipientPHIDs(array($user->getPHID())) 246 - ->execute(); 247 - $awards = mpull($awards, null, 'getBadgePHID'); 248 - 249 - $badges = array(); 250 - foreach ($awards as $award) { 251 - $badge = $award->getBadge(); 252 - if ($badge->getStatus() == PhabricatorBadgesBadge::STATUS_ACTIVE) { 253 - $badges[$award->getBadgePHID()] = $badge; 254 - } 255 - } 256 - } 257 - 258 - if (count($badges)) { 259 - $flex = new PHUIBadgeBoxView(); 260 - 261 - foreach ($badges as $badge) { 262 - if ($badge) { 263 - $awarder_info = array(); 264 - 265 - $award = idx($awards, $badge->getPHID(), null); 266 - $awarder_phid = $award->getAwarderPHID(); 267 - $awarder_handle = $viewer->renderHandle($awarder_phid); 268 - 269 - $awarder_info = pht( 270 - 'Awarded by %s', 271 - $awarder_handle->render()); 272 - 273 - $item = id(new PHUIBadgeView()) 274 - ->setIcon($badge->getIcon()) 275 - ->setHeader($badge->getName()) 276 - ->setSubhead($badge->getFlavor()) 277 - ->setQuality($badge->getQuality()) 278 - ->setHref($badge->getViewURI()) 279 - ->addByLine($awarder_info); 280 - 281 - $flex->addItem($item); 282 - } 283 - } 284 - } else { 285 - $flex = id(new PHUIInfoView()) 286 - ->setSeverity(PHUIInfoView::SEVERITY_NODATA) 287 - ->appendChild(pht('User does not have any badges.')); 288 - } 289 - 290 - // Best option? 291 - $badges = id(new PhabricatorBadgesQuery()) 292 - ->setViewer($viewer) 293 - ->withStatuses(array( 294 - PhabricatorBadgesBadge::STATUS_ACTIVE, 295 - )) 296 - ->requireCapabilities( 297 - array( 298 - PhabricatorPolicyCapability::CAN_VIEW, 299 - PhabricatorPolicyCapability::CAN_EDIT, 300 - )) 301 - ->execute(); 302 - 303 - $button = id(new PHUIButtonView()) 304 - ->setTag('a') 305 - ->setIcon('fa-plus') 306 - ->setText(pht('Award')) 307 - ->setWorkflow(true) 308 - ->setHref('/badges/award/'.$user->getID().'/'); 309 - 310 - $can_award = false; 311 - if (count($badges)) { 312 - $can_award = true; 313 - } 314 - 315 - $header = id(new PHUIHeaderView()) 316 - ->setHeader(pht('Badges')); 317 - 318 - if (count($badges)) { 319 - $header->addActionLink($button); 320 - } 321 - 322 - $box = id(new PHUIObjectBoxView()) 323 - ->setHeader($header) 324 - ->addClass('project-view-badges') 325 - ->appendChild($flex) 326 222 ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY); 327 223 328 224 return $box;
+10
src/applications/people/engine/PhabricatorPeopleProfileMenuEngine.php
··· 6 6 const ITEM_PROFILE = 'people.profile'; 7 7 const ITEM_MANAGE = 'people.manage'; 8 8 const ITEM_PICTURE = 'people.picture'; 9 + const ITEM_BADGES = 'people.badges'; 9 10 10 11 protected function isMenuEngineConfigurable() { 11 12 return false; ··· 30 31 $items[] = $this->newItem() 31 32 ->setBuiltinKey(self::ITEM_PROFILE) 32 33 ->setMenuItemKey(PhabricatorPeopleDetailsProfileMenuItem::MENUITEMKEY); 34 + 35 + $have_badges = PhabricatorApplication::isClassInstalledForViewer( 36 + 'PhabricatorBadgesApplication', 37 + $viewer); 38 + if ($have_badges) { 39 + $items[] = $this->newItem() 40 + ->setBuiltinKey(self::ITEM_BADGES) 41 + ->setMenuItemKey(PhabricatorPeopleBadgesProfileMenuItem::MENUITEMKEY); 42 + } 33 43 34 44 $have_maniphest = PhabricatorApplication::isClassInstalledForViewer( 35 45 'PhabricatorManiphestApplication',
+59
src/applications/people/menuitem/PhabricatorPeopleBadgesProfileMenuItem.php
··· 1 + <?php 2 + 3 + final class PhabricatorPeopleBadgesProfileMenuItem 4 + extends PhabricatorProfileMenuItem { 5 + 6 + const MENUITEMKEY = 'people.badges'; 7 + 8 + public function getMenuItemTypeName() { 9 + return pht('Badges'); 10 + } 11 + 12 + private function getDefaultName() { 13 + return pht('Badges'); 14 + } 15 + 16 + public function canHideMenuItem( 17 + PhabricatorProfileMenuItemConfiguration $config) { 18 + return true; 19 + } 20 + 21 + public function getDisplayName( 22 + PhabricatorProfileMenuItemConfiguration $config) { 23 + $name = $config->getMenuItemProperty('name'); 24 + 25 + if (strlen($name)) { 26 + return $name; 27 + } 28 + 29 + return $this->getDefaultName(); 30 + } 31 + 32 + public function buildEditEngineFields( 33 + PhabricatorProfileMenuItemConfiguration $config) { 34 + return array( 35 + id(new PhabricatorTextEditField()) 36 + ->setKey('name') 37 + ->setLabel(pht('Name')) 38 + ->setPlaceholder($this->getDefaultName()) 39 + ->setValue($config->getMenuItemProperty('name')), 40 + ); 41 + } 42 + 43 + protected function newNavigationMenuItems( 44 + PhabricatorProfileMenuItemConfiguration $config) { 45 + 46 + $user = $config->getProfileObject(); 47 + $id = $user->getID(); 48 + 49 + $item = $this->newItem() 50 + ->setHref("/people/badges/{$id}/") 51 + ->setName($this->getDisplayName($config)) 52 + ->setIcon('fa-trophy'); 53 + 54 + return array( 55 + $item, 56 + ); 57 + } 58 + 59 + }
-4
webroot/rsrc/css/application/project/project-view.css
··· 67 67 text-align: center; 68 68 } 69 69 70 - .project-view-badges .phui-badge-flex-view { 71 - background-color: #fff; 72 - } 73 - 74 70 .project-view-home .phui-box-grey .phui-oi-attribute .phui-icon-view { 75 71 color: {$lightgreytext}; 76 72 }
+2 -3
webroot/rsrc/css/phui/phui-badge.css
··· 3 3 */ 4 4 5 5 .phui-badge-flex-view { 6 - padding: 12px 4px 8px; 6 + padding: 0; 7 7 overflow: hidden; 8 - text-align: center; 9 8 } 10 9 11 10 .phui-badge-flex-item { 12 11 display: inline-block; 13 - padding: 4px 8px; 12 + padding: 4px 12px 4px 0; 14 13 } 15 14 16 15 .phui-badge-flex-view.flex-view-collapsed {