diff --git a/externals/recaptcha/LICENSE b/externals/recaptcha/LICENSE
deleted file mode 100644
index b612f71f01..0000000000
--- a/externals/recaptcha/LICENSE
+++ /dev/null
@@ -1,22 +0,0 @@
-Copyright (c) 2007 reCAPTCHA -- http://recaptcha.net
-AUTHORS:
- Mike Crawford
- Ben Maurer
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
diff --git a/externals/recaptcha/recaptchalib.php b/externals/recaptcha/recaptchalib.php
deleted file mode 100644
index 32c4f4d758..0000000000
--- a/externals/recaptcha/recaptchalib.php
+++ /dev/null
@@ -1,277 +0,0 @@
- $value )
- $req .= $key . '=' . urlencode( stripslashes($value) ) . '&';
-
- // Cut the last '&'
- $req=substr($req,0,strlen($req)-1);
- return $req;
-}
-
-
-
-/**
- * Submits an HTTP POST to a reCAPTCHA server
- * @param string $host
- * @param string $path
- * @param array $data
- * @param int port
- * @return array response
- */
-function _recaptcha_http_post($host, $path, $data, $port = 80) {
-
- $req = _recaptcha_qsencode ($data);
-
- $http_request = "POST $path HTTP/1.0\r\n";
- $http_request .= "Host: $host\r\n";
- $http_request .= "Content-Type: application/x-www-form-urlencoded;\r\n";
- $http_request .= "Content-Length: " . strlen($req) . "\r\n";
- $http_request .= "User-Agent: reCAPTCHA/PHP\r\n";
- $http_request .= "\r\n";
- $http_request .= $req;
-
- $response = '';
- if( false == ( $fs = @fsockopen($host, $port, $errno, $errstr, 10) ) ) {
- die ('Could not open socket');
- }
-
- fwrite($fs, $http_request);
-
- while ( !feof($fs) )
- $response .= fgets($fs, 1160); // One TCP-IP packet
- fclose($fs);
- $response = explode("\r\n\r\n", $response, 2);
-
- return $response;
-}
-
-
-
-/**
- * Gets the challenge HTML (javascript and non-javascript version).
- * This is called from the browser, and the resulting reCAPTCHA HTML widget
- * is embedded within the HTML form it was called from.
- * @param string $pubkey A public key for reCAPTCHA
- * @param string $error The error given by reCAPTCHA (optional, default is null)
- * @param boolean $use_ssl Should the request be made over ssl? (optional, default is false)
-
- * @return string - The HTML to be embedded in the user's form.
- */
-function recaptcha_get_html ($pubkey, $error = null, $use_ssl = false)
-{
- if ($pubkey == null || $pubkey == '') {
- die ("To use reCAPTCHA you must get an API key from https://www.google.com/recaptcha/admin/create");
- }
-
- if ($use_ssl) {
- $server = RECAPTCHA_API_SECURE_SERVER;
- } else {
- $server = RECAPTCHA_API_SERVER;
- }
-
- $errorpart = "";
- if ($error) {
- $errorpart = "&error=" . $error;
- }
- return '
-
- ';
-}
-
-
-
-
-/**
- * A ReCaptchaResponse is returned from recaptcha_check_answer()
- */
-class ReCaptchaResponse {
- var $is_valid;
- var $error;
-}
-
-
-/**
- * Calls an HTTP POST function to verify if the user's guess was correct
- * @param string $privkey
- * @param string $remoteip
- * @param string $challenge
- * @param string $response
- * @param array $extra_params an array of extra variables to post to the server
- * @return ReCaptchaResponse
- */
-function recaptcha_check_answer ($privkey, $remoteip, $challenge, $response, $extra_params = array())
-{
- if ($privkey == null || $privkey == '') {
- die ("To use reCAPTCHA you must get an API key from https://www.google.com/recaptcha/admin/create");
- }
-
- if ($remoteip == null || $remoteip == '') {
- die ("For security reasons, you must pass the remote ip to reCAPTCHA");
- }
-
-
-
- //discard spam submissions
- if ($challenge == null || strlen($challenge) == 0 || $response == null || strlen($response) == 0) {
- $recaptcha_response = new ReCaptchaResponse();
- $recaptcha_response->is_valid = false;
- $recaptcha_response->error = 'incorrect-captcha-sol';
- return $recaptcha_response;
- }
-
- $response = _recaptcha_http_post (RECAPTCHA_VERIFY_SERVER, "/recaptcha/api/verify",
- array (
- 'privatekey' => $privkey,
- 'remoteip' => $remoteip,
- 'challenge' => $challenge,
- 'response' => $response
- ) + $extra_params
- );
-
- $answers = explode ("\n", $response [1]);
- $recaptcha_response = new ReCaptchaResponse();
-
- if (trim ($answers [0]) == 'true') {
- $recaptcha_response->is_valid = true;
- }
- else {
- $recaptcha_response->is_valid = false;
- $recaptcha_response->error = $answers [1];
- }
- return $recaptcha_response;
-
-}
-
-/**
- * gets a URL where the user can sign up for reCAPTCHA. If your application
- * has a configuration page where you enter a key, you should provide a link
- * using this function.
- * @param string $domain The domain where the page is hosted
- * @param string $appname The name of your application
- */
-function recaptcha_get_signup_url ($domain = null, $appname = null) {
- return "https://www.google.com/recaptcha/admin/create?" . _recaptcha_qsencode (array ('domains' => $domain, 'app' => $appname));
-}
-
-function _recaptcha_aes_pad($val) {
- $block_size = 16;
- $numpad = $block_size - (strlen ($val) % $block_size);
- return str_pad($val, strlen ($val) + $numpad, chr($numpad));
-}
-
-/* Mailhide related code */
-
-function _recaptcha_aes_encrypt($val,$ky) {
- if (! function_exists ("mcrypt_encrypt")) {
- die ("To use reCAPTCHA Mailhide, you need to have the mcrypt php module installed.");
- }
- $mode=MCRYPT_MODE_CBC;
- $enc=MCRYPT_RIJNDAEL_128;
- $val=_recaptcha_aes_pad($val);
- return mcrypt_encrypt($enc, $ky, $val, $mode, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0");
-}
-
-
-function _recaptcha_mailhide_urlbase64 ($x) {
- return strtr(base64_encode ($x), '+/', '-_');
-}
-
-/* gets the reCAPTCHA Mailhide url for a given email, public key and private key */
-function recaptcha_mailhide_url($pubkey, $privkey, $email) {
- if ($pubkey == '' || $pubkey == null || $privkey == "" || $privkey == null) {
- die ("To use reCAPTCHA Mailhide, you have to sign up for a public and private key, " .
- "you can do so at http://www.google.com/recaptcha/mailhide/apikey");
- }
-
-
- $ky = pack('H*', $privkey);
- $cryptmail = _recaptcha_aes_encrypt ($email, $ky);
-
- return "http://www.google.com/recaptcha/mailhide/d?k=" . $pubkey . "&c=" . _recaptcha_mailhide_urlbase64 ($cryptmail);
-}
-
-/**
- * gets the parts of the email to expose to the user.
- * eg, given johndoe@example,com return ["john", "example.com"].
- * the email is then displayed as john...@example.com
- */
-function _recaptcha_mailhide_email_parts ($email) {
- $arr = preg_split("/@/", $email );
-
- if (strlen ($arr[0]) <= 4) {
- $arr[0] = substr ($arr[0], 0, 1);
- } else if (strlen ($arr[0]) <= 6) {
- $arr[0] = substr ($arr[0], 0, 3);
- } else {
- $arr[0] = substr ($arr[0], 0, 4);
- }
- return $arr;
-}
-
-/**
- * Gets html to display an email address given a public an private key.
- * to get a key, go to:
- *
- * http://www.google.com/recaptcha/mailhide/apikey
- */
-function recaptcha_mailhide_html($pubkey, $privkey, $email) {
- $emailparts = _recaptcha_mailhide_email_parts ($email);
- $url = recaptcha_mailhide_url ($pubkey, $privkey, $email);
-
- return htmlentities($emailparts[0]) . "...@" . htmlentities ($emailparts [1]);
-
-}
-
-
-?>
diff --git a/resources/celerity/map.php b/resources/celerity/map.php
index 4344b23a8d..7bf3396ca8 100644
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -9,8 +9,8 @@ return array(
'names' => array(
'conpherence.pkg.css' => 'e25569a9',
'conpherence.pkg.js' => '6249a1cf',
- 'core.pkg.css' => 'ea0e9c0c',
- 'core.pkg.js' => '2291d3b2',
+ 'core.pkg.css' => '867a3ad4',
+ 'core.pkg.js' => '1fa7c0c5',
'darkconsole.pkg.js' => 'e7393ebb',
'differential.pkg.css' => '4815647b',
'differential.pkg.js' => 'ddfeb49b',
@@ -27,17 +27,16 @@ return array(
'rsrc/css/aphront/notification.css' => '3f6c89c9',
'rsrc/css/aphront/panel-view.css' => '8427b78d',
'rsrc/css/aphront/phabricator-nav-view.css' => 'b29426e9',
- 'rsrc/css/aphront/table-view.css' => '3225137a',
+ 'rsrc/css/aphront/table-view.css' => '213a5981',
'rsrc/css/aphront/tokenizer.css' => '9a8cb501',
- 'rsrc/css/aphront/tooltip.css' => '1a07aea8',
+ 'rsrc/css/aphront/tooltip.css' => '173b9431',
'rsrc/css/aphront/typeahead-browse.css' => '8904346a',
'rsrc/css/aphront/typeahead.css' => 'd4f16145',
'rsrc/css/application/almanac/almanac.css' => 'dbb9b3af',
'rsrc/css/application/auth/auth.css' => '0877ed6e',
- 'rsrc/css/application/base/main-menu-view.css' => '8eac4166',
+ 'rsrc/css/application/base/main-menu-view.css' => '62c04564',
'rsrc/css/application/base/notification-menu.css' => '6a697e43',
- 'rsrc/css/application/base/phabricator-application-launch-view.css' => '95351601',
- 'rsrc/css/application/base/phui-theme.css' => '798c69b8',
+ 'rsrc/css/application/base/phui-theme.css' => '9f261c6b',
'rsrc/css/application/base/standard-page-view.css' => '894d8a25',
'rsrc/css/application/chatlog/chatlog.css' => 'd295b020',
'rsrc/css/application/conduit/conduit-api.css' => '7bc725c4',
@@ -68,7 +67,7 @@ return array(
'rsrc/css/application/differential/table-of-contents.css' => 'ae4b7a55',
'rsrc/css/application/diffusion/diffusion-icons.css' => 'd678600a',
'rsrc/css/application/diffusion/diffusion-readme.css' => '297373eb',
- 'rsrc/css/application/diffusion/diffusion-source.css' => '68b30fd3',
+ 'rsrc/css/application/diffusion/diffusion-source.css' => '750add59',
'rsrc/css/application/feed/feed.css' => 'ecd4ec57',
'rsrc/css/application/files/global-drag-and-drop.css' => '5c1b47c2',
'rsrc/css/application/flag/flag.css' => 'bba8f811',
@@ -82,6 +81,7 @@ return array(
'rsrc/css/application/objectselector/object-selector.css' => '85ee8ce6',
'rsrc/css/application/owners/owners-path-editor.css' => '2f00933b',
'rsrc/css/application/paste/paste.css' => '1898e534',
+ 'rsrc/css/application/people/people-picture-menu-item.css' => '1ac65ef7',
'rsrc/css/application/people/people-profile.css' => '2473d929',
'rsrc/css/application/phame/phame.css' => '53fa6236',
'rsrc/css/application/pholio/pholio-edit.css' => '07676f51',
@@ -97,12 +97,12 @@ 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' => 'f25746f5',
- 'rsrc/css/application/project/project-view.css' => '6936dc6e',
+ 'rsrc/css/application/project/project-view.css' => 'ceabdbaa',
'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',
'rsrc/css/application/releeph/releeph-request-typeahead.css' => '667a48ae',
- 'rsrc/css/application/search/application-search-view.css' => '8452c849',
+ 'rsrc/css/application/search/application-search-view.css' => '20ae9d85',
'rsrc/css/application/search/search-results.css' => '64ad079a',
'rsrc/css/application/slowvote/slowvote.css' => 'a94b7230',
'rsrc/css/application/tokens/tokens.css' => '3d0f239e',
@@ -113,13 +113,13 @@ return array(
'rsrc/css/core/z-index.css' => '5e72c4e0',
'rsrc/css/diviner/diviner-shared.css' => 'aa3656aa',
'rsrc/css/font/font-aleo.css' => '8bdb2835',
- 'rsrc/css/font/font-awesome.css' => '2b7ebbcc',
+ 'rsrc/css/font/font-awesome.css' => 'e838e088',
'rsrc/css/font/font-lato.css' => 'c7ccd872',
'rsrc/css/font/phui-font-icon-base.css' => '870a7360',
'rsrc/css/layout/phabricator-filetree-view.css' => 'fccf9f82',
'rsrc/css/layout/phabricator-source-code-view.css' => '4383192f',
'rsrc/css/phui/calendar/phui-calendar-day.css' => '572b1893',
- 'rsrc/css/phui/calendar/phui-calendar-list.css' => 'fcc9fb41',
+ 'rsrc/css/phui/calendar/phui-calendar-list.css' => 'eb5c774b',
'rsrc/css/phui/calendar/phui-calendar-month.css' => '8e10e92c',
'rsrc/css/phui/calendar/phui-calendar.css' => '477acfaa',
'rsrc/css/phui/object-item/phui-oi-big-ui.css' => '19f9369b',
@@ -131,10 +131,10 @@ return array(
'rsrc/css/phui/phui-action-list.css' => '5679229f',
'rsrc/css/phui/phui-action-panel.css' => '91c7b835',
'rsrc/css/phui/phui-badge.css' => '3baef8db',
- 'rsrc/css/phui/phui-basic-nav-view.css' => '7093573b',
+ 'rsrc/css/phui/phui-basic-nav-view.css' => '3d4b207b',
'rsrc/css/phui/phui-big-info-view.css' => 'bd903741',
- 'rsrc/css/phui/phui-box.css' => '33b629f8',
- 'rsrc/css/phui/phui-button.css' => '00ddac15',
+ 'rsrc/css/phui/phui-box.css' => '269cbc99',
+ 'rsrc/css/phui/phui-button.css' => '7eaff361',
'rsrc/css/phui/phui-chart.css' => '6bf6f78e',
'rsrc/css/phui/phui-cms.css' => 'be43c8a8',
'rsrc/css/phui/phui-comment-form.css' => '48fbd65d',
@@ -162,17 +162,16 @@ return array(
'rsrc/css/phui/phui-object-box.css' => '8b289e3d',
'rsrc/css/phui/phui-pager.css' => 'bea33d23',
'rsrc/css/phui/phui-pinboard-view.css' => '2495140e',
- 'rsrc/css/phui/phui-profile-menu.css' => 'c71ecdcd',
- 'rsrc/css/phui/phui-property-list-view.css' => '6d8e58ac',
+ 'rsrc/css/phui/phui-property-list-view.css' => '2dc7993f',
'rsrc/css/phui/phui-remarkup-preview.css' => '1a8f2591',
- 'rsrc/css/phui/phui-segment-bar-view.css' => '46342871',
+ 'rsrc/css/phui/phui-segment-bar-view.css' => 'b1d1b892',
'rsrc/css/phui/phui-spacing.css' => '042804d6',
'rsrc/css/phui/phui-status.css' => 'd5263e49',
'rsrc/css/phui/phui-tag-view.css' => '84d65f26',
'rsrc/css/phui/phui-timeline-view.css' => 'bc523970',
- 'rsrc/css/phui/phui-two-column-view.css' => 'f63cad3c',
- 'rsrc/css/phui/workboards/phui-workboard-color.css' => 'b60ef38a',
- 'rsrc/css/phui/workboards/phui-workboard.css' => 'c88912ee',
+ 'rsrc/css/phui/phui-two-column-view.css' => '8a1074c7',
+ 'rsrc/css/phui/workboards/phui-workboard-color.css' => 'f0551a33',
+ 'rsrc/css/phui/workboards/phui-workboard.css' => '3bc85455',
'rsrc/css/phui/workboards/phui-workcard.css' => 'cca5fa92',
'rsrc/css/phui/workboards/phui-workpanel.css' => 'a3a63478',
'rsrc/css/sprite-login.css' => '587d92d7',
@@ -189,10 +188,10 @@ return array(
'rsrc/externals/font/aleo/aleo-regular.ttf' => '751e7479',
'rsrc/externals/font/aleo/aleo-regular.woff' => 'c3744be9',
'rsrc/externals/font/aleo/aleo-regular.woff2' => '851aa0ee',
- 'rsrc/externals/font/fontawesome/fontawesome-webfont.eot' => '59b3076c',
- 'rsrc/externals/font/fontawesome/fontawesome-webfont.ttf' => '45ad7e57',
- 'rsrc/externals/font/fontawesome/fontawesome-webfont.woff' => 'f861e2a8',
- 'rsrc/externals/font/fontawesome/fontawesome-webfont.woff2' => '0ee0f078',
+ 'rsrc/externals/font/fontawesome/fontawesome-webfont.eot' => '24a7064f',
+ 'rsrc/externals/font/fontawesome/fontawesome-webfont.ttf' => '0039fe26',
+ 'rsrc/externals/font/fontawesome/fontawesome-webfont.woff' => 'de978a43',
+ 'rsrc/externals/font/fontawesome/fontawesome-webfont.woff2' => '2a832057',
'rsrc/externals/font/lato/lato-bold.eot' => '99fbcf8c',
'rsrc/externals/font/lato/lato-bold.svg' => '2aa83045',
'rsrc/externals/font/lato/lato-bold.ttf' => '0a7141f7',
@@ -488,7 +487,7 @@ return array(
'rsrc/js/core/ShapedRequest.js' => '7cbe244b',
'rsrc/js/core/TextAreaUtils.js' => '320810c8',
'rsrc/js/core/Title.js' => '485aaa6c',
- 'rsrc/js/core/ToolTip.js' => 'b5c62c3b',
+ 'rsrc/js/core/ToolTip.js' => '8fadb715',
'rsrc/js/core/behavior-active-nav.js' => 'e379b58e',
'rsrc/js/core/behavior-audio-source.js' => '59b251eb',
'rsrc/js/core/behavior-autofocus.js' => '7319e029',
@@ -529,7 +528,7 @@ return array(
'rsrc/js/core/behavior-time-typeahead.js' => '522431f7',
'rsrc/js/core/behavior-toggle-class.js' => '92b9ec77',
'rsrc/js/core/behavior-tokenizer.js' => 'b3a4b884',
- 'rsrc/js/core/behavior-tooltip.js' => '42fcb747',
+ 'rsrc/js/core/behavior-tooltip.js' => 'c420b0b9',
'rsrc/js/core/behavior-user-menu.js' => '31420f77',
'rsrc/js/core/behavior-watch-anchor.js' => '9f36c42d',
'rsrc/js/core/behavior-workflow.js' => '0a3f3021',
@@ -553,11 +552,11 @@ return array(
'aphront-list-filter-view-css' => '5d6f0526',
'aphront-multi-column-view-css' => '84cc6640',
'aphront-panel-view-css' => '8427b78d',
- 'aphront-table-view-css' => '3225137a',
+ 'aphront-table-view-css' => '213a5981',
'aphront-tokenizer-control-css' => '9a8cb501',
- 'aphront-tooltip-css' => '1a07aea8',
+ 'aphront-tooltip-css' => '173b9431',
'aphront-typeahead-control-css' => 'd4f16145',
- 'application-search-view-css' => '8452c849',
+ 'application-search-view-css' => '20ae9d85',
'auth-css' => '0877ed6e',
'bulk-job-css' => 'df9c1d4a',
'changeset-view-manager' => 'a2828756',
@@ -583,10 +582,10 @@ return array(
'differential-table-of-contents-css' => 'ae4b7a55',
'diffusion-icons-css' => 'd678600a',
'diffusion-readme-css' => '297373eb',
- 'diffusion-source-css' => '68b30fd3',
+ 'diffusion-source-css' => '750add59',
'diviner-shared-css' => 'aa3656aa',
'font-aleo' => '8bdb2835',
- 'font-fontawesome' => '2b7ebbcc',
+ 'font-fontawesome' => 'e838e088',
'font-lato' => 'c7ccd872',
'global-drag-and-drop-css' => '5c1b47c2',
'harbormaster-css' => 'f491c9f4',
@@ -681,7 +680,7 @@ return array(
'javelin-behavior-phabricator-reveal-content' => '60821bc7',
'javelin-behavior-phabricator-search-typeahead' => '06c32383',
'javelin-behavior-phabricator-show-older-transactions' => '94c65b72',
- 'javelin-behavior-phabricator-tooltips' => '42fcb747',
+ 'javelin-behavior-phabricator-tooltips' => 'c420b0b9',
'javelin-behavior-phabricator-transaction-comment-form' => 'b23b49e6',
'javelin-behavior-phabricator-transaction-list' => '13c739ea',
'javelin-behavior-phabricator-watch-anchor' => '9f36c42d',
@@ -779,9 +778,9 @@ return array(
'owners-path-editor-css' => '2f00933b',
'paste-css' => '1898e534',
'path-typeahead' => 'f7fc67ec',
+ 'people-picture-menu-item-css' => '1ac65ef7',
'people-profile-css' => '2473d929',
'phabricator-action-list-view-css' => '5679229f',
- 'phabricator-application-launch-view-css' => '95351601',
'phabricator-busy' => '59a7976a',
'phabricator-chatlog-css' => 'd295b020',
'phabricator-content-source-view-css' => '4b8b05d4',
@@ -798,7 +797,7 @@ return array(
'phabricator-flag-css' => 'bba8f811',
'phabricator-keyboard-shortcut' => '1ae869f2',
'phabricator-keyboard-shortcut-manager' => '4a021c10',
- 'phabricator-main-menu-view' => '8eac4166',
+ 'phabricator-main-menu-view' => '62c04564',
'phabricator-nav-view-css' => 'b29426e9',
'phabricator-notification' => 'ccf1cbf8',
'phabricator-notification-css' => '3f6c89c9',
@@ -814,7 +813,7 @@ return array(
'phabricator-standard-page-view' => '894d8a25',
'phabricator-textareautils' => '320810c8',
'phabricator-title' => '485aaa6c',
- 'phabricator-tooltip' => 'b5c62c3b',
+ 'phabricator-tooltip' => '8fadb715',
'phabricator-ui-example-css' => '528b19de',
'phabricator-uiexample-javelin-view' => 'd4a14807',
'phabricator-uiexample-reactor-button' => 'd19198c8',
@@ -839,13 +838,13 @@ return array(
'phriction-document-css' => '4282e4ad',
'phui-action-panel-css' => '91c7b835',
'phui-badge-view-css' => '3baef8db',
- 'phui-basic-nav-view-css' => '7093573b',
+ 'phui-basic-nav-view-css' => '3d4b207b',
'phui-big-info-view-css' => 'bd903741',
- 'phui-box-css' => '33b629f8',
- 'phui-button-css' => '00ddac15',
+ 'phui-box-css' => '269cbc99',
+ 'phui-button-css' => '7eaff361',
'phui-calendar-css' => '477acfaa',
'phui-calendar-day-css' => '572b1893',
- 'phui-calendar-list-css' => 'fcc9fb41',
+ 'phui-calendar-list-css' => 'eb5c774b',
'phui-calendar-month-css' => '8e10e92c',
'phui-chart-css' => '6bf6f78e',
'phui-cms-css' => 'be43c8a8',
@@ -883,18 +882,17 @@ return array(
'phui-oi-simple-ui-css' => 'a8beebea',
'phui-pager-css' => 'bea33d23',
'phui-pinboard-view-css' => '2495140e',
- 'phui-profile-menu-css' => 'c71ecdcd',
- 'phui-property-list-view-css' => '6d8e58ac',
+ 'phui-property-list-view-css' => '2dc7993f',
'phui-remarkup-preview-css' => '1a8f2591',
- 'phui-segment-bar-view-css' => '46342871',
+ 'phui-segment-bar-view-css' => 'b1d1b892',
'phui-spacing-css' => '042804d6',
'phui-status-list-view-css' => 'd5263e49',
'phui-tag-view-css' => '84d65f26',
- 'phui-theme-css' => '798c69b8',
+ 'phui-theme-css' => '9f261c6b',
'phui-timeline-view-css' => 'bc523970',
- 'phui-two-column-view-css' => 'f63cad3c',
- 'phui-workboard-color-css' => 'b60ef38a',
- 'phui-workboard-view-css' => 'c88912ee',
+ 'phui-two-column-view-css' => '8a1074c7',
+ 'phui-workboard-color-css' => 'f0551a33',
+ 'phui-workboard-view-css' => '3bc85455',
'phui-workcard-view-css' => 'cca5fa92',
'phui-workpanel-view-css' => 'a3a63478',
'phuix-action-list-view' => 'b5c256b8',
@@ -908,7 +906,7 @@ return array(
'policy-transaction-detail-css' => '82100a43',
'ponder-view-css' => 'fbd45f96',
'project-card-view-css' => 'f25746f5',
- 'project-view-css' => '6936dc6e',
+ 'project-view-css' => 'ceabdbaa',
'releeph-core' => '9b3c5733',
'releeph-preview-branch' => 'b7a6f4a5',
'releeph-request-differential-create-dialog' => '8d8b92cd',
@@ -1178,12 +1176,6 @@ return array(
'javelin-dom',
'javelin-request',
),
- '42fcb747' => array(
- 'javelin-behavior',
- 'javelin-behavior-device',
- 'javelin-stratcom',
- 'phabricator-tooltip',
- ),
'44959b73' => array(
'javelin-util',
'javelin-uri',
@@ -1375,6 +1367,9 @@ return array(
'javelin-magical-init',
'javelin-util',
),
+ '62c04564' => array(
+ 'phui-theme-css',
+ ),
'62dfea03' => array(
'javelin-install',
'javelin-util',
@@ -1612,8 +1607,11 @@ return array(
'javelin-stratcom',
'javelin-util',
),
- '8eac4166' => array(
- 'phui-theme-css',
+ '8fadb715' => array(
+ 'javelin-install',
+ 'javelin-util',
+ 'javelin-dom',
+ 'javelin-vector',
),
'8ff5e24c' => array(
'javelin-behavior',
@@ -1888,12 +1886,6 @@ return array(
'javelin-install',
'javelin-dom',
),
- 'b5c62c3b' => array(
- 'javelin-install',
- 'javelin-util',
- 'javelin-dom',
- 'javelin-vector',
- ),
'b5d57730' => array(
'javelin-install',
'javelin-stratcom',
@@ -1969,6 +1961,12 @@ return array(
'javelin-install',
'javelin-dom',
),
+ 'c420b0b9' => array(
+ 'javelin-behavior',
+ 'javelin-behavior-device',
+ 'javelin-stratcom',
+ 'phabricator-tooltip',
+ ),
'c587b80f' => array(
'javelin-install',
),
@@ -2318,8 +2316,6 @@ return array(
'phui-spacing-css',
'phui-form-css',
'phui-icon-view-css',
- 'phui-profile-menu-css',
- 'phabricator-application-launch-view-css',
'phabricator-action-list-view-css',
'phui-property-list-view-css',
'phui-tag-view-css',
diff --git a/resources/celerity/packages.php b/resources/celerity/packages.php
index 288bd69ed2..4822633426 100644
--- a/resources/celerity/packages.php
+++ b/resources/celerity/packages.php
@@ -130,9 +130,7 @@ return array(
'phui-spacing-css',
'phui-form-css',
'phui-icon-view-css',
- 'phui-profile-menu-css',
- 'phabricator-application-launch-view-css',
'phabricator-action-list-view-css',
'phui-property-list-view-css',
'phui-tag-view-css',
diff --git a/resources/sql/autopatches/20170131.dashboard.personal.01.php b/resources/sql/autopatches/20170131.dashboard.personal.01.php
new file mode 100644
index 0000000000..af62c017e8
--- /dev/null
+++ b/resources/sql/autopatches/20170131.dashboard.personal.01.php
@@ -0,0 +1,45 @@
+establishConnection('r');
+$table_name = 'dashboard_install';
+
+$search_table = new PhabricatorProfileMenuItemConfiguration();
+$search_conn = $search_table->establishConnection('w');
+$search_table_name = 'search_profilepanelconfiguration';
+
+$viewer = PhabricatorUser::getOmnipotentUser();
+$profile_phid = id(new PhabricatorHomeApplication())->getPHID();
+$menu_item_key = PhabricatorDashboardProfileMenuItem::MENUITEMKEY;
+
+foreach (new LiskRawMigrationIterator($conn, $table_name) as $install) {
+
+ $dashboard_phid = $install['dashboardPHID'];
+ $new_phid = id(new PhabricatorProfileMenuItemConfiguration())->generatePHID();
+ $menu_item_properties = json_encode(
+ array('dashboardPHID' => $dashboard_phid, 'name' => ''));
+
+ $custom_phid = $install['objectPHID'];
+ if ($custom_phid == 'dashboard:default') {
+ $custom_phid = null;
+ }
+
+ $menu_item_order = 0;
+
+ queryfx(
+ $search_conn,
+ 'INSERT INTO %T (phid, profilePHID, menuItemKey, menuItemProperties, '.
+ 'visibility, dateCreated, dateModified, menuItemOrder, customPHID) VALUES '.
+ '(%s, %s, %s, %s, %s, %d, %d, %d, %ns)',
+ $search_table_name,
+ $new_phid,
+ $profile_phid,
+ $menu_item_key,
+ $menu_item_properties,
+ 'visible',
+ PhabricatorTime::getNow(),
+ PhabricatorTime::getNow(),
+ $menu_item_order,
+ $custom_phid);
+
+}
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
index f145dd88ec..033e7c15d2 100644
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -635,6 +635,7 @@ phutil_register_library_map(array(
'DiffusionCommitEditController' => 'applications/diffusion/controller/DiffusionCommitEditController.php',
'DiffusionCommitEditEngine' => 'applications/diffusion/editor/DiffusionCommitEditEngine.php',
'DiffusionCommitFulltextEngine' => 'applications/repository/search/DiffusionCommitFulltextEngine.php',
+ 'DiffusionCommitHasPackageEdgeType' => 'applications/diffusion/edge/DiffusionCommitHasPackageEdgeType.php',
'DiffusionCommitHasRevisionEdgeType' => 'applications/diffusion/edge/DiffusionCommitHasRevisionEdgeType.php',
'DiffusionCommitHasRevisionRelationship' => 'applications/diffusion/relationships/DiffusionCommitHasRevisionRelationship.php',
'DiffusionCommitHasTaskEdgeType' => 'applications/diffusion/edge/DiffusionCommitHasTaskEdgeType.php',
@@ -1209,6 +1210,9 @@ phutil_register_library_map(array(
'HarbormasterBuildableTransactionEditor' => 'applications/harbormaster/editor/HarbormasterBuildableTransactionEditor.php',
'HarbormasterBuildableTransactionQuery' => 'applications/harbormaster/query/HarbormasterBuildableTransactionQuery.php',
'HarbormasterBuildableViewController' => 'applications/harbormaster/controller/HarbormasterBuildableViewController.php',
+ 'HarbormasterBuildkiteBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterBuildkiteBuildStepImplementation.php',
+ 'HarbormasterBuildkiteBuildableInterface' => 'applications/harbormaster/interface/HarbormasterBuildkiteBuildableInterface.php',
+ 'HarbormasterBuildkiteHookController' => 'applications/harbormaster/controller/HarbormasterBuildkiteHookController.php',
'HarbormasterBuiltinBuildStepGroup' => 'applications/harbormaster/stepgroup/HarbormasterBuiltinBuildStepGroup.php',
'HarbormasterCircleCIBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterCircleCIBuildStepImplementation.php',
'HarbormasterCircleCIBuildableInterface' => 'applications/harbormaster/interface/HarbormasterCircleCIBuildableInterface.php',
@@ -1684,6 +1688,7 @@ phutil_register_library_map(array(
'PHUIHandleView' => 'applications/phid/view/PHUIHandleView.php',
'PHUIHeadThingView' => 'view/phui/PHUIHeadThingView.php',
'PHUIHeaderView' => 'view/phui/PHUIHeaderView.php',
+ 'PHUIHomeView' => 'applications/home/view/PHUIHomeView.php',
'PHUIHovercardUIExample' => 'applications/uiexample/examples/PHUIHovercardUIExample.php',
'PHUIHovercardView' => 'view/phui/PHUIHovercardView.php',
'PHUIIconCircleView' => 'view/phui/PHUIIconCircleView.php',
@@ -1824,7 +1829,6 @@ phutil_register_library_map(array(
'PhabricatorApplicationEditController' => 'applications/meta/controller/PhabricatorApplicationEditController.php',
'PhabricatorApplicationEditHTTPParameterHelpView' => 'applications/transactions/view/PhabricatorApplicationEditHTTPParameterHelpView.php',
'PhabricatorApplicationEmailCommandsController' => 'applications/meta/controller/PhabricatorApplicationEmailCommandsController.php',
- 'PhabricatorApplicationLaunchView' => 'applications/meta/view/PhabricatorApplicationLaunchView.php',
'PhabricatorApplicationPanelController' => 'applications/meta/controller/PhabricatorApplicationPanelController.php',
'PhabricatorApplicationProfileMenuItem' => 'applications/search/menuitem/PhabricatorApplicationProfileMenuItem.php',
'PhabricatorApplicationQuery' => 'applications/meta/query/PhabricatorApplicationQuery.php',
@@ -1887,10 +1891,12 @@ phutil_register_library_map(array(
'PhabricatorAuditManagementWorkflow' => 'applications/audit/management/PhabricatorAuditManagementWorkflow.php',
'PhabricatorAuditReplyHandler' => 'applications/audit/mail/PhabricatorAuditReplyHandler.php',
'PhabricatorAuditStatusConstants' => 'applications/audit/constants/PhabricatorAuditStatusConstants.php',
+ 'PhabricatorAuditSynchronizeManagementWorkflow' => 'applications/audit/management/PhabricatorAuditSynchronizeManagementWorkflow.php',
'PhabricatorAuditTransaction' => 'applications/audit/storage/PhabricatorAuditTransaction.php',
'PhabricatorAuditTransactionComment' => 'applications/audit/storage/PhabricatorAuditTransactionComment.php',
'PhabricatorAuditTransactionQuery' => 'applications/audit/query/PhabricatorAuditTransactionQuery.php',
'PhabricatorAuditTransactionView' => 'applications/audit/view/PhabricatorAuditTransactionView.php',
+ 'PhabricatorAuditUpdateOwnersManagementWorkflow' => 'applications/audit/management/PhabricatorAuditUpdateOwnersManagementWorkflow.php',
'PhabricatorAuthAccountView' => 'applications/auth/view/PhabricatorAuthAccountView.php',
'PhabricatorAuthApplication' => 'applications/auth/application/PhabricatorAuthApplication.php',
'PhabricatorAuthAuthFactorPHIDType' => 'applications/auth/phid/PhabricatorAuthAuthFactorPHIDType.php',
@@ -2467,7 +2473,6 @@ phutil_register_library_map(array(
'PhabricatorDashboardEditController' => 'applications/dashboard/controller/PhabricatorDashboardEditController.php',
'PhabricatorDashboardIconSet' => 'applications/dashboard/icon/PhabricatorDashboardIconSet.php',
'PhabricatorDashboardInstall' => 'applications/dashboard/storage/PhabricatorDashboardInstall.php',
- 'PhabricatorDashboardInstallController' => 'applications/dashboard/controller/PhabricatorDashboardInstallController.php',
'PhabricatorDashboardLayoutConfig' => 'applications/dashboard/layoutconfig/PhabricatorDashboardLayoutConfig.php',
'PhabricatorDashboardListController' => 'applications/dashboard/controller/PhabricatorDashboardListController.php',
'PhabricatorDashboardManageController' => 'applications/dashboard/controller/PhabricatorDashboardManageController.php',
@@ -2673,7 +2678,6 @@ phutil_register_library_map(array(
'PhabricatorFactUpdateIterator' => 'applications/fact/extract/PhabricatorFactUpdateIterator.php',
'PhabricatorFavoritesApplication' => 'applications/favorites/application/PhabricatorFavoritesApplication.php',
'PhabricatorFavoritesController' => 'applications/favorites/controller/PhabricatorFavoritesController.php',
- 'PhabricatorFavoritesMainController' => 'applications/favorites/controller/PhabricatorFavoritesMainController.php',
'PhabricatorFavoritesMainMenuBarExtension' => 'applications/favorites/engineextension/PhabricatorFavoritesMainMenuBarExtension.php',
'PhabricatorFavoritesMenuItemController' => 'applications/favorites/controller/PhabricatorFavoritesMenuItemController.php',
'PhabricatorFavoritesProfileMenuEngine' => 'applications/favorites/engine/PhabricatorFavoritesProfileMenuEngine.php',
@@ -2835,17 +2839,16 @@ phutil_register_library_map(array(
'PhabricatorHomeApplication' => 'applications/home/application/PhabricatorHomeApplication.php',
'PhabricatorHomeConstants' => 'applications/home/constants/PhabricatorHomeConstants.php',
'PhabricatorHomeController' => 'applications/home/controller/PhabricatorHomeController.php',
- 'PhabricatorHomeMainController' => 'applications/home/controller/PhabricatorHomeMainController.php',
- 'PhabricatorHomeManageProfileMenuItem' => 'applications/home/menuitem/PhabricatorHomeManageProfileMenuItem.php',
- 'PhabricatorHomeMenuController' => 'applications/home/controller/PhabricatorHomeMenuController.php',
+ 'PhabricatorHomeLauncherProfileMenuItem' => 'applications/home/menuitem/PhabricatorHomeLauncherProfileMenuItem.php',
'PhabricatorHomeMenuItemController' => 'applications/home/controller/PhabricatorHomeMenuItemController.php',
- 'PhabricatorHomePreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorHomePreferencesSettingsPanel.php',
'PhabricatorHomeProfileMenuEngine' => 'applications/home/engine/PhabricatorHomeProfileMenuEngine.php',
+ 'PhabricatorHomeProfileMenuItem' => 'applications/home/menuitem/PhabricatorHomeProfileMenuItem.php',
'PhabricatorHovercardEngineExtension' => 'applications/search/engineextension/PhabricatorHovercardEngineExtension.php',
'PhabricatorHovercardEngineExtensionModule' => 'applications/search/engineextension/PhabricatorHovercardEngineExtensionModule.php',
'PhabricatorIDsSearchEngineExtension' => 'applications/search/engineextension/PhabricatorIDsSearchEngineExtension.php',
'PhabricatorIDsSearchField' => 'applications/search/field/PhabricatorIDsSearchField.php',
'PhabricatorIRCProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorIRCProtocolAdapter.php',
+ 'PhabricatorIconDatasource' => 'applications/files/typeahead/PhabricatorIconDatasource.php',
'PhabricatorIconRemarkupRule' => 'applications/macro/markup/PhabricatorIconRemarkupRule.php',
'PhabricatorIconSet' => 'applications/files/iconset/PhabricatorIconSet.php',
'PhabricatorIconSetEditField' => 'applications/transactions/editfield/PhabricatorIconSetEditField.php',
@@ -2881,6 +2884,7 @@ phutil_register_library_map(array(
'PhabricatorKeyring' => 'applications/files/keyring/PhabricatorKeyring.php',
'PhabricatorKeyringConfigOptionType' => 'applications/files/keyring/PhabricatorKeyringConfigOptionType.php',
'PhabricatorLDAPAuthProvider' => 'applications/auth/provider/PhabricatorLDAPAuthProvider.php',
+ 'PhabricatorLabelProfileMenuItem' => 'applications/search/menuitem/PhabricatorLabelProfileMenuItem.php',
'PhabricatorLegalpadApplication' => 'applications/legalpad/application/PhabricatorLegalpadApplication.php',
'PhabricatorLegalpadConfigOptions' => 'applications/legalpad/config/PhabricatorLegalpadConfigOptions.php',
'PhabricatorLegalpadDocumentPHIDType' => 'applications/legalpad/phid/PhabricatorLegalpadDocumentPHIDType.php',
@@ -2960,6 +2964,7 @@ phutil_register_library_map(array(
'PhabricatorMainMenuBarExtension' => 'view/page/menu/PhabricatorMainMenuBarExtension.php',
'PhabricatorMainMenuSearchView' => 'view/page/menu/PhabricatorMainMenuSearchView.php',
'PhabricatorMainMenuView' => 'view/page/menu/PhabricatorMainMenuView.php',
+ 'PhabricatorManageProfileMenuItem' => 'applications/search/menuitem/PhabricatorManageProfileMenuItem.php',
'PhabricatorManagementWorkflow' => 'infrastructure/management/PhabricatorManagementWorkflow.php',
'PhabricatorManiphestApplication' => 'applications/maniphest/application/PhabricatorManiphestApplication.php',
'PhabricatorManiphestConfigOptions' => 'applications/maniphest/config/PhabricatorManiphestConfigOptions.php',
@@ -3319,6 +3324,7 @@ phutil_register_library_map(array(
'PhabricatorPeopleNewController' => 'applications/people/controller/PhabricatorPeopleNewController.php',
'PhabricatorPeopleNoOwnerDatasource' => 'applications/people/typeahead/PhabricatorPeopleNoOwnerDatasource.php',
'PhabricatorPeopleOwnerDatasource' => 'applications/people/typeahead/PhabricatorPeopleOwnerDatasource.php',
+ 'PhabricatorPeoplePictureProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeoplePictureProfileMenuItem.php',
'PhabricatorPeopleProfileController' => 'applications/people/controller/PhabricatorPeopleProfileController.php',
'PhabricatorPeopleProfileEditController' => 'applications/people/controller/PhabricatorPeopleProfileEditController.php',
'PhabricatorPeopleProfileManageController' => 'applications/people/controller/PhabricatorPeopleProfileManageController.php',
@@ -5088,6 +5094,7 @@ phutil_register_library_map(array(
'PhabricatorExtendedPolicyInterface',
'HarbormasterBuildableInterface',
'HarbormasterCircleCIBuildableInterface',
+ 'HarbormasterBuildkiteBuildableInterface',
'PhabricatorApplicationTransactionInterface',
'PhabricatorDestructibleInterface',
),
@@ -5343,6 +5350,7 @@ phutil_register_library_map(array(
'DiffusionCommitEditController' => 'DiffusionController',
'DiffusionCommitEditEngine' => 'PhabricatorEditEngine',
'DiffusionCommitFulltextEngine' => 'PhabricatorFulltextEngine',
+ 'DiffusionCommitHasPackageEdgeType' => 'PhabricatorEdgeType',
'DiffusionCommitHasRevisionEdgeType' => 'PhabricatorEdgeType',
'DiffusionCommitHasRevisionRelationship' => 'DiffusionCommitRelationship',
'DiffusionCommitHasTaskEdgeType' => 'PhabricatorEdgeType',
@@ -6014,6 +6022,8 @@ phutil_register_library_map(array(
'HarbormasterBuildableTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
'HarbormasterBuildableTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'HarbormasterBuildableViewController' => 'HarbormasterController',
+ 'HarbormasterBuildkiteBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
+ 'HarbormasterBuildkiteHookController' => 'HarbormasterController',
'HarbormasterBuiltinBuildStepGroup' => 'HarbormasterBuildStepGroup',
'HarbormasterCircleCIBuildStepImplementation' => 'HarbormasterBuildStepImplementation',
'HarbormasterCircleCIHookController' => 'HarbormasterController',
@@ -6556,6 +6566,7 @@ phutil_register_library_map(array(
'PHUIHandleView' => 'AphrontView',
'PHUIHeadThingView' => 'AphrontTagView',
'PHUIHeaderView' => 'AphrontTagView',
+ 'PHUIHomeView' => 'AphrontTagView',
'PHUIHovercardUIExample' => 'PhabricatorUIExample',
'PHUIHovercardView' => 'AphrontTagView',
'PHUIIconCircleView' => 'AphrontTagView',
@@ -6708,7 +6719,6 @@ phutil_register_library_map(array(
'PhabricatorApplicationEditController' => 'PhabricatorApplicationsController',
'PhabricatorApplicationEditHTTPParameterHelpView' => 'AphrontView',
'PhabricatorApplicationEmailCommandsController' => 'PhabricatorApplicationsController',
- 'PhabricatorApplicationLaunchView' => 'AphrontTagView',
'PhabricatorApplicationPanelController' => 'PhabricatorApplicationsController',
'PhabricatorApplicationProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorApplicationQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
@@ -6782,10 +6792,12 @@ phutil_register_library_map(array(
'PhabricatorAuditManagementWorkflow' => 'PhabricatorManagementWorkflow',
'PhabricatorAuditReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
'PhabricatorAuditStatusConstants' => 'Phobject',
+ 'PhabricatorAuditSynchronizeManagementWorkflow' => 'PhabricatorAuditManagementWorkflow',
'PhabricatorAuditTransaction' => 'PhabricatorModularTransaction',
'PhabricatorAuditTransactionComment' => 'PhabricatorApplicationTransactionComment',
'PhabricatorAuditTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorAuditTransactionView' => 'PhabricatorApplicationTransactionView',
+ 'PhabricatorAuditUpdateOwnersManagementWorkflow' => 'PhabricatorAuditManagementWorkflow',
'PhabricatorAuthAccountView' => 'AphrontView',
'PhabricatorAuthApplication' => 'PhabricatorApplication',
'PhabricatorAuthAuthFactorPHIDType' => 'PhabricatorPHIDType',
@@ -7467,7 +7479,6 @@ phutil_register_library_map(array(
'PhabricatorDashboardEditController' => 'PhabricatorDashboardController',
'PhabricatorDashboardIconSet' => 'PhabricatorIconSet',
'PhabricatorDashboardInstall' => 'PhabricatorDashboardDAO',
- 'PhabricatorDashboardInstallController' => 'PhabricatorDashboardController',
'PhabricatorDashboardLayoutConfig' => 'Phobject',
'PhabricatorDashboardListController' => 'PhabricatorDashboardController',
'PhabricatorDashboardManageController' => 'PhabricatorDashboardController',
@@ -7691,7 +7702,6 @@ phutil_register_library_map(array(
'PhabricatorFactUpdateIterator' => 'PhutilBufferedIterator',
'PhabricatorFavoritesApplication' => 'PhabricatorApplication',
'PhabricatorFavoritesController' => 'PhabricatorController',
- 'PhabricatorFavoritesMainController' => 'PhabricatorFavoritesController',
'PhabricatorFavoritesMainMenuBarExtension' => 'PhabricatorMainMenuBarExtension',
'PhabricatorFavoritesMenuItemController' => 'PhabricatorFavoritesController',
'PhabricatorFavoritesProfileMenuEngine' => 'PhabricatorProfileMenuEngine',
@@ -7889,17 +7899,16 @@ phutil_register_library_map(array(
'PhabricatorHomeApplication' => 'PhabricatorApplication',
'PhabricatorHomeConstants' => 'PhabricatorHomeController',
'PhabricatorHomeController' => 'PhabricatorController',
- 'PhabricatorHomeMainController' => 'PhabricatorHomeController',
- 'PhabricatorHomeManageProfileMenuItem' => 'PhabricatorProfileMenuItem',
- 'PhabricatorHomeMenuController' => 'PhabricatorHomeController',
+ 'PhabricatorHomeLauncherProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorHomeMenuItemController' => 'PhabricatorHomeController',
- 'PhabricatorHomePreferencesSettingsPanel' => 'PhabricatorSettingsPanel',
'PhabricatorHomeProfileMenuEngine' => 'PhabricatorProfileMenuEngine',
+ 'PhabricatorHomeProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorHovercardEngineExtension' => 'Phobject',
'PhabricatorHovercardEngineExtensionModule' => 'PhabricatorConfigModule',
'PhabricatorIDsSearchEngineExtension' => 'PhabricatorSearchEngineExtension',
'PhabricatorIDsSearchField' => 'PhabricatorSearchField',
'PhabricatorIRCProtocolAdapter' => 'PhabricatorProtocolAdapter',
+ 'PhabricatorIconDatasource' => 'PhabricatorTypeaheadDatasource',
'PhabricatorIconRemarkupRule' => 'PhutilRemarkupRule',
'PhabricatorIconSet' => 'Phobject',
'PhabricatorIconSetEditField' => 'PhabricatorEditField',
@@ -7935,6 +7944,7 @@ phutil_register_library_map(array(
'PhabricatorKeyring' => 'Phobject',
'PhabricatorKeyringConfigOptionType' => 'PhabricatorConfigJSONOptionType',
'PhabricatorLDAPAuthProvider' => 'PhabricatorAuthProvider',
+ 'PhabricatorLabelProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorLegalpadApplication' => 'PhabricatorApplication',
'PhabricatorLegalpadConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorLegalpadDocumentPHIDType' => 'PhabricatorPHIDType',
@@ -8014,6 +8024,7 @@ phutil_register_library_map(array(
'PhabricatorMainMenuBarExtension' => 'Phobject',
'PhabricatorMainMenuSearchView' => 'AphrontView',
'PhabricatorMainMenuView' => 'AphrontView',
+ 'PhabricatorManageProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorManagementWorkflow' => 'PhutilArgumentWorkflow',
'PhabricatorManiphestApplication' => 'PhabricatorApplication',
'PhabricatorManiphestConfigOptions' => 'PhabricatorApplicationConfigOptions',
@@ -8447,6 +8458,7 @@ phutil_register_library_map(array(
'PhabricatorPeopleNewController' => 'PhabricatorPeopleController',
'PhabricatorPeopleNoOwnerDatasource' => 'PhabricatorTypeaheadDatasource',
'PhabricatorPeopleOwnerDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
+ 'PhabricatorPeoplePictureProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorPeopleProfileController' => 'PhabricatorPeopleController',
'PhabricatorPeopleProfileEditController' => 'PhabricatorPeopleProfileController',
'PhabricatorPeopleProfileManageController' => 'PhabricatorPeopleProfileController',
@@ -8783,6 +8795,7 @@ phutil_register_library_map(array(
'PhabricatorMentionableInterface',
'HarbormasterBuildableInterface',
'HarbormasterCircleCIBuildableInterface',
+ 'HarbormasterBuildkiteBuildableInterface',
'PhabricatorCustomFieldInterface',
'PhabricatorApplicationTransactionInterface',
'PhabricatorFulltextInterface',
diff --git a/src/applications/audit/conduit/AuditQueryConduitAPIMethod.php b/src/applications/audit/conduit/AuditQueryConduitAPIMethod.php
index d65c939a61..f3858afbf8 100644
--- a/src/applications/audit/conduit/AuditQueryConduitAPIMethod.php
+++ b/src/applications/audit/conduit/AuditQueryConduitAPIMethod.php
@@ -112,7 +112,7 @@ final class AuditQueryConduitAPIMethod extends AuditConduitAPIMethod {
'id' => $request->getID(),
'commitPHID' => $request->getCommitPHID(),
'auditorPHID' => $request->getAuditorPHID(),
- 'reasons' => $request->getAuditReasons(),
+ 'reasons' => array(),
'status' => $request->getAuditStatus(),
);
}
diff --git a/src/applications/audit/constants/PhabricatorAuditActionConstants.php b/src/applications/audit/constants/PhabricatorAuditActionConstants.php
index d323540795..aa467d41c4 100644
--- a/src/applications/audit/constants/PhabricatorAuditActionConstants.php
+++ b/src/applications/audit/constants/PhabricatorAuditActionConstants.php
@@ -12,36 +12,4 @@ final class PhabricatorAuditActionConstants extends Phobject {
const INLINE = 'audit:inline';
const ACTION = 'audit:action';
- public static function getActionNameMap() {
- $map = array(
- self::COMMENT => pht('Comment'),
- self::CONCERN => pht("Raise Concern \xE2\x9C\x98"),
- self::ACCEPT => pht("Accept Commit \xE2\x9C\x94"),
- self::RESIGN => pht('Resign from Audit'),
- self::CLOSE => pht('Close Audit'),
- self::ADD_CCS => pht('Add Subscribers'),
- self::ADD_AUDITORS => pht('Add Auditors'),
- );
-
- return $map;
- }
-
- public static function getActionName($constant) {
- $map = self::getActionNameMap();
- return idx($map, $constant, pht('Unknown'));
- }
-
- public static function getActionPastTenseVerb($action) {
- $map = array(
- self::COMMENT => pht('commented on'),
- self::CONCERN => pht('raised a concern with'),
- self::ACCEPT => pht('accepted'),
- self::RESIGN => pht('resigned from'),
- self::CLOSE => pht('closed'),
- self::ADD_CCS => pht('added CCs to'),
- self::ADD_AUDITORS => pht('added auditors to'),
- );
- return idx($map, $action, pht('updated'));
- }
-
}
diff --git a/src/applications/audit/editor/PhabricatorAuditEditor.php b/src/applications/audit/editor/PhabricatorAuditEditor.php
index 952cf50a77..1c43b08905 100644
--- a/src/applications/audit/editor/PhabricatorAuditEditor.php
+++ b/src/applications/audit/editor/PhabricatorAuditEditor.php
@@ -5,7 +5,6 @@ final class PhabricatorAuditEditor
const MAX_FILES_SHOWN_IN_EMAIL = 1000;
- private $auditReasonMap = array();
private $affectedFiles;
private $rawPatch;
private $auditorPHIDs = array();
@@ -13,26 +12,6 @@ final class PhabricatorAuditEditor
private $didExpandInlineState = false;
private $oldAuditStatus = null;
- public function addAuditReason($phid, $reason) {
- if (!isset($this->auditReasonMap[$phid])) {
- $this->auditReasonMap[$phid] = array();
- }
- $this->auditReasonMap[$phid][] = $reason;
- return $this;
- }
-
- private function getAuditReasons($phid) {
- if (isset($this->auditReasonMap[$phid])) {
- return $this->auditReasonMap[$phid];
- }
- if ($this->getIsHeraldEditor()) {
- $name = 'herald';
- } else {
- $name = $this->getActor()->getUsername();
- }
- return array(pht('Added by %s.', $name));
- }
-
public function setRawPatch($patch) {
$this->rawPatch = $patch;
return $this;
@@ -62,7 +41,6 @@ final class PhabricatorAuditEditor
// TODO: These will get modernized eventually, but that can happen one
// at a time later on.
$types[] = PhabricatorAuditActionConstants::INLINE;
- $types[] = PhabricatorAuditActionConstants::ADD_AUDITORS;
return $types;
}
@@ -107,10 +85,6 @@ final class PhabricatorAuditEditor
case PhabricatorAuditActionConstants::INLINE:
case PhabricatorAuditTransaction::TYPE_COMMIT:
return null;
- case PhabricatorAuditActionConstants::ADD_AUDITORS:
- // TODO: For now, just record the added PHIDs. Eventually, turn these
- // into real edge transactions, probably?
- return array();
}
return parent::getCustomTransactionOldValue($object, $xaction);
@@ -122,7 +96,6 @@ final class PhabricatorAuditEditor
switch ($xaction->getTransactionType()) {
case PhabricatorAuditActionConstants::INLINE:
- case PhabricatorAuditActionConstants::ADD_AUDITORS:
case PhabricatorAuditTransaction::TYPE_COMMIT:
return $xaction->getNewValue();
}
@@ -136,7 +109,6 @@ final class PhabricatorAuditEditor
switch ($xaction->getTransactionType()) {
case PhabricatorAuditActionConstants::INLINE:
- case PhabricatorAuditActionConstants::ADD_AUDITORS:
case PhabricatorAuditTransaction::TYPE_COMMIT:
return;
}
@@ -157,57 +129,6 @@ final class PhabricatorAuditEditor
$reply->setHasReplies(1)->save();
}
return;
- case PhabricatorAuditActionConstants::ADD_AUDITORS:
- $new = $xaction->getNewValue();
- if (!is_array($new)) {
- $new = array();
- }
-
- $old = $xaction->getOldValue();
- if (!is_array($old)) {
- $old = array();
- }
-
- $add = array_diff_key($new, $old);
-
- $actor = $this->requireActor();
-
- $requests = $object->getAudits();
- $requests = mpull($requests, null, 'getAuditorPHID');
- foreach ($add as $phid) {
- if (isset($requests[$phid])) {
- $request = $requests[$phid];
-
- // Only update an existing request if the current status is not
- // an interesting status.
- if ($request->isInteresting()) {
- continue;
- }
- } else {
- $request = id(new PhabricatorRepositoryAuditRequest())
- ->setCommitPHID($object->getPHID())
- ->setAuditorPHID($phid);
- }
-
- if ($this->getIsHeraldEditor()) {
- $audit_requested = $xaction->getMetadataValue('auditStatus');
- $audit_reason_map = $xaction->getMetadataValue('auditReasonMap');
- $audit_reason = $audit_reason_map[$phid];
- } else {
- $audit_requested = PhabricatorAuditStatusConstants::AUDIT_REQUESTED;
- $audit_reason = $this->getAuditReasons($phid);
- }
-
- $request
- ->setAuditStatus($audit_requested)
- ->setAuditReasons($audit_reason)
- ->save();
-
- $requests[$phid] = $request;
- }
-
- $object->attachAudits($requests);
- return;
}
return parent::applyCustomExternalTransaction($object, $xaction);
@@ -375,35 +296,26 @@ final class PhabricatorAuditEditor
private function createAuditRequestTransactionFromCommitMessage(
PhabricatorRepositoryCommit $commit) {
+ $actor = $this->getActor();
$data = $commit->getCommitData();
$message = $data->getCommitMessage();
- $matches = null;
- if (!preg_match('/^Auditors?:\s*(.*)$/im', $message, $matches)) {
- return array();
- }
-
- $phids = id(new PhabricatorObjectListQuery())
- ->setViewer($this->getActor())
- ->setAllowPartialResults(true)
- ->setAllowedTypes(
- array(
- PhabricatorPeopleUserPHIDType::TYPECONST,
- PhabricatorProjectProjectPHIDType::TYPECONST,
- ))
- ->setObjectList($matches[1])
- ->execute();
+ $result = DifferentialCommitMessageParser::newStandardParser($actor)
+ ->setRaiseMissingFieldErrors(false)
+ ->parseFields($message);
+ $field_key = DifferentialAuditorsCommitMessageField::FIELDKEY;
+ $phids = idx($result, $field_key, null);
if (!$phids) {
return array();
}
- foreach ($phids as $phid) {
- $this->addAuditReason($phid, pht('Requested by Author'));
- }
- return id(new PhabricatorAuditTransaction())
- ->setTransactionType(PhabricatorAuditActionConstants::ADD_AUDITORS)
- ->setNewValue(array_fuse($phids));
+ return $commit->getApplicationTransactionTemplate()
+ ->setTransactionType(DiffusionCommitAuditorsTransaction::TRANSACTIONTYPE)
+ ->setNewValue(
+ array(
+ '+' => array_fuse($phids),
+ ));
}
protected function sortTransactions(array $xactions) {
diff --git a/src/applications/audit/management/PhabricatorAuditManagementWorkflow.php b/src/applications/audit/management/PhabricatorAuditManagementWorkflow.php
index a55a58374d..6112a38e1d 100644
--- a/src/applications/audit/management/PhabricatorAuditManagementWorkflow.php
+++ b/src/applications/audit/management/PhabricatorAuditManagementWorkflow.php
@@ -1,4 +1,90 @@
'all',
+ 'help' => pht('Update all commits in all repositories.'),
+ ),
+ array(
+ 'name' => 'objects',
+ 'wildcard' => true,
+ 'help' => pht('Update named commits and repositories.'),
+ ),
+ );
+ }
+
+ protected function loadCommitsWithConstraints(PhutilArgumentParser $args) {
+ $viewer = $this->getViewer();
+
+ $all = $args->getArg('all');
+ $names = $args->getArg('objects');
+
+ if (!$names && !$all) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'Specify "--all" to affect everything, or a list of specific '.
+ 'commits or repositories to affect.'));
+ } else if ($names && $all) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'Specify either a list of objects to affect or "--all", but not '.
+ 'both.'));
+ }
+
+ if ($all) {
+ $objects = new LiskMigrationIterator(new PhabricatorRepository());
+ } else {
+ $query = id(new PhabricatorObjectQuery())
+ ->setViewer($viewer)
+ ->withNames($names);
+
+ $query->execute();
+
+ $objects = array();
+
+ $results = $query->getNamedResults();
+ foreach ($names as $name) {
+ if (!isset($results[$name])) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'Object "%s" is not a valid object.',
+ $name));
+ }
+
+ $object = $results[$name];
+ if (!($object instanceof PhabricatorRepository) &&
+ !($object instanceof PhabricatorRepositoryCommit)) {
+ throw new PhutilArgumentUsageException(
+ pht(
+ 'Object "%s" is not a valid repository or commit.',
+ $name));
+ }
+
+ $objects[] = $object;
+ }
+ }
+
+ return $objects;
+ }
+
+ protected function loadCommitsForConstraintObject($object) {
+ $viewer = $this->getViewer();
+
+ if ($object instanceof PhabricatorRepository) {
+ $commits = id(new DiffusionCommitQuery())
+ ->setViewer($viewer)
+ ->withRepository($object)
+ ->execute();
+ } else {
+ $commits = array($object);
+ }
+
+ return $commits;
+ }
+
+}
diff --git a/src/applications/audit/management/PhabricatorAuditSynchronizeManagementWorkflow.php b/src/applications/audit/management/PhabricatorAuditSynchronizeManagementWorkflow.php
new file mode 100644
index 0000000000..db6ce096c4
--- /dev/null
+++ b/src/applications/audit/management/PhabricatorAuditSynchronizeManagementWorkflow.php
@@ -0,0 +1,60 @@
+setName('synchronize')
+ ->setExamples('**synchronize** ...')
+ ->setSynopsis(pht('Update audit status for commits.'))
+ ->setArguments(
+ array_merge(
+ $this->getCommitConstraintArguments(),
+ array()));
+ }
+
+ public function execute(PhutilArgumentParser $args) {
+ $viewer = $this->getViewer();
+ $objects = $this->loadCommitsWithConstraints($args);
+
+ foreach ($objects as $object) {
+ $commits = $this->loadCommitsForConstraintObject($object);
+ foreach ($commits as $commit) {
+ $commit = id(new DiffusionCommitQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($commit->getPHID()))
+ ->needAuditRequests(true)
+ ->executeOne();
+ if (!$commit) {
+ continue;
+ }
+
+ $old_status = $commit->getAuditStatus();
+ $commit->updateAuditStatus($commit->getAudits());
+ $new_status = $commit->getAuditStatus();
+
+ if ($old_status == $new_status) {
+ echo tsprintf(
+ "%s\n",
+ pht(
+ 'No changes for "%s".',
+ $commit->getDisplayName()));
+ } else {
+ echo tsprintf(
+ "%s\n",
+ pht(
+ 'Updating "%s": "%s" -> "%s".',
+ $commit->getDisplayName(),
+ PhabricatorAuditCommitStatusConstants::getStatusName(
+ $old_status),
+ PhabricatorAuditCommitStatusConstants::getStatusName(
+ $new_status)));
+
+ $commit->save();
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/applications/audit/management/PhabricatorAuditUpdateOwnersManagementWorkflow.php b/src/applications/audit/management/PhabricatorAuditUpdateOwnersManagementWorkflow.php
new file mode 100644
index 0000000000..883bcd4065
--- /dev/null
+++ b/src/applications/audit/management/PhabricatorAuditUpdateOwnersManagementWorkflow.php
@@ -0,0 +1,55 @@
+setName('update-owners')
+ ->setExamples('**update-owners** ...')
+ ->setSynopsis(pht('Update package relationships for commits.'))
+ ->setArguments(
+ array_merge(
+ $this->getCommitConstraintArguments(),
+ array()));
+ }
+
+ public function execute(PhutilArgumentParser $args) {
+ $viewer = $this->getViewer();
+ $objects = $this->loadCommitsWithConstraints($args);
+
+ foreach ($objects as $object) {
+ $commits = $this->loadCommitsForConstraintObject($object);
+
+ foreach ($commits as $commit) {
+ $repository = $commit->getRepository();
+
+ $affected_paths = PhabricatorOwnerPathQuery::loadAffectedPaths(
+ $repository,
+ $commit,
+ $viewer);
+
+ $affected_packages = PhabricatorOwnersPackage::loadAffectedPackages(
+ $repository,
+ $affected_paths);
+
+ $monograms = mpull($affected_packages, 'getMonogram');
+ if ($monograms) {
+ $monograms = implode(', ', $monograms);
+ } else {
+ $monograms = pht('none');
+ }
+
+ echo tsprintf(
+ "%s\n",
+ pht(
+ 'Updating "%s" (%s)...',
+ $commit->getDisplayName(),
+ $monograms));
+
+ $commit->writeOwnersEdges(mpull($affected_packages, 'getPHID'));
+ }
+ }
+ }
+
+}
diff --git a/src/applications/audit/query/PhabricatorCommitSearchEngine.php b/src/applications/audit/query/PhabricatorCommitSearchEngine.php
index f24b12d2b8..7f429dd596 100644
--- a/src/applications/audit/query/PhabricatorCommitSearchEngine.php
+++ b/src/applications/audit/query/PhabricatorCommitSearchEngine.php
@@ -45,6 +45,14 @@ final class PhabricatorCommitSearchEngine
$query->withRepositoryPHIDs($map['repositoryPHIDs']);
}
+ if ($map['packagePHIDs']) {
+ $query->withPackagePHIDs($map['packagePHIDs']);
+ }
+
+ if ($map['unreachable'] !== null) {
+ $query->withUnreachable($map['unreachable']);
+ }
+
return $query;
}
@@ -78,6 +86,23 @@ final class PhabricatorCommitSearchEngine
->setConduitKey('repositories')
->setAliases(array('repository', 'repositories', 'repositoryPHID'))
->setDatasource(new DiffusionRepositoryDatasource()),
+ id(new PhabricatorSearchDatasourceField())
+ ->setLabel(pht('Packages'))
+ ->setKey('packagePHIDs')
+ ->setConduitKey('packages')
+ ->setAliases(array('package', 'packages', 'packagePHID'))
+ ->setDatasource(new PhabricatorOwnersPackageDatasource()),
+ id(new PhabricatorSearchThreeStateField())
+ ->setLabel(pht('Unreachable'))
+ ->setKey('unreachable')
+ ->setOptions(
+ pht('(Show All)'),
+ pht('Show Only Unreachable Commits'),
+ pht('Hide Unreachable Commits'))
+ ->setDescription(
+ pht(
+ 'Find or exclude unreachable commits which are not ancestors of '.
+ 'any branch, tag, or ref.')),
);
}
@@ -116,7 +141,8 @@ final class PhabricatorCommitSearchEngine
$query
->setParameter('responsiblePHIDs', array($viewer_phid))
->setParameter('statuses', $open)
- ->setParameter('bucket', $bucket_key);
+ ->setParameter('bucket', $bucket_key)
+ ->setParameter('unreachable', false);
return $query;
case 'authored':
$query
diff --git a/src/applications/audit/view/PhabricatorAuditListView.php b/src/applications/audit/view/PhabricatorAuditListView.php
index 9207230e7c..e249406724 100644
--- a/src/applications/audit/view/PhabricatorAuditListView.php
+++ b/src/applications/audit/view/PhabricatorAuditListView.php
@@ -140,6 +140,7 @@ final class PhabricatorAuditListView extends AphrontView {
->setObjectName($commit_name)
->setHeader($commit_desc)
->setHref($commit_link)
+ ->setDisabled($commit->isUnreachable())
->addByline(pht('Author: %s', $author_name))
->addIcon('none', $committed);
diff --git a/src/applications/base/PhabricatorApplication.php b/src/applications/base/PhabricatorApplication.php
index caf2dd4612..b791aaeef8 100644
--- a/src/applications/base/PhabricatorApplication.php
+++ b/src/applications/base/PhabricatorApplication.php
@@ -596,14 +596,18 @@ abstract class PhabricatorApplication
protected function getProfileMenuRouting($controller) {
$edit_route = $this->getEditRoutePattern();
+ $mode_route = '(?Pglobal|custom)/';
+
return array(
'(?Pview)/(?P[^/]+)/' => $controller,
'(?Phide)/(?P[^/]+)/' => $controller,
'(?Pdefault)/(?P[^/]+)/' => $controller,
'(?Pconfigure)/' => $controller,
- '(?Preorder)/' => $controller,
+ '(?Pconfigure)/'.$mode_route => $controller,
+ '(?Preorder)/'.$mode_route => $controller,
'(?Pedit)/'.$edit_route => $controller,
- '(?Pnew)/(?[^/]+)/'.$edit_route => $controller,
+ '(?Pnew)/'.$mode_route.'(?[^/]+)/'.$edit_route
+ => $controller,
'(?Pbuiltin)/(?[^/]+)/'.$edit_route
=> $controller,
);
diff --git a/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php b/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php
index 2395ad9365..1171266c0c 100644
--- a/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php
+++ b/src/applications/celerity/postprocessor/CelerityDefaultPostprocessor.php
@@ -209,10 +209,7 @@ final class CelerityDefaultPostprocessor
'menu.profile.icon.disabled' => 'rgba(255,255,255,.4)',
'menu.main.height' => '44px',
-
'menu.profile.width' => '240px',
- 'menu.profile.width.collapsed' => '88px',
- 'menu.profile.item.height' => '46px',
);
}
diff --git a/src/applications/config/option/PhabricatorRecaptchaConfigOptions.php b/src/applications/config/option/PhabricatorRecaptchaConfigOptions.php
index 356bcfd4dd..59bbc5c62f 100644
--- a/src/applications/config/option/PhabricatorRecaptchaConfigOptions.php
+++ b/src/applications/config/option/PhabricatorRecaptchaConfigOptions.php
@@ -23,6 +23,7 @@ final class PhabricatorRecaptchaConfigOptions
return array(
$this->newOption('recaptcha.enabled', 'bool', false)
+ ->setLocked(true)
->setBoolOptions(
array(
pht('Enable Recaptcha'),
@@ -35,6 +36,7 @@ final class PhabricatorRecaptchaConfigOptions
'failed login attempts. This hinders brute-force attacks against '.
'user passwords. For more information, see http://recaptcha.net/')),
$this->newOption('recaptcha.public-key', 'string', null)
+ ->setLocked(true)
->setDescription(
pht('Recaptcha public key, obtained by signing up for Recaptcha.')),
$this->newOption('recaptcha.private-key', 'string', null)
diff --git a/src/applications/dashboard/application/PhabricatorDashboardApplication.php b/src/applications/dashboard/application/PhabricatorDashboardApplication.php
index a79cf133a3..54ec5db20a 100644
--- a/src/applications/dashboard/application/PhabricatorDashboardApplication.php
+++ b/src/applications/dashboard/application/PhabricatorDashboardApplication.php
@@ -30,7 +30,6 @@ final class PhabricatorDashboardApplication extends PhabricatorApplication {
'create/' => 'PhabricatorDashboardEditController',
'copy/(?:(?P\d+)/)?' => 'PhabricatorDashboardCopyController',
'edit/(?:(?P\d+)/)?' => 'PhabricatorDashboardEditController',
- 'install/(?P\d+)/' => 'PhabricatorDashboardInstallController',
'uninstall/(?P\d+)/' => 'PhabricatorDashboardUninstallController',
'addpanel/(?P\d+)/' => 'PhabricatorDashboardAddPanelController',
'movepanel/(?P\d+)/' => 'PhabricatorDashboardMovePanelController',
diff --git a/src/applications/dashboard/controller/PhabricatorDashboardEditController.php b/src/applications/dashboard/controller/PhabricatorDashboardEditController.php
index a167c91188..09ab373950 100644
--- a/src/applications/dashboard/controller/PhabricatorDashboardEditController.php
+++ b/src/applications/dashboard/controller/PhabricatorDashboardEditController.php
@@ -245,7 +245,7 @@ final class PhabricatorDashboardEditController
switch ($template) {
case 'simple':
- $v_name = pht('New Simple Dashboard');
+ $v_name = pht("%s's Dashboard", $viewer->getUsername());
$welcome_panel = $this->newPanel(
$request,
@@ -260,8 +260,9 @@ final class PhabricatorDashboardEditController
"rustic, authentic feel.\n\n".
"You can drag, remove, add, and edit panels to customize the ".
"rest of this dashboard to show the information you want.\n\n".
- "To install this dashboard on the home page, use the ".
- "**Install Dashboard** action link above."),
+ "To install this dashboard on the home page, edit your personal ".
+ "or global menu on the homepage and click Dashboard under ".
+ "New Menu Item on the right."),
));
$panel_phids[] = $welcome_panel->getPHID();
@@ -276,14 +277,25 @@ final class PhabricatorDashboardEditController
));
$panel_phids[] = $feed_panel->getPHID();
+ $revision_panel = $this->newPanel(
+ $request,
+ $viewer,
+ 'query',
+ pht('Active Revisions'),
+ array(
+ 'class' => 'DifferentialRevisionSearchEngine',
+ 'key' => 'active',
+ ));
+ $panel_phids[] = $revision_panel->getPHID();
+
$task_panel = $this->newPanel(
$request,
$viewer,
'query',
- pht('Open Tasks'),
+ pht('Assigned Tasks'),
array(
'class' => 'ManiphestTaskSearchEngine',
- 'key' => 'open',
+ 'key' => 'assigned',
));
$panel_phids[] = $task_panel->getPHID();
@@ -302,6 +314,7 @@ final class PhabricatorDashboardEditController
$layout = id(new PhabricatorDashboardLayoutConfig())
->setLayoutMode($mode_2_and_1)
->setPanelLocation(0, $welcome_panel->getPHID())
+ ->setPanelLocation(0, $revision_panel->getPHID())
->setPanelLocation(0, $task_panel->getPHID())
->setPanelLocation(0, $commit_panel->getPHID())
->setPanelLocation(1, $feed_panel->getPHID());
diff --git a/src/applications/dashboard/controller/PhabricatorDashboardInstallController.php b/src/applications/dashboard/controller/PhabricatorDashboardInstallController.php
deleted file mode 100644
index 504aacaac8..0000000000
--- a/src/applications/dashboard/controller/PhabricatorDashboardInstallController.php
+++ /dev/null
@@ -1,148 +0,0 @@
-getViewer();
- $this->id = $request->getURIData('id');
-
- $dashboard = id(new PhabricatorDashboardQuery())
- ->setViewer($viewer)
- ->withIDs(array($this->id))
- ->executeOne();
- if (!$dashboard) {
- return new Aphront404Response();
- }
- $dashboard_phid = $dashboard->getPHID();
-
- $object_phid = $request->getStr('objectPHID', $viewer->getPHID());
- switch ($object_phid) {
- case PhabricatorHomeApplication::DASHBOARD_DEFAULT:
- if (!$viewer->getIsAdmin()) {
- return new Aphront404Response();
- }
- break;
- default:
- $object = id(new PhabricatorObjectQuery())
- ->setViewer($viewer)
- ->requireCapabilities(
- array(
- PhabricatorPolicyCapability::CAN_VIEW,
- PhabricatorPolicyCapability::CAN_EDIT,
- ))
- ->withPHIDs(array($object_phid))
- ->executeOne();
- if (!$object) {
- return new Aphront404Response();
- }
- break;
- }
-
- $installer_phid = $viewer->getPHID();
- $application_class = $request->getStr(
- 'applicationClass',
- 'PhabricatorHomeApplication');
-
- if ($request->isFormPost()) {
- $dashboard_install = id(new PhabricatorDashboardInstall())
- ->loadOneWhere(
- 'objectPHID = %s AND applicationClass = %s',
- $object_phid,
- $application_class);
- if (!$dashboard_install) {
- $dashboard_install = id(new PhabricatorDashboardInstall())
- ->setObjectPHID($object_phid)
- ->setApplicationClass($application_class);
- }
- $dashboard_install
- ->setInstallerPHID($installer_phid)
- ->setDashboardPHID($dashboard_phid)
- ->save();
- return id(new AphrontRedirectResponse())
- ->setURI($this->getRedirectURI($application_class, $object_phid));
- }
-
- $dialog = $this->newDialog()
- ->setTitle(pht('Install Dashboard'))
- ->addHiddenInput('objectPHID', $object_phid)
- ->addCancelButton($this->getCancelURI($application_class, $object_phid))
- ->addSubmitButton(pht('Install Dashboard'));
-
- switch ($application_class) {
- case 'PhabricatorHomeApplication':
- if ($viewer->getPHID() == $object_phid) {
- if ($viewer->getIsAdmin()) {
- $dialog->setWidth(AphrontDialogView::WIDTH_FORM);
-
- $form = id(new AphrontFormView())
- ->setUser($viewer)
- ->appendRemarkupInstructions(
- pht('Choose where to install this dashboard.'))
- ->appendChild(
- id(new AphrontFormRadioButtonControl())
- ->setName('objectPHID')
- ->setValue(PhabricatorHomeApplication::DASHBOARD_DEFAULT)
- ->addButton(
- PhabricatorHomeApplication::DASHBOARD_DEFAULT,
- pht('Default Dashboard for All Users'),
- pht(
- 'Install this dashboard as the global default dashboard '.
- 'for all users. Users can install a personal dashboard '.
- 'to replace it. All users who have not configured '.
- 'a personal dashboard will be affected by this change.'))
- ->addButton(
- $viewer->getPHID(),
- pht('Personal Home Page Dashboard'),
- pht(
- 'Install this dashboard as your personal home page '.
- 'dashboard. Only you will be affected by this change.')));
-
- $dialog->appendChild($form->buildLayoutView());
- } else {
- $dialog->appendParagraph(
- pht('Install this dashboard on your home page?'));
- }
- } else {
- $dialog->appendParagraph(
- pht(
- 'Install this dashboard as the home page dashboard for %s?',
- phutil_tag(
- 'strong',
- array(),
- $viewer->renderHandle($object_phid))));
- }
- break;
- default:
- throw new Exception(
- pht(
- 'Unknown dashboard application class "%s"!',
- $application_class));
- }
-
- return $dialog;
- }
-
- private function getCancelURI($application_class, $object_phid) {
- $uri = null;
- switch ($application_class) {
- case 'PhabricatorHomeApplication':
- $uri = '/dashboard/view/'.$this->id.'/';
- break;
- }
- return $uri;
- }
-
- private function getRedirectURI($application_class, $object_phid) {
- $uri = null;
- switch ($application_class) {
- case 'PhabricatorHomeApplication':
- $uri = '/';
- break;
- }
- return $uri;
- }
-
-}
diff --git a/src/applications/dashboard/controller/PhabricatorDashboardManageController.php b/src/applications/dashboard/controller/PhabricatorDashboardManageController.php
index 9724db21f7..d5902ce93d 100644
--- a/src/applications/dashboard/controller/PhabricatorDashboardManageController.php
+++ b/src/applications/dashboard/controller/PhabricatorDashboardManageController.php
@@ -156,26 +156,6 @@ final class PhabricatorDashboardManageController
->setHref($this->getApplicationURI("copy/{$id}/"))
->setWorkflow(true));
- $installed_dashboard = id(new PhabricatorDashboardInstall())
- ->loadOneWhere(
- 'objectPHID = %s AND applicationClass = %s',
- $viewer->getPHID(),
- 'PhabricatorHomeApplication');
- if ($installed_dashboard &&
- $installed_dashboard->getDashboardPHID() == $dashboard->getPHID()) {
- $title_install = pht('Uninstall Dashboard');
- $href_install = "uninstall/{$id}/";
- } else {
- $title_install = pht('Install Dashboard');
- $href_install = "install/{$id}/";
- }
- $curtain->addAction(
- id(new PhabricatorActionView())
- ->setName($title_install)
- ->setIcon('fa-wrench')
- ->setHref($this->getApplicationURI($href_install))
- ->setWorkflow(true));
-
return $curtain;
}
diff --git a/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php b/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php
index 22ab5b20bf..286d749a18 100644
--- a/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php
+++ b/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php
@@ -72,8 +72,10 @@ final class PhabricatorDashboardPanelRenderingEngine extends Phobject {
if (!$panel) {
return $this->renderErrorPanel(
- pht('Missing Panel'),
- pht('This panel does not exist.'));
+ pht('Missing or Restricted Panel'),
+ pht(
+ 'This panel does not exist, or you do not have permission '.
+ 'to see it.'));
}
$panel_type = $panel->getImplementation();
@@ -166,10 +168,13 @@ final class PhabricatorDashboardPanelRenderingEngine extends Phobject {
}
$icon = id(new PHUIIconView())
->setIcon('fa-warning red msr');
+
$content = id(new PHUIBoxView())
->addClass('dashboard-box')
+ ->addMargin(PHUI::MARGIN_MEDIUM)
->appendChild($icon)
->appendChild($body);
+
return $this->renderPanelDiv(
$content,
$header);
@@ -203,10 +208,17 @@ final class PhabricatorDashboardPanelRenderingEngine extends Phobject {
$box->appendChild($content);
}
- $box->setHeader($header)
+ $box
+ ->setHeader($header)
->setID($id)
- ->addSigil('dashboard-panel')
- ->setMetadata(array('objectPHID' => $panel->getPHID()));
+ ->addSigil('dashboard-panel');
+
+ if ($panel) {
+ $box->setMetadata(
+ array(
+ 'objectPHID' => $panel->getPHID(),
+ ));
+ }
return phutil_tag_div('dashboard-pane', $box);
}
@@ -243,6 +255,10 @@ final class PhabricatorDashboardPanelRenderingEngine extends Phobject {
PHUIHeaderView $header) {
$panel = $this->getPanel();
+ if (!$panel) {
+ return $header;
+ }
+
$dashboard_id = $this->getDashboardID();
$edit_uri = id(new PhutilURI(
'/dashboard/panel/edit/'.$panel->getID().'/'));
diff --git a/src/applications/dashboard/engine/PhabricatorDashboardRenderingEngine.php b/src/applications/dashboard/engine/PhabricatorDashboardRenderingEngine.php
index 8f0c43a914..f9af84973e 100644
--- a/src/applications/dashboard/engine/PhabricatorDashboardRenderingEngine.php
+++ b/src/applications/dashboard/engine/PhabricatorDashboardRenderingEngine.php
@@ -32,7 +32,7 @@ final class PhabricatorDashboardRenderingEngine extends Phobject {
$dashboard_id = celerity_generate_unique_node_id();
$result = id(new AphrontMultiColumnView())
->setID($dashboard_id)
- ->setFluidlayout(true)
+ ->setFluidLayout(true)
->setGutter(AphrontMultiColumnView::GUTTER_LARGE);
if ($this->arrangeMode) {
@@ -43,17 +43,27 @@ final class PhabricatorDashboardRenderingEngine extends Phobject {
foreach ($panel_grid_locations as $column => $panel_column_locations) {
$panel_phids = $panel_column_locations;
- $column_panels = array_select_keys($panels, $panel_phids);
+
+ // TODO: This list may contain duplicates when the dashboard itself
+ // does not? Perhaps this is related to T10612. For now, just unique
+ // the list before moving on.
+ $panel_phids = array_unique($panel_phids);
+
$column_result = array();
- foreach ($column_panels as $panel) {
- $column_result[] = id(new PhabricatorDashboardPanelRenderingEngine())
+ foreach ($panel_phids as $panel_phid) {
+ $panel_engine = id(new PhabricatorDashboardPanelRenderingEngine())
->setViewer($viewer)
- ->setPanel($panel)
->setDashboardID($dashboard->getID())
->setEnableAsyncRendering(true)
->setParentPanelPHIDs(array())
- ->setHeaderMode($h_mode)
- ->renderPanel();
+ ->setHeaderMode($h_mode);
+
+ $panel = idx($panels, $panel_phid);
+ if ($panel) {
+ $panel_engine->setPanel($panel);
+ }
+
+ $column_result[] = $panel_engine->renderPanel();
}
$column_class = $layout_config->getColumnClass(
$column,
diff --git a/src/applications/dashboard/query/PhabricatorDashboardQuery.php b/src/applications/dashboard/query/PhabricatorDashboardQuery.php
index a54cfbc47c..c005197df2 100644
--- a/src/applications/dashboard/query/PhabricatorDashboardQuery.php
+++ b/src/applications/dashboard/query/PhabricatorDashboardQuery.php
@@ -70,8 +70,13 @@ final class PhabricatorDashboardQuery
$panel_phids = $edge_query->getDestinationPHIDs();
if ($panel_phids) {
+ // NOTE: We explicitly disable policy exceptions when loading panels.
+ // If a particular panel is invalid or not visible to the viewer,
+ // we'll still render the dashboard, just not that panel.
+
$panels = id(new PhabricatorDashboardPanelQuery())
->setParentQuery($this)
+ ->setRaisePolicyExceptions(false)
->setViewer($this->getViewer())
->withPHIDs($panel_phids)
->execute();
diff --git a/src/applications/differential/controller/DifferentialRevisionViewController.php b/src/applications/differential/controller/DifferentialRevisionViewController.php
index f126345fb7..d31da8fefc 100644
--- a/src/applications/differential/controller/DifferentialRevisionViewController.php
+++ b/src/applications/differential/controller/DifferentialRevisionViewController.php
@@ -443,7 +443,7 @@ final class DifferentialRevisionViewController extends DifferentialController {
$operations_box = $this->buildOperationsBox($revision);
$crumbs = $this->buildApplicationCrumbs();
- $crumbs->addTextCrumb($monogram, $revision->getURI());
+ $crumbs->addTextCrumb($monogram);
$crumbs->setBorder(true);
$filetree_on = $viewer->compareUserSetting(
diff --git a/src/applications/differential/field/DifferentialAuditorsCommitMessageField.php b/src/applications/differential/field/DifferentialAuditorsCommitMessageField.php
index 94f629e557..bd4d9f3009 100644
--- a/src/applications/differential/field/DifferentialAuditorsCommitMessageField.php
+++ b/src/applications/differential/field/DifferentialAuditorsCommitMessageField.php
@@ -9,12 +9,19 @@ final class DifferentialAuditorsCommitMessageField
return pht('Auditors');
}
+ public function getFieldAliases() {
+ return array(
+ 'Auditor',
+ );
+ }
+
public function parseFieldValue($value) {
return $this->parseObjectList(
$value,
array(
PhabricatorPeopleUserPHIDType::TYPECONST,
PhabricatorProjectProjectPHIDType::TYPECONST,
+ PhabricatorOwnersPackagePHIDType::TYPECONST,
));
}
diff --git a/src/applications/differential/storage/DifferentialDiff.php b/src/applications/differential/storage/DifferentialDiff.php
index d8c2ea9786..8976fdd8f0 100644
--- a/src/applications/differential/storage/DifferentialDiff.php
+++ b/src/applications/differential/storage/DifferentialDiff.php
@@ -7,6 +7,7 @@ final class DifferentialDiff
PhabricatorExtendedPolicyInterface,
HarbormasterBuildableInterface,
HarbormasterCircleCIBuildableInterface,
+ HarbormasterBuildkiteBuildableInterface,
PhabricatorApplicationTransactionInterface,
PhabricatorDestructibleInterface {
@@ -621,6 +622,27 @@ final class DifferentialDiff
return $ref;
}
+
+/* -( HarbormasterBuildkiteBuildableInterface )---------------------------- */
+
+ public function getBuildkiteBranch() {
+ $ref = $this->getStagingRef();
+
+ // NOTE: Circa late January 2017, Buildkite fails with the error message
+ // "Tags have been disabled for this project" if we pass the "refs/tags/"
+ // prefix via the API and the project doesn't have GitHub tag builds
+ // enabled, even if GitHub builds are disabled. The tag builds fine
+ // without this prefix.
+ $ref = preg_replace('(^refs/tags/)', '', $ref);
+
+ return $ref;
+ }
+
+ public function getBuildkiteCommit() {
+ return 'HEAD';
+ }
+
+
public function getStagingRef() {
// TODO: We're just hoping to get lucky. Instead, `arc` should store
// where it sent changes and we should only provide staging details
diff --git a/src/applications/differential/xaction/DifferentialRevisionCommandeerTransaction.php b/src/applications/differential/xaction/DifferentialRevisionCommandeerTransaction.php
index 4e4cd9600f..34cedcbb73 100644
--- a/src/applications/differential/xaction/DifferentialRevisionCommandeerTransaction.php
+++ b/src/applications/differential/xaction/DifferentialRevisionCommandeerTransaction.php
@@ -41,7 +41,7 @@ final class DifferentialRevisionCommandeerTransaction
}
public function getCommandSummary() {
- return pht('Commadeer a revision.');
+ return pht('Commandeer a revision.');
}
public function generateOldValue($object) {
diff --git a/src/applications/diffusion/controller/DiffusionCommitController.php b/src/applications/diffusion/controller/DiffusionCommitController.php
index f88c463633..f8847b7e3b 100644
--- a/src/applications/diffusion/controller/DiffusionCommitController.php
+++ b/src/applications/diffusion/controller/DiffusionCommitController.php
@@ -855,12 +855,6 @@ final class DiffusionCommitController extends DiffusionController {
PhabricatorAuditStatusConstants::getStatusColor($code),
PhabricatorAuditStatusConstants::getStatusName($code));
- $note = array();
- foreach ($request->getAuditReasons() as $reason) {
- $note[] = phutil_tag('div', array(), $reason);
- }
- $item->setNote($note);
-
$auditor_phid = $request->getAuditorPHID();
$target = $viewer->renderHandle($auditor_phid);
$item->setTarget($target);
diff --git a/src/applications/diffusion/edge/DiffusionCommitHasPackageEdgeType.php b/src/applications/diffusion/edge/DiffusionCommitHasPackageEdgeType.php
new file mode 100644
index 0000000000..7a35b30a76
--- /dev/null
+++ b/src/applications/diffusion/edge/DiffusionCommitHasPackageEdgeType.php
@@ -0,0 +1,8 @@
+getMonogram());
- }
-
$xaction = $adapter->newTransaction()
- ->setTransactionType(PhabricatorAuditActionConstants::ADD_AUDITORS)
- ->setNewValue($phids)
- ->setMetadataValue(
- 'auditStatus',
- PhabricatorAuditStatusConstants::AUDIT_REQUIRED)
- ->setMetadataValue('auditReasonMap', $reason_map);
+ ->setTransactionType(DiffusionCommitAuditorsTransaction::TRANSACTIONTYPE)
+ ->setNewValue(
+ array(
+ '+' => $phids,
+ ));
$adapter->queueTransaction($xaction);
diff --git a/src/applications/diffusion/query/DiffusionCommitQuery.php b/src/applications/diffusion/query/DiffusionCommitQuery.php
index fddaf547bd..1521f5770c 100644
--- a/src/applications/diffusion/query/DiffusionCommitQuery.php
+++ b/src/applications/diffusion/query/DiffusionCommitQuery.php
@@ -13,6 +13,8 @@ final class DiffusionCommitQuery
private $identifierMap;
private $responsiblePHIDs;
private $statuses;
+ private $packagePHIDs;
+ private $unreachable;
private $needAuditRequests;
private $auditIDs;
@@ -124,17 +126,19 @@ final class DiffusionCommitQuery
return $this;
}
- public function withStatuses(array $statuses) {
- $this->statuses = $statuses;
+ public function withPackagePHIDs(array $package_phids) {
+ $this->packagePHIDs = $package_phids;
return $this;
}
- public function withAuditStatus($status) {
- // TODO: Replace callers with `withStatuses()`.
- return $this->withStatuses(
- array(
- $status,
- ));
+ public function withUnreachable($unreachable) {
+ $this->unreachable = $unreachable;
+ return $this;
+ }
+
+ public function withStatuses(array $statuses) {
+ $this->statuses = $statuses;
+ return $this;
}
public function withEpochRange($min, $max) {
@@ -498,6 +502,28 @@ final class DiffusionCommitQuery
$this->statuses);
}
+ if ($this->packagePHIDs !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'package.dst IN (%Ls)',
+ $this->packagePHIDs);
+ }
+
+ if ($this->unreachable !== null) {
+ if ($this->unreachable) {
+ $where[] = qsprintf(
+ $conn,
+ '(commit.importStatus & %d) = %d',
+ PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE,
+ PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE);
+ } else {
+ $where[] = qsprintf(
+ $conn,
+ '(commit.importStatus & %d) = 0',
+ PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE);
+ }
+ }
+
return $where;
}
@@ -519,6 +545,10 @@ final class DiffusionCommitQuery
return (bool)$this->responsiblePHIDs;
}
+ private function shouldJoinOwners() {
+ return (bool)$this->packagePHIDs;
+ }
+
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
$join = parent::buildJoinClauseParts($conn);
$audit_request = new PhabricatorRepositoryAuditRequest();
@@ -537,6 +567,15 @@ final class DiffusionCommitQuery
$audit_request->getTableName());
}
+ if ($this->shouldJoinOwners()) {
+ $join[] = qsprintf(
+ $conn,
+ 'JOIN %T package ON commit.phid = package.src
+ AND package.type = %s',
+ PhabricatorEdgeConfig::TABLE_NAME_EDGE,
+ DiffusionCommitHasPackageEdgeType::EDGECONST);
+ }
+
return $join;
}
@@ -549,6 +588,10 @@ final class DiffusionCommitQuery
return true;
}
+ if ($this->shouldJoinOwners()) {
+ return true;
+ }
+
return parent::shouldGroupQueryResultRows();
}
diff --git a/src/applications/diffusion/query/DiffusionCommitRequiredActionResultBucket.php b/src/applications/diffusion/query/DiffusionCommitRequiredActionResultBucket.php
index 405e1ecd27..41840bcb72 100644
--- a/src/applications/diffusion/query/DiffusionCommitRequiredActionResultBucket.php
+++ b/src/applications/diffusion/query/DiffusionCommitRequiredActionResultBucket.php
@@ -128,6 +128,10 @@ final class DiffusionCommitRequiredActionResultBucket
$should_audit = array_fuse($should_audit);
foreach ($objects as $key => $object) {
+ if (isset($phids[$object->getAuthorPHID()])) {
+ continue;
+ }
+
if (!$this->hasAuditorsWithStatus($object, $phids, $should_audit)) {
continue;
}
diff --git a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php
index 8f9493a67a..f12dfcf4c0 100644
--- a/src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php
+++ b/src/applications/diffusion/query/lowlevel/DiffusionLowLevelResolveRefsQuery.php
@@ -178,7 +178,10 @@ final class DiffusionLowLevelResolveRefsQuery
// repositories with a huge number of tags.
$tag_refs = id(new DiffusionLowLevelGitRefQuery())
->setRepository($repository)
- ->withIsTag(true)
+ ->withRefTypes(
+ array(
+ PhabricatorRepositoryRefCursor::TYPE_TAG,
+ ))
->executeQuery();
foreach ($tag_refs as $tag_ref) {
$tag_map[$tag_ref->getShortName()] = $tag_ref->getCommitIdentifier();
diff --git a/src/applications/favorites/application/PhabricatorFavoritesApplication.php b/src/applications/favorites/application/PhabricatorFavoritesApplication.php
index 1d5d3ae2aa..3a6acd4ebc 100644
--- a/src/applications/favorites/application/PhabricatorFavoritesApplication.php
+++ b/src/applications/favorites/application/PhabricatorFavoritesApplication.php
@@ -21,8 +21,7 @@ final class PhabricatorFavoritesApplication extends PhabricatorApplication {
public function getRoutes() {
return array(
'/favorites/' => array(
- '' => 'PhabricatorFavoritesMainController',
- '(?Pglobal|personal)/item/' => $this->getProfileMenuRouting(
+ 'menu/' => $this->getProfileMenuRouting(
'PhabricatorFavoritesMenuItemController'),
),
);
diff --git a/src/applications/favorites/controller/PhabricatorFavoritesMainController.php b/src/applications/favorites/controller/PhabricatorFavoritesMainController.php
deleted file mode 100644
index 54197a6635..0000000000
--- a/src/applications/favorites/controller/PhabricatorFavoritesMainController.php
+++ /dev/null
@@ -1,63 +0,0 @@
-getViewer();
-
- if (!$viewer->getIsAdmin()) {
- $uri = '/favorites/personal/item/configure/';
- return id(new AphrontRedirectResponse())->setURI($uri);
- }
-
- $menu = id(new PHUIObjectItemListView())
- ->setUser($viewer);
-
- $menu->addItem(
- id(new PHUIObjectItemView())
- ->setHeader(pht('Personal Menu Items'))
- ->setHref($this->getApplicationURI('personal/item/configure/'))
- ->setImageURI($viewer->getProfileImageURI())
- ->addAttribute(pht('Edit favorites for your personal account.')));
-
- $icon = id(new PHUIIconView())
- ->setIcon('fa-globe')
- ->setBackground('bg-blue');
-
- $menu->addItem(
- id(new PHUIObjectItemView())
- ->setHeader(pht('Global Menu Items'))
- ->setHref($this->getApplicationURI('global/item/configure/'))
- ->setImageIcon($icon)
- ->addAttribute(pht('Edit global default favorites for all users.')));
-
- $crumbs = $this->buildApplicationCrumbs();
- $crumbs->addTextCrumb(pht('Manage'));
- $crumbs->setBorder(true);
-
- $box = id(new PHUIObjectBoxView())
- ->setObjectList($menu);
-
- $header = id(new PHUIHeaderView())
- ->setHeader(pht('Manage Favorites'))
- ->setHeaderIcon('fa-star-o');
-
- $view = id(new PHUITwoColumnView())
- ->setHeader($header)
- ->setFooter(array(
- $box,
- ));
-
- return $this->newPage()
- ->setTitle(pht('Manage'))
- ->setCrumbs($crumbs)
- ->appendChild($view);
-
- }
-
-}
diff --git a/src/applications/favorites/controller/PhabricatorFavoritesMenuItemController.php b/src/applications/favorites/controller/PhabricatorFavoritesMenuItemController.php
index 912d9efe0d..dd4bf77cf0 100644
--- a/src/applications/favorites/controller/PhabricatorFavoritesMenuItemController.php
+++ b/src/applications/favorites/controller/PhabricatorFavoritesMenuItemController.php
@@ -5,13 +5,6 @@ final class PhabricatorFavoritesMenuItemController
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
- $type = $request->getURIData('type');
- $custom_phid = null;
- $menu = PhabricatorProfileMenuEngine::MENU_GLOBAL;
- if ($type == 'personal') {
- $custom_phid = $viewer->getPHID();
- $menu = PhabricatorProfileMenuEngine::MENU_PERSONAL;
- }
$application = 'PhabricatorFavoritesApplication';
$favorites = id(new PhabricatorApplicationQuery())
@@ -22,9 +15,8 @@ final class PhabricatorFavoritesMenuItemController
$engine = id(new PhabricatorFavoritesProfileMenuEngine())
->setProfileObject($favorites)
- ->setCustomPHID($custom_phid)
+ ->setCustomPHID($viewer->getPHID())
->setController($this)
- ->setMenuType($menu)
->setShowNavigation(false);
return $engine->buildResponse();
diff --git a/src/applications/favorites/engine/PhabricatorFavoritesProfileMenuEngine.php b/src/applications/favorites/engine/PhabricatorFavoritesProfileMenuEngine.php
index 87b59e3e0f..0ac31e7de8 100644
--- a/src/applications/favorites/engine/PhabricatorFavoritesProfileMenuEngine.php
+++ b/src/applications/favorites/engine/PhabricatorFavoritesProfileMenuEngine.php
@@ -8,14 +8,7 @@ final class PhabricatorFavoritesProfileMenuEngine
}
public function getItemURI($path) {
- $object = $this->getProfileObject();
- $custom = $this->getCustomPHID();
-
- if ($custom) {
- return "/favorites/personal/item/{$path}";
- } else {
- return "/favorites/global/item/{$path}";
- }
+ return "/favorites/menu/{$path}";
}
protected function getBuiltinProfileItems($object) {
diff --git a/src/applications/favorites/engineextension/PhabricatorFavoritesMainMenuBarExtension.php b/src/applications/favorites/engineextension/PhabricatorFavoritesMainMenuBarExtension.php
index 990ecad993..9afb836b94 100644
--- a/src/applications/favorites/engineextension/PhabricatorFavoritesMainMenuBarExtension.php
+++ b/src/applications/favorites/engineextension/PhabricatorFavoritesMainMenuBarExtension.php
@@ -50,16 +50,8 @@ final class PhabricatorFavoritesMainMenuBarExtension
$menu_engine = id(new PhabricatorFavoritesProfileMenuEngine())
->setViewer($viewer)
- ->setProfileObject($favorites);
-
- if ($viewer->getPHID()) {
- $menu_engine
- ->setCustomPHID($viewer->getPHID())
- ->setMenuType(PhabricatorProfileMenuEngine::MENU_COMBINED);
- } else {
- $menu_engine
- ->setMenuType(PhabricatorProfileMenuEngine::MENU_GLOBAL);
- }
+ ->setProfileObject($favorites)
+ ->setCustomPHID($viewer->getPHID());
$filter_view = $menu_engine->buildNavigation();
@@ -69,14 +61,10 @@ final class PhabricatorFavoritesMainMenuBarExtension
$view = id(new PhabricatorActionListView())
->setViewer($viewer);
foreach ($item_views as $item) {
- $type = null;
- if (!strlen($item->getName())) {
- $type = PhabricatorActionView::TYPE_DIVIDER;
- }
$action = id(new PhabricatorActionView())
->setName($item->getName())
->setHref($item->getHref())
- ->setType($type);
+ ->setType($item->getType());
$view->addAction($action);
}
@@ -87,7 +75,7 @@ final class PhabricatorFavoritesMainMenuBarExtension
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Favorites'))
- ->setHref('/favorites/'));
+ ->setHref('/favorites/menu/configure/'));
}
return $view;
diff --git a/src/applications/files/transform/PhabricatorFileThumbnailTransform.php b/src/applications/files/transform/PhabricatorFileThumbnailTransform.php
index 546021fc95..2c61743078 100644
--- a/src/applications/files/transform/PhabricatorFileThumbnailTransform.php
+++ b/src/applications/files/transform/PhabricatorFileThumbnailTransform.php
@@ -58,9 +58,9 @@ final class PhabricatorFileThumbnailTransform
public function generateTransforms() {
return array(
id(new PhabricatorFileThumbnailTransform())
- ->setName(pht("Profile (200px \xC3\x97 200px)"))
+ ->setName(pht("Profile (400px \xC3\x97 400px)"))
->setKey(self::TRANSFORM_PROFILE)
- ->setDimensions(200, 200)
+ ->setDimensions(400, 400)
->setScaleUp(true),
id(new PhabricatorFileThumbnailTransform())
->setName(pht("Pinboard (280px \xC3\x97 210px)"))
diff --git a/src/applications/files/typeahead/PhabricatorIconDatasource.php b/src/applications/files/typeahead/PhabricatorIconDatasource.php
new file mode 100644
index 0000000000..63b554a9b7
--- /dev/null
+++ b/src/applications/files/typeahead/PhabricatorIconDatasource.php
@@ -0,0 +1,46 @@
+buildResults();
+ return $this->filterResultsAgainstTokens($results);
+ }
+
+ protected function renderSpecialTokens(array $values) {
+ return $this->renderTokensFromResults($this->buildResults(), $values);
+ }
+
+ private function buildResults() {
+ $raw_query = $this->getRawQuery();
+
+ $icons = id(new PHUIIconView())->getIcons();
+
+ $results = array();
+ foreach ($icons as $icon) {
+ $display_name = str_replace('fa-', '', $icon);
+ $result = id(new PhabricatorTypeaheadResult())
+ ->setPHID($icon)
+ ->setName($icon)
+ ->setIcon($icon)
+ ->setDisplayname($display_name)
+ ->addAttribute($icon);
+
+ $results[$icon] = $result;
+ }
+ return $results;
+ }
+
+}
diff --git a/src/applications/harbormaster/application/PhabricatorHarbormasterApplication.php b/src/applications/harbormaster/application/PhabricatorHarbormasterApplication.php
index ed7104965a..ed164b2d3b 100644
--- a/src/applications/harbormaster/application/PhabricatorHarbormasterApplication.php
+++ b/src/applications/harbormaster/application/PhabricatorHarbormasterApplication.php
@@ -94,6 +94,7 @@ final class PhabricatorHarbormasterApplication extends PhabricatorApplication {
),
'hook/' => array(
'circleci/' => 'HarbormasterCircleCIHookController',
+ 'buildkite/' => 'HarbormasterBuildkiteHookController',
),
),
);
diff --git a/src/applications/harbormaster/controller/HarbormasterBuildkiteHookController.php b/src/applications/harbormaster/controller/HarbormasterBuildkiteHookController.php
new file mode 100644
index 0000000000..df50ea9126
--- /dev/null
+++ b/src/applications/harbormaster/controller/HarbormasterBuildkiteHookController.php
@@ -0,0 +1,111 @@
+newHookResponse(pht('OK: Ignored event.'));
+ }
+
+ $build = idx($body, 'build');
+ if (!is_array($build)) {
+ throw new Exception(
+ pht(
+ 'Expected "%s" property to contain a dictionary.',
+ 'build'));
+ }
+
+ $meta_data = idx($build, 'meta_data');
+ if (!is_array($meta_data)) {
+ throw new Exception(
+ pht(
+ 'Expected "%s" property to contain a dictionary.',
+ 'build.meta_data'));
+ }
+
+ $target_phid = idx($meta_data, 'buildTargetPHID');
+ if (!$target_phid) {
+ return $this->newHookResponse(pht('OK: No Harbormaster target PHID.'));
+ }
+
+ $viewer = PhabricatorUser::getOmnipotentUser();
+ $target = id(new HarbormasterBuildTargetQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($target_phid))
+ ->needBuildSteps(true)
+ ->executeOne();
+ if (!$target) {
+ throw new Exception(
+ pht(
+ 'Harbormaster build target "%s" does not exist.',
+ $target_phid));
+ }
+
+ $step = $target->getBuildStep();
+ $impl = $step->getStepImplementation();
+ if (!($impl instanceof HarbormasterBuildkiteBuildStepImplementation)) {
+ throw new Exception(
+ pht(
+ 'Harbormaster build target "%s" is not a Buildkite build step. '.
+ 'Only Buildkite steps may be updated via the Buildkite hook.',
+ $target_phid));
+ }
+
+ $webhook_token = $impl->getSetting('webhook.token');
+ $request_token = $request->getHTTPHeader('X-Buildkite-Token');
+
+ if (!phutil_hashes_are_identical($webhook_token, $request_token)) {
+ throw new Exception(
+ pht(
+ 'Buildkite request to target "%s" had the wrong authentication '.
+ 'token. The Buildkite pipeline and Harbormaster build step must '.
+ 'be configured with the same token.',
+ $target_phid));
+ }
+
+ $state = idx($build, 'state');
+ switch ($state) {
+ case 'passed':
+ $message_type = HarbormasterMessageType::MESSAGE_PASS;
+ break;
+ default:
+ $message_type = HarbormasterMessageType::MESSAGE_FAIL;
+ break;
+ }
+
+ $api_method = 'harbormaster.sendmessage';
+ $api_params = array(
+ 'buildTargetPHID' => $target_phid,
+ 'type' => $message_type,
+ );
+
+ $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
+
+ id(new ConduitCall($api_method, $api_params))
+ ->setUser($viewer)
+ ->execute();
+
+ unset($unguarded);
+
+ return $this->newHookResponse(pht('OK: Processed event.'));
+ }
+
+ private function newHookResponse($message) {
+ $response = new AphrontWebpageResponse();
+ $response->setContent($message);
+ return $response;
+ }
+
+}
diff --git a/src/applications/harbormaster/interface/HarbormasterBuildkiteBuildableInterface.php b/src/applications/harbormaster/interface/HarbormasterBuildkiteBuildableInterface.php
new file mode 100644
index 0000000000..6d44d034f4
--- /dev/null
+++ b/src/applications/harbormaster/interface/HarbormasterBuildkiteBuildableInterface.php
@@ -0,0 +1,11 @@
+getBuildable();
+
+ $object = $buildable->getBuildableObject();
+ if (!($object instanceof HarbormasterBuildkiteBuildableInterface)) {
+ throw new Exception(
+ pht('This object does not support builds with Buildkite.'));
+ }
+
+ $organization = $this->getSetting('organization');
+ $pipeline = $this->getSetting('pipeline');
+
+ $uri = urisprintf(
+ 'https://api.buildkite.com/v2/organizations/%s/pipelines/%s/builds',
+ $organization,
+ $pipeline);
+
+ $data_structure = array(
+ 'commit' => $object->getBuildkiteCommit(),
+ 'branch' => $object->getBuildkiteBranch(),
+ 'message' => pht(
+ 'Harbormaster Build %s ("%s") for %s',
+ $build->getID(),
+ $build->getName(),
+ $buildable->getMonogram()),
+ 'env' => array(
+ 'HARBORMASTER_BUILD_TARGET_PHID' => $build_target->getPHID(),
+ ),
+ 'meta_data' => array(
+ 'buildTargetPHID' => $build_target->getPHID(),
+ ),
+ );
+
+ $json_data = phutil_json_encode($data_structure);
+
+ $credential_phid = $this->getSetting('token');
+ $api_token = id(new PassphraseCredentialQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($credential_phid))
+ ->needSecrets(true)
+ ->executeOne();
+ if (!$api_token) {
+ throw new Exception(
+ pht(
+ 'Unable to load API token ("%s")!',
+ $credential_phid));
+ }
+
+ $token = $api_token->getSecret()->openEnvelope();
+
+ $future = id(new HTTPSFuture($uri, $json_data))
+ ->setMethod('POST')
+ ->addHeader('Content-Type', 'application/json')
+ ->addHeader('Accept', 'application/json')
+ ->addHeader('Authorization', "Bearer {$token}")
+ ->setTimeout(60);
+
+ $this->resolveFutures(
+ $build,
+ $build_target,
+ array($future));
+
+ $this->logHTTPResponse($build, $build_target, $future, pht('Buildkite'));
+
+ list($status, $body) = $future->resolve();
+ if ($status->isError()) {
+ throw new HarbormasterBuildFailureException();
+ }
+
+ $response = phutil_json_decode($body);
+
+ $uri_key = 'web_url';
+ $build_uri = idx($response, $uri_key);
+ if (!$build_uri) {
+ throw new Exception(
+ pht(
+ 'Buildkite did not return a "%s"!',
+ $uri_key));
+ }
+
+ $target_phid = $build_target->getPHID();
+
+ $api_method = 'harbormaster.createartifact';
+ $api_params = array(
+ 'buildTargetPHID' => $target_phid,
+ 'artifactType' => HarbormasterURIArtifact::ARTIFACTCONST,
+ 'artifactKey' => 'buildkite.uri',
+ 'artifactData' => array(
+ 'uri' => $build_uri,
+ 'name' => pht('View in Buildkite'),
+ 'ui.external' => true,
+ ),
+ );
+
+ id(new ConduitCall($api_method, $api_params))
+ ->setUser($viewer)
+ ->execute();
+ }
+
+ public function getFieldSpecifications() {
+ return array(
+ 'token' => array(
+ 'name' => pht('API Token'),
+ 'type' => 'credential',
+ 'credential.type'
+ => PassphraseTokenCredentialType::CREDENTIAL_TYPE,
+ 'credential.provides'
+ => PassphraseTokenCredentialType::PROVIDES_TYPE,
+ 'required' => true,
+ ),
+ 'organization' => array(
+ 'name' => pht('Organization Name'),
+ 'type' => 'text',
+ 'required' => true,
+ ),
+ 'pipeline' => array(
+ 'name' => pht('Pipeline Name'),
+ 'type' => 'text',
+ 'required' => true,
+ ),
+ 'webhook.token' => array(
+ 'name' => pht('Webhook Token'),
+ 'type' => 'text',
+ 'required' => true,
+ ),
+ );
+ }
+
+ public function supportsWaitForMessage() {
+ return false;
+ }
+
+ public function shouldWaitForMessage(HarbormasterBuildTarget $target) {
+ return true;
+ }
+
+}
diff --git a/src/applications/home/application/PhabricatorHomeApplication.php b/src/applications/home/application/PhabricatorHomeApplication.php
index a860bc8fb3..f3ab64552b 100644
--- a/src/applications/home/application/PhabricatorHomeApplication.php
+++ b/src/applications/home/application/PhabricatorHomeApplication.php
@@ -22,14 +22,16 @@ final class PhabricatorHomeApplication extends PhabricatorApplication {
public function getRoutes() {
return array(
- '/' => 'PhabricatorHomeMainController',
- '/(?Phome)/' => 'PhabricatorHomeMainController',
- '/home/' => array(
- 'menu/' => array(
- '' => 'PhabricatorHomeMenuController',
- '(?Pglobal|personal)/item/' => $this->getProfileMenuRouting(
+ '/' => 'PhabricatorHomeMenuItemController',
+
+ // NOTE: If you visit "/" on mobile, you get just the menu. If you visit
+ // "/home/" on mobile, you get the content. From the normal desktop
+ // UI, there's no difference between these pages.
+
+ '/(?Phome)/' => array(
+ '' => 'PhabricatorHomeMenuItemController',
+ 'menu/' => $this->getProfileMenuRouting(
'PhabricatorHomeMenuItemController'),
- ),
),
);
}
diff --git a/src/applications/home/constants/PhabricatorHomeConstants.php b/src/applications/home/constants/PhabricatorHomeConstants.php
index d1613acf58..b639c79abd 100644
--- a/src/applications/home/constants/PhabricatorHomeConstants.php
+++ b/src/applications/home/constants/PhabricatorHomeConstants.php
@@ -3,7 +3,9 @@
final class PhabricatorHomeConstants
extends PhabricatorHomeController {
+ const ITEM_HOME = 'home.dashboard';
const ITEM_LAUNCHER = 'home.launcher';
const ITEM_MANAGE = 'home.manage.menu';
+ const ITEM_APPS_LABEL = 'home.apps.label';
}
diff --git a/src/applications/home/controller/PhabricatorHomeController.php b/src/applications/home/controller/PhabricatorHomeController.php
index 65a412faad..7c46525ee0 100644
--- a/src/applications/home/controller/PhabricatorHomeController.php
+++ b/src/applications/home/controller/PhabricatorHomeController.php
@@ -2,69 +2,42 @@
abstract class PhabricatorHomeController extends PhabricatorController {
- public function buildNav() {
- $user = $this->getRequest()->getUser();
+ private $home;
+ private $profileMenu;
- $nav = new AphrontSideNavFilterView();
- $nav->setBaseURI(new PhutilURI('/'));
+ public function buildApplicationMenu() {
+ $menu = $this->newApplicationMenu();
- $applications = id(new PhabricatorApplicationQuery())
- ->setViewer($user)
- ->withInstalled(true)
- ->withUnlisted(false)
- ->withLaunchable(true)
- ->execute();
-
- $pinned = $user->getUserSetting(
- PhabricatorPinnedApplicationsSetting::SETTINGKEY);
-
- // Force "Applications" to appear at the bottom.
- $meta_app = 'PhabricatorApplicationsApplication';
- $pinned = array_fuse($pinned);
- unset($pinned[$meta_app]);
- $pinned[$meta_app] = $meta_app;
- $applications[$meta_app] = PhabricatorApplication::getByClass($meta_app);
-
- $tiles = array();
-
- $home_app = new PhabricatorHomeApplication();
-
- $tiles[] = id(new PhabricatorApplicationLaunchView())
- ->setApplication($home_app)
- ->addClass('phabricator-application-launch-phone-only')
- ->setUser($user);
-
- foreach ($pinned as $pinned_application) {
- if (empty($applications[$pinned_application])) {
- continue;
- }
-
- $application = $applications[$pinned_application];
-
- $tile = id(new PhabricatorApplicationLaunchView())
- ->setApplication($application)
- ->setUser($user);
-
- $tiles[] = $tile;
+ $profile_menu = $this->getProfileMenu();
+ if ($profile_menu) {
+ $menu->setProfileMenu($profile_menu);
}
- $nav->addCustomBlock(
- phutil_tag(
- 'div',
- array(
- 'class' => 'application-tile-group',
- ),
- $tiles));
+ return $menu;
+ }
- $nav->addFilter(
- '',
- pht('Customize Menu...'),
- '/settings/panel/home/');
+ protected function getProfileMenu() {
+ if (!$this->profileMenu) {
+ $viewer = $this->getViewer();
+ $applications = id(new PhabricatorApplicationQuery())
+ ->setViewer($viewer)
+ ->withClasses(array('PhabricatorHomeApplication'))
+ ->withInstalled(true)
+ ->execute();
+ $home = head($applications);
+ if (!$home) {
+ return null;
+ }
- $nav->addClass('phabricator-side-menu-home');
- $nav->selectFilter(null);
+ $engine = id(new PhabricatorHomeProfileMenuEngine())
+ ->setViewer($viewer)
+ ->setProfileObject($home)
+ ->setCustomPHID($viewer->getPHID());
- return $nav;
+ $this->profileMenu = $engine->buildNavigation();
+ }
+
+ return $this->profileMenu;
}
}
diff --git a/src/applications/home/controller/PhabricatorHomeMenuController.php b/src/applications/home/controller/PhabricatorHomeMenuController.php
deleted file mode 100644
index 13c4750489..0000000000
--- a/src/applications/home/controller/PhabricatorHomeMenuController.php
+++ /dev/null
@@ -1,57 +0,0 @@
-getViewer();
-
- $menu = id(new PHUIObjectItemListView())
- ->setUser($viewer);
-
- $menu->addItem(
- id(new PHUIObjectItemView())
- ->setHeader(pht('Personal Menu Items'))
- ->setHref($this->getApplicationURI('menu/personal/item/configure/'))
- ->setImageURI($viewer->getProfileImageURI())
- ->addAttribute(pht('Edit the menu for your personal account.')));
-
- $icon = id(new PHUIIconView())
- ->setIcon('fa-globe')
- ->setBackground('bg-blue');
-
- $menu->addItem(
- id(new PHUIObjectItemView())
- ->setHeader(pht('Global Menu Items'))
- ->setHref($this->getApplicationURI('menu/global/item/configure/'))
- ->setImageIcon($icon)
- ->addAttribute(pht('Edit the global default menu for all users.')));
-
- $crumbs = $this->buildApplicationCrumbs();
- $crumbs->addTextCrumb(pht('Manage'));
- $crumbs->setBorder(true);
-
- $box = id(new PHUIObjectBoxView())
- ->setObjectList($menu);
-
- $header = id(new PHUIHeaderView())
- ->setHeader(pht('Manage Home Menu'))
- ->setHeaderIcon('fa-home');
-
- $view = id(new PHUITwoColumnView())
- ->setHeader($header)
- ->setFooter(array(
- $box,
- ));
-
- return $this->newPage()
- ->setTitle(pht('Manage Home Menu'))
- ->setCrumbs($crumbs)
- ->appendChild($view);
-
- }
-
-}
diff --git a/src/applications/home/controller/PhabricatorHomeMenuItemController.php b/src/applications/home/controller/PhabricatorHomeMenuItemController.php
index 01f5233884..ce97bba477 100644
--- a/src/applications/home/controller/PhabricatorHomeMenuItemController.php
+++ b/src/applications/home/controller/PhabricatorHomeMenuItemController.php
@@ -3,15 +3,21 @@
final class PhabricatorHomeMenuItemController
extends PhabricatorHomeController {
+ public function shouldAllowPublic() {
+ return true;
+ }
+
+ public function isGlobalDragAndDropUploadEnabled() {
+ return true;
+ }
+
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
- $type = $request->getURIData('type');
- $custom_phid = null;
- $menu = PhabricatorProfileMenuEngine::MENU_GLOBAL;
- if ($type == 'personal') {
- $custom_phid = $viewer->getPHID();
- $menu = PhabricatorProfileMenuEngine::MENU_PERSONAL;
- }
+
+ // Test if we should show mobile users the menu or the page content:
+ // if you visit "/", you just get the menu. If you visit "/home/", you
+ // get the content.
+ $is_content = $request->getURIData('content');
$application = 'PhabricatorHomeApplication';
$home_app = id(new PhabricatorApplicationQuery())
@@ -22,10 +28,13 @@ final class PhabricatorHomeMenuItemController
$engine = id(new PhabricatorHomeProfileMenuEngine())
->setProfileObject($home_app)
- ->setCustomPHID($custom_phid)
- ->setMenuType($menu)
+ ->setCustomPHID($viewer->getPHID())
->setController($this)
- ->setShowNavigation(false);
+ ->setShowContentCrumbs(false);
+
+ if (!$is_content) {
+ $engine->addContentPageClass('phabricator-home');
+ }
return $engine->buildResponse();
}
diff --git a/src/applications/home/engine/PhabricatorHomeProfileMenuEngine.php b/src/applications/home/engine/PhabricatorHomeProfileMenuEngine.php
index 528edba697..cbfe62aaa6 100644
--- a/src/applications/home/engine/PhabricatorHomeProfileMenuEngine.php
+++ b/src/applications/home/engine/PhabricatorHomeProfileMenuEngine.php
@@ -8,14 +8,25 @@ final class PhabricatorHomeProfileMenuEngine
}
public function getItemURI($path) {
- $object = $this->getProfileObject();
- $custom = $this->getCustomPHID();
+ return "/home/menu/{$path}";
+ }
- if ($custom) {
- return "/home/menu/personal/item/{$path}";
- } else {
- return "/home/menu/global/item/{$path}";
- }
+ protected function buildItemViewContent(
+ PhabricatorProfileMenuItemConfiguration $item) {
+ $viewer = $this->getViewer();
+
+ // Add content to the document so that you can drag-and-drop files onto
+ // the home page or any home dashboard to upload them.
+
+ $upload = id(new PhabricatorGlobalUploadTargetView())
+ ->setUser($viewer);
+
+ $content = parent::buildItemViewContent($item);
+
+ return array(
+ $content,
+ $upload,
+ );
}
protected function getBuiltinProfileItems($object) {
@@ -30,13 +41,23 @@ final class PhabricatorHomeProfileMenuEngine
->withLaunchable(true)
->execute();
+ // Default Home Dashboard
+ $items[] = $this->newItem()
+ ->setBuiltinKey(PhabricatorHomeConstants::ITEM_HOME)
+ ->setMenuItemKey(PhabricatorHomeProfileMenuItem::MENUITEMKEY);
+
+ $items[] = $this->newItem()
+ ->setBuiltinKey(PhabricatorHomeConstants::ITEM_APPS_LABEL)
+ ->setMenuItemKey(PhabricatorLabelProfileMenuItem::MENUITEMKEY)
+ ->setMenuItemProperties(array('name' => pht('Applications')));
+
foreach ($applications as $application) {
if (!$application->isPinnedByDefault($viewer)) {
continue;
}
$properties = array(
- 'name' => $application->getName(),
+ 'name' => '',
'application' => $application->getPHID(),
);
@@ -46,11 +67,12 @@ final class PhabricatorHomeProfileMenuEngine
->setMenuItemProperties($properties);
}
- // Single Manage Item, switches URI based on admin/user
+ // Hotlink to More Applications Launcher...
$items[] = $this->newItem()
- ->setBuiltinKey(PhabricatorHomeConstants::ITEM_MANAGE)
- ->setMenuItemKey(
- PhabricatorHomeManageProfileMenuItem::MENUITEMKEY);
+ ->setBuiltinKey(PhabricatorHomeConstants::ITEM_LAUNCHER)
+ ->setMenuItemKey(PhabricatorHomeLauncherProfileMenuItem::MENUITEMKEY);
+
+ $items[] = $this->newManageItem();
return $items;
}
diff --git a/src/applications/home/menuitem/PhabricatorHomeLauncherProfileMenuItem.php b/src/applications/home/menuitem/PhabricatorHomeLauncherProfileMenuItem.php
new file mode 100644
index 0000000000..a727fbced6
--- /dev/null
+++ b/src/applications/home/menuitem/PhabricatorHomeLauncherProfileMenuItem.php
@@ -0,0 +1,66 @@
+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) {
+ $viewer = $this->getViewer();
+
+ $name = $this->getDisplayName($config);
+ $icon = 'fa-globe';
+ $href = '/applications/';
+
+ $item = $this->newItem()
+ ->setHref($href)
+ ->setName($name)
+ ->setIcon($icon);
+
+ return array(
+ $item,
+ );
+ }
+
+}
diff --git a/src/applications/home/menuitem/PhabricatorHomeProfileMenuItem.php b/src/applications/home/menuitem/PhabricatorHomeProfileMenuItem.php
new file mode 100644
index 0000000000..8b3eb4fe2d
--- /dev/null
+++ b/src/applications/home/menuitem/PhabricatorHomeProfileMenuItem.php
@@ -0,0 +1,69 @@
+getMenuItemProperty('name');
+
+ if (strlen($name)) {
+ return $name;
+ }
+
+ return $this->getDefaultName();
+ }
+
+ public function newPageContent(
+ PhabricatorProfileMenuItemConfiguration $config) {
+ $viewer = $this->getViewer();
+
+ return id(new PHUIHomeView())
+ ->setViewer($viewer);
+ }
+
+ 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) {
+ $viewer = $this->getViewer();
+
+ $name = $this->getDisplayName($config);
+ $icon = 'fa-home';
+ $href = $this->getItemViewURI($config);
+
+ $item = $this->newItem()
+ ->setHref($href)
+ ->setName($name)
+ ->setIcon($icon);
+
+ return array(
+ $item,
+ );
+ }
+
+}
diff --git a/src/applications/home/controller/PhabricatorHomeMainController.php b/src/applications/home/view/PHUIHomeView.php
similarity index 81%
rename from src/applications/home/controller/PhabricatorHomeMainController.php
rename to src/applications/home/view/PHUIHomeView.php
index 405959fe67..c511908326 100644
--- a/src/applications/home/controller/PhabricatorHomeMainController.php
+++ b/src/applications/home/view/PHUIHomeView.php
@@ -1,57 +1,17 @@
getViewer();
-
- $dashboard = PhabricatorDashboardInstall::getDashboard(
- $viewer,
- $viewer->getPHID(),
- get_class($this->getCurrentApplication()));
-
- if (!$dashboard) {
- $dashboard = PhabricatorDashboardInstall::getDashboard(
- $viewer,
- PhabricatorHomeApplication::DASHBOARD_DEFAULT,
- get_class($this->getCurrentApplication()));
- }
-
- if ($dashboard) {
- $content = id(new PhabricatorDashboardRenderingEngine())
- ->setViewer($viewer)
- ->setDashboard($dashboard)
- ->renderDashboard();
- } else {
- $content = $this->buildMainResponse();
- }
-
- if (!$request->getURIData('only')) {
- $nav = $this->buildNav();
- $nav->appendChild(
- array(
- $content,
- id(new PhabricatorGlobalUploadTargetView())->setUser($viewer),
- ));
- $content = $nav;
- }
-
- return $this->newPage()
- ->setTitle('Phabricator')
- ->addClass('phabricator-home')
- ->appendChild($content);
-
- }
-
- private function buildMainResponse() {
+ protected function getTagContent() {
require_celerity_resource('phabricator-dashboard-css');
$viewer = $this->getViewer();
diff --git a/src/applications/macro/controller/PhabricatorMacroEditController.php b/src/applications/macro/controller/PhabricatorMacroEditController.php
index 4e6fa314b0..110296c172 100644
--- a/src/applications/macro/controller/PhabricatorMacroEditController.php
+++ b/src/applications/macro/controller/PhabricatorMacroEditController.php
@@ -185,6 +185,7 @@ final class PhabricatorMacroEditController extends PhabricatorMacroController {
if (!$macro->getID()) {
if ($current_file) {
$current_file_view = id(new PhabricatorFileLinkView())
+ ->setViewer($viewer)
->setFilePHID($current_file->getPHID())
->setFileName($current_file->getName())
->setFileViewable(true)
diff --git a/src/applications/meta/view/PhabricatorApplicationLaunchView.php b/src/applications/meta/view/PhabricatorApplicationLaunchView.php
deleted file mode 100644
index 7d98b6d7fa..0000000000
--- a/src/applications/meta/view/PhabricatorApplicationLaunchView.php
+++ /dev/null
@@ -1,69 +0,0 @@
-application = $application;
- return $this;
- }
-
- protected function getTagName() {
- return $this->application ? 'a' : 'div';
- }
-
- protected function getTagAttributes() {
- $application = $this->application;
- return array(
- 'class' => array('phabricator-application-launch-container'),
- 'href' => $application ? $application->getBaseURI() : null,
- );
- }
-
- protected function getTagContent() {
- $application = $this->application;
-
- require_celerity_resource('phabricator-application-launch-view-css');
-
- $content = array();
- $icon = null;
- if ($application) {
- $content[] = phutil_tag(
- 'span',
- array(
- 'class' => 'phabricator-application-launch-name',
- ),
- $application->getName());
-
- $content[] = phutil_tag(
- 'span',
- array(
- 'class' => 'phabricator-application-launch-description',
- ),
- $application->getShortDescription());
-
- $classes = array();
- $classes[] = 'phabricator-application-launch-icon';
-
- $styles = array();
- $classes[] = $application->getIcon();
- $classes[] = 'phui-icon-view';
- $classes[] = 'phui-font-fa';
-
- $icon = phutil_tag(
- 'span',
- array(
- 'class' => implode(' ', $classes),
- 'style' => nonempty(implode('; ', $styles), null),
- ),
- '');
- }
-
- return array(
- $icon,
- $content,
- );
- }
-
-}
diff --git a/src/applications/owners/controller/PhabricatorOwnersDetailController.php b/src/applications/owners/controller/PhabricatorOwnersDetailController.php
index b641290f56..b6260416da 100644
--- a/src/applications/owners/controller/PhabricatorOwnersDetailController.php
+++ b/src/applications/owners/controller/PhabricatorOwnersDetailController.php
@@ -68,14 +68,14 @@ final class PhabricatorOwnersDetailController
$commit_uri = id(new PhutilURI('/diffusion/commit/'))
->setQueryParams(
array(
- 'auditorPHIDs' => $package->getPHID(),
+ 'package' => $package->getPHID(),
));
$status_concern = PhabricatorAuditCommitStatusConstants::CONCERN_RAISED;
$attention_commits = id(new DiffusionCommitQuery())
->setViewer($request->getUser())
- ->withAuditorPHIDs(array($package->getPHID()))
+ ->withPackagePHIDs(array($package->getPHID()))
->withStatuses(
array(
$status_concern,
@@ -102,7 +102,7 @@ final class PhabricatorOwnersDetailController
$all_commits = id(new DiffusionCommitQuery())
->setViewer($request->getUser())
- ->withAuditorPHIDs(array($package->getPHID()))
+ ->withPackagePHIDs(array($package->getPHID()))
->needCommitData(true)
->needAuditRequests(true)
->setLimit(25)
diff --git a/src/applications/people/controller/PhabricatorPeopleProfileController.php b/src/applications/people/controller/PhabricatorPeopleProfileController.php
index 1e0a9cbe18..bd44169ef7 100644
--- a/src/applications/people/controller/PhabricatorPeopleProfileController.php
+++ b/src/applications/people/controller/PhabricatorPeopleProfileController.php
@@ -60,4 +60,64 @@ abstract class PhabricatorPeopleProfileController
return $crumbs;
}
+ public function buildProfileHeader() {
+ $user = $this->user;
+ $viewer = $this->getViewer();
+
+ $profile = $user->loadUserProfile();
+ $picture = $user->getProfileImageURI();
+
+ $profile_icon = PhabricatorPeopleIconSet::getIconIcon($profile->getIcon());
+ $profile_title = $profile->getDisplayTitle();
+
+ $roles = array();
+ if ($user->getIsAdmin()) {
+ $roles[] = pht('Administrator');
+ }
+ if ($user->getIsDisabled()) {
+ $roles[] = pht('Disabled');
+ }
+ if (!$user->getIsApproved()) {
+ $roles[] = pht('Not Approved');
+ }
+ if ($user->getIsSystemAgent()) {
+ $roles[] = pht('Bot');
+ }
+ if ($user->getIsMailingList()) {
+ $roles[] = pht('Mailing List');
+ }
+
+ $tag = null;
+ if ($roles) {
+ $tag = id(new PHUITagView())
+ ->setName(implode(', ', $roles))
+ ->addClass('project-view-header-tag')
+ ->setType(PHUITagView::TYPE_SHADE);
+ }
+
+ $header = id(new PHUIHeaderView())
+ ->setHeader(array($user->getFullName(), $tag))
+ ->setImage($picture)
+ ->setProfileHeader(true)
+ ->addClass('people-profile-header');
+
+ if ($user->getIsDisabled()) {
+ $header->setStatus('fa-ban', 'red', pht('Disabled'));
+ } else {
+ $header->setStatus($profile_icon, 'bluegrey', $profile_title);
+ }
+
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $user,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ if ($can_edit) {
+ $id = $user->getID();
+ $header->setImageEditURL($this->getApplicationURI("picture/{$id}/"));
+ }
+
+ return $header;
+ }
+
}
diff --git a/src/applications/people/controller/PhabricatorPeopleProfileManageController.php b/src/applications/people/controller/PhabricatorPeopleProfileManageController.php
index ba533c041c..84acfe1af9 100644
--- a/src/applications/people/controller/PhabricatorPeopleProfileManageController.php
+++ b/src/applications/people/controller/PhabricatorPeopleProfileManageController.php
@@ -23,20 +23,7 @@ final class PhabricatorPeopleProfileManageController
}
$this->setUser($user);
-
- $profile = $user->loadUserProfile();
- $picture = $user->getProfileImageURI();
-
- $profile_icon = PhabricatorPeopleIconSet::getIconIcon($profile->getIcon());
- $profile_icon = id(new PHUIIconView())
- ->setIcon($profile_icon);
- $profile_title = $profile->getDisplayTitle();
-
- $header = id(new PHUIHeaderView())
- ->setHeader($user->getFullName())
- ->setSubheader(array($profile_icon, $profile_title))
- ->setImage($picture)
- ->setProfileHeader(true);
+ $header = $this->buildProfileHeader();
$curtain = $this->buildCurtain($user);
$properties = $this->buildPropertyView($user);
@@ -51,8 +38,10 @@ final class PhabricatorPeopleProfileManageController
$manage = id(new PHUITwoColumnView())
->setHeader($header)
+ ->addClass('project-view-home')
->setCurtain($curtain)
->addPropertySection(pht('Details'), $properties);
+ require_celerity_resource('project-view-css');
return $this->newPage()
->setTitle(
diff --git a/src/applications/people/controller/PhabricatorPeopleProfileViewController.php b/src/applications/people/controller/PhabricatorPeopleProfileViewController.php
index 17fc67e2f3..34a69c3bfb 100644
--- a/src/applications/people/controller/PhabricatorPeopleProfileViewController.php
+++ b/src/applications/people/controller/PhabricatorPeopleProfileViewController.php
@@ -23,30 +23,7 @@ final class PhabricatorPeopleProfileViewController
}
$this->setUser($user);
-
- $profile = $user->loadUserProfile();
- $picture = $user->getProfileImageURI();
-
- $profile_icon = PhabricatorPeopleIconSet::getIconIcon($profile->getIcon());
- $profile_icon = id(new PHUIIconView())
- ->setIcon($profile_icon);
- $profile_title = $profile->getDisplayTitle();
-
- $header = id(new PHUIHeaderView())
- ->setHeader($user->getFullName())
- ->setSubheader(array($profile_icon, $profile_title))
- ->setImage($picture)
- ->setProfileHeader(true);
-
- $can_edit = PhabricatorPolicyFilter::hasCapability(
- $viewer,
- $user,
- PhabricatorPolicyCapability::CAN_EDIT);
-
- if ($can_edit) {
- $id = $user->getID();
- $header->setImageEditURL($this->getApplicationURI("picture/{$id}/"));
- }
+ $header = $this->buildProfileHeader();
$properties = $this->buildPropertyView($user);
$name = $user->getUsername();
@@ -158,12 +135,9 @@ final class PhabricatorPeopleProfileViewController
}
} else {
- $error = id(new PHUIBoxView())
- ->addClass('mlb')
- ->appendChild(pht('User does not belong to any projects.'));
$list = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NODATA)
- ->appendChild($error);
+ ->appendChild(pht('User does not belong to any projects.'));
}
$box = id(new PHUIObjectBoxView())
@@ -302,12 +276,9 @@ final class PhabricatorPeopleProfileViewController
}
}
} else {
- $error = id(new PHUIBoxView())
- ->addClass('mlb')
- ->appendChild(pht('User does not have any badges.'));
$flex = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NODATA)
- ->appendChild($error);
+ ->appendChild(pht('User does not have any badges.'));
}
// Best option?
diff --git a/src/applications/people/engine/PhabricatorPeopleProfileMenuEngine.php b/src/applications/people/engine/PhabricatorPeopleProfileMenuEngine.php
index 3d67872772..2fd2ec3bd2 100644
--- a/src/applications/people/engine/PhabricatorPeopleProfileMenuEngine.php
+++ b/src/applications/people/engine/PhabricatorPeopleProfileMenuEngine.php
@@ -5,6 +5,7 @@ final class PhabricatorPeopleProfileMenuEngine
const ITEM_PROFILE = 'people.profile';
const ITEM_MANAGE = 'people.manage';
+ const ITEM_PICTURE = 'people.picture';
protected function isMenuEngineConfigurable() {
return false;
@@ -22,6 +23,10 @@ final class PhabricatorPeopleProfileMenuEngine
$items = array();
+ $items[] = $this->newItem()
+ ->setBuiltinKey(self::ITEM_PICTURE)
+ ->setMenuItemKey(PhabricatorPeoplePictureProfileMenuItem::MENUITEMKEY);
+
$items[] = $this->newItem()
->setBuiltinKey(self::ITEM_PROFILE)
->setMenuItemKey(PhabricatorPeopleDetailsProfileMenuItem::MENUITEMKEY);
diff --git a/src/applications/people/menuitem/PhabricatorPeopleDetailsProfileMenuItem.php b/src/applications/people/menuitem/PhabricatorPeopleDetailsProfileMenuItem.php
index 0f5cb6c1fa..d7d36b4ed5 100644
--- a/src/applications/people/menuitem/PhabricatorPeopleDetailsProfileMenuItem.php
+++ b/src/applications/people/menuitem/PhabricatorPeopleDetailsProfileMenuItem.php
@@ -39,17 +39,14 @@ final class PhabricatorPeopleDetailsProfileMenuItem
PhabricatorProfileMenuItemConfiguration $config) {
$user = $config->getProfileObject();
-
- $picture = $user->getProfileImageURI();
- $name = $user->getUsername();
$href = urisprintf(
'/p/%s/',
$user->getUsername());
$item = $this->newItem()
->setHref($href)
- ->setName($name)
- ->setProfileImage($picture);
+ ->setName(pht('Profile'))
+ ->setIcon('fa-user');
return array(
$item,
diff --git a/src/applications/people/menuitem/PhabricatorPeoplePictureProfileMenuItem.php b/src/applications/people/menuitem/PhabricatorPeoplePictureProfileMenuItem.php
new file mode 100644
index 0000000000..89fb7b9681
--- /dev/null
+++ b/src/applications/people/menuitem/PhabricatorPeoplePictureProfileMenuItem.php
@@ -0,0 +1,76 @@
+getDefaultName();
+ }
+
+ public function buildEditEngineFields(
+ PhabricatorProfileMenuItemConfiguration $config) {
+ return array();
+ }
+
+ protected function newNavigationMenuItems(
+ PhabricatorProfileMenuItemConfiguration $config) {
+
+ $user = $config->getProfileObject();
+ require_celerity_resource('people-picture-menu-item-css');
+
+ $picture = $user->getProfileImageURI();
+ $name = $user->getUsername();
+ $href = urisprintf(
+ '/p/%s/',
+ $user->getUsername());
+
+ $photo = phutil_tag(
+ 'img',
+ array(
+ 'src' => $picture,
+ 'class' => 'people-menu-image',
+ ));
+
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $this->getViewer(),
+ $user,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ if ($can_edit) {
+ $id = $user->getID();
+ $href = "/people/picture/{$id}/";
+ }
+
+ $view = phutil_tag_div('people-menu-image-container', $photo);
+ $view = phutil_tag(
+ 'a',
+ array(
+ 'href' => $href,
+ ),
+ $view);
+
+ $item = $this->newItem()
+ ->appendChild($view);
+
+ return array(
+ $item,
+ );
+ }
+
+}
diff --git a/src/applications/pholio/view/PholioMockImagesView.php b/src/applications/pholio/view/PholioMockImagesView.php
index 6c92f1d21f..c95d8827d3 100644
--- a/src/applications/pholio/view/PholioMockImagesView.php
+++ b/src/applications/pholio/view/PholioMockImagesView.php
@@ -180,8 +180,6 @@ final class PholioMockImagesView extends AphrontView {
'pholio-mock-view',
$this->getBehaviorConfig());
- $mockview = '';
-
$mock_wrapper = javelin_tag(
'div',
array(
@@ -220,7 +218,7 @@ final class PholioMockImagesView extends AphrontView {
),
'');
- $mockview[] = phutil_tag(
+ return phutil_tag(
'div',
array(
'class' => 'pholio-mock-image-container',
@@ -228,7 +226,6 @@ final class PholioMockImagesView extends AphrontView {
),
array($mock_wrapper, $inline_comments_holder));
- return $mockview;
}
private function getImagePageURI(PholioImage $image, PholioMock $mock) {
diff --git a/src/applications/project/application/PhabricatorProjectApplication.php b/src/applications/project/application/PhabricatorProjectApplication.php
index 8988f0f73a..95f65253a8 100644
--- a/src/applications/project/application/PhabricatorProjectApplication.php
+++ b/src/applications/project/application/PhabricatorProjectApplication.php
@@ -7,7 +7,7 @@ final class PhabricatorProjectApplication extends PhabricatorApplication {
}
public function getShortDescription() {
- return pht('Get Organized');
+ return pht('Projects, Tags, and Teams');
}
public function isPinnedByDefault(PhabricatorUser $viewer) {
diff --git a/src/applications/project/engine/PhabricatorProjectProfileMenuEngine.php b/src/applications/project/engine/PhabricatorProjectProfileMenuEngine.php
index a39e7a8236..ef16b2eee8 100644
--- a/src/applications/project/engine/PhabricatorProjectProfileMenuEngine.php
+++ b/src/applications/project/engine/PhabricatorProjectProfileMenuEngine.php
@@ -7,6 +7,10 @@ final class PhabricatorProjectProfileMenuEngine
return true;
}
+ protected function isMenuEnginePersonalizable() {
+ return false;
+ }
+
public function getItemURI($path) {
$project = $this->getProfileObject();
$id = $project->getID();
diff --git a/src/applications/project/menuitem/PhabricatorProjectPointsProfileMenuItem.php b/src/applications/project/menuitem/PhabricatorProjectPointsProfileMenuItem.php
index 020e5626de..b64b4bb7a0 100644
--- a/src/applications/project/menuitem/PhabricatorProjectPointsProfileMenuItem.php
+++ b/src/applications/project/menuitem/PhabricatorProjectPointsProfileMenuItem.php
@@ -68,17 +68,16 @@ final class PhabricatorProjectPointsProfileMenuItem
->setLimit($limit + 1)
->execute();
+ $error = array();
if (count($tasks) > $limit) {
- return $this->renderError(
+ $error[] =
pht(
- 'Too many tasks to compute statistics for (more than %s).',
- new PhutilNumber($limit)));
+ 'Too many tasks (%s).',
+ new PhutilNumber($limit));
}
if (!$tasks) {
- return $this->renderError(
- pht(
- 'This milestone has no tasks yet.'));
+ $error[] = pht('This milestone has no tasks.');
}
$statuses = array();
@@ -111,14 +110,11 @@ final class PhabricatorProjectPointsProfileMenuItem
}
if ($no_points == count($tasks)) {
- return $this->renderError(
- pht('No tasks have assigned point values.'));
+ $error[] = pht('No tasks have points assigned.');
}
-
if (!$points_total) {
- return $this->renderError(
- pht('All tasks with assigned point values are worth zero points.'));
+ $error[] = pht('No tasks have positive points.');
}
$label = pht(
@@ -158,6 +154,10 @@ final class PhabricatorProjectPointsProfileMenuItem
->setTooltip($tooltip);
}
+ if ($error) {
+ $bar->setLabel(head($error));
+ }
+
$bar = phutil_tag(
'div',
array(
@@ -173,20 +173,4 @@ final class PhabricatorProjectPointsProfileMenuItem
);
}
- private function renderError($message) {
- $message = phutil_tag(
- 'div',
- array(
- 'class' => 'phui-profile-menu-error',
- ),
- $message);
-
- $item = $this->newItem()
- ->appendChild($message);
-
- return array(
- $item,
- );
- }
-
}
diff --git a/src/applications/repository/storage/PhabricatorRepositoryCommit.php b/src/applications/repository/storage/PhabricatorRepositoryCommit.php
index 5d826f9118..c6ddece457 100644
--- a/src/applications/repository/storage/PhabricatorRepositoryCommit.php
+++ b/src/applications/repository/storage/PhabricatorRepositoryCommit.php
@@ -11,6 +11,7 @@ final class PhabricatorRepositoryCommit
PhabricatorMentionableInterface,
HarbormasterBuildableInterface,
HarbormasterCircleCIBuildableInterface,
+ HarbormasterBuildkiteBuildableInterface,
PhabricatorCustomFieldInterface,
PhabricatorApplicationTransactionInterface,
PhabricatorFulltextInterface,
@@ -262,6 +263,29 @@ final class PhabricatorRepositoryCommit
return isset($map[$audit->getAuditorPHID()]);
}
+ public function writeOwnersEdges(array $package_phids) {
+ $src_phid = $this->getPHID();
+ $edge_type = DiffusionCommitHasPackageEdgeType::EDGECONST;
+
+ $editor = new PhabricatorEdgeEditor();
+
+ $dst_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
+ $src_phid,
+ $edge_type);
+
+ foreach ($dst_phids as $dst_phid) {
+ $editor->removeEdge($src_phid, $edge_type, $dst_phid);
+ }
+
+ foreach ($package_phids as $package_phid) {
+ $editor->addEdge($src_phid, $edge_type, $package_phid);
+ }
+
+ $editor->save();
+
+ return $this;
+ }
+
public function getAuditorPHIDsForEdit() {
$audits = $this->getAudits();
return mpull($audits, 'getAuditorPHID');
@@ -567,6 +591,44 @@ final class PhabricatorRepositoryCommit
}
+/* -( HarbormasterBuildkiteBuildableInterface )---------------------------- */
+
+
+ public function getBuildkiteBranch() {
+ $viewer = PhabricatorUser::getOmnipotentUser();
+ $repository = $this->getRepository();
+
+ $branches = DiffusionQuery::callConduitWithDiffusionRequest(
+ $viewer,
+ DiffusionRequest::newFromDictionary(
+ array(
+ 'repository' => $repository,
+ 'user' => $viewer,
+ )),
+ 'diffusion.branchquery',
+ array(
+ 'contains' => $this->getCommitIdentifier(),
+ 'repository' => $repository->getPHID(),
+ ));
+
+ if (!$branches) {
+ throw new Exception(
+ pht(
+ 'Commit "%s" is not an ancestor of any branch head, so it can not '.
+ 'be built with Buildkite.',
+ $this->getCommitIdentifier()));
+ }
+
+ $branch = head($branches);
+
+ return 'refs/heads/'.$branch['shortName'];
+ }
+
+ public function getBuildkiteCommit() {
+ return $this->getCommitIdentifier();
+ }
+
+
/* -( PhabricatorCustomFieldInterface )------------------------------------ */
diff --git a/src/applications/repository/worker/PhabricatorRepositoryCommitOwnersWorker.php b/src/applications/repository/worker/PhabricatorRepositoryCommitOwnersWorker.php
index 20869ce273..5deb975fa9 100644
--- a/src/applications/repository/worker/PhabricatorRepositoryCommitOwnersWorker.php
+++ b/src/applications/repository/worker/PhabricatorRepositoryCommitOwnersWorker.php
@@ -28,6 +28,7 @@ final class PhabricatorRepositoryCommitOwnersWorker
private function triggerOwnerAudits(
PhabricatorRepository $repository,
PhabricatorRepositoryCommit $commit) {
+ $viewer = PhabricatorUser::getOmnipotentUser();
if (!$repository->shouldPublish()) {
return;
@@ -42,20 +43,29 @@ final class PhabricatorRepositoryCommitOwnersWorker
$repository,
$affected_paths);
+ $commit->writeOwnersEdges(mpull($affected_packages, 'getPHID'));
+
if (!$affected_packages) {
return;
}
- $data = id(new PhabricatorRepositoryCommitData())->loadOneWhere(
- 'commitID = %d',
- $commit->getID());
- $commit->attachCommitData($data);
+ $commit = id(new DiffusionCommitQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($commit->getPHID()))
+ ->needCommitData(true)
+ ->needAuditRequests(true)
+ ->executeOne();
+ if (!$commit) {
+ return;
+ }
+
+ $data = $commit->getCommitData();
$author_phid = $data->getCommitDetail('authorPHID');
$revision_id = $data->getCommitDetail('differential.revisionID');
if ($revision_id) {
$revision = id(new DifferentialRevisionQuery())
- ->setViewer(PhabricatorUser::getOmnipotentUser())
+ ->setViewer($viewer)
->withIDs(array($revision_id))
->needReviewerStatus(true)
->executeOne();
@@ -63,13 +73,10 @@ final class PhabricatorRepositoryCommitOwnersWorker
$revision = null;
}
- $requests = id(new PhabricatorRepositoryAuditRequest())
- ->loadAllWhere(
- 'commitPHID = %s',
- $commit->getPHID());
+ $requests = $commit->getAudits();
$requests = mpull($requests, null, 'getAuditorPHID');
-
+ $auditor_phids = array();
foreach ($affected_packages as $package) {
$request = idx($requests, $package->getPHID());
if ($request) {
@@ -77,61 +84,78 @@ final class PhabricatorRepositoryCommitOwnersWorker
continue;
}
- if ($package->getAuditingEnabled()) {
- $reasons = $this->checkAuditReasons(
- $commit,
- $package,
- $author_phid,
- $revision);
-
- if ($reasons) {
- $audit_status = PhabricatorAuditStatusConstants::AUDIT_REQUIRED;
- } else {
- $audit_status = PhabricatorAuditStatusConstants::AUDIT_NOT_REQUIRED;
- }
- } else {
- $reasons = array();
- $audit_status = PhabricatorAuditStatusConstants::NONE;
+ $should_audit = $this->shouldTriggerAudit(
+ $commit,
+ $package,
+ $author_phid,
+ $revision);
+ if (!$should_audit) {
+ continue;
}
- $relationship = new PhabricatorRepositoryAuditRequest();
- $relationship->setAuditorPHID($package->getPHID());
- $relationship->setCommitPHID($commit->getPHID());
- $relationship->setAuditReasons($reasons);
- $relationship->setAuditStatus($audit_status);
-
- $relationship->save();
-
- $requests[$package->getPHID()] = $relationship;
+ $auditor_phids[] = $package->getPHID();
}
- $commit->updateAuditStatus($requests);
- $commit->save();
+ // If none of the packages are triggering audits, we're all done.
+ if (!$auditor_phids) {
+ return;
+ }
+
+ $audit_type = DiffusionCommitAuditorsTransaction::TRANSACTIONTYPE;
+
+ $owners_phid = id(new PhabricatorOwnersApplication())
+ ->getPHID();
+
+ $content_source = $this->newContentSource();
+
+ $xactions = array();
+ $xactions[] = $commit->getApplicationTransactionTemplate()
+ ->setTransactionType($audit_type)
+ ->setNewValue(
+ array(
+ '+' => array_fuse($auditor_phids),
+ ));
+
+ $editor = $commit->getApplicationTransactionEditor()
+ ->setActor($viewer)
+ ->setActingAsPHID($owners_phid)
+ ->setContinueOnNoEffect(true)
+ ->setContinueOnMissingFields(true)
+ ->setContentSource($content_source);
+
+ $editor->applyTransactions($commit, $xactions);
}
- private function checkAuditReasons(
+ private function shouldTriggerAudit(
PhabricatorRepositoryCommit $commit,
PhabricatorOwnersPackage $package,
$author_phid,
$revision) {
+ // Don't trigger an audit if auditing isn't enabled for the package.
+ if (!$package->getAuditingEnabled()) {
+ return false;
+ }
+
+ // Trigger an audit if we don't recognize the commit's author.
+ if (!$author_phid) {
+ return true;
+ }
+
$owner_phids = PhabricatorOwnersOwner::loadAffiliatedUserPHIDs(
array(
$package->getID(),
));
$owner_phids = array_fuse($owner_phids);
- $reasons = array();
-
- if (!$author_phid) {
- $reasons[] = pht('Commit Author Not Recognized');
- } else if (isset($owner_phids[$author_phid])) {
- return $reasons;
+ // Don't trigger an audit if the author is a package owner.
+ if (isset($owner_phids[$author_phid])) {
+ return false;
}
+ // Trigger an audit of there is no corresponding revision.
if (!$revision) {
- $reasons[] = pht('No Revision Specified');
- return $reasons;
+ return true;
}
$accepted_statuses = array(
@@ -157,11 +181,13 @@ final class PhabricatorRepositoryCommitOwnersWorker
}
}
- if (!$found_accept) {
- $reasons[] = pht('Owners Not Involved');
+ // Don't trigger an audit if a package owner already reviewed the
+ // revision.
+ if ($found_accept) {
+ return false;
}
- return $reasons;
+ return true;
}
}
diff --git a/src/applications/search/controller/PhabricatorApplicationSearchController.php b/src/applications/search/controller/PhabricatorApplicationSearchController.php
index cdd6b9335a..c7c2b432b4 100644
--- a/src/applications/search/controller/PhabricatorApplicationSearchController.php
+++ b/src/applications/search/controller/PhabricatorApplicationSearchController.php
@@ -169,12 +169,15 @@ final class PhabricatorApplicationSearchController
}
$submit = id(new AphrontFormSubmitControl())
- ->setValue(pht('Execute Query'));
+ ->setValue(pht('Search'));
if ($run_query && !$named_query && $user->isLoggedIn()) {
- $submit->addCancelButton(
- '/search/edit/'.$saved_query->getQueryKey().'/',
- pht('Save Custom Query...'));
+ $save_button = id(new PHUIButtonView())
+ ->setTag('a')
+ ->setHref('/search/edit/'.$saved_query->getQueryKey().'/')
+ ->setText(pht('Save Query'))
+ ->setIcon('fa-floppy-o');
+ $submit->addButton($save_button);
}
// TODO: A "Create Dashboard Panel" action goes here somewhere once
diff --git a/src/applications/search/engine/PhabricatorProfileMenuEngine.php b/src/applications/search/engine/PhabricatorProfileMenuEngine.php
index 1af25a4eef..f41424a4e7 100644
--- a/src/applications/search/engine/PhabricatorProfileMenuEngine.php
+++ b/src/applications/search/engine/PhabricatorProfileMenuEngine.php
@@ -6,16 +6,20 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
private $profileObject;
private $customPHID;
private $items;
- private $menuType = self::MENU_GLOBAL;
private $defaultItem;
private $controller;
private $navigation;
private $showNavigation = true;
+ private $editMode;
+ private $pageClasses = array();
+ private $showContentCrumbs = true;
- const MENU_GLOBAL = 'global';
- const MENU_PERSONAL = 'personal';
- const MENU_COMBINED = 'menu';
const ITEM_CUSTOM_DIVIDER = 'engine.divider';
+ const ITEM_MANAGE = 'item.configure';
+
+ const MODE_COMBINED = 'combined';
+ const MODE_GLOBAL = 'global';
+ const MODE_CUSTOM = 'custom';
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
@@ -44,6 +48,21 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
return $this->customPHID;
}
+ private function getEditModeCustomPHID() {
+ $mode = $this->getEditMode();
+
+ switch ($mode) {
+ case self::MODE_CUSTOM:
+ $custom_phid = $this->getCustomPHID();
+ break;
+ case self::MODE_GLOBAL:
+ $custom_phid = null;
+ break;
+ }
+
+ return $custom_phid;
+ }
+
public function setController(PhabricatorController $controller) {
$this->controller = $controller;
return $this;
@@ -60,19 +79,10 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
}
public function getDefaultItem() {
- $this->loadItems();
+ $this->getItems();
return $this->defaultItem;
}
- public function setMenuType($type) {
- $this->menuType = $type;
- return $this;
- }
-
- private function getMenuType() {
- return $this->menuType;
- }
-
public function setShowNavigation($show) {
$this->showNavigation = $show;
return $this;
@@ -82,6 +92,20 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
return $this->showNavigation;
}
+ public function addContentPageClass($class) {
+ $this->pageClasses[] = $class;
+ return $this;
+ }
+
+ public function setShowContentCrumbs($show_content_crumbs) {
+ $this->showContentCrumbs = $show_content_crumbs;
+ return $this;
+ }
+
+ public function getShowContentCrumbs() {
+ return $this->showContentCrumbs;
+ }
+
abstract public function getItemURI($path);
abstract protected function isMenuEngineConfigurable();
@@ -93,6 +117,10 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
return array();
}
+ protected function getEditMode() {
+ return $this->editMode;
+ }
+
public function buildResponse() {
$controller = $this->getController();
@@ -106,18 +134,24 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
$item_action = 'view';
}
+ $is_view = ($item_action == 'view');
+
// If the engine is not configurable, don't respond to any of the editing
// or configuration routes.
if (!$this->isMenuEngineConfigurable()) {
- switch ($item_action) {
- case 'view':
- break;
- default:
- return new Aphront404Response();
+ if (!$is_view) {
+ return new Aphront404Response();
}
}
$item_id = $request->getURIData('itemID');
+
+ // If we miss on the MenuEngine route, try the EditEngine route. This will
+ // be populated while editing items.
+ if (!$item_id) {
+ $item_id = $request->getURIData('id');
+ }
+
$item_list = $this->getItems();
$selected_item = null;
@@ -140,7 +174,7 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
}
if (!$selected_item) {
- if ($item_action == 'view') {
+ if ($is_view) {
$selected_item = $this->getDefaultItem();
}
}
@@ -171,24 +205,38 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
$crumbs = $controller->buildApplicationCrumbsForEditEngine();
- // TODO: This stuff might need a little tweaking at some point, since it
- // causes "Global" and "Personal" to show up in contexts where they don't
- // make sense, notably Projects.
- if ($item_action != 'view') {
- $navigation->selectFilter('item.configure');
- switch ($this->getMenuType()) {
- case 'personal':
- $crumbs->addTextCrumb(pht('Personal'));
- break;
- case 'global':
- $crumbs->addTextCrumb(pht('Global'));
- break;
+ if (!$is_view) {
+ $navigation->selectFilter(self::ITEM_MANAGE);
+
+ if ($selected_item) {
+ if ($selected_item->getCustomPHID()) {
+ $edit_mode = 'custom';
+ } else {
+ $edit_mode = 'global';
+ }
+ } else {
+ $edit_mode = $request->getURIData('itemEditMode');
}
+
+ $available_modes = $this->getViewerEditModes($viewer);
+ if ($available_modes) {
+ $available_modes = array_fuse($available_modes);
+ if (isset($available_modes[$edit_mode])) {
+ $this->editMode = $edit_mode;
+ } else {
+ if ($item_action != 'configure') {
+ return new Aphront404Response();
+ }
+ }
+ }
+ $page_title = pht('Configure Menu');
+ } else {
+ $page_title = $selected_item->getDisplayName();
}
switch ($item_action) {
case 'view':
- $navigation->selectFilter($selected_item->getItemIdentifier());
+ $navigation->selectFilter($selected_item->getDefaultMenuItemKey());
$content = $this->buildItemViewContent($selected_item);
$crumbs->addTextCrumb($selected_item->getDisplayName());
@@ -197,15 +245,40 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
}
break;
case 'configure':
- $content = $this->buildItemConfigureContent($item_list);
- $crumbs->addTextCrumb(pht('Configure Menu'));
+ $mode = $this->getEditMode();
+ if (!$mode) {
+ $crumbs->addTextCrumb(pht('Configure Menu'));
+ $content = $this->buildMenuEditModeContent();
+ } else {
+ if (count($available_modes) > 1) {
+ $crumbs->addTextCrumb(
+ pht('Configure Menu'),
+ $this->getItemURI('configure/'));
+
+ switch ($mode) {
+ case self::MODE_CUSTOM:
+ $crumbs->addTextCrumb(pht('Personal'));
+ break;
+ case self::MODE_GLOBAL:
+ $crumbs->addTextCrumb(pht('Global'));
+ break;
+ }
+ } else {
+ $crumbs->addTextCrumb(pht('Configure Menu'));
+ }
+ $edit_list = $this->loadItems($mode);
+ $content = $this->buildItemConfigureContent($edit_list);
+ }
break;
case 'reorder':
- $content = $this->buildItemReorderContent($item_list);
+ $mode = $this->getEditMode();
+ $edit_list = $this->loadItems($mode);
+ $content = $this->buildItemReorderContent($edit_list);
break;
case 'new':
$item_key = $request->getURIData('itemKey');
- $content = $this->buildItemNewContent($item_key);
+ $mode = $this->getEditMode();
+ $content = $this->buildItemNewContent($item_key, $mode);
break;
case 'builtin':
$content = $this->buildItemBuiltinContent($selected_item);
@@ -214,6 +287,9 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
$content = $this->buildItemHideContent($selected_item);
break;
case 'default':
+ if (!$this->isMenuEnginePinnable()) {
+ return new Aphront404Response();
+ }
$content = $this->buildItemDefaultContent(
$selected_item,
$item_list);
@@ -239,14 +315,23 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
$crumbs->setBorder(true);
$page = $controller->newPage()
- ->setTitle(pht('Configure Menu'))
- ->setCrumbs($crumbs)
+ ->setTitle($page_title)
->appendChild($content);
+ if (!$is_view || $this->getShowContentCrumbs()) {
+ $page->setCrumbs($crumbs);
+ }
+
if ($this->getShowNavigation()) {
$page->setNavigation($navigation);
}
+ if ($is_view) {
+ foreach ($this->pageClasses as $class) {
+ $page->addClass($class);
+ }
+ }
+
return $page;
}
@@ -254,7 +339,6 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
if ($this->navigation) {
return $this->navigation;
}
-
$nav = id(new AphrontSideNavFilterView())
->setIsProfileMenu(true)
->setBaseURI(new PhutilURI($this->getItemURI('')));
@@ -291,8 +375,8 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
if (count($items) == 1) {
$item = head($items);
if ($item->getKey() === null) {
- $item_identifier = $menu_item->getItemIdentifier();
- $item->setKey($item_identifier);
+ $default_key = $menu_item->getDefaultMenuItemKey();
+ $item->setKey($default_key);
}
}
@@ -309,31 +393,30 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
private function getItems() {
if ($this->items === null) {
- $this->items = $this->loadItems();
+ $this->items = $this->loadItems(self::MODE_COMBINED);
}
return $this->items;
}
- private function loadItems() {
+ private function loadItems($mode) {
$viewer = $this->getViewer();
$object = $this->getProfileObject();
- $items = $this->loadBuiltinProfileItems();
+ $items = $this->loadBuiltinProfileItems($mode);
$query = id(new PhabricatorProfileMenuItemConfigurationQuery())
->setViewer($viewer)
->withProfilePHIDs(array($object->getPHID()));
- $menu_type = $this->getMenuType();
- switch ($menu_type) {
- case self::MENU_GLOBAL:
+ switch ($mode) {
+ case self::MODE_GLOBAL:
$query->withCustomPHIDs(array(), true);
break;
- case self::MENU_PERSONAL:
+ case self::MODE_CUSTOM:
$query->withCustomPHIDs(array($this->getCustomPHID()), false);
break;
- case self::MENU_COMBINED:
+ case self::MODE_COMBINED:
$query->withCustomPHIDs(array($this->getCustomPHID()), true);
break;
}
@@ -369,7 +452,7 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
}
}
- $items = $this->arrangeItems($items);
+ $items = $this->arrangeItems($items, $mode);
// Make sure exactly one valid item is marked as default.
$default = null;
@@ -379,9 +462,13 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
continue;
}
- if ($item->isDefault()) {
- $default = $item;
- break;
+ // If this engine doesn't support pinning items, don't respect any
+ // setting which might be present in the database.
+ if ($this->isMenuEnginePinnable()) {
+ if ($item->isDefault()) {
+ $default = $item;
+ break;
+ }
}
if ($first === null) {
@@ -400,20 +487,19 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
return $items;
}
- private function loadBuiltinProfileItems() {
+ private function loadBuiltinProfileItems($mode) {
$object = $this->getProfileObject();
- $menu_type = $this->getMenuType();
- switch ($menu_type) {
- case self::MENU_GLOBAL:
+ switch ($mode) {
+ case self::MODE_GLOBAL:
$builtins = $this->getBuiltinProfileItems($object);
break;
- case self::MENU_PERSONAL:
+ case self::MODE_CUSTOM:
$builtins = $this->getBuiltinCustomProfileItems(
$object,
$this->getCustomPHID());
break;
- case self::MENU_COMBINED:
+ case self::MODE_COMBINED:
$builtins = array();
$builtins[] = $this->getBuiltinCustomProfileItems(
$object,
@@ -490,6 +576,15 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
}
public function getConfigureURI() {
+ $mode = $this->getEditMode();
+
+ switch ($mode) {
+ case self::MODE_CUSTOM:
+ return $this->getItemURI('configure/custom/');
+ case self::MODE_GLOBAL:
+ return $this->getItemURI('configure/global/');
+ }
+
return $this->getItemURI('configure/');
}
@@ -501,7 +596,8 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
// object the menu appears on. If you're reordering custom items, you only
// need to be able to edit the custom object. Currently, the custom object
// is always the viewing user's own user object.
- $custom_phid = $this->getCustomPHID();
+ $custom_phid = $this->getEditModeCustomPHID();
+
if (!$custom_phid) {
PhabricatorPolicyFilter::requireCapability(
$viewer,
@@ -589,11 +685,102 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
->setURI($this->getConfigureURI());
}
- private function buildItemViewContent(
+ protected function buildItemViewContent(
PhabricatorProfileMenuItemConfiguration $item) {
return $item->newPageContent();
}
+ private function getViewerEditModes() {
+ $modes = array();
+
+ $viewer = $this->getViewer();
+
+ if ($viewer->isLoggedIn() && $this->isMenuEnginePersonalizable()) {
+ $modes[] = self::MODE_CUSTOM;
+ }
+
+ $object = $this->getProfileObject();
+ $can_edit = PhabricatorPolicyFilter::hasCapability(
+ $viewer,
+ $object,
+ PhabricatorPolicyCapability::CAN_EDIT);
+
+ if ($can_edit) {
+ $modes[] = self::MODE_GLOBAL;
+ }
+
+ return $modes;
+ }
+
+ protected function isMenuEnginePersonalizable() {
+ return true;
+ }
+
+ /**
+ * Does this engine support pinning items?
+ *
+ * Personalizable menus disable pinning by default since it creates a number
+ * of weird edge cases without providing many benefits for current menus.
+ *
+ * @return bool True if items may be pinned as default items.
+ */
+ protected function isMenuEnginePinnable() {
+ return !$this->isMenuEnginePersonalizable();
+ }
+
+ private function buildMenuEditModeContent() {
+ $viewer = $this->getViewer();
+
+ $modes = $this->getViewerEditModes($viewer);
+ if (!$modes) {
+ return new Aphront404Response();
+ }
+
+ if (count($modes) == 1) {
+ $mode = head($modes);
+ return id(new AphrontRedirectResponse())
+ ->setURI($this->getItemURI("configure/{$mode}/"));
+ }
+
+ $menu = id(new PHUIObjectItemListView())
+ ->setUser($viewer);
+
+ $modes = array_fuse($modes);
+
+ if (isset($modes['custom'])) {
+ $menu->addItem(
+ id(new PHUIObjectItemView())
+ ->setHeader(pht('Personal Menu Items'))
+ ->setHref($this->getItemURI('configure/custom/'))
+ ->setImageURI($viewer->getProfileImageURI())
+ ->addAttribute(pht('Edit the menu for your personal account.')));
+ }
+
+ if (isset($modes['global'])) {
+ $icon = id(new PHUIIconView())
+ ->setIcon('fa-globe')
+ ->setBackground('bg-blue');
+
+ $menu->addItem(
+ id(new PHUIObjectItemView())
+ ->setHeader(pht('Global Menu Items'))
+ ->setHref($this->getItemURI('configure/global/'))
+ ->setImageIcon($icon)
+ ->addAttribute(pht('Edit the global default menu for all users.')));
+ }
+
+ $box = id(new PHUIObjectBoxView())
+ ->setObjectList($menu);
+
+ $header = id(new PHUIHeaderView())
+ ->setHeader(pht('Manage Menu'))
+ ->setHeaderIcon('fa-list');
+
+ return id(new PHUITwoColumnView())
+ ->setHeader($header)
+ ->setFooter($box);
+ }
+
private function buildItemConfigureContent(array $items) {
$viewer = $this->getViewer();
$object = $this->getProfileObject();
@@ -617,11 +804,13 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
$list_id = celerity_generate_unique_node_id();
+ $mode = $this->getEditMode();
+
Javelin::initBehavior(
'reorder-profile-menu-items',
array(
'listID' => $list_id,
- 'orderURI' => $this->getItemURI('reorder/'),
+ 'orderURI' => $this->getItemURI("reorder/{$mode}/"),
));
$list = id(new PHUIObjectItemListView())
@@ -663,14 +852,16 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
$default_uri = $this->getItemURI("default/{$builtin_key}/");
}
- if ($item->isDefault()) {
- $default_icon = 'fa-thumb-tack green';
- $default_text = pht('Current Default');
- } else if ($item->canMakeDefault()) {
- $default_icon = 'fa-thumb-tack';
- $default_text = pht('Make Default');
- } else {
- $default_text = null;
+ $default_text = null;
+
+ if ($this->isMenuEnginePinnable()) {
+ if ($item->isDefault()) {
+ $default_icon = 'fa-thumb-tack green';
+ $default_text = pht('Current Default');
+ } else if ($item->canMakeDefault()) {
+ $default_icon = 'fa-thumb-tack';
+ $default_text = pht('Make Default');
+ }
}
if ($default_text !== null) {
@@ -739,12 +930,13 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
}
$item_key = $item_type->getMenuItemKey();
+ $edit_mode = $this->getEditMode();
$action_list->addAction(
id(new PhabricatorActionView())
->setIcon($item_type->getMenuItemTypeIcon())
->setName($item_type->getMenuItemTypeName())
- ->setHref($this->getItemURI("new/{$item_key}/"))
+ ->setHref($this->getItemURI("new/{$edit_mode}/{$item_key}/"))
->setWorkflow(true));
}
@@ -789,7 +981,7 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
return $view;
}
- private function buildItemNewContent($item_key) {
+ private function buildItemNewContent($item_key, $mode) {
$item_types = PhabricatorProfileMenuItem::getAllMenuItems();
$item_type = idx($item_types, $item_key);
if (!$item_type) {
@@ -801,7 +993,8 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
return new Aphront404Response();
}
- $custom_phid = $this->getCustomPHID();
+ $custom_phid = $this->getEditModeCustomPHID();
+
$configuration = PhabricatorProfileMenuItemConfiguration::initializeNewItem(
$object,
$item_type,
@@ -829,12 +1022,13 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
$viewer = $this->getViewer();
$object = $this->getProfileObject();
$controller = $this->getController();
+ $custom_phid = $this->getEditModeCustomPHID();
return id(new PhabricatorProfileMenuEditEngine())
->setMenuEngine($this)
->setProfileObject($object)
->setController($controller)
- ->setCustomPHID($this->getCustomPHID())
+ ->setCustomPHID($custom_phid)
->buildResponse();
}
@@ -860,6 +1054,7 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
$object = $this->getProfileObject();
$controller = $this->getController();
+ $custom_phid = $this->getEditModeCustomPHID();
return id(new PhabricatorProfileMenuEditEngine())
->setIsBuiltin(true)
@@ -867,7 +1062,7 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
->setProfileObject($object)
->setNewMenuItemConfiguration($configuration)
->setController($controller)
- ->setCustomPHID($this->getCustomPHID())
+ ->setCustomPHID($custom_phid)
->buildResponse();
}
@@ -1009,12 +1204,18 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
return PhabricatorProfileMenuItemConfiguration::initializeNewBuiltin();
}
+ protected function newManageItem() {
+ return $this->newItem()
+ ->setBuiltinKey(self::ITEM_MANAGE)
+ ->setMenuItemKey(PhabricatorManageProfileMenuItem::MENUITEMKEY);
+ }
+
public function adjustDefault($key) {
$controller = $this->getController();
$request = $controller->getRequest();
$viewer = $request->getViewer();
- $items = $this->loadItems();
+ $items = $this->loadItems(self::MODE_COMBINED);
// To adjust the default item, we first change any existing items that
// are marked as defaults to "visible", then make the new default item
@@ -1075,13 +1276,15 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
return $this;
}
- private function arrangeItems(array $items) {
+ private function arrangeItems(array $items, $mode) {
// Sort the items.
$items = msortv($items, 'getSortVector');
+ $object = $this->getProfileObject();
+
// If we have some global items and some custom items and are in "combined"
// mode, put a hard-coded divider item between them.
- if ($this->getMenuType() == self::MENU_COMBINED) {
+ if ($mode == self::MODE_COMBINED) {
$list = array();
$seen_custom = false;
$seen_global = false;
@@ -1093,6 +1296,7 @@ abstract class PhabricatorProfileMenuEngine extends Phobject {
$list[] = $this->newItem()
->setBuiltinKey(self::ITEM_CUSTOM_DIVIDER)
->setMenuItemKey(PhabricatorDividerProfileMenuItem::MENUITEMKEY)
+ ->attachProfileObject($object)
->attachMenuItem(
new PhabricatorDividerProfileMenuItem());
}
diff --git a/src/applications/search/menuitem/PhabricatorApplicationProfileMenuItem.php b/src/applications/search/menuitem/PhabricatorApplicationProfileMenuItem.php
index 7d87bc25d4..92477522ba 100644
--- a/src/applications/search/menuitem/PhabricatorApplicationProfileMenuItem.php
+++ b/src/applications/search/menuitem/PhabricatorApplicationProfileMenuItem.php
@@ -88,6 +88,12 @@ final class PhabricatorApplicationProfileMenuItem
->setName($this->getDisplayName($config))
->setIcon($app->getIcon());
+ // Don't show tooltip if they've set a custom name
+ $name = $config->getMenuItemProperty('name');
+ if (!strlen($name)) {
+ $item->setTooltip($app->getShortDescription());
+ }
+
return array(
$item,
);
diff --git a/src/applications/search/menuitem/PhabricatorDashboardProfileMenuItem.php b/src/applications/search/menuitem/PhabricatorDashboardProfileMenuItem.php
index 002e5778d5..c442a2cc46 100644
--- a/src/applications/search/menuitem/PhabricatorDashboardProfileMenuItem.php
+++ b/src/applications/search/menuitem/PhabricatorDashboardProfileMenuItem.php
@@ -60,8 +60,8 @@ final class PhabricatorDashboardProfileMenuItem
}
$engine = id(new PhabricatorDashboardRenderingEngine())
- ->setViewer($viewer)
- ->setDashboard($dashboard);
+ ->setViewer($viewer)
+ ->setDashboard($dashboard);
return $engine->renderDashboard();
}
@@ -89,9 +89,11 @@ final class PhabricatorDashboardProfileMenuItem
public function getDisplayName(
PhabricatorProfileMenuItemConfiguration $config) {
$dashboard = $this->getDashboard();
+
if (!$dashboard) {
return pht('(Restricted/Invalid Dashboard)');
}
+
if (strlen($this->getName($config))) {
return $this->getName($config);
} else {
diff --git a/src/applications/search/menuitem/PhabricatorDividerProfileMenuItem.php b/src/applications/search/menuitem/PhabricatorDividerProfileMenuItem.php
index 255e0d4b72..e6a6e608e6 100644
--- a/src/applications/search/menuitem/PhabricatorDividerProfileMenuItem.php
+++ b/src/applications/search/menuitem/PhabricatorDividerProfileMenuItem.php
@@ -38,6 +38,7 @@ final class PhabricatorDividerProfileMenuItem
PhabricatorProfileMenuItemConfiguration $config) {
$item = $this->newItem()
+ ->setType(PHUIListItemView::TYPE_DIVIDER)
->addClass('phui-divider');
return array(
diff --git a/src/applications/search/menuitem/PhabricatorLabelProfileMenuItem.php b/src/applications/search/menuitem/PhabricatorLabelProfileMenuItem.php
new file mode 100644
index 0000000000..1f769905d7
--- /dev/null
+++ b/src/applications/search/menuitem/PhabricatorLabelProfileMenuItem.php
@@ -0,0 +1,75 @@
+getLabelName($config);
+ }
+
+ public function buildEditEngineFields(
+ PhabricatorProfileMenuItemConfiguration $config) {
+ return array(
+ id(new PhabricatorTextEditField())
+ ->setKey(self::FIELD_NAME)
+ ->setLabel(pht('Name'))
+ ->setIsRequired(true)
+ ->setValue($this->getLabelName($config)),
+ );
+ }
+
+ private function getLabelName(
+ PhabricatorProfileMenuItemConfiguration $config) {
+ return $config->getMenuItemProperty('name');
+ }
+
+ protected function newNavigationMenuItems(
+ PhabricatorProfileMenuItemConfiguration $config) {
+
+ $name = $this->getLabelName($config);
+
+ $item = $this->newItem()
+ ->setName($name)
+ ->setType(PHUIListItemView::TYPE_LABEL);
+
+ return array(
+ $item,
+ );
+ }
+
+ public function validateTransactions(
+ PhabricatorProfileMenuItemConfiguration $config,
+ $field_key,
+ $value,
+ array $xactions) {
+
+ $viewer = $this->getViewer();
+ $errors = array();
+
+ if ($field_key == self::FIELD_NAME) {
+ if ($this->isEmptyTransaction($value, $xactions)) {
+ $errors[] = $this->newRequiredError(
+ pht('You must choose a label name.'),
+ $field_key);
+ }
+ }
+
+ return $errors;
+ }
+}
diff --git a/src/applications/home/menuitem/PhabricatorHomeManageProfileMenuItem.php b/src/applications/search/menuitem/PhabricatorManageProfileMenuItem.php
similarity index 68%
rename from src/applications/home/menuitem/PhabricatorHomeManageProfileMenuItem.php
rename to src/applications/search/menuitem/PhabricatorManageProfileMenuItem.php
index 514cd8e026..d5de555975 100644
--- a/src/applications/home/menuitem/PhabricatorHomeManageProfileMenuItem.php
+++ b/src/applications/search/menuitem/PhabricatorManageProfileMenuItem.php
@@ -1,16 +1,16 @@
getViewer();
- if ($viewer->isLoggedIn()) {
- $admin = $viewer->getIsAdmin();
- $name = $this->getDisplayName($config);
- $icon = 'fa-pencil';
- $href = '/home/menu/personal/item/configure/';
- if ($admin) {
- $href = '/home/menu/';
- }
-
- $item = $this->newItem()
- ->setHref($href)
- ->setName($name)
- ->setIcon($icon);
+ if (!$viewer->isLoggedIn()) {
+ return array();
}
+ $engine = $this->getEngine();
+ $href = $engine->getItemURI('configure/');
+
+ $name = $this->getDisplayName($config);
+ $icon = 'fa-pencil';
+
+ $item = $this->newItem()
+ ->setHref($href)
+ ->setName($name)
+ ->setIcon($icon);
+
return array(
$item,
);
diff --git a/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php b/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php
index 83a84ac306..230989e88e 100644
--- a/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php
+++ b/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php
@@ -117,6 +117,7 @@ final class PhabricatorProfileMenuItemConfigurationQuery
unset($page[$key]);
continue;
}
+
$item->attachProfileObject($profile);
}
diff --git a/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php b/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php
index f178cc49fe..37124393fb 100644
--- a/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php
+++ b/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php
@@ -195,6 +195,14 @@ final class PhabricatorProfileMenuItemConfiguration
return $this->getBuiltinKey();
}
+ public function getDefaultMenuItemKey() {
+ if ($this->getBuiltinKey()) {
+ return $this->getBuiltinKey();
+ }
+
+ return $this->getPHID();
+ }
+
public function newPageContent() {
return $this->getMenuItem()->newPageContent($this);
}
diff --git a/src/applications/settings/panel/PhabricatorHomePreferencesSettingsPanel.php b/src/applications/settings/panel/PhabricatorHomePreferencesSettingsPanel.php
deleted file mode 100644
index ac950eade1..0000000000
--- a/src/applications/settings/panel/PhabricatorHomePreferencesSettingsPanel.php
+++ /dev/null
@@ -1,223 +0,0 @@
-getViewer();
- $preferences = $this->getPreferences();
-
- $pinned_key = PhabricatorPinnedApplicationsSetting::SETTINGKEY;
- $pinned = $preferences->getSettingValue($pinned_key);
-
- $apps = id(new PhabricatorApplicationQuery())
- ->setViewer($viewer)
- ->withInstalled(true)
- ->withUnlisted(false)
- ->withLaunchable(true)
- ->execute();
-
- $app_list = array();
- foreach ($pinned as $app) {
- if (isset($apps[$app])) {
- $app_list[$app] = $apps[$app];
- }
- }
-
- if ($request->getBool('reset')) {
- if ($request->isFormPost()) {
- $this->writePinnedApplications($preferences, null);
- return id(new AphrontRedirectResponse())
- ->setURI($this->getPanelURI());
- }
-
- return $this->newDialog()
- ->setTitle(pht('Reset Applications'))
- ->addHiddenInput('reset', 'true')
- ->appendParagraph(
- pht('Reset pinned applications to their defaults?'))
- ->addSubmitButton(pht('Reset Applications'))
- ->addCancelButton($this->getPanelURI());
- }
-
-
- if ($request->getBool('add')) {
- $options = array();
- foreach ($apps as $app) {
- $options[get_class($app)] = $app->getName();
- }
- asort($options);
-
- unset($options['PhabricatorApplicationsApplication']);
-
- if ($request->isFormPost()) {
- $pins = $request->getArr('pin');
- $phid = head($pins);
- $app = id(new PhabricatorApplicationQuery())
- ->setViewer($viewer)
- ->withPHIDs(array($phid))
- ->executeOne();
- if ($app) {
- $pin = get_class($app);
- } else {
- // This likely means the user submitted an empty form
- // which will cause nothing to happen.
- $pin = '';
- }
- if (isset($options[$pin]) && !in_array($pin, $pinned)) {
- $pinned[] = $pin;
-
- $this->writePinnedApplications($preferences, $pinned);
-
- return id(new AphrontRedirectResponse())
- ->setURI($this->getPanelURI());
- }
- }
-
- $options_control = id(new AphrontFormTokenizerControl())
- ->setName('pin')
- ->setLabel(pht('Application'))
- ->setDatasource(new PhabricatorApplicationDatasource())
- ->setLimit(1);
-
- $form = id(new AphrontFormView())
- ->setViewer($viewer)
- ->addHiddenInput('add', 'true')
- ->appendRemarkupInstructions(
- pht('Choose an application to pin to your home page.'))
- ->appendControl($options_control);
-
- return $this->newDialog()
- ->setWidth(AphrontDialogView::WIDTH_FORM)
- ->setTitle(pht('Pin Application'))
- ->appendChild($form->buildLayoutView())
- ->addSubmitButton(pht('Pin Application'))
- ->addCancelButton($this->getPanelURI());
- }
-
- $unpin = $request->getStr('unpin');
- if ($unpin) {
- $app = idx($apps, $unpin);
- if ($app) {
- if ($request->isFormPost()) {
- $pinned = array_diff($pinned, array($unpin));
-
- $this->writePinnedApplications($preferences, $pinned);
-
- return id(new AphrontRedirectResponse())
- ->setURI($this->getPanelURI());
- }
-
- return $this->newDialog()
- ->setTitle(pht('Unpin Application'))
- ->addHiddenInput('unpin', $unpin)
- ->appendParagraph(
- pht(
- 'Unpin the %s application from your home page?',
- phutil_tag('strong', array(), $app->getName())))
- ->addSubmitButton(pht('Unpin Application'))
- ->addCancelButton($this->getPanelURI());
- }
- }
-
- $order = $request->getStrList('order');
- if ($order && $request->validateCSRF()) {
- $this->writePinnedApplications($preferences, $order);
-
- return id(new AphrontRedirectResponse())
- ->setURI($this->getPanelURI());
- }
-
- $list_id = celerity_generate_unique_node_id();
-
- $list = id(new PHUIObjectItemListView())
- ->setViewer($viewer)
- ->setID($list_id)
- ->setDrag(true);
-
- Javelin::initBehavior(
- 'reorder-applications',
- array(
- 'listID' => $list_id,
- 'panelURI' => $this->getPanelURI(),
- ));
-
- foreach ($app_list as $key => $application) {
- if ($key == 'PhabricatorApplicationsApplication') {
- continue;
- }
-
- $icon = $application->getIcon();
- if (!$icon) {
- $icon = 'fa-globe';
- }
-
- $item = id(new PHUIObjectItemView())
- ->setHeader($application->getName())
- ->setImageIcon($icon)
- ->setGrippable(true);
-
- $item->addAction(
- id(new PHUIListItemView())
- ->setIcon('fa-times')
- ->setHref($this->getPanelURI().'?unpin='.$key)
- ->setWorkflow(true));
-
- $item->addSigil('pinned-application');
- $item->setMetadata(
- array(
- 'applicationClass' => $key,
- ));
-
- $list->addItem($item);
- }
-
- $header = id(new PHUIHeaderView())
- ->setHeader(pht('Pinned Applications'))
- ->addActionLink(
- id(new PHUIButtonView())
- ->setTag('a')
- ->setText(pht('Pin Application'))
- ->setHref($this->getPanelURI().'?add=true')
- ->setWorkflow(true)
- ->setIcon('fa-thumb-tack'))
- ->addActionLink(
- id(new PHUIButtonView())
- ->setTag('a')
- ->setText(pht('Reset to Defaults'))
- ->setHref($this->getPanelURI().'?reset=true')
- ->setWorkflow(true)
- ->setIcon('fa-recycle'));
-
- $box = id(new PHUIObjectBoxView())
- ->setHeader($header)
- ->setObjectList($list);
-
- return $box;
- }
-
- private function writePinnedApplications(
- PhabricatorUserPreferences $preferences,
- $pinned) {
-
- $pinned_key = PhabricatorPinnedApplicationsSetting::SETTINGKEY;
- $this->writeSetting($preferences, $pinned_key, $pinned);
- }
-
-}
diff --git a/src/docs/user/userguide/audit.diviner b/src/docs/user/userguide/audit.diviner
index de6878ed53..806d37adab 100644
--- a/src/docs/user/userguide/audit.diviner
+++ b/src/docs/user/userguide/audit.diviner
@@ -156,6 +156,46 @@ Audit Tips
- Press "?" to view keyboard shortcuts.
+Audit Maintenance
+=================
+
+The `bin/audit` command allows you to perform several maintenance operations.
+Get more information about a command by running:
+
+```
+phabricator/ $ ./bin/audit help
+```
+
+Supported operations are:
+
+**Delete Audits**: Delete audits that match certain parameters with
+`bin/audit delete`.
+
+You can use this command to forcibly delete requests which may have triggered
+incorrectly (for example, because a package or Herald rule was configured in an
+overbroad way).
+
+After deleting audits, you may want to run `bin/audit synchronize` to
+synchronize audit state.
+
+**Synchronize Audit State**: Synchronize the audit state of commits to the
+current open audit requests with `bin/audit synchronize`.
+
+Normally, overall audit state is automatically kept up to date as changes are
+made to an audit. However, if you delete audits or manually update the database
+to make changes to audit request state, the state of corresponding commits may
+no longer be correct.
+
+This command will update commits so their overall audit state reflects the
+cumulative state of their actual audit requests.
+
+**Update Owners Package Membership**: Update which Owners packages commits
+belong to with `bin/audit update-owners`.
+
+Normally, commits are automatically associated with packages when they are
+imported. You can use this command to manually rebuild this association if
+you run into problems with it.
+
Next Steps
==========
diff --git a/src/docs/user/userguide/profile_menu.diviner b/src/docs/user/userguide/profile_menu.diviner
index 2b4ffc4be8..2725e71851 100644
--- a/src/docs/user/userguide/profile_menu.diviner
+++ b/src/docs/user/userguide/profile_menu.diviner
@@ -16,21 +16,12 @@ Supported Applications
These applications currently support profile menus:
-| Application | Support |
-|-----|-----|
-| Projects | Full |
-| People | //Read-Only// |
-
-
-Collapsing and Expanding
-========================
-
-To collapse a full-width profile menu, click
-{nav icon="angle-left", name="Collapse"}. To expand a narrow menu, click
-{nav icon="angle-right", name="Expand"}.
-
-If you're logged in, this setting is sticky, and all menus will respect your
-preference.
+| Application | Customization | Support |
+|-----|-----|-----|
+| Home | Global/Personal | Full |
+| Projects | Per Project | Full |
+| Favorites Menu | Global/Personal | Full |
+| People | None | //Read-Only// |
Editing Menus
@@ -40,7 +31,10 @@ You can only edit an object's menu if you can edit the object. For example, you
must have permission to edit a project in order to reconfigure the menu for the
project.
-To edit a menu, click {nav icon="cogs", name="Manage"} in the menu, then click
+To edit a menu, click {nav icon="cogs", name="Manage"} or {nav icon="pencil",
+name="Edit ..."} in the menu itself. If you are an administrator and the
+application supports Global/Personal customization, you'll have the option
+of editing either the Global settings or your own Personal menu, otherwise click
{nav icon="th-list", name="Edit Menu"}. This brings you to the menu
configuration interface which allows you to add and remove items, reorder the
menu, edit existing items, and choose a default item.
@@ -94,7 +88,7 @@ To remove items, click the {nav icon="times", name="Delete"} action.
Builtin items can not be deleted and have a
{nav icon="times", name="Disable"} action instead, which will hide them but
not delete them. You an re-enable a disabled item with the
-{nav icon="plus', name="Enable"} action.
+{nav icon="plus", name="Enable"} action.
A few items can not be hidden or deleted. For example, the
{nav icon="cogs", name="Manage"} item must always be available in the menu
@@ -130,17 +124,36 @@ When you add items, you can choose between different types of items to add.
Which item types are available depends on what sort of object you are editing
the menu for, but most objects support these items:
- - {icon link} **Link**: Allows you to create an item which links to
- somewhere else in Phabricator, or to an external site.
+
- {icon minus} **Divider**: Adds a visual separator to the menu. This is
purely cosmetic.
- - {icon coffee} **Motivator**: Motivate your employees with inspirational
- quotes. A new quote every day!
+ - {icon map-marker} **Label**: Lets you label sections of menu items.
+ This is also purely cosmetic.
+ - {icon link} **Link**: Allows you to create an item which links to
+ somewhere else in Phabricator, or to an external site.
+ - {icon plus} **Form**: Provides quick access to custom and built-in forms
+ from any application that supports EditEngine.
+ - {icon briefcase} **Projects**: Provides quick access to a project.
+ - {icon globe} **Applications**: Provides quick access to your favorite
+ applications. Can be renamed.
+ - {icon tachometer} **Dashboard**: Provides quick access to your favorite
+ dashboard. These items will display with the current nav on the item
+ you've attached it to.
+
To learn more about how an item works, try adding it. You can always delete
it later if it doesn't do what you wanted.
+Dashboard Integration
+=====================
+
+Dashboards are directly integrated with Profile Menus. If you add a Dashboard
+to a Project or to a Home menu, that Dashboard will be presented in the
+context of that menu. This allows customization of different pages of content
+without having the user leave Home or the Project.
+
+
Writing New Item Types
======================
diff --git a/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php b/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php
index 291097a6c0..428f3955cf 100644
--- a/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php
+++ b/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php
@@ -1588,6 +1588,26 @@ final class PhabricatorUSEnglishTranslation
),
),
+ '%s added %s auditor(s): %s.' => array(
+ array(
+ '%s added an auditor: %3$s.',
+ '%s added auditors: %3$s.',
+ ),
+ ),
+
+ '%s removed %s auditor(s): %s.' => array(
+ array(
+ '%s removed an auditor: %3$s.',
+ '%s removed auditors: %3$s.',
+ ),
+ ),
+
+ '%s edited %s auditor(s), removed %s: %s; added %s: %s.' => array(
+ array(
+ '%s edited auditors, removed: %4$s; added: %6$s.',
+ ),
+ ),
+
);
}
diff --git a/src/view/form/control/AphrontFormRecaptchaControl.php b/src/view/form/control/AphrontFormRecaptchaControl.php
index 078eb1afc2..8c056b5bf6 100644
--- a/src/view/form/control/AphrontFormRecaptchaControl.php
+++ b/src/view/form/control/AphrontFormRecaptchaControl.php
@@ -1,10 +1,5 @@
getBool('recaptcha_response_field');
+ return $request->getBool('g-recaptcha-response');
}
public static function processCaptcha(AphrontRequest $request) {
@@ -33,30 +23,35 @@ final class AphrontFormRecaptchaControl extends AphrontFormControl {
return true;
}
- self::requireLib();
+ $uri = 'https://www.google.com/recaptcha/api/siteverify';
+ $params = array(
+ 'secret' => PhabricatorEnv::getEnvConfig('recaptcha.private-key'),
+ 'response' => $request->getStr('g-recaptcha-response'),
+ 'remoteip' => $request->getRemoteAddress(),
+ );
- $challenge = $request->getStr('recaptcha_challenge_field');
- $response = $request->getStr('recaptcha_response_field');
- $resp = recaptcha_check_answer(
- PhabricatorEnv::getEnvConfig('recaptcha.private-key'),
- $_SERVER['REMOTE_ADDR'],
- $challenge,
- $response);
+ list($body) = id(new HTTPSFuture($uri, $params))
+ ->setMethod('POST')
+ ->resolvex();
- return (bool)@$resp->is_valid;
+ $json = phutil_json_decode($body);
+ return (bool)idx($json, 'success');
}
protected function renderInput() {
- self::requireLib();
+ $js = 'https://www.google.com/recaptcha/api.js';
+ $pubkey = PhabricatorEnv::getEnvConfig('recaptcha.public-key');
- $uri = new PhutilURI(PhabricatorEnv::getEnvConfig('phabricator.base-uri'));
- $protocol = $uri->getProtocol();
- $use_ssl = ($protocol == 'https');
+ return array(
+ phutil_tag('div', array(
+ 'class' => 'g-recaptcha',
+ 'data-sitekey' => $pubkey,
+ )),
- return phutil_safe_html(recaptcha_get_html(
- PhabricatorEnv::getEnvConfig('recaptcha.public-key'),
- $error = null,
- $use_ssl));
+ phutil_tag('script', array(
+ 'type' => 'text/javascript',
+ 'src' => $js,
+ )),
+ );
}
-
}
diff --git a/src/view/form/control/AphrontFormSubmitControl.php b/src/view/form/control/AphrontFormSubmitControl.php
index 2d4bb09f5e..c2bf716848 100644
--- a/src/view/form/control/AphrontFormSubmitControl.php
+++ b/src/view/form/control/AphrontFormSubmitControl.php
@@ -2,20 +2,23 @@
final class AphrontFormSubmitControl extends AphrontFormControl {
- private $cancelButton;
+ private $buttons = array();
public function addCancelButton($href, $label = null) {
if (!$label) {
$label = pht('Cancel');
}
+ $button = id(new PHUIButtonView())
+ ->setTag('a')
+ ->setHref($href)
+ ->setText($label)
+ ->setColor(PHUIButtonView::GREY);
+ $this->addButton($button);
+ return $this;
+ }
- $this->cancelButton = phutil_tag(
- 'a',
- array(
- 'href' => $href,
- 'class' => 'button grey',
- ),
- $label);
+ public function addButton(PHUIButtonView $button) {
+ $this->buttons[] = $button;
return $this;
}
@@ -38,7 +41,7 @@ final class AphrontFormSubmitControl extends AphrontFormControl {
return array(
$submit_button,
- $this->cancelButton,
+ $this->buttons,
);
}
diff --git a/src/view/layout/AphrontSideNavFilterView.php b/src/view/layout/AphrontSideNavFilterView.php
index 9dd3720646..87c0d920e8 100644
--- a/src/view/layout/AphrontSideNavFilterView.php
+++ b/src/view/layout/AphrontSideNavFilterView.php
@@ -204,7 +204,6 @@ final class AphrontSideNavFilterView extends AphrontView {
private function renderFlexNav() {
require_celerity_resource('phabricator-nav-view-css');
- require_celerity_resource('phui-profile-menu-css');
$nav_classes = array();
$nav_classes[] = 'phabricator-nav';
@@ -310,7 +309,7 @@ final class AphrontSideNavFilterView extends AphrontView {
$classes[] = 'phui-navigation-shell';
if ($this->getIsProfileMenu()) {
- $classes[] = 'phui-profile-menu';
+ $classes[] = 'phui-profile-menu phui-basic-nav';
} else {
$classes[] = 'phui-basic-nav';
}
diff --git a/src/view/phui/PHUIIconView.php b/src/view/phui/PHUIIconView.php
index cdc3825135..d6b1ad0ccd 100644
--- a/src/view/phui/PHUIIconView.php
+++ b/src/view/phui/PHUIIconView.php
@@ -507,6 +507,7 @@ final class PHUIIconView extends AphrontTagView {
'fa-pied-piper-square',
'fa-pied-piper',
'fa-pied-piper-alt',
+ 'fa-pied-piper-pp',
'fa-drupal',
'fa-joomla',
'fa-language',
@@ -781,6 +782,69 @@ final class PHUIIconView extends AphrontTagView {
'fa-snapchat',
'fa-snapchat-ghost',
'fa-snapchat-square',
+ 'fa-first-order',
+ 'fa-yoast',
+ 'fa-themeisle',
+ 'fa-google-plus-circle',
+ 'fa-google-plus-official',
+ 'fa-fa',
+ 'fa-font-awesome',
+ 'fa-handshake-o',
+ 'fa-envelope-open',
+ 'fa-envelope-open-o',
+ 'fa-linode',
+ 'fa-address-book',
+ 'fa-address-book-o',
+ 'fa-vcard',
+ 'fa-address-card',
+ 'fa-vcard-o',
+ 'fa-address-card-o',
+ 'fa-user-circle',
+ 'fa-user-circle-o',
+ 'fa-user-o:before',
+ 'fa-id-badge',
+ 'fa-drivers-license',
+ 'fa-id-card',
+ 'fa-drivers-license-o',
+ 'fa-id-card-o',
+ 'fa-quora',
+ 'fa-free-code-camp',
+ 'fa-telegram',
+ 'fa-thermometer-4',
+ 'fa-thermometer',
+ 'fa-thermometer-full',
+ 'fa-thermometer-3',
+ 'fa-thermometer-three-quarters',
+ 'fa-thermometer-2',
+ 'fa-thermometer-half',
+ 'fa-thermometer-1',
+ 'fa-thermometer-quarter',
+ 'fa-thermometer-0:',
+ 'fa-thermometer-empty',
+ 'fa-shower',
+ 'fa-bathtub',
+ 'fa-s15',
+ 'fa-bath',
+ 'fa-podcast',
+ 'fa-window-maximize',
+ 'fa-window-minimize',
+ 'fa-window-restore',
+ 'fa-times-rectangle',
+ 'fa-window-close',
+ 'fa-times-rectangle-o',
+ 'fa-window-close-o',
+ 'fa-bandcamp',
+ 'fa-grav',
+ 'fa-etsy',
+ 'fa-imdb',
+ 'fa-ravelry',
+ 'fa-eercast',
+ 'fa-microchip',
+ 'fa-snowflake-o',
+ 'fa-superpowers',
+ 'fa-wpexplorer',
+ 'fa-meetup',
+
);
}
diff --git a/src/view/phui/PHUIListItemView.php b/src/view/phui/PHUIListItemView.php
index 0ca942dd6a..7b6e55a697 100644
--- a/src/view/phui/PHUIListItemView.php
+++ b/src/view/phui/PHUIListItemView.php
@@ -30,6 +30,7 @@ final class PHUIListItemView extends AphrontTagView {
private $hideInApplicationMenu;
private $icons = array();
private $openInNewWindow = false;
+ private $tooltip;
public function setOpenInNewWindow($open_in_new_window) {
$this->openInNewWindow = $open_in_new_window;
@@ -176,6 +177,11 @@ final class PHUIListItemView extends AphrontTagView {
return $this->icons;
}
+ public function setTooltip($tooltip) {
+ $this->tooltip = $tooltip;
+ return $this;
+ }
+
protected function getTagName() {
return 'li';
}
@@ -185,7 +191,7 @@ final class PHUIListItemView extends AphrontTagView {
$classes[] = 'phui-list-item-view';
$classes[] = 'phui-list-item-'.$this->type;
- if ($this->icon) {
+ if ($this->icon || $this->profileImage) {
$classes[] = 'phui-list-item-has-icon';
}
@@ -230,6 +236,16 @@ final class PHUIListItemView extends AphrontTagView {
'align' => 'E',
);
} else {
+ if ($this->tooltip) {
+ Javelin::initBehavior('phabricator-tooltips');
+ $sigil = 'has-tooltip';
+ $meta = array(
+ 'tip' => $this->tooltip,
+ 'align' => 'E',
+ 'size' => 300,
+ );
+ }
+
$external = null;
if ($this->isExternal) {
$external = " \xE2\x86\x97";
diff --git a/webroot/rsrc/css/aphront/table-view.css b/webroot/rsrc/css/aphront/table-view.css
index 980118d31a..1d59435e3b 100644
--- a/webroot/rsrc/css/aphront/table-view.css
+++ b/webroot/rsrc/css/aphront/table-view.css
@@ -210,7 +210,9 @@ th.aphront-table-view-sortable-selected {
padding: 0px;
}
-
+.aphront-table-view td.icon + td.icon {
+ padding-left: 8px;
+}
div.single-display-line-bounds {
width: 100%;
diff --git a/webroot/rsrc/css/aphront/tooltip.css b/webroot/rsrc/css/aphront/tooltip.css
index f2c3151373..e6ff46cbed 100644
--- a/webroot/rsrc/css/aphront/tooltip.css
+++ b/webroot/rsrc/css/aphront/tooltip.css
@@ -7,15 +7,40 @@
padding: 5px;
}
+.jx-tooltip-appear {
+ animation: 0.5s tooltip-appear;
+
+ /* Without this, there's a nasty pop-in effect at the end of the animation
+ when Safari changes font smoothing. The text becomes visibly more bold
+ after the last frame of animation. */
+ -webkit-font-smoothing: subpixel-antialiased;
+}
+
+@keyframes tooltip-appear {
+ 0% {
+ opacity: 0;
+ }
+
+ 100% {
+ opacity: 1;
+ }
+}
+
+.jx-tooltip-hidden {
+ opacity: 0;
+}
+
.jx-tooltip-inner {
position: relative;
- background: rgba({$alphablack}, .9);
+ background: #000;
border-radius: 3px;
}
.jx-tooltip {
- color: #f9f9f9;
+ color: #fff;
font-size: {$normalfontsize};
+ -webkit-font-smoothing: antialiased;
+ font-weight: bold;
padding: 6px 8px;
overflow: hidden;
white-space: pre-wrap;
diff --git a/webroot/rsrc/css/application/base/main-menu-view.css b/webroot/rsrc/css/application/base/main-menu-view.css
index 2bafa7f8f5..0860909382 100644
--- a/webroot/rsrc/css/application/base/main-menu-view.css
+++ b/webroot/rsrc/css/application/base/main-menu-view.css
@@ -164,10 +164,7 @@
right: 0;
position: absolute;
font-size: {$normalfontsize};
- border-width: 1px;
- border-color: {$lightblueborder};
- border-radius: 3px;
- border-style: solid;
+ border: none;
background-color: #fff;
height: 28px;
padding: 3px 28px 3px 52px;
@@ -180,13 +177,13 @@
font-size: {$biggestfontsize};
width: 100%;
padding-left: 50px;
+ border: 1px solid {$lightblueborder};
}
.phabricator-main-menu .phabricator-main-menu-search input:focus {
background: #fff;
opacity: 1;
color: {$darkbluetext};
- border-color: {$sky};
box-shadow: none;
}
diff --git a/webroot/rsrc/css/application/base/phabricator-application-launch-view.css b/webroot/rsrc/css/application/base/phabricator-application-launch-view.css
deleted file mode 100644
index 714fa849e6..0000000000
--- a/webroot/rsrc/css/application/base/phabricator-application-launch-view.css
+++ /dev/null
@@ -1,103 +0,0 @@
-/**
- * @provides phabricator-application-launch-view-css
- */
-
-
-/* - Application List ----------------------------------------------------------
-
- Spacing container for the list of large application buttons.
-
-*/
-
-
-.application-tile-group {
- overflow: hidden;
-}
-
-
-/* - Application Launch Button -------------------------------------------------
-
- Spacing container for the list of large application buttons.
-
-*/
-
-a.phabricator-application-launch-container,
-div.phabricator-application-launch-container {
- display: block;
- float: left;
- overflow: hidden;
- position: relative;
- text-decoration: none;
- width: 100%;
- border-top-right-radius: 3px;
- border-bottom-right-radius: 3px;
- padding: 4px 0;
-}
-
-.device-phone div.phabricator-application-launch-container {
- display: none;
-}
-
-.phabricator-application-launch-icon {
- position: absolute;
- width: 38px;
- height: 18px;
- top: 6px;
- left: 0;
- font-size: 18px;
- text-align: center;
- vertical-align: bottom;
- color: {$darkbluetext};
- text-shadow: {$whitetextshadow};
-}
-
-.device-desktop a.phabricator-application-launch-container:hover {
- background-color: rgba({$alphablack},.07);
- text-decoration: none;
-}
-
-.device-desktop a.phabricator-application-launch-container:hover
- .phabricator-application-launch-icon {
- color: {$sky};
-}
-
-.phabricator-application-launch-name {
- display: block;
- font-weight: bold;
- color: {$darkbluetext};
- font-size: {$normalfontsize};
- margin-left: 36px;
-}
-
-.phabricator-application-launch-description {
- color: {$bluetext};
- font-size: {$smallestfontsize};
- margin-left: 36px;
- text-overflow: ellipsis;
- width: 150px;
- overflow: hidden;
- white-space: nowrap;
- display: inline-block;
- padding: 2px 0 0 0;
-}
-
-.phabricator-application-launch-attention {
- position: absolute;
- top: 8px;
- right: 8px;
- color: {$darkbluetext};
- font-weight: bold;
- font-size: {$smallerfontsize};
-}
-
-.phabricator-application-attention-count {
- color: {$fire};
-}
-
-a.phabricator-application-launch-phone-only {
- display: none;
-}
-
-.device-phone a.phabricator-application-launch-phone-only {
- display: block;
-}
diff --git a/webroot/rsrc/css/application/base/phui-theme.css b/webroot/rsrc/css/application/base/phui-theme.css
index 5313f2099b..d2a2c85ac7 100644
--- a/webroot/rsrc/css/application/base/phui-theme.css
+++ b/webroot/rsrc/css/application/base/phui-theme.css
@@ -5,7 +5,7 @@
/*--- Header Colors ----------------------------------------------------------*/
.phui-theme-blindigo .phabricator-main-menu-background {
- background: #41506e;
+ background: #4a5f88;
}
.phui-theme-dark .phabricator-main-menu-background {
diff --git a/webroot/rsrc/css/application/diffusion/diffusion-source.css b/webroot/rsrc/css/application/diffusion/diffusion-source.css
index 7f404e3b9b..1af068fe1a 100644
--- a/webroot/rsrc/css/application/diffusion/diffusion-source.css
+++ b/webroot/rsrc/css/application/diffusion/diffusion-source.css
@@ -26,6 +26,7 @@
padding-bottom: 1px;
padding-left: 8px;
width: 100%;
+ word-break: break-all;
}
.diffusion-browse-type-form {
diff --git a/webroot/rsrc/css/application/people/people-picture-menu-item.css b/webroot/rsrc/css/application/people/people-picture-menu-item.css
new file mode 100644
index 0000000000..4f3d36fa9d
--- /dev/null
+++ b/webroot/rsrc/css/application/people/people-picture-menu-item.css
@@ -0,0 +1,18 @@
+/**
+ * @provides people-picture-menu-item-css
+ */
+
+.people-menu-image {
+ width: 160px;
+ height: 160px;
+ border: 1px solid {$thinblueborder};
+}
+
+.people-menu-image-container {
+ background: #fff;
+ padding: 4px;
+ border-radius: 3px;
+ border: 1px solid {$lightblueborder};
+ margin: 4px 0px 16px 20px;
+ display: inline-block;
+}
diff --git a/webroot/rsrc/css/application/project/project-view.css b/webroot/rsrc/css/application/project/project-view.css
index 5b72e1c9eb..9ba6a9f7e5 100644
--- a/webroot/rsrc/css/application/project/project-view.css
+++ b/webroot/rsrc/css/application/project/project-view.css
@@ -87,3 +87,11 @@
.profile-no-badges {
padding: 24px 0;
}
+
+.people-profile-header.phui-profile-header .phui-header-col1 {
+ display: none;
+}
+
+.device .people-profile-header.phui-profile-header .phui-header-col1 {
+ display: table-cell;
+}
diff --git a/webroot/rsrc/css/application/search/application-search-view.css b/webroot/rsrc/css/application/search/application-search-view.css
index 1320df9c0f..aa1b5c4f97 100644
--- a/webroot/rsrc/css/application/search/application-search-view.css
+++ b/webroot/rsrc/css/application/search/application-search-view.css
@@ -6,6 +6,10 @@
background-color: #fff;
}
+.application-search-view .phui-crumbs-view {
+ background-color: #fff;
+}
+
.application-search-view .application-search-results.phui-object-box {
margin: 0;
padding: 0 16px 24px;
@@ -17,7 +21,7 @@
}
.application-search-view .application-search-results .phui-profile-header {
- padding: 16px 8px;
+ padding: 22px 8px;
border-bottom: 1px solid {$thinblueborder};
}
diff --git a/webroot/rsrc/css/font/font-awesome.css b/webroot/rsrc/css/font/font-awesome.css
index 4157229cb3..1579f70f22 100644
--- a/webroot/rsrc/css/font/font-awesome.css
+++ b/webroot/rsrc/css/font/font-awesome.css
@@ -3,15 +3,15 @@
*/
/*!
- * Font Awesome 4.6.0 by @davegandy - http://fontawesome.io - @fontawesome
+ * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome
* License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
*/
/* FONT PATH
* -------------------------- */
@font-face {
font-family: 'FontAwesome';
- src: url('/rsrc/externals/font/fontawesome/fontawesome-webfont.eot?v=4.6.0');
- src: url('/rsrc/externals/font/fontawesome/fontawesome-webfont.eot?#iefix&v=4.6.0') format('embedded-opentype'), url('/rsrc/externals/font/fontawesome/fontawesome-webfont.woff2?v=4.6.0') format('woff2'), url('/rsrc/externals/font/fontawesome/fontawesome-webfont.woff?v=4.6.0') format('woff'), url('/rsrc/externals/font/fontawesome/fontawesome-webfont.ttf?v=4.6.0') format('truetype'), url('/rsrc/externals/font/fontawesome/fontawesome-webfont.svg?v=4.6.0#fontawesomeregular') format('svg');
+ src: url('/rsrc/externals/font/fontawesome/fontawesome-webfont.eot?v=4.7.0');
+ src: url('/rsrc/externals/font/fontawesome/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'), url('/rsrc/externals/font/fontawesome/fontawesome-webfont.woff2?v=4.7.0') format('woff2'), url('/rsrc/externals/font/fontawesome/fontawesome-webfont.woff?v=4.7.0') format('woff'), url('/rsrc/externals/font/fontawesome/fontawesome-webfont.ttf?v=4.7.0') format('truetype'), url('/rsrc/externals/font/fontawesome/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');
font-weight: normal;
font-style: normal;
}
@@ -1246,7 +1246,7 @@
.fa-digg:before {
content: "\f1a6";
}
-.fa-pied-piper:before {
+.fa-pied-piper-pp:before {
content: "\f1a7";
}
.fa-pied-piper-alt:before {
@@ -1372,6 +1372,7 @@
content: "\f1ce";
}
.fa-ra:before,
+.fa-resistance:before,
.fa-rebel:before {
content: "\f1d0";
}
@@ -1694,6 +1695,7 @@
content: "\f23e";
}
.fa-battery-4:before,
+.fa-battery:before,
.fa-battery-full:before {
content: "\f240";
}
@@ -2020,6 +2022,163 @@
.fa-snapchat-square:before {
content: "\f2ad";
}
+.fa-pied-piper:before {
+ content: "\f2ae";
+}
+.fa-first-order:before {
+ content: "\f2b0";
+}
+.fa-yoast:before {
+ content: "\f2b1";
+}
+.fa-themeisle:before {
+ content: "\f2b2";
+}
+.fa-google-plus-circle:before,
+.fa-google-plus-official:before {
+ content: "\f2b3";
+}
+.fa-fa:before,
+.fa-font-awesome:before {
+ content: "\f2b4";
+}
+.fa-handshake-o:before {
+ content: "\f2b5";
+}
+.fa-envelope-open:before {
+ content: "\f2b6";
+}
+.fa-envelope-open-o:before {
+ content: "\f2b7";
+}
+.fa-linode:before {
+ content: "\f2b8";
+}
+.fa-address-book:before {
+ content: "\f2b9";
+}
+.fa-address-book-o:before {
+ content: "\f2ba";
+}
+.fa-vcard:before,
+.fa-address-card:before {
+ content: "\f2bb";
+}
+.fa-vcard-o:before,
+.fa-address-card-o:before {
+ content: "\f2bc";
+}
+.fa-user-circle:before {
+ content: "\f2bd";
+}
+.fa-user-circle-o:before {
+ content: "\f2be";
+}
+.fa-user-o:before {
+ content: "\f2c0";
+}
+.fa-id-badge:before {
+ content: "\f2c1";
+}
+.fa-drivers-license:before,
+.fa-id-card:before {
+ content: "\f2c2";
+}
+.fa-drivers-license-o:before,
+.fa-id-card-o:before {
+ content: "\f2c3";
+}
+.fa-quora:before {
+ content: "\f2c4";
+}
+.fa-free-code-camp:before {
+ content: "\f2c5";
+}
+.fa-telegram:before {
+ content: "\f2c6";
+}
+.fa-thermometer-4:before,
+.fa-thermometer:before,
+.fa-thermometer-full:before {
+ content: "\f2c7";
+}
+.fa-thermometer-3:before,
+.fa-thermometer-three-quarters:before {
+ content: "\f2c8";
+}
+.fa-thermometer-2:before,
+.fa-thermometer-half:before {
+ content: "\f2c9";
+}
+.fa-thermometer-1:before,
+.fa-thermometer-quarter:before {
+ content: "\f2ca";
+}
+.fa-thermometer-0:before,
+.fa-thermometer-empty:before {
+ content: "\f2cb";
+}
+.fa-shower:before {
+ content: "\f2cc";
+}
+.fa-bathtub:before,
+.fa-s15:before,
+.fa-bath:before {
+ content: "\f2cd";
+}
+.fa-podcast:before {
+ content: "\f2ce";
+}
+.fa-window-maximize:before {
+ content: "\f2d0";
+}
+.fa-window-minimize:before {
+ content: "\f2d1";
+}
+.fa-window-restore:before {
+ content: "\f2d2";
+}
+.fa-times-rectangle:before,
+.fa-window-close:before {
+ content: "\f2d3";
+}
+.fa-times-rectangle-o:before,
+.fa-window-close-o:before {
+ content: "\f2d4";
+}
+.fa-bandcamp:before {
+ content: "\f2d5";
+}
+.fa-grav:before {
+ content: "\f2d6";
+}
+.fa-etsy:before {
+ content: "\f2d7";
+}
+.fa-imdb:before {
+ content: "\f2d8";
+}
+.fa-ravelry:before {
+ content: "\f2d9";
+}
+.fa-eercast:before {
+ content: "\f2da";
+}
+.fa-microchip:before {
+ content: "\f2db";
+}
+.fa-snowflake-o:before {
+ content: "\f2dc";
+}
+.fa-superpowers:before {
+ content: "\f2dd";
+}
+.fa-wpexplorer:before {
+ content: "\f2de";
+}
+.fa-meetup:before {
+ content: "\f2e0";
+}
.sr-only {
position: absolute;
width: 1px;
diff --git a/webroot/rsrc/css/phui/calendar/phui-calendar-list.css b/webroot/rsrc/css/phui/calendar/phui-calendar-list.css
index d5a4a89dc9..f7d5e24683 100644
--- a/webroot/rsrc/css/phui/calendar/phui-calendar-list.css
+++ b/webroot/rsrc/css/phui/calendar/phui-calendar-list.css
@@ -66,7 +66,7 @@
.phui-calendar-list-item-empty {
color: {$lightgreytext};
- padding: 0 12px;
+ padding: 0;
font-style: italic;
}
diff --git a/webroot/rsrc/css/phui/phui-basic-nav-view.css b/webroot/rsrc/css/phui/phui-basic-nav-view.css
index 30647e3ece..12605cc552 100644
--- a/webroot/rsrc/css/phui/phui-basic-nav-view.css
+++ b/webroot/rsrc/css/phui/phui-basic-nav-view.css
@@ -19,26 +19,33 @@
display: table-cell;
position: relative;
vertical-align: top;
- width: {$menu.profile.width};
- max-width: {$menu.profile.width};
margin-top: 0;
overflow: hidden;
}
-.phui-basic-nav.phui-navigation-shell .phabricator-nav-local {
- width: 205px;
- padding-top: 4px;
- padding-right: 8px;
+.phabricator-home.device-phone .phabricator-nav-content {
+ display: none;
}
-.phui-basic-nav .phabricator-side-menu {
- background-color: {$page.sidenav};
+.phabricator-home .phui-basic-nav .phabricator-side-menu {
+ background: transparent;
+}
+
+.device-phone.phabricator-home .phui-basic-nav .phabricator-side-menu
+ .phui-list-item-selected {
+ background-color: transparent;
+ border-left-color: transparent;
+ font-weight: normal;
+}
+
+.phui-basic-nav.phui-navigation-shell .phabricator-nav-local {
+ width: 205px;
+ padding-top: 12px;
+ padding-right: 8px;
}
.phui-two-column-view .phui-basic-nav.phui-navigation-shell
.phabricator-nav-local {
- width: {$menu.profile.width};
- max-width: {$menu.profile.width};
padding-right: 0;
padding-top: 0;
}
@@ -55,13 +62,12 @@
display: block;
white-space: nowrap;
text-decoration: none;
- font-size: 13px;
-webkit-font-smoothing: antialiased;
}
.phui-basic-nav .phabricator-side-menu .phui-list-item-href {
display: block;
- padding: 6px 8px 6px 24px;
+ padding: 6px 8px 6px 20px;
color: {$darkbluetext};
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
@@ -69,10 +75,30 @@
text-overflow: ellipsis
}
+.phui-basic-nav .phabricator-side-menu .phui-list-item-has-icon
+ .phui-list-item-href {
+ padding-left: 12px;
+ }
+
.phui-basic-nav .phabricator-side-menu .phui-list-item-icon {
- margin-left: -12px;
+ margin-left: -4px;
text-align: center;
- width: 24px;
+ width: 30px;
+}
+
+.phui-basic-nav .phabricator-side-menu .phui-divider {
+ border-bottom: 1px solid rgba({$alphablack},.08);
+ margin: 0 0 8px 8px;
+ padding: 8px 0 0 0;
+}
+
+.phui-basic-nav .phabricator-side-menu .phui-list-item-icon.phuihead-small {
+ display: inline-block;
+ height: 16px;
+ width: 16px;
+ border-radius: 3px;
+ background-size: 100%;
+ margin: -2px 7px -2px 3px;
}
.phui-basic-nav .phabricator-side-menu .phui-list-item-selected {
@@ -91,7 +117,7 @@
.phui-basic-nav .phabricator-side-menu .phui-list-item-selected
.phui-list-item-href {
- padding-left: 20px;
+ margin-left: -4px;
}
.phui-basic-nav .phabricator-side-menu .phui-list-item-type-label {
@@ -108,3 +134,12 @@
text-decoration: none;
background-color: rgba({$alphablack},.07);
}
+
+.phui-basic-nav .phabricator-side-menu .phui-list-item-type-link +
+ .phui-list-item-type-label {
+ margin-top: 12px;
+}
+
+.phui-basic-nav .phui-profile-segment-bar {
+ padding: 4px 4px 8px 12px;
+}
diff --git a/webroot/rsrc/css/phui/phui-box.css b/webroot/rsrc/css/phui/phui-box.css
index ef0816f826..9dc99c6f8c 100644
--- a/webroot/rsrc/css/phui/phui-box.css
+++ b/webroot/rsrc/css/phui/phui-box.css
@@ -49,10 +49,11 @@
.phui-object-box.phui-box-blue div.phui-info-severity-nodata,
.phui-object-box.phui-box-grey div.phui-info-severity-nodata {
- background: transparent;
- padding: 20px 4px 24px;
+ background: #fff;
+ padding: 32px 0;
text-align: center;
border: none;
+ margin: 0;
color: {$greytext};
}
diff --git a/webroot/rsrc/css/phui/phui-button.css b/webroot/rsrc/css/phui/phui-button.css
index 5d35688c1f..d801cdb513 100644
--- a/webroot/rsrc/css/phui/phui-button.css
+++ b/webroot/rsrc/css/phui/phui-button.css
@@ -47,6 +47,18 @@ input[type="submit"] {
border-radius: 3px;
}
+button .phui-icon-view,
+a.button .phui-icon-view,
+button.green .phui-icon-view,
+a.button.green .phui-icon-view {
+ color: white;
+}
+
+button.grey .phui-icon-view,
+a.button.grey .phui-icon-view {
+ color: {$darkbluetext};
+}
+
/* Buttons with images (full size only) */
button.icon,
a.icon,
diff --git a/webroot/rsrc/css/phui/phui-profile-menu.css b/webroot/rsrc/css/phui/phui-profile-menu.css
deleted file mode 100644
index 6f8934dd39..0000000000
--- a/webroot/rsrc/css/phui/phui-profile-menu.css
+++ /dev/null
@@ -1,162 +0,0 @@
-/**
- * @provides phui-profile-menu-css
- */
-
-.device-desktop .phui-profile-menu .phabricator-nav-local {
- width: {$menu.profile.width.collapsed};
- max-width: {$menu.profile.width.collapsed};
-}
-
-.device-desktop .phui-profile-menu .phabricator-nav-content {
- display: table-cell;
- margin-left: 0;
-}
-
-.phui-profile-menu .phui-basic-nav {
- width: 205px;
-}
-
-.phui-profile-menu .phabricator-side-menu {
- background: #dee0e7;
- width: 240px;
-}
-
-.phabricator-side-menu .phui-profile-menu-footer-1 {
- background: #dee0e7;
-}
-
-.phui-profile-menu .phabricator-side-menu .phui-list-item-view {
- position: relative;
-}
-
-.phui-profile-menu .phabricator-side-menu .phui-list-item-href {
- display: block;
- text-decoration: none;
- padding: 0 8px 0 48px;
- height: 48px;
- font-size: {$biggerfontsize};
- -webkit-font-smoothing: antialiased;
- line-height: 22px;
- overflow: hidden;
- color: {$darkbluetext};
- text-overflow: ellipsis;
- line-height: 48px;
-}
-
-.phui-profile-menu .phabricator-side-menu .phui-list-item-icon,
-.phui-profile-menu .phabricator-side-menu
- .phui-list-item-href .phui-list-item-icon {
- position: absolute;
- top: 12px;
- left: 13px;
- font-size: 24px;
- width: 24px;
- height: 24px;
- line-height: 24px;
- text-align: center;
- color: {$darkbluetext};
- background-size: 100%;
-}
-
-.phui-profile-menu .phabricator-side-menu .phui-list-item-href {
- text-align: center;
- padding: 42px 4px 14px;
- line-height: 14px;
- height: auto;
- font-size: {$smallerfontsize};
-}
-
-.phui-profile-menu .phabricator-side-menu .phui-list-item-name {
- display: block;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-.phui-profile-menu .phabricator-side-menu .phui-list-item-icon,
-.phui-profile-menu .phabricator-side-menu .phui-list-item-href
- .phui-list-item-icon {
- top: 14px;
- left: 32px;
-}
-
-.phui-profile-menu .phabricator-side-menu
- .phui-list-item-disabled
- .phui-list-item-icon {
- color: {$lightgreytext};
-}
-
-.phui-profile-menu .phabricator-side-menu .phui-icon-view {
- border-radius: 3px;
-}
-
-.device-desktop .phui-profile-menu .phabricator-side-menu
- .phui-list-item-href:hover {
- background-color: rgba({$alphablack},0.05);
-}
-
-.phui-profile-menu .phabricator-side-menu .phui-list-item-selected
- .phui-list-item-href {
- background-color: rgba({$alphablack},0.1);
-}
-
-.phui-profile-menu .phabricator-side-menu .phui-list-item-selected
- .phui-list-item-href:hover {
- background-color: rgba({$alphablack},0.15);
-}
-
-.phui-profile-menu .phabricator-side-menu .phui-divider {
- margin: 4px 0;
- border-bottom: 1px solid rgba({$alphablack}, 0.2);
-}
-
-.phui-profile-menu .phabricator-side-menu .phui-motivator {
- white-space: normal;
- padding: 18px 15px;
- font-size: 12px;
- color: {$darkbluetext};
-}
-
-.phui-profile-menu .phabricator-side-menu .phui-motivator .phui-icon-view {
- position: static;
- font-size: 12px;
- color: {$darkbluetext};
-}
-
-.phui-profile-menu .phabricator-side-menu .phui-profile-menu-error {
- color: rgba({$alphawhite}, 0.5);
- font-size: {$smallerfontsize};
- padding: 16px;
-}
-
-.phui-profile-menu .phabricator-side-menu .phui-profile-menu-error {
- padding: 16px 8px;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-.phui-profile-menu .phabricator-side-menu .phui-list-item-disabled
- .phui-list-item-href,
-.phui-profile-menu .phui-list-sidenav .phui-list-item-disabled
- .phui-list-item-href {
- color: rgba({$lightgreytext});
-}
-
-.phui-profile-menu .phabricator-side-menu .phui-profile-segment-bar {
- color: {$darkbluetext};
- font-size: {$smallerfontsize};
- -webkit-font-smoothing: antialiased;
- padding: 8px 12px 16px;
-}
-
-.phui-profile-menu .phabricator-side-menu .phui-profile-segment-bar {
- padding: 8px 8px 16px;
-}
-
-.phui-profile-menu .phabricator-side-menu .phui-profile-menu-spacer {
- box-sizing: border-box;
- height: {$menu.profile.item.height};
-}
-
-!print .phui-profile-menu .phabricator-side-menu {
- display: none;
-}
diff --git a/webroot/rsrc/css/phui/phui-property-list-view.css b/webroot/rsrc/css/phui/phui-property-list-view.css
index 769a139664..16a78e219c 100644
--- a/webroot/rsrc/css/phui/phui-property-list-view.css
+++ b/webroot/rsrc/css/phui/phui-property-list-view.css
@@ -61,7 +61,7 @@
}
.device .phui-property-list-key,
-.phui-property-list-stacked .phui-property-list-properties
+div.phui-property-list-stacked .phui-property-list-properties
.phui-property-list-key {
padding-left: 4px;
text-align: left;
diff --git a/webroot/rsrc/css/phui/phui-segment-bar-view.css b/webroot/rsrc/css/phui/phui-segment-bar-view.css
index 92665165ba..b3ce7677e3 100644
--- a/webroot/rsrc/css/phui/phui-segment-bar-view.css
+++ b/webroot/rsrc/css/phui/phui-segment-bar-view.css
@@ -7,14 +7,14 @@
margin-bottom: 4px;
}
-.phui-profile-menu-collapsed .phui-segment-bar-label {
- width: 74px;
+.phui-basic-nav .phui-segment-bar-label {
+ width: 180px;
overflow: hidden;
text-overflow: ellipsis;
}
.phui-segment-bar-segments {
- background: {$lightgreybackground};
+ background: rgba({$alphablue}, .1);
border-radius: 4px;
position: relative;
overflow: hidden;
diff --git a/webroot/rsrc/css/phui/phui-two-column-view.css b/webroot/rsrc/css/phui/phui-two-column-view.css
index 80e51d95da..5418644b28 100644
--- a/webroot/rsrc/css/phui/phui-two-column-view.css
+++ b/webroot/rsrc/css/phui/phui-two-column-view.css
@@ -121,7 +121,7 @@
.device-desktop .phui-main-column .phui-property-list-key {
margin-left: 0;
- width: 160px;
+ width: 180px;
}
.device-desktop .phui-main-column .phui-property-list-value {
diff --git a/webroot/rsrc/css/phui/workboards/phui-workboard-color.css b/webroot/rsrc/css/phui/workboards/phui-workboard-color.css
index 13c9847af9..b16184e34b 100644
--- a/webroot/rsrc/css/phui/workboards/phui-workboard-color.css
+++ b/webroot/rsrc/css/phui/workboards/phui-workboard-color.css
@@ -60,6 +60,10 @@ body.phui-workboard-color .phabricator-side-menu .phui-profile-menu-footer-1 {
color: rgba({$alphawhite},.8);
}
+.phui-workboard-color .phui-segment-bar-label {
+ color: rgba({$alphawhite},.8);
+}
+
/* Gradients */
.phui-workboard-gradient-red {
diff --git a/webroot/rsrc/css/phui/workboards/phui-workboard.css b/webroot/rsrc/css/phui/workboards/phui-workboard.css
index 43fd3a85e4..10450ad836 100644
--- a/webroot/rsrc/css/phui/workboards/phui-workboard.css
+++ b/webroot/rsrc/css/phui/workboards/phui-workboard.css
@@ -32,7 +32,7 @@
}
.device-desktop .project-board-wrapper .phui-workboard-view-shadow {
- left: {$menu.profile.width.collapsed};
+ left: 212px;
}
!print .project-board-wrapper .phui-workboard-view-shadow {
diff --git a/webroot/rsrc/externals/font/fontawesome/fontawesome-webfont.eot b/webroot/rsrc/externals/font/fontawesome/fontawesome-webfont.eot
index b15a0f5131..e9f60ca953 100644
Binary files a/webroot/rsrc/externals/font/fontawesome/fontawesome-webfont.eot and b/webroot/rsrc/externals/font/fontawesome/fontawesome-webfont.eot differ
diff --git a/webroot/rsrc/externals/font/fontawesome/fontawesome-webfont.ttf b/webroot/rsrc/externals/font/fontawesome/fontawesome-webfont.ttf
index cec09e000a..35acda2fa1 100644
Binary files a/webroot/rsrc/externals/font/fontawesome/fontawesome-webfont.ttf and b/webroot/rsrc/externals/font/fontawesome/fontawesome-webfont.ttf differ
diff --git a/webroot/rsrc/externals/font/fontawesome/fontawesome-webfont.woff b/webroot/rsrc/externals/font/fontawesome/fontawesome-webfont.woff
index 52d8116589..400014a4b0 100644
Binary files a/webroot/rsrc/externals/font/fontawesome/fontawesome-webfont.woff and b/webroot/rsrc/externals/font/fontawesome/fontawesome-webfont.woff differ
diff --git a/webroot/rsrc/externals/font/fontawesome/fontawesome-webfont.woff2 b/webroot/rsrc/externals/font/fontawesome/fontawesome-webfont.woff2
index 65aeb01eb7..4d13fc6040 100644
Binary files a/webroot/rsrc/externals/font/fontawesome/fontawesome-webfont.woff2 and b/webroot/rsrc/externals/font/fontawesome/fontawesome-webfont.woff2 differ
diff --git a/webroot/rsrc/js/core/ToolTip.js b/webroot/rsrc/js/core/ToolTip.js
index 0d09a31eba..6fef44ab55 100644
--- a/webroot/rsrc/js/core/ToolTip.js
+++ b/webroot/rsrc/js/core/ToolTip.js
@@ -11,6 +11,7 @@ JX.install('Tooltip', {
statics: {
_node: null,
+ _last: null,
_lock: 0,
show : function(root, scale, align, content) {
@@ -61,6 +62,28 @@ JX.install('Tooltip', {
// Jump through some hoops trying to auto-position the tooltip
var pos = self._getSmartPosition(align, root, node);
pos.setPos(node);
+
+ // Animate the tip if we haven't shown any tips recently. If we are
+ // already showing a tip (or hid one very recently) just show the tip
+ // immediately. This makes hunting for a particular item by mousing
+ // through tips smoother: you only have to sit through the animation
+ // once, at the beginning.
+
+ var is_recent = false;
+
+ var last_tip = self._last;
+ if (last_tip) {
+ // If we recently hid a tip, compute how many milliseconds ago we
+ // hid it.
+ var last_tip_age = (new Date().getTime() - self._last);
+ if (last_tip_age < 500) {
+ is_recent = true;
+ }
+ }
+
+ if (!is_recent) {
+ JX.DOM.alterClass(node, 'jx-tooltip-appear', true);
+ }
},
_getSmartPosition: function (align, root, node) {
@@ -193,6 +216,7 @@ JX.install('Tooltip', {
if (this._node) {
JX.DOM.remove(this._node);
this._node = null;
+ this._last = new Date().getTime();
}
},
diff --git a/webroot/rsrc/js/core/behavior-tooltip.js b/webroot/rsrc/js/core/behavior-tooltip.js
index 8b9b8d4f23..336a5b28d0 100644
--- a/webroot/rsrc/js/core/behavior-tooltip.js
+++ b/webroot/rsrc/js/core/behavior-tooltip.js
@@ -26,7 +26,7 @@ JX.behavior('phabricator-tooltips', function() {
JX.Tooltip.show(
e.getNode('has-tooltip'),
- data.size || 120,
+ data.size || 160,
data.align || 'N',
data.tip);
});