1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-01-26 14:38:19 +01:00

(stable) Promote 2018 Week 31

This commit is contained in:
epriestley 2018-08-06 11:02:45 -07:00
commit b45047f952
32 changed files with 468 additions and 92 deletions

View file

@ -9,7 +9,7 @@ return array(
'names' => array( 'names' => array(
'conpherence.pkg.css' => 'e68cf1fa', 'conpherence.pkg.css' => 'e68cf1fa',
'conpherence.pkg.js' => '15191c65', 'conpherence.pkg.js' => '15191c65',
'core.pkg.css' => 'e5233bff', 'core.pkg.css' => 'f515619b',
'core.pkg.js' => '2058ec09', 'core.pkg.js' => '2058ec09',
'differential.pkg.css' => '06dc617c', 'differential.pkg.css' => '06dc617c',
'differential.pkg.js' => 'ef19e026', 'differential.pkg.js' => 'ef19e026',
@ -154,7 +154,7 @@ return array(
'rsrc/css/phui/phui-form-view.css' => 'ae9f8d16', 'rsrc/css/phui/phui-form-view.css' => 'ae9f8d16',
'rsrc/css/phui/phui-form.css' => '7aaa04e3', 'rsrc/css/phui/phui-form.css' => '7aaa04e3',
'rsrc/css/phui/phui-head-thing.css' => 'fd311e5f', 'rsrc/css/phui/phui-head-thing.css' => 'fd311e5f',
'rsrc/css/phui/phui-header-view.css' => '31dc6c72', 'rsrc/css/phui/phui-header-view.css' => 'edeb9252',
'rsrc/css/phui/phui-hovercard.css' => 'f0592bcf', 'rsrc/css/phui/phui-hovercard.css' => 'f0592bcf',
'rsrc/css/phui/phui-icon-set-selector.css' => '87db8fee', 'rsrc/css/phui/phui-icon-set-selector.css' => '87db8fee',
'rsrc/css/phui/phui-icon.css' => 'cf24ceec', 'rsrc/css/phui/phui-icon.css' => 'cf24ceec',
@ -821,7 +821,7 @@ return array(
'phui-form-css' => '7aaa04e3', 'phui-form-css' => '7aaa04e3',
'phui-form-view-css' => 'ae9f8d16', 'phui-form-view-css' => 'ae9f8d16',
'phui-head-thing-view-css' => 'fd311e5f', 'phui-head-thing-view-css' => 'fd311e5f',
'phui-header-view-css' => '31dc6c72', 'phui-header-view-css' => 'edeb9252',
'phui-hovercard' => '1bd28176', 'phui-hovercard' => '1bd28176',
'phui-hovercard-view-css' => 'f0592bcf', 'phui-hovercard-view-css' => 'f0592bcf',
'phui-icon-set-selector-css' => '87db8fee', 'phui-icon-set-selector-css' => '87db8fee',

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_phriction.phriction_document
ADD spacePHID VARBINARY(64) DEFAULT NULL;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_project.project
ADD COLUMN spacePHID VARBINARY(64) DEFAULT NULL;

View file

@ -27,7 +27,15 @@ $args->parsePartial(
'param' => pht('port'), 'param' => pht('port'),
'help' => pht('Port number to connect to.'), 'help' => pht('Port number to connect to.'),
), ),
array(
'name' => 'options',
'short' => 'o',
'param' => pht('options'),
'repeat' => true,
'help' => pht('SSH options.'),
),
)); ));
$unconsumed_argv = $args->getUnconsumedArgumentVector(); $unconsumed_argv = $args->getUnconsumedArgumentVector();
if (function_exists('pcntl_signal')) { if (function_exists('pcntl_signal')) {
@ -113,6 +121,25 @@ if ($port) {
$arguments[] = $port; $arguments[] = $port;
} }
$options = $args->getArg('options');
$allowed_ssh_options = array('SendEnv=GIT_PROTOCOL');
if (!empty($options)) {
foreach ($options as $option) {
if (array_search($option, $allowed_ssh_options) !== false) {
$pattern[] = '-o %s';
$arguments[] = $option;
} else {
throw new Exception(
pht(
'Disallowed ssh option "%s" given with "-o". '.
'Allowed options are: %s.',
$option,
implode(', ', $allowed_ssh_options)));
}
}
}
$pattern[] = '--'; $pattern[] = '--';
$pattern[] = '%s'; $pattern[] = '%s';

View file

@ -9752,6 +9752,7 @@ phutil_register_library_map(array(
'PhabricatorFerretInterface', 'PhabricatorFerretInterface',
'PhabricatorConduitResultInterface', 'PhabricatorConduitResultInterface',
'PhabricatorColumnProxyInterface', 'PhabricatorColumnProxyInterface',
'PhabricatorSpacesInterface',
), ),
'PhabricatorProjectAddHeraldAction' => 'PhabricatorProjectHeraldAction', 'PhabricatorProjectAddHeraldAction' => 'PhabricatorProjectHeraldAction',
'PhabricatorProjectApplication' => 'PhabricatorApplication', 'PhabricatorProjectApplication' => 'PhabricatorApplication',
@ -11128,6 +11129,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationTransactionInterface', 'PhabricatorApplicationTransactionInterface',
'PhabricatorConduitResultInterface', 'PhabricatorConduitResultInterface',
'PhabricatorPolicyCodexInterface', 'PhabricatorPolicyCodexInterface',
'PhabricatorSpacesInterface',
), ),
'PhrictionDocumentAuthorHeraldField' => 'PhrictionDocumentHeraldField', 'PhrictionDocumentAuthorHeraldField' => 'PhrictionDocumentHeraldField',
'PhrictionDocumentContentHeraldField' => 'PhrictionDocumentHeraldField', 'PhrictionDocumentContentHeraldField' => 'PhrictionDocumentHeraldField',

View file

