1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-01-25 05:58:21 +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(
'conpherence.pkg.css' => 'e68cf1fa',
'conpherence.pkg.js' => '15191c65',
'core.pkg.css' => 'e5233bff',
'core.pkg.css' => 'f515619b',
'core.pkg.js' => '2058ec09',
'differential.pkg.css' => '06dc617c',
'differential.pkg.js' => 'ef19e026',
@ -154,7 +154,7 @@ return array(
'rsrc/css/phui/phui-form-view.css' => 'ae9f8d16',
'rsrc/css/phui/phui-form.css' => '7aaa04e3',
'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-icon-set-selector.css' => '87db8fee',
'rsrc/css/phui/phui-icon.css' => 'cf24ceec',
@ -821,7 +821,7 @@ return array(
'phui-form-css' => '7aaa04e3',
'phui-form-view-css' => 'ae9f8d16',
'phui-head-thing-view-css' => 'fd311e5f',
'phui-header-view-css' => '31dc6c72',
'phui-header-view-css' => 'edeb9252',
'phui-hovercard' => '1bd28176',
'phui-hovercard-view-css' => 'f0592bcf',
'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'),
'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();
if (function_exists('pcntl_signal')) {
@ -113,6 +121,25 @@ if ($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[] = '%s';

View file

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

View file

@ -9,6 +9,14 @@ final class DiffusionGetRecentCommitsByPathConduitAPIMethod
return 'diffusion.getrecentcommitsbypath';
}
public function getMethodStatus() {
return self::METHOD_STATUS_DEPRECATED;
}
public function getMethodStatusDescription() {
return pht('Obsoleted by "diffusion.historyquery".');
}
public function getMethodDescription() {
return pht(
'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() {
return 'nonempty list<string>';
}
@ -36,6 +50,10 @@ final class DiffusionGetRecentCommitsByPathConduitAPIMethod
'branch' => $request->getValue('branch'),
));
if ($drequest === null) {
throw new ConduitException('ERR_NOT_FOUND');
}
$limit = nonempty(
$request->getValue('limit'),
self::DEFAULT_LIMIT);

View file

@ -14,6 +14,8 @@ final class DiffusionPushLogListView extends AphrontView {
$logs = $this->logs;
$viewer = $this->getViewer();
$reject_herald = PhabricatorRepositoryPushLog::REJECT_HERALD;
$handle_phids = array();
foreach ($logs as $log) {
$handle_phids[] = $log->getPusherPHID();
@ -21,9 +23,13 @@ final class DiffusionPushLogListView extends AphrontView {
if ($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.
$remotes_visible = $viewer->getIsAdmin();
@ -74,10 +80,17 @@ final class DiffusionPushLogListView extends AphrontView {
$flag_names);
$reject_code = $log->getPushEvent()->getRejectCode();
$reject_label = idx(
$reject_map,
$reject_code,
pht('Unknown ("%s")', $reject_code));
if ($reject_code == $reject_herald) {
$rule_phid = $log->getPushEvent()->getRejectDetails();
$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(
phutil_tag(

View file

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

View file

@ -643,7 +643,13 @@ final class HarbormasterBuildLog
$pos += $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)) {
$map[] = array(

View file

@ -19,10 +19,18 @@ final class HeraldRuleDatasource
$viewer = $this->getViewer();
$raw_query = $this->getRawQuery();
$rules = id(new HeraldRuleQuery())
->setViewer($viewer)
->withDatasourceQuery($raw_query)
->execute();
$query = id(new HeraldRuleQuery())
->setViewer($viewer);
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())
->setViewer($viewer)

View file

@ -6,6 +6,9 @@ abstract class PhabricatorMailImplementationAdapter extends Phobject {
private $priority;
private $options = array();
private $supportsInbound = true;
private $supportsOutbound = true;
final public function getAdapterType() {
return $this->getPhobjectClassConstant('ADAPTERTYPE');
}
@ -67,6 +70,24 @@ abstract class PhabricatorMailImplementationAdapter extends Phobject {
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) {
if (!array_key_exists($key, $this->options)) {
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
// any configured Mailgun mailer.
$mailers = PhabricatorMetaMTAMail::newMailersWithTypes(
$mailers = PhabricatorMetaMTAMail::newMailers(
array(
PhabricatorMailImplementationMailgunAdapter::ADAPTERTYPE,
'inbound' => true,
'types' => array(
PhabricatorMailImplementationMailgunAdapter::ADAPTERTYPE,
),
));
foreach ($mailers as $mailer) {
$api_key = $mailer->getOption('api-key');

View file

@ -12,9 +12,12 @@ final class PhabricatorMetaMTAPostmarkReceiveController
*/
public function handleRequest(AphrontRequest $request) {
// Don't process requests if we don't have a configured Postmark adapter.
$mailers = PhabricatorMetaMTAMail::newMailersWithTypes(
$mailers = PhabricatorMetaMTAMail::newMailers(
array(
PhabricatorMailImplementationPostmarkAdapter::ADAPTERTYPE,
'inbound' => true,
'types' => array(
PhabricatorMailImplementationPostmarkAdapter::ADAPTERTYPE,
),
));
if (!$mailers) {
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
// actually sent this request, but require a configured SendGrid mailer
// before we activate this endpoint.
$mailers = PhabricatorMetaMTAMail::newMailersWithTypes(
$mailers = PhabricatorMetaMTAMail::newMailers(
array(
PhabricatorMailImplementationSendGridAdapter::ADAPTERTYPE,
'inbound' => true,
'types' => array(
PhabricatorMailImplementationSendGridAdapter::ADAPTERTYPE,
),
));
if (!$mailers) {
return new Aphront404Response();

View file

@ -168,7 +168,8 @@ final class PhabricatorMailManagementSendTestWorkflow
$mailer_key = $args->getArg('mailer');
if ($mailer_key !== null) {
$mailers = PhabricatorMetaMTAMail::newMailers();
$mailers = PhabricatorMetaMTAMail::newMailers(array());
$mailers = mpull($mailers, null, 'getKey');
if (!isset($mailers[$mailer_key])) {
throw new PhutilArgumentUsageException(
@ -178,6 +179,13 @@ final class PhabricatorMailManagementSendTestWorkflow
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));
}

View file

@ -494,7 +494,10 @@ final class PhabricatorMetaMTAMail
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');
if ($try_mailers) {
@ -505,21 +508,15 @@ final class PhabricatorMetaMTAMail
return $this->sendWithMailers($mailers);
}
public static function newMailersWithTypes(array $types) {
$mailers = self::newMailers();
$types = array_fuse($types);
public static function newMailers(array $constraints) {
PhutilTypeSpec::checkMap(
$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();
$config = PhabricatorEnv::getEnvConfig('cluster.mailers');
@ -565,10 +562,45 @@ final class PhabricatorMetaMTAMail
$options = idx($spec, 'options', array()) + $defaults;
$mailer->setOptions($options);
$mailer->setSupportsInbound(idx($spec, 'inbound', true));
$mailer->setSupportsOutbound(idx($spec, 'outbound', true));
$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();
$groups = mgroup($mailers, 'getPriority');
krsort($groups);
@ -589,9 +621,24 @@ final class PhabricatorMetaMTAMail
public function sendWithMailers(array $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
->setStatus(PhabricatorMailOutboundStatus::STATUS_VOID)
->setMessage(pht('No mailers are configured.'))
->setMessage($void_message)
->save();
}

View file

@ -118,11 +118,80 @@ final class PhabricatorMailConfigTestCase
$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->overrideEnvConfig('cluster.mailers', $config);
$mailers = PhabricatorMetaMTAMail::newMailers();
$mailers = PhabricatorMetaMTAMail::newMailers($constraints);
unset($env);
return $mailers;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -11,7 +11,8 @@ final class PhabricatorProject extends PhabricatorProjectDAO
PhabricatorFulltextInterface,
PhabricatorFerretInterface,
PhabricatorConduitResultInterface,
PhabricatorColumnProxyInterface {
PhabricatorColumnProxyInterface,
PhabricatorSpacesInterface {
protected $name;
protected $status = PhabricatorProjectStatus::STATUS_ACTIVE;
@ -38,6 +39,7 @@ final class PhabricatorProject extends PhabricatorProjectDAO
protected $projectPathKey;
protected $properties = array();
protected $spacePHID;
private $memberPHIDs = self::ATTACHABLE;
private $watcherPHIDs = self::ATTACHABLE;
@ -59,7 +61,10 @@ final class PhabricatorProject extends PhabricatorProjectDAO
const ITEM_MILESTONES = 'project.milestones';
const ITEM_SUBPROJECTS = 'project.subprojects';
public static function initializeNewProject(PhabricatorUser $actor) {
public static function initializeNewProject(
PhabricatorUser $actor,
PhabricatorProject $parent = null) {
$app = id(new PhabricatorApplicationQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withClasses(array('PhabricatorProjectApplication'))
@ -72,6 +77,14 @@ final class PhabricatorProject extends PhabricatorProjectDAO
$join_policy = $app->getPolicy(
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_color = PhabricatorProjectIconSet::getDefaultColorKey();
@ -82,6 +95,7 @@ final class PhabricatorProject extends PhabricatorProjectDAO
->setViewPolicy($view_policy)
->setEditPolicy($edit_policy)
->setJoinPolicy($join_policy)
->setSpacePHID($space_phid)
->setIsMembershipLocked(0)
->attachMemberPHIDs(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 )----------------------------------- */

View file

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

View file

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

View file

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

View file

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

View file

@ -79,6 +79,14 @@ final class TransactionSearchConduitAPIMethod
));
$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) {
$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
and failover behavior. See below for details.
- `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:

View file

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

View file

@ -294,7 +294,8 @@ body .phui-header-shell.phui-bleed-header
}
.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};
}