1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-02 19:52:44 +01:00

(stable) Promote 2017 Week 5

This commit is contained in:
epriestley 2017-02-03 18:39:39 -08:00
commit 43fae71e17
113 changed files with 2419 additions and 1917 deletions

View file

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

View file

@ -1,277 +0,0 @@
<?php
/*
* This is a PHP library that handles calling reCAPTCHA.
* - Documentation and latest version
* http://recaptcha.net/plugins/php/
* - Get a reCAPTCHA API Key
* https://www.google.com/recaptcha/admin/create
* - Discussion group
* http://groups.google.com/group/recaptcha
*
* 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.
*/
/**
* The reCAPTCHA server URL's
*/
define("RECAPTCHA_API_SERVER", "http://www.google.com/recaptcha/api");
define("RECAPTCHA_API_SECURE_SERVER", "https://www.google.com/recaptcha/api");
define("RECAPTCHA_VERIFY_SERVER", "www.google.com");
/**
* Encodes the given data into a query string format
* @param $data - array of string elements to be encoded
* @return string - encoded request
*/
function _recaptcha_qsencode ($data) {
$req = "";
foreach ( $data as $key => $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 <a href='https://www.google.com/recaptcha/admin/create'>https://www.google.com/recaptcha/admin/create</a>");
}
if ($use_ssl) {
$server = RECAPTCHA_API_SECURE_SERVER;
} else {
$server = RECAPTCHA_API_SERVER;
}
$errorpart = "";
if ($error) {
$errorpart = "&amp;error=" . $error;
}
return '<script type="text/javascript" src="'. $server . '/challenge?k=' . $pubkey . $errorpart . '"></script>
<noscript>
<iframe src="'. $server . '/noscript?k=' . $pubkey . $errorpart . '" height="300" width="500" frameborder="0"></iframe><br/>
<textarea name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
<input type="hidden" name="recaptcha_response_field" value="manual_challenge"/>
</noscript>';
}
/**
* 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 <a href='https://www.google.com/recaptcha/admin/create'>https://www.google.com/recaptcha/admin/create</a>");
}
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 <a href='http://www.google.com/recaptcha/mailhide/apikey'>http://www.google.com/recaptcha/mailhide/apikey</a>");
}
$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]) . "<a href='" . htmlentities ($url) .
"' onclick=\"window.open('" . htmlentities ($url) . "', '', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=500,height=300'); return false;\" title=\"Reveal this e-mail address\">...</a>@" . htmlentities ($emailparts [1]);
}
?>

View file

@ -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',

View file

@ -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',

View file

@ -0,0 +1,45 @@
<?php
$table = new PhabricatorDashboard();
$conn = $table->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);
}

View file

@ -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',

View file

@ -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(),
);
}

View file

@ -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'));
}
}

View file

@ -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) {

View file

@ -1,4 +1,90 @@
<?php
abstract class PhabricatorAuditManagementWorkflow
extends PhabricatorManagementWorkflow {}
extends PhabricatorManagementWorkflow {
protected function getCommitConstraintArguments() {
return array(
array(
'name' => '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;
}
}

View file

@ -0,0 +1,60 @@
<?php
final class PhabricatorAuditSynchronizeManagementWorkflow
extends PhabricatorAuditManagementWorkflow {
protected function didConstruct() {
$this
->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();
}
}
}
}
}

View file

@ -0,0 +1,55 @@
<?php
final class PhabricatorAuditUpdateOwnersManagementWorkflow
extends PhabricatorAuditManagementWorkflow {
protected function didConstruct() {
$this
->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'));
}
}
}
}

View file

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

View file

@ -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);

View file

@ -596,14 +596,18 @@ abstract class PhabricatorApplication
protected function getProfileMenuRouting($controller) {
$edit_route = $this->getEditRoutePattern();
$mode_route = '(?P<itemEditMode>global|custom)/';
return array(
'(?P<itemAction>view)/(?P<itemID>[^/]+)/' => $controller,
'(?P<itemAction>hide)/(?P<itemID>[^/]+)/' => $controller,
'(?P<itemAction>default)/(?P<itemID>[^/]+)/' => $controller,
'(?P<itemAction>configure)/' => $controller,
'(?P<itemAction>reorder)/' => $controller,
'(?P<itemAction>configure)/'.$mode_route => $controller,
'(?P<itemAction>reorder)/'.$mode_route => $controller,
'(?P<itemAction>edit)/'.$edit_route => $controller,
'(?P<itemAction>new)/(?<itemKey>[^/]+)/'.$edit_route => $controller,
'(?P<itemAction>new)/'.$mode_route.'(?<itemKey>[^/]+)/'.$edit_route
=> $controller,
'(?P<itemAction>builtin)/(?<itemID>[^/]+)/'.$edit_route
=> $controller,
);

View file

@ -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',
);
}

View file

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

View file

@ -30,7 +30,6 @@ final class PhabricatorDashboardApplication extends PhabricatorApplication {
'create/' => 'PhabricatorDashboardEditController',
'copy/(?:(?P<id>\d+)/)?' => 'PhabricatorDashboardCopyController',
'edit/(?:(?P<id>\d+)/)?' => 'PhabricatorDashboardEditController',
'install/(?P<id>\d+)/' => 'PhabricatorDashboardInstallController',
'uninstall/(?P<id>\d+)/' => 'PhabricatorDashboardUninstallController',
'addpanel/(?P<id>\d+)/' => 'PhabricatorDashboardAddPanelController',
'movepanel/(?P<id>\d+)/' => 'PhabricatorDashboardMovePanelController',

View file

@ -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());

View file

@ -1,148 +0,0 @@
<?php
final class PhabricatorDashboardInstallController
extends PhabricatorDashboardController {
private $id;
public function handleRequest(AphrontRequest $request) {
$viewer = $request->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;
}
}

View file

@ -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;
}

View file

@ -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().'/'));

View file

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

View file

@ -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();

View file

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

View file

@ -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,
));
}

View file

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

View file

@ -41,7 +41,7 @@ final class DifferentialRevisionCommandeerTransaction
}
public function getCommandSummary() {
return pht('Commadeer a revision.');
return pht('Commandeer a revision.');
}
public function generateOldValue($object) {

View file

@ -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);

View file

@ -0,0 +1,8 @@
<?php
final class DiffusionCommitHasPackageEdgeType
extends PhabricatorEdgeType {
const EDGECONST = 65;
}

View file

@ -39,19 +39,12 @@ abstract class DiffusionAuditorsHeraldAction
$phids = array_fuse(array_keys($targets));
// TODO: Convert this to be translatable, structured data eventually.
$reason_map = array();
foreach ($phids as $phid) {
$reason_map[$phid][] = pht('%s Triggered Audit', $rule->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);

View file

@ -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();
}

View file

@ -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;
}

View file

@ -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();

View file

@ -21,8 +21,7 @@ final class PhabricatorFavoritesApplication extends PhabricatorApplication {
public function getRoutes() {
return array(
'/favorites/' => array(
'' => 'PhabricatorFavoritesMainController',
'(?P<type>global|personal)/item/' => $this->getProfileMenuRouting(
'menu/' => $this->getProfileMenuRouting(
'PhabricatorFavoritesMenuItemController'),
),
);

View file

@ -1,63 +0,0 @@
<?php
final class PhabricatorFavoritesMainController
extends PhabricatorFavoritesController {
public function shouldAllowPublic() {
return false;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->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);
}
}

View file

@ -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();

View file

@ -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) {

View file

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

View file

@ -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)"))

View file

@ -0,0 +1,46 @@
<?php
final class PhabricatorIconDatasource extends PhabricatorTypeaheadDatasource {
public function getPlaceholderText() {
return pht('Type an icon name...');
}
public function getBrowseTitle() {
return pht('Browse Icons');
}
public function getDatasourceApplicationClass() {
return 'PhabricatorFilesApplication';
}
public function loadResults() {
$results = $this->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;
}
}

View file

@ -94,6 +94,7 @@ final class PhabricatorHarbormasterApplication extends PhabricatorApplication {
),
'hook/' => array(
'circleci/' => 'HarbormasterCircleCIHookController',
'buildkite/' => 'HarbormasterBuildkiteHookController',
),
),
);

View file

@ -0,0 +1,111 @@
<?php
final class HarbormasterBuildkiteHookController
extends HarbormasterController {
public function shouldRequireLogin() {
return false;
}
/**
* @phutil-external-symbol class PhabricatorStartup
*/
public function handleRequest(AphrontRequest $request) {
$raw_body = PhabricatorStartup::getRawInput();
$body = phutil_json_decode($raw_body);
$event = idx($body, 'event');
if ($event != 'build.finished') {
return $this->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;
}
}