@ -9,6 +9,14 @@ final class DiffusionGetRecentCommitsByPathConduitAPIMethod
return 'diffusion.getrecentcommitsbypath'; return 'diffusion.getrecentcommitsbypath';
} }
public function getMethodStatus() {
return self::METHOD_STATUS_DEPRECATED;
}
public function getMethodStatusDescription() {
return pht('Obsoleted by "diffusion.historyquery".');
}
public function getMethodDescription() { public function getMethodDescription() {
return pht( return pht(
'Get commit identifiers for recent commits affecting a given path.'); 'Get commit identifiers for recent commits affecting a given path.');
@ -23,6 +31,12 @@ final class DiffusionGetRecentCommitsByPathConduitAPIMethod
); );
} }
protected function defineErrorTypes() {
return array(
'ERR_NOT_FOUND' => pht('Repository was not found.'),
);
}
protected function defineReturnType() { protected function defineReturnType() {
return 'nonempty list<string>'; return 'nonempty list<string>';
} }
@ -36,6 +50,10 @@ final class DiffusionGetRecentCommitsByPathConduitAPIMethod
'branch' => $request->getValue('branch'), 'branch' => $request->getValue('branch'),
)); ));
if ($drequest === null) {
throw new ConduitException('ERR_NOT_FOUND');
}
$limit = nonempty( $limit = nonempty(
$request->getValue('limit'), $request->getValue('limit'),
self::DEFAULT_LIMIT); self::DEFAULT_LIMIT);

View file

