Merge branch 'master' into blender-tweaks

This commit is contained in:
2020-11-18 14:29:07 +01:00
161 changed files with 3639 additions and 3232 deletions

View File

@@ -1,722 +0,0 @@
<?php
/**
*
* Copyright (c) 2011, Dan Myers.
* Parts copyright (c) 2008, Donovan Schonknecht.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* This is a modified BSD license (the third clause has been removed).
* The BSD license may be found here:
* http://www.opensource.org/licenses/bsd-license.php
*
* Amazon Simple Email Service is a trademark of Amazon.com, Inc. or its affiliates.
*
* SimpleEmailService is based on Donovan Schonknecht's Amazon S3 PHP class, found here:
* http://undesigned.org.za/2007/10/22/amazon-s3-php-class
*
*/
/**
* Amazon SimpleEmailService PHP class
*
* @link http://sourceforge.net/projects/php-aws-ses/
* version 0.8.1
*
*/
class SimpleEmailService
{
protected $__accessKey; // AWS Access key
protected $__secretKey; // AWS Secret key
protected $__host;
public function getAccessKey() { return $this->__accessKey; }
public function getSecretKey() { return $this->__secretKey; }
public function getHost() { return $this->__host; }
protected $__verifyHost = 1;
protected $__verifyPeer = 1;
// verifyHost and verifyPeer determine whether curl verifies ssl certificates.
// It may be necessary to disable these checks on certain systems.
// These only have an effect if SSL is enabled.
public function verifyHost() { return $this->__verifyHost; }
public function enableVerifyHost($enable = true) { $this->__verifyHost = $enable; }
public function verifyPeer() { return $this->__verifyPeer; }
public function enableVerifyPeer($enable = true) { $this->__verifyPeer = $enable; }
// If you use exceptions, errors will be communicated by throwing a
// SimpleEmailServiceException. By default, they will be trigger_error()'d.
protected $__useExceptions = 0;
public function useExceptions() { return $this->__useExceptions; }
public function enableUseExceptions($enable = true) { $this->__useExceptions = $enable; }
/**
* Constructor
*
* @param string $accessKey Access key
* @param string $secretKey Secret key
* @return void
*/
public function __construct($accessKey = null, $secretKey = null, $host = 'email.us-east-1.amazonaws.com') {
if (!function_exists('simplexml_load_string')) {
throw new Exception(
pht(
'The PHP SimpleXML extension is not available, but this '.
'extension is required to send mail via Amazon SES, because '.
'Amazon SES returns API responses in XML format. Install or '.
'enable the SimpleXML extension.'));
}
// Catch mistakes with reading the wrong column out of the SES
// documentation. See T10728.
if (preg_match('(-smtp)', $host)) {
throw new Exception(
pht(
'Amazon SES is not configured correctly: the configured SES '.
'endpoint ("%s") is an SMTP endpoint. Instead, use an API (HTTPS) '.
'endpoint.',
$host));
}
if ($accessKey !== null && $secretKey !== null) {
$this->setAuth($accessKey, $secretKey);
}
$this->__host = $host;
}
/**
* Set AWS access key and secret key
*
* @param string $accessKey Access key
* @param string $secretKey Secret key
* @return void
*/
public function setAuth($accessKey, $secretKey) {
$this->__accessKey = $accessKey;
$this->__secretKey = $secretKey;
}
/**
* Lists the email addresses that have been verified and can be used as the 'From' address
*
* @return An array containing two items: a list of verified email addresses, and the request id.
*/
public function listVerifiedEmailAddresses() {
$rest = new SimpleEmailServiceRequest($this, 'GET');
$rest->setParameter('Action', 'ListVerifiedEmailAddresses');
$rest = $rest->getResponse();
$response = array();
if(!isset($rest->body)) {
return $response;
}
$addresses = array();
foreach($rest->body->ListVerifiedEmailAddressesResult->VerifiedEmailAddresses->member as $address) {
$addresses[] = (string)$address;
}
$response['Addresses'] = $addresses;
$response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId;
return $response;
}
/**
* Requests verification of the provided email address, so it can be used
* as the 'From' address when sending emails through SimpleEmailService.
*
* After submitting this request, you should receive a verification email
* from Amazon at the specified address containing instructions to follow.
*
* @param string email The email address to get verified
* @return The request id for this request.
*/
public function verifyEmailAddress($email) {
$rest = new SimpleEmailServiceRequest($this, 'POST');
$rest->setParameter('Action', 'VerifyEmailAddress');
$rest->setParameter('EmailAddress', $email);
$rest = $rest->getResponse();
$response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId;
return $response;
}
/**
* Removes the specified email address from the list of verified addresses.
*
* @param string email The email address to remove
* @return The request id for this request.
*/
public function deleteVerifiedEmailAddress($email) {
$rest = new SimpleEmailServiceRequest($this, 'DELETE');
$rest->setParameter('Action', 'DeleteVerifiedEmailAddress');
$rest->setParameter('EmailAddress', $email);
$rest = $rest->getResponse();
$response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId;
return $response;
}
/**
* Retrieves information on the current activity limits for this account.
* See http://docs.amazonwebservices.com/ses/latest/APIReference/API_GetSendQuota.html
*
* @return An array containing information on this account's activity limits.
*/
public function getSendQuota() {
$rest = new SimpleEmailServiceRequest($this, 'GET');
$rest->setParameter('Action', 'GetSendQuota');
$rest = $rest->getResponse();
$response = array();
if(!isset($rest->body)) {
return $response;
}
$response['Max24HourSend'] = (string)$rest->body->GetSendQuotaResult->Max24HourSend;
$response['MaxSendRate'] = (string)$rest->body->GetSendQuotaResult->MaxSendRate;
$response['SentLast24Hours'] = (string)$rest->body->GetSendQuotaResult->SentLast24Hours;
$response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId;
return $response;
}
/**
* Retrieves statistics for the last two weeks of activity on this account.
* See http://docs.amazonwebservices.com/ses/latest/APIReference/API_GetSendStatistics.html
*
* @return An array of activity statistics. Each array item covers a 15-minute period.
*/
public function getSendStatistics() {
$rest = new SimpleEmailServiceRequest($this, 'GET');
$rest->setParameter('Action', 'GetSendStatistics');
$rest = $rest->getResponse();
$response = array();
if(!isset($rest->body)) {
return $response;
}
$datapoints = array();
foreach($rest->body->GetSendStatisticsResult->SendDataPoints->member as $datapoint) {
$p = array();
$p['Bounces'] = (string)$datapoint->Bounces;
$p['Complaints'] = (string)$datapoint->Complaints;
$p['DeliveryAttempts'] = (string)$datapoint->DeliveryAttempts;
$p['Rejects'] = (string)$datapoint->Rejects;
$p['Timestamp'] = (string)$datapoint->Timestamp;
$datapoints[] = $p;
}
$response['SendDataPoints'] = $datapoints;
$response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId;
return $response;
}
public function sendRawEmail($raw) {
$rest = new SimpleEmailServiceRequest($this, 'POST');
$rest->setParameter('Action', 'SendRawEmail');
$rest->setParameter('RawMessage.Data', base64_encode($raw));
$rest = $rest->getResponse();
$response['MessageId'] = (string)$rest->body->SendEmailResult->MessageId;
$response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId;
return $response;
}
/**
* Given a SimpleEmailServiceMessage object, submits the message to the service for sending.
*
* @return An array containing the unique identifier for this message and a separate request id.
* Returns false if the provided message is missing any required fields.
*/
public function sendEmail($sesMessage) {
if(!$sesMessage->validate()) {
return false;
}
$rest = new SimpleEmailServiceRequest($this, 'POST');
$rest->setParameter('Action', 'SendEmail');
$i = 1;
foreach($sesMessage->to as $to) {
$rest->setParameter('Destination.ToAddresses.member.'.$i, $to);
$i++;
}
if(is_array($sesMessage->cc)) {
$i = 1;
foreach($sesMessage->cc as $cc) {
$rest->setParameter('Destination.CcAddresses.member.'.$i, $cc);
$i++;
}
}
if(is_array($sesMessage->bcc)) {
$i = 1;
foreach($sesMessage->bcc as $bcc) {
$rest->setParameter('Destination.BccAddresses.member.'.$i, $bcc);
$i++;
}
}
if(is_array($sesMessage->replyto)) {
$i = 1;
foreach($sesMessage->replyto as $replyto) {
$rest->setParameter('ReplyToAddresses.member.'.$i, $replyto);
$i++;
}
}
$rest->setParameter('Source', $sesMessage->from);
if($sesMessage->returnpath != null) {
$rest->setParameter('ReturnPath', $sesMessage->returnpath);
}
if($sesMessage->subject != null && strlen($sesMessage->subject) > 0) {
$rest->setParameter('Message.Subject.Data', $sesMessage->subject);
if($sesMessage->subjectCharset != null && strlen($sesMessage->subjectCharset) > 0) {
$rest->setParameter('Message.Subject.Charset', $sesMessage->subjectCharset);
}
}
if($sesMessage->messagetext != null && strlen($sesMessage->messagetext) > 0) {
$rest->setParameter('Message.Body.Text.Data', $sesMessage->messagetext);
if($sesMessage->messageTextCharset != null && strlen($sesMessage->messageTextCharset) > 0) {
$rest->setParameter('Message.Body.Text.Charset', $sesMessage->messageTextCharset);
}
}
if($sesMessage->messagehtml != null && strlen($sesMessage->messagehtml) > 0) {
$rest->setParameter('Message.Body.Html.Data', $sesMessage->messagehtml);
if($sesMessage->messageHtmlCharset != null && strlen($sesMessage->messageHtmlCharset) > 0) {
$rest->setParameter('Message.Body.Html.Charset', $sesMessage->messageHtmlCharset);
}
}
$rest = $rest->getResponse();
$response['MessageId'] = (string)$rest->body->SendEmailResult->MessageId;
$response['RequestId'] = (string)$rest->body->ResponseMetadata->RequestId;
return $response;
}
/**
* Trigger an error message
*
* @internal Used by member functions to output errors
* @param array $error Array containing error information
* @return string
*/
public function __triggerError($functionname, $error)
{
if($error == false) {
$message = sprintf("SimpleEmailService::%s(): Encountered an error, but no description given", $functionname);
}
else if(isset($error['curl']) && $error['curl'])
{
$message = sprintf("SimpleEmailService::%s(): %s %s", $functionname, $error['code'], $error['message']);
}
else if(isset($error['Error']))
{
$e = $error['Error'];
$message = sprintf("SimpleEmailService::%s(): %s - %s: %s\nRequest Id: %s\n", $functionname, $e['Type'], $e['Code'], $e['Message'], $error['RequestId']);
}
if ($this->useExceptions()) {
throw new SimpleEmailServiceException($message);
} else {
trigger_error($message, E_USER_WARNING);
}
}
/**
* Callback handler for 503 retries.
*
* @internal Used by SimpleDBRequest to call the user-specified callback, if set
* @param $attempt The number of failed attempts so far
* @return The retry delay in microseconds, or 0 to stop retrying.
*/
public function __executeServiceTemporarilyUnavailableRetryDelay($attempt)
{
if(is_callable($this->__serviceUnavailableRetryDelayCallback)) {
$callback = $this->__serviceUnavailableRetryDelayCallback;
return $callback($attempt);
}
return 0;
}
}
final class SimpleEmailServiceRequest
{
private $ses, $verb, $parameters = array();
public $response;
/**
* Constructor
*
* @param string $ses The SimpleEmailService object making this request
* @param string $action action
* @param string $verb HTTP verb
* @return mixed
*/
function __construct($ses, $verb) {
$this->ses = $ses;
$this->verb = $verb;
$this->response = new STDClass;
$this->response->error = false;
}
/**
* Set request parameter
*
* @param string $key Key
* @param string $value Value
* @param boolean $replace Whether to replace the key if it already exists (default true)
* @return void
*/
public function setParameter($key, $value, $replace = true) {
if(!$replace && isset($this->parameters[$key]))
{
$temp = (array)($this->parameters[$key]);
$temp[] = $value;
$this->parameters[$key] = $temp;
}
else
{
$this->parameters[$key] = $value;
}
}
/**
* Get the response
*
* @return object | false
*/
public function getResponse() {
$params = array();
foreach ($this->parameters as $var => $value)
{
if(is_array($value))
{
foreach($value as $v)
{
$params[] = $var.'='.$this->__customUrlEncode($v);
}
}
else
{
$params[] = $var.'='.$this->__customUrlEncode($value);
}
}
sort($params, SORT_STRING);
// must be in format 'Sun, 06 Nov 1994 08:49:37 GMT'
$date = gmdate('D, d M Y H:i:s e');
$query = implode('&', $params);
$headers = array();
$headers[] = 'Date: '.$date;
$headers[] = 'Host: '.$this->ses->getHost();
$auth = 'AWS3-HTTPS AWSAccessKeyId='.$this->ses->getAccessKey();
$auth .= ',Algorithm=HmacSHA256,Signature='.$this->__getSignature($date);
$headers[] = 'X-Amzn-Authorization: '.$auth;
$url = 'https://'.$this->ses->getHost().'/';
// Basic setup
$curl = curl_init();
curl_setopt($curl, CURLOPT_USERAGENT, 'SimpleEmailService/php');
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, ($this->ses->verifyHost() ? 2 : 0));
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, ($this->ses->verifyPeer() ? 1 : 0));
// Request types
switch ($this->verb) {
case 'GET':
$url .= '?'.$query;
break;
case 'POST':
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $this->verb);
curl_setopt($curl, CURLOPT_POSTFIELDS, $query);
$headers[] = 'Content-Type: application/x-www-form-urlencoded';
break;
case 'DELETE':
$url .= '?'.$query;
curl_setopt($curl, CURLOPT_CUSTOMREQUEST, 'DELETE');
break;
default: break;
}
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_HEADER, false);
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, false);
curl_setopt($curl, CURLOPT_WRITEFUNCTION, array(&$this, '__responseWriteCallback'));
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, true);
// Execute, grab errors
if (!curl_exec($curl)) {
throw new SimpleEmailServiceException(
pht(
'Encountered an error while making an HTTP request to Amazon SES '.
'(cURL Error #%d): %s',
curl_errno($curl),
curl_error($curl)));
}
$this->response->code = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($this->response->code != 200) {
throw new SimpleEmailServiceException(
pht(
'Unexpected HTTP status while making request to Amazon SES: '.
'expected 200, got %s.',
$this->response->code));
}
@curl_close($curl);
// Parse body into XML
if ($this->response->error === false && isset($this->response->body)) {
$this->response->body = simplexml_load_string($this->response->body);
// Grab SES errors
if (!in_array($this->response->code, array(200, 201, 202, 204))
&& isset($this->response->body->Error)) {
$error = $this->response->body->Error;
$output = array();
$output['curl'] = false;
$output['Error'] = array();
$output['Error']['Type'] = (string)$error->Type;
$output['Error']['Code'] = (string)$error->Code;
$output['Error']['Message'] = (string)$error->Message;
$output['RequestId'] = (string)$this->response->body->RequestId;
$this->response->error = $output;
unset($this->response->body);
}
}
return $this->response;
}
/**
* CURL write callback
*
* @param resource &$curl CURL resource
* @param string &$data Data
* @return integer
*/
private function __responseWriteCallback(&$curl, &$data) {
if(!isset($this->response->body)) $this->response->body = '';
$this->response->body .= $data;
return strlen($data);
}
/**
* Contributed by afx114
* URL encode the parameters as per http://docs.amazonwebservices.com/AWSECommerceService/latest/DG/index.html?Query_QueryAuth.html
* PHP's rawurlencode() follows RFC 1738, not RFC 3986 as required by Amazon. The only difference is the tilde (~), so convert it back after rawurlencode
* See: http://www.morganney.com/blog/API/AWS-Product-Advertising-API-Requires-a-Signed-Request.php
*
* @param string $var String to encode
* @return string
*/
private function __customUrlEncode($var) {
return str_replace('%7E', '~', rawurlencode($var));
}
/**
* Generate the auth string using Hmac-SHA256
*
* @internal Used by SimpleDBRequest::getResponse()
* @param string $string String to sign
* @return string
*/
private function __getSignature($string) {
return base64_encode(hash_hmac('sha256', $string, $this->ses->getSecretKey(), true));
}
}
final class SimpleEmailServiceMessage {
// these are public for convenience only
// these are not to be used outside of the SimpleEmailService class!
public $to, $cc, $bcc, $replyto;
public $from, $returnpath;
public $subject, $messagetext, $messagehtml;
public $subjectCharset, $messageTextCharset, $messageHtmlCharset;
function __construct() {
$to = array();
$cc = array();
$bcc = array();
$replyto = array();
$from = null;
$returnpath = null;
$subject = null;
$messagetext = null;
$messagehtml = null;
$subjectCharset = null;
$messageTextCharset = null;
$messageHtmlCharset = null;
}
/**
* addTo, addCC, addBCC, and addReplyTo have the following behavior:
* If a single address is passed, it is appended to the current list of addresses.
* If an array of addresses is passed, that array is merged into the current list.
*/
function addTo($to) {
if(!is_array($to)) {
$this->to[] = $to;
}
else {
$this->to = array_merge($this->to, $to);
}
}
function addCC($cc) {
if(!is_array($cc)) {
$this->cc[] = $cc;
}
else {
$this->cc = array_merge($this->cc, $cc);
}
}
function addBCC($bcc) {
if(!is_array($bcc)) {
$this->bcc[] = $bcc;
}
else {
$this->bcc = array_merge($this->bcc, $bcc);
}
}
function addReplyTo($replyto) {
if(!is_array($replyto)) {
$this->replyto[] = $replyto;
}
else {
$this->replyto = array_merge($this->replyto, $replyto);
}
}
function setFrom($from) {
$this->from = $from;
}
function setReturnPath($returnpath) {
$this->returnpath = $returnpath;
}
function setSubject($subject) {
$this->subject = $subject;
}
function setSubjectCharset($charset) {
$this->subjectCharset = $charset;
}
function setMessageFromString($text, $html = null) {
$this->messagetext = $text;
$this->messagehtml = $html;
}
function setMessageFromFile($textfile, $htmlfile = null) {
if(file_exists($textfile) && is_file($textfile) && is_readable($textfile)) {
$this->messagetext = file_get_contents($textfile);
}
if(file_exists($htmlfile) && is_file($htmlfile) && is_readable($htmlfile)) {
$this->messagehtml = file_get_contents($htmlfile);
}
}
function setMessageFromURL($texturl, $htmlurl = null) {
$this->messagetext = file_get_contents($texturl);
if($htmlurl !== null) {
$this->messagehtml = file_get_contents($htmlurl);
}
}
function setMessageCharset($textCharset, $htmlCharset = null) {
$this->messageTextCharset = $textCharset;
$this->messageHtmlCharset = $htmlCharset;
}
/**
* Validates whether the message object has sufficient information to submit a request to SES.
* This does not guarantee the message will arrive, nor that the request will succeed;
* instead, it makes sure that no required fields are missing.
*
* This is used internally before attempting a SendEmail or SendRawEmail request,
* but it can be used outside of this file if verification is desired.
* May be useful if e.g. the data is being populated from a form; developers can generally
* use this function to verify completeness instead of writing custom logic.
*
* @return boolean
*/
public function validate() {
if(count($this->to) == 0)
return false;
if($this->from == null || strlen($this->from) == 0)
return false;
if($this->messagetext == null)
return false;
return true;
}
}
/**
* Thrown by SimpleEmailService when errors occur if you call
* enableUseExceptions(true).
*/
final class SimpleEmailServiceException extends Exception {
}

View File