View file

@ -0,0 +1,11 @@
<?php
/**
* Support for Buildkite.
*/
interface HarbormasterBuildkiteBuildableInterface {
public function getBuildkiteBranch();
public function getBuildkiteCommit();
}

View file

@ -0,0 +1,210 @@
<?php
final class HarbormasterBuildkiteBuildStepImplementation
extends HarbormasterBuildStepImplementation {
public function getName() {
return pht('Build with Buildkite');
}
public function getGenericDescription() {
return pht('Trigger a build in Buildkite.');
}
public function getBuildStepGroupKey() {
return HarbormasterExternalBuildStepGroup::GROUPKEY;
}
public function getDescription() {
return pht('Run a build in Buildkite.');
}
public function getEditInstructions() {
$hook_uri = '/harbormaster/hook/buildkite/';
$hook_uri = PhabricatorEnv::getProductionURI($hook_uri);
return pht(<<<EOTEXT
WARNING: This build step is new and experimental!
To build **revisions** with Buildkite, they must:
- belong to a tracked repository;
- the repository must have a Staging Area configured;
- you must configure a Buildkite pipeline for that Staging Area; and
- you must configure the webhook described below.
To build **commits** with Buildkite, they must:
- belong to a tracked repository;
- you must configure a Buildkite pipeline for that repository; and
- you must configure the webhook described below.
Webhook Configuration
=====================
In {nav Settings} for your Organization in Buildkite, under
{nav Notification Services}, add a new **Webook Notification**.
Use these settings:
- **Webhook URL**: %s
- **Token**: The "Webhook Token" field below and the "Token" field in
Buildkite should both be set to the same nonempty value (any random
secret). You can use copy/paste the value Buildkite generates into
this form.
- **Events**: Only **build.finish** needs to be active.
Environment
===========
These variables will be available in the build environment:
| Variable | Description |
|----------|-------------|
| `HARBORMASTER_BUILD_TARGET_PHID` | PHID of the Build Target.
EOTEXT
,
$hook_uri);
}
public function execute(
HarbormasterBuild $build,
HarbormasterBuildTarget $build_target) {
$viewer = PhabricatorUser::getOmnipotentUser();
$buildable = $build->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;
}
}