@ -14,6 +14,8 @@ final class DiffusionPushLogListView extends AphrontView {
$logs = $this->logs; $logs = $this->logs;
$viewer = $this->getViewer(); $viewer = $this->getViewer();
$reject_herald = PhabricatorRepositoryPushLog::REJECT_HERALD;
$handle_phids = array(); $handle_phids = array();
foreach ($logs as $log) { foreach ($logs as $log) {
$handle_phids[] = $log->getPusherPHID(); $handle_phids[] = $log->getPusherPHID();
@ -21,9 +23,13 @@ final class DiffusionPushLogListView extends AphrontView {
if ($device_phid) { if ($device_phid) {
$handle_phids[] = $device_phid; $handle_phids[] = $device_phid;
} }
if ($log->getPushEvent()->getRejectCode() == $reject_herald) {
$handle_phids[] = $log->getPushEvent()->getRejectDetails();
}
} }
$handles = $viewer->loadHandles($handle_phids); $viewer->loadHandles($handle_phids);
// Only administrators can view remote addresses. // Only administrators can view remote addresses.
$remotes_visible = $viewer->getIsAdmin(); $remotes_visible = $viewer->getIsAdmin();
@ -74,10 +80,17 @@ final class DiffusionPushLogListView extends AphrontView {
$flag_names); $flag_names);
$reject_code = $log->getPushEvent()->getRejectCode(); $reject_code = $log->getPushEvent()->getRejectCode();
$reject_label = idx(
$reject_map, if ($reject_code == $reject_herald) {
$reject_code, $rule_phid = $log->getPushEvent()->getRejectDetails();
pht('Unknown ("%s")', $reject_code)); $handle = $viewer->renderHandle($rule_phid);
$reject_label = pht('Blocked: %s', $handle);
} else {
$reject_label = idx(
$reject_map,
$reject_code,
pht('Unknown ("%s")', $reject_code));
}
$rows[] = array( $rows[] = array(
phutil_tag( phutil_tag(

View file

@ -159,10 +159,22 @@ final class PhabricatorFile extends PhabricatorFileDAO
public function saveAndIndex() { public function saveAndIndex() {
$this->save(); $this->save();
PhabricatorSearchWorker::queueDocumentForIndexing($this->getPHID());
if ($this->isIndexableFile()) {
PhabricatorSearchWorker::queueDocumentForIndexing($this->getPHID());
}
return $this; return $this;
} }
private function isIndexableFile() {
if ($this->getIsChunk()) {
return false;
}
return true;
}
public function getMonogram() { public function getMonogram() {
return 'F'.$this->getID(); return 'F'.$this->getID();
} }

View file

@ -643,7 +643,13 @@ final class HarbormasterBuildLog
$pos += $slice_length; $pos += $slice_length;
$map_bytes += $slice_length; $map_bytes += $slice_length;
$line_count += count(preg_split("/\r\n|\r|\n/", $slice)) - 1;
// Count newlines in the slice. This goofy approach is meaningfully
// faster than "preg_match_all()" or "preg_split()". See PHI766.
$n_rn = substr_count($slice, "\r\n");
$n_r = substr_count($slice, "\r");
$n_n = substr_count($slice, "\n");
$line_count += ($n_rn) + ($n_r - $n_rn) + ($n_n - $n_rn);
if ($map_bytes >= ($marker_distance - $max_utf8_width)) { if ($map_bytes >= ($marker_distance - $max_utf8_width)) {
$map[] = array( $map[] = array(

View file

@ -19,10 +19,18 @@ final class HeraldRuleDatasource
$viewer = $this->getViewer(); $viewer = $this->getViewer();
$raw_query = $this->getRawQuery(); $raw_query = $this->getRawQuery();
$rules = id(new HeraldRuleQuery()) $query = id(new HeraldRuleQuery())
->setViewer($viewer) ->setViewer($viewer);
->withDatasourceQuery($raw_query)
->execute(); if (preg_match('/^[hH]\d+\z/', $raw_query)) {
$id = trim($raw_query, 'hH');
$id = (int)$id;
$query->withIDs(array($id));
} else {
$query->withDatasourceQuery($raw_query);
}
$rules = $query->execute();
$handles = id(new PhabricatorHandleQuery()) $handles = id(new PhabricatorHandleQuery())
->setViewer($viewer) ->setViewer($viewer)

View file

@ -6,6 +6,9 @@ abstract class PhabricatorMailImplementationAdapter extends Phobject {
private $priority; private $priority;
private $options = array(); private $options = array();
private $supportsInbound = true;
private $supportsOutbound = true;
final public function getAdapterType() { final public function getAdapterType() {
return $this->getPhobjectClassConstant('ADAPTERTYPE'); return $this->getPhobjectClassConstant('ADAPTERTYPE');
} }
@ -67,6 +70,24 @@ abstract class PhabricatorMailImplementationAdapter extends Phobject {
return $this->priority; return $this->priority;
} }
final public function setSupportsInbound($supports_inbound) {
$this->supportsInbound = $supports_inbound;
return $this;
}
final public function getSupportsInbound() {
return $this->supportsInbound;
}
final public function setSupportsOutbound($supports_outbound) {
$this->supportsOutbound = $supports_outbound;
return $this;
}
final public function getSupportsOutbound() {
return $this->supportsOutbound;
}
final public function getOption($key) { final public function getOption($key) {
if (!array_key_exists($key, $this->options)) { if (!array_key_exists($key, $this->options)) {
throw new Exception( throw new Exception(

View file

@ -17,9 +17,12 @@ final class PhabricatorMetaMTAMailgunReceiveController
// inbound mail from any of them. Test the signature to see if it matches // inbound mail from any of them. Test the signature to see if it matches
// any configured Mailgun mailer. // any configured Mailgun mailer.
$mailers = PhabricatorMetaMTAMail::newMailersWithTypes( $mailers = PhabricatorMetaMTAMail::newMailers(
array( array(
PhabricatorMailImplementationMailgunAdapter::ADAPTERTYPE, 'inbound' => true,
'types' => array(
PhabricatorMailImplementationMailgunAdapter::ADAPTERTYPE,
),
)); ));
foreach ($mailers as $mailer) { foreach ($mailers as $mailer) {
$api_key = $mailer->getOption('api-key'); $api_key = $mailer->getOption('api-key');

View file

@ -12,9 +12,12 @@ final class PhabricatorMetaMTAPostmarkReceiveController
*/ */
public function handleRequest(AphrontRequest $request) { public function handleRequest(AphrontRequest $request) {
// Don't process requests if we don't have a configured Postmark adapter. // Don't process requests if we don't have a configured Postmark adapter.
$mailers = PhabricatorMetaMTAMail::newMailersWithTypes( $mailers = PhabricatorMetaMTAMail::newMailers(
array( array(
PhabricatorMailImplementationPostmarkAdapter::ADAPTERTYPE, 'inbound' => true,
'types' => array(
PhabricatorMailImplementationPostmarkAdapter::ADAPTERTYPE,
),
)); ));
if (!$mailers) { if (!$mailers) {
return new Aphront404Response(); return new Aphront404Response();

View file

@ -11,9 +11,12 @@ final class PhabricatorMetaMTASendGridReceiveController
// SendGrid doesn't sign payloads so we can't be sure that SendGrid // SendGrid doesn't sign payloads so we can't be sure that SendGrid
// actually sent this request, but require a configured SendGrid mailer // actually sent this request, but require a configured SendGrid mailer
// before we activate this endpoint. // before we activate this endpoint.
$mailers = PhabricatorMetaMTAMail::newMailersWithTypes( $mailers = PhabricatorMetaMTAMail::newMailers(
array( array(
PhabricatorMailImplementationSendGridAdapter::ADAPTERTYPE, 'inbound' => true,
'types' => array(
PhabricatorMailImplementationSendGridAdapter::ADAPTERTYPE,
),
)); ));
if (!$mailers) { if (!$mailers) {
return new Aphront404Response(); return new Aphront404Response();

View file

@ -168,7 +168,8 @@ final class PhabricatorMailManagementSendTestWorkflow
$mailer_key = $args->getArg('mailer'); $mailer_key = $args->getArg('mailer');
if ($mailer_key !== null) { if ($mailer_key !== null) {
$mailers = PhabricatorMetaMTAMail::newMailers(); $mailers = PhabricatorMetaMTAMail::newMailers(array());
$mailers = mpull($mailers, null, 'getKey'); $mailers = mpull($mailers, null, 'getKey');
if (!isset($mailers[$mailer_key])) { if (!isset($mailers[$mailer_key])) {
throw new PhutilArgumentUsageException( throw new PhutilArgumentUsageException(
@ -178,6 +179,13 @@ final class PhabricatorMailManagementSendTestWorkflow
implode(', ', array_keys($mailers)))); implode(', ', array_keys($mailers))));
} }
if (!$mailers[$mailer_key]->getSupportsOutbound()) {
throw new PhutilArgumentUsageException(
pht(
'Mailer ("%s") is not configured to support outbound mail.',
$mailer_key));
}
$mail->setTryMailers(array($mailer_key)); $mail->setTryMailers(array($mailer_key));
} }

View file

@ -494,7 +494,10 @@ final class PhabricatorMetaMTAMail
throw new Exception(pht('Trying to send an already-sent mail!')); throw new Exception(pht('Trying to send an already-sent mail!'));
} }
$mailers = self::newMailers(); $mailers = self::newMailers(
array(
'outbound' => true,
));
$try_mailers = $this->getParam('mailers.try'); $try_mailers = $this->getParam('mailers.try');
if ($try_mailers) { if ($try_mailers) {
@ -505,21 +508,15 @@ final class PhabricatorMetaMTAMail
return $this->sendWithMailers($mailers); return $this->sendWithMailers($mailers);
} }
public static function newMailersWithTypes(array $types) { public static function newMailers(array $constraints) {
$mailers = self::newMailers(); PhutilTypeSpec::checkMap(
$types = array_fuse($types); $constraints,
array(
'types' => 'optional list<string>',
'inbound' => 'optional bool',
'outbound' => 'optional bool',
));
foreach ($mailers as $key => $mailer) {
$mailer_type = $mailer->getAdapterType();
if (!isset($types[$mailer_type])) {
unset($mailers[$key]);
}
}
return array_values($mailers);
}
public static function newMailers() {
$mailers = array(); $mailers = array();
$config = PhabricatorEnv::getEnvConfig('cluster.mailers'); $config = PhabricatorEnv::getEnvConfig('cluster.mailers');
@ -565,10 +562,45 @@ final class PhabricatorMetaMTAMail
$options = idx($spec, 'options', array()) + $defaults; $options = idx($spec, 'options', array()) + $defaults;
$mailer->setOptions($options); $mailer->setOptions($options);
$mailer->setSupportsInbound(idx($spec, 'inbound', true));
$mailer->setSupportsOutbound(idx($spec, 'outbound', true));
$mailers[] = $mailer; $mailers[] = $mailer;
} }
} }
// Remove mailers with the wrong types.
if (isset($constraints['types'])) {
$types = $constraints['types'];
$types = array_fuse($types);
foreach ($mailers as $key => $mailer) {
$mailer_type = $mailer->getAdapterType();
if (!isset($types[$mailer_type])) {
unset($mailers[$key]);
}
}
}
// If we're only looking for inbound mailers, remove mailers with inbound
// support disabled.
if (!empty($constraints['inbound'])) {
foreach ($mailers as $key => $mailer) {
if (!$mailer->getSupportsInbound()) {
unset($mailers[$key]);
}
}
}
// If we're only looking for outbound mailers, remove mailers with outbound
// support disabled.
if (!empty($constraints['outbound'])) {
foreach ($mailers as $key => $mailer) {
if (!$mailer->getSupportsOutbound()) {
unset($mailers[$key]);
}
}
}
$sorted = array(); $sorted = array();
$groups = mgroup($mailers, 'getPriority'); $groups = mgroup($mailers, 'getPriority');
krsort($groups); krsort($groups);
@ -589,9 +621,24 @@ final class PhabricatorMetaMTAMail
public function sendWithMailers(array $mailers) { public function sendWithMailers(array $mailers) {
if (!$mailers) { if (!$mailers) {
$any_mailers = self::newMailers();
// NOTE: We can end up here with some custom list of "$mailers", like
// from a unit test. In that case, this message could be misleading. We
// can't really tell if the caller made up the list, so just assume they
// aren't tricking us.
if ($any_mailers) {
$void_message = pht(
'No configured mailers support sending outbound mail.');
} else {
$void_message = pht(
'No mailers are configured.');
}
return $this return $this
->setStatus(PhabricatorMailOutboundStatus::STATUS_VOID) ->setStatus(PhabricatorMailOutboundStatus::STATUS_VOID)
->setMessage(pht('No mailers are configured.')) ->setMessage($void_message)
->save(); ->save();
} }

View file

@ -118,11 +118,80 @@ final class PhabricatorMailConfigTestCase
$this->assertTrue($saw_a1 && $saw_a2); $this->assertTrue($saw_a1 && $saw_a2);
} }
private function newMailersWithConfig(array $config) { public function testMailerConstraints() {
$config = array(
array(
'key' => 'X1',
'type' => 'test',
),
array(
'key' => 'X1-in',
'type' => 'test',
'outbound' => false,
),
array(
'key' => 'X1-out',
'type' => 'test',
'inbound' => false,
),
array(
'key' => 'X1-void',
'type' => 'test',
'inbound' => false,
'outbound' => false,
),
);
$mailers = $this->newMailersWithConfig(
$config,
array());
$this->assertEqual(4, count($mailers));
$mailers = $this->newMailersWithConfig(
$config,
array(
'inbound' => true,
));
$this->assertEqual(2, count($mailers));
$mailers = $this->newMailersWithConfig(
$config,
array(
'outbound' => true,
));
$this->assertEqual(2, count($mailers));
$mailers = $this->newMailersWithConfig(
$config,
array(
'inbound' => true,
'outbound' => true,
));
$this->assertEqual(1, count($mailers));
$mailers = $this->newMailersWithConfig(
$config,
array(
'types' => array('test'),
));
$this->assertEqual(4, count($mailers));
$mailers = $this->newMailersWithConfig(
$config,
array(
'types' => array('duck'),
));
$this->assertEqual(0, count($mailers));
}
private function newMailersWithConfig(
array $config,
array $constraints = array()) {
$env = PhabricatorEnv::beginScopedEnv(); $env = PhabricatorEnv::beginScopedEnv();
$env->overrideEnvConfig('cluster.mailers', $config); $env->overrideEnvConfig('cluster.mailers', $config);
$mailers = PhabricatorMetaMTAMail::newMailers(); $mailers = PhabricatorMetaMTAMail::newMailers($constraints);
unset($env); unset($env);
return $mailers; return $mailers;

View file

@ -254,7 +254,8 @@ final class PhrictionDocumentController
PhrictionContent $content, PhrictionContent $content,
$slug) { $slug) {
$viewer = $this->getRequest()->getUser(); $viewer = $this->getViewer();
$view = id(new PHUIPropertyListView()) $view = id(new PHUIPropertyListView())
->setUser($viewer) ->setUser($viewer)
->setObject($document); ->setObject($document);
@ -263,6 +264,10 @@ final class PhrictionDocumentController
pht('Last Author'), pht('Last Author'),
$viewer->renderHandle($content->getAuthorPHID())); $viewer->renderHandle($content->getAuthorPHID()));
$view->addProperty(
pht('Last Edited'),
phabricator_datetime($content->getDateCreated(), $viewer));
return $view; return $view;
} }

View file

@ -121,6 +121,8 @@ final class PhrictionEditController
$v_projects = array_reverse($v_projects); $v_projects = array_reverse($v_projects);
} }
$v_space = $document->getSpacePHID();
if ($request->isFormPost()) { if ($request->isFormPost()) {
$title = $request->getStr('title'); $title = $request->getStr('title');
@ -131,6 +133,7 @@ final class PhrictionEditController
$v_edit = $request->getStr('editPolicy'); $v_edit = $request->getStr('editPolicy');
$v_cc = $request->getArr('cc'); $v_cc = $request->getArr('cc');
$v_projects = $request->getArr('projects'); $v_projects = $request->getArr('projects');
$v_space = $request->getStr('spacePHID');
$xactions = array(); $xactions = array();
$xactions[] = id(new PhrictionTransaction()) $xactions[] = id(new PhrictionTransaction())
@ -146,6 +149,9 @@ final class PhrictionEditController
$xactions[] = id(new PhrictionTransaction()) $xactions[] = id(new PhrictionTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY) ->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)
->setNewValue($v_edit); ->setNewValue($v_edit);
$xactions[] = id(new PhrictionTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_SPACE)
->setNewValue($v_space);
$xactions[] = id(new PhrictionTransaction()) $xactions[] = id(new PhrictionTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS) ->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS)
->setNewValue(array('=' => $v_cc)); ->setNewValue(array('=' => $v_cc));
@ -192,6 +198,7 @@ final class PhrictionEditController
$document->setViewPolicy($v_view); $document->setViewPolicy($v_view);
$document->setEditPolicy($v_edit); $document->setEditPolicy($v_edit);
$document->setSpacePHID($v_space);
} }
} }
@ -267,7 +274,9 @@ final class PhrictionEditController
->setDatasource(new PhabricatorMetaMTAMailableDatasource())) ->setDatasource(new PhabricatorMetaMTAMailableDatasource()))
->appendChild( ->appendChild(
id(new AphrontFormPolicyControl()) id(new AphrontFormPolicyControl())
->setViewer($viewer)
->setName('viewPolicy') ->setName('viewPolicy')
->setSpacePHID($v_space)
->setPolicyObject($document) ->setPolicyObject($document)
->setCapability($view_capability) ->setCapability($view_capability)
->setPolicies($policies) ->setPolicies($policies)

View file

@ -131,6 +131,7 @@ final class PhrictionDocumentSearchEngine
$item = id(new PHUIObjectItemView()) $item = id(new PHUIObjectItemView())
->setHeader($content->getTitle()) ->setHeader($content->getTitle())
->setObject($document)
->setHref($slug_uri) ->setHref($slug_uri)
->addByline($byline) ->addByline($byline)
->addIcon('none', $updated); ->addIcon('none', $updated);

View file

@ -12,7 +12,8 @@ final class PhrictionDocument extends PhrictionDAO
PhabricatorProjectInterface, PhabricatorProjectInterface,
PhabricatorApplicationTransactionInterface, PhabricatorApplicationTransactionInterface,
PhabricatorConduitResultInterface, PhabricatorConduitResultInterface,
PhabricatorPolicyCodexInterface { PhabricatorPolicyCodexInterface,
PhabricatorSpacesInterface {
protected $slug; protected $slug;
protected $depth; protected $depth;
@ -21,6 +22,7 @@ final class PhrictionDocument extends PhrictionDAO
protected $mailKey; protected $mailKey;
protected $viewPolicy; protected $viewPolicy;
protected $editPolicy; protected $editPolicy;
protected $spacePHID;
private $contentObject = self::ATTACHABLE; private $contentObject = self::ATTACHABLE;
private $ancestors = array(); private $ancestors = array();
@ -81,12 +83,16 @@ final class PhrictionDocument extends PhrictionDAO
} }
if ($parent_doc) { if ($parent_doc) {
$document->setViewPolicy($parent_doc->getViewPolicy()); $document
$document->setEditPolicy($parent_doc->getEditPolicy()); ->setViewPolicy($parent_doc->getViewPolicy())
->setEditPolicy($parent_doc->getEditPolicy())
->setSpacePHID($parent_doc->getSpacePHID());
} else { } else {
$default_view_policy = PhabricatorPolicies::getMostOpenPolicy(); $default_view_policy = PhabricatorPolicies::getMostOpenPolicy();
$document->setViewPolicy($default_view_policy); $document
$document->setEditPolicy(PhabricatorPolicies::POLICY_USER); ->setViewPolicy($default_view_policy)
->setEditPolicy(PhabricatorPolicies::POLICY_USER)
->setSpacePHID($actor->getDefaultSpacePHID());
} }
return $document; return $document;
@ -202,6 +208,15 @@ final class PhrictionDocument extends PhrictionDAO
} }
/* -( PhabricatorSpacesInterface )----------------------------------------- */
public function getSpacePHID() {
return $this->spacePHID;
}
/* -( PhabricatorSubscribableInterface )----------------------------------- */ /* -( PhabricatorSubscribableInterface )----------------------------------- */

View file

@ -106,8 +106,8 @@ final class PhabricatorProjectSubprojectsController
->addClass('project-view-people-home') ->addClass('project-view-people-home')
->setMainColumn(array( ->setMainColumn(array(
$info_view, $info_view,
$milestone_list,
$subproject_list, $subproject_list,
$milestone_list,
)); ));
return $this->newPage() return $this->newPage()
@ -132,28 +132,11 @@ final class PhabricatorProjectSubprojectsController
$project, $project,
PhabricatorPolicyCapability::CAN_EDIT); PhabricatorPolicyCapability::CAN_EDIT);
$allows_milestones = $project->supportsMilestones();
$allows_subprojects = $project->supportsSubprojects(); $allows_subprojects = $project->supportsSubprojects();
$allows_milestones = $project->supportsMilestones();
$curtain = $this->newCurtainView(); $curtain = $this->newCurtainView();
if ($allows_milestones && $milestones) {
$milestone_text = pht('Create Next Milestone');
} else {
$milestone_text = pht('Create Milestone');
}
$can_milestone = ($can_create && $can_edit && $allows_milestones);
$milestone_href = "/project/edit/?milestone={$id}";
$curtain->addAction(
id(new PhabricatorActionView())
->setName($milestone_text)
->setIcon('fa-plus')
->setHref($milestone_href)
->setDisabled(!$can_milestone)
->setWorkflow(!$can_milestone));
$can_subproject = ($can_create && $can_edit && $allows_subprojects); $can_subproject = ($can_create && $can_edit && $allows_subprojects);
// If we're offering to create the first subproject, we're going to warn // If we're offering to create the first subproject, we're going to warn
@ -176,22 +159,22 @@ final class PhabricatorProjectSubprojectsController
->setDisabled($subproject_disabled) ->setDisabled($subproject_disabled)
->setWorkflow($subproject_workflow)); ->setWorkflow($subproject_workflow));
if ($allows_milestones && $milestones) {
if (!$project->supportsMilestones()) { $milestone_text = pht('Create Next Milestone');
$note = pht(
'This project is already a milestone, and milestones may not '.
'have their own milestones.');
} else { } else {
if (!$milestones) { $milestone_text = pht('Create Milestone');
$note = pht('Milestones can be created for this project.');
} else {
$note = pht('This project has milestones.');
}
} }
$curtain->newPanel() $can_milestone = ($can_create && $can_edit && $allows_milestones);
->setHeaderText(pht('Milestones')) $milestone_href = "/project/edit/?milestone={$id}";
->appendChild($note);
$curtain->addAction(
id(new PhabricatorActionView())
->setName($milestone_text)
->setIcon('fa-plus')
->setHref($milestone_href)
->setDisabled(!$can_milestone)
->setWorkflow(!$can_milestone));
if (!$project->supportsSubprojects()) { if (!$project->supportsSubprojects()) {
$note = pht( $note = pht(
@ -209,6 +192,22 @@ final class PhabricatorProjectSubprojectsController
->setHeaderText(pht('Subprojects')) ->setHeaderText(pht('Subprojects'))
->appendChild($note); ->appendChild($note);
if (!$project->supportsSubprojects()) {
$note = pht(
'This project is already a milestone, and milestones may not '.
'have their own milestones.');
} else {
if (!$milestones) {
$note = pht('Milestones can be created for this project.');
} else {
$note = pht('This project has milestones.');
}
}
$curtain->newPanel()
->setHeaderText(pht('Milestones'))
->appendChild($note);
return $curtain; return $curtain;
} }

View file

@ -51,7 +51,11 @@ final class PhabricatorProjectEditEngine
} }
protected function newEditableObject() { protected function newEditableObject() {
return PhabricatorProject::initializeNewProject($this->getViewer()); $parent = nonempty($this->parentProject, $this->milestoneProject);
return PhabricatorProject::initializeNewProject(
$this->getViewer(),
$parent);
} }
protected function newObjectQuery() { protected function newObjectQuery() {
@ -112,6 +116,7 @@ final class PhabricatorProjectEditEngine
PhabricatorTransactions::TYPE_VIEW_POLICY, PhabricatorTransactions::TYPE_VIEW_POLICY,
PhabricatorTransactions::TYPE_EDIT_POLICY, PhabricatorTransactions::TYPE_EDIT_POLICY,
PhabricatorTransactions::TYPE_JOIN_POLICY, PhabricatorTransactions::TYPE_JOIN_POLICY,
PhabricatorTransactions::TYPE_SPACE,
PhabricatorProjectIconTransaction::TRANSACTIONTYPE, PhabricatorProjectIconTransaction::TRANSACTIONTYPE,
PhabricatorProjectColorTransaction::TRANSACTIONTYPE, PhabricatorProjectColorTransaction::TRANSACTIONTYPE,
); );

View file

@ -11,7 +11,8 @@ final class PhabricatorProject extends PhabricatorProjectDAO
PhabricatorFulltextInterface, PhabricatorFulltextInterface,
PhabricatorFerretInterface, PhabricatorFerretInterface,
PhabricatorConduitResultInterface, PhabricatorConduitResultInterface,
PhabricatorColumnProxyInterface { PhabricatorColumnProxyInterface,
PhabricatorSpacesInterface {
protected $name; protected $name;
protected $status = PhabricatorProjectStatus::STATUS_ACTIVE; protected $status = PhabricatorProjectStatus::STATUS_ACTIVE;
@ -38,6 +39,7 @@ final class PhabricatorProject extends PhabricatorProjectDAO
protected $projectPathKey; protected $projectPathKey;
protected $properties = array(); protected $properties = array();
protected $spacePHID;
private $memberPHIDs = self::ATTACHABLE; private $memberPHIDs = self::ATTACHABLE;
private $watcherPHIDs = self::ATTACHABLE; private $watcherPHIDs = self::ATTACHABLE;
@ -59,7 +61,10 @@ final class PhabricatorProject extends PhabricatorProjectDAO
const ITEM_MILESTONES = 'project.milestones'; const ITEM_MILESTONES = 'project.milestones';
const ITEM_SUBPROJECTS = 'project.subprojects'; const ITEM_SUBPROJECTS = 'project.subprojects';
public static function initializeNewProject(PhabricatorUser $actor) { public static function initializeNewProject(
PhabricatorUser $actor,
PhabricatorProject $parent = null) {
$app = id(new PhabricatorApplicationQuery()) $app = id(new PhabricatorApplicationQuery())
->setViewer(PhabricatorUser::getOmnipotentUser()) ->setViewer(PhabricatorUser::getOmnipotentUser())
->withClasses(array('PhabricatorProjectApplication')) ->withClasses(array('PhabricatorProjectApplication'))
@ -72,6 +77,14 @@ final class PhabricatorProject extends PhabricatorProjectDAO
$join_policy = $app->getPolicy( $join_policy = $app->getPolicy(
ProjectDefaultJoinCapability::CAPABILITY); ProjectDefaultJoinCapability::CAPABILITY);
// If this is the child of some other project, default the Space to the
// Space of the parent.
if ($parent) {
$space_phid = $parent->getSpacePHID();
} else {
$space_phid = $actor->getDefaultSpacePHID();
}
$default_icon = PhabricatorProjectIconSet::getDefaultIconKey(); $default_icon = PhabricatorProjectIconSet::getDefaultIconKey();
$default_color = PhabricatorProjectIconSet::getDefaultColorKey(); $default_color = PhabricatorProjectIconSet::getDefaultColorKey();
@ -82,6 +95,7 @@ final class PhabricatorProject extends PhabricatorProjectDAO
->setViewPolicy($view_policy) ->setViewPolicy($view_policy)
->setEditPolicy($edit_policy) ->setEditPolicy($edit_policy)
->setJoinPolicy($join_policy) ->setJoinPolicy($join_policy)
->setSpacePHID($space_phid)
->setIsMembershipLocked(0) ->setIsMembershipLocked(0)
->attachMemberPHIDs(array()) ->attachMemberPHIDs(array())
->attachSlugs(array()) ->attachSlugs(array())
@ -697,6 +711,17 @@ final class PhabricatorProject extends PhabricatorProjectDAO
} }
/* -( PhabricatorSpacesInterface )----------------------------------------- */
public function getSpacePHID() {
if ($this->isMilestone()) {
return $this->getParentProject()->getSpacePHID();
}
return $this->spacePHID;
}
/* -( PhabricatorDestructibleInterface )----------------------------------- */ /* -( PhabricatorDestructibleInterface )----------------------------------- */

View file

@ -57,6 +57,7 @@ final class PhabricatorProjectListView extends AphrontView {
$icon_name = $project->getDisplayIconName(); $icon_name = $project->getDisplayIconName();
$item = id(new PHUIObjectItemView()) $item = id(new PHUIObjectItemView())
->setObject($project)
->setHeader($project->getName()) ->setHeader($project->getName())
->setHref("/project/view/{$id}/") ->setHref("/project/view/{$id}/")
->setImageURI($project->getProfileImageURI()) ->setImageURI($project->getProfileImageURI())

View file

@ -12,6 +12,7 @@ final class PhabricatorRepositoryPushLogQuery
private $pushEventPHIDs; private $pushEventPHIDs;
private $epochMin; private $epochMin;
private $epochMax; private $epochMax;
private $blockingHeraldRulePHIDs;
public function withIDs(array $ids) { public function withIDs(array $ids) {
$this->ids = $ids; $this->ids = $ids;
@ -54,6 +55,11 @@ final class PhabricatorRepositoryPushLogQuery
return $this; return $this;
} }
public function withBlockingHeraldRulePHIDs(array $phids) {
$this->blockingHeraldRulePHIDs = $phids;
return $this;
}
public function newResultObject() { public function newResultObject() {
return new PhabricatorRepositoryPushLog(); return new PhabricatorRepositoryPushLog();
} }
@ -89,71 +95,105 @@ final class PhabricatorRepositoryPushLogQuery
if ($this->ids !== null) { if ($this->ids !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn, $conn,
'id IN (%Ld)', 'log.id IN (%Ld)',
$this->ids); $this->ids);
} }
if ($this->phids !== null) { if ($this->phids !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn, $conn,
'phid IN (%Ls)', 'log.phid IN (%Ls)',
$this->phids); $this->phids);
} }
if ($this->repositoryPHIDs !== null) { if ($this->repositoryPHIDs !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn, $conn,
'repositoryPHID IN (%Ls)', 'log.repositoryPHID IN (%Ls)',
$this->repositoryPHIDs); $this->repositoryPHIDs);
} }
if ($this->pusherPHIDs !== null) { if ($this->pusherPHIDs !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn, $conn,
'pusherPHID in (%Ls)', 'log.pusherPHID in (%Ls)',
$this->pusherPHIDs); $this->pusherPHIDs);
} }
if ($this->pushEventPHIDs !== null) { if ($this->pushEventPHIDs !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn, $conn,
'pushEventPHID in (%Ls)', 'log.pushEventPHID in (%Ls)',
$this->pushEventPHIDs); $this->pushEventPHIDs);
} }
if ($this->refTypes !== null) { if ($this->refTypes !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn, $conn,
'refType IN (%Ls)', 'log.refType IN (%Ls)',
$this->refTypes); $this->refTypes);
} }
if ($this->newRefs !== null) { if ($this->newRefs !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn, $conn,
'refNew IN (%Ls)', 'log.refNew IN (%Ls)',
$this->newRefs); $this->newRefs);
} }
if ($this->epochMin !== null) { if ($this->epochMin !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn, $conn,
'epoch >= %d', 'log.epoch >= %d',
$this->epochMin); $this->epochMin);
} }
if ($this->epochMax !== null) { if ($this->epochMax !== null) {
$where[] = qsprintf( $where[] = qsprintf(
$conn, $conn,
'epoch <= %d', 'log.epoch <= %d',
$this->epochMax); $this->epochMax);
} }
if ($this->blockingHeraldRulePHIDs !== null) {
$where[] = qsprintf(
$conn,
'(event.rejectCode = %d AND event.rejectDetails IN (%Ls))',
PhabricatorRepositoryPushLog::REJECT_HERALD,
$this->blockingHeraldRulePHIDs);
}
return $where; return $where;
} }
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
$joins = parent::buildJoinClauseParts($conn);
if ($this->shouldJoinPushEventTable()) {
$joins[] = qsprintf(
$conn,
'JOIN %T event ON event.phid = log.pushEventPHID',
id(new PhabricatorRepositoryPushEvent())->getTableName());
}
return $joins;
}
private function shouldJoinPushEventTable() {
if ($this->blockingHeraldRulePHIDs !== null) {
return true;
}
return false;
}
public function getQueryApplicationClass() { public function getQueryApplicationClass() {
return 'PhabricatorDiffusionApplication'; return 'PhabricatorDiffusionApplication';
} }
protected function getPrimaryTableAlias() {
return 'log';
}
} }