@@ -9,13 +9,13 @@ return array(
'names' => array(
'conpherence.pkg.css' => '0e3cf785',
'conpherence.pkg.js' => '020aebcf',
'core.pkg.css' => '0fbedea0',
'core.pkg.js' => '74ad315f',
'core.pkg.css' => '704ac9c8',
'core.pkg.js' => '0c06d967',
'dark-console.pkg.js' => '187792c2',
'differential.pkg.css' => '5c459f92',
'differential.pkg.js' => '218fda21',
'differential.pkg.js' => '5080baf4',
'diffusion.pkg.css' => '42c75c37',
'diffusion.pkg.js' => 'a98c0bf7',
'diffusion.pkg.js' => '78c9885d',
'maniphest.pkg.css' => '35995d6d',
'maniphest.pkg.js' => 'c9308721',
'rsrc/audio/basic/alert.mp3' => '17889334',
@@ -73,7 +73,7 @@ return array(
'rsrc/css/application/diffusion/diffusion-icons.css' => '23b31a1b',
'rsrc/css/application/diffusion/diffusion-readme.css' => 'b68a76e4',
'rsrc/css/application/diffusion/diffusion-repository.css' => 'b89e8c6c',
'rsrc/css/application/diffusion/diffusion.css' => 'b54c77b0',
'rsrc/css/application/diffusion/diffusion.css' => 'e46232d6',
'rsrc/css/application/feed/feed.css' => 'd8b6e3f8',
'rsrc/css/application/files/global-drag-and-drop.css' => '1d2713a4',
'rsrc/css/application/flag/flag.css' => '2b77be8d',
@@ -113,14 +113,18 @@ return array(
'rsrc/css/application/slowvote/slowvote.css' => '1694baed',
'rsrc/css/application/tokens/tokens.css' => 'ce5a50bd',
'rsrc/css/application/uiexample/example.css' => 'b4795059',
'rsrc/css/core/core.css' => '1b29ed61',
'rsrc/css/core/remarkup.css' => 'c286eaef',
'rsrc/css/core/core.css' => 'b3ebd90d',
'rsrc/css/core/remarkup.css' => '24d48a73',
'rsrc/css/core/syntax.css' => '548567f6',
'rsrc/css/core/z-index.css' => 'ac3bfcd4',
'rsrc/css/diviner/diviner-shared.css' => '4bd263b0',
'rsrc/css/font/font-awesome.css' => '3883938a',
'rsrc/css/font/font-lato.css' => '23631304',
'rsrc/css/font/phui-font-icon-base.css' => '303c9b87',
'rsrc/css/fuel/fuel-grid.css' => '66697240',
'rsrc/css/fuel/fuel-handle-list.css' => '2c4cbeca',
'rsrc/css/fuel/fuel-map.css' => 'd6e31510',
'rsrc/css/fuel/fuel-menu.css' => '21f5d199',
'rsrc/css/layout/phabricator-source-code-view.css' => '03d7ac28',
'rsrc/css/phui/button/phui-button-bar.css' => 'a4aa75c4',
'rsrc/css/phui/button/phui-button-simple.css' => '1ff278aa',
@@ -133,7 +137,7 @@ return array(
'rsrc/css/phui/object-item/phui-oi-color.css' => 'b517bfa0',
'rsrc/css/phui/object-item/phui-oi-drag-ui.css' => 'da15d3dc',
'rsrc/css/phui/object-item/phui-oi-flush-ui.css' => '490e2e2e',
'rsrc/css/phui/object-item/phui-oi-list-view.css' => 'd7723ecc',
'rsrc/css/phui/object-item/phui-oi-list-view.css' => 'af98a277',
'rsrc/css/phui/object-item/phui-oi-simple-ui.css' => '6a30fa46',
'rsrc/css/phui/phui-action-list.css' => '1b0085b2',
'rsrc/css/phui/phui-action-panel.css' => '6c386cbf',
@@ -381,7 +385,7 @@ return array(
'rsrc/js/application/dashboard/behavior-dashboard-move-panels.js' => 'a2ab19be',
'rsrc/js/application/dashboard/behavior-dashboard-query-panel-select.js' => '1e413dc9',
'rsrc/js/application/dashboard/behavior-dashboard-tab-panel.js' => '0116d3e8',
'rsrc/js/application/diff/DiffChangeset.js' => '39dcf2c3',
'rsrc/js/application/diff/DiffChangeset.js' => '3b6e1fde',
'rsrc/js/application/diff/DiffChangesetList.js' => 'cc2c5de5',
'rsrc/js/application/diff/DiffInline.js' => '511a1315',
'rsrc/js/application/diff/DiffPathView.js' => '8207abf9',
@@ -392,7 +396,7 @@ return array(
'rsrc/js/application/diffusion/ExternalEditorLinkEngine.js' => '48a8641f',
'rsrc/js/application/diffusion/behavior-audit-preview.js' => 'b7b73831',
'rsrc/js/application/diffusion/behavior-commit-branches.js' => '4b671572',
'rsrc/js/application/diffusion/behavior-commit-graph.js' => 'ef836bf2',
'rsrc/js/application/diffusion/behavior-commit-graph.js' => 'ac10c917',
'rsrc/js/application/diffusion/behavior-locate-file.js' => '87428eb2',
'rsrc/js/application/diffusion/behavior-pull-lastmodified.js' => 'c715c123',
'rsrc/js/application/doorkeeper/behavior-doorkeeper-tag.js' => '6a85bc5a',
@@ -475,7 +479,7 @@ return array(
'rsrc/js/core/behavior-choose-control.js' => '04f8a1e3',
'rsrc/js/core/behavior-copy.js' => 'cf32921f',
'rsrc/js/core/behavior-detect-timezone.js' => '78bc5d94',
'rsrc/js/core/behavior-device.js' => '0cf79f45',
'rsrc/js/core/behavior-device.js' => 'ac2b1e01',
'rsrc/js/core/behavior-drag-and-drop-textarea.js' => '7ad020a5',
'rsrc/js/core/behavior-fancy-datepicker.js' => '956f3eeb',
'rsrc/js/core/behavior-form.js' => '55d7b788',
@@ -569,13 +573,17 @@ return array(
'differential-revision-history-css' => '8aa3eac5',
'differential-revision-list-css' => '93d2df7d',
'differential-table-of-contents-css' => 'bba788b9',
'diffusion-css' => 'b54c77b0',
'diffusion-css' => 'e46232d6',
'diffusion-icons-css' => '23b31a1b',
'diffusion-readme-css' => 'b68a76e4',
'diffusion-repository-css' => 'b89e8c6c',
'diviner-shared-css' => '4bd263b0',
'font-fontawesome' => '3883938a',
'font-lato' => '23631304',
'fuel-grid-css' => '66697240',
'fuel-handle-list-css' => '2c4cbeca',
'fuel-map-css' => 'd6e31510',
'fuel-menu-css' => '21f5d199',
'global-drag-and-drop-css' => '1d2713a4',
'harbormaster-css' => '8dfe16b2',
'herald-css' => '648d39e2',
@@ -613,11 +621,11 @@ return array(
'javelin-behavior-day-view' => '727a5a61',
'javelin-behavior-desktop-notifications-control' => '070679fe',
'javelin-behavior-detect-timezone' => '78bc5d94',
'javelin-behavior-device' => '0cf79f45',
'javelin-behavior-device' => 'ac2b1e01',
'javelin-behavior-differential-diff-radios' => '925fe8cd',
'javelin-behavior-differential-populate' => 'b86ef6c2',
'javelin-behavior-diffusion-commit-branches' => '4b671572',
'javelin-behavior-diffusion-commit-graph' => 'ef836bf2',
'javelin-behavior-diffusion-commit-graph' => 'ac10c917',
'javelin-behavior-diffusion-locate-file' => '87428eb2',
'javelin-behavior-diffusion-pull-lastmodified' => 'c715c123',
'javelin-behavior-document-engine' => '243d6c22',
@@ -773,12 +781,12 @@ return array(
'phabricator-busy' => '5202e831',
'phabricator-chatlog-css' => 'abdc76ee',
'phabricator-content-source-view-css' => 'cdf0d579',
'phabricator-core-css' => '1b29ed61',
'phabricator-core-css' => 'b3ebd90d',
'phabricator-countdown-css' => 'bff8012f',
'phabricator-darklog' => '3b869402',
'phabricator-darkmessage' => '26cd4b73',
'phabricator-dashboard-css' => '5a205b9d',
'phabricator-diff-changeset' => '39dcf2c3',
'phabricator-diff-changeset' => '3b6e1fde',
'phabricator-diff-changeset-list' => 'cc2c5de5',
'phabricator-diff-inline' => '511a1315',
'phabricator-diff-path-view' => '8207abf9',
@@ -800,7 +808,7 @@ return array(
'phabricator-object-selector-css' => 'ee77366f',
'phabricator-phtize' => '2f1db1ed',
'phabricator-prefab' => '5793d835',
'phabricator-remarkup-css' => 'c286eaef',
'phabricator-remarkup-css' => '24d48a73',
'phabricator-search-results-css' => '9ea70ace',
'phabricator-shaped-request' => '995f5102',
'phabricator-slowvote-css' => '1694baed',
@@ -869,7 +877,7 @@ return array(
'phui-oi-color-css' => 'b517bfa0',
'phui-oi-drag-ui-css' => 'da15d3dc',
'phui-oi-flush-ui-css' => '490e2e2e',
'phui-oi-list-view-css' => 'd7723ecc',
'phui-oi-list-view-css' => 'af98a277',
'phui-oi-simple-ui-css' => '6a30fa46',
'phui-pager-css' => 'd022c7ad',
'phui-pinboard-view-css' => '1f08f5d8',
@@ -1005,13 +1013,6 @@ return array(
'javelin-dom',
'phabricator-draggable-list',
),
'0cf79f45' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-vector',
'javelin-install',
),
'0d2490ce' => array(
'javelin-install',
),
@@ -1231,7 +1232,14 @@ return array(
'trigger-rule',
'trigger-rule-type',
),
'39dcf2c3' => array(
'3ae89b20' => array(
'phui-workcard-view-css',
),
'3b4899b0' => array(
'javelin-behavior',
'phabricator-prefab',
),
'3b6e1fde' => array(
'javelin-dom',
'javelin-util',
'javelin-stratcom',
@@ -1245,13 +1253,6 @@ return array(
'phuix-button-view',
'javelin-external-editor-link-engine',
),
'3ae89b20' => array(
'phui-workcard-view-css',
),
'3b4899b0' => array(
'javelin-behavior',
'phabricator-prefab',
),
'3dc5ad43' => array(
'javelin-behavior',
'javelin-stratcom',
@@ -1924,6 +1925,18 @@ return array(
'javelin-dom',
'phabricator-notification',
),
'ac10c917' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
),
'ac2b1e01' => array(
'javelin-behavior',
'javelin-stratcom',
'javelin-dom',
'javelin-vector',
'javelin-install',
),
'ad258e28' => array(
'javelin-behavior',
'javelin-dom',
@@ -2176,11 +2189,6 @@ return array(
'ee77366f' => array(
'aphront-dialog-view-css',
),
'ef836bf2' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
),
'f340a484' => array(
'javelin-install',
'javelin-dom',

View File

@@ -40,7 +40,8 @@ foreach ($lists as $list) {
if (!$username_okay) {
echo pht(
'Failed to migrate mailing list "%s": unable to generate a unique '.
'username for it.')."\n";
'username for it.',
$name)."\n";
continue;
}

View File

@@ -211,21 +211,29 @@ try {
->setUniqueMethod('getName')
->execute();
$command_list = array_keys($workflows);
$command_list = implode(', ', $command_list);
$error_lines = array();
$error_lines[] = pht('Welcome to Phabricator.');
$error_lines[] = pht(
'You are logged in as %s.',
$user_name);
if (!$original_argv) {
throw new Exception(
pht(
"Welcome to Phabricator.\n\n".
"You are logged in as %s.\n\n".
"You haven't specified a command to run. This means you're requesting ".
"an interactive shell, but Phabricator does not provide an ".
"interactive shell over SSH.\n\n".
"Usually, you should run a command like `%s` or `%s` ".
"rather than connecting directly with SSH.\n\n".
"Supported commands are: %s.",
$user_name,
'git clone',
'hg push',
implode(', ', array_keys($workflows))));
$error_lines[] = pht(
'You have not specified a command to run. This means you are requesting '.
'an interactive shell, but Phabricator does not provide interactive '.
'shells over SSH.');
$error_lines[] = pht(
'(Usually, you should run a command like "git clone" or "hg push" '.
'instead of connecting directly with SSH.)');
$error_lines[] = pht(
'Supported commands are: %s.',
$command_list);
$error_lines = implode("\n\n", $error_lines);
throw new PhutilArgumentUsageException($error_lines);
}
$log_argv = implode(' ', $original_argv);
@@ -247,7 +255,20 @@ try {
$parsed_args = new PhutilArgumentParser($parseable_argv);
if (empty($workflows[$command])) {
throw new Exception(pht('Invalid command.'));
$error_lines[] = pht(
'You have specified the command "%s", but that command is not '.
'supported by Phabricator. As received by Phabricator, your entire '.
'argument list was:',
$command);
$error_lines[] = csprintf(' $ ssh ... -- %Ls', $parseable_argv);
$error_lines[] = pht(
'Supported commands are: %s.',
$command_list);
$error_lines = implode("\n\n", $error_lines);
throw new PhutilArgumentUsageException($error_lines);
}
$workflow = $parsed_args->parseWorkflows($workflows);

View File

@@ -770,6 +770,7 @@ phutil_register_library_map(array(
'DiffusionCommitEditEngine' => 'applications/diffusion/editor/DiffusionCommitEditEngine.php',
'DiffusionCommitFerretEngine' => 'applications/repository/search/DiffusionCommitFerretEngine.php',
'DiffusionCommitFulltextEngine' => 'applications/repository/search/DiffusionCommitFulltextEngine.php',
'DiffusionCommitGraphView' => 'applications/diffusion/view/DiffusionCommitGraphView.php',
'DiffusionCommitHasPackageEdgeType' => 'applications/diffusion/edge/DiffusionCommitHasPackageEdgeType.php',
'DiffusionCommitHasRevisionEdgeType' => 'applications/diffusion/edge/DiffusionCommitHasRevisionEdgeType.php',
'DiffusionCommitHasRevisionRelationship' => 'applications/diffusion/relationships/DiffusionCommitHasRevisionRelationship.php',
@@ -782,7 +783,6 @@ phutil_register_library_map(array(
'DiffusionCommitHookEngine' => 'applications/diffusion/engine/DiffusionCommitHookEngine.php',
'DiffusionCommitHookRejectException' => 'applications/diffusion/exception/DiffusionCommitHookRejectException.php',
'DiffusionCommitListController' => 'applications/diffusion/controller/DiffusionCommitListController.php',
'DiffusionCommitListView' => 'applications/diffusion/view/DiffusionCommitListView.php',
'DiffusionCommitMergeHeraldField' => 'applications/diffusion/herald/DiffusionCommitMergeHeraldField.php',
'DiffusionCommitMessageHeraldField' => 'applications/diffusion/herald/DiffusionCommitMessageHeraldField.php',
'DiffusionCommitPackageAuditHeraldField' => 'applications/diffusion/herald/DiffusionCommitPackageAuditHeraldField.php',
@@ -861,12 +861,8 @@ phutil_register_library_map(array(
'DiffusionGitWireProtocolCapabilities' => 'applications/diffusion/protocol/DiffusionGitWireProtocolCapabilities.php',
'DiffusionGitWireProtocolRef' => 'applications/diffusion/protocol/DiffusionGitWireProtocolRef.php',
'DiffusionGitWireProtocolRefList' => 'applications/diffusion/protocol/DiffusionGitWireProtocolRefList.php',
'DiffusionGraphController' => 'applications/diffusion/controller/DiffusionGraphController.php',
'DiffusionHistoryController' => 'applications/diffusion/controller/DiffusionHistoryController.php',
'DiffusionHistoryListView' => 'applications/diffusion/view/DiffusionHistoryListView.php',
'DiffusionHistoryQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionHistoryQueryConduitAPIMethod.php',
'DiffusionHistoryTableView' => 'applications/diffusion/view/DiffusionHistoryTableView.php',
'DiffusionHistoryView' => 'applications/diffusion/view/DiffusionHistoryView.php',
'DiffusionHovercardEngineExtension' => 'applications/diffusion/engineextension/DiffusionHovercardEngineExtension.php',
'DiffusionIdentityAssigneeDatasource' => 'applications/diffusion/typeahead/DiffusionIdentityAssigneeDatasource.php',
'DiffusionIdentityAssigneeEditField' => 'applications/diffusion/editfield/DiffusionIdentityAssigneeEditField.php',
@@ -877,6 +873,8 @@ phutil_register_library_map(array(
'DiffusionIdentityViewController' => 'applications/diffusion/controller/DiffusionIdentityViewController.php',
'DiffusionInlineCommentController' => 'applications/diffusion/controller/DiffusionInlineCommentController.php',
'DiffusionInternalAncestorsConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionInternalAncestorsConduitAPIMethod.php',
'DiffusionInternalCommitSearchConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionInternalCommitSearchConduitAPIMethod.php',
'DiffusionInternalCommitSearchEngine' => 'applications/audit/query/DiffusionInternalCommitSearchEngine.php',
'DiffusionInternalGitRawDiffQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionInternalGitRawDiffQueryConduitAPIMethod.php',
'DiffusionLastModifiedController' => 'applications/diffusion/controller/DiffusionLastModifiedController.php',
'DiffusionLastModifiedQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionLastModifiedQueryConduitAPIMethod.php',
@@ -1063,7 +1061,6 @@ phutil_register_library_map(array(
'DiffusionSyncLogSearchEngine' => 'applications/diffusion/query/DiffusionSyncLogSearchEngine.php',
'DiffusionTagListController' => 'applications/diffusion/controller/DiffusionTagListController.php',
'DiffusionTagListView' => 'applications/diffusion/view/DiffusionTagListView.php',
'DiffusionTagTableView' => 'applications/diffusion/view/DiffusionTagTableView.php',
'DiffusionTaggedRepositoriesFunctionDatasource' => 'applications/diffusion/typeahead/DiffusionTaggedRepositoriesFunctionDatasource.php',
'DiffusionTagsQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionTagsQueryConduitAPIMethod.php',
'DiffusionURIEditConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionURIEditConduitAPIMethod.php',
@@ -1310,6 +1307,17 @@ phutil_register_library_map(array(
'FlagDeleteConduitAPIMethod' => 'applications/flag/conduit/FlagDeleteConduitAPIMethod.php',
'FlagEditConduitAPIMethod' => 'applications/flag/conduit/FlagEditConduitAPIMethod.php',
'FlagQueryConduitAPIMethod' => 'applications/flag/conduit/FlagQueryConduitAPIMethod.php',
'FuelComponentView' => 'view/fuel/FuelComponentView.php',
'FuelGridCellView' => 'view/fuel/FuelGridCellView.php',
'FuelGridRowView' => 'view/fuel/FuelGridRowView.php',
'FuelGridView' => 'view/fuel/FuelGridView.php',
'FuelHandleListItemView' => 'view/fuel/FuelHandleListItemView.php',
'FuelHandleListView' => 'view/fuel/FuelHandleListView.php',
'FuelMapItemView' => 'view/fuel/FuelMapItemView.php',
'FuelMapView' => 'view/fuel/FuelMapView.php',
'FuelMenuItemView' => 'view/fuel/FuelMenuItemView.php',
'FuelMenuView' => 'view/fuel/FuelMenuView.php',
'FuelView' => 'view/fuel/FuelView.php',
'FundBacker' => 'applications/fund/storage/FundBacker.php',
'FundBackerCart' => 'applications/fund/phortune/FundBackerCart.php',
'FundBackerEditor' => 'applications/fund/editor/FundBackerEditor.php',
@@ -1422,12 +1430,16 @@ phutil_register_library_map(array(
'HarbormasterBuildStep' => 'applications/harbormaster/storage/configuration/HarbormasterBuildStep.php',
'HarbormasterBuildStepCoreCustomField' => 'applications/harbormaster/customfield/HarbormasterBuildStepCoreCustomField.php',
'HarbormasterBuildStepCustomField' => 'applications/harbormaster/customfield/HarbormasterBuildStepCustomField.php',
'HarbormasterBuildStepEditAPIMethod' => 'applications/harbormaster/conduit/HarbormasterBuildStepEditAPIMethod.php',
'HarbormasterBuildStepEditEngine' => 'applications/harbormaster/editor/HarbormasterBuildStepEditEngine.php',
'HarbormasterBuildStepEditor' => 'applications/harbormaster/editor/HarbormasterBuildStepEditor.php',
'HarbormasterBuildStepGroup' => 'applications/harbormaster/stepgroup/HarbormasterBuildStepGroup.php',
'HarbormasterBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterBuildStepImplementation.php',
'HarbormasterBuildStepImplementationTestCase' => 'applications/harbormaster/step/__tests__/HarbormasterBuildStepImplementationTestCase.php',
'HarbormasterBuildStepPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildStepPHIDType.php',
'HarbormasterBuildStepQuery' => 'applications/harbormaster/query/HarbormasterBuildStepQuery.php',
'HarbormasterBuildStepSearchAPIMethod' => 'applications/harbormaster/conduit/HarbormasterBuildStepSearchAPIMethod.php',
'HarbormasterBuildStepSearchEngine' => 'applications/harbormaster/query/HarbormasterBuildStepSearchEngine.php',
'HarbormasterBuildStepTransaction' => 'applications/harbormaster/storage/configuration/HarbormasterBuildStepTransaction.php',
'HarbormasterBuildStepTransactionQuery' => 'applications/harbormaster/query/HarbormasterBuildStepTransactionQuery.php',
'HarbormasterBuildTarget' => 'applications/harbormaster/storage/build/HarbormasterBuildTarget.php',
@@ -1549,6 +1561,7 @@ phutil_register_library_map(array(
'HeraldBuildableState' => 'applications/herald/state/HeraldBuildableState.php',
'HeraldCallWebhookAction' => 'applications/herald/action/HeraldCallWebhookAction.php',
'HeraldCommentAction' => 'applications/herald/action/HeraldCommentAction.php',
'HeraldCommentContentField' => 'applications/herald/field/HeraldCommentContentField.php',
'HeraldCommitAdapter' => 'applications/diffusion/herald/HeraldCommitAdapter.php',
'HeraldCondition' => 'applications/herald/storage/HeraldCondition.php',
'HeraldConditionTranscript' => 'applications/herald/storage/transcript/HeraldConditionTranscript.php',
@@ -2172,6 +2185,7 @@ phutil_register_library_map(array(
'PeopleUserLogGarbageCollector' => 'applications/people/garbagecollector/PeopleUserLogGarbageCollector.php',
'Phabricator404Controller' => 'applications/base/controller/Phabricator404Controller.php',
'PhabricatorAWSConfigOptions' => 'applications/config/option/PhabricatorAWSConfigOptions.php',
'PhabricatorAWSSESFuture' => 'applications/metamta/future/PhabricatorAWSSESFuture.php',
'PhabricatorAccessControlTestCase' => 'applications/base/controller/__tests__/PhabricatorAccessControlTestCase.php',
'PhabricatorAccessLog' => 'infrastructure/log/PhabricatorAccessLog.php',
'PhabricatorAccessLogConfigOptions' => 'applications/config/option/PhabricatorAccessLogConfigOptions.php',
@@ -2281,7 +2295,6 @@ phutil_register_library_map(array(
'PhabricatorAuditController' => 'applications/audit/controller/PhabricatorAuditController.php',
'PhabricatorAuditEditor' => 'applications/audit/editor/PhabricatorAuditEditor.php',
'PhabricatorAuditInlineComment' => 'applications/audit/storage/PhabricatorAuditInlineComment.php',
'PhabricatorAuditListView' => 'applications/audit/view/PhabricatorAuditListView.php',
'PhabricatorAuditMailReceiver' => 'applications/audit/mail/PhabricatorAuditMailReceiver.php',
'PhabricatorAuditManagementDeleteWorkflow' => 'applications/audit/management/PhabricatorAuditManagementDeleteWorkflow.php',
'PhabricatorAuditManagementWorkflow' => 'applications/audit/management/PhabricatorAuditManagementWorkflow.php',
@@ -4582,7 +4595,6 @@ phutil_register_library_map(array(
'PhabricatorRepositoryManagementImportingWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementImportingWorkflow.php',
'PhabricatorRepositoryManagementListPathsWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementListPathsWorkflow.php',
'PhabricatorRepositoryManagementListWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementListWorkflow.php',
'PhabricatorRepositoryManagementLookupUsersWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementLookupUsersWorkflow.php',
'PhabricatorRepositoryManagementMaintenanceWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementMaintenanceWorkflow.php',
'PhabricatorRepositoryManagementMarkImportedWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementMarkImportedWorkflow.php',
'PhabricatorRepositoryManagementMarkReachableWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementMarkReachableWorkflow.php',
@@ -4655,8 +4667,6 @@ phutil_register_library_map(array(
'PhabricatorRepositoryType' => 'applications/repository/constants/PhabricatorRepositoryType.php',
'PhabricatorRepositoryURI' => 'applications/repository/storage/PhabricatorRepositoryURI.php',
'PhabricatorRepositoryURIIndex' => 'applications/repository/storage/PhabricatorRepositoryURIIndex.php',
'PhabricatorRepositoryURINormalizer' => 'applications/repository/data/PhabricatorRepositoryURINormalizer.php',
'PhabricatorRepositoryURINormalizerTestCase' => 'applications/repository/data/__tests__/PhabricatorRepositoryURINormalizerTestCase.php',
'PhabricatorRepositoryURIPHIDType' => 'applications/repository/phid/PhabricatorRepositoryURIPHIDType.php',
'PhabricatorRepositoryURIQuery' => 'applications/repository/query/PhabricatorRepositoryURIQuery.php',
'PhabricatorRepositoryURITestCase' => 'applications/repository/storage/__tests__/PhabricatorRepositoryURITestCase.php',
@@ -5959,9 +5969,11 @@ phutil_register_library_map(array(
'celerity_get_resource_uri' => 'applications/celerity/api.php',
'hsprintf' => 'infrastructure/markup/render.php',
'javelin_tag' => 'infrastructure/javelin/markup.php',
'phabricator_absolute_datetime' => 'view/viewutils.php',
'phabricator_date' => 'view/viewutils.php',
'phabricator_datetime' => 'view/viewutils.php',
'phabricator_datetimezone' => 'view/viewutils.php',
'phabricator_dual_datetime' => 'view/viewutils.php',
'phabricator_form' => 'infrastructure/javelin/markup.php',
'phabricator_format_local_time' => 'view/viewutils.php',
'phabricator_relative_date' => 'view/viewutils.php',
@@ -6867,6 +6879,7 @@ phutil_register_library_map(array(
'DiffusionCommitEditEngine' => 'PhabricatorEditEngine',
'DiffusionCommitFerretEngine' => 'PhabricatorFerretEngine',
'DiffusionCommitFulltextEngine' => 'PhabricatorFulltextEngine',
'DiffusionCommitGraphView' => 'DiffusionView',
'DiffusionCommitHasPackageEdgeType' => 'PhabricatorEdgeType',
'DiffusionCommitHasRevisionEdgeType' => 'PhabricatorEdgeType',
'DiffusionCommitHasRevisionRelationship' => 'DiffusionCommitRelationship',
@@ -6879,7 +6892,6 @@ phutil_register_library_map(array(
'DiffusionCommitHookEngine' => 'Phobject',
'DiffusionCommitHookRejectException' => 'Exception',
'DiffusionCommitListController' => 'DiffusionController',
'DiffusionCommitListView' => 'AphrontView',
'DiffusionCommitMergeHeraldField' => 'DiffusionCommitHeraldField',
'DiffusionCommitMessageHeraldField' => 'DiffusionCommitHeraldField',
'DiffusionCommitPackageAuditHeraldField' => 'DiffusionCommitHeraldField',
@@ -6961,12 +6973,8 @@ phutil_register_library_map(array(
'DiffusionGitWireProtocolCapabilities' => 'Phobject',
'DiffusionGitWireProtocolRef' => 'Phobject',
'DiffusionGitWireProtocolRefList' => 'Phobject',
'DiffusionGraphController' => 'DiffusionController',
'DiffusionHistoryController' => 'DiffusionController',
'DiffusionHistoryListView' => 'DiffusionHistoryView',
'DiffusionHistoryQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
'DiffusionHistoryTableView' => 'DiffusionHistoryView',
'DiffusionHistoryView' => 'DiffusionView',
'DiffusionHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension',
'DiffusionIdentityAssigneeDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
'DiffusionIdentityAssigneeEditField' => 'PhabricatorTokenizerEditField',
@@ -6977,6 +6985,8 @@ phutil_register_library_map(array(
'DiffusionIdentityViewController' => 'DiffusionController',
'DiffusionInlineCommentController' => 'PhabricatorInlineCommentController',
'DiffusionInternalAncestorsConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
'DiffusionInternalCommitSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
'DiffusionInternalCommitSearchEngine' => 'PhabricatorApplicationSearchEngine',
'DiffusionInternalGitRawDiffQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
'DiffusionLastModifiedController' => 'DiffusionController',
'DiffusionLastModifiedQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
@@ -7162,7 +7172,6 @@ phutil_register_library_map(array(
'DiffusionSyncLogSearchEngine' => 'PhabricatorApplicationSearchEngine',
'DiffusionTagListController' => 'DiffusionController',
'DiffusionTagListView' => 'DiffusionView',
'DiffusionTagTableView' => 'DiffusionView',
'DiffusionTaggedRepositoriesFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
'DiffusionTagsQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
'DiffusionURIEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
@@ -7455,6 +7464,17 @@ phutil_register_library_map(array(
'FlagDeleteConduitAPIMethod' => 'FlagConduitAPIMethod',
'FlagEditConduitAPIMethod' => 'FlagConduitAPIMethod',
'FlagQueryConduitAPIMethod' => 'FlagConduitAPIMethod',
'FuelComponentView' => 'FuelView',
'FuelGridCellView' => 'FuelComponentView',
'FuelGridRowView' => 'FuelView',
'FuelGridView' => 'FuelComponentView',
'FuelHandleListItemView' => 'FuelView',
'FuelHandleListView' => 'FuelComponentView',
'FuelMapItemView' => 'AphrontView',
'FuelMapView' => 'FuelComponentView',
'FuelMenuItemView' => 'FuelView',
'FuelMenuView' => 'FuelComponentView',
'FuelView' => 'AphrontView',
'FundBacker' => array(
'FundDAO',
'PhabricatorPolicyInterface',
@@ -7614,18 +7634,23 @@ phutil_register_library_map(array(
'PhabricatorApplicationTransactionInterface',
'PhabricatorPolicyInterface',
'PhabricatorCustomFieldInterface',
'PhabricatorConduitResultInterface',
),
'HarbormasterBuildStepCoreCustomField' => array(
'HarbormasterBuildStepCustomField',
'PhabricatorStandardCustomFieldInterface',
),
'HarbormasterBuildStepCustomField' => 'PhabricatorCustomField',
'HarbormasterBuildStepEditAPIMethod' => 'PhabricatorEditEngineAPIMethod',
'HarbormasterBuildStepEditEngine' => 'PhabricatorEditEngine',
'HarbormasterBuildStepEditor' => 'PhabricatorApplicationTransactionEditor',
'HarbormasterBuildStepGroup' => 'Phobject',
'HarbormasterBuildStepImplementation' => 'Phobject',
'HarbormasterBuildStepImplementationTestCase' => 'PhabricatorTestCase',
'HarbormasterBuildStepPHIDType' => 'PhabricatorPHIDType',
'HarbormasterBuildStepQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'HarbormasterBuildStepSearchAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
'HarbormasterBuildStepSearchEngine' => 'PhabricatorApplicationSearchEngine',
'HarbormasterBuildStepTransaction' => 'PhabricatorApplicationTransaction',
'HarbormasterBuildStepTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'HarbormasterBuildTarget' => array(
@@ -7758,6 +7783,7 @@ phutil_register_library_map(array(
'HeraldBuildableState' => 'HeraldState',
'HeraldCallWebhookAction' => 'HeraldAction',
'HeraldCommentAction' => 'HeraldAction',
'HeraldCommentContentField' => 'HeraldField',
'HeraldCommitAdapter' => array(
'HeraldAdapter',
'HarbormasterBuildableAdapterInterface',
@@ -8479,6 +8505,7 @@ phutil_register_library_map(array(
'PeopleUserLogGarbageCollector' => 'PhabricatorGarbageCollector',
'Phabricator404Controller' => 'PhabricatorController',
'PhabricatorAWSConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorAWSSESFuture' => 'PhutilAWSFuture',
'PhabricatorAccessControlTestCase' => 'PhabricatorTestCase',
'PhabricatorAccessLog' => 'Phobject',
'PhabricatorAccessLogConfigOptions' => 'PhabricatorApplicationConfigOptions',
@@ -8603,7 +8630,6 @@ phutil_register_library_map(array(
'PhabricatorAuditController' => 'PhabricatorController',
'PhabricatorAuditEditor' => 'PhabricatorApplicationTransactionEditor',
'PhabricatorAuditInlineComment' => 'PhabricatorInlineComment',
'PhabricatorAuditListView' => 'AphrontView',
'PhabricatorAuditMailReceiver' => 'PhabricatorObjectMailReceiver',
'PhabricatorAuditManagementDeleteWorkflow' => 'PhabricatorAuditManagementWorkflow',
'PhabricatorAuditManagementWorkflow' => 'PhabricatorManagementWorkflow',
@@ -11315,7 +11341,6 @@ phutil_register_library_map(array(
'PhabricatorRepositoryManagementImportingWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
'PhabricatorRepositoryManagementListPathsWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
'PhabricatorRepositoryManagementListWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
'PhabricatorRepositoryManagementLookupUsersWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
'PhabricatorRepositoryManagementMaintenanceWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
'PhabricatorRepositoryManagementMarkImportedWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
'PhabricatorRepositoryManagementMarkReachableWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
@@ -11412,8 +11437,6 @@ phutil_register_library_map(array(
'PhabricatorConduitResultInterface',
),
'PhabricatorRepositoryURIIndex' => 'PhabricatorRepositoryDAO',
'PhabricatorRepositoryURINormalizer' => 'Phobject',
'PhabricatorRepositoryURINormalizerTestCase' => 'PhabricatorTestCase',
'PhabricatorRepositoryURIPHIDType' => 'PhabricatorPHIDType',
'PhabricatorRepositoryURIQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorRepositoryURITestCase' => 'PhabricatorTestCase',

View File

@@ -0,0 +1,75 @@
<?php
final class DiffusionInternalCommitSearchEngine
extends PhabricatorApplicationSearchEngine {
public function getResultTypeDescription() {
return pht('Diffusion Raw Commits');
}
public function getApplicationClassName() {
return 'PhabricatorDiffusionApplication';
}
public function newQuery() {
return new DiffusionCommitQuery();
}
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
if ($map['repositoryPHIDs']) {
$query->withRepositoryPHIDs($map['repositoryPHIDs']);
}
return $query;
}
protected function buildCustomSearchFields() {
return array(
id(new PhabricatorSearchDatasourceField())
->setLabel(pht('Repositories'))
->setKey('repositoryPHIDs')
->setDatasource(new DiffusionRepositoryFunctionDatasource())
->setDescription(pht('Find commits in particular repositories.')),
);
}
protected function getURI($path) {
return null;
}
protected function renderResultList(
array $commits,
PhabricatorSavedQuery $query,
array $handles) {
return null;
}
protected function getObjectWireFieldsForConduit(
$object,
array $field_extensions,
array $extension_data) {
$commit = $object;
$viewer = $this->requireViewer();
$repository = $commit->getRepository();
$identifier = $commit->getCommitIdentifier();
id(new DiffusionRepositoryClusterEngine())
->setViewer($viewer)
->setRepository($repository)
->synchronizeWorkingCopyBeforeRead();
$ref = id(new DiffusionLowLevelCommitQuery())
->setRepository($repository)
->withIdentifier($identifier)
->execute();
return array(
'ref' => $ref->newDictionary(),
);
}
}

View File

@@ -221,9 +221,9 @@ final class PhabricatorCommitSearchEngine
$bucket = $this->getResultBucket($query);
$template = id(new PhabricatorAuditListView())
$template = id(new DiffusionCommitGraphView())
->setViewer($viewer)
->setShowDrafts(true);
->setShowAuditors(true);
$views = array();
if ($bucket) {
@@ -235,37 +235,31 @@ final class PhabricatorCommitSearchEngine
foreach ($groups as $group) {
// Don't show groups in Dashboard Panels
if ($group->getObjects() || !$this->isPanelContext()) {
$views[] = id(clone $template)
$item_list = id(clone $template)
->setCommits($group->getObjects())
->newObjectItemListView();
$views[] = $item_list
->setHeader($group->getName())
->setNoDataString($group->getNoDataString())
->setCommits($group->getObjects());
->setNoDataString($group->getNoDataString());
}
}
} catch (Exception $ex) {
$this->addError($ex->getMessage());
}
} else {
$views[] = id(clone $template)
->setCommits($commits)
->setNoDataString(pht('No commits found.'));
}
if (!$views) {
$views[] = id(new PhabricatorAuditListView())
->setViewer($viewer)
$item_list = id(clone $template)
->setCommits($commits)
->newObjectItemListView();
$views[] = $item_list
->setNoDataString(pht('No commits found.'));
}
if (count($views) == 1) {
$list = head($views)->buildList();
} else {
$list = $views;
}
$result = new PhabricatorApplicationSearchResultView();
$result->setContent($list);
return $result;
return id(new PhabricatorApplicationSearchResultView())
->setContent($views);
}
protected function getNewUserBody() {

View File

@@ -1,179 +0,0 @@
<?php
final class PhabricatorAuditListView extends AphrontView {
private $commits = array();
private $header;
private $showDrafts;
private $noDataString;
private $highlightedAudits;
public function setNoDataString($no_data_string) {
$this->noDataString = $no_data_string;
return $this;
}
public function getNoDataString() {
return $this->noDataString;
}
public function setHeader($header) {
$this->header = $header;
return $this;
}
public function getHeader() {
return $this->header;
}
public function setShowDrafts($show_drafts) {
$this->showDrafts = $show_drafts;
return $this;
}
public function getShowDrafts() {
return $this->showDrafts;
}
/**
* These commits should have both commit data and audit requests attached.
*/
public function setCommits(array $commits) {
assert_instances_of($commits, 'PhabricatorRepositoryCommit');
$this->commits = mpull($commits, null, 'getPHID');
return $this;
}
public function getCommits() {
return $this->commits;
}
private function getCommitDescription($phid) {
if ($this->commits === null) {
return pht('(Unknown Commit)');
}
$commit = idx($this->commits, $phid);
if (!$commit) {
return pht('(Unknown Commit)');
}
$summary = $commit->getCommitData()->getSummary();
if (strlen($summary)) {
return $summary;
}
// No summary, so either this is still importing or just has an empty
// commit message.
if (!$commit->isImported()) {
return pht('(Importing Commit...)');
} else {
return pht('(Untitled Commit)');
}
}
public function render() {
$list = $this->buildList();
$list->setFlush(true);
return $list->render();
}
public function buildList() {
$viewer = $this->getViewer();
$rowc = array();
$phids = array();
foreach ($this->getCommits() as $commit) {
$phids[] = $commit->getPHID();
foreach ($commit->getAudits() as $audit) {
$phids[] = $audit->getAuditorPHID();
}
$author_phid = $commit->getAuthorPHID();
if ($author_phid) {
$phids[] = $author_phid;
}
}
$handles = $viewer->loadHandles($phids);
$show_drafts = $this->getShowDrafts();
$draft_icon = id(new PHUIIconView())
->setIcon('fa-comment yellow')
->addSigil('has-tooltip')
->setMetadata(
array(
'tip' => pht('Unsubmitted Comments'),
));
$list = new PHUIObjectItemListView();
foreach ($this->commits as $commit) {
$commit_phid = $commit->getPHID();
$commit_handle = $handles[$commit_phid];
$committed = null;
$commit_name = $commit_handle->getName();
$commit_link = $commit_handle->getURI();
$commit_desc = $this->getCommitDescription($commit_phid);
$committed = phabricator_datetime($commit->getEpoch(), $viewer);
$status = $commit->getAuditStatusObject();
$status_text = $status->getName();
$status_color = $status->getColor();
$status_icon = $status->getIcon();
$author_phid = $commit->getAuthorPHID();
if ($author_phid) {
$author_name = $handles[$author_phid]->renderLink();
} else {
$author_name = $commit->getCommitData()->getAuthorName();
}
$item = id(new PHUIObjectItemView())
->setObjectName($commit_name)
->setHeader($commit_desc)
->setHref($commit_link)
->setDisabled($commit->isUnreachable())
->addByline(pht('Author: %s', $author_name))
->addIcon('none', $committed);
if ($show_drafts) {
if ($commit->getHasDraft($viewer)) {
$item->addAttribute($draft_icon);
}
}
$audits = $commit->getAudits();
$auditor_phids = mpull($audits, 'getAuditorPHID');
if ($auditor_phids) {
$auditor_list = $handles->newSublist($auditor_phids)
->renderList()
->setAsInline(true);
} else {
$auditor_list = phutil_tag('em', array(), pht('None'));
}
$item->addAttribute(pht('Auditors: %s', $auditor_list));
if ($status_color) {
$item->setStatusIcon($status_icon.' '.$status_color, $status_text);
}
$list->addItem($item);
}
if ($this->noDataString) {
$list->setNoDataString($this->noDataString);
}
if ($this->header) {
$list->setHeader($this->header);
}
return $list;
}
}

View File

@@ -181,6 +181,12 @@ final class PhabricatorAuthPasswordEngine
$normal_password = phutil_utf8_strtolower($raw_password);
if (strlen($normal_password) >= $minimum_similarity) {
foreach ($normal_map as $term => $source) {
// See T2312. This may be required if the term list includes numeric
// strings like "12345", which will be cast to integers when used as
// array keys.
$term = phutil_string_cast($term);
if (strpos($term, $normal_password) === false &&
strpos($normal_password, $term) === false) {
continue;

View File

@@ -100,9 +100,17 @@ final class PhabricatorConduitAPIController
}
} catch (Exception $ex) {
$result = null;
$error_code = ($ex instanceof ConduitException
? 'ERR-CONDUIT-CALL'
: 'ERR-CONDUIT-CORE');
if ($ex instanceof ConduitException) {
$error_code = 'ERR-CONDUIT-CALL';
} else {
$error_code = 'ERR-CONDUIT-CORE';
// See T13581. When a Conduit method raises an uncaught exception
// other than a "ConduitException", log it.
phlog($ex);
}
$error_info = $ex->getMessage();
}

View File

@@ -142,6 +142,8 @@ abstract class PhabricatorConduitController extends PhabricatorController {
$parts[] = '--conduit-token ';
$parts[] = phutil_tag('strong', array(), '<conduit-token>');
$parts[] = ' ';
$parts[] = '--';
$parts[] = ' ';
$parts[] = $method->getAPIMethodName();

View File

@@ -120,9 +120,21 @@ abstract class ConduitAPIMethod
public function executeMethod(ConduitAPIRequest $request) {
$this->setViewer($request->getUser());
$client = $this->newConduitCallProxyClient($request);
if ($client) {
// We're proxying, so just make an intracluster call.
return $client->callMethodSynchronous(
$this->getAPIMethodName(),
$request->getAllParameters());
}
return $this->execute($request);
}
protected function newConduitCallProxyClient(ConduitAPIRequest $request) {
return null;
}
abstract public function getAPIMethodName();
/**

View File

@@ -51,6 +51,10 @@ final class ConduitAPIRequest extends Phobject {
return $this->user;
}
public function getViewer() {
return $this->getUser();
}
public function setOAuthToken(
PhabricatorOAuthServerAccessToken $oauth_token) {
$this->oauthToken = $oauth_token;

View File

@@ -8,14 +8,21 @@ final class PhabricatorDaemonsSetupCheck extends PhabricatorSetupCheck {
protected function executeChecks() {
$task_daemon = id(new PhabricatorDaemonLogQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE)
->withDaemonClasses(array('PhabricatorTaskmasterDaemon'))
->setLimit(1)
->execute();
try {
$task_daemons = id(new PhabricatorDaemonLogQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE)
->withDaemonClasses(array('PhabricatorTaskmasterDaemon'))
->setLimit(1)
->execute();
if (!$task_daemon) {
$no_daemons = !$task_daemons;
} catch (Exception $ex) {
// Just skip this warning if the query fails for some reason.
$no_daemons = false;
}
if ($no_daemons) {
$doc_href = PhabricatorEnv::getDoclink('Managing Daemons with phd');
$summary = pht(

View File

@@ -322,6 +322,9 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck {
'directly supported. Prefixes and other strings may be customized with '.
'"translation.override".');
$phd_reason = pht(
'Use "bin/phd debug ..." to get a detailed daemon execution log.');
$ancient_config += array(
'phid.external-loaders' =>
pht(
@@ -539,6 +542,9 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck {
'phd.pid-directory' => pht(
'Phabricator daemons no longer use PID files.'),
'phd.trace' => $phd_reason,
'phd.verbose' => $phd_reason,
);
return $ancient_config;

View File

@@ -43,22 +43,6 @@ final class PhabricatorPHDConfigOptions
"configuration changes are picked up by the daemons ".
"automatically, but pool sizes can not be changed without a ".
"restart.")),
$this->newOption('phd.verbose', 'bool', false)
->setLocked(true)
->setBoolOptions(
array(
pht('Verbose mode'),
pht('Normal mode'),
))
->setSummary(pht("Launch daemons in 'verbose' mode by default."))
->setDescription(
pht(
"Launch daemons in 'verbose' mode by default. This creates a lot ".
"of output, but can help debug issues. Daemons launched in debug ".
"mode with '%s' are always launched in verbose mode. ".
"See also '%s'.",
'phd debug',
'phd.trace')),
$this->newOption('phd.user', 'string', null)
->setLocked(true)
->setSummary(pht('System user to run daemons as.'))
@@ -68,22 +52,6 @@ final class PhabricatorPHDConfigOptions
'user will own the working copies of any repositories that '.
'Phabricator imports or manages. This option is new and '.
'experimental.')),
$this->newOption('phd.trace', 'bool', false)
->setLocked(true)
->setBoolOptions(
array(
pht('Trace mode'),
pht('Normal mode'),
))
->setSummary(pht("Launch daemons in 'trace' mode by default."))
->setDescription(
pht(
"Launch daemons in 'trace' mode by default. This creates an ".
"ENORMOUS amount of output, but can help debug issues. Daemons ".
"launched in debug mode with '%s' are always launched in ".
"trace mode. See also '%s'.",
'phd debug',
'phd.verbose')),
$this->newOption('phd.garbage-collection', 'wild', array())
->setLocked(true)
->setLockedMessage(

View File

@@ -116,11 +116,11 @@ abstract class PhabricatorDaemonManagementWorkflow
$trace = PhutilArgumentParser::isTraceModeEnabled();
$flags = array();
if ($trace || PhabricatorEnv::getEnvConfig('phd.trace')) {
if ($trace) {
$flags[] = '--trace';
}
if ($debug || PhabricatorEnv::getEnvConfig('phd.verbose')) {
if ($debug) {
$flags[] = '--verbose';
}

View File

@@ -510,6 +510,11 @@ final class DifferentialRevisionViewController
->setLoadEntireGraph(true)
->loadGraph();
if (!$stack_graph->isEmpty()) {
// See PHI1900. The graph UI element now tries to figure out the correct
// height automatically, but currently can't in this case because the
// element is not visible when the page loads. Set an explicit height.
$stack_graph->setHeight(34);
$stack_table = $stack_graph->newGraphTable();
$parent_type = DifferentialRevisionDependsOnRevisionEdgeType::EDGECONST;

View File

@@ -36,7 +36,8 @@ final class PhabricatorDifferentialRebuildChangesetsWorkflow
throw new PhutilArgumentUsageException(
pht(
'Object "%s" specified by "--revision" must be a Differential '.
'revision.'));
'revision.',
$revision_identifier));
}
} else {
$revision = id(new DifferentialRevisionQuery())

View File

@@ -1053,14 +1053,30 @@ final class DifferentialChangesetParser extends Phobject {
$this->comments = id(new PHUIDiffInlineThreader())
->reorderAndThreadCommments($this->comments);
$old_max_display = 1;
foreach ($this->old as $old) {
if (isset($old['line'])) {
$old_max_display = $old['line'];
}
}
$new_max_display = 1;
foreach ($this->new as $new) {
if (isset($new['line'])) {
$new_max_display = $new['line'];
}
}
foreach ($this->comments as $comment) {
$final = $comment->getLineNumber() +
$comment->getLineLength();
$final = max(1, $final);
$display_line = $comment->getLineNumber() + $comment->getLineLength();
$display_line = max(1, $display_line);
if ($this->isCommentOnRightSideWhenDisplayed($comment)) {
$new_comments[$final][] = $comment;
$display_line = min($new_max_display, $display_line);
$new_comments[$display_line][] = $comment;
} else {
$old_comments[$final][] = $comment;
$display_line = min($old_max_display, $display_line);
$old_comments[$display_line][] = $comment;
}
}
}
@@ -1327,6 +1343,10 @@ final class DifferentialChangesetParser extends Phobject {
$not_covered = 0;
foreach ($this->new as $k => $new) {
if ($new === null) {
continue;
}
if (!$new['line']) {
continue;
}

View File

@@ -778,12 +778,19 @@ final class DifferentialRevisionQuery
*/
protected function shouldGroupQueryResultRows() {
$join_triggers = array_merge(
$this->pathIDs,
$this->ccs,
$this->reviewers);
if (count($this->pathIDs) > 1) {
return true;
}
if (count($join_triggers) > 1) {
if (count($this->ccs) > 1) {
return true;
}
if (count($this->reviewers) > 1) {
return true;
}
if (count($this->commitHashes) > 1) {
return true;
}

View File

@@ -83,7 +83,9 @@ final class DifferentialChangesetOneUpRenderer
$cells = array();
if ($is_old) {
if ($p['htype']) {
if (empty($p['oline'])) {
if ($p['htype'] === '\\') {
$class = 'comment';
} else if (empty($p['oline'])) {
$class = 'left old old-full';
} else {
$class = 'left old';
@@ -129,7 +131,9 @@ final class DifferentialChangesetOneUpRenderer
$cells[] = $no_coverage;
} else {
if ($p['htype']) {
if (empty($p['oline'])) {
if ($p['htype'] === '\\') {
$class = 'comment';
} else if (empty($p['oline'])) {
$class = 'right new new-full';
} else {
$class = 'right new';

View File

@@ -505,6 +505,8 @@ abstract class DifferentialChangesetRenderer extends Phobject {
$ospec['htype'] = $old[$ii]['type'];
if (isset($old_render[$ii])) {
$ospec['render'] = $old_render[$ii];
} else if ($ospec['htype'] === '\\') {
$ospec['render'] = $old[$ii]['text'];
}
}
@@ -514,6 +516,8 @@ abstract class DifferentialChangesetRenderer extends Phobject {
$nspec['htype'] = $new[$ii]['type'];
if (isset($new_render[$ii])) {
$nspec['render'] = $new_render[$ii];
} else if ($nspec['htype'] === '\\') {
$nspec['render'] = $new[$ii]['text'];
}
}

View File

@@ -747,9 +747,10 @@ final class DifferentialDiff
$prop->delete();
}
$viewstates = id(new DifferentialViewStateQuery())
$viewstate_query = id(new DifferentialViewStateQuery())
->setViewer($viewer)
->withObjectPHIDs(array($this->getPHID()));
$viewstates = new PhabricatorQueryIterator($viewstate_query);
foreach ($viewstates as $viewstate) {
$viewstate->delete();
}

View File

@@ -1033,9 +1033,10 @@ final class DifferentialRevision extends DifferentialDAO
$dummy_path->getTableName(),
$this->getID());
$viewstates = id(new DifferentialViewStateQuery())
$viewstate_query = id(new DifferentialViewStateQuery())
->setViewer($viewer)
->withObjectPHIDs(array($this->getPHID()));
$viewstates = new PhabricatorQueryIterator($viewstate_query);
foreach ($viewstates as $viewstate) {
$viewstate->delete();
}

View File

@@ -6,12 +6,15 @@ final class DifferentialRevisionAbandonTransaction
const TRANSACTIONTYPE = 'differential.revision.abandon';
const ACTIONKEY = 'abandon';
protected function getRevisionActionLabel() {
protected function getRevisionActionLabel(
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return pht('Abandon Revision');
}
protected function getRevisionActionDescription(
DifferentialRevision $revision) {
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return pht('This revision will be abandoned and closed.');
}

View File

@@ -6,12 +6,15 @@ final class DifferentialRevisionAcceptTransaction
const TRANSACTIONTYPE = 'differential.revision.accept';
const ACTIONKEY = 'accept';
protected function getRevisionActionLabel() {
protected function getRevisionActionLabel(
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return pht('Accept Revision');
}
protected function getRevisionActionDescription(
DifferentialRevision $revision) {
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return pht('These changes will be approved.');
}

View File

@@ -17,7 +17,9 @@ abstract class DifferentialRevisionActionTransaction
}
abstract protected function validateAction($object, PhabricatorUser $viewer);
abstract protected function getRevisionActionLabel();
abstract protected function getRevisionActionLabel(
DifferentialRevision $revision,
PhabricatorUser $viewer);
protected function validateOptionValue($object, $actor, array $value) {
return null;
@@ -53,15 +55,23 @@ abstract class DifferentialRevisionActionTransaction
}
protected function getRevisionActionDescription(
DifferentialRevision $revision) {
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return null;
}
protected function getRevisionActionSubmitButtonText(
DifferentialRevision $revision) {
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return null;
}
protected function getRevisionActionMetadata(
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return array();
}
public static function loadAllActions() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
@@ -105,17 +115,19 @@ abstract class DifferentialRevisionActionTransaction
->setValue(true);
if ($this->isActionAvailable($revision, $viewer)) {
$label = $this->getRevisionActionLabel();
$label = $this->getRevisionActionLabel($revision, $viewer);
if ($label !== null) {
$field->setCommentActionLabel($label);
$description = $this->getRevisionActionDescription($revision);
$description = $this->getRevisionActionDescription($revision, $viewer);
$field->setActionDescription($description);
$group_key = $this->getRevisionActionGroupKey();
$field->setCommentActionGroupKey($group_key);
$button_text = $this->getRevisionActionSubmitButtonText($revision);
$button_text = $this->getRevisionActionSubmitButtonText(
$revision,
$viewer);
$field->setActionSubmitButtonText($button_text);
// Currently, every revision action conflicts with every other
@@ -144,6 +156,11 @@ abstract class DifferentialRevisionActionTransaction
$field->setOptions($options);
$field->setValue($value);
}
$metadata = $this->getRevisionActionMetadata($revision, $viewer);
foreach ($metadata as $metadata_key => $metadata_value) {
$field->setMetadataValue($metadata_key, $metadata_value);
}
}
}

View File

@@ -6,12 +6,15 @@ final class DifferentialRevisionCloseTransaction
const TRANSACTIONTYPE = 'differential.revision.close';
const ACTIONKEY = 'close';
protected function getRevisionActionLabel() {
protected function getRevisionActionLabel(
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return pht('Close Revision');
}
protected function getRevisionActionDescription(
DifferentialRevision $revision) {
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return pht('This revision will be closed.');
}

View File

@@ -6,12 +6,15 @@ final class DifferentialRevisionCommandeerTransaction
const TRANSACTIONTYPE = 'differential.revision.commandeer';
const ACTIONKEY = 'commandeer';
protected function getRevisionActionLabel() {
protected function getRevisionActionLabel(
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return pht('Commandeer Revision');
}
protected function getRevisionActionDescription(
DifferentialRevision $revision) {
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return pht('You will take control of this revision and become its author.');
}

View File

@@ -6,12 +6,15 @@ final class DifferentialRevisionPlanChangesTransaction
const TRANSACTIONTYPE = 'differential.revision.plan';
const ACTIONKEY = 'plan-changes';
protected function getRevisionActionLabel() {
protected function getRevisionActionLabel(
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return pht('Plan Changes');
}
protected function getRevisionActionDescription(
DifferentialRevision $revision) {
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return pht(
'This revision will be removed from review queues until it is revised.');
}

View File

@@ -6,12 +6,15 @@ final class DifferentialRevisionReclaimTransaction
const TRANSACTIONTYPE = 'differential.revision.reclaim';
const ACTIONKEY = 'reclaim';
protected function getRevisionActionLabel() {
protected function getRevisionActionLabel(
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return pht('Reclaim Revision');
}
protected function getRevisionActionDescription(
DifferentialRevision $revision) {
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return pht('This revision will be reclaimed and reopened.');
}

View File

@@ -6,12 +6,15 @@ final class DifferentialRevisionRejectTransaction
const TRANSACTIONTYPE = 'differential.revision.reject';
const ACTIONKEY = 'reject';
protected function getRevisionActionLabel() {
protected function getRevisionActionLabel(
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return pht('Request Changes');
}
protected function getRevisionActionDescription(
DifferentialRevision $revision) {
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return pht('This revision will be returned to the author for updates.');
}

View File

@@ -6,12 +6,15 @@ final class DifferentialRevisionReopenTransaction
const TRANSACTIONTYPE = 'differential.revision.reopen';
const ACTIONKEY = 'reopen';
protected function getRevisionActionLabel() {
protected function getRevisionActionLabel(
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return pht('Reopen Revision');
}
protected function getRevisionActionDescription(
DifferentialRevision $revision) {
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return pht('This revision will be reopened for review.');
}

View File

@@ -84,7 +84,8 @@ final class DifferentialRevisionRepositoryTransaction
$errors[] = $this->newInvalidError(
pht(
'Repository "%s" is not a valid repository, or you do not have '.
'permission to view it.'),
'permission to view it.',
$new_value),
$xaction);
}
}

View File

@@ -6,21 +6,63 @@ final class DifferentialRevisionRequestReviewTransaction
const TRANSACTIONTYPE = 'differential.revision.request';
const ACTIONKEY = 'request-review';
protected function getRevisionActionLabel() {
const SOURCE_HARBORMASTER = 'harbormaster';
const SOURCE_AUTHOR = 'author';
const SOURCE_VIEWER = 'viewer';
protected function getRevisionActionLabel(
DifferentialRevision $revision,
PhabricatorUser $viewer) {
// See PHI1810. Allow non-authors to "Request Review" on draft revisions
// to promote them out of the draft state. This smoothes over the workflow
// where an author asks for review of an urgent change but has not used
// "Request Review" to skip builds.
if ($revision->isDraft()) {
if (!$this->isViewerRevisionAuthor($revision, $viewer)) {
return pht('Begin Review Now');
}
}
return pht('Request Review');
}
protected function getRevisionActionDescription(
DifferentialRevision $revision) {
DifferentialRevision $revision,
PhabricatorUser $viewer) {
if ($revision->isDraft()) {
return pht('This revision will be submitted to reviewers for feedback.');
if (!$this->isViewerRevisionAuthor($revision, $viewer)) {
return pht(
'This revision will be moved out of the draft state so you can '.
'review it immediately.');
} else {
return pht(
'This revision will be submitted to reviewers for feedback.');
}
} else {
return pht('This revision will be returned to reviewers for feedback.');
}
}
protected function getRevisionActionMetadata(
DifferentialRevision $revision,
PhabricatorUser $viewer) {
$map = array();
if ($revision->isDraft()) {
$action_source = $this->getActorSourceType(
$revision,
$viewer);
$map['promotion.source'] = $action_source;
}
return $map;
}
protected function getRevisionActionSubmitButtonText(
DifferentialRevision $revision) {
DifferentialRevision $revision,
PhabricatorUser $viewer) {
// See PHI975. When the action stack will promote the revision out of
// draft, change the button text from "Submit Quietly".
@@ -72,29 +114,43 @@ final class DifferentialRevisionRequestReviewTransaction
'revisions.'));
}
// When revisions automatically promote out of "Draft" after builds finish,
// the viewer may be acting as the Harbormaster application.
if (!$viewer->isOmnipotent()) {
if (!$this->isViewerRevisionAuthor($object, $viewer)) {
throw new Exception(
pht(
'You can not request review of this revision because you are not '.
'the author of the revision.'));
}
}
$this->getActorSourceType($object, $viewer);
}
public function getTitle() {
return pht(
'%s requested review of this revision.',
$this->renderAuthor());
$source = $this->getDraftPromotionSource();
switch ($source) {
case self::SOURCE_HARBORMASTER:
case self::SOURCE_VIEWER:
case self::SOURCE_AUTHOR:
return pht(
'%s published this revision for review.',
$this->renderAuthor());
default:
return pht(
'%s requested review of this revision.',
$this->renderAuthor());
}
}
public function getTitleForFeed() {
return pht(
'%s requested review of %s.',
$this->renderAuthor(),
$this->renderObject());
$source = $this->getDraftPromotionSource();
switch ($source) {
case self::SOURCE_HARBORMASTER:
case self::SOURCE_VIEWER:
case self::SOURCE_AUTHOR:
return pht(
'%s published %s for review.',
$this->renderAuthor(),
$this->renderObject());
default:
return pht(
'%s requested review of %s.',
$this->renderAuthor(),
$this->renderObject());
}
}
public function getTransactionTypeForConduit($xaction) {
@@ -105,4 +161,36 @@ final class DifferentialRevisionRequestReviewTransaction
return array();
}
private function getDraftPromotionSource() {
return $this->getMetadataValue('promotion.source');
}
private function getActorSourceType(
DifferentialRevision $revision,
PhabricatorUser $viewer) {
$is_harbormaster = $viewer->isOmnipotent();
$is_author = $this->isViewerRevisionAuthor($revision, $viewer);
$is_draft = $revision->isDraft();
if ($is_harbormaster) {
// When revisions automatically promote out of "Draft" after builds
// finish, the viewer may be acting as the Harbormaster application.
$source = self::SOURCE_HARBORMASTER;
} else if ($is_author) {
$source = self::SOURCE_AUTHOR;
} else if ($is_draft) {
// Non-authors are allowed to "Request Review" on draft revisions, to
// force them into review immediately.
$source = self::SOURCE_VIEWER;
} else {
throw new Exception(
pht(
'You can not request review of this revision because you are not '.
'the author of the revision and it is not currently a draft.'));
}
return $source;
}
}

View File

@@ -6,12 +6,15 @@ final class DifferentialRevisionResignTransaction
const TRANSACTIONTYPE = 'differential.revision.resign';
const ACTIONKEY = 'resign';
protected function getRevisionActionLabel() {
protected function getRevisionActionLabel(
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return pht('Resign as Reviewer');
}
protected function getRevisionActionDescription(
DifferentialRevision $revision) {
DifferentialRevision $revision,
PhabricatorUser $viewer) {
return pht('You will resign as a reviewer for this change.');
}

View File

@@ -52,7 +52,6 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication {
'change/(?P<dblob>.*)' => 'DiffusionChangeController',
'clone/' => 'DiffusionCloneController',
'history/(?P<dblob>.*)' => 'DiffusionHistoryController',
'graph/(?P<dblob>.*)' => 'DiffusionGraphController',
'browse/(?P<dblob>.*)' => 'DiffusionBrowseController',
'document/(?P<dblob>.*)'
=> 'DiffusionDocumentController',

View File

@@ -0,0 +1,58 @@
<?php
final class DiffusionInternalCommitSearchConduitAPIMethod
extends PhabricatorSearchEngineAPIMethod {
public function getAPIMethodName() {
return 'internal.commit.search';
}
public function newSearchEngine() {
return new DiffusionInternalCommitSearchEngine();
}
public function getMethodSummary() {
return pht('Read raw information about commits.');
}
protected function newConduitCallProxyClient(ConduitAPIRequest $request) {
$viewer = $request->getViewer();
$constraints = $request->getValue('constraints');
if (is_array($constraints)) {
$repository_phids = idx($constraints, 'repositoryPHIDs');
} else {
$repository_phids = array();
}
$repository_phid = null;
if (is_array($repository_phids)) {
if (phutil_is_natural_list($repository_phids)) {
if (count($repository_phids) === 1) {
$value = head($repository_phids);
if (is_string($value)) {
$repository_phid = $value;
}
}
}
}
if ($repository_phid === null) {
throw new Exception(
pht(
'This internal method must be invoked with a "repositoryPHIDs" '.
'constraint with exactly one value.'));
}
$repository = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->withPHIDs(array($repository_phid))
->executeOne();
if (!$repository) {
return array();
}
return $repository->newConduitClientForRequest($request);
}
}

View File

@@ -38,7 +38,6 @@ final class DiffusionQueryCommitsConduitAPIMethod
protected function execute(ConduitAPIRequest $request) {
$need_messages = $request->getValue('needMessages');
$bypass_cache = $request->getValue('bypassCache');
$viewer = $request->getUser();
$query = id(new DiffusionCommitQuery())
@@ -53,12 +52,6 @@ final class DiffusionQueryCommitsConduitAPIMethod
->executeOne();
if ($repository) {
$query->withRepository($repository);
if ($bypass_cache) {
id(new DiffusionRepositoryClusterEngine())
->setViewer($viewer)
->setRepository($repository)
->synchronizeWorkingCopyBeforeRead();
}
}
}
@@ -96,48 +89,22 @@ final class DiffusionQueryCommitsConduitAPIMethod
'repositoryPHID' => $commit->getRepository()->getPHID(),
'identifier' => $commit->getCommitIdentifier(),
'epoch' => $commit->getEpoch(),
'authorEpoch' => $commit_data->getCommitDetail('authorEpoch'),
'authorEpoch' => $commit_data->getAuthorEpoch(),
'uri' => $uri,
'isImporting' => !$commit->isImported(),
'summary' => $commit->getSummary(),
'authorPHID' => $commit->getAuthorPHID(),
'committerPHID' => $commit_data->getCommitDetail('committerPHID'),
'author' => $commit_data->getAuthorName(),
'authorName' => $commit_data->getCommitDetail('authorName'),
'authorEmail' => $commit_data->getCommitDetail('authorEmail'),
'committer' => $commit_data->getCommitDetail('committer'),
'committerName' => $commit_data->getCommitDetail('committerName'),
'committerEmail' => $commit_data->getCommitDetail('committerEmail'),
'author' => $commit_data->getAuthorString(),
'authorName' => $commit_data->getAuthorDisplayName(),
'authorEmail' => $commit_data->getAuthorEmail(),
'committer' => $commit_data->getCommitterString(),
'committerName' => $commit_data->getCommitterDisplayName(),
'committerEmail' => $commit_data->getCommitterEmail(),
'hashes' => array(),
);
if ($bypass_cache) {
$lowlevel_commitref = id(new DiffusionLowLevelCommitQuery())
->setRepository($commit->getRepository())
->withIdentifier($commit->getCommitIdentifier())
->execute();
$dict['authorEpoch'] = $lowlevel_commitref->getAuthorEpoch();
$dict['author'] = $lowlevel_commitref->getAuthor();
$dict['authorName'] = $lowlevel_commitref->getAuthorName();
$dict['authorEmail'] = $lowlevel_commitref->getAuthorEmail();
$dict['committer'] = $lowlevel_commitref->getCommitter();
$dict['committerName'] = $lowlevel_commitref->getCommitterName();
$dict['committerEmail'] = $lowlevel_commitref->getCommitterEmail();
if ($need_messages) {
$dict['message'] = $lowlevel_commitref->getMessage();
}
foreach ($lowlevel_commitref->getHashes() as $hash) {
$dict['hashes'][] = array(
'type' => $hash->getHashType(),
'value' => $hash->getHashValue(),
);
}
}
if ($need_messages && !$bypass_cache) {
if ($need_messages) {
$dict['message'] = $commit_data->getCommitMessage();
}

View File

@@ -85,6 +85,35 @@ abstract class DiffusionQueryConduitAPIMethod
* should occur after @{method:getResult}, like formatting a timestamp.
*/
final protected function execute(ConduitAPIRequest $request) {
$drequest = $this->getDiffusionRequest();
// We pass this flag on to prevent proxying of any other Conduit calls
// which we need to make in order to respond to this one. Although we
// could safely proxy them, we take a big performance hit in the common
// case, and doing more proxying wouldn't exercise any additional code so
// we wouldn't gain a testability/predictability benefit.
$is_cluster_request = $request->getIsClusterRequest();
$drequest->setIsClusterRequest($is_cluster_request);
$viewer = $request->getViewer();
$repository = $drequest->getRepository();
// TODO: Allow web UI queries opt out of this if they don't care about
// fetching the most up-to-date data? Synchronization can be slow, and a
// lot of web reads are probably fine if they're a few seconds out of
// date.
id(new DiffusionRepositoryClusterEngine())
->setViewer($viewer)
->setRepository($repository)
->synchronizeWorkingCopyBeforeRead();
return $this->getResult($request);
}
protected function newConduitCallProxyClient(ConduitAPIRequest $request) {
$viewer = $request->getViewer();
$identifier = $request->getValue('repository');
if ($identifier === null) {
$identifier = $request->getValue('callsign');
@@ -92,7 +121,7 @@ abstract class DiffusionQueryConduitAPIMethod
$drequest = DiffusionRequest::newFromDictionary(
array(
'user' => $request->getUser(),
'user' => $viewer,
'repository' => $identifier,
'branch' => $request->getValue('branch'),
'path' => $request->getValue('path'),
@@ -106,46 +135,16 @@ abstract class DiffusionQueryConduitAPIMethod
$identifier));
}
// Figure out whether we're going to handle this request on this device,
// or proxy it to another node in the cluster.
// If this is a cluster request and we need to proxy, we'll explode here
// to prevent infinite recursion.
$is_cluster_request = $request->getIsClusterRequest();
$viewer = $request->getUser();
$repository = $drequest->getRepository();
$client = $repository->newConduitClient(
$viewer,
$is_cluster_request);
$client = $repository->newConduitClientForRequest($request);
if ($client) {
// We're proxying, so just make an intracluster call.
return $client->callMethodSynchronous(
$this->getAPIMethodName(),
$request->getAllParameters());
} else {
// We pass this flag on to prevent proxying of any other Conduit calls
// which we need to make in order to respond to this one. Although we
// could safely proxy them, we take a big performance hit in the common
// case, and doing more proxying wouldn't exercise any additional code so
// we wouldn't gain a testability/predictability benefit.
$drequest->setIsClusterRequest($is_cluster_request);
$this->setDiffusionRequest($drequest);
// TODO: Allow web UI queries opt out of this if they don't care about
// fetching the most up-to-date data? Synchronization can be slow, and a
// lot of web reads are probably fine if they're a few seconds out of
// date.
id(new DiffusionRepositoryClusterEngine())
->setViewer($viewer)
->setRepository($repository)
->synchronizeWorkingCopyBeforeRead();
return $this->getResult($request);
return $client;
}
$this->setDiffusionRequest($drequest);
return null;
}
protected function getResult(ConduitAPIRequest $request) {

View File

@@ -292,7 +292,6 @@ final class DiffusionBrowseController extends DiffusionController {
$empty_result = null;
$browse_panel = null;
$branch_panel = null;
if (!$results->isValidResults()) {
$empty_result = new DiffusionEmptyResultView();
$empty_result->setDiffusionRequest($drequest);
@@ -328,12 +327,6 @@ final class DiffusionBrowseController extends DiffusionController {
->setTable($browse_table)
->addClass('diffusion-mobile-view')
->setPager($pager);
$path = $drequest->getPath();
$is_branch = (!strlen($path) && $repository->supportsBranchComparison());
if ($is_branch) {
$branch_panel = $this->buildBranchTable();
}
}
$open_revisions = $this->buildOpenRevisions();
@@ -359,7 +352,6 @@ final class DiffusionBrowseController extends DiffusionController {
->setFooter(
array(
$bar,
$branch_panel,
$empty_result,
$browse_panel,
$open_revisions,
@@ -1074,59 +1066,4 @@ final class DiffusionBrowseController extends DiffusionController {
return $file;
}
private function buildBranchTable() {
$viewer = $this->getViewer();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$branch = $drequest->getBranch();
$default_branch = $repository->getDefaultBranch();
if ($branch === $default_branch) {
return null;
}
$pager = id(new PHUIPagerView())
->setPageSize(10);
try {
$results = $this->callConduitWithDiffusionRequest(
'diffusion.historyquery',
array(
'commit' => $branch,
'against' => $default_branch,
'path' => $drequest->getPath(),
'offset' => $pager->getOffset(),
'limit' => $pager->getPageSize() + 1,
));
} catch (Exception $ex) {
return null;
}
$history = DiffusionPathChange::newFromConduit($results['pathChanges']);
$history = $pager->sliceResults($history);
if (!$history) {
return null;
}
$history_table = id(new DiffusionHistoryTableView())
->setViewer($viewer)
->setDiffusionRequest($drequest)
->setHistory($history)
->setParents($results['parents'])
->setFilterParents(true)
->setIsHead(true)
->setIsTail(!$pager->getHasMorePages());
$header = id(new PHUIHeaderView())
->setHeader(pht('%s vs %s', $branch, $default_branch));
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->addClass('diffusion-mobile-view')
->setTable($history_table);
}
}

View File

@@ -90,10 +90,9 @@ final class DiffusionCommitController extends DiffusionController {
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->appendChild($warning_message);
$list = id(new DiffusionCommitListView())
$list = id(new DiffusionCommitGraphView())
->setViewer($viewer)
->setCommits($commits)
->setNoDataString(pht('No recent commits.'));
->setCommits($commits);
$crumbs->addTextCrumb(pht('Ambiguous Commit'));
@@ -183,7 +182,6 @@ final class DiffusionCommitController extends DiffusionController {
}
$curtain = $this->buildCurtain($commit, $repository);
$subheader = $this->buildSubheaderView($commit, $commit_data);
$details = $this->buildPropertyListView(
$commit,
$commit_data,
@@ -483,7 +481,6 @@ final class DiffusionCommitController extends DiffusionController {
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setSubheader($subheader)
->setCurtain($curtain)
->setMainColumn(
array(
@@ -625,33 +622,50 @@ final class DiffusionCommitController extends DiffusionController {
}
}
$author_epoch = $data->getCommitDetail('authorEpoch');
$provenance_list = new PHUIStatusListView();
$committed_info = id(new PHUIStatusItemView())
->setNote(phabricator_datetime($commit->getEpoch(), $viewer))
->setTarget($commit->renderAnyCommitter($viewer, $handles));
$author_view = $commit->newCommitAuthorView($viewer);
if ($author_view) {
$author_date = $data->getAuthorEpoch();
$author_date = phabricator_datetime($author_date, $viewer);
$committed_list = new PHUIStatusListView();
$committed_list->addItem($committed_info);
$view->addProperty(
pht('Committed'),
$committed_list);
$provenance_list->addItem(
id(new PHUIStatusItemView())
->setTarget($author_view)
->setNote(pht('Authored on %s', $author_date)));
}
if (!$commit->isAuthorSameAsCommitter()) {
$committer_view = $commit->newCommitCommitterView($viewer);
if ($committer_view) {
$committer_date = $commit->getEpoch();
$committer_date = phabricator_datetime($committer_date, $viewer);
$provenance_list->addItem(
id(new PHUIStatusItemView())
->setTarget($committer_view)
->setNote(pht('Committed on %s', $committer_date)));
}
}
if ($push_logs) {
$pushed_list = new PHUIStatusListView();
foreach ($push_logs as $push_log) {
$pushed_item = id(new PHUIStatusItemView())
->setTarget($handles[$push_log->getPusherPHID()]->renderLink())
->setNote(phabricator_datetime($push_log->getEpoch(), $viewer));
$pushed_list->addItem($pushed_item);
}
$pusher_date = $push_log->getEpoch();
$pusher_date = phabricator_datetime($pusher_date, $viewer);
$view->addProperty(
pht('Pushed'),
$pushed_list);
$pusher_view = $handles[$push_log->getPusherPHID()]->renderLink();
$provenance_list->addItem(
id(new PHUIStatusItemView())
->setTarget($pusher_view)
->setNote(pht('Pushed on %s', $pusher_date)));
}
}
$view->addProperty(pht('Provenance'), $provenance_list);
$reviewer_phid = $data->getCommitDetail('reviewerPHID');
if ($reviewer_phid) {
$view->addProperty(
@@ -743,52 +757,6 @@ final class DiffusionCommitController extends DiffusionController {
return $view;
}
private function buildSubheaderView(
PhabricatorRepositoryCommit $commit,
PhabricatorRepositoryCommitData $data) {
$viewer = $this->getViewer();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
if ($repository->isSVN()) {
return null;
}
$author_phid = $commit->getAuthorDisplayPHID();
$author_name = $data->getAuthorName();
$author_epoch = $data->getCommitDetail('authorEpoch');
$date = null;
if ($author_epoch !== null) {
$date = phabricator_datetime($author_epoch, $viewer);
}
if ($author_phid) {
$handles = $viewer->loadHandles(array($author_phid));
$image_uri = $handles[$author_phid]->getImageURI();
$image_href = $handles[$author_phid]->getURI();
$author = $handles[$author_phid]->renderLink();
} else if (strlen($author_name)) {
$author = $author_name;
$image_uri = null;
$image_href = null;
} else {
return null;
}
$author = phutil_tag('strong', array(), $author);
if ($date) {
$content = pht('Authored by %s on %s.', $author, $date);
} else {
$content = pht('Authored by %s.', $author);
}
return id(new PHUIHeadThingView())
->setImage($image_uri)
->setImageHref($image_href)
->setContent($content);
}
private function buildComments(PhabricatorRepositoryCommit $commit) {
$timeline = $this->buildTransactionTimeline(
$commit,
@@ -843,15 +811,15 @@ final class DiffusionCommitController extends DiffusionController {
new PhutilNumber($limit)));
}
$history_table = id(new DiffusionHistoryTableView())
->setUser($viewer)
$commit_list = id(new DiffusionCommitGraphView())
->setViewer($viewer)
->setDiffusionRequest($drequest)
->setHistory($merges);
$panel = id(new PHUIObjectBoxView())
->setHeaderText(pht('Merged Changes'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($history_table);
->setObjectList($commit_list->newObjectItemListView());
if ($caption) {
$panel->setInfoView($caption);
}

View File

@@ -285,7 +285,6 @@ final class DiffusionCompareController extends DiffusionController {
$request = $this->getRequest();
$viewer = $this->getViewer();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
if (!$history) {
return $this->renderStatusMessage(
@@ -296,8 +295,8 @@ final class DiffusionCompareController extends DiffusionController {
phutil_tag('strong', array(), $against_ref)));
}
$history_table = id(new DiffusionHistoryTableView())
->setUser($viewer)
$history_view = id(new DiffusionCommitGraphView())
->setViewer($viewer)
->setDiffusionRequest($drequest)
->setHistory($history)
->setParents($results['parents'])
@@ -305,15 +304,6 @@ final class DiffusionCompareController extends DiffusionController {
->setIsHead(!$pager->getOffset())
->setIsTail(!$pager->getHasMorePages());
$header = id(new PHUIHeaderView())
->setHeader(pht('Commits'));
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($history_table)
->addClass('diffusion-mobile-view')
->setPager($pager);
return $history_view;
}
}

View File

@@ -210,9 +210,6 @@ abstract class DiffusionController extends PhabricatorController {
case 'history':
$view_name = pht('History');
break;
case 'graph':
$view_name = pht('Graph');
break;
case 'browse':
$view_name = pht('Browse');
break;
@@ -553,17 +550,6 @@ abstract class DiffusionController extends PhabricatorController {
)))
->setSelected($key == 'history'));
$view->addMenuItem(
id(new PHUIListItemView())
->setKey('graph')
->setName(pht('Graph'))
->setIcon('fa-code-fork')
->setHref($drequest->generateURI(
array(
'action' => 'graph',
)))
->setSelected($key == 'graph'));
return $view;
}

View File

@@ -1,110 +0,0 @@
<?php
final class DiffusionGraphController extends DiffusionController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$response = $this->loadDiffusionContext();
if ($response) {
return $response;
}
require_celerity_resource('diffusion-css');
$viewer = $this->getViewer();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$pager = id(new PHUIPagerView())
->readFromRequest($request);
$params = array(
'commit' => $drequest->getCommit(),
'path' => $drequest->getPath(),
'offset' => $pager->getOffset(),
'limit' => $pager->getPageSize() + 1,
);
$history_results = $this->callConduitWithDiffusionRequest(
'diffusion.historyquery',
$params);
$history = DiffusionPathChange::newFromConduit(
$history_results['pathChanges']);
$history = $pager->sliceResults($history);
$graph = id(new DiffusionHistoryTableView())
->setViewer($viewer)
->setDiffusionRequest($drequest)
->setHistory($history);
$show_graph = !strlen($drequest->getPath());
if ($show_graph) {
$graph->setParents($history_results['parents']);
$graph->setIsHead(!$pager->getOffset());
$graph->setIsTail(!$pager->getHasMorePages());
}
$header = $this->buildHeader($drequest);
$crumbs = $this->buildCrumbs(
array(
'branch' => true,
'path' => true,
'view' => 'graph',
));
$crumbs->setBorder(true);
$title = array(
pht('Graph'),
$repository->getDisplayName(),
);
$graph_view = id(new PHUIObjectBoxView())
->setHeaderText(pht('History Graph'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($graph)
->addClass('diffusion-mobile-view')
->setPager($pager);
$tabs = $this->buildTabsView('graph');
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setTabs($tabs)
->setFooter($graph_view);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function buildHeader(DiffusionRequest $drequest) {
$viewer = $this->getViewer();
$repository = $drequest->getRepository();
$no_path = !strlen($drequest->getPath());
if ($no_path) {
$header_text = pht('Graph');
} else {
$header_text = $this->renderPathLinks($drequest, $mode = 'history');
}
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader($header_text)
->setHeaderIcon('fa-code-fork');
if (!$repository->isSVN()) {
$branch_tag = $this->renderBranchTag($drequest);
$header->addTag($branch_tag);
}
return $header;
}
}

View File

@@ -35,11 +35,29 @@ final class DiffusionHistoryController extends DiffusionController {
$history = $pager->sliceResults($history);
$history_list = id(new DiffusionHistoryListView())
$history_list = id(new DiffusionCommitGraphView())
->setViewer($viewer)
->setDiffusionRequest($drequest)
->setHistory($history);
// NOTE: If we have a path (like "src/"), many nodes in the graph are
// likely to be missing (since the path wasn't touched by those commits).
// If we draw the graph, commits will often appear to be unrelated because
// intermediate nodes are omitted. Just drop the graph.
// The ideal behavior would be to load the entire graph and then connect
// ancestors appropriately, but this would currrently be prohibitively
// expensive in the general case.
$show_graph = !strlen($drequest->getPath());
if ($show_graph) {
$history_list
->setParents($history_results['parents'])
->setIsHead(!$pager->getOffset())
->setIsTail(!$pager->getHasMorePages());
}
$header = $this->buildHeader($drequest);
$crumbs = $this->buildCrumbs(

View File

@@ -53,14 +53,6 @@ final class DiffusionLastModifiedController extends DiffusionController {
}
}
$phids = array();
foreach ($commits as $commit) {
$phids[] = $commit->getCommitterDisplayPHID();
$phids[] = $commit->getAuthorDisplayPHID();
}
$phids = array_filter($phids);
$handles = $this->loadViewerHandles($phids);
$branch = $drequest->loadBranch();
if ($branch && $commits) {
$lint_query = id(new DiffusionLintCountQuery())
@@ -83,7 +75,6 @@ final class DiffusionLastModifiedController extends DiffusionController {
$output[$path] = $this->renderColumns(
$prequest,
$handles,
$commit,
idx($lint, $path));
}
@@ -93,11 +84,9 @@ final class DiffusionLastModifiedController extends DiffusionController {
private function renderColumns(
DiffusionRequest $drequest,
array $handles,
PhabricatorRepositoryCommit $commit = null,
$lint = null) {
assert_instances_of($handles, 'PhabricatorObjectHandle');
$viewer = $this->getRequest()->getUser();
$viewer = $this->getViewer();
if ($commit) {
$epoch = $commit->getEpoch();
@@ -110,13 +99,6 @@ final class DiffusionLastModifiedController extends DiffusionController {
$date = '';
}
$author = $commit->renderAuthor($viewer, $handles);
$committer = $commit->renderCommitter($viewer, $handles);
if ($author != $committer) {
$author = hsprintf('%s/%s', $author, $committer);
}
$data = $commit->getCommitData();
$details = DiffusionView::linkDetail(
$drequest->getRepository(),
@@ -124,11 +106,9 @@ final class DiffusionLastModifiedController extends DiffusionController {
$data->getSummary());
$details = AphrontTableView::renderSingleDisplayLine($details);
$return = array(
'commit' => $modified,
'date' => $date,
'author' => $author,
'details' => $details,
);

View File

@@ -299,16 +299,10 @@ final class DiffusionRepositoryController extends DiffusionController {
$handles,
$browse_pager);
$content[] = $this->buildHistoryTable(
$history_results,
$history,
$history_exception);
if ($readme) {
$content[] = $readme;
}
try {
$branch_button = $this->buildBranchList($drequest);
$this->branchButton = $branch_button;
@@ -428,51 +422,6 @@ final class DiffusionRepositoryController extends DiffusionController {
return null;
}
private function buildHistoryTable(
$history_results,
$history,
$history_exception) {
$request = $this->getRequest();
$viewer = $request->getUser();
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
if ($history_exception) {
if ($repository->isImporting()) {
return $this->renderStatusMessage(
pht('Still Importing...'),
pht(
'This repository is still importing. History is not yet '.
'available.'));
} else {
return $this->renderStatusMessage(
pht('Unable to Retrieve History'),
$history_exception->getMessage());
}
}
$history_table = id(new DiffusionHistoryTableView())
->setUser($viewer)
->setDiffusionRequest($drequest)
->setHistory($history)
->setIsHead(true);
if ($history_results) {
$history_table->setParents($history_results['parents']);
}
$panel = id(new PHUIObjectBoxView())
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->addClass('diffusion-mobile-view');
$header = id(new PHUIHeaderView())
->setHeader(pht('Recent Commits'));
$panel->setHeader($header);
$panel->setTable($history_table);
return $panel;
}
private function buildBranchList(DiffusionRequest $drequest) {
$viewer = $this->getViewer();

View File

@@ -922,18 +922,26 @@ final class DiffusionServeController extends DiffusionController {
// This is a pretty funky fix: it would be nice to more precisely detect
// that a request is a `--depth N` clone request, but we don't have any code
// to decode protocol frames yet. Instead, look for reasonable evidence
// in the error and output that we're looking at a `--depth` clone.
// in the output that we're looking at a `--depth` clone.
// For evidence this isn't completely crazy, see:
// https://github.com/schacon/grack/pull/7
// A valid x-git-upload-pack-result response during packfile negotiation
// should end with a flush packet ("0000"). As long as that packet
// terminates the response body in the response, we'll assume the response
// is correct and complete.
// See https://git-scm.com/docs/pack-protocol#_packfile_negotiation
$stdout_regexp = '(^Content-Type: application/x-git-upload-pack-result)m';
$stderr_regexp = '(The remote end hung up unexpectedly)';
$has_pack = preg_match($stdout_regexp, $stdout);
$is_hangup = preg_match($stderr_regexp, $stderr);
return $has_pack && $is_hangup;
if (strlen($stdout) >= 4) {
$has_flush_packet = (substr($stdout, -4) === "0000");
} else {
$has_flush_packet = false;
}
return ($has_pack && $has_flush_packet);
}
private function getCommonEnvironment(PhabricatorUser $viewer) {

View File

@@ -34,4 +34,21 @@ final class DiffusionCommitHash extends Phobject {
}
return $hash_objects;
}
public static function newFromDictionary(array $map) {
$hash_type = idx($map, 'type');
$hash_value = idx($map, 'value');
return id(new self())
->setHashType($hash_type)
->setHashValue($hash_value);
}
public function newDictionary() {
return array(
'type' => $this->hashType,
'value' => $this->hashValue,
);
}
}

View File

@@ -10,28 +10,48 @@ final class DiffusionCommitRef extends Phobject {
private $committerEmail;
private $hashes = array();
public static function newFromConduitResult(array $result) {
$ref = id(new DiffusionCommitRef())
->setAuthorEpoch(idx($result, 'authorEpoch'))
->setCommitterEmail(idx($result, 'committerEmail'))
->setCommitterName(idx($result, 'committerName'))
->setAuthorEmail(idx($result, 'authorEmail'))
->setAuthorName(idx($result, 'authorName'))
->setMessage(idx($result, 'message'));
public function newDictionary() {
$hashes = $this->getHashes();
$hashes = mpull($hashes, 'newDictionary');
$hashes = array_values($hashes);
$hashes = array();
foreach (idx($result, 'hashes', array()) as $hash_result) {
$hashes[] = id(new DiffusionCommitHash())
->setHashType(idx($hash_result, 'type'))
->setHashValue(idx($hash_result, 'value'));
return array(
'authorEpoch' => $this->authorEpoch,
'authorName' => $this->authorName,
'authorEmail' => $this->authorEmail,
'committerName' => $this->committerName,
'committerEmail' => $this->committerEmail,
'message' => $this->message,
'hashes' => $hashes,
);
}
public static function newFromDictionary(array $map) {
$hashes = idx($map, 'hashes', array());
foreach ($hashes as $key => $hash_map) {
$hashes[$key] = DiffusionCommitHash::newFromDictionary($hash_map);
}
$hashes = array_values($hashes);
$ref->setHashes($hashes);
$author_epoch = idx($map, 'authorEpoch');
$author_name = idx($map, 'authorName');
$author_email = idx($map, 'authorEmail');
$committer_name = idx($map, 'committerName');
$committer_email = idx($map, 'committerEmail');
$message = idx($map, 'message');
return $ref;
return id(new self())
->setAuthorEpoch($author_epoch)
->setAuthorName($author_name)
->setAuthorEmail($author_email)
->setCommitterName($committer_name)
->setCommitterEmail($committer_email)
->setMessage($message)
->setHashes($hashes);
}
public function setHashes(array $hashes) {
assert_instances_of($hashes, 'DiffusionCommitHash');
$this->hashes = $hashes;
return $this;
}

View File

@@ -104,7 +104,7 @@ final class DiffusionPathChange extends Phobject {
public function getAuthorName() {
if ($this->getCommitData()) {
return $this->getCommitData()->getAuthorName();
return $this->getCommitData()->getAuthorString();
}
return null;
}

View File

@@ -68,10 +68,15 @@ final class DiffusionMercurialCommandEngine
// http://selenic.com/pipermail/mercurial-devel/2011-February/028541.html
//
// After Jan 2015, it may also fail to write to a revision branch cache.
//
// Separately, it may fail to write to a different branch cache, and may
// encounter issues reading the branch cache.
$ignore = array(
'ignoring untrusted configuration option',
"couldn't write revision branch cache:",
"couldn't write branch cache:",
'invalid branchheads cache',
);
foreach ($ignore as $key => $pattern) {

View File

@@ -73,17 +73,11 @@ abstract class DiffusionQuery extends PhabricatorQuery {
$params = $params + $core_params;
$client = $repository->newConduitClient(
$future = $repository->newConduitFuture(
$user,
$method,
$params,
$drequest->getIsClusterRequest());
if (!$client) {
$result = id(new ConduitCall($method, $params))
->setUser($user)
->execute();
$future = new ImmediateFuture($result);
} else {
$future = $client->callMethod($method, $params);
}
if (!$return_future) {
return $future->resolve();

View File

@@ -76,7 +76,6 @@ final class DiffusionBrowseTableView extends DiffusionView {
$dict = array(
'lint' => celerity_generate_unique_node_id(),
'date' => celerity_generate_unique_node_id(),
'author' => celerity_generate_unique_node_id(),
'details' => celerity_generate_unique_node_id(),
);

View File

@@ -0,0 +1,642 @@
<?php
final class DiffusionCommitGraphView
extends DiffusionView {
private $history;
private $commits;
private $isHead;
private $isTail;
private $parents;
private $filterParents;
private $commitMap;
private $buildableMap;
private $revisionMap;
private $showAuditors;
public function setHistory(array $history) {
assert_instances_of($history, 'DiffusionPathChange');
$this->history = $history;
return $this;
}
public function getHistory() {
return $this->history;
}
public function setCommits(array $commits) {
assert_instances_of($commits, 'PhabricatorRepositoryCommit');
$this->commits = $commits;
return $this;
}
public function getCommits() {
return $this->commits;
}
public function setShowAuditors($show_auditors) {
$this->showAuditors = $show_auditors;
return $this;
}
public function getShowAuditors() {
return $this->showAuditors;
}
public function setParents(array $parents) {
$this->parents = $parents;
return $this;
}
public function getParents() {
return $this->parents;
}
public function setIsHead($is_head) {
$this->isHead = $is_head;
return $this;
}
public function getIsHead() {
return $this->isHead;
}
public function setIsTail($is_tail) {
$this->isTail = $is_tail;
return $this;
}
public function getIsTail() {
return $this->isTail;
}
public function setFilterParents($filter_parents) {
$this->filterParents = $filter_parents;
return $this;
}
public function getFilterParents() {
return $this->filterParents;
}
private function getRepository() {
$drequest = $this->getDiffusionRequest();
if (!$drequest) {
return null;
}
return $drequest->getRepository();
}
public function newObjectItemListView() {
$list_view = id(new PHUIObjectItemListView());
$item_views = $this->newObjectItemViews();
foreach ($item_views as $item_view) {
$list_view->addItem($item_view);
}
return $list_view;
}
private function newObjectItemViews() {
$viewer = $this->getViewer();
require_celerity_resource('diffusion-css');
$show_builds = $this->shouldShowBuilds();
$show_revisions = $this->shouldShowRevisions();
$show_auditors = $this->shouldShowAuditors();
$phids = array();
if ($show_revisions) {
$revision_map = $this->getRevisionMap();
foreach ($revision_map as $revisions) {
foreach ($revisions as $revision) {
$phids[] = $revision->getPHID();
}
}
}
$commits = $this->getCommitMap();
foreach ($commits as $commit) {
$author_phid = $commit->getAuthorDisplayPHID();
if ($author_phid !== null) {
$phids[] = $author_phid;
}
}
if ($show_auditors) {
foreach ($commits as $commit) {
$audits = $commit->getAudits();
foreach ($audits as $auditor) {
$phids[] = $auditor->getAuditorPHID();
}
}
}
$handles = $viewer->loadHandles($phids);
$views = array();
$items = $this->newHistoryItems();
foreach ($items as $hash => $item) {
$content = array();
$commit = $item['commit'];
$commit_description = $this->getCommitDescription($commit);
$commit_link = $this->getCommitURI($hash);
$short_hash = $this->getCommitObjectName($hash);
$is_disabled = $this->getCommitIsDisabled($commit);
$item_view = id(new PHUIObjectItemView())
->setViewer($viewer)
->setHeader($commit_description)
->setObjectName($short_hash)
->setHref($commit_link)
->setDisabled($is_disabled);
$this->addBrowseAction($item_view, $hash);
if ($show_builds) {
$this->addBuildAction($item_view, $hash);
}
$this->addAuditAction($item_view, $hash);
if ($show_auditors) {
$auditor_list = $item_view->newMapView();
if ($commit) {
$auditors = $this->newAuditorList($commit, $handles);
$auditor_list->newItem()
->setName(pht('Auditors'))
->setValue($auditors);
}
}
$property_list = $item_view->newMapView();
if ($commit) {
$author_view = $this->getCommitAuthorView($commit);
if ($author_view) {
$property_list->newItem()
->setName(pht('Author'))
->setValue($author_view);
}
}
if ($show_revisions) {
if ($commit) {
$revisions = $this->getRevisions($commit);
if ($revisions) {
$list_view = $handles->newSublist(mpull($revisions, 'getPHID'))
->newListView();
$property_list->newItem()
->setName(pht('Revisions'))
->setValue($list_view);
}
}
}
$views[$hash] = $item_view;
}
return $views;
}
private function newObjectItemRows() {
$viewer = $this->getViewer();
$items = $this->newHistoryItems();
$views = $this->newObjectItemViews();
$last_date = null;
$rows = array();
foreach ($items as $hash => $item) {
$item_epoch = $item['epoch'];
$item_date = phabricator_date($item_epoch, $viewer);
if ($item_date !== $last_date) {
$last_date = $item_date;
$header = $item_date;
} else {
$header = null;
}
$item_view = $views[$hash];
$list_view = id(new PHUIObjectItemListView())
->setViewer($viewer)
->setFlush(true)
->addItem($item_view);
if ($header !== null) {
$list_view->setHeader($header);
}
$rows[] = $list_view;
}
return $rows;
}
public function render() {
$rows = $this->newObjectItemRows();
$graph = $this->newGraphView();
foreach ($rows as $idx => $row) {
$cells = array();
if ($graph) {
$cells[] = phutil_tag(
'td',
array(
'class' => 'diffusion-commit-graph-path-cell',
),
$graph[$idx]);
}
$cells[] = phutil_tag(
'td',
array(
'class' => 'diffusion-commit-graph-content-cell',
),
$row);
$rows[$idx] = phutil_tag('tr', array(), $cells);
}
$table = phutil_tag(
'table',
array(
'class' => 'diffusion-commit-graph-table',
),
$rows);
return $table;
}
private function newGraphView() {
if (!$this->getParents()) {
return null;
}
$parents = $this->getParents();
// If we're filtering parents, remove relationships which point to
// commits that are not part of the visible graph. Otherwise, we get
// a big tree of nonsense when viewing release branches like "stable"
// versus "master".
if ($this->getFilterParents()) {
foreach ($parents as $key => $nodes) {
foreach ($nodes as $nkey => $node) {
if (empty($parents[$node])) {
unset($parents[$key][$nkey]);
}
}
}
}
return id(new PHUIDiffGraphView())
->setIsHead($this->getIsHead())
->setIsTail($this->getIsTail())
->renderGraph($parents);
}
private function shouldShowBuilds() {
$viewer = $this->getViewer();
$show_builds = PhabricatorApplication::isClassInstalledForViewer(
'PhabricatorHarbormasterApplication',
$this->getUser());
return $show_builds;
}
private function shouldShowRevisions() {
$viewer = $this->getViewer();
$show_revisions = PhabricatorApplication::isClassInstalledForViewer(
'PhabricatorDifferentialApplication',
$viewer);
return $show_revisions;
}
private function shouldShowAuditors() {
return $this->getShowAuditors();
}
private function newHistoryItems() {
$items = array();
$history = $this->getHistory();
if ($history !== null) {
foreach ($history as $history_item) {
$commit_hash = $history_item->getCommitIdentifier();
$items[$commit_hash] = array(
'epoch' => $history_item->getEpoch(),
'hash' => $commit_hash,
'commit' => $this->getCommit($commit_hash),
);
}
} else {
$commits = $this->getCommitMap();
foreach ($commits as $commit) {
$commit_hash = $commit->getCommitIdentifier();
$items[$commit_hash] = array(
'epoch' => $commit->getEpoch(),
'hash' => $commit_hash,
'commit' => $commit,
);
}
}
return $items;
}
private function getCommitDescription($commit) {
if (!$commit) {
return phutil_tag('em', array(), pht("Discovering\xE2\x80\xA6"));
}
// We can show details once the message and change have been imported.
$partial_import = PhabricatorRepositoryCommit::IMPORTED_MESSAGE |
PhabricatorRepositoryCommit::IMPORTED_CHANGE;
if (!$commit->isPartiallyImported($partial_import)) {
return phutil_tag('em', array(), pht("Importing\xE2\x80\xA6"));
}
return $commit->getCommitData()->getSummary();
}
private function getCommitURI($hash) {
$repository = $this->getRepository();
if ($repository) {
return $repository->getCommitURI($hash);
}
$commit = $this->getCommit($hash);
if ($commit) {
return $commit->getURI();
}
return null;
}
private function getCommitObjectName($hash) {
$repository = $this->getRepository();
if ($repository) {
return $repository->formatCommitName(
$hash,
$is_local = true);
}
$commit = $this->getCommit($hash);
if ($commit) {
return $commit->getDisplayName();
}
return null;
}
private function getCommitIsDisabled($commit) {
if (!$commit) {
return true;
}
if ($commit->isUnreachable()) {
return true;
}
return false;
}
private function getCommitAuthorView($commit) {
if (!$commit) {
return null;
}
$viewer = $this->getViewer();
$author_phid = $commit->getAuthorDisplayPHID();
if ($author_phid) {
return $viewer->loadHandles(array($author_phid))
->newListView();
}
return $commit->newCommitAuthorView($viewer);
}
private function getCommit($hash) {
$commit_map = $this->getCommitMap();
return idx($commit_map, $hash);
}
private function getCommitMap() {
if ($this->commitMap === null) {
$commit_list = $this->newCommitList();
$this->commitMap = mpull($commit_list, null, 'getCommitIdentifier');
}
return $this->commitMap;
}
private function addBrowseAction(PHUIObjectItemView $item, $hash) {
$repository = $this->getRepository();
if (!$repository) {
return;
}
$drequest = $this->getDiffusionRequest();
$path = $drequest->getPath();
$uri = $drequest->generateURI(
array(
'action' => 'browse',
'path' => $path,
'commit' => $hash,
));
$menu_item = $item->newMenuItem()
->setName(pht('Browse Repository'))
->setURI($uri);
$menu_item->newIcon()
->setIcon('fa-folder-open-o')
->setColor('bluegrey');
}
private function addBuildAction(PHUIObjectItemView $item, $hash) {
$is_disabled = true;
$buildable = null;
$commit = $this->getCommit($hash);
if ($commit) {
$buildable = $this->getBuildable($commit);
}
if ($buildable) {
$icon = $buildable->getStatusIcon();
$color = $buildable->getStatusColor();
$name = $buildable->getStatusDisplayName();
$uri = $buildable->getURI();
} else {
$icon = 'fa-times';
$color = 'grey';
$name = pht('No Builds');
$uri = null;
}
$menu_item = $item->newMenuItem()
->setName($name)
->setURI($uri)
->setDisabled(($uri === null));
$menu_item->newIcon()
->setIcon($icon)
->setColor($color);
}
private function addAuditAction(PHUIObjectItemView $item_view, $hash) {
$commit = $this->getCommit($hash);
if ($commit) {
$status = $commit->getAuditStatusObject();
$text = $status->getName();
$icon = $status->getIcon();
$is_disabled = $status->isNoAudit();
if ($is_disabled) {
$uri = null;
$color = 'grey';
} else {
$color = $status->getColor();
$uri = $commit->getURI();
}
} else {
$text = pht('No Audit');
$color = 'grey';
$icon = 'fa-times';
$uri = null;
$is_disabled = true;
}
$menu_item = $item_view->newMenuItem()
->setName($text)
->setURI($uri)
->setBackgroundColor($color)
->setDisabled($is_disabled);
$menu_item->newIcon()
->setIcon($icon)
->setColor($color);
}
private function getBuildable(PhabricatorRepositoryCommit $commit) {
$buildable_map = $this->getBuildableMap();
return idx($buildable_map, $commit->getPHID());
}
private function getBuildableMap() {
if ($this->buildableMap === null) {
$commits = $this->getCommitMap();
$buildables = $this->loadBuildables($commits);
$this->buildableMap = $buildables;
}
return $this->buildableMap;
}
private function getRevisions(PhabricatorRepositoryCommit $commit) {
$revision_map = $this->getRevisionMap();
return idx($revision_map, $commit->getPHID(), array());
}
private function getRevisionMap() {
if ($this->revisionMap === null) {
$this->revisionMap = $this->newRevisionMap();
}
return $this->revisionMap;
}
private function newRevisionMap() {
$viewer = $this->getViewer();
$commits = $this->getCommitMap();
return DiffusionCommitRevisionQuery::loadRevisionMapForCommits(
$viewer,
$commits);
}
private function newCommitList() {
$commits = $this->getCommits();
if ($commits !== null) {
return $commits;
}
$repository = $this->getRepository();
if (!$repository) {
return array();
}
$history = $this->getHistory();
if ($history === null) {
return array();
}
$identifiers = array();
foreach ($history as $item) {
$identifiers[] = $item->getCommitIdentifier();
}
if (!$identifiers) {
return array();
}
$viewer = $this->getViewer();
$commits = id(new DiffusionCommitQuery())
->setViewer($viewer)
->withRepository($repository)
->withIdentifiers($identifiers)
->needCommitData(true)
->needIdentities(true)
->execute();
return $commits;
}
private function newAuditorList(
PhabricatorRepositoryCommit $commit,
$handles) {
$auditors = $commit->getAudits();
if (!$auditors) {
return phutil_tag('em', array(), pht('None'));
}
$auditor_phids = mpull($auditors, 'getAuditorPHID');
$auditor_list = $handles->newSublist($auditor_phids)
->newListView();
return $auditor_list;
}
}

View File

@@ -1,177 +0,0 @@
<?php
final class DiffusionCommitListView extends AphrontView {
private $commits = array();
private $noDataString;
public function setNoDataString($no_data_string) {
$this->noDataString = $no_data_string;
return $this;
}
public function setHeader($header) {
$this->header = $header;
return $this;
}
public function setCommits(array $commits) {
assert_instances_of($commits, 'PhabricatorRepositoryCommit');
$this->commits = mpull($commits, null, 'getPHID');
return $this;
}
public function getCommits() {
return $this->commits;
}
public function setHandles(array $handles) {
assert_instances_of($handles, 'PhabricatorObjectHandle');
$this->handles = $handles;
return $this;
}
private function getRequiredHandlePHIDs() {
$phids = array();
foreach ($this->history as $item) {
$data = $item->getCommitData();
if ($data) {
if ($data->getCommitDetail('authorPHID')) {
$phids[$data->getCommitDetail('authorPHID')] = true;
}
if ($data->getCommitDetail('committerPHID')) {
$phids[$data->getCommitDetail('committerPHID')] = true;
}
}
}
return array_keys($phids);
}
private function getCommitDescription($phid) {
if ($this->commits === null) {
return pht('(Unknown Commit)');
}
$commit = idx($this->commits, $phid);
if (!$commit) {
return pht('(Unknown Commit)');
}
$summary = $commit->getCommitData()->getSummary();
if (strlen($summary)) {
return $summary;
}
// No summary, so either this is still importing or just has an empty
// commit message.
if (!$commit->isImported()) {
return pht('(Importing Commit...)');
} else {
return pht('(Untitled Commit)');
}
}
public function render() {
require_celerity_resource('diffusion-css');
return $this->buildList();
}
public function buildList() {
$viewer = $this->getViewer();
$rowc = array();
$phids = array();
foreach ($this->getCommits() as $commit) {
$phids[] = $commit->getPHID();
$author_phid = $commit->getAuthorPHID();
if ($author_phid) {
$phids[] = $author_phid;
}
}
$handles = $viewer->loadHandles($phids);
$cur_date = 0;
$view = array();
foreach ($this->commits as $commit) {
$new_date = phabricator_date($commit->getEpoch(), $viewer);
if ($cur_date !== $new_date) {
$date = ucfirst(
phabricator_relative_date($commit->getEpoch(), $viewer));
$header = id(new PHUIHeaderView())
->setHeader($date);
$list = id(new PHUIObjectItemListView())
->setFlush(true)
->addClass('diffusion-history-list');
$view[] = id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setObjectList($list);
}
$commit_phid = $commit->getPHID();
$commit_handle = $handles[$commit_phid];
$committed = null;
$commit_name = $commit_handle->getName();
$commit_link = $commit_handle->getURI();
$commit_desc = $this->getCommitDescription($commit_phid);
$committed = phabricator_datetime($commit->getEpoch(), $viewer);
$engine = PhabricatorMarkupEngine::newDifferentialMarkupEngine();
$engine->setConfig('viewer', $viewer);
$commit_data = $commit->getCommitData();
$message = $commit_data->getCommitMessage();
$message = $engine->markupText($message);
$message = phutil_tag_div(
'diffusion-history-message phabricator-remarkup', $message);
$author_phid = $commit->getAuthorPHID();
if ($author_phid) {
$author_name = $handles[$author_phid]->renderLink();
$author_image_uri = $handles[$author_phid]->getImageURI();
} else {
$author_name = $commit->getCommitData()->getAuthorName();
$author_image_uri =
celerity_get_resource_uri('/rsrc/image/people/user0.png');
}
$commit_tag = id(new PHUITagView())
->setName($commit_name)
->setType(PHUITagView::TYPE_SHADE)
->setColor(PHUITagView::COLOR_INDIGO)
->setBorder(PHUITagView::BORDER_NONE)
->setSlimShady(true);
$item = id(new PHUIObjectItemView())
->setHeader($commit_desc)
->setHref($commit_link)
->setDisabled($commit->isUnreachable())
->setDescription($message)
->setImageURI($author_image_uri)
->addByline(pht('Author: %s', $author_name))
->addIcon('none', $committed)
->addAttribute($commit_tag);
$list->addItem($item);
$cur_date = $new_date;
}
if (!$view) {
$list = id(new PHUIObjectItemListView())
->setFlush(true)
->setNoDataString($this->noDataString);
$view = id(new PHUIObjectBoxView())
->setHeaderText(pht('Recent Commits'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setObjectList($list);
}
return $view;
}
}

View File

@@ -1,172 +0,0 @@
<?php
final class DiffusionHistoryListView extends DiffusionHistoryView {
public function render() {
$drequest = $this->getDiffusionRequest();
$viewer = $this->getUser();
$repository = $drequest->getRepository();
require_celerity_resource('diffusion-css');
Javelin::initBehavior('phabricator-tooltips');
$buildables = $this->loadBuildables(
mpull($this->getHistory(), 'getCommit'));
$show_revisions = PhabricatorApplication::isClassInstalledForViewer(
'PhabricatorDifferentialApplication',
$viewer);
$handles = $viewer->loadHandles($this->getRequiredHandlePHIDs());
$show_builds = PhabricatorApplication::isClassInstalledForViewer(
'PhabricatorHarbormasterApplication',
$this->getUser());
$cur_date = null;
$view = array();
foreach ($this->getHistory() as $history) {
$epoch = $history->getEpoch();
$new_date = phabricator_date($history->getEpoch(), $viewer);
if ($cur_date !== $new_date) {
$date = ucfirst(
phabricator_relative_date($history->getEpoch(), $viewer));
$header = id(new PHUIHeaderView())
->setHeader($date);
$list = id(new PHUIObjectItemListView())
->setFlush(true)
->addClass('diffusion-history-list');
$view[] = id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->addClass('diffusion-mobile-view')
->setObjectList($list);
}
if ($epoch) {
$committed = $viewer->formatShortDateTime($epoch);
} else {
$committed = null;
}
$data = $history->getCommitData();
$author_phid = $committer = $committer_phid = null;
if ($data) {
$author_phid = $data->getCommitDetail('authorPHID');
$committer_phid = $data->getCommitDetail('committerPHID');
$committer = $data->getCommitDetail('committer');
}
if ($author_phid && isset($handles[$author_phid])) {
$author_name = $handles[$author_phid]->renderLink();
$author_image = $handles[$author_phid]->getImageURI();
} else {
$author_name = self::renderName($history->getAuthorName());
$author_image =
celerity_get_resource_uri('/rsrc/image/people/user0.png');
}
$different_committer = false;
if ($committer_phid) {
$different_committer = ($committer_phid != $author_phid);
} else if ($committer != '') {
$different_committer = ($committer != $history->getAuthorName());
}
if ($different_committer) {
if ($committer_phid && isset($handles[$committer_phid])) {
$committer = $handles[$committer_phid]->renderLink();
} else {
$committer = self::renderName($committer);
}
$author_name = hsprintf('%s / %s', $author_name, $committer);
}
// We can show details once the message and change have been imported.
$partial_import = PhabricatorRepositoryCommit::IMPORTED_MESSAGE |
PhabricatorRepositoryCommit::IMPORTED_CHANGE;
$commit = $history->getCommit();
if ($commit && $commit->isPartiallyImported($partial_import) && $data) {
$commit_desc = $history->getSummary();
} else {
$commit_desc = phutil_tag('em', array(), pht("Importing\xE2\x80\xA6"));
}
$browse_button = $this->linkBrowse(
$history->getPath(),
array(
'commit' => $history->getCommitIdentifier(),
'branch' => $drequest->getBranch(),
'type' => $history->getFileType(),
),
true);
$diff_tag = null;
if ($show_revisions && $commit) {
$revisions = $this->getRevisionsForCommit($commit);
if ($revisions) {
$revision = head($revisions);
$diff_tag = id(new PHUITagView())
->setName($revision->getMonogram())
->setType(PHUITagView::TYPE_SHADE)
->setColor(PHUITagView::COLOR_BLUE)
->setHref($revision->getURI())
->setBorder(PHUITagView::BORDER_NONE)
->setSlimShady(true);
}
}
$build_view = null;
if ($show_builds) {
$buildable = idx($buildables, $commit->getPHID());
if ($buildable !== null) {
$build_view = $this->renderBuildable($buildable, 'button');
}
}
$message = null;
$commit_link = $repository->getCommitURI(
$history->getCommitIdentifier());
$commit_name = $repository->formatCommitName(
$history->getCommitIdentifier(), $local = true);
$committed = phabricator_datetime($commit->getEpoch(), $viewer);
$author_name = phutil_tag(
'strong',
array(
'class' => 'diffusion-history-author-name',
),
$author_name);
$authored = pht('%s on %s.', $author_name, $committed);
$commit_tag = id(new PHUITagView())
->setName($commit_name)
->setType(PHUITagView::TYPE_SHADE)
->setColor(PHUITagView::COLOR_INDIGO)
->setBorder(PHUITagView::BORDER_NONE)
->setSlimShady(true);
$item = id(new PHUIObjectItemView())
->setHeader($commit_desc)
->setHref($commit_link)
->setDisabled($commit->isUnreachable())
->setDescription($message)
->setImageURI($author_image)
->addAttribute(array($commit_tag, ' ', $diff_tag)) // For Copy Pasta
->addAttribute($authored)
->setSideColumn(array(
$build_view,
$browse_button,
));
$list->addItem($item);
$cur_date = $new_date;
}
return $view;
}
}

View File

@@ -1,208 +0,0 @@
<?php
final class DiffusionHistoryTableView extends DiffusionHistoryView {
public function render() {
$drequest = $this->getDiffusionRequest();
$viewer = $this->getUser();
$buildables = $this->loadBuildables(
mpull($this->getHistory(), 'getCommit'));
$has_any_build = false;
$show_revisions = PhabricatorApplication::isClassInstalledForViewer(
'PhabricatorDifferentialApplication',
$viewer);
$handles = $viewer->loadHandles($this->getRequiredHandlePHIDs());
$graph = null;
if ($this->getParents()) {
$parents = $this->getParents();
// If we're filtering parents, remove relationships which point to
// commits that are not part of the visible graph. Otherwise, we get
// a big tree of nonsense when viewing release branches like "stable"
// versus "master".
if ($this->getFilterParents()) {
foreach ($parents as $key => $nodes) {
foreach ($nodes as $nkey => $node) {
if (empty($parents[$node])) {
unset($parents[$key][$nkey]);
}
}
}
}
$graph = id(new PHUIDiffGraphView())
->setIsHead($this->getIsHead())
->setIsTail($this->getIsTail())
->renderGraph($parents);
}
$show_builds = PhabricatorApplication::isClassInstalledForViewer(
'PhabricatorHarbormasterApplication',
$this->getUser());
$rows = array();
$ii = 0;
foreach ($this->getHistory() as $history) {
$epoch = $history->getEpoch();
if ($epoch) {
$committed = $viewer->formatShortDateTime($epoch);
} else {
$committed = null;
}
$data = $history->getCommitData();
$author_phid = $committer = $committer_phid = null;
if ($data) {
$author_phid = $data->getCommitDetail('authorPHID');
$committer_phid = $data->getCommitDetail('committerPHID');
$committer = $data->getCommitDetail('committer');
}
if ($author_phid && isset($handles[$author_phid])) {
$author = $handles[$author_phid]->renderLink();
} else {
$author = self::renderName($history->getAuthorName());
}
$different_committer = false;
if ($committer_phid) {
$different_committer = ($committer_phid != $author_phid);
} else if ($committer != '') {
$different_committer = ($committer != $history->getAuthorName());
}
if ($different_committer) {
if ($committer_phid && isset($handles[$committer_phid])) {
$committer = $handles[$committer_phid]->renderLink();
} else {
$committer = self::renderName($committer);
}
$author = hsprintf('%s/%s', $author, $committer);
}
// We can show details once the message and change have been imported.
$partial_import = PhabricatorRepositoryCommit::IMPORTED_MESSAGE |
PhabricatorRepositoryCommit::IMPORTED_CHANGE;
$commit = $history->getCommit();
if ($commit && $commit->isPartiallyImported($partial_import) && $data) {
$summary = AphrontTableView::renderSingleDisplayLine(
$history->getSummary());
} else {
$summary = phutil_tag('em', array(), pht("Importing\xE2\x80\xA6"));
}
$build = null;
if ($show_builds) {
$buildable = idx($buildables, $commit->getPHID());
if ($buildable !== null) {
$build = $this->renderBuildable($buildable);
$has_any_build = true;
}
}
$browse = $this->linkBrowse(
$history->getPath(),
array(
'commit' => $history->getCommitIdentifier(),
'branch' => $drequest->getBranch(),
'type' => $history->getFileType(),
));
$status = $commit->getAuditStatusObject();
$icon = $status->getIcon();
$color = $status->getColor();
$name = $status->getName();
$audit_view = id(new PHUIIconView())
->setIcon($icon, $color)
->addSigil('has-tooltip')
->setMetadata(
array(
'tip' => $name,
));
$revision_link = null;
if ($commit) {
$revisions = $this->getRevisionsForCommit($commit);
if ($revisions) {
$revision = head($revisions);
$revision_link = phutil_tag(
'a',
array(
'href' => $revision->getURI(),
),
$revision->getMonogram());
}
}
$rows[] = array(
$graph ? $graph[$ii++] : null,
$browse,
self::linkCommit(
$drequest->getRepository(),
$history->getCommitIdentifier()),
$build,
$audit_view,
$revision_link,
$author,
$summary,
$committed,
);
}
$view = new AphrontTableView($rows);
$view->setHeaders(
array(
null,
null,
pht('Commit'),
null,
null,
null,
pht('Author'),
pht('Details'),
pht('Committed'),
));
$view->setColumnClasses(
array(
'threads',
'nudgeright',
'',
'icon',
'icon',
'',
'',
'wide',
'right',
));
$view->setColumnVisibility(
array(
$graph ? true : false,
true,
true,
$has_any_build,
true,
$show_revisions,
));
$view->setDeviceVisibility(
array(
$graph ? true : false,
true,
true,
true,
true,
true,
false,
true,
false,
));
return $view->render();
}
}

View File

@@ -1,117 +0,0 @@
<?php
abstract class DiffusionHistoryView extends DiffusionView {
private $history;
private $revisions = array();
private $handles = array();
private $isHead;
private $isTail;
private $parents;
private $filterParents;
private $revisionMap;
public function setHistory(array $history) {
assert_instances_of($history, 'DiffusionPathChange');
$this->history = $history;
return $this;
}
public function getHistory() {
return $this->history;
}
public function setHandles(array $handles) {
assert_instances_of($handles, 'PhabricatorObjectHandle');
$this->handles = $handles;
return $this;
}
public function getRequiredHandlePHIDs() {
$phids = array();
foreach ($this->history as $item) {
$data = $item->getCommitData();
if ($data) {
if ($data->getCommitDetail('authorPHID')) {
$phids[$data->getCommitDetail('authorPHID')] = true;
}
if ($data->getCommitDetail('committerPHID')) {
$phids[$data->getCommitDetail('committerPHID')] = true;
}
}
}
return array_keys($phids);
}
public function setParents(array $parents) {
$this->parents = $parents;
return $this;
}
public function getParents() {
return $this->parents;
}
public function setIsHead($is_head) {
$this->isHead = $is_head;
return $this;
}
public function getIsHead() {
return $this->isHead;
}
public function setIsTail($is_tail) {
$this->isTail = $is_tail;
return $this;
}
public function getIsTail() {
return $this->isTail;
}
public function setFilterParents($filter_parents) {
$this->filterParents = $filter_parents;
return $this;
}
public function getFilterParents() {
return $this->filterParents;
}
public function render() {}
final protected function getRevisionsForCommit(
PhabricatorRepositoryCommit $commit) {
if ($this->revisionMap === null) {
$this->revisionMap = $this->newRevisionMap();
}
return idx($this->revisionMap, $commit->getPHID(), array());
}
private function newRevisionMap() {
$history = $this->history;
$commits = array();
foreach ($history as $item) {
$commit = $item->getCommit();
if ($commit) {
// NOTE: The "commit" objects in the history list may be undiscovered,
// and thus not yet have PHIDs. Only load data for commits with PHIDs.
if (!$commit->getPHID()) {
continue;
}
$commits[] = $commit;
}
}
return DiffusionCommitRevisionQuery::loadRevisionMapForCommits(
$this->getViewer(),
$commits);
}
}

View File

@@ -150,7 +150,7 @@ final class DiffusionTagListView extends DiffusionView {
if ($commit->getAuthorPHID()) {
$author = $this->handles[$commit->getAuthorPHID()]->renderLink();
} else if ($commit->getCommitData()) {
$author = self::renderName($commit->getCommitData()->getAuthorName());
$author = self::renderName($commit->getCommitData()->getAuthorString());
} else {
$author = self::renderName($tag->getAuthor());
}

View File

@@ -1,140 +0,0 @@
<?php
final class DiffusionTagTableView extends DiffusionView {
private $tags;
private $commits = array();
private $handles = array();
public function setTags($tags) {
$this->tags = $tags;
return $this;
}
public function setCommits(array $commits) {
$this->commits = mpull($commits, null, 'getCommitIdentifier');
return $this;
}
public function setHandles(array $handles) {
$this->handles = $handles;
return $this;
}
public function getRequiredHandlePHIDs() {
return array_filter(mpull($this->commits, 'getAuthorPHID'));
}
public function render() {
$drequest = $this->getDiffusionRequest();
$repository = $drequest->getRepository();
$viewer = $this->getViewer();
$buildables = $this->loadBuildables($this->commits);
$has_builds = false;
$rows = array();
foreach ($this->tags as $tag) {
$commit = idx($this->commits, $tag->getCommitIdentifier());
$tag_link = phutil_tag(
'a',
array(
'href' => $drequest->generateURI(
array(
'action' => 'browse',
'commit' => $tag->getName(),
)),
),
$tag->getName());
$commit_link = phutil_tag(
'a',
array(
'href' => $drequest->generateURI(
array(
'action' => 'commit',
'commit' => $tag->getCommitIdentifier(),
)),
),
$repository->formatCommitName(
$tag->getCommitIdentifier()));
$author = null;
if ($commit && $commit->getAuthorPHID()) {
$author = $this->handles[$commit->getAuthorPHID()]->renderLink();
} else if ($commit && $commit->getCommitData()) {
$author = self::renderName($commit->getCommitData()->getAuthorName());
} else {
$author = self::renderName($tag->getAuthor());
}
$description = null;
if ($tag->getType() == 'git/tag') {
// In Git, a tag may be a "real" tag, or just a reference to a commit.
// If it's a real tag, use the message on the tag, since this may be
// unique data which isn't otherwise available.
$description = $tag->getDescription();
} else {
if ($commit) {
$description = $commit->getSummary();
} else {
$description = $tag->getDescription();
}
}
$build = null;
if ($commit) {
$buildable = idx($buildables, $commit->getPHID());
if ($buildable) {
$build = $this->renderBuildable($buildable);
$has_builds = true;
}
}
$history = $this->linkTagHistory($tag->getName());
$rows[] = array(
$history,
$tag_link,
$commit_link,
$build,
$author,
$description,
$viewer->formatShortDateTime($tag->getEpoch()),
);
}
$table = id(new AphrontTableView($rows))
->setHeaders(
array(
null,
pht('Tag'),
pht('Commit'),
null,
pht('Author'),
pht('Description'),
pht('Created'),
))
->setColumnClasses(
array(
'nudgeright',
'pri',
'',
'',
'',
'wide',
'right',
))
->setColumnVisibility(
array(
true,
true,
true,
$has_builds,
));
return $table->render();
}
}

View File

@@ -55,6 +55,12 @@ final class DrydockRepositoryOperation extends DrydockDAO
'key_repository' => array(
'columns' => array('repositoryPHID', 'operationState'),
),
'key_state' => array(
'columns' => array('operationState'),
),
'key_author' => array(
'columns' => array('authorPHID', 'operationState'),
),
),
) + parent::getConfiguration();
}

View File

@@ -43,6 +43,7 @@ final class PhabricatorChartFunctionArgumentParser
pht(
'Chart function "%s" emitted an argument specification ("%s") with '.
'no type. Each argument specification must have a valid type.',
$this->getFunctionArgumentSignature(),
$name));
}

View File

@@ -6,7 +6,7 @@ final class PhabricatorFactDaemon extends PhabricatorDaemon {
protected function run() {
$this->setEngines(PhabricatorFactEngine::loadAllEngines());
while (!$this->shouldExit()) {
do {
PhabricatorCaches::destroyRequestCache();
$iterators = $this->getAllApplicationIterators();
@@ -14,9 +14,14 @@ final class PhabricatorFactDaemon extends PhabricatorDaemon {
$this->processIteratorWithCursor($iterator_name, $iterator);
}
$this->log(pht('Zzz...'));
$this->sleep(15);
}
$sleep_duration = 60;
if ($this->shouldHibernate($sleep_duration)) {
break;
}
$this->sleep($sleep_duration);
} while (!$this->shouldExit());
}
public static function getAllApplicationIterators() {

View File

@@ -85,15 +85,8 @@ final class PhabricatorJupyterDocumentEngine
if ($utype === $vtype) {
switch ($utype) {
case 'markdown':
$usource = idx($ucell, 'source');
if (is_array($usource)) {
$usource = implode('', $usource);
}
$vsource = idx($vcell, 'source');
if (is_array($vsource)) {
$vsource = implode('', $vsource);
}
$usource = $this->readString($ucell, 'source');
$vsource = $this->readString($vcell, 'source');
$diff = id(new PhutilProseDifferenceEngine())
->getDiff($usource, $vsource);
@@ -117,8 +110,6 @@ final class PhabricatorJupyterDocumentEngine
$vsource = idx($vcell, 'raw');
$udisplay = idx($ucell, 'display');
$vdisplay = idx($vcell, 'display');
$ulabel = idx($ucell, 'label');
$vlabel = idx($vcell, 'label');
$intraline_segments = ArcanistDiffUtils::generateIntralineDiff(
$usource,
@@ -142,15 +133,15 @@ final class PhabricatorJupyterDocumentEngine
$vdisplay,
$v_segments);
$u_content = $this->newCodeLineCell($ucell, $usource);
$v_content = $this->newCodeLineCell($vcell, $vsource);
list($u_label, $u_content) = $this->newCodeLineCell($ucell, $usource);
list($v_label, $v_content) = $this->newCodeLineCell($vcell, $vsource);
$classes = array(
'jupyter-cell-flush',
);
$u_content = $this->newJupyterCell($ulabel, $u_content, $classes);
$v_content = $this->newJupyterCell($vlabel, $v_content, $classes);
$u_content = $this->newJupyterCell($u_label, $u_content, $classes);
$v_content = $this->newJupyterCell($v_label, $v_content, $classes);
$u_content = $this->newCellContainer($u_content);
$v_content = $this->newCellContainer($v_content);
@@ -259,10 +250,7 @@ final class PhabricatorJupyterDocumentEngine
$hash_input = $cell['raw'];
break;
case 'markdown':
$hash_input = $cell['source'];
if (is_array($hash_input)) {
$hash_input = implode('', $cell['source']);
}
$hash_input = $this->readString($cell, 'source');
break;
default:
$hash_input = serialize($cell);
@@ -334,7 +322,6 @@ final class PhabricatorJupyterDocumentEngine
'be rendered as a Jupyter notebook.'));
}
$nbformat = idx($data, 'nbformat');
if (!strlen($nbformat)) {
throw new Exception(
@@ -376,10 +363,7 @@ final class PhabricatorJupyterDocumentEngine
foreach ($cells as $cell) {
$cell_type = idx($cell, 'cell_type');
if ($cell_type === 'markdown') {
$source = $cell['source'];
if (is_array($source)) {
$source = implode('', $source);
}
$source = $this->readString($cell, 'source');
// Attempt to split contiguous blocks of markdown into smaller
// pieces.
@@ -404,11 +388,7 @@ final class PhabricatorJupyterDocumentEngine
$label = $this->newCellLabel($cell);
$lines = idx($cell, 'source');
if (!is_array($lines)) {
$lines = array();
}
$lines = $this->readStringList($cell, 'source');
$content = $this->highlightLines($lines);
$count = count($lines);
@@ -526,10 +506,7 @@ final class PhabricatorJupyterDocumentEngine
}
private function newMarkdownCell(array $cell) {
$content = idx($cell, 'source');
if (!is_array($content)) {
$content = array();
}
$content = $this->readStringList($cell, 'source');
// TODO: This should ideally highlight as Markdown, but the "md"
// highlighter in Pygments is painfully slow and not terribly useful.
@@ -549,11 +526,7 @@ final class PhabricatorJupyterDocumentEngine
private function newCodeCell(array $cell) {
$label = $this->newCellLabel($cell);
$content = idx($cell, 'source');
if (!is_array($content)) {
$content = array();
}
$content = $this->readStringList($cell, 'source');
$content = $this->highlightLines($content);
$outputs = array();
@@ -660,11 +633,7 @@ final class PhabricatorJupyterDocumentEngine
continue;
}
$raw_data = $data[$image_format];
if (!is_array($raw_data)) {
$raw_data = array($raw_data);
}
$raw_data = implode('', $raw_data);
$raw_data = $this->readString($data, $image_format);
$content = phutil_tag(
'img',
@@ -695,11 +664,7 @@ final class PhabricatorJupyterDocumentEngine
break;
case 'stream':
default:
$content = idx($output, 'text');
if (!is_array($content)) {
$content = array();
}
$content = implode('', $content);
$content = $this->readString($output, 'text');
break;
}
@@ -761,4 +726,23 @@ final class PhabricatorJupyterDocumentEngine
return true;
}
private function readString(array $src, $key) {
$list = $this->readStringList($src, $key);
return implode('', $list);
}
private function readStringList(array $src, $key) {
$list = idx($src, $key);
if (is_array($list)) {
$list = $list;
} else if (is_string($list)) {
$list = array($list);
} else {
$list = array();
}
return $list;
}
}

View File

@@ -12,4 +12,16 @@ final class PhabricatorFileHasObjectEdgeType extends PhabricatorEdgeType {
return true;
}
public function getConduitKey() {
return 'file.attached-objects';
}
public function getConduitName() {
return pht('File Has Object');
}
public function getConduitDescription() {
return pht('The source file is attached to the destination object.');
}
}

View File

@@ -0,0 +1,20 @@
<?php
final class HarbormasterBuildStepEditAPIMethod
extends PhabricatorEditEngineAPIMethod {
public function getAPIMethodName() {
return 'harbormaster.step.edit';
}
public function newEditEngine() {
return new HarbormasterBuildStepEditEngine();
}
public function getMethodSummary() {
return pht(
'Apply transactions to create a new build step or edit an existing '.
'one.');
}
}

View File

@@ -0,0 +1,18 @@
<?php
final class HarbormasterBuildStepSearchAPIMethod
extends PhabricatorSearchEngineAPIMethod {
public function getAPIMethodName() {
return 'harbormaster.step.search';
}
public function newSearchEngine() {
return new HarbormasterBuildStepSearchEngine();
}
public function getMethodSummary() {
return pht('Retrieve information about Harbormaster build steps.');
}
}

View File

@@ -0,0 +1,107 @@
<?php
final class HarbormasterBuildStepEditEngine
extends PhabricatorEditEngine {
const ENGINECONST = 'harbormaster.buildstep';
private $buildPlan;
public function setBuildPlan(HarbormasterBuildPlan $build_plan) {
$this->buildPlan = $build_plan;
return $this;
}
public function getBuildPlan() {
if ($this->buildPlan === null) {
throw new PhutilInvalidStateException('setBuildPlan');
}
return $this->buildPlan;
}
public function isEngineConfigurable() {
return false;
}
public function getEngineName() {
return pht('Harbormaster Build Steps');
}
public function getSummaryHeader() {
return pht('Edit Harbormaster Build Step Configurations');
}
public function getSummaryText() {
return pht('This engine is used to edit Harbormaster build steps.');
}
public function getEngineApplicationClass() {
return 'PhabricatorHarbormasterApplication';
}
protected function newEditableObject() {
$viewer = $this->getViewer();
$plan = HarbormasterBuildPlan::initializeNewBuildPlan($viewer);
$this->setBuildPlan($plan);
$plan = $this->getBuildPlan();
$step = HarbormasterBuildStep::initializeNewStep($viewer);
$step->setBuildPlanPHID($plan->getPHID());
$step->attachBuildPlan($plan);
return $step;
}
protected function newObjectQuery() {
return new HarbormasterBuildStepQuery();
}
protected function getObjectCreateTitleText($object) {
return pht('Create Build Step');
}
protected function getObjectCreateButtonText($object) {
return pht('Create Build Step');
}
protected function getObjectEditTitleText($object) {
return pht('Edit Build Step: %s', $object->getName());
}
protected function getObjectEditShortText($object) {
return pht('Edit Build Step');
}
protected function getObjectCreateShortText() {
return pht('Create Build Step');
}
protected function getObjectName() {
return pht('Build Step');
}
protected function getEditorURI() {
return '/harbormaster/step/edit/';
}
protected function getObjectCreateCancelURI($object) {
return '/harbormaster/step/';
}
protected function getObjectViewURI($object) {
$id = $object->getID();
return "/harbormaster/step/{$id}/";
}
protected function buildCustomEditFields($object) {
$fields = array();
return $fields;
}
}

View File

@@ -54,6 +54,7 @@ final class HarbormasterManagementPublishWorkflow
pht(
'Object "%s" is not a HarbormasterBuildable (it is a "%s"). '.
'Name one or more buildables to publish, like "B123".',
$name,
get_class($result)));
}
}

View File

@@ -0,0 +1,58 @@
<?php
final class HarbormasterBuildStepSearchEngine
extends PhabricatorApplicationSearchEngine {
public function getResultTypeDescription() {
return pht('Harbormaster Build Steps');
}
public function getApplicationClassName() {
return 'PhabricatorHarbormasterApplication';
}
public function newQuery() {
return new HarbormasterBuildStepQuery();
}
protected function buildCustomSearchFields() {
return array();
}
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
return $query;
}
protected function getURI($path) {
return '/harbormaster/step/'.$path;
}
protected function getBuiltinQueryNames() {
return array(
'all' => pht('All Steps'),
);
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function renderResultList(
array $plans,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($plans, 'HarbormasterBuildStep');
return null;
}
}

View File

@@ -14,7 +14,7 @@ final class HarbormasterWaitForPreviousBuildStepImplementation
}
public function getBuildStepGroupKey() {
return HarbormasterPrototypeBuildStepGroup::GROUPKEY;
return HarbormasterControlBuildStepGroup::GROUPKEY;
}
public function execute(

View File

@@ -177,6 +177,8 @@ final class HarbormasterBuildLog
pht(
'Attempt to load log bytes (%d - %d) failed: failed to '.
'load a single contiguous range. Actual ranges: %s.',
$offset,
$end,
implode('; ', $display_ranges)));
}

View File

@@ -4,7 +4,8 @@ final class HarbormasterBuildStep extends HarbormasterDAO
implements
PhabricatorApplicationTransactionInterface,
PhabricatorPolicyInterface,
PhabricatorCustomFieldInterface {
PhabricatorCustomFieldInterface,
PhabricatorConduitResultInterface {
protected $name;
protected $description;
@@ -169,5 +170,45 @@ final class HarbormasterBuildStep extends HarbormasterDAO
return $this;
}
/* -( PhabricatorConduitResultInterface )---------------------------------- */
public function getFieldSpecificationsForConduit() {
return array(
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('name')
->setType('string')
->setDescription(pht('The name of the build step.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('description')
->setType('remarkup')
->setDescription(pht('The build step description.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('buildPlanPHID')
->setType('phid')
->setDescription(
pht(
'The PHID of the build plan this build step belongs to.')),
);
}
public function getFieldValuesForConduit() {
// T6203: This can be removed once the field becomes non-nullable.
$name = $this->getName();
$name = phutil_string_cast($name);
return array(
'name' => $name,
'description' => array(
'raw' => $this->getDescription(),
),
'buildPlanPHID' => $this->getBuildPlanPHID(),
);
}
public function getConduitSearchAttachments() {
return array();
}
}

View File

@@ -0,0 +1,43 @@
<?php
final class HeraldCommentContentField extends HeraldField {
const FIELDCONST = 'comment.content';
public function getHeraldFieldName() {
return pht('Comment content');
}
public function getFieldGroupKey() {
return HeraldTransactionsFieldGroup::FIELDGROUPKEY;
}
public function getHeraldFieldValue($object) {
$adapter = $this->getAdapter();
$xactions = $adapter->getAppliedTransactions();
$result = array();
foreach ($xactions as $xaction) {
if (!$xaction->hasComment()) {
continue;
}
$comment = $xaction->getComment();
$content = $comment->getContent();
$result[] = $content;
}
return $result;
}
public function supportsObject($object) {
return true;
}
protected function getHeraldFieldStandardType() {
return self::STANDARD_TEXT_LIST;
}
}

View File

@@ -28,6 +28,17 @@ final class HeraldWebhookCallManagementWorkflow
'name' => 'secure',
'help' => pht('Set the "secure" flag on the request.'),
),
array(
'name' => 'count',
'param' => 'N',
'help' => pht('Make a total of __N__ copies of the call.'),
),
array(
'name' => 'background',
'help' => pht(
'Instead of making calls in the foreground, add the tasks '.
'to the daemon queue.'),
),
));
}
@@ -41,6 +52,17 @@ final class HeraldWebhookCallManagementWorkflow
'Specify a webhook to call with "--id".'));
}
$count = $args->getArg('count');
if ($count === null) {
$count = 1;
}
if ($count <= 0) {
throw new PhutilArgumentUsageException(
pht(
'Specified "--count" must be larger than 0.'));
}
$hook = id(new HeraldWebhookQuery())
->setViewer($viewer)
->withIDs(array($id))
@@ -69,6 +91,8 @@ final class HeraldWebhookCallManagementWorkflow
$object = head($objects);
}
$is_background = $args->getArg('background');
$xaction_query =
PhabricatorApplicationTransactionQuery::newQueryForObject($object);
@@ -80,25 +104,49 @@ final class HeraldWebhookCallManagementWorkflow
$application_phid = id(new PhabricatorHeraldApplication())->getPHID();
$request = HeraldWebhookRequest::initializeNewWebhookRequest($hook)
->setObjectPHID($object->getPHID())
->setIsTestAction(true)
->setIsSilentAction((bool)$args->getArg('silent'))
->setIsSecureAction((bool)$args->getArg('secure'))
->setTriggerPHIDs(array($application_phid))
->setTransactionPHIDs(mpull($xactions, 'getPHID'))
->save();
if ($is_background) {
echo tsprintf(
"%s\n",
pht(
'Queueing webhook calls...'));
$progress_bar = id(new PhutilConsoleProgressBar())
->setTotal($count);
} else {
echo tsprintf(
"%s\n",
pht(
'Calling webhook...'));
PhabricatorWorker::setRunAllTasksInProcess(true);
}
PhabricatorWorker::setRunAllTasksInProcess(true);
$request->queueCall();
for ($ii = 0; $ii < $count; $ii++) {
$request = HeraldWebhookRequest::initializeNewWebhookRequest($hook)
->setObjectPHID($object->getPHID())
->setIsTestAction(true)
->setIsSilentAction((bool)$args->getArg('silent'))
->setIsSecureAction((bool)$args->getArg('secure'))
->setTriggerPHIDs(array($application_phid))
->setTransactionPHIDs(mpull($xactions, 'getPHID'))
->save();
$request->reload();
$request->queueCall();
echo tsprintf(
"%s\n",
pht(
'Success, got HTTP %s from webhook.',
$request->getErrorCode()));
if ($is_background) {
$progress_bar->update(1);
} else {
$request->reload();
echo tsprintf(
"%s\n",
pht(
'Success, got HTTP %s from webhook.',
$request->getErrorCode()));
}
}
if ($is_background) {
$progress_bar->done();
}
return 0;
}

View File

@@ -199,8 +199,7 @@ final class ManiphestTaskPriority extends ManiphestConstants {
throw new Exception(
pht(
'Configuration is not valid. Maniphest priority configurations '.
'must be dictionaries.',
$config));
'must be dictionaries.'));
}
$all_keywords = array();

View File

@@ -65,6 +65,7 @@ abstract class PhabricatorMailAdapter
pht(
'Adapter ("%s") is configured for medium "%s", but this is not '.
'a supported delivery medium. Supported media are: %s.',
get_class($this),
$medium,
implode(', ', $native_map)));
}

View File

@@ -17,6 +17,7 @@ final class PhabricatorMailAmazonSESAdapter
array(
'access-key' => 'string',
'secret-key' => 'string',
'region' => 'string',
'endpoint' => 'string',
));
}
@@ -25,6 +26,7 @@ final class PhabricatorMailAmazonSESAdapter
return array(
'access-key' => null,
'secret-key' => null,
'region' => null,
'endpoint' => null,
);
}
@@ -45,23 +47,33 @@ final class PhabricatorMailAmazonSESAdapter
$mailer->Send();
}
/**
* @phutil-external-symbol class SimpleEmailService
*/
public function executeSend($body) {
$key = $this->getOption('access-key');
$secret = $this->getOption('secret-key');
$secret = new PhutilOpaqueEnvelope($secret);
$region = $this->getOption('region');
$endpoint = $this->getOption('endpoint');
$root = phutil_get_library_root('phabricator');
$root = dirname($root);
require_once $root.'/externals/amazon-ses/ses.php';
$data = array(
'Action' => 'SendRawEmail',
'RawMessage.Data' => base64_encode($body),
);
$service = new SimpleEmailService($key, $secret, $endpoint);
$service->enableUseExceptions(true);
return $service->sendRawEmail($body);
$data = phutil_build_http_querystring($data);
$future = id(new PhabricatorAWSSESFuture())
->setAccessKey($key)
->setSecretKey($secret)
->setRegion($region)
->setEndpoint($endpoint)
->setHTTPMethod('POST')
->setData($data);
$future->resolve();
return true;
}
}

View File

@@ -12,6 +12,7 @@ final class PhabricatorMailAdapterTestCase
array(
'access-key' => 'test',
'secret-key' => 'test',
'region' => 'test',
'endpoint' => 'test',
),
),

View File

@@ -0,0 +1,21 @@
<?php
final class PhabricatorAWSSESFuture extends PhutilAWSFuture {
private $parameters;
public function getServiceName() {
return 'ses';
}
protected function didReceiveResult($result) {
list($status, $body, $headers) = $result;
if (!$status->isError()) {
return $body;
}
return parent::didReceiveResult($result);
}
}

View File

@@ -64,24 +64,6 @@ final class PhabricatorMetaMTAMailQuery
$this->actorPHIDs);
}
if ($this->recipientPHIDs !== null) {
$where[] = qsprintf(
$conn,
'recipient.dst IN (%Ls)',
$this->recipientPHIDs);
}
if ($this->actorPHIDs === null && $this->recipientPHIDs === null) {
$viewer = $this->getViewer();
if (!$viewer->isOmnipotent()) {
$where[] = qsprintf(
$conn,
'edge.dst = %s OR actorPHID = %s',
$viewer->getPHID(),
$viewer->getPHID());
}
}
if ($this->createdMin !== null) {
$where[] = qsprintf(
$conn,
@@ -102,26 +84,29 @@ final class PhabricatorMetaMTAMailQuery
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
$joins = parent::buildJoinClauseParts($conn);
if ($this->actorPHIDs === null && $this->recipientPHIDs === null) {
if ($this->shouldJoinRecipients()) {
$joins[] = qsprintf(
$conn,
'LEFT JOIN %T edge ON mail.phid = edge.src AND edge.type = %d',
'JOIN %T recipient
ON mail.phid = recipient.src
AND recipient.type = %d
AND recipient.dst IN (%Ls)',
PhabricatorEdgeConfig::TABLE_NAME_EDGE,
PhabricatorMetaMTAMailHasRecipientEdgeType::EDGECONST);
}
if ($this->recipientPHIDs !== null) {
$joins[] = qsprintf(
$conn,
'LEFT JOIN %T recipient '.
'ON mail.phid = recipient.src AND recipient.type = %d',
PhabricatorEdgeConfig::TABLE_NAME_EDGE,
PhabricatorMetaMTAMailHasRecipientEdgeType::EDGECONST);
PhabricatorMetaMTAMailHasRecipientEdgeType::EDGECONST,
$this->recipientPHIDs);
}
return $joins;
}
private function shouldJoinRecipients() {
if ($this->recipientPHIDs === null) {
return false;
}
return true;
}
protected function getPrimaryTableAlias() {
return 'mail';
}
@@ -134,4 +119,14 @@ final class PhabricatorMetaMTAMailQuery
return 'PhabricatorMetaMTAApplication';
}
protected function shouldGroupQueryResultRows() {
if ($this->shouldJoinRecipients()) {
if (count($this->recipientPHIDs) > 1) {
return true;
}
}
return parent::shouldGroupQueryResultRows();
}
}

View File

@@ -98,7 +98,9 @@ final class PhabricatorNotificationServersConfigType
'Notification server configuration describes an invalid host '.
'("%s", at index "%s"). This is an "admin" service but it has a '.
'"path" property. This property is only valid for "client" '.
'services.'));
'services.',
$host,
$index));
}
// We can't guarantee that you didn't just give the same host two

View File

@@ -85,7 +85,8 @@ final class NuanceGitHubRawEventTestCase
throw new Exception(
pht(
'Expected test file "%s" to contain exactly two sections, '.
'but it has more than two sections.'));
'but it has more than two sections.',
$file));
}
list($input, $expect) = $parts;

View File

@@ -82,12 +82,15 @@ final class PhabricatorOwnersDetailController
))
->needCommitData(true)
->needAuditRequests(true)
->needIdentities(true)
->setLimit(10)
->execute();
$view = id(new PhabricatorAuditListView())
->setUser($viewer)
->setNoDataString(pht('This package has no open problem commits.'))
->setCommits($attention_commits);
$view = id(new DiffusionCommitGraphView())
->setViewer($viewer)
->setCommits($attention_commits)
->newObjectItemListView();
$view->setNoDataString(pht('This package has no open problem commits.'));
$commit_views[] = array(
'view' => $view,
@@ -105,13 +108,16 @@ final class PhabricatorOwnersDetailController
->withPackagePHIDs(array($package->getPHID()))
->needCommitData(true)
->needAuditRequests(true)
->needIdentities(true)
->setLimit(25)
->execute();
$view = id(new PhabricatorAuditListView())
->setUser($viewer)
$view = id(new DiffusionCommitGraphView())
->setViewer($viewer)
->setCommits($all_commits)
->setNoDataString(pht('No commits in this package.'));
->newObjectItemListView();
$view->setNoDataString(pht('No commits in this package.'));
$commit_views[] = array(
'view' => $view,

View File

@@ -58,13 +58,13 @@ final class PhabricatorPeopleProfileCommitsController
->setViewer($viewer)
->withAuthorPHIDs(array($user->getPHID()))
->needCommitData(true)
->needIdentities(true)
->setLimit(100)
->execute();
$list = id(new DiffusionCommitListView())
$list = id(new DiffusionCommitGraphView())
->setViewer($viewer)
->setCommits($commits)
->setNoDataString(pht('No recent commits.'));
->setCommits($commits);
return $list;
}

View File

@@ -104,6 +104,10 @@ final class PhabricatorHandleList
->setHandleList($this);
}
public function newListView() {
return id(new FuelHandleListView())
->addHandleList($this);
}
/**
* Return a @{class:PHUIHandleView} which can render a specific handle.

View File

@@ -61,6 +61,8 @@ final class PholioMockViewController extends PholioController {
new PholioTransactionQuery());
$timeline->setMock($mock);
$timeline->setQuoteRef($mock->getMonogram());
$curtain = $this->buildCurtainView($mock);
$details = $this->buildDescriptionView($mock);
@@ -80,6 +82,7 @@ final class PholioMockViewController extends PholioController {
->appendChild($mock_view);
$add_comment = $this->buildAddCommentView($mock, $comment_form_id);
$add_comment->setTransactionTimeline($timeline);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($mock->getMonogram(), $mock->getURI());

View File

@@ -71,7 +71,6 @@ final class PhragmentRevertController extends PhragmentController {
->appendParagraph(pht(
'Reverting this fragment to version %d will create a new version of '.
'the fragment. It will not delete any version history.',
$version->getSequence(),
$version->getSequence()));
return id(new AphrontDialogResponse())->setDialog($dialog);
}

View File

@@ -401,7 +401,7 @@ final class PhrictionDocumentController
$view->addProperty(
pht('Last Edited'),
phabricator_datetime($content->getDateCreated(), $viewer));
phabricator_dual_datetime($content->getDateCreated(), $viewer));
return $view;
}

View File

@@ -266,8 +266,7 @@ final class PhabricatorPolicy
return pht(
'Members of a particular project can take this action. (You '.
'can not see this object, so the name of this project is '.
'restricted.)',
$handle->getFullName());
'restricted.)');
} else if ($type == PhabricatorPeopleUserPHIDType::TYPECONST) {
return pht(
'%s can take this action.',

View File

@@ -224,6 +224,7 @@ final class PhabricatorProjectIconSet
'Icon key "%s" is not a valid icon key. Icon keys must be 1-32 '.
'characters long and contain only lowercase letters. For example, '.
'"%s" and "%s" are reasonable keys.',
$value['key'],
'tag',
'group'));
}

View File

@@ -656,6 +656,11 @@ final class PhabricatorProjectQuery
if ($this->memberPHIDs || $this->watcherPHIDs || $this->nameTokens) {
return true;
}
if ($this->slugs) {
return true;
}
return parent::shouldGroupQueryResultRows();
}

View File

@@ -1,165 +0,0 @@
<?php
/**
* Normalize repository URIs. For example, these URIs are generally equivalent
* and all point at the same repository:
*
* ssh://user@host/repo
* ssh://user@host/repo/
* ssh://user@host:22/repo
* user@host:/repo
* ssh://user@host/repo.git
*
* This class can be used to normalize URIs like this, in order to detect
* alternate spellings of the same repository URI. In particular, the
* @{method:getNormalizedPath} method will return:
*
* repo
*
* ...for all of these URIs. Generally, usage looks like this:
*
* $norm_a = new PhabricatorRepositoryURINormalizer($type, $uri_a);
* $norm_b = new PhabricatorRepositoryURINormalizer($type, $uri_b);
*
* if ($norm_a->getNormalizedPath() == $norm_b->getNormalizedPath()) {
* // URIs appear to point at the same repository.
* } else {
* // URIs are very unlikely to be the same repository.
* }
*
* Because a repository can be hosted at arbitrarily many arbitrary URIs, there
* is no way to completely prevent false negatives by only examining URIs
* (that is, repositories with totally different URIs could really be the same).
* However, normalization is relatively aggressive and false negatives should
* be rare: if normalization says two URIs are different repositories, they
* probably are.
*
* @task normal Normalizing URIs
*/
final class PhabricatorRepositoryURINormalizer extends Phobject {
const TYPE_GIT = 'git';
const TYPE_SVN = 'svn';
const TYPE_MERCURIAL = 'hg';
private $type;
private $uri;
public function __construct($type, $uri) {
switch ($type) {
case self::TYPE_GIT:
case self::TYPE_SVN:
case self::TYPE_MERCURIAL:
break;
default:
throw new Exception(pht('Unknown URI type "%s"!', $type));
}
$this->type = $type;
$this->uri = $uri;
}
public static function getAllURITypes() {
return array(
self::TYPE_GIT,
self::TYPE_SVN,
self::TYPE_MERCURIAL,
);
}
/* -( Normalizing URIs )--------------------------------------------------- */
/**
* @task normal
*/
public function getPath() {
switch ($this->type) {
case self::TYPE_GIT:
$uri = new PhutilURI($this->uri);
return $uri->getPath();
case self::TYPE_SVN:
case self::TYPE_MERCURIAL:
$uri = new PhutilURI($this->uri);
if ($uri->getProtocol()) {
return $uri->getPath();
}
return $this->uri;
}
}
public function getNormalizedURI() {
return $this->getNormalizedDomain().'/'.$this->getNormalizedPath();
}
/**
* @task normal
*/
public function getNormalizedPath() {
$path = $this->getPath();
$path = trim($path, '/');
switch ($this->type) {
case self::TYPE_GIT:
$path = preg_replace('/\.git$/', '', $path);
break;
case self::TYPE_SVN:
case self::TYPE_MERCURIAL:
break;
}
// If this is a Phabricator URI, strip it down to the callsign. We mutably
// allow you to clone repositories as "/diffusion/X/anything.git", for
// example.
$matches = null;
if (preg_match('@^(diffusion/(?:[A-Z]+|\d+))@', $path, $matches)) {
$path = $matches[1];
}
return $path;
}
public function getNormalizedDomain() {
$domain = null;
$uri = new PhutilURI($this->uri);
$domain = $uri->getDomain();
if (!strlen($domain)) {
return '<void>';
}
$domain = phutil_utf8_strtolower($domain);
// See T13435. If the domain for a repository URI is same as the install
// base URI, store it as a "<base-uri>" token instead of the actual domain
// so that the index does not fall out of date if the install moves.
$base_uri = PhabricatorEnv::getURI('/');
$base_uri = new PhutilURI($base_uri);
$base_domain = $base_uri->getDomain();
$base_domain = phutil_utf8_strtolower($base_domain);
if ($domain === $base_domain) {
return '<base-uri>';
}
// Likewise, store a token for the "SSH Host" domain so it can be changed
// without requiring an index rebuild.
$ssh_host = PhabricatorEnv::getEnvConfig('diffusion.ssh-host');
if (strlen($ssh_host)) {
$ssh_host = phutil_utf8_strtolower($ssh_host);
if ($domain === $ssh_host) {
return '<ssh-host>';
}
}
return $domain;
}
}

View File

@@ -1,81 +0,0 @@
<?php
final class PhabricatorRepositoryURINormalizerTestCase
extends PhabricatorTestCase {
public function testGitURINormalizer() {
$cases = array(
'ssh://user@domain.com/path.git' => 'path',
'https://user@domain.com/path.git' => 'path',
'git@domain.com:path.git' => 'path',
'ssh://user@gitserv002.com/path.git' => 'path',
'ssh://htaft@domain.com/path.git' => 'path',
'ssh://user@domain.com/bananas.git' => 'bananas',
'git@domain.com:bananas.git' => 'bananas',
'user@domain.com:path/repo' => 'path/repo',
'user@domain.com:path/repo/' => 'path/repo',
'file:///path/to/local/repo.git' => 'path/to/local/repo',
'/path/to/local/repo.git' => 'path/to/local/repo',
'ssh://something.com/diffusion/X/anything.git' => 'diffusion/X',
'ssh://something.com/diffusion/X/' => 'diffusion/X',
);
$type_git = PhabricatorRepositoryURINormalizer::TYPE_GIT;
foreach ($cases as $input => $expect) {
$normal = new PhabricatorRepositoryURINormalizer($type_git, $input);
$this->assertEqual(
$expect,
$normal->getNormalizedPath(),
pht('Normalized Git path for "%s".', $input));
}
}
public function testDomainURINormalizer() {
$base_domain = 'base.phabricator.example.com';
$ssh_domain = 'ssh.phabricator.example.com';
$env = PhabricatorEnv::beginScopedEnv();
$env->overrideEnvConfig('phabricator.base-uri', 'http://'.$base_domain);
$env->overrideEnvConfig('diffusion.ssh-host', $ssh_domain);
$cases = array(
'/' => '<void>',
'/path/to/local/repo.git' => '<void>',
'ssh://user@domain.com/path.git' => 'domain.com',
'ssh://user@DOMAIN.COM/path.git' => 'domain.com',
'http://'.$base_domain.'/diffusion/X/' => '<base-uri>',
'ssh://'.$ssh_domain.'/diffusion/X/' => '<ssh-host>',
'git@'.$ssh_domain.':bananas.git' => '<ssh-host>',
);
$type_git = PhabricatorRepositoryURINormalizer::TYPE_GIT;
foreach ($cases as $input => $expect) {
$normal = new PhabricatorRepositoryURINormalizer($type_git, $input);
$this->assertEqual(
$expect,
$normal->getNormalizedDomain(),
pht('Normalized domain for "%s".', $input));
}
}
public function testSVNURINormalizer() {
$cases = array(
'file:///path/to/repo' => 'path/to/repo',
'file:///path/to/repo/' => 'path/to/repo',
);
$type_svn = PhabricatorRepositoryURINormalizer::TYPE_SVN;
foreach ($cases as $input => $expect) {
$normal = new PhabricatorRepositoryURINormalizer($type_svn, $input);
$this->assertEqual(
$expect,
$normal->getNormalizedPath(),
pht('Normalized SVN path for "%s".', $input));
}
}
}

View File

@@ -290,13 +290,13 @@ final class PhabricatorRepositoryDiscoveryEngine
$remote_root = (string)($xml->entry[0]->repository[0]->root[0]);
$expect_root = $repository->getSubversionPathURI();
$normal_type_svn = PhabricatorRepositoryURINormalizer::TYPE_SVN;
$normal_type_svn = ArcanistRepositoryURINormalizer::TYPE_SVN;
$remote_normal = id(new PhabricatorRepositoryURINormalizer(
$remote_normal = id(new ArcanistRepositoryURINormalizer(
$normal_type_svn,
$remote_root))->getNormalizedPath();
$expect_normal = id(new PhabricatorRepositoryURINormalizer(
$expect_normal = id(new ArcanistRepositoryURINormalizer(
$normal_type_svn,
$expect_root))->getNormalizedPath();

View File

@@ -553,6 +553,7 @@ final class PhabricatorRepositoryRefEngine
}
$closeable_flag = PhabricatorRepositoryCommit::IMPORTED_CLOSEABLE;
$published_flag = PhabricatorRepositoryCommit::IMPORTED_PUBLISH;
$all_commits = ipull($all_commits, null, 'commitIdentifier');
foreach ($identifiers as $identifier) {
@@ -566,12 +567,21 @@ final class PhabricatorRepositoryRefEngine
$identifier));
}
if (!($row['importStatus'] & $closeable_flag)) {
$import_status = $row['importStatus'];
if (!($import_status & $closeable_flag)) {
// Set the "closeable" flag.
$import_status = ($import_status | $closeable_flag);
// See T13580. Clear the "published" flag, so publishing executes
// again. We may have previously performed a no-op "publish" on the
// commit to make sure it has all bits in the "IMPORTED_ALL" bitmask.
$import_status = ($import_status & ~$published_flag);
queryfx(
$conn,
'UPDATE %T SET importStatus = (importStatus | %d) WHERE id = %d',
'UPDATE %T SET importStatus = %d WHERE id = %d',
$commit_table->getTableName(),
$closeable_flag,
$import_status,
$row['id']);
$data = array(

View File

@@ -1,115 +0,0 @@
<?php
final class PhabricatorRepositoryManagementLookupUsersWorkflow
extends PhabricatorRepositoryManagementWorkflow {
protected function didConstruct() {
$this
->setName('lookup-users')
->setExamples('**lookup-users** __commit__ ...')
->setSynopsis(
pht('Resolve user accounts for users attached to __commit__.'))
->setArguments(
array(
array(
'name' => 'commits',
'wildcard' => true,
),
));
}
public function execute(PhutilArgumentParser $args) {
$commits = $this->loadCommits($args, 'commits');
if (!$commits) {
throw new PhutilArgumentUsageException(
pht('Specify one or more commits to resolve users for.'));
}
$console = PhutilConsole::getConsole();
foreach ($commits as $commit) {
$repo = $commit->getRepository();
$name = $repo->formatCommitName($commit->getCommitIdentifier());
$console->writeOut(
"%s\n",
pht('Examining commit %s...', $name));
$refs_raw = DiffusionQuery::callConduitWithDiffusionRequest(
$this->getViewer(),
DiffusionRequest::newFromDictionary(
array(
'repository' => $repo,
'user' => $this->getViewer(),
)),
'diffusion.querycommits',
array(
'repositoryPHID' => $repo->getPHID(),
'phids' => array($commit->getPHID()),
'bypassCache' => true,
));
if (empty($refs_raw['data'])) {
throw new Exception(
pht(
'Unable to retrieve details for commit "%s"!',
$commit->getPHID()));
}
$ref = DiffusionCommitRef::newFromConduitResult(head($refs_raw['data']));
$author = $ref->getAuthor();
$console->writeOut(
"%s\n",
pht('Raw author string: %s', coalesce($author, 'null')));
if ($author !== null) {
$handle = $this->resolveUser($commit, $author);
if ($handle) {
$console->writeOut(
"%s\n",
pht('Phabricator user: %s', $handle->getFullName()));
} else {
$console->writeOut(
"%s\n",
pht('Unable to resolve a corresponding Phabricator user.'));
}
}
$committer = $ref->getCommitter();
$console->writeOut(
"%s\n",
pht('Raw committer string: %s', coalesce($committer, 'null')));
if ($committer !== null) {
$handle = $this->resolveUser($commit, $committer);
if ($handle) {
$console->writeOut(
"%s\n",
pht('Phabricator user: %s', $handle->getFullName()));
} else {
$console->writeOut(
"%s\n",
pht('Unable to resolve a corresponding Phabricator user.'));
}
}
}
return 0;
}
private function resolveUser(PhabricatorRepositoryCommit $commit, $name) {
$phid = id(new DiffusionResolveUserQuery())
->withName($name)
->execute();
if (!$phid) {
return null;
}
return id(new PhabricatorHandleQuery())
->setViewer($this->getViewer())
->withPHIDs(array($phid))
->executeOne();
}
}

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