@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.

Make documentation items in user menu update as you navigate in Quicksand

Summary: Ref T5867. I sure love Javascript.

Test Plan: Navigated between Home, Diffusion and Differential, opening the user profile menu. Saw appropraite help items.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T5867

Differential Revision: https://secure.phabricator.com/D17214

+139 -70
+19 -14
resources/celerity/map.php
··· 10 10 'conpherence.pkg.css' => '0b64e988', 11 11 'conpherence.pkg.js' => '6249a1cf', 12 12 'core.pkg.css' => '85f51b68', 13 - 'core.pkg.js' => '2c684890', 13 + 'core.pkg.js' => '666970d7', 14 14 'darkconsole.pkg.js' => 'e7393ebb', 15 15 'differential.pkg.css' => '9535a7e6', 16 16 'differential.pkg.js' => 'ddfeb49b', ··· 374 374 'rsrc/image/texture/table_header_hover.png' => '038ec3b9', 375 375 'rsrc/image/texture/table_header_tall.png' => 'd56b434f', 376 376 'rsrc/js/application/aphlict/Aphlict.js' => '5359e785', 377 - 'rsrc/js/application/aphlict/behavior-aphlict-dropdown.js' => '2a171a9d', 377 + 'rsrc/js/application/aphlict/behavior-aphlict-dropdown.js' => 'caade6f2', 378 378 'rsrc/js/application/aphlict/behavior-aphlict-listen.js' => 'fb20ac8d', 379 379 'rsrc/js/application/aphlict/behavior-aphlict-status.js' => '5e2634b9', 380 380 'rsrc/js/application/aphlict/behavior-desktop-notifications-control.js' => 'edd1ba66', ··· 530 530 'rsrc/js/core/behavior-toggle-class.js' => '92b9ec77', 531 531 'rsrc/js/core/behavior-tokenizer.js' => 'b3a4b884', 532 532 'rsrc/js/core/behavior-tooltip.js' => '42fcb747', 533 + 'rsrc/js/core/behavior-user-menu.js' => '31420f77', 533 534 'rsrc/js/core/behavior-watch-anchor.js' => '9f36c42d', 534 535 'rsrc/js/core/behavior-workflow.js' => '0a3f3021', 535 536 'rsrc/js/core/phtize.js' => 'd254d646', ··· 595 596 'inline-comment-summary-css' => '51efda3a', 596 597 'javelin-aphlict' => '5359e785', 597 598 'javelin-behavior' => '61cbc29a', 598 - 'javelin-behavior-aphlict-dropdown' => '2a171a9d', 599 + 'javelin-behavior-aphlict-dropdown' => 'caade6f2', 599 600 'javelin-behavior-aphlict-listen' => 'fb20ac8d', 600 601 'javelin-behavior-aphlict-status' => '5e2634b9', 601 602 'javelin-behavior-aphront-basic-tokenizer' => 'b3a4b884', ··· 719 720 'javelin-behavior-toggle-widget' => '3dbf94d5', 720 721 'javelin-behavior-typeahead-browse' => '635de1ec', 721 722 'javelin-behavior-typeahead-search' => '93d0c9e3', 723 + 'javelin-behavior-user-menu' => '31420f77', 722 724 'javelin-behavior-view-placeholder' => '47830651', 723 725 'javelin-behavior-workflow' => '0a3f3021', 724 726 'javelin-color' => '7e41274a', ··· 1114 1116 'javelin-install', 1115 1117 'javelin-util', 1116 1118 ), 1117 - '2a171a9d' => array( 1118 - 'javelin-behavior', 1119 - 'javelin-request', 1120 - 'javelin-stratcom', 1121 - 'javelin-vector', 1122 - 'javelin-dom', 1123 - 'javelin-uri', 1124 - 'javelin-behavior-device', 1125 - 'phabricator-title', 1126 - 'phabricator-favicon', 1127 - ), 1128 1119 '2b8de964' => array( 1129 1120 'javelin-install', 1130 1121 'javelin-util', ··· 1143 1134 ), 1144 1135 '2ee659ce' => array( 1145 1136 'javelin-install', 1137 + ), 1138 + '31420f77' => array( 1139 + 'javelin-behavior', 1146 1140 ), 1147 1141 '320810c8' => array( 1148 1142 'javelin-install', ··· 1998 1992 'javelin-dom', 1999 1993 'javelin-stratcom', 2000 1994 'phabricator-phtize', 1995 + ), 1996 + 'caade6f2' => array( 1997 + 'javelin-behavior', 1998 + 'javelin-request', 1999 + 'javelin-stratcom', 2000 + 'javelin-vector', 2001 + 'javelin-dom', 2002 + 'javelin-uri', 2003 + 'javelin-behavior-device', 2004 + 'phabricator-title', 2005 + 'phabricator-favicon', 2001 2006 ), 2002 2007 'ccf1cbf8' => array( 2003 2008 'javelin-install',
+14 -3
src/applications/base/PhabricatorApplication.php
··· 175 175 foreach ($articles as $article) { 176 176 $item = id(new PhabricatorActionView()) 177 177 ->setName($article['name']) 178 - ->setHref($article['href']); 178 + ->setHref($article['href']) 179 + ->addSigil('help-item') 180 + ->setOpenInNewWindow(true); 179 181 $items[] = $item; 180 182 } 181 183 } ··· 189 191 $href = '/applications/mailcommands/'.$class.'/'.$key.'/'; 190 192 $item = id(new PhabricatorActionView()) 191 193 ->setName($spec['name']) 192 - ->setHref($href); 194 + ->setHref($href) 195 + ->addSigil('help-item') 196 + ->setOpenInNewWindow(true); 193 197 $items[] = $item; 194 198 } 195 199 } 196 200 197 - return $items; 201 + if ($items) { 202 + $divider = id(new PhabricatorActionView()) 203 + ->addSigil('help-item') 204 + ->setType(PhabricatorActionView::TYPE_DIVIDER); 205 + array_unshift($items, $divider); 206 + } 207 + 208 + return array_values($items); 198 209 } 199 210 200 211 public function getHelpDocumentationArticles(PhabricatorUser $viewer) {
+25 -16
src/applications/home/application/PhabricatorHomeApplication.php
··· 2 2 3 3 final class PhabricatorHomeApplication extends PhabricatorApplication { 4 4 5 - private $application; 6 - 7 5 const DASHBOARD_DEFAULT = 'dashboard:default'; 8 6 9 7 public function getBaseURI() { ··· 53 51 } 54 52 55 53 $image = $viewer->getProfileImageURI(); 56 - if ($controller) { 57 - $this->application = $controller->getCurrentApplication(); 58 - } 59 54 60 55 $profile_image = id(new PHUIIconView()) 61 56 ->setImage($image) 62 57 ->setHeadSize(PHUIIconView::HEAD_SMALL); 58 + 59 + if ($controller) { 60 + $application = $controller->getCurrentApplication(); 61 + } else { 62 + $application = null; 63 + } 64 + $dropdown_menu = $this->renderUserDropdown($viewer, $application); 65 + 66 + $menu_id = celerity_generate_unique_node_id(); 67 + 68 + Javelin::initBehavior( 69 + 'user-menu', 70 + array( 71 + 'menuID' => $menu_id, 72 + 'menu' => $dropdown_menu->getDropdownMenuMetadata(), 73 + )); 63 74 64 75 return id(new PHUIButtonView()) 76 + ->setID($menu_id) 65 77 ->setTag('a') 66 78 ->setHref('/p/'.$viewer->getUsername().'/') 67 79 ->setIcon($profile_image) 68 80 ->addClass('phabricator-core-user-menu') 69 - ->setNoCSS(true) 70 - ->setDropdown(true) 71 - ->setDropdownMenu($this->renderUserDropdown($viewer)); 81 + ->setHasCaret(true) 82 + ->setNoCSS(true); 72 83 } 73 84 74 - private function renderUserDropdown(PhabricatorUser $viewer) { 85 + private function renderUserDropdown( 86 + PhabricatorUser $viewer, 87 + $application) { 75 88 76 89 $view = id(new PhabricatorActionListView()) 77 90 ->setViewer($viewer); ··· 98 111 ->setHref('/people/manage/'.$viewer->getID().'/')); 99 112 100 113 // Help Menus 101 - if ($this->application) { 102 - $application = $this->application; 114 + if ($application) { 103 115 $help_links = $application->getHelpMenuItems($viewer); 104 116 if ($help_links) { 105 - $view->addAction( 106 - id(new PhabricatorActionView()) 107 - ->setType(PhabricatorActionView::TYPE_DIVIDER)); 108 - 109 117 foreach ($help_links as $link) { 110 - $link->setOpenInNewWindow(true); 111 118 $view->addAction($link); 112 119 } 113 120 } ··· 116 123 // Logout Menu 117 124 $view->addAction( 118 125 id(new PhabricatorActionView()) 126 + ->addSigil('logout-item') 119 127 ->setType(PhabricatorActionView::TYPE_DIVIDER)); 120 128 121 129 $view->addAction( 122 130 id(new PhabricatorActionView()) 123 131 ->setName(pht('Log Out %s', $viewer->getUsername())) 132 + ->addSigil('logout-item') 124 133 ->setHref('/logout/') 125 134 ->setWorkflow(true)); 126 135
+1 -1
src/view/layout/PhabricatorActionView.php
··· 278 278 array($icon, $this->name, $caret)); 279 279 } 280 280 } else { 281 - $item = phutil_tag( 281 + $item = javelin_tag( 282 282 'span', 283 283 array( 284 284 'class' => 'phabricator-action-view-item',
+12 -17
src/view/page/PhabricatorStandardPageView.php
··· 767 767 ->setViewer($viewer); 768 768 $dropdown_query->execute(); 769 769 770 - $rendered_dropdowns = array(); 771 - $applications = array( 772 - 'PhabricatorHomeApplication', 773 - ); 774 - foreach ($applications as $application_class) { 775 - if (!PhabricatorApplication::isClassInstalledForViewer( 776 - $application_class, 777 - $viewer)) { 778 - continue; 779 - } 780 - $application = PhabricatorApplication::getByClass($application_class); 781 - $menu = $application->buildMainMenuExtraNodes($viewer, $controller); 782 - // TODO: Doesn't work with Quicksand active. 783 - $rendered_dropdowns[$application_class] = hsprintf('%s', $menu); 784 - } 785 - 786 770 $hisec_warning_config = $this->getHighSecurityWarningConfig(); 787 771 788 772 $console_config = null; ··· 798 782 799 783 $application_class = null; 800 784 $application_search_icon = null; 785 + $application_help = null; 801 786 $controller = $this->getController(); 802 787 if ($controller) { 803 788 $application = $controller->getCurrentApplication(); ··· 806 791 if ($application->getApplicationSearchDocumentTypes()) { 807 792 $application_search_icon = $application->getIcon(); 808 793 } 794 + 795 + $help_items = $application->getHelpMenuItems($viewer); 796 + if ($help_items) { 797 + $help_list = id(new PhabricatorActionListView()) 798 + ->setViewer($viewer); 799 + foreach ($help_items as $help_item) { 800 + $help_list->addAction($help_item); 801 + } 802 + $application_help = $help_list->getDropdownMenuMetadata(); 803 + } 809 804 } 810 805 } 811 806 ··· 817 812 $dropdown_query->getConpherenceData(), 818 813 ), 819 814 'globalDragAndDrop' => $upload_enabled, 820 - 'aphlictDropdowns' => $rendered_dropdowns, 821 815 'hisecWarningConfig' => $hisec_warning_config, 822 816 'consoleConfig' => $console_config, 823 817 'applicationClass' => $application_class, 824 818 'applicationSearchIcon' => $application_search_icon, 819 + 'helpItems' => $application_help, 825 820 ) + $this->buildAphlictListenConfigData(); 826 821 } 827 822
+11 -1
src/view/phui/PHUIButtonView.php
··· 28 28 private $name; 29 29 private $tooltip; 30 30 private $noCSS; 31 + private $hasCaret; 31 32 32 33 public function setName($name) { 33 34 $this->name = $name; ··· 91 92 public function setNoCSS($no_css) { 92 93 $this->noCSS = $no_css; 93 94 return $this; 95 + } 96 + 97 + public function setHasCaret($has_caret) { 98 + $this->hasCaret = $has_caret; 99 + return $this; 100 + } 101 + 102 + public function getHasCaret() { 103 + return $this->hasCaret; 94 104 } 95 105 96 106 public function setIcon($icon, $first = true) { ··· 201 211 } 202 212 203 213 $caret = null; 204 - if ($this->dropdown) { 214 + if ($this->dropdown || $this->getHasCaret()) { 205 215 $caret = phutil_tag('span', array('class' => 'caret'), ''); 206 216 } 207 217
-18
webroot/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js
··· 91 91 null, 92 92 function (e) { 93 93 var data = e.getData(); 94 - if (config.local && config.applicationClass) { 95 - var local_dropdowns = data.newResponse.aphlictDropdowns; 96 - if (local_dropdowns[config.applicationClass]) { 97 - JX.DOM.replace( 98 - dropdown, 99 - JX.$H(local_dropdowns[config.applicationClass])); 100 - dropdown = JX.$(config.dropdownID); 101 - if (dropdown.childNodes.length === 0) { 102 - JX.DOM.hide(bubble); 103 - } else { 104 - JX.DOM.show(bubble); 105 - } 106 - } else { 107 - JX.DOM.hide(bubble); 108 - } 109 - return; 110 - } 111 - 112 94 if (!data.fromServer) { 113 95 return; 114 96 }
+57
webroot/rsrc/js/core/behavior-user-menu.js
··· 1 + /** 2 + * @provides javelin-behavior-user-menu 3 + * @requires javelin-behavior 4 + */ 5 + 6 + JX.behavior('user-menu', function(config) { 7 + var node = JX.$(config.menuID); 8 + var list = JX.$H(config.menu.items).getFragment().firstChild; 9 + 10 + var menu = new JX.PHUIXDropdownMenu(node); 11 + 12 + menu.listen('open', function() { 13 + menu.setContent(list); 14 + }); 15 + 16 + // When the user navigates to a new page, we may need to update the links 17 + // to documentation in the menu. 18 + JX.Stratcom.listen('quicksand-redraw', null, function(e) { 19 + var data = e.getData(); 20 + 21 + var new_help = data.newResponse.helpItems; 22 + var nodes; 23 + if (new_help) { 24 + nodes = JX.$H(new_help.items).getFragment().firstChild.children; 25 + } else { 26 + nodes = []; 27 + } 28 + 29 + var ii; 30 + 31 + var tail = []; 32 + for (ii = list.children.length - 1; ii >= 0; ii--) { 33 + var node = list.children[ii]; 34 + 35 + // Remove any old help items. 36 + if (JX.Stratcom.hasSigil(node.firstChild, 'help-item')) { 37 + JX.DOM.remove(node); 38 + } 39 + 40 + // Place the logout items aside, if any exist. 41 + if (JX.Stratcom.hasSigil(node.firstChild, 'logout-item')) { 42 + JX.DOM.remove(node); 43 + tail.push(node); 44 + } 45 + } 46 + 47 + while (nodes.length) { 48 + list.appendChild(nodes[0]); 49 + } 50 + 51 + tail.reverse(); 52 + for (ii = 0; ii < tail.length; ii++) { 53 + list.appendChild(tail[ii]); 54 + } 55 + }); 56 + 57 + });