View file

@ -22,14 +22,16 @@ final class PhabricatorHomeApplication extends PhabricatorApplication {
public function getRoutes() {
return array(
'/' => 'PhabricatorHomeMainController',
'/(?P<only>home)/' => 'PhabricatorHomeMainController',
'/home/' => array(
'menu/' => array(
'' => 'PhabricatorHomeMenuController',
'(?P<type>global|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.
'/(?P<content>home)/' => array(
'' => 'PhabricatorHomeMenuItemController',
'menu/' => $this->getProfileMenuRouting(
'PhabricatorHomeMenuItemController'),
),
),
);
}

View file

@ -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';
}

View file

@ -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;
}
}

View file

@ -1,57 +0,0 @@
<?php
final class PhabricatorHomeMenuController extends PhabricatorHomeController {
public function shouldAllowPublic() {
return false;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->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);
}
}

View file

@ -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();
}

View file

@ -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;
}

View file

@ -0,0 +1,66 @@
<?php
final class PhabricatorHomeLauncherProfileMenuItem
extends PhabricatorProfileMenuItem {
const MENUITEMKEY = 'home.launcher.menu';
public function getMenuItemTypeName() {
return pht('More Applications');
}
private function getDefaultName() {
return pht('More Applications');
}
public function canHideMenuItem(
PhabricatorProfileMenuItemConfiguration $config) {
return false;
}
public function canMakeDefault(
PhabricatorProfileMenuItemConfiguration $config) {
return false;
}
public function getDisplayName(
PhabricatorProfileMenuItemConfiguration $config) {
$name = $config->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,
);
}
}

View file

@ -0,0 +1,69 @@
<?php
final class PhabricatorHomeProfileMenuItem
extends PhabricatorProfileMenuItem {
const MENUITEMKEY = 'home.dashboard';
public function getMenuItemTypeName() {
return pht('Built-in Homepage');
}
private function getDefaultName() {
return pht('Home');
}
public function canMakeDefault(
PhabricatorProfileMenuItemConfiguration $config) {
return true;
}
public function getDisplayName(
PhabricatorProfileMenuItemConfiguration $config) {
$name = $config->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,
);
}
}

View file

@ -1,57 +1,17 @@
<?php
final class PhabricatorHomeMainController extends PhabricatorHomeController {
final class PHUIHomeView
extends AphrontTagView {
public function shouldAllowPublic() {
return true;
protected function getTagName() {
return null;
}
public function isGlobalDragAndDropUploadEnabled() {
return true;
protected function getTagAttributes() {
return array();
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->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();

View file

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

View file

@ -1,69 +0,0 @@
<?php
final class PhabricatorApplicationLaunchView extends AphrontTagView {
private $application;
public function setApplication(PhabricatorApplication $application) {
$this->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,
);
}
}

View file

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

View file

@ -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;
}
}

View file

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

View file

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

View file

@ -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);

View file

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

View file

@ -0,0 +1,76 @@
<?php
final class PhabricatorPeoplePictureProfileMenuItem
extends PhabricatorProfileMenuItem {
const MENUITEMKEY = 'people.picture';
public function getMenuItemTypeName() {
return pht('User Picture');
}
private function getDefaultName() {
return pht('User Picture');
}
public function canHideMenuItem(
PhabricatorProfileMenuItemConfiguration $config) {
return false;
}
public function getDisplayName(
PhabricatorProfileMenuItemConfiguration $config) {
return $this->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,
);
}
}

