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

(stable) Promote 2015 Week 35

This commit is contained in:
epriestley 2015-08-29 05:28:10 -07:00
commit 5125045738
55 changed files with 1115 additions and 206 deletions

View file

@ -11,7 +11,7 @@ return array(
'core.pkg.js' => 'a590b451',
'darkconsole.pkg.js' => 'e7393ebb',
'differential.pkg.css' => '2de124c9',
'differential.pkg.js' => 'ebef29b1',
'differential.pkg.js' => '813c1633',
'diffusion.pkg.css' => '385e85b3',
'diffusion.pkg.js' => '0115b37c',
'maniphest.pkg.css' => '4845691a',
@ -407,7 +407,7 @@ return array(
'rsrc/js/application/releeph/releeph-preview-branch.js' => 'b2b4fbaf',
'rsrc/js/application/releeph/releeph-request-state-change.js' => 'a0b57eb8',
'rsrc/js/application/releeph/releeph-request-typeahead.js' => 'de2e896f',
'rsrc/js/application/repository/repository-crossreference.js' => 'bea81850',
'rsrc/js/application/repository/repository-crossreference.js' => 'e5339c43',
'rsrc/js/application/search/behavior-reorder-queries.js' => 'e9581f08',
'rsrc/js/application/slowvote/behavior-slowvote-embed.js' => '887ad43f',
'rsrc/js/application/transactions/behavior-show-older-transactions.js' => 'dbbf48b6',
@ -643,7 +643,7 @@ return array(
'javelin-behavior-remarkup-preview' => 'f7379f45',
'javelin-behavior-reorder-applications' => '76b9fc3e',
'javelin-behavior-reorder-columns' => 'e1d25dfb',
'javelin-behavior-repository-crossreference' => 'bea81850',
'javelin-behavior-repository-crossreference' => 'e5339c43',
'javelin-behavior-scrollbar' => '834a1173',
'javelin-behavior-search-reorder-queries' => 'e9581f08',
'javelin-behavior-select-on-click' => '4e3e79a6',
@ -1745,12 +1745,6 @@ return array(
'javelin-util',
'phabricator-shaped-request',
),
'bea81850' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-uri',
),
'c1700f6f' => array(
'javelin-install',
'javelin-util',
@ -1896,6 +1890,12 @@ return array(
'javelin-behavior',
'javelin-dom',
),
'e5339c43' => array(
'javelin-behavior',
'javelin-dom',
'javelin-stratcom',
'javelin-uri',
),
'e5822781' => array(
'javelin-behavior',
'javelin-dom',

View file

@ -0,0 +1 @@
DROP TABLE {$NAMESPACE}_nuance.nuance_queueitem;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_nuance.nuance_item
ADD queuePHID VARBINARY(64) NOT NULL;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_nuance.nuance_source
ADD defaultQueuePHID VARBINARY(64) NOT NULL;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_nuance.nuance_item
DROP dateNuanced;

View file

@ -798,6 +798,7 @@ phutil_register_library_map(array(
'DrydockBlueprintCoreCustomField' => 'applications/drydock/customfield/DrydockBlueprintCoreCustomField.php',
'DrydockBlueprintCreateController' => 'applications/drydock/controller/DrydockBlueprintCreateController.php',
'DrydockBlueprintCustomField' => 'applications/drydock/customfield/DrydockBlueprintCustomField.php',
'DrydockBlueprintDatasource' => 'applications/drydock/typeahead/DrydockBlueprintDatasource.php',
'DrydockBlueprintEditController' => 'applications/drydock/controller/DrydockBlueprintEditController.php',
'DrydockBlueprintEditor' => 'applications/drydock/editor/DrydockBlueprintEditor.php',
'DrydockBlueprintImplementation' => 'applications/drydock/blueprint/DrydockBlueprintImplementation.php',
@ -822,6 +823,7 @@ phutil_register_library_map(array(
'DrydockInterface' => 'applications/drydock/interface/DrydockInterface.php',
'DrydockLease' => 'applications/drydock/storage/DrydockLease.php',
'DrydockLeaseController' => 'applications/drydock/controller/DrydockLeaseController.php',
'DrydockLeaseDatasource' => 'applications/drydock/typeahead/DrydockLeaseDatasource.php',
'DrydockLeaseListController' => 'applications/drydock/controller/DrydockLeaseListController.php',
'DrydockLeaseListView' => 'applications/drydock/view/DrydockLeaseListView.php',
'DrydockLeasePHIDType' => 'applications/drydock/phid/DrydockLeasePHIDType.php',
@ -847,6 +849,7 @@ phutil_register_library_map(array(
'DrydockResource' => 'applications/drydock/storage/DrydockResource.php',
'DrydockResourceCloseController' => 'applications/drydock/controller/DrydockResourceCloseController.php',
'DrydockResourceController' => 'applications/drydock/controller/DrydockResourceController.php',
'DrydockResourceDatasource' => 'applications/drydock/typeahead/DrydockResourceDatasource.php',
'DrydockResourceListController' => 'applications/drydock/controller/DrydockResourceListController.php',
'DrydockResourceListView' => 'applications/drydock/view/DrydockResourceListView.php',
'DrydockResourcePHIDType' => 'applications/drydock/phid/DrydockResourcePHIDType.php',
@ -1269,6 +1272,7 @@ phutil_register_library_map(array(
'MultimeterSampleController' => 'applications/multimeter/controller/MultimeterSampleController.php',
'MultimeterViewer' => 'applications/multimeter/storage/MultimeterViewer.php',
'NuanceConduitAPIMethod' => 'applications/nuance/conduit/NuanceConduitAPIMethod.php',
'NuanceConsoleController' => 'applications/nuance/controller/NuanceConsoleController.php',
'NuanceController' => 'applications/nuance/controller/NuanceController.php',
'NuanceCreateItemConduitAPIMethod' => 'applications/nuance/conduit/NuanceCreateItemConduitAPIMethod.php',
'NuanceDAO' => 'applications/nuance/storage/NuanceDAO.php',
@ -1284,9 +1288,9 @@ phutil_register_library_map(array(
'NuancePhabricatorFormSourceDefinition' => 'applications/nuance/source/NuancePhabricatorFormSourceDefinition.php',
'NuanceQuery' => 'applications/nuance/query/NuanceQuery.php',
'NuanceQueue' => 'applications/nuance/storage/NuanceQueue.php',
'NuanceQueueDatasource' => 'applications/nuance/typeahead/NuanceQueueDatasource.php',
'NuanceQueueEditController' => 'applications/nuance/controller/NuanceQueueEditController.php',
'NuanceQueueEditor' => 'applications/nuance/editor/NuanceQueueEditor.php',
'NuanceQueueItem' => 'applications/nuance/storage/NuanceQueueItem.php',
'NuanceQueueListController' => 'applications/nuance/controller/NuanceQueueListController.php',
'NuanceQueuePHIDType' => 'applications/nuance/phid/NuanceQueuePHIDType.php',
'NuanceQueueQuery' => 'applications/nuance/query/NuanceQueueQuery.php',
@ -4478,6 +4482,7 @@ phutil_register_library_map(array(
),
'DrydockBlueprintCreateController' => 'DrydockBlueprintController',
'DrydockBlueprintCustomField' => 'PhabricatorCustomField',
'DrydockBlueprintDatasource' => 'PhabricatorTypeaheadDatasource',
'DrydockBlueprintEditController' => 'DrydockBlueprintController',
'DrydockBlueprintEditor' => 'PhabricatorApplicationTransactionEditor',
'DrydockBlueprintImplementation' => 'Phobject',
@ -4505,6 +4510,7 @@ phutil_register_library_map(array(
'PhabricatorPolicyInterface',
),
'DrydockLeaseController' => 'DrydockController',
'DrydockLeaseDatasource' => 'PhabricatorTypeaheadDatasource',
'DrydockLeaseListController' => 'DrydockLeaseController',
'DrydockLeaseListView' => 'AphrontView',
'DrydockLeasePHIDType' => 'PhabricatorPHIDType',
@ -4536,6 +4542,7 @@ phutil_register_library_map(array(
),
'DrydockResourceCloseController' => 'DrydockResourceController',
'DrydockResourceController' => 'DrydockController',
'DrydockResourceDatasource' => 'PhabricatorTypeaheadDatasource',
'DrydockResourceListController' => 'DrydockResourceController',
'DrydockResourceListView' => 'AphrontView',
'DrydockResourcePHIDType' => 'PhabricatorPHIDType',
@ -5049,6 +5056,7 @@ phutil_register_library_map(array(
'MultimeterSampleController' => 'MultimeterController',
'MultimeterViewer' => 'MultimeterDimension',
'NuanceConduitAPIMethod' => 'ConduitAPIMethod',
'NuanceConsoleController' => 'NuanceController',
'NuanceController' => 'PhabricatorController',
'NuanceCreateItemConduitAPIMethod' => 'NuanceConduitAPIMethod',
'NuanceDAO' => 'PhabricatorLiskDAO',
@ -5072,9 +5080,9 @@ phutil_register_library_map(array(
'PhabricatorPolicyInterface',
'PhabricatorApplicationTransactionInterface',
),
'NuanceQueueDatasource' => 'PhabricatorTypeaheadDatasource',
'NuanceQueueEditController' => 'NuanceController',
'NuanceQueueEditor' => 'PhabricatorApplicationTransactionEditor',
'NuanceQueueItem' => 'NuanceDAO',
'NuanceQueueListController' => 'NuanceController',
'NuanceQueuePHIDType' => 'PhabricatorPHIDType',
'NuanceQueueQuery' => 'NuanceQuery',

View file

@ -949,6 +949,12 @@ final class PhabricatorAuditEditor
);
}
protected function getCustomWorkerStateEncoding() {
return array(
'rawPatch' => self::STORAGE_ENCODING_BINARY,
);
}
protected function loadCustomWorkerState(array $state) {
$this->rawPatch = idx($state, 'rawPatch');
$this->affectedFiles = idx($state, 'affectedFiles');

View file

@ -37,11 +37,25 @@ final class PhabricatorDashboardTextPanelType
PhabricatorDashboardPanelRenderingEngine $engine) {
$text = $panel->getProperty('text', '');
$oneoff = id(new PhabricatorMarkupOneOff())->setContent($text);
$field = 'default';
$text_content = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($text),
'default',
$viewer);
// NOTE: We're taking extra steps here to prevent creation of a text panel
// which embeds itself using `{Wnnn}`, recursing indefinitely.
$parent_key = PhabricatorDashboardRemarkupRule::KEY_PARENT_PANEL_PHIDS;
$parent_phids = $engine->getParentPanelPHIDs();
$parent_phids[] = $panel->getPHID();
$markup_engine = id(new PhabricatorMarkupEngine())
->setViewer($viewer)
->setContextObject($panel)
->setAuxiliaryConfig($parent_key, $parent_phids);
$text_content = $markup_engine
->addObject($oneoff, $field)
->process()
->getOutput($oneoff, $field);
return id(new PHUIPropertyListView())
->addTextContent($text_content);

View file

@ -3,6 +3,8 @@
final class PhabricatorDashboardRemarkupRule
extends PhabricatorObjectRemarkupRule {
const KEY_PARENT_PANEL_PHIDS = 'dashboard.parentPanelPHIDs';
protected function getObjectNamePrefix() {
return 'W';
}
@ -21,12 +23,16 @@ final class PhabricatorDashboardRemarkupRule
PhabricatorObjectHandle $handle,
$options) {
$viewer = $this->getEngine()->getConfig('viewer');
$engine = $this->getEngine();
$viewer = $engine->getConfig('viewer');
$parent_key = self::KEY_PARENT_PANEL_PHIDS;
$parent_phids = $engine->getConfig($parent_key, array());
return id(new PhabricatorDashboardPanelRenderingEngine())
->setViewer($viewer)
->setPanel($object)
->setParentPanelPHIDs(array())
->setParentPanelPHIDs($parent_phids)
->renderPanel();
}

View file

@ -76,6 +76,9 @@ final class DifferentialCustomFieldRevertsParserTestCase
),
),
// This tests a degenerate regex behavior, see T9268.
'Reverts aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaz' => array(),
"This doesn't revert anything" => array(),
'nonrevert of r11' => array(),
'fixed a bug' => array(),

View file

@ -29,6 +29,9 @@ final class DrydockLeasePHIDType extends PhabricatorPHIDType {
$lease = $objects[$phid];
$id = $lease->getID();
$handle->setName(pht(
'Lease %d',
$id));
$handle->setURI("/drydock/lease/{$id}/");
}
}

View file

@ -29,6 +29,7 @@ final class DrydockResourcePHIDType extends PhabricatorPHIDType {
$resource = $objects[$phid];
$id = $resource->getID();
$handle->setName($resource->getName());
$handle->setURI("/drydock/resource/{$id}/");
}
}

View file

@ -4,6 +4,7 @@ final class DrydockBlueprintQuery extends DrydockQuery {
private $ids;
private $phids;
private $datasourceQuery;
public function withIDs(array $ids) {
$this->ids = $ids;
@ -15,6 +16,11 @@ final class DrydockBlueprintQuery extends DrydockQuery {
return $this;
}
public function withDatasourceQuery($query) {
$this->datasourceQuery = $query;
return $this;
}
protected function loadPage() {
$table = new DrydockBlueprint();
$conn_r = $table->establishConnection('r');
@ -45,20 +51,27 @@ final class DrydockBlueprintQuery extends DrydockQuery {
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->ids) {
if ($this->ids !== null) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
if ($this->phids !== null) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
if ($this->datasourceQuery !== null) {
$where[] = qsprintf(
$conn_r,
'blueprintName LIKE %>',
$this->datasourceQuery);
}
return $this->formatWhereClause($where);
}

View file

@ -6,6 +6,7 @@ final class DrydockLeaseQuery extends DrydockQuery {
private $phids;
private $resourceIDs;
private $statuses;
private $datasourceQuery;
public function withIDs(array $ids) {
$this->ids = $ids;
@ -31,6 +32,11 @@ final class DrydockLeaseQuery extends DrydockQuery {
return new DrydockLease();
}
public function withDatasourceQuery($query) {
$this->datasourceQuery = $query;
return $this;
}
protected function loadPage() {
return $this->loadStandardPage($this->newResultObject());
}
@ -41,7 +47,7 @@ final class DrydockLeaseQuery extends DrydockQuery {
$resources = id(new DrydockResourceQuery())
->setParentQuery($this)
->setViewer($this->getViewer())
->withIDs($resource_ids)
->withIDs(array_unique($resource_ids))
->execute();
} else {
$resources = array();
@ -93,6 +99,13 @@ final class DrydockLeaseQuery extends DrydockQuery {
$this->statuses);
}
if ($this->datasourceQuery !== null) {
$where[] = qsprintf(
$conn,
'id = %d',
(int)$this->datasourceQuery);
}
return $where;
}

View file

@ -36,7 +36,7 @@ final class DrydockLogQuery extends DrydockQuery {
$resources = id(new DrydockResourceQuery())
->setParentQuery($this)
->setViewer($this->getViewer())
->withIDs($resource_ids)
->withIDs(array_unique($resource_ids))
->execute();
} else {
$resources = array();
@ -59,7 +59,7 @@ final class DrydockLogQuery extends DrydockQuery {
$leases = id(new DrydockLeaseQuery())
->setParentQuery($this)
->setViewer($this->getViewer())
->withIDs($lease_ids)
->withIDs(array_unique($lease_ids))
->execute();
} else {
$leases = array();
@ -91,14 +91,14 @@ final class DrydockLogQuery extends DrydockQuery {
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->resourceIDs) {
if ($this->resourceIDs !== null) {
$where[] = qsprintf(
$conn_r,
'resourceID IN (%Ld)',
$this->resourceIDs);
}
if ($this->leaseIDs) {
if ($this->leaseIDs !== null) {
$where[] = qsprintf(
$conn_r,
'leaseID IN (%Ld)',

View file

@ -11,16 +11,71 @@ final class DrydockLogSearchEngine extends PhabricatorApplicationSearchEngine {
}
public function buildSavedQueryFromRequest(AphrontRequest $request) {
return new PhabricatorSavedQuery();
$query = new PhabricatorSavedQuery();
$query->setParameter(
'resourcePHIDs',
$this->readListFromRequest($request, 'resources'));
$query->setParameter(
'leasePHIDs',
$this->readListFromRequest($request, 'leases'));
return $query;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
return new DrydockLogQuery();
$resource_phids = $saved->getParameter('resourcePHIDs', array());
$lease_phids = $saved->getParameter('leasePHIDs', array());
// TODO: Change logs to use PHIDs instead of IDs.
$resource_ids = array();
$lease_ids = array();
if ($resource_phids) {
$resource_ids = id(new DrydockResourceQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs($resource_phids)
->execute();
$resource_ids = mpull($resource_ids, 'getID');
}
if ($lease_phids) {
$lease_ids = id(new DrydockLeaseQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs($lease_phids)
->execute();
$lease_ids = mpull($lease_ids, 'getID');
}
$query = new DrydockLogQuery();
if ($resource_ids) {
$query->withResourceIDs($resource_ids);
}
if ($lease_ids) {
$query->withLeaseIDs($lease_ids);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved) {}
PhabricatorSavedQuery $saved) {
$form
->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new DrydockResourceDatasource())
->setName('resources')
->setLabel(pht('Resources'))
->setValue($saved->getParameter('resourcePHIDs', array())))
->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new DrydockLeaseDatasource())
->setName('leases')
->setLabel(pht('Leases'))
->setValue($saved->getParameter('leasePHIDs', array())));
}
protected function getURI($path) {
return '/drydock/log/'.$path;

View file

@ -7,6 +7,7 @@ final class DrydockResourceQuery extends DrydockQuery {
private $statuses;
private $types;
private $blueprintPHIDs;
private $datasourceQuery;
public function withIDs(array $ids) {
$this->ids = $ids;
@ -33,6 +34,11 @@ final class DrydockResourceQuery extends DrydockQuery {
return $this;
}
public function withDatasourceQuery($query) {
$this->datasourceQuery = $query;
return $this;
}
protected function loadPage() {
$table = new DrydockResource();
$conn_r = $table->establishConnection('r');
@ -53,41 +59,48 @@ final class DrydockResourceQuery extends DrydockQuery {
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
if ($this->ids) {
if ($this->ids !== null) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
if ($this->phids !== null) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
if ($this->types) {
if ($this->types !== null) {
$where[] = qsprintf(
$conn_r,
'type IN (%Ls)',
$this->types);
}
if ($this->statuses) {
if ($this->statuses !== null) {
$where[] = qsprintf(
$conn_r,
'status IN (%Ls)',
$this->statuses);
}
if ($this->blueprintPHIDs) {
if ($this->blueprintPHIDs !== null) {
$where[] = qsprintf(
$conn_r,
'blueprintPHID IN (%Ls)',
$this->blueprintPHIDs);
}
if ($this->datasourceQuery !== null) {
$where[] = qsprintf(
$conn_r,
'name LIKE %>',
$this->datasourceQuery);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);

View file

@ -40,7 +40,7 @@ final class DrydockBlueprint extends DrydockDAO
),
self::CONFIG_COLUMN_SCHEMA => array(
'className' => 'text255',
'blueprintName' => 'text255',
'blueprintName' => 'sort255',
),
) + parent::getConfiguration();
}

View file

@ -0,0 +1,36 @@
<?php
final class DrydockBlueprintDatasource
extends PhabricatorTypeaheadDatasource {
public function getPlaceholderText() {
return pht('Type a blueprint name...');
}
public function getDatasourceApplicationClass() {
return 'PhabricatorDrydockApplication';
}
public function loadResults() {
$viewer = $this->getViewer();
$raw_query = $this->getRawQuery();
$blueprints = id(new DrydockBlueprintQuery())
->setViewer($viewer)
->withDatasourceQuery($raw_query)
->execute();
$handles = id(new PhabricatorHandleQuery())
->setViewer($viewer)
->withPHIDs(mpull($blueprints, 'getPHID'))
->execute();
$results = array();
foreach ($handles as $handle) {
$results[] = id(new PhabricatorTypeaheadResult())
->setName($handle->getName())
->setPHID($handle->getPHID());
}
return $results;
}
}

View file

@ -0,0 +1,36 @@
<?php
final class DrydockLeaseDatasource
extends PhabricatorTypeaheadDatasource {
public function getPlaceholderText() {
return pht('Type a lease ID (exact match)...');
}
public function getDatasourceApplicationClass() {
return 'PhabricatorDrydockApplication';
}
public function loadResults() {
$viewer = $this->getViewer();
$raw_query = $this->getRawQuery();
$leases = id(new DrydockLeaseQuery())
->setViewer($viewer)
->withDatasourceQuery($raw_query)
->execute();
$handles = id(new PhabricatorHandleQuery())
->setViewer($viewer)
->withPHIDs(mpull($leases, 'getPHID'))
->execute();
$results = array();
foreach ($handles as $handle) {
$results[] = id(new PhabricatorTypeaheadResult())
->setName($handle->getName())
->setPHID($handle->getPHID());
}
return $results;
}
}

View file

@ -0,0 +1,36 @@
<?php
final class DrydockResourceDatasource
extends PhabricatorTypeaheadDatasource {
public function getPlaceholderText() {
return pht('Type a resource name...');
}
public function getDatasourceApplicationClass() {
return 'PhabricatorDrydockApplication';
}
public function loadResults() {
$viewer = $this->getViewer();
$raw_query = $this->getRawQuery();
$resources = id(new DrydockResourceQuery())
->setViewer($viewer)
->withDatasourceQuery($raw_query)
->execute();
$handles = id(new PhabricatorHandleQuery())
->setViewer($viewer)
->withPHIDs(mpull($resources, 'getPHID'))
->execute();
$results = array();
foreach ($handles as $handle) {
$results[] = id(new PhabricatorTypeaheadResult())
->setName($handle->getName())
->setPHID($handle->getPHID());
}
return $results;
}
}

View file

@ -21,13 +21,18 @@ final class DrydockLogListView extends AphrontView {
$resource_uri = '/drydock/resource/'.$log->getResourceID().'/';
$lease_uri = '/drydock/lease/'.$log->getLeaseID().'/';
$resource_name = $log->getResourceID();
if ($log->getResourceID() !== null) {
$resource_name = $log->getResource()->getName();
}
$rows[] = array(
phutil_tag(
'a',
array(
'href' => $resource_uri,
),
$log->getResourceID()),
$resource_name),
phutil_tag(
'a',
array(
@ -35,7 +40,7 @@ final class DrydockLogListView extends AphrontView {
),
$log->getLeaseID()),
$log->getMessage(),
phabricator_date($log->getEpoch(), $viewer),
phabricator_datetime($log->getEpoch(), $viewer),
);
}

View file

@ -24,6 +24,7 @@ final class HarbormasterLeaseHostBuildStepImplementation
// Create the lease.
$lease = id(new DrydockLease())
->setResourceType('host')
->setOwnerPHID($build_target->getPHID())
->setAttributes(
array(
'platform' => $settings['platform'],

View file

@ -234,11 +234,19 @@ final class LegalpadDocumentSignController extends LegalpadController {
$document,
PhabricatorPolicyCapability::CAN_EDIT);
// Use the last content update as the modified date. We don't want to
// show that a document like a TOS was "updated" by an incidental change
// to a field like the preamble or privacy settings which does not acutally
// affect the content of the agreement.
$content_updated = $document_body->getDateCreated();
// NOTE: We're avoiding `setPolicyObject()` here so we don't pick up
// extra UI elements that are unnecessary and clutter the signature page.
// These details are available on the "Manage" page.
$header = id(new PHUIHeaderView())
->setHeader($title)
->setUser($viewer)
->setPolicyObject($document)
->setEpoch($document->getDateModified())
->setEpoch($content_updated)
->addActionLink(
id(new PHUIButtonView())
->setTag('a')
@ -258,15 +266,16 @@ final class LegalpadDocumentSignController extends LegalpadController {
'default',
$viewer);
// NOTE: We're avoiding `setObject()` here so we don't pick up extra UI
// elements like "Subscribers". This information is available on the
// "Manage" page, but just clutters up the "Signature" page.
$preamble = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($document)
->addSectionHeader(pht('Preamble'))
->addTextContent($preamble_text);
$preamble_box = new PHUIPropertyGroupView();
$preamble_box->addPropertyList($preamble);
}
$content = id(new PHUIDocumentView())

View file

@ -7,11 +7,18 @@ final class PhabricatorMailManagementVolumeWorkflow
$this
->setName('volume')
->setSynopsis(
pht('Show how much mail users have received in the last 30 days.'))
pht('Show how much mail users have received recently.'))
->setExamples(
'**volume**')
->setArguments(
array(
array(
'name' => 'days',
'param' => 'days',
'default' => 30,
'help' => pht(
'Number of days back (default 30).'),
),
));
}
@ -19,7 +26,16 @@ final class PhabricatorMailManagementVolumeWorkflow
$console = PhutilConsole::getConsole();
$viewer = $this->getViewer();
$since = (PhabricatorTime::getNow() - phutil_units('30 days in seconds'));
$days = (int)$args->getArg('days');
if ($days < 1) {
throw new PhutilArgumentUsageException(
pht(
'Period specified with --days must be at least 1.'));
}
$duration = phutil_units("{$days} days in seconds");
$since = (PhabricatorTime::getNow() - $duration);
$until = PhabricatorTime::getNow();
$mails = id(new PhabricatorMetaMTAMailQuery())
@ -95,7 +111,9 @@ final class PhabricatorMailManagementVolumeWorkflow
$table->draw();
echo "\n";
echo pht('Mail sent in the last 30 days.')."\n";
echo pht(
'Mail sent in the last %s day(s).',
new PhutilNumber($days))."\n";
echo pht(
'"Unfiltered" is raw volume before rules applied.')."\n";
echo pht(

View file

@ -10,7 +10,7 @@ final class PhabricatorMetaMTAMail
const RETRY_DELAY = 5;
protected $actorPHID;
protected $parameters;
protected $parameters = array();
protected $status;
protected $message;
protected $relatedPHID;
@ -69,6 +69,13 @@ final class PhabricatorMetaMTAMail
}
protected function getParam($param, $default = null) {
// Some old mail was saved without parameters because no parameters were
// set or encoding failed. Recover in these cases so we can perform
// mail migrations, see T9251.
if (!is_array($this->parameters)) {
$this->parameters = array();
}
return idx($this->parameters, $param, $default);
}

View file

@ -38,6 +38,7 @@ final class PhabricatorNuanceApplication extends PhabricatorApplication {
public function getRoutes() {
return array(
'/nuance/' => array(
'' => 'NuanceConsoleController',
'item/' => array(
'view/(?P<id>[1-9]\d*)/' => 'NuanceItemViewController',
'edit/(?P<id>[1-9]\d*)/' => 'NuanceItemEditController',

View file

@ -0,0 +1,46 @@
<?php
final class NuanceConsoleController extends NuanceController {
public function shouldAllowPublic() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$menu = id(new PHUIObjectItemListView())
->setUser($viewer);
$menu->addItem(
id(new PHUIObjectItemView())
->setHeader(pht('Queues'))
->setHref($this->getApplicationURI('queue/'))
->setFontIcon('fa-align-left')
->addAttribute(pht('Manage Nuance queues.')));
$menu->addItem(
id(new PHUIObjectItemView())
->setHeader(pht('Sources'))
->setHref($this->getApplicationURI('source/'))
->setFontIcon('fa-filter')
->addAttribute(pht('Manage Nuance sources.')));
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Console'));
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Console'))
->setObjectList($menu);
return $this->buildApplicationPage(
array(
$crumbs,
$box,
),
array(
'title' => pht('Nuance Console'),
));
}
}

View file

@ -3,30 +3,102 @@
final class NuanceItemEditController extends NuanceController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$viewer = $this->getViewer();
$id = $request->getURIData('id');
if (!$id) {
$item = new NuanceItem();
} else {
$item = id(new NuanceItemQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
}
$item = id(new NuanceItemQuery())
->setViewer($viewer)
->withIDs(array($id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$item) {
return new Aphront404Response();
}
$title = pht('Item %d', $item->getID());
$crumbs = $this->buildApplicationCrumbs();
$title = 'TODO';
$crumbs->addTextCrumb($title);
$crumbs->addTextCrumb(pht('Edit'));
$properties = $this->buildPropertyView($item);
$actions = $this->buildActionView($item);
$properties->setActionList($actions);
$box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->addPropertyList($properties);
$timeline = $this->buildTransactionTimeline(
$item,
new NuanceItemTransactionQuery());
$timeline->setShouldTerminate(true);
return $this->buildApplicationPage(
$crumbs,
array(
$crumbs,
$box,
$timeline,
),
array(
'title' => $title,
));
}
private function buildPropertyView(NuanceItem $item) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($item);
$properties->addProperty(
pht('Date Created'),
phabricator_datetime($item->getDateCreated(), $viewer));
$properties->addProperty(
pht('Requestor'),
$viewer->renderHandle($item->getRequestorPHID()));
$properties->addProperty(
pht('Source'),
$viewer->renderHandle($item->getSourcePHID()));
$properties->addProperty(
pht('Queue'),
$viewer->renderHandle($item->getQueuePHID()));
$source = $item->getSource();
$definition = $source->requireDefinition();
$definition->renderItemEditProperties(
$viewer,
$item,
$properties);
return $properties;
}
private function buildActionView(NuanceItem $item) {
$viewer = $this->getViewer();
$id = $item->getID();
$actions = id(new PhabricatorActionListView())
->setUser($viewer);
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('View Item'))
->setIcon('fa-eye')
->setHref($this->getApplicationURI("item/view/{$id}/")));
return $actions;
}
}

View file

@ -3,25 +3,84 @@
final class NuanceItemViewController extends NuanceController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$viewer = $this->getViewer();
$id = $request->getURIData('id');
$item = id(new NuanceItemQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$item) {
return new Aphront404Response();
}
$title = pht('Item %d', $item->getID());
$crumbs = $this->buildApplicationCrumbs();
$title = 'TODO';
$crumbs->addTextCrumb($title);
$properties = $this->buildPropertyView($item);
$actions = $this->buildActionView($item);
$properties->setActionList($actions);
$box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->addPropertyList($properties);
return $this->buildApplicationPage(
$crumbs,
array(
$crumbs,
$box,
),
array(
'title' => $title,
));
}
private function buildPropertyView(NuanceItem $item) {
$viewer = $this->getViewer();
$properties = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($item);
$properties->addProperty(
pht('Date Created'),
phabricator_datetime($item->getDateCreated(), $viewer));
$source = $item->getSource();
$definition = $source->requireDefinition();
$definition->renderItemViewProperties(
$viewer,
$item,
$properties);
return $properties;
}
private function buildActionView(NuanceItem $item) {
$viewer = $this->getViewer();
$id = $item->getID();
$actions = id(new PhabricatorActionListView())
->setUser($viewer);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$item,
PhabricatorPolicyCapability::CAN_EDIT);
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Item'))
->setIcon('fa-pencil')
->setHref($this->getApplicationURI("item/edit/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
return $actions;
}
}

View file

@ -13,7 +13,7 @@ final class NuanceSourceActionController extends NuanceController {
return new Aphront404Response();
}
$def = NuanceSourceDefinition::getDefinitionForSource($source);
$def = $source->requireDefinition();
$def->setActor($viewer);
$response = $def->handleActionRequest($request);

View file

@ -41,7 +41,7 @@ final class NuanceSourceEditController extends NuanceController {
$cancel_uri = $source->getURI();
}
$definition = NuanceSourceDefinition::getDefinitionForSource($source);
$definition = $source->requireDefinition();
$definition->setActor($viewer);
$response = $definition->buildEditLayout($request);

View file

@ -13,7 +13,7 @@ final class NuanceSourceViewController extends NuanceController {
return new Aphront404Response();
}
$source_phid = $source->getPHID();
$source_id = $source->getID();
$timeline = $this->buildTransactionTimeline(
$source,
@ -34,10 +34,29 @@ final class NuanceSourceViewController extends NuanceController {
$crumbs->addTextCrumb($title);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$source,
PhabricatorPolicyCapability::CAN_EDIT);
$routing_list = id(new PHUIPropertyListView())
->addProperty(
pht('Default Queue'),
$viewer->renderHandle($source->getDefaultQueuePHID()));
$routing_header = id(new PHUIHeaderView())
->setHeader(pht('Routing Rules'));
$routing = id(new PHUIObjectBoxView())
->setHeader($routing_header)
->addPropertyList($routing_list);
return $this->buildApplicationPage(
array(
$crumbs,
$box,
$routing,
$timeline,
),
array(
@ -77,6 +96,13 @@ final class NuanceSourceViewController extends NuanceController {
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$request = $this->getRequest();
$definition = $source->requireDefinition();
$source_actions = $definition->getSourceViewActions($request);
foreach ($source_actions as $source_action) {
$actions->addAction($source_action);
}
return $actions;
}
@ -90,7 +116,7 @@ final class NuanceSourceViewController extends NuanceController {
->setObject($source)
->setActionList($actions);
$definition = NuanceSourceDefinition::getDefinitionForSource($source);
$definition = $source->requireDefinition();
$properties->addProperty(
pht('Source Type'),
$definition->getName());

View file

@ -18,6 +18,7 @@ final class NuanceItemEditor
$types[] = NuanceItemTransaction::TYPE_SOURCE;
$types[] = NuanceItemTransaction::TYPE_REQUESTOR;
$types[] = NuanceItemTransaction::TYPE_PROPERTY;
$types[] = NuanceItemTransaction::TYPE_QUEUE;
$types[] = PhabricatorTransactions::TYPE_EDGE;
$types[] = PhabricatorTransactions::TYPE_COMMENT;
@ -38,6 +39,8 @@ final class NuanceItemEditor
return $object->getSourcePHID();
case NuanceItemTransaction::TYPE_OWNER:
return $object->getOwnerPHID();
case NuanceItemTransaction::TYPE_QUEUE:
return $object->getQueuePHID();
case NuanceItemTransaction::TYPE_PROPERTY:
$key = $xaction->getMetadataValue(
NuanceItemTransaction::PROPERTY_KEY);
@ -56,6 +59,7 @@ final class NuanceItemEditor
case NuanceItemTransaction::TYPE_SOURCE:
case NuanceItemTransaction::TYPE_OWNER:
case NuanceItemTransaction::TYPE_PROPERTY:
case NuanceItemTransaction::TYPE_QUEUE:
return $xaction->getNewValue();
}
@ -76,6 +80,9 @@ final class NuanceItemEditor
case NuanceItemTransaction::TYPE_OWNER:
$object->setOwnerPHID($xaction->getNewValue());
break;
case NuanceItemTransaction::TYPE_QUEUE:
$object->setQueuePHID($xaction->getNewValue());
break;
case NuanceItemTransaction::TYPE_PROPERTY:
$key = $xaction->getMetadataValue(
NuanceItemTransaction::PROPERTY_KEY);
@ -93,6 +100,7 @@ final class NuanceItemEditor
case NuanceItemTransaction::TYPE_SOURCE:
case NuanceItemTransaction::TYPE_OWNER:
case NuanceItemTransaction::TYPE_PROPERTY:
case NuanceItemTransaction::TYPE_QUEUE:
return;
}

View file

@ -15,6 +15,7 @@ final class NuanceSourceEditor
$types = parent::getTransactionTypes();
$types[] = NuanceSourceTransaction::TYPE_NAME;
$types[] = NuanceSourceTransaction::TYPE_DEFAULT_QUEUE;
$types[] = PhabricatorTransactions::TYPE_EDGE;
$types[] = PhabricatorTransactions::TYPE_COMMENT;
@ -31,6 +32,8 @@ final class NuanceSourceEditor
switch ($xaction->getTransactionType()) {
case NuanceSourceTransaction::TYPE_NAME:
return $object->getName();
case NuanceSourceTransaction::TYPE_DEFAULT_QUEUE:
return $object->getDefaultQueuePHID();
}
return parent::getCustomTransactionOldValue($object, $xaction);
@ -42,6 +45,7 @@ final class NuanceSourceEditor
switch ($xaction->getTransactionType()) {
case NuanceSourceTransaction::TYPE_NAME:
case NuanceSourceTransaction::TYPE_DEFAULT_QUEUE:
return $xaction->getNewValue();
}
@ -56,6 +60,9 @@ final class NuanceSourceEditor
case NuanceSourceTransaction::TYPE_NAME:
$object->setName($xaction->getNewValue());
break;
case NuanceSourceTransaction::TYPE_DEFAULT_QUEUE:
$object->setDefaultQueuePHID($xaction->getNewValue());
break;
}
}
@ -65,6 +72,7 @@ final class NuanceSourceEditor
switch ($xaction->getTransactionType()) {
case NuanceSourceTransaction::TYPE_NAME:
case NuanceSourceTransaction::TYPE_DEFAULT_QUEUE:
return;
}
@ -95,6 +103,19 @@ final class NuanceSourceEditor
$errors[] = $error;
}
break;
case NuanceSourceTransaction::TYPE_DEFAULT_QUEUE:
foreach ($xactions as $xaction) {
if (!$xaction->getNewValue()) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Required'),
pht('Sources must have a default queue.'),
$xaction);
$error->setIsMissingFieldError(true);
$errors[] = $error;
}
}
break;
}
return $errors;

View file

@ -29,7 +29,9 @@ final class NuanceRequestorPHIDType extends PhabricatorPHIDType {
foreach ($handles as $phid => $handle) {
$requestor = $objects[$phid];
$handle->setName($requestor->getBestName());
// TODO: This is currently useless and should be far more informative.
$handle->setName(pht('Requestor %d', $requestor->getID()));
$handle->setURI($requestor->getURI());
}
}

View file

@ -17,25 +17,42 @@ final class NuanceItemQuery
return $this;
}
public function withSourcePHIDs($source_phids) {
public function withSourcePHIDs(array $source_phids) {
$this->sourcePHIDs = $source_phids;
return $this;
}
public function newResultObject() {
return new NuanceItem();
}
protected function loadPage() {
$table = new NuanceItem();
$conn = $table->establishConnection('r');
return $this->loadStandardPage($this->newResultObject());
}
$data = queryfx_all(
$conn,
'%Q FROM %T %Q %Q %Q',
$this->buildSelectClause($conn),
$table->getTableName(),
$this->buildWhereClause($conn),
$this->buildOrderClause($conn),
$this->buildLimitClause($conn));
protected function willFilterPage(array $items) {
$source_phids = mpull($items, 'getSourcePHID');
return $table->loadAllFromArray($data);
// NOTE: We always load sources, even if the viewer can't formally see
// them. If they can see the item, they're allowed to be aware of the
// source in some sense.
$sources = id(new NuanceSourceQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPHIDs($source_phids)
->execute();
$sources = mpull($sources, null, 'getPHID');
foreach ($items as $key => $item) {
$source = idx($sources, $item->getSourcePHID());
if (!$source) {
$this->didRejectResult($items[$key]);
unset($items[$key]);
continue;
}
$item->attachSource($source);
}
return $items;
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {

View file

@ -15,6 +15,17 @@ final class NuancePhabricatorFormSourceDefinition
return 'phabricator-form';
}
public function getSourceViewActions(AphrontRequest $request) {
$actions = array();
$actions[] = id(new PhabricatorActionView())
->setName(pht('View Form'))
->setIcon('fa-align-justify')
->setHref($this->getActionURI());
return $actions;
}
public function updateItems() {
return null;
}
@ -50,7 +61,7 @@ final class NuancePhabricatorFormSourceDefinition
if ($request->isFormPost()) {
$properties = array(
'complaint' => (string)$request->getStr('text'),
'complaint' => (string)$request->getStr('complaint'),
);
$content_source = PhabricatorContentSource::newFromRequest($request);
@ -89,4 +100,33 @@ final class NuancePhabricatorFormSourceDefinition
return $box;
}
public function renderItemViewProperties(
PhabricatorUser $viewer,
NuanceItem $item,
PHUIPropertyListView $view) {
$this->renderItemCommonProperties($viewer, $item, $view);
}
public function renderItemEditProperties(
PhabricatorUser $viewer,
NuanceItem $item,
PHUIPropertyListView $view) {
$this->renderItemCommonProperties($viewer, $item, $view);
}
private function renderItemCommonProperties(
PhabricatorUser $viewer,
NuanceItem $item,
PHUIPropertyListView $view) {
$complaint = $item->getNuanceProperty('complaint');
$complaint = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($complaint),
'default',
$viewer);
$view->addSectionHeader(pht('Complaint'));
$view->addTextContent($complaint);
}
}

View file

@ -43,18 +43,8 @@ abstract class NuanceSourceDefinition extends Phobject {
return $source;
}
/**
* Gives a @{class:NuanceSourceDefinition} object for a given
* @{class:NuanceSource}. Note you still need to @{method:setActor}
* before the @{class:NuanceSourceDefinition} object will be useful.
*/
public static function getDefinitionForSource(NuanceSource $source) {
$definitions = self::getAllDefinitions();
$map = mpull($definitions, null, 'getSourceTypeConstant');
$definition = $map[$source->getType()];
$definition->setSourceObject($source);
return $definition;
public function getSourceViewActions(AphrontRequest $request) {
return array();
}
public static function getAllDefinitions() {
@ -179,25 +169,39 @@ abstract class NuanceSourceDefinition extends Phobject {
$form = $this->augmentEditForm($form, $ex);
$default_phid = $source->getDefaultQueuePHID();
if ($default_phid) {
$default_queues = array($default_phid);
} else {
$default_queues = array();
}
$form
->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Default Queue'))
->setName('defaultQueuePHIDs')
->setLimit(1)
->setDatasource(new NuanceQueueDatasource())
->setValue($default_queues))
->appendChild(
id(new AphrontFormPolicyControl())
->setUser($user)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
->setPolicyObject($source)
->setPolicies($policies)
->setName('viewPolicy'))
->setUser($user)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
->setPolicyObject($source)
->setPolicies($policies)
->setName('viewPolicy'))
->appendChild(
id(new AphrontFormPolicyControl())
->setUser($user)
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
->setPolicyObject($source)
->setPolicies($policies)
->setName('editPolicy'))
->setUser($user)
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
->setPolicyObject($source)
->setPolicies($policies)
->setName('editPolicy'))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($source->getURI())
->setValue(pht('Save')));
->addCancelButton($source->getURI())
->setValue(pht('Save')));
return $form;
}
@ -223,13 +227,19 @@ abstract class NuanceSourceDefinition extends Phobject {
$transactions[] = id(new NuanceSourceTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)
->setNewValue($request->getStr('editPolicy'));
$transactions[] = id(new NuanceSourceTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)
->setNewValue($request->getStr('viewPolicy'));
$transactions[] = id(new NuanceSourceTransaction())
$transactions[] = id(new NuanceSourceTransaction())
->setTransactionType(NuanceSourceTransaction::TYPE_NAME)
->setNewvalue($request->getStr('name'));
$transactions[] = id(new NuanceSourceTransaction())
->setTransactionType(NuanceSourceTransaction::TYPE_DEFAULT_QUEUE)
->setNewvalue(head($request->getArr('defaultQueuePHIDs')));
return $transactions;
}
@ -261,6 +271,12 @@ abstract class NuanceSourceDefinition extends Phobject {
->setTransactionType(NuanceItemTransaction::TYPE_REQUESTOR)
->setNewValue($requestor->getPHID());
// TODO: Eventually, apply real routing rules. For now, just put everything
// in the default queue for the source.
$xactions[] = id(new NuanceItemTransaction())
->setTransactionType(NuanceItemTransaction::TYPE_QUEUE)
->setNewValue($source->getDefaultQueuePHID());
foreach ($properties as $key => $property) {
$xactions[] = id(new NuanceItemTransaction())
->setTransactionType(NuanceItemTransaction::TYPE_PROPERTY)
@ -278,6 +294,20 @@ abstract class NuanceSourceDefinition extends Phobject {
return $item;
}
public function renderItemViewProperties(
PhabricatorUser $viewer,
NuanceItem $item,
PHUIPropertyListView $view) {
return;
}
public function renderItemEditProperties(
PhabricatorUser $viewer,
NuanceItem $item,
PHUIPropertyListView $view) {
return;
}
/* -( Handling Action Requests )------------------------------------------- */
@ -286,4 +316,9 @@ abstract class NuanceSourceDefinition extends Phobject {
return new Aphront404Response();
}
public function getActionURI($path = null) {
$source_id = $this->getSourceObject()->getID();
return '/action/'.$source_id.'/'.ltrim($path, '/');
}
}

View file

@ -17,11 +17,12 @@ final class NuanceItem
protected $sourceLabel;
protected $data = array();
protected $mailKey;
protected $dateNuanced;
protected $queuePHID;
private $source = self::ATTACHABLE;
public static function initializeNewItem() {
return id(new NuanceItem())
->setDateNuanced(time())
->setStatus(self::STATUS_OPEN);
}
@ -36,17 +37,19 @@ final class NuanceItem
'sourceLabel' => 'text255?',
'status' => 'uint32',
'mailKey' => 'bytes20',
'dateNuanced' => 'epoch',
),
self::CONFIG_KEY_SCHEMA => array(
'key_source' => array(
'columns' => array('sourcePHID', 'status', 'dateNuanced', 'id'),
'columns' => array('sourcePHID', 'status'),
),
'key_owner' => array(
'columns' => array('ownerPHID', 'status', 'dateNuanced', 'id'),
'columns' => array('ownerPHID', 'status'),
),
'key_contacter' => array(
'columns' => array('requestorPHID', 'status', 'dateNuanced', 'id'),
'key_requestor' => array(
'columns' => array('requestorPHID', 'status'),
),
'key_queue' => array(
'columns' => array('queuePHID', 'status'),
),
),
) + parent::getConfiguration();
@ -142,7 +145,6 @@ final class NuanceItem
'sourceLabel' => $this->getSourceLabel(),
'dateCreated' => $this->getDateCreated(),
'dateModified' => $this->getDateModified(),
'dateNuanced' => $this->getDateNuanced(),
);
}

View file

@ -9,6 +9,7 @@ final class NuanceItemTransaction
const TYPE_REQUESTOR = 'nuance.item.requestor';
const TYPE_SOURCE = 'nuance.item.source';
const TYPE_PROPERTY = 'nuance.item.property';
const TYPE_QUEUE = 'nuance.item.queue';
public function getApplicationTransactionType() {
return NuanceItemPHIDType::TYPECONST;
@ -18,4 +19,55 @@ final class NuanceItemTransaction
return new NuanceItemTransactionComment();
}
public function shouldHide() {
$old = $this->getOldValue();
$type = $this->getTransactionType();
switch ($type) {
case self::TYPE_REQUESTOR:
case self::TYPE_SOURCE:
return ($old === null);
}
return parent::shouldHide();
}
public function getRequiredHandlePHIDs() {
$old = $this->getOldValue();
$new = $this->getNewValue();
$type = $this->getTransactionType();
$phids = parent::getRequiredHandlePHIDs();
switch ($type) {
case self::TYPE_QUEUE:
if ($old) {
$phids[] = $old;
}
if ($new) {
$phids[] = $new;
}
break;
}
return $phids;
}
public function getTitle() {
$old = $this->getOldValue();
$new = $this->getNewValue();
$type = $this->getTransactionType();
$author_phid = $this->getAuthorPHID();
switch ($type) {
case self::TYPE_QUEUE:
return pht(
'%s routed this item to the %s queue.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($new));
}
return parent::getTitle();
}
}

View file

@ -1,34 +0,0 @@
<?php
final class NuanceQueueItem
extends NuanceDAO {
protected $queuePHID;
protected $itemPHID;
protected $itemStatus;
protected $itemDateNuanced;
protected function getConfiguration() {
return array(
self::CONFIG_COLUMN_SCHEMA => array(
'itemStatus' => 'uint32',
'itemDateNuanced' => 'epoch',
),
self::CONFIG_KEY_SCHEMA => array(
'key_one_per_queue' => array(
'columns' => array('itemPHID', 'queuePHID'),
'unique' => true,
),
'key_queue' => array(
'columns' => array(
'queuePHID',
'itemStatus',
'itemDateNuanced',
'id',
),
),
),
) + parent::getConfiguration();
}
}

View file

@ -12,4 +12,34 @@ final class NuanceQueueTransaction extends NuanceTransaction {
return new NuanceQueueTransactionComment();
}
public function shouldHide() {
$old = $this->getOldValue();
$type = $this->getTransactionType();
switch ($type) {
case self::TYPE_NAME:
return ($old === null);
}
return parent::shouldHide();
}
public function getTitle() {
$old = $this->getOldValue();
$new = $this->getNewValue();
$type = $this->getTransactionType();
$author_phid = $this->getAuthorPHID();
switch ($type) {
case self::TYPE_NAME:
return pht(
'%s renamed this queue from "%s" to "%s".',
$this->renderHandleLink($author_phid),
$old,
$new);
}
return parent::getTitle();
}
}

View file

@ -11,6 +11,9 @@ final class NuanceSource extends NuanceDAO
protected $mailKey;
protected $viewPolicy;
protected $editPolicy;
protected $defaultQueuePHID;
private $definition;
protected function getConfiguration() {
return array(
@ -62,6 +65,31 @@ final class NuanceSource extends NuanceDAO
->setEditPolicy($edit_policy);
}
public function getDefinition() {
if ($this->definition === null) {
$definitions = NuanceSourceDefinition::getAllDefinitions();
if (isset($definitions[$this->getType()])) {
$definition = clone $definitions[$this->getType()];
$definition->setSourceObject($this);
$this->definition = $definition;
}
}
return $this->definition;
}
public function requireDefinition() {
$definition = $this->getDefinition();
if (!$definition) {
throw new Exception(
pht(
'Unable to load source definition implementation for source '.
'type "%s".',
$this->getType()));
}
return $definition;
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */

View file

@ -3,7 +3,8 @@
final class NuanceSourceTransaction
extends NuanceTransaction {
const TYPE_NAME = 'name-source';
const TYPE_NAME = 'source.name';
const TYPE_DEFAULT_QUEUE = 'source.queue.default';
public function getApplicationTransactionType() {
return NuanceSourcePHIDType::TYPECONST;
@ -13,27 +14,63 @@ final class NuanceSourceTransaction
return new NuanceSourceTransactionComment();
}
public function getTitle() {
public function shouldHide() {
$old = $this->getOldValue();
$new = $this->getNewValue();
$author_phid = $this->getAuthorPHID();
$type = $this->getTransactionType();
switch ($this->getTransactionType()) {
switch ($type) {
case self::TYPE_DEFAULT_QUEUE:
return !$old;
case self::TYPE_NAME:
if ($old === null) {
return pht(
'%s created this source.',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s renamed this source from "%s" to "%s".',
$this->renderHandleLink($author_phid),
$old,
$new);
return ($old === null);
}
return parent::shouldHide();
}
public function getRequiredHandlePHIDs() {
$old = $this->getOldValue();
$new = $this->getNewValue();
$type = $this->getTransactionType();
$phids = parent::getRequiredHandlePHIDs();
switch ($type) {
case self::TYPE_DEFAULT_QUEUE:
if ($old) {
$phids[] = $old;
}
if ($new) {
$phids[] = $new;
}
break;
}
return $phids;
}
public function getTitle() {
$old = $this->getOldValue();
$new = $this->getNewValue();
$type = $this->getTransactionType();
$author_phid = $this->getAuthorPHID();
switch ($type) {
case self::TYPE_DEFAULT_QUEUE:
return pht(
'%s changed the default queue from %s to %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($old),
$this->renderHandleLink($new));
case self::TYPE_NAME:
return pht(
'%s renamed this source from "%s" to "%s".',
$this->renderHandleLink($author_phid),
$old,
$new);
}
return parent::getTitle();
}
}

View file

@ -0,0 +1,38 @@
<?php
final class NuanceQueueDatasource
extends PhabricatorTypeaheadDatasource {
public function getBrowseTitle() {
return pht('Browse Queues');
}
public function getPlaceholderText() {
return pht('Type a queue name...');
}
public function getDatasourceApplicationClass() {
return 'PhabricatorNuanceApplication';
}
public function loadResults() {
$viewer = $this->getViewer();
$raw_query = $this->getRawQuery();
$results = array();
// TODO: Make this use real typeahead logic.
$query = new NuanceQueueQuery();
$queues = $this->executeQuery($query);
foreach ($queues as $queue) {
$results[] = id(new PhabricatorTypeaheadResult())
->setName($queue->getName())
->setURI('/nuance/queue/'.$queue->getID().'/')
->setPHID($queue->getPHID());
}
return $this->filterResultsAgainstTokens($results);
}
}

View file

@ -28,19 +28,12 @@ final class PhabricatorProjectColumnQuery
return $this;
}
public function newResultObject() {
return new PhabricatorProjectColumn();
}
protected function loadPage() {
$table = new PhabricatorProjectColumn();
$conn_r = $table->establishConnection('r');
$data = queryfx_all(
$conn_r,
'SELECT * FROM %T %Q %Q %Q',
$table->getTableName(),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
return $table->loadAllFromArray($data);
return $this->loadStandardPage($this->newResultObject());
}
protected function willFilterPage(array $page) {
@ -60,6 +53,7 @@ final class PhabricatorProjectColumnQuery
$phid = $column->getProjectPHID();
$project = idx($projects, $phid);
if (!$project) {
$this->didRejectResult($page[$key]);
unset($page[$key]);
continue;
}
@ -69,40 +63,38 @@ final class PhabricatorProjectColumnQuery
return $page;
}
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->ids) {
if ($this->ids !== null) {
$where[] = qsprintf(
$conn_r,
$conn,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
if ($this->phids !== null) {
$where[] = qsprintf(
$conn_r,
$conn,
'phid IN (%Ls)',
$this->phids);
}
if ($this->projectPHIDs) {
if ($this->projectPHIDs !== null) {
$where[] = qsprintf(
$conn_r,
$conn,
'projectPHID IN (%Ls)',
$this->projectPHIDs);
}
if ($this->statuses !== null) {
$where[] = qsprintf(
$conn_r,
$conn,
'status IN (%Ld)',
$this->statuses);
}
$where[] = $this->buildPagingClause($conn_r);
return $this->formatWhereClause($where);
return $where;
}
public function getQueryApplicationClass() {

View file

@ -69,6 +69,8 @@ abstract class PhabricatorApplicationTransactionEditor
private $feedNotifyPHIDs = array();
private $feedRelatedPHIDs = array();
const STORAGE_ENCODING_BINARY = 'binary';
/**
* Get the class name for the application this editor is a part of.
*
@ -2637,6 +2639,21 @@ abstract class PhabricatorApplicationTransactionEditor
}
/**
* @task mail
*/
private function runHeraldMailRules(array $messages) {
foreach ($messages as $message) {
$engine = new HeraldEngine();
$adapter = id(new PhabricatorMailOutboundMailHeraldAdapter())
->setObject($message);
$rules = $engine->loadRulesForAdapter($adapter);
$effects = $engine->applyRules($rules, $adapter);
$engine->applyEffects($effects, $adapter, $rules);
}
}
/* -( Publishing Feed Stories )-------------------------------------------- */
@ -3060,9 +3077,13 @@ abstract class PhabricatorApplicationTransactionEditor
$state[$property] = $this->$property;
}
$custom_state = $this->getCustomWorkerState();
$custom_encoding = $this->getCustomWorkerStateEncoding();
$state += array(
'excludeMailRecipientPHIDs' => $this->getExcludeMailRecipientPHIDs(),
'custom' => $this->getCustomWorkerState(),
'custom' => $this->encodeStateForStorage($custom_state, $custom_encoding),
'custom.encoding' => $custom_encoding,
);
return $state;
@ -3080,6 +3101,21 @@ abstract class PhabricatorApplicationTransactionEditor
}
/**
* Hook; return storage encoding for custom properties which need to be
* passed to workers.
*
* This primarily allows binary data to be passed to workers and survive
* JSON encoding.
*
* @return dict<string, string> Property encodings.
* @task workers
*/
protected function getCustomWorkerStateEncoding() {
return array();
}
/**
* Load editor state using a dictionary emitted by @{method:getWorkerState}.
*
@ -3097,7 +3133,10 @@ abstract class PhabricatorApplicationTransactionEditor
$exclude = idx($state, 'excludeMailRecipientPHIDs', array());
$this->setExcludeMailRecipientPHIDs($exclude);
$custom = idx($state, 'custom', array());
$custom_state = idx($state, 'custom', array());
$custom_encodings = idx($state, 'custom.encoding', array());
$custom = $this->decodeStateFromStorage($custom_state, $custom_encodings);
$this->loadCustomWorkerState($custom);
return $this;
@ -3143,16 +3182,85 @@ abstract class PhabricatorApplicationTransactionEditor
);
}
private function runHeraldMailRules(array $messages) {
foreach ($messages as $message) {
$engine = new HeraldEngine();
$adapter = id(new PhabricatorMailOutboundMailHeraldAdapter())
->setObject($message);
/**
* Apply encodings prior to storage.
*
* See @{method:getCustomWorkerStateEncoding}.
*
* @param map<string, wild> Map of values to encode.
* @param map<string, string> Map of encodings to apply.
* @return map<string, wild> Map of encoded values.
* @task workers
*/
final private function encodeStateForStorage(
array $state,
array $encodings) {
$rules = $engine->loadRulesForAdapter($adapter);
$effects = $engine->applyRules($rules, $adapter);
$engine->applyEffects($effects, $adapter, $rules);
foreach ($state as $key => $value) {
$encoding = idx($encodings, $key);
switch ($encoding) {
case self::STORAGE_ENCODING_BINARY:
// The mechanics of this encoding (serialize + base64) are a little
// awkward, but it allows us encode arrays and still be JSON-safe
// with binary data.
$value = @serialize($value);
if ($value === false) {
throw new Exception(
pht(
'Failed to serialize() value for key "%s".',
$key));
}
$value = base64_encode($value);
if ($value === false) {
throw new Exception(
pht(
'Failed to base64 encode value for key "%s".',
$key));
}
break;
}
$state[$key] = $value;
}
return $state;
}
/**
* Undo storage encoding applied when storing state.
*
* See @{method:getCustomWorkerStateEncoding}.
*
* @param map<string, wild> Map of encoded values.
* @param map<string, string> Map of encodings.
* @return map<string, wild> Map of decoded values.
* @task workers
*/
final private function decodeStateFromStorage(
array $state,
array $encodings) {
foreach ($state as $key => $value) {
$encoding = idx($encodings, $key);
switch ($encoding) {
case self::STORAGE_ENCODING_BINARY:
$value = base64_decode($value);
if ($value === false) {
throw new Exception(
pht(
'Failed to base64_decode() value for key "%s".',
$key));
}
$value = unserialize($value);
break;
}
$state[$key] = $value;
}
return $state;
}
}

View file

@ -26,9 +26,14 @@ final class PhabricatorApplicationTransactionPublishWorker
* Load the object the transactions affect.
*/
private function loadObject() {
$data = $this->getTaskData();
$viewer = PhabricatorUser::getOmnipotentUser();
$data = $this->getTaskData();
if (!is_array($data)) {
throw new PhabricatorWorkerPermanentFailureException(
pht('Task has invalid task data.'));
}
$phid = idx($data, 'objectPHID');
if (!$phid) {
throw new PhabricatorWorkerPermanentFailureException(

View file

@ -65,7 +65,7 @@ write such a script, and run this command to see its output:
To actually build the symbol index, pipe this data to the
`import_repository_symbols.php` script, providing the repository callsign:
$ ./scripts/symbols/import_repository_symbols.php rREPO < symbols_data
$ ./scripts/symbols/import_repository_symbols.php REPO < symbols_data
Then just set up a cronjob to run that however often you like.

View file

@ -24,8 +24,8 @@ abstract class PhabricatorCustomFieldMonogramParser
'(?:^|\b)'.
$prefix_regex.
$infix_regex.
'((?:'.$monogram_pattern.'[,\s]*)+)'.
'(?:\band\s+('.$monogram_pattern.'))?'.
'((?:'.$monogram_pattern.'(?:\b|$)[,\s]*)+)'.
'(?:\band\s+('.$monogram_pattern.'(?:\b|$)))?'.
$suffix_regex.
'(?:$|\b)'.
'/';

View file

@ -1369,6 +1369,11 @@ final class PhabricatorUSEnglishTranslation
'This action has no effect on targets: %2$s.',
),
'Mail sent in the last %s day(s).' => array(
'Mail sent in the last day.',
'Mail sent in the last %s days.',
),
);
}

View file

@ -44,6 +44,7 @@ final class PhabricatorMarkupEngine extends Phobject {
private $contextObject;
private $version = 15;
private $engineCaches = array();
private $auxiliaryConfig = array();
/* -( Markup Pipeline )---------------------------------------------------- */
@ -122,6 +123,10 @@ final class PhabricatorMarkupEngine extends Phobject {
$engines[$key] = $info['object']->newMarkupEngine($info['field']);
$engines[$key]->setConfig('viewer', $this->viewer);
$engines[$key]->setConfig('contextObject', $this->contextObject);
foreach ($this->auxiliaryConfig as $aux_key => $aux_value) {
$engines[$key]->setConfig($aux_key, $aux_value);
}
}
// Load or build the preprocessor caches.
@ -310,6 +315,12 @@ final class PhabricatorMarkupEngine extends Phobject {
return $this;
}
public function setAuxiliaryConfig($key, $value) {
// TODO: This is gross and should be removed. Avoid use.
$this->auxiliaryConfig[$key] = $value;
return $this;
}
/* -( Engine Construction )------------------------------------------------ */

View file

@ -1651,7 +1651,7 @@ abstract class LiskDAO extends Phobject {
if ($deserialize) {
$data[$col] = json_decode($data[$col], true);
} else {
$data[$col] = json_encode($data[$col]);
$data[$col] = phutil_json_encode($data[$col]);
}
break;
default:

View file

@ -40,8 +40,7 @@ JX.behavior('repository-crossreference', function(config, statics) {
'tag:span',
function(e) {
if (e.getType() === 'mouseout') {
highlighted && JX.DOM.alterClass(highlighted, classHighlight, false);
highlighted = null;
unhighlight();
return;
}
if (!isSignalkey(e)) {
@ -59,10 +58,14 @@ JX.behavior('repository-crossreference', function(config, statics) {
target = target.parentNode;
}
} else if (e.getType() === 'click') {
openSearch(highlighted, lang);
openSearch(e.getTarget(), lang);
}
});
}
function unhighlight() {
highlighted && JX.DOM.alterClass(highlighted, classHighlight, false);
highlighted = null;
}
function openSearch(target, lang) {
var symbol = target.textContent || target.innerText;
@ -110,6 +113,7 @@ JX.behavior('repository-crossreference', function(config, statics) {
linkAll(e.getData().container);
});
JX.Stratcom.listen(
['keydown', 'keyup'],
null,
@ -117,14 +121,28 @@ JX.behavior('repository-crossreference', function(config, statics) {
if (e.getRawEvent().keyCode !== signalKey) {
return;
}
statics.active = (e.getType() === 'keydown');
linked.forEach(function(element) {
JX.DOM.alterClass(element, classMouseCursor, statics.active);
});
setCursorMode(e.getType() === 'keydown');
if (!statics.active) {
highlighted && JX.DOM.alterClass(highlighted, classHighlight, false);
highlighted = null;
unhighlight();
}
});
JX.Stratcom.listen(
'blur',
null,
function(e) {
if (e.getTarget()) {
return;
}
unhighlight();
setCursorMode(false);
});
function setCursorMode(active) {
statics.active = active;
linked.forEach(function(element) {
JX.DOM.alterClass(element, classMouseCursor, statics.active);
});
}
});