View file

@ -32,6 +32,10 @@ final class PhabricatorRepositoryPushLogSearchEngine
$map['createdEnd']); $map['createdEnd']);
} }
if ($map['blockingHeraldRulePHIDs']) {
$query->withBlockingHeraldRulePHIDs($map['blockingHeraldRulePHIDs']);
}
return $query; return $query;
} }
@ -43,13 +47,19 @@ final class PhabricatorRepositoryPushLogSearchEngine
->setAliases(array('repository', 'repositories', 'repositoryPHID')) ->setAliases(array('repository', 'repositories', 'repositoryPHID'))
->setLabel(pht('Repositories')) ->setLabel(pht('Repositories'))
->setDescription( ->setDescription(
pht('Search for pull logs for specific repositories.')), pht('Search for push logs for specific repositories.')),
id(new PhabricatorUsersSearchField()) id(new PhabricatorUsersSearchField())
->setKey('pusherPHIDs') ->setKey('pusherPHIDs')
->setAliases(array('pusher', 'pushers', 'pusherPHID')) ->setAliases(array('pusher', 'pushers', 'pusherPHID'))
->setLabel(pht('Pushers')) ->setLabel(pht('Pushers'))
->setDescription( ->setDescription(
pht('Search for pull logs by specific users.')), pht('Search for push logs by specific users.')),
id(new PhabricatorSearchDatasourceField())
->setDatasource(new HeraldRuleDatasource())
->setKey('blockingHeraldRulePHIDs')
->setLabel(pht('Blocked By'))
->setDescription(
pht('Search for pushes blocked by particular Herald rules.')),
id(new PhabricatorSearchDateField()) id(new PhabricatorSearchDateField())
->setLabel(pht('Created After')) ->setLabel(pht('Created After'))
->setKey('createdStart'), ->setKey('createdStart'),
@ -149,6 +159,9 @@ final class PhabricatorRepositoryPushLogSearchEngine
id(new PhabricatorStringExportField()) id(new PhabricatorStringExportField())
->setKey('resultName') ->setKey('resultName')
->setLabel(pht('Result Name')), ->setLabel(pht('Result Name')),
id(new PhabricatorStringExportField())
->setKey('resultDetails')
->setLabel(pht('Result Details')),
id(new PhabricatorIntExportField()) id(new PhabricatorIntExportField())
->setKey('writeWait') ->setKey('writeWait')
->setLabel(pht('Write Wait (us)')), ->setLabel(pht('Write Wait (us)')),
@ -237,6 +250,7 @@ final class PhabricatorRepositoryPushLogSearchEngine
'flagNames' => $flag_names, 'flagNames' => $flag_names,
'result' => $result, 'result' => $result,
'resultName' => $result_name, 'resultName' => $result_name,
'resultDetails' => $event->getRejectDetails(),
'writeWait' => $event->getWriteWait(), 'writeWait' => $event->getWriteWait(),
'readWait' => $event->getReadWait(), 'readWait' => $event->getReadWait(),
'hostWait' => $event->getHostWait(), 'hostWait' => $event->getHostWait(),