View file

@ -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) {

View file

@ -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) {

View file

@ -7,6 +7,10 @@ final class PhabricatorProjectProfileMenuEngine
return true;
}
protected function isMenuEnginePersonalizable() {
return false;
}
public function getItemURI($path) {
$project = $this->getProfileObject();
$id = $project->getID();

View file

@ -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,
);
}
}

View file

@ -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 )------------------------------------ */

View file

@ -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;
}
}

View file

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

View file

@ -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());
}

View file

@ -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,
);

View file

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

View file

@ -38,6 +38,7 @@ final class PhabricatorDividerProfileMenuItem
PhabricatorProfileMenuItemConfiguration $config) {
$item = $this->newItem()
->setType(PHUIListItemView::TYPE_DIVIDER)
->addClass('phui-divider');
return array(

View file

@ -0,0 +1,75 @@
<?php
final class PhabricatorLabelProfileMenuItem
extends PhabricatorProfileMenuItem {
const MENUITEMKEY = 'label';
const FIELD_NAME = 'name';
public function getMenuItemTypeIcon() {
return 'fa-map-signs';
}
public function getMenuItemTypeName() {
return pht('Label');
}
public function canAddToObject($object) {
return true;
}
public function getDisplayName(
PhabricatorProfileMenuItemConfiguration $config) {
return $this->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;
}
}

View file

@ -1,16 +1,16 @@
<?php
final class PhabricatorHomeManageProfileMenuItem
final class PhabricatorManageProfileMenuItem
extends PhabricatorProfileMenuItem {
const MENUITEMKEY = 'home.manage.menu';
const MENUITEMKEY = 'menu.manage';
public function getMenuItemTypeName() {
return pht('Manage Home Menu');
return pht('Manage Menu');
}
private function getDefaultName() {
return pht('Manage');
return pht('Edit Menu');
}
public function canHideMenuItem(
@ -49,21 +49,21 @@ final class PhabricatorHomeManageProfileMenuItem
PhabricatorProfileMenuItemConfiguration $config) {
$viewer = $this->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,
);

View file

@ -117,6 +117,7 @@ final class PhabricatorProfileMenuItemConfigurationQuery
unset($page[$key]);
continue;
}
$item->attachProfileObject($profile);
}

View file

@ -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);
}

View file

@ -1,223 +0,0 @@
<?php
final class PhabricatorHomePreferencesSettingsPanel
extends PhabricatorSettingsPanel {
public function getPanelKey() {
return 'home';
}
public function getPanelName() {
return pht('Home Page');
}
public function getPanelGroupKey() {
return PhabricatorSettingsApplicationsPanelGroup::PANELGROUPKEY;
}
public function isTemplatePanel() {
return true;
}
public function processRequest(AphrontRequest $request) {
$viewer = $this->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);
}
}

View file

@ -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 <command>
```
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
==========

View file

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

View file

@ -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.',
),
),
);
}

View file

@ -1,10 +1,5 @@
<?php
/**
*
* @phutil-external-symbol function recaptcha_get_html
* @phutil-external-symbol function recaptcha_check_answer
*/
final class AphrontFormRecaptchaControl extends AphrontFormControl {
protected function getCustomControlClass() {
@ -19,13 +14,8 @@ final class AphrontFormRecaptchaControl extends AphrontFormControl {
return PhabricatorEnv::getEnvConfig('recaptcha.enabled');
}
private static function requireLib() {
$root = phutil_get_library_root('phabricator');
require_once dirname($root).'/externals/recaptcha/recaptchalib.php';
}
public static function hasCaptchaResponse(AphrontRequest $request) {
return $request->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,
)),
);
}
}

View file

@ -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,
);
}

View file

@ -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';
}

View file

@ -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',
);
}

View file

@ -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";

View file

@ -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%;

View file

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

View file

@ -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;
}

View file

@ -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;
}

View file

@ -5,7 +5,7 @@
/*--- Header Colors ----------------------------------------------------------*/
.phui-theme-blindigo .phabricator-main-menu-background {
background: #41506e;
background: #4a5f88;
}
.phui-theme-dark .phabricator-main-menu-background {

View file

@ -26,6 +26,7 @@
padding-bottom: 1px;
padding-left: 8px;
width: 100%;
word-break: break-all;
}
.diffusion-browse-type-form {

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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};
}

View file

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

View file

@ -66,7 +66,7 @@
.phui-calendar-list-item-empty {
color: {$lightgreytext};
padding: 0 12px;
padding: 0;
font-style: italic;
}

View file

@ -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;
}

View file

@ -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};
}

Some files were not shown because too many files have changed in this diff Show more