View file

@ -49,6 +49,9 @@ final class PhabricatorRepositoryPushEvent
'key_identifier' => array( 'key_identifier' => array(
'columns' => array('requestIdentifier'), 'columns' => array('requestIdentifier'),
), ),
'key_reject' => array(
'columns' => array('rejectCode', 'rejectDetails'),
),
), ),
) + parent::getConfiguration(); ) + parent::getConfiguration();
} }

View file

@ -79,6 +79,14 @@ final class TransactionSearchConduitAPIMethod
)); ));
$with_phids = idx($constraints, 'phids'); $with_phids = idx($constraints, 'phids');
if ($with_phids === array()) {
throw new Exception(
pht(
'Constraint "phids" to "transaction.search" requires nonempty list, '.
'empty list provided.'));
}
if ($with_phids) { if ($with_phids) {
$xaction_query->withPHIDs($with_phids); $xaction_query->withPHIDs($with_phids);
} }

View file

@ -82,6 +82,10 @@ The supported keys for each mailer are:
- `priority`: Optional string. Advanced option which controls load balancing - `priority`: Optional string. Advanced option which controls load balancing
and failover behavior. See below for details. and failover behavior. See below for details.
- `options`: Optional map. Additional options for the mailer type. - `options`: Optional map. Additional options for the mailer type.
- `inbound`: Optional bool. Use `false` to prevent this mailer from being
used to receive inbound mail.
- `outbound`: Optional bool. Use `false` to prevent this mailer from being
used to send outbound mail.
The `type` field can be used to select these third-party mailers: The `type` field can be used to select these third-party mailers:

View file

@ -43,6 +43,8 @@ final class PhabricatorClusterMailersConfigType
'type' => 'string', 'type' => 'string',
'priority' => 'optional int', 'priority' => 'optional int',
'options' => 'optional wild', 'options' => 'optional wild',
'inbound' => 'optional bool',
'outbound' => 'optional bool',
)); ));
} catch (Exception $ex) { } catch (Exception $ex) {
throw $this->newException( throw $this->newException(

View file

@ -294,7 +294,8 @@ body .phui-header-shell.phui-bleed-header
} }
.spaces-name .phui-handle, .spaces-name .phui-handle,
.spaces-name a.phui-handle { .spaces-name a.phui-handle,
.phui-profile-header.phui-header-shell .spaces-name .phui-handle {
color: {$sh-redtext}; color: {$sh-redtext};
} }