1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-15 10:00:55 +01:00

(stable) Promote 2017 Week 21

This commit is contained in:
epriestley 2017-05-26 16:03:32 -07:00
commit c7eae8f031
175 changed files with 3504 additions and 1757 deletions

View file

@ -1,30 +1,3 @@
<?php
// Destroy duplicate drafts before storage adjustment adds a unique key to this
// table. See T1191. We retain the newest draft.
// (We can't easily do this in a single SQL statement because MySQL won't let us
// modify a table that's joined in a subquery.)
$table = new DifferentialDraft();
$conn_w = $table->establishConnection('w');
$duplicates = queryfx_all(
$conn_w,
'SELECT DISTINCT u.id id FROM %T u
JOIN %T v
ON u.objectPHID = v.objectPHID
AND u.authorPHID = v.authorPHID
AND u.draftKey = v.draftKey
AND u.id < v.id',
$table->getTableName(),
$table->getTableName());
$duplicates = ipull($duplicates, 'id');
foreach (PhabricatorLiskDAO::chunkSQL($duplicates) as $chunk) {
queryfx(
$conn_w,
'DELETE FROM %T WHERE id IN (%Q)',
$table->getTableName(),
$chunk);
}
// This table has been removed; see T12104 for details.

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_nuance.nuance_item
MODIFY itemKey VARCHAR(64) COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,8 @@
ALTER TABLE {$NAMESPACE}_nuance.nuance_itemcommand
ADD dateCreated INT UNSIGNED NOT NULL;
ALTER TABLE {$NAMESPACE}_nuance.nuance_itemcommand
ADD dateModified INT UNSIGNED NOT NULL;
ALTER TABLE {$NAMESPACE}_nuance.nuance_itemcommand
ADD queuePHID VARBINARY(64);

View file

@ -0,0 +1,5 @@
ALTER TABLE {$NAMESPACE}_nuance.nuance_itemcommand
ADD status VARCHAR(64) NOT NULL;
UPDATE {$NAMESPACE}_nuance.nuance_itemcommand
SET status = 'done' WHERE status = '';

View file

@ -0,0 +1 @@
DROP TABLE {$NAMESPACE}_differential.differential_draft;

View file

@ -0,0 +1,11 @@
<?php
$table = new PhabricatorProject();
foreach (new LiskMigrationIterator($table) as $project) {
PhabricatorSearchWorker::queueDocumentForIndexing(
$project->getPHID(),
array(
'force' => true,
));
}

View file

@ -441,7 +441,6 @@ phutil_register_library_map(array(
'DifferentialDiffTransactionQuery' => 'applications/differential/query/DifferentialDiffTransactionQuery.php',
'DifferentialDiffViewController' => 'applications/differential/controller/DifferentialDiffViewController.php',
'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'applications/differential/doorkeeper/DifferentialDoorkeeperRevisionFeedStoryPublisher.php',
'DifferentialDraft' => 'applications/differential/storage/DifferentialDraft.php',
'DifferentialExactUserFunctionDatasource' => 'applications/differential/typeahead/DifferentialExactUserFunctionDatasource.php',
'DifferentialFieldParseException' => 'applications/differential/exception/DifferentialFieldParseException.php',
'DifferentialFieldValidationException' => 'applications/differential/exception/DifferentialFieldValidationException.php',
@ -661,6 +660,7 @@ phutil_register_library_map(array(
'DiffusionCommitHookEngine' => 'applications/diffusion/engine/DiffusionCommitHookEngine.php',
'DiffusionCommitHookRejectException' => 'applications/diffusion/exception/DiffusionCommitHookRejectException.php',
'DiffusionCommitListController' => 'applications/diffusion/controller/DiffusionCommitListController.php',
'DiffusionCommitListView' => 'applications/diffusion/view/DiffusionCommitListView.php',
'DiffusionCommitMergeHeraldField' => 'applications/diffusion/herald/DiffusionCommitMergeHeraldField.php',
'DiffusionCommitMessageHeraldField' => 'applications/diffusion/herald/DiffusionCommitMessageHeraldField.php',
'DiffusionCommitPackageAuditHeraldField' => 'applications/diffusion/herald/DiffusionCommitPackageAuditHeraldField.php',
@ -1494,6 +1494,7 @@ phutil_register_library_map(array(
'ManiphestPointsConfigOptionType' => 'applications/maniphest/config/ManiphestPointsConfigOptionType.php',
'ManiphestPriorityConfigOptionType' => 'applications/maniphest/config/ManiphestPriorityConfigOptionType.php',
'ManiphestPriorityEmailCommand' => 'applications/maniphest/command/ManiphestPriorityEmailCommand.php',
'ManiphestPrioritySearchConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestPrioritySearchConduitAPIMethod.php',
'ManiphestProjectNameFulltextEngineExtension' => 'applications/maniphest/engineextension/ManiphestProjectNameFulltextEngineExtension.php',
'ManiphestQueryConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestQueryConduitAPIMethod.php',
'ManiphestQueryStatusesConduitAPIMethod' => 'applications/maniphest/conduit/ManiphestQueryStatusesConduitAPIMethod.php',
@ -1600,11 +1601,13 @@ phutil_register_library_map(array(
'MultimeterLabel' => 'applications/multimeter/storage/MultimeterLabel.php',
'MultimeterSampleController' => 'applications/multimeter/controller/MultimeterSampleController.php',
'MultimeterViewer' => 'applications/multimeter/storage/MultimeterViewer.php',
'NuanceCommandImplementation' => 'applications/nuance/command/NuanceCommandImplementation.php',
'NuanceConduitAPIMethod' => 'applications/nuance/conduit/NuanceConduitAPIMethod.php',
'NuanceConsoleController' => 'applications/nuance/controller/NuanceConsoleController.php',
'NuanceContentSource' => 'applications/nuance/contentsource/NuanceContentSource.php',
'NuanceController' => 'applications/nuance/controller/NuanceController.php',
'NuanceDAO' => 'applications/nuance/storage/NuanceDAO.php',
'NuanceFormItemType' => 'applications/nuance/item/NuanceFormItemType.php',
'NuanceGitHubEventItemType' => 'applications/nuance/item/NuanceGitHubEventItemType.php',
'NuanceGitHubImportCursor' => 'applications/nuance/cursor/NuanceGitHubImportCursor.php',
'NuanceGitHubIssuesImportCursor' => 'applications/nuance/cursor/NuanceGitHubIssuesImportCursor.php',
@ -1620,16 +1623,25 @@ phutil_register_library_map(array(
'NuanceItemActionController' => 'applications/nuance/controller/NuanceItemActionController.php',
'NuanceItemCommand' => 'applications/nuance/storage/NuanceItemCommand.php',
'NuanceItemCommandQuery' => 'applications/nuance/query/NuanceItemCommandQuery.php',
'NuanceItemCommandSpec' => 'applications/nuance/command/NuanceItemCommandSpec.php',
'NuanceItemCommandTransaction' => 'applications/nuance/xaction/NuanceItemCommandTransaction.php',
'NuanceItemController' => 'applications/nuance/controller/NuanceItemController.php',
'NuanceItemEditor' => 'applications/nuance/editor/NuanceItemEditor.php',
'NuanceItemListController' => 'applications/nuance/controller/NuanceItemListController.php',
'NuanceItemManageController' => 'applications/nuance/controller/NuanceItemManageController.php',
'NuanceItemOwnerTransaction' => 'applications/nuance/xaction/NuanceItemOwnerTransaction.php',
'NuanceItemPHIDType' => 'applications/nuance/phid/NuanceItemPHIDType.php',
'NuanceItemPropertyTransaction' => 'applications/nuance/xaction/NuanceItemPropertyTransaction.php',
'NuanceItemQuery' => 'applications/nuance/query/NuanceItemQuery.php',
'NuanceItemQueueTransaction' => 'applications/nuance/xaction/NuanceItemQueueTransaction.php',
'NuanceItemRequestorTransaction' => 'applications/nuance/xaction/NuanceItemRequestorTransaction.php',
'NuanceItemSearchEngine' => 'applications/nuance/query/NuanceItemSearchEngine.php',
'NuanceItemSourceTransaction' => 'applications/nuance/xaction/NuanceItemSourceTransaction.php',
'NuanceItemStatusTransaction' => 'applications/nuance/xaction/NuanceItemStatusTransaction.php',
'NuanceItemTransaction' => 'applications/nuance/storage/NuanceItemTransaction.php',
'NuanceItemTransactionComment' => 'applications/nuance/storage/NuanceItemTransactionComment.php',
'NuanceItemTransactionQuery' => 'applications/nuance/query/NuanceItemTransactionQuery.php',
'NuanceItemTransactionType' => 'applications/nuance/xaction/NuanceItemTransactionType.php',
'NuanceItemType' => 'applications/nuance/item/NuanceItemType.php',
'NuanceItemUpdateWorker' => 'applications/nuance/worker/NuanceItemUpdateWorker.php',
'NuanceItemViewController' => 'applications/nuance/controller/NuanceItemViewController.php',
@ -1645,18 +1657,22 @@ phutil_register_library_map(array(
'NuanceQueueEditEngine' => 'applications/nuance/editor/NuanceQueueEditEngine.php',
'NuanceQueueEditor' => 'applications/nuance/editor/NuanceQueueEditor.php',
'NuanceQueueListController' => 'applications/nuance/controller/NuanceQueueListController.php',
'NuanceQueueNameTransaction' => 'applications/nuance/xaction/NuanceQueueNameTransaction.php',
'NuanceQueuePHIDType' => 'applications/nuance/phid/NuanceQueuePHIDType.php',
'NuanceQueueQuery' => 'applications/nuance/query/NuanceQueueQuery.php',
'NuanceQueueSearchEngine' => 'applications/nuance/query/NuanceQueueSearchEngine.php',
'NuanceQueueTransaction' => 'applications/nuance/storage/NuanceQueueTransaction.php',
'NuanceQueueTransactionComment' => 'applications/nuance/storage/NuanceQueueTransactionComment.php',
'NuanceQueueTransactionQuery' => 'applications/nuance/query/NuanceQueueTransactionQuery.php',
'NuanceQueueTransactionType' => 'applications/nuance/xaction/NuanceQueueTransactionType.php',
'NuanceQueueViewController' => 'applications/nuance/controller/NuanceQueueViewController.php',
'NuanceQueueWorkController' => 'applications/nuance/controller/NuanceQueueWorkController.php',
'NuanceSchemaSpec' => 'applications/nuance/storage/NuanceSchemaSpec.php',
'NuanceSource' => 'applications/nuance/storage/NuanceSource.php',
'NuanceSourceActionController' => 'applications/nuance/controller/NuanceSourceActionController.php',
'NuanceSourceController' => 'applications/nuance/controller/NuanceSourceController.php',
'NuanceSourceDefaultEditCapability' => 'applications/nuance/capability/NuanceSourceDefaultEditCapability.php',
'NuanceSourceDefaultQueueTransaction' => 'applications/nuance/xaction/NuanceSourceDefaultQueueTransaction.php',
'NuanceSourceDefaultViewCapability' => 'applications/nuance/capability/NuanceSourceDefaultViewCapability.php',
'NuanceSourceDefinition' => 'applications/nuance/source/NuanceSourceDefinition.php',
'NuanceSourceDefinitionTestCase' => 'applications/nuance/source/__tests__/NuanceSourceDefinitionTestCase.php',
@ -1666,14 +1682,17 @@ phutil_register_library_map(array(
'NuanceSourceListController' => 'applications/nuance/controller/NuanceSourceListController.php',
'NuanceSourceManageCapability' => 'applications/nuance/capability/NuanceSourceManageCapability.php',
'NuanceSourceNameNgrams' => 'applications/nuance/storage/NuanceSourceNameNgrams.php',
'NuanceSourceNameTransaction' => 'applications/nuance/xaction/NuanceSourceNameTransaction.php',
'NuanceSourcePHIDType' => 'applications/nuance/phid/NuanceSourcePHIDType.php',
'NuanceSourceQuery' => 'applications/nuance/query/NuanceSourceQuery.php',
'NuanceSourceSearchEngine' => 'applications/nuance/query/NuanceSourceSearchEngine.php',
'NuanceSourceTransaction' => 'applications/nuance/storage/NuanceSourceTransaction.php',
'NuanceSourceTransactionComment' => 'applications/nuance/storage/NuanceSourceTransactionComment.php',
'NuanceSourceTransactionQuery' => 'applications/nuance/query/NuanceSourceTransactionQuery.php',
'NuanceSourceTransactionType' => 'applications/nuance/xaction/NuanceSourceTransactionType.php',
'NuanceSourceViewController' => 'applications/nuance/controller/NuanceSourceViewController.php',
'NuanceTransaction' => 'applications/nuance/storage/NuanceTransaction.php',
'NuanceTrashCommand' => 'applications/nuance/command/NuanceTrashCommand.php',
'NuanceWorker' => 'applications/nuance/worker/NuanceWorker.php',
'OwnersConduitAPIMethod' => 'applications/owners/conduit/OwnersConduitAPIMethod.php',
'OwnersEditConduitAPIMethod' => 'applications/owners/conduit/OwnersEditConduitAPIMethod.php',
@ -3457,10 +3476,12 @@ phutil_register_library_map(array(
'PhabricatorPeopleProfileManageController' => 'applications/people/controller/PhabricatorPeopleProfileManageController.php',
'PhabricatorPeopleProfileMenuEngine' => 'applications/people/engine/PhabricatorPeopleProfileMenuEngine.php',
'PhabricatorPeopleProfilePictureController' => 'applications/people/controller/PhabricatorPeopleProfilePictureController.php',
'PhabricatorPeopleProfileRevisionsController' => 'applications/people/controller/PhabricatorPeopleProfileRevisionsController.php',
'PhabricatorPeopleProfileTasksController' => 'applications/people/controller/PhabricatorPeopleProfileTasksController.php',
'PhabricatorPeopleProfileViewController' => 'applications/people/controller/PhabricatorPeopleProfileViewController.php',
'PhabricatorPeopleQuery' => 'applications/people/query/PhabricatorPeopleQuery.php',
'PhabricatorPeopleRenameController' => 'applications/people/controller/PhabricatorPeopleRenameController.php',
'PhabricatorPeopleRevisionsProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeopleRevisionsProfileMenuItem.php',
'PhabricatorPeopleSearchEngine' => 'applications/people/query/PhabricatorPeopleSearchEngine.php',
'PhabricatorPeopleTasksProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeopleTasksProfileMenuItem.php',
'PhabricatorPeopleTestDataGenerator' => 'applications/people/lipsum/PhabricatorPeopleTestDataGenerator.php',
@ -3615,6 +3636,7 @@ phutil_register_library_map(array(
'PhabricatorProjectEditController' => 'applications/project/controller/PhabricatorProjectEditController.php',
'PhabricatorProjectEditEngine' => 'applications/project/engine/PhabricatorProjectEditEngine.php',
'PhabricatorProjectEditPictureController' => 'applications/project/controller/PhabricatorProjectEditPictureController.php',
'PhabricatorProjectFilterTransaction' => 'applications/project/xaction/PhabricatorProjectFilterTransaction.php',
'PhabricatorProjectFulltextEngine' => 'applications/project/search/PhabricatorProjectFulltextEngine.php',
'PhabricatorProjectHeraldAction' => 'applications/project/herald/PhabricatorProjectHeraldAction.php',
'PhabricatorProjectHeraldAdapter' => 'applications/project/herald/PhabricatorProjectHeraldAdapter.php',
@ -3628,6 +3650,7 @@ phutil_register_library_map(array(
'PhabricatorProjectListController' => 'applications/project/controller/PhabricatorProjectListController.php',
'PhabricatorProjectListView' => 'applications/project/view/PhabricatorProjectListView.php',
'PhabricatorProjectLockController' => 'applications/project/controller/PhabricatorProjectLockController.php',
'PhabricatorProjectLockTransaction' => 'applications/project/xaction/PhabricatorProjectLockTransaction.php',
'PhabricatorProjectLogicalAncestorDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalAncestorDatasource.php',
'PhabricatorProjectLogicalDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalDatasource.php',
'PhabricatorProjectLogicalOrNotDatasource' => 'applications/project/typeahead/PhabricatorProjectLogicalOrNotDatasource.php',
@ -3645,6 +3668,7 @@ phutil_register_library_map(array(
'PhabricatorProjectMembersRemoveController' => 'applications/project/controller/PhabricatorProjectMembersRemoveController.php',
'PhabricatorProjectMembersViewController' => 'applications/project/controller/PhabricatorProjectMembersViewController.php',
'PhabricatorProjectMenuItemController' => 'applications/project/controller/PhabricatorProjectMenuItemController.php',
'PhabricatorProjectMilestoneTransaction' => 'applications/project/xaction/PhabricatorProjectMilestoneTransaction.php',
'PhabricatorProjectMoveController' => 'applications/project/controller/PhabricatorProjectMoveController.php',
'PhabricatorProjectNameContextFreeGrammar' => 'applications/project/lipsum/PhabricatorProjectNameContextFreeGrammar.php',
'PhabricatorProjectNameTransaction' => 'applications/project/xaction/PhabricatorProjectNameTransaction.php',
@ -3653,6 +3677,7 @@ phutil_register_library_map(array(
'PhabricatorProjectOrUserDatasource' => 'applications/project/typeahead/PhabricatorProjectOrUserDatasource.php',
'PhabricatorProjectOrUserFunctionDatasource' => 'applications/project/typeahead/PhabricatorProjectOrUserFunctionDatasource.php',
'PhabricatorProjectPHIDResolver' => 'applications/phid/resolver/PhabricatorProjectPHIDResolver.php',
'PhabricatorProjectParentTransaction' => 'applications/project/xaction/PhabricatorProjectParentTransaction.php',
'PhabricatorProjectPictureProfileMenuItem' => 'applications/project/menuitem/PhabricatorProjectPictureProfileMenuItem.php',
'PhabricatorProjectPointsProfileMenuItem' => 'applications/project/menuitem/PhabricatorProjectPointsProfileMenuItem.php',
'PhabricatorProjectProfileController' => 'applications/project/controller/PhabricatorProjectProfileController.php',
@ -3670,6 +3695,7 @@ phutil_register_library_map(array(
'PhabricatorProjectSilencedEdgeType' => 'applications/project/edge/PhabricatorProjectSilencedEdgeType.php',
'PhabricatorProjectSlug' => 'applications/project/storage/PhabricatorProjectSlug.php',
'PhabricatorProjectSlugsTransaction' => 'applications/project/xaction/PhabricatorProjectSlugsTransaction.php',
'PhabricatorProjectSortTransaction' => 'applications/project/xaction/PhabricatorProjectSortTransaction.php',
'PhabricatorProjectStandardCustomField' => 'applications/project/customfield/PhabricatorProjectStandardCustomField.php',
'PhabricatorProjectStatus' => 'applications/project/constants/PhabricatorProjectStatus.php',
'PhabricatorProjectStatusTransaction' => 'applications/project/xaction/PhabricatorProjectStatusTransaction.php',
@ -3681,6 +3707,7 @@ phutil_register_library_map(array(
'PhabricatorProjectTransactionEditor' => 'applications/project/editor/PhabricatorProjectTransactionEditor.php',
'PhabricatorProjectTransactionQuery' => 'applications/project/query/PhabricatorProjectTransactionQuery.php',
'PhabricatorProjectTransactionType' => 'applications/project/xaction/PhabricatorProjectTransactionType.php',
'PhabricatorProjectTypeTransaction' => 'applications/project/xaction/PhabricatorProjectTypeTransaction.php',
'PhabricatorProjectUIEventListener' => 'applications/project/events/PhabricatorProjectUIEventListener.php',
'PhabricatorProjectUpdateController' => 'applications/project/controller/PhabricatorProjectUpdateController.php',
'PhabricatorProjectUserFunctionDatasource' => 'applications/project/typeahead/PhabricatorProjectUserFunctionDatasource.php',
@ -3689,7 +3716,9 @@ phutil_register_library_map(array(
'PhabricatorProjectWatchController' => 'applications/project/controller/PhabricatorProjectWatchController.php',
'PhabricatorProjectWatcherListView' => 'applications/project/view/PhabricatorProjectWatcherListView.php',
'PhabricatorProjectWorkboardBackgroundColor' => 'applications/project/constants/PhabricatorProjectWorkboardBackgroundColor.php',
'PhabricatorProjectWorkboardBackgroundTransaction' => 'applications/project/xaction/PhabricatorProjectWorkboardBackgroundTransaction.php',
'PhabricatorProjectWorkboardProfileMenuItem' => 'applications/project/menuitem/PhabricatorProjectWorkboardProfileMenuItem.php',
'PhabricatorProjectWorkboardTransaction' => 'applications/project/xaction/PhabricatorProjectWorkboardTransaction.php',
'PhabricatorProjectsAncestorsSearchEngineAttachment' => 'applications/project/engineextension/PhabricatorProjectsAncestorsSearchEngineAttachment.php',
'PhabricatorProjectsCurtainExtension' => 'applications/project/engineextension/PhabricatorProjectsCurtainExtension.php',
'PhabricatorProjectsEditEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php',
@ -4619,11 +4648,14 @@ phutil_register_library_map(array(
'PhrictionDocument' => 'applications/phriction/storage/PhrictionDocument.php',
'PhrictionDocumentAuthorHeraldField' => 'applications/phriction/herald/PhrictionDocumentAuthorHeraldField.php',
'PhrictionDocumentContentHeraldField' => 'applications/phriction/herald/PhrictionDocumentContentHeraldField.php',
'PhrictionDocumentContentTransaction' => 'applications/phriction/xaction/PhrictionDocumentContentTransaction.php',
'PhrictionDocumentController' => 'applications/phriction/controller/PhrictionDocumentController.php',
'PhrictionDocumentDeleteTransaction' => 'applications/phriction/xaction/PhrictionDocumentDeleteTransaction.php',
'PhrictionDocumentFulltextEngine' => 'applications/phriction/search/PhrictionDocumentFulltextEngine.php',
'PhrictionDocumentHeraldAdapter' => 'applications/phriction/herald/PhrictionDocumentHeraldAdapter.php',
'PhrictionDocumentHeraldField' => 'applications/phriction/herald/PhrictionDocumentHeraldField.php',
'PhrictionDocumentHeraldFieldGroup' => 'applications/phriction/herald/PhrictionDocumentHeraldFieldGroup.php',
'PhrictionDocumentMoveAwayTransaction' => 'applications/phriction/xaction/PhrictionDocumentMoveAwayTransaction.php',
'PhrictionDocumentMoveToTransaction' => 'applications/phriction/xaction/PhrictionDocumentMoveToTransaction.php',
'PhrictionDocumentPHIDType' => 'applications/phriction/phid/PhrictionDocumentPHIDType.php',
'PhrictionDocumentPathHeraldField' => 'applications/phriction/herald/PhrictionDocumentPathHeraldField.php',
@ -5359,7 +5391,6 @@ phutil_register_library_map(array(
'DifferentialDiffTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'DifferentialDiffViewController' => 'DifferentialController',
'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'DoorkeeperFeedStoryPublisher',
'DifferentialDraft' => 'DifferentialDAO',
'DifferentialExactUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
'DifferentialFieldParseException' => 'Exception',
'DifferentialFieldValidationException' => 'Exception',
@ -5602,6 +5633,7 @@ phutil_register_library_map(array(
'DiffusionCommitHookEngine' => 'Phobject',
'DiffusionCommitHookRejectException' => 'Exception',
'DiffusionCommitListController' => 'DiffusionController',
'DiffusionCommitListView' => 'AphrontView',
'DiffusionCommitMergeHeraldField' => 'DiffusionCommitHeraldField',
'DiffusionCommitMessageHeraldField' => 'DiffusionCommitHeraldField',
'DiffusionCommitPackageAuditHeraldField' => 'DiffusionCommitHeraldField',
@ -6562,6 +6594,7 @@ phutil_register_library_map(array(
'ManiphestPointsConfigOptionType' => 'PhabricatorConfigJSONOptionType',
'ManiphestPriorityConfigOptionType' => 'PhabricatorConfigJSONOptionType',
'ManiphestPriorityEmailCommand' => 'ManiphestEmailCommand',
'ManiphestPrioritySearchConduitAPIMethod' => 'ManiphestConduitAPIMethod',
'ManiphestProjectNameFulltextEngineExtension' => 'PhabricatorFulltextEngineExtension',
'ManiphestQueryConduitAPIMethod' => 'ManiphestConduitAPIMethod',
'ManiphestQueryStatusesConduitAPIMethod' => 'ManiphestConduitAPIMethod',
@ -6687,11 +6720,13 @@ phutil_register_library_map(array(
'MultimeterLabel' => 'MultimeterDimension',
'MultimeterSampleController' => 'MultimeterController',
'MultimeterViewer' => 'MultimeterDimension',
'NuanceCommandImplementation' => 'Phobject',
'NuanceConduitAPIMethod' => 'ConduitAPIMethod',
'NuanceConsoleController' => 'NuanceController',
'NuanceContentSource' => 'PhabricatorContentSource',
'NuanceController' => 'PhabricatorController',
'NuanceDAO' => 'PhabricatorLiskDAO',
'NuanceFormItemType' => 'NuanceItemType',
'NuanceGitHubEventItemType' => 'NuanceItemType',
'NuanceGitHubImportCursor' => 'NuanceImportCursor',
'NuanceGitHubIssuesImportCursor' => 'NuanceGitHubImportCursor',
@ -6717,16 +6752,25 @@ phutil_register_library_map(array(
'PhabricatorPolicyInterface',
),
'NuanceItemCommandQuery' => 'NuanceQuery',
'NuanceItemCommandSpec' => 'Phobject',
'NuanceItemCommandTransaction' => 'NuanceItemTransactionType',
'NuanceItemController' => 'NuanceController',
'NuanceItemEditor' => 'PhabricatorApplicationTransactionEditor',
'NuanceItemListController' => 'NuanceItemController',
'NuanceItemManageController' => 'NuanceController',
'NuanceItemOwnerTransaction' => 'NuanceItemTransactionType',
'NuanceItemPHIDType' => 'PhabricatorPHIDType',
'NuanceItemPropertyTransaction' => 'NuanceItemTransactionType',
'NuanceItemQuery' => 'NuanceQuery',
'NuanceItemQueueTransaction' => 'NuanceItemTransactionType',
'NuanceItemRequestorTransaction' => 'NuanceItemTransactionType',
'NuanceItemSearchEngine' => 'PhabricatorApplicationSearchEngine',
'NuanceItemSourceTransaction' => 'NuanceItemTransactionType',
'NuanceItemStatusTransaction' => 'NuanceItemTransactionType',
'NuanceItemTransaction' => 'NuanceTransaction',
'NuanceItemTransactionComment' => 'PhabricatorApplicationTransactionComment',
'NuanceItemTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'NuanceItemTransactionType' => 'PhabricatorModularTransactionType',
'NuanceItemType' => 'Phobject',
'NuanceItemUpdateWorker' => 'NuanceWorker',
'NuanceItemViewController' => 'NuanceController',
@ -6746,13 +6790,16 @@ phutil_register_library_map(array(
'NuanceQueueEditEngine' => 'PhabricatorEditEngine',
'NuanceQueueEditor' => 'PhabricatorApplicationTransactionEditor',
'NuanceQueueListController' => 'NuanceQueueController',
'NuanceQueueNameTransaction' => 'NuanceQueueTransactionType',
'NuanceQueuePHIDType' => 'PhabricatorPHIDType',
'NuanceQueueQuery' => 'NuanceQuery',
'NuanceQueueSearchEngine' => 'PhabricatorApplicationSearchEngine',
'NuanceQueueTransaction' => 'NuanceTransaction',
'NuanceQueueTransactionComment' => 'PhabricatorApplicationTransactionComment',
'NuanceQueueTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'NuanceQueueTransactionType' => 'PhabricatorModularTransactionType',
'NuanceQueueViewController' => 'NuanceQueueController',
'NuanceQueueWorkController' => 'NuanceQueueController',
'NuanceSchemaSpec' => 'PhabricatorConfigSchemaSpec',
'NuanceSource' => array(
'NuanceDAO',
@ -6763,6 +6810,7 @@ phutil_register_library_map(array(
'NuanceSourceActionController' => 'NuanceController',
'NuanceSourceController' => 'NuanceController',
'NuanceSourceDefaultEditCapability' => 'PhabricatorPolicyCapability',
'NuanceSourceDefaultQueueTransaction' => 'NuanceSourceTransactionType',
'NuanceSourceDefaultViewCapability' => 'PhabricatorPolicyCapability',
'NuanceSourceDefinition' => 'Phobject',
'NuanceSourceDefinitionTestCase' => 'PhabricatorTestCase',
@ -6772,14 +6820,17 @@ phutil_register_library_map(array(
'NuanceSourceListController' => 'NuanceSourceController',
'NuanceSourceManageCapability' => 'PhabricatorPolicyCapability',
'NuanceSourceNameNgrams' => 'PhabricatorSearchNgrams',
'NuanceSourceNameTransaction' => 'NuanceSourceTransactionType',
'NuanceSourcePHIDType' => 'PhabricatorPHIDType',
'NuanceSourceQuery' => 'NuanceQuery',
'NuanceSourceSearchEngine' => 'PhabricatorApplicationSearchEngine',
'NuanceSourceTransaction' => 'NuanceTransaction',
'NuanceSourceTransactionComment' => 'PhabricatorApplicationTransactionComment',
'NuanceSourceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'NuanceSourceTransactionType' => 'PhabricatorModularTransactionType',
'NuanceSourceViewController' => 'NuanceSourceController',
'NuanceTransaction' => 'PhabricatorApplicationTransaction',
'NuanceTransaction' => 'PhabricatorModularTransaction',
'NuanceTrashCommand' => 'NuanceCommandImplementation',
'NuanceWorker' => 'PhabricatorWorker',
'OwnersConduitAPIMethod' => 'ConduitAPIMethod',
'OwnersEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
@ -8825,10 +8876,12 @@ phutil_register_library_map(array(
'PhabricatorPeopleProfileManageController' => 'PhabricatorPeopleProfileController',
'PhabricatorPeopleProfileMenuEngine' => 'PhabricatorProfileMenuEngine',
'PhabricatorPeopleProfilePictureController' => 'PhabricatorPeopleProfileController',
'PhabricatorPeopleProfileRevisionsController' => 'PhabricatorPeopleProfileController',
'PhabricatorPeopleProfileTasksController' => 'PhabricatorPeopleProfileController',
'PhabricatorPeopleProfileViewController' => 'PhabricatorPeopleProfileController',
'PhabricatorPeopleQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorPeopleRenameController' => 'PhabricatorPeopleController',
'PhabricatorPeopleRevisionsProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorPeopleSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhabricatorPeopleTasksProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorPeopleTestDataGenerator' => 'PhabricatorTestDataGenerator',
@ -9032,6 +9085,7 @@ phutil_register_library_map(array(
'PhabricatorProjectEditController' => 'PhabricatorProjectController',
'PhabricatorProjectEditEngine' => 'PhabricatorEditEngine',
'PhabricatorProjectEditPictureController' => 'PhabricatorProjectController',
'PhabricatorProjectFilterTransaction' => 'PhabricatorProjectTransactionType',
'PhabricatorProjectFulltextEngine' => 'PhabricatorFulltextEngine',
'PhabricatorProjectHeraldAction' => 'HeraldAction',
'PhabricatorProjectHeraldAdapter' => 'HeraldAdapter',
@ -9044,6 +9098,7 @@ phutil_register_library_map(array(
'PhabricatorProjectListController' => 'PhabricatorProjectController',
'PhabricatorProjectListView' => 'AphrontView',
'PhabricatorProjectLockController' => 'PhabricatorProjectController',
'PhabricatorProjectLockTransaction' => 'PhabricatorProjectTransactionType',
'PhabricatorProjectLogicalAncestorDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
'PhabricatorProjectLogicalDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
'PhabricatorProjectLogicalOrNotDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
@ -9061,6 +9116,7 @@ phutil_register_library_map(array(
'PhabricatorProjectMembersRemoveController' => 'PhabricatorProjectController',
'PhabricatorProjectMembersViewController' => 'PhabricatorProjectController',
'PhabricatorProjectMenuItemController' => 'PhabricatorProjectController',
'PhabricatorProjectMilestoneTransaction' => 'PhabricatorProjectTypeTransaction',
'PhabricatorProjectMoveController' => 'PhabricatorProjectController',
'PhabricatorProjectNameContextFreeGrammar' => 'PhutilContextFreeGrammar',
'PhabricatorProjectNameTransaction' => 'PhabricatorProjectTransactionType',
@ -9069,6 +9125,7 @@ phutil_register_library_map(array(
'PhabricatorProjectOrUserDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
'PhabricatorProjectOrUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
'PhabricatorProjectPHIDResolver' => 'PhabricatorPHIDResolver',
'PhabricatorProjectParentTransaction' => 'PhabricatorProjectTypeTransaction',
'PhabricatorProjectPictureProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorProjectPointsProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorProjectProfileController' => 'PhabricatorProjectController',
@ -9086,6 +9143,7 @@ phutil_register_library_map(array(
'PhabricatorProjectSilencedEdgeType' => 'PhabricatorEdgeType',
'PhabricatorProjectSlug' => 'PhabricatorProjectDAO',
'PhabricatorProjectSlugsTransaction' => 'PhabricatorProjectTransactionType',
'PhabricatorProjectSortTransaction' => 'PhabricatorProjectTransactionType',
'PhabricatorProjectStandardCustomField' => array(
'PhabricatorProjectCustomField',
'PhabricatorStandardCustomFieldInterface',
@ -9100,6 +9158,7 @@ phutil_register_library_map(array(
'PhabricatorProjectTransactionEditor' => 'PhabricatorApplicationTransactionEditor',
'PhabricatorProjectTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorProjectTransactionType' => 'PhabricatorModularTransactionType',
'PhabricatorProjectTypeTransaction' => 'PhabricatorProjectTransactionType',
'PhabricatorProjectUIEventListener' => 'PhabricatorEventListener',
'PhabricatorProjectUpdateController' => 'PhabricatorProjectController',
'PhabricatorProjectUserFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
@ -9108,7 +9167,9 @@ phutil_register_library_map(array(
'PhabricatorProjectWatchController' => 'PhabricatorProjectController',
'PhabricatorProjectWatcherListView' => 'PhabricatorProjectUserListView',
'PhabricatorProjectWorkboardBackgroundColor' => 'Phobject',
'PhabricatorProjectWorkboardBackgroundTransaction' => 'PhabricatorProjectTransactionType',
'PhabricatorProjectWorkboardProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorProjectWorkboardTransaction' => 'PhabricatorProjectTransactionType',
'PhabricatorProjectsAncestorsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
'PhabricatorProjectsCurtainExtension' => 'PHUICurtainExtension',
'PhabricatorProjectsEditEngineExtension' => 'PhabricatorEditEngineExtension',
@ -10266,11 +10327,14 @@ phutil_register_library_map(array(
),
'PhrictionDocumentAuthorHeraldField' => 'PhrictionDocumentHeraldField',
'PhrictionDocumentContentHeraldField' => 'PhrictionDocumentHeraldField',
'PhrictionDocumentContentTransaction' => 'PhrictionDocumentTransactionType',
'PhrictionDocumentController' => 'PhrictionController',
'PhrictionDocumentDeleteTransaction' => 'PhrictionDocumentTransactionType',
'PhrictionDocumentFulltextEngine' => 'PhabricatorFulltextEngine',
'PhrictionDocumentHeraldAdapter' => 'HeraldAdapter',
'PhrictionDocumentHeraldField' => 'HeraldField',
'PhrictionDocumentHeraldFieldGroup' => 'HeraldFieldGroup',
'PhrictionDocumentMoveAwayTransaction' => 'PhrictionDocumentTransactionType',
'PhrictionDocumentMoveToTransaction' => 'PhrictionDocumentTransactionType',
'PhrictionDocumentPHIDType' => 'PhabricatorPHIDType',
'PhrictionDocumentPathHeraldField' => 'PhrictionDocumentHeraldField',

View file

@ -125,7 +125,7 @@ final class PhabricatorCalendarEventViewController
->setName(pht('Imported'))
->setIcon('fa-download')
->setHref($event->getImportSource()->getURI())
->setShade('orange'));
->setColor(PHUITagView::COLOR_ORANGE));
}
foreach ($this->buildRSVPActions($event) as $action) {

View file

@ -29,7 +29,7 @@ final class PHUIUserAvailabilityView
$away_tag = id(new PHUITagView())
->setType(PHUITagView::TYPE_SHADE)
->setShade($color)
->setColor($color)
->setName($name)
->setDotColor($color);

View file

@ -71,7 +71,7 @@ abstract class ConpherenceController extends PhabricatorController {
if (strlen($data['topic'])) {
$topic = id(new PHUITagView())
->setName($data['topic'])
->setShade(PHUITagView::COLOR_VIOLET)
->setColor(PHUITagView::COLOR_VIOLET)
->setType(PHUITagView::TYPE_SHADE)
->addClass('conpherence-header-topic');
$header->addTag($topic);

View file

@ -19,10 +19,9 @@ final class PhabricatorDaemonLogGarbageCollector
queryfx(
$conn_w,
'DELETE FROM %T WHERE dateCreated < %d AND status != %s LIMIT 100',
'DELETE FROM %T WHERE dateModified < %d LIMIT 100',
$table->getTableName(),
$this->getGarbageEpoch(),
PhabricatorDaemonLog::STATUS_RUNNING);
$this->getGarbageEpoch());
return ($conn_w->getAffectedRows() == 100);
}

View file

@ -37,13 +37,13 @@ final class PhabricatorDaemonLog extends PhabricatorDaemonDAO
'status' => array(
'columns' => array('status'),
),
'dateCreated' => array(
'columns' => array('dateCreated'),
),
'key_daemonID' => array(
'columns' => array('daemonID'),
'unique' => true,
),
'key_modified' => array(
'columns' => array('dateModified'),
),
),
) + parent::getConfiguration();
}

View file

@ -47,6 +47,7 @@ final class PhabricatorDashboardViewController
->setTag('a')
->setText('Install Dashboard')
->setIcon('fa-plus')
->setColor(PHUIButtonView::GREEN)
->setWorkflow(true)
->setHref($this->getApplicationURI("/install/{$id}/"));
$header->addActionLink($install_button);

View file

@ -65,7 +65,7 @@ final class DifferentialRevisionStatus extends Phobject {
$tag = id(new PHUITagView())
->setName($status_name)
->setIcon(self::getRevisionStatusIcon($status))
->setShade(self::getRevisionStatusColor($status))
->setColor(self::getRevisionStatusColor($status))
->setType(PHUITagView::TYPE_SHADE);
return $tag;

View file

@ -1,23 +0,0 @@
<?php
final class DifferentialDraft extends DifferentialDAO {
protected $objectPHID;
protected $authorPHID;
protected $draftKey;
protected function getConfiguration() {
return array(
self::CONFIG_COLUMN_SCHEMA => array(
'draftKey' => 'text64',
),
self::CONFIG_KEY_SCHEMA => array(
'key_unique' => array(
'columns' => array('objectPHID', 'authorPHID', 'draftKey'),
'unique' => true,
),
),
) + parent::getConfiguration();
}
}

View file

@ -50,7 +50,8 @@ final class DifferentialRevisionAcceptTransaction
protected function getActionOptions(
PhabricatorUser $viewer,
DifferentialRevision $revision) {
DifferentialRevision $revision,
$include_accepted = false) {
$reviewers = $revision->getReviewers();
@ -98,10 +99,13 @@ final class DifferentialRevisionAcceptTransaction
}
}
if ($reviewer->isAccepted($diff_phid)) {
// If a reviewer is already in a full "accepted" state, don't
// include that reviewer as an option.
continue;
if (!$include_accepted) {
if ($reviewer->isAccepted($diff_phid)) {
// If a reviewer is already in a full "accepted" state, don't
// include that reviewer as an option unless we're listing all
// reviwers, including reviewers who have already accepted.
continue;
}
}
$reviewer_phids[$reviewer_phid] = $reviewer_phid;
@ -185,7 +189,12 @@ final class DifferentialRevisionAcceptTransaction
'least one reviewer.'));
}
list($options) = $this->getActionOptions($actor, $object);
// NOTE: We're including reviewers who have already been accepted in this
// check. Legitimate users may race one another to accept on behalf of
// packages. If we get a form submission which includes a reviewer which
// someone has already accepted, that's fine. See T12757.
list($options) = $this->getActionOptions($actor, $object, true);
foreach ($value as $phid) {
if (!isset($options[$phid])) {
throw new Exception(

View file

@ -17,6 +17,16 @@ abstract class DifferentialRevisionReviewTransaction
$viewer = $this->getActor();
list($options, $default) = $this->getActionOptions($viewer, $object);
// Remove reviewers which aren't actionable. In the case of "Accept", we
// may allow the transaction to proceed with some reviewers who have
// already accepted, to avoid race conditions where two reviewers fill
// out the form at the same time and accept on behalf of the same package.
// It's okay for these reviewers to survive validation, but they should
// not survive beyond this point.
$value = array_fuse($value);
$value = array_intersect($value, array_keys($options));
$value = array_values($value);
sort($default);
sort($value);

View file

@ -332,7 +332,7 @@ abstract class DiffusionController extends PhabricatorController {
$tag = id(new PHUITagView())
->setName($commit)
->setShade('indigo')
->setColor(PHUITagView::COLOR_INDIGO)
->setType(PHUITagView::TYPE_SHADE);
return $tag;

View file

@ -83,11 +83,11 @@ final class DiffusionRepositoryManagePanelsController
->setTag('a')
->setText(pht('View Repository'))
->setHref($repository->getURI())
->setIcon('fa-code'));
->setIcon('fa-code')
->setColor(PHUIButtonView::GREEN));
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setNavigation($nav)
->setMainColumn($content);
$curtain = $panel->buildManagementPanelCurtain();
@ -98,6 +98,7 @@ final class DiffusionRepositoryManagePanelsController
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($nav)
->appendChild($view);
}
@ -112,6 +113,12 @@ final class DiffusionRepositoryManagePanelsController
$nav = id(new AphrontSideNavFilterView())
->setBaseURI($base_uri);
$item = id(new PHUIListItemView())
->setName(pht('manage'))
->setType(PHUIListItemView::TYPE_LABEL);
$nav->addMenuItem($item);
foreach ($panels as $panel) {
$key = $panel->getManagementPanelKey();
$label = $panel->getManagementPanelLabel();

View file

@ -37,9 +37,10 @@ final class DiffusionRepositoryActionsManagementPanel
);
}
protected function buildManagementPanelActions() {
public function buildManagementPanelCurtain() {
$repository = $this->getRepository();
$viewer = $this->getViewer();
$action_list = $this->getNewActionList();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
@ -48,14 +49,15 @@ final class DiffusionRepositoryActionsManagementPanel
$actions_uri = $this->getEditPageURI();
return array(
$action_list->addAction(
id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Actions'))
->setHref($actions_uri)
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit),
);
->setWorkflow(!$can_edit));
return $this->getNewCurtainView($action_list);
}
public function buildManagementPanelContent() {
@ -63,8 +65,7 @@ final class DiffusionRepositoryActionsManagementPanel
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setViewer($viewer)
->setActionList($this->newActions());
->setViewer($viewer);
$notify = $repository->getDetail('herald-disabled')
? pht('Off')

View file

@ -46,9 +46,10 @@ final class DiffusionRepositoryAutomationManagementPanel
return 'fa-truck';
}
protected function buildManagementPanelActions() {
public function buildManagementPanelCurtain() {
$repository = $this->getRepository();
$viewer = $this->getViewer();
$action_list = $this->getNewActionList();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
@ -60,20 +61,23 @@ final class DiffusionRepositoryAutomationManagementPanel
$automation_uri = $this->getEditPageURI();
$test_uri = $repository->getPathURI('edit/testautomation/');
return array(
$action_list->addAction(
id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Automation'))
->setHref($automation_uri)
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit),
->setWorkflow(!$can_edit));
$action_list->addAction(
id(new PhabricatorActionView())
->setIcon('fa-gamepad')
->setName(pht('Test Configuration'))
->setWorkflow(true)
->setDisabled(!$can_test)
->setHref($test_uri),
);
->setHref($test_uri));
return $this->getNewCurtainView($action_list);
}
public function buildManagementPanelContent() {
@ -81,8 +85,7 @@ final class DiffusionRepositoryAutomationManagementPanel
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setViewer($viewer)
->setActionList($this->newActions());
->setViewer($viewer);
$blueprint_phids = $repository->getAutomationBlueprintPHIDs();
if (!$blueprint_phids) {

View file

@ -33,9 +33,10 @@ final class DiffusionRepositoryBasicsManagementPanel
);
}
protected function buildManagementPanelActions() {
public function buildManagementPanelCurtain() {
$repository = $this->getRepository();
$viewer = $this->getViewer();
$action_list = $this->getNewActionList();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
@ -67,38 +68,47 @@ final class DiffusionRepositoryBasicsManagementPanel
$can_dangerous = ($can_edit && $repository->canAllowDangerousChanges());
}
return array(
$action_list->addAction(
id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Basic Information'))
->setHref($edit_uri)
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit),
->setWorkflow(!$can_edit));
$action_list->addAction(
id(new PhabricatorActionView())
->setIcon('fa-text-width')
->setName(pht('Edit Text Encoding'))
->setHref($encoding_uri)
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit),
->setWorkflow(!$can_edit));
$action_list->addAction(
id(new PhabricatorActionView())
->setIcon($dangerous_icon)
->setName($dangerous_name)
->setHref($dangerous_uri)
->setDisabled(!$can_dangerous)
->setWorkflow(true),
->setWorkflow(true));
$action_list->addAction(
id(new PhabricatorActionView())
->setHref($activate_uri)
->setIcon($activate_icon)
->setName($activate_label)
->setDisabled(!$can_edit)
->setWorkflow(true),
->setWorkflow(true));
$action_list->addAction(
id(new PhabricatorActionView())
->setName(pht('Delete Repository'))
->setIcon('fa-times')
->setHref($delete_uri)
->setDisabled(true)
->setWorkflow(true),
);
->setWorkflow(true));
return $this->getNewCurtainView($action_list);
}
public function buildManagementPanelContent() {
@ -108,6 +118,7 @@ final class DiffusionRepositoryBasicsManagementPanel
$repository = $this->getRepository();
$is_new = $repository->isNewlyInitialized();
$info_view = null;
if ($is_new) {
$messages = array();
@ -131,8 +142,6 @@ final class DiffusionRepositoryBasicsManagementPanel
$info_view = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
->setErrors($messages);
$basics->setInfoView($info_view);
}
$result[] = $basics;
@ -142,7 +151,7 @@ final class DiffusionRepositoryBasicsManagementPanel
$result[] = $this->newBox(pht('Description'), $description);
}
return $result;
return array($info_view, $result);
}
private function buildBasics() {
@ -150,8 +159,7 @@ final class DiffusionRepositoryBasicsManagementPanel
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setViewer($viewer)
->setActionList($this->newActions());
->setViewer($viewer);
$name = $repository->getName();
$view->addProperty(pht('Name'), $name);

View file

@ -41,9 +41,10 @@ final class DiffusionRepositoryBranchesManagementPanel
);
}
protected function buildManagementPanelActions() {
public function buildManagementPanelCurtain() {
$repository = $this->getRepository();
$viewer = $this->getViewer();
$action_list = $this->getNewActionList();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
@ -52,14 +53,15 @@ final class DiffusionRepositoryBranchesManagementPanel
$branches_uri = $this->getEditPageURI();
return array(
$action_list->addAction(
id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Branches'))
->setHref($branches_uri)
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit),
);
->setWorkflow(!$can_edit));
return $this->getNewCurtainView($action_list);
}
public function buildManagementPanelContent() {
@ -67,8 +69,7 @@ final class DiffusionRepositoryBranchesManagementPanel
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setViewer($viewer)
->setActionList($this->newActions());
->setViewer($viewer);
$default_branch = nonempty(
$repository->getHumanReadableDetail('default-branch'),

View file

@ -21,6 +21,10 @@ final class DiffusionRepositoryDocumentationManagementPanel
return null;
}
public function buildManagementPanelCurtain() {
return null;
}
public function getPanelNavigationURI() {
return PhabricatorEnv::getDoclink(
'Diffusion User Guide: Managing Repositories');

View file

@ -21,5 +21,8 @@ final class DiffusionRepositoryHistoryManagementPanel
return $this->newTimeline();
}
public function buildManagementPanelCurtain() {
return null;
}
}

View file

@ -37,6 +37,7 @@ abstract class DiffusionRepositoryManagementPanel
abstract public function getManagementPanelLabel();
abstract public function getManagementPanelOrder();
abstract public function buildManagementPanelContent();
abstract public function buildManagementPanelCurtain();
public function getManagementPanelIcon() {
return 'fa-pencil';
@ -51,41 +52,20 @@ abstract class DiffusionRepositoryManagementPanel
return true;
}
final protected function newActions() {
$actions = $this->buildManagementPanelActions();
if (!$actions) {
return null;
}
public function getNewActionList() {
$viewer = $this->getViewer();
$action_id = celerity_generate_unique_node_id();
$action_list = id(new PhabricatorActionListView())
->setViewer($viewer);
foreach ($actions as $action) {
$action_list->addAction($action);
}
return $action_list;
return id(new PhabricatorActionListView())
->setViewer($viewer)
->setID($action_id);
}
public function buildManagementPanelCurtain() {
// TODO: Delete or fix this, curtains always render in the left gutter
// at the moment.
return null;
$actions = $this->newActions();
if (!$actions) {
return null;
}
public function getNewCurtainView(PhabricatorActionListView $action_list) {
$viewer = $this->getViewer();
$curtain = id(new PHUICurtainView())
return id(new PHUICurtainView())
->setViewer($viewer)
->setActionList($actions);
return $curtain;
->setActionList($action_list);
}
public static function getAllPanels() {

View file

@ -54,9 +54,10 @@ final class DiffusionRepositoryPoliciesManagementPanel
);
}
protected function buildManagementPanelActions() {
public function buildManagementPanelCurtain() {
$repository = $this->getRepository();
$viewer = $this->getViewer();
$action_list = $this->getNewActionList();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
@ -65,14 +66,15 @@ final class DiffusionRepositoryPoliciesManagementPanel
$edit_uri = $this->getEditPageURI();
return array(
$action_list->addAction(
id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Policies'))
->setHref($edit_uri)
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit),
);
->setWorkflow(!$can_edit));
return $this->getNewCurtainView($action_list);
}
public function buildManagementPanelContent() {
@ -80,8 +82,7 @@ final class DiffusionRepositoryPoliciesManagementPanel
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setViewer($viewer)
->setActionList($this->newActions());
->setViewer($viewer);
$descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions(
$viewer,

View file

@ -37,9 +37,10 @@ final class DiffusionRepositoryStagingManagementPanel
);
}
protected function buildManagementPanelActions() {
public function buildManagementPanelCurtain() {
$repository = $this->getRepository();
$viewer = $this->getViewer();
$action_list = $this->getNewActionList();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
@ -48,14 +49,15 @@ final class DiffusionRepositoryStagingManagementPanel
$staging_uri = $this->getEditPageURI();
return array(
$action_list->addAction(
id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Staging'))
->setHref($staging_uri)
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit),
);
->setWorkflow(!$can_edit));
return $this->getNewCurtainView($action_list);
}
public function buildManagementPanelContent() {
@ -63,8 +65,7 @@ final class DiffusionRepositoryStagingManagementPanel
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setViewer($viewer)
->setActionList($this->newActions());
->setViewer($viewer);
$staging_uri = $repository->getStagingURI();
if (!$staging_uri) {

View file

@ -28,9 +28,10 @@ final class DiffusionRepositoryStatusManagementPanel
return 'fa-check grey';
}
protected function buildManagementPanelActions() {
public function buildManagementPanelCurtain() {
$repository = $this->getRepository();
$viewer = $this->getViewer();
$action_list = $this->getNewActionList();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
@ -39,14 +40,15 @@ final class DiffusionRepositoryStatusManagementPanel
$update_uri = $repository->getPathURI('edit/update/');
return array(
$action_list->addAction(
id(new PhabricatorActionView())
->setIcon('fa-refresh')
->setName(pht('Update Now'))
->setWorkflow(true)
->setDisabled(!$can_edit)
->setHref($update_uri),
);
->setHref($update_uri));
return $this->getNewCurtainView($action_list);
}
public function buildManagementPanelContent() {
@ -54,8 +56,7 @@ final class DiffusionRepositoryStatusManagementPanel
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setViewer($viewer)
->setActionList($this->newActions());
->setViewer($viewer);
$view->addProperty(
pht('Update Frequency'),

View file

@ -25,6 +25,22 @@ final class DiffusionRepositoryStorageManagementPanel
}
}
public function buildManagementPanelCurtain() {
$repository = $this->getRepository();
$viewer = $this->getViewer();
$action_list = $this->getNewActionList();
$doc_href = PhabricatorEnv::getDoclink('Cluster: Repositories');
$action_list->addAction(
id(new PhabricatorActionView())
->setIcon('fa-book')
->setHref($doc_href)
->setName(pht('Cluster Documentation')));
return $this->getNewCurtainView($action_list);
}
public function buildManagementPanelContent() {
return array(
$this->buildStorageStatusPanel(),
@ -55,13 +71,9 @@ final class DiffusionRepositoryStorageManagementPanel
$view->addProperty(pht('Storage Path'), $storage_path);
$view->addProperty(pht('Storage Cluster'), $storage_service);
$header = id(new PHUIHeaderView())
->setHeader(pht('Storage'));
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->addPropertyList($view);
$box = $this->newBox(pht('Storage'), null);
$box->addPropertyList($view);
return $box;
}
private function buildClusterStatusPanel() {
@ -231,21 +243,9 @@ final class DiffusionRepositoryStorageManagementPanel
'date',
));
$doc_href = PhabricatorEnv::getDoclink('Cluster: Repositories');
$header = id(new PHUIHeaderView())
->setHeader(pht('Cluster Status'))
->addActionLink(
id(new PHUIButtonView())
->setIcon('fa-book')
->setHref($doc_href)
->setTag('a')
->setText(pht('Documentation')));
return id(new PHUIObjectBoxView())
->setHeader($header)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($table);
$box = $this->newBox(pht('Cluster Status'), null);
$box->setTable($table);
return $box;
}
}

View file

@ -36,9 +36,10 @@ final class DiffusionRepositorySubversionManagementPanel
);
}
protected function buildManagementPanelActions() {
public function buildManagementPanelCurtain() {
$repository = $this->getRepository();
$viewer = $this->getViewer();
$action_list = $this->getNewActionList();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
@ -47,14 +48,15 @@ final class DiffusionRepositorySubversionManagementPanel
$subversion_uri = $this->getEditPageURI();
return array(
$action_list->addAction(
id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Properties'))
->setHref($subversion_uri)
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit),
);
->setWorkflow(!$can_edit));
return $this->getNewCurtainView($action_list);
}
public function buildManagementPanelContent() {
@ -62,8 +64,7 @@ final class DiffusionRepositorySubversionManagementPanel
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setViewer($viewer)
->setActionList($this->newActions());
->setViewer($viewer);
$default_branch = nonempty(
$repository->getHumanReadableDetail('svn-subpath'),

View file

@ -34,9 +34,10 @@ final class DiffusionRepositorySymbolsManagementPanel
);
}
protected function buildManagementPanelActions() {
public function buildManagementPanelCurtain() {
$repository = $this->getRepository();
$viewer = $this->getViewer();
$action_list = $this->getNewActionList();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
@ -45,14 +46,15 @@ final class DiffusionRepositorySymbolsManagementPanel
$symbols_uri = $this->getEditPageURI();
return array(
$action_list->addAction(
id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Symbols'))
->setHref($symbols_uri)
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit),
);
->setWorkflow(!$can_edit));
return $this->getNewCurtainView($action_list);
}
public function buildManagementPanelContent() {
@ -60,8 +62,7 @@ final class DiffusionRepositorySymbolsManagementPanel
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setViewer($viewer)
->setActionList($this->newActions());
->setViewer($viewer);
$languages = $repository->getSymbolLanguages();
if ($languages) {

View file

@ -17,6 +17,35 @@ final class DiffusionRepositoryURIsManagementPanel
return 400;
}
public function buildManagementPanelCurtain() {
$repository = $this->getRepository();
$viewer = $this->getViewer();
$action_list = $this->getNewActionList();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$repository,
PhabricatorPolicyCapability::CAN_EDIT);
$doc_href = PhabricatorEnv::getDoclink('Diffusion User Guide: URIs');
$add_href = $repository->getPathURI('uri/edit/');
$action_list->addAction(
id(new PhabricatorActionView())
->setIcon('fa-plus')
->setHref($add_href)
->setDisabled(!$can_edit)
->setName(pht('Add New URI')));
$action_list->addAction(
id(new PhabricatorActionView())
->setIcon('fa-book')
->setHref($doc_href)
->setName(pht('URI Documentation')));
return $this->getNewCurtainView($action_list);
}
public function buildManagementPanelContent() {
$repository = $this->getRepository();
$viewer = $this->getViewer();
@ -95,24 +124,6 @@ final class DiffusionRepositoryURIsManagementPanel
null,
));
$doc_href = PhabricatorEnv::getDoclink('Diffusion User Guide: URIs');
$add_href = $repository->getPathURI('uri/edit/');
$header = id(new PHUIHeaderView())
->setHeader(pht('Repository URIs'))
->addActionLink(
id(new PHUIButtonView())
->setIcon('fa-plus')
->setHref($add_href)
->setTag('a')
->setText(pht('Add New URI')))
->addActionLink(
id(new PHUIButtonView())
->setIcon('fa-book')
->setHref($doc_href)
->setTag('a')
->setText(pht('Documentation')));
$is_new = $repository->isNewlyInitialized();
$messages = array();
@ -123,11 +134,7 @@ final class DiffusionRepositoryURIsManagementPanel
$host_message = pht('Phabricator is hosting this repository.');
}
$messages[] = array(
id(new PHUIIconView())->setIcon('fa-folder'),
' ',
$host_message,
);
$messages[] = $host_message;
} else {
if ($is_new) {
$observe_message = pht(
@ -137,22 +144,17 @@ final class DiffusionRepositoryURIsManagementPanel
'This repository is hosted remotely. Phabricator is observing it.');
}
$messages[] = array(
id(new PHUIIconView())->setIcon('fa-download'),
' ',
$observe_message,
);
$messages[] = $observe_message;
}
$info_view = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
->setErrors($messages);
return id(new PHUIObjectBoxView())
->setHeader($header)
->setInfoView($info_view)
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($table);
$box = $this->newBox(pht('Repository URIs'), null);
$box->setTable($table);
return array($info_view, $box);
}
}

View file

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

View file

@ -67,16 +67,16 @@ final class FeedQueryConduitAPIMethod extends FeedConduitAPIMethod {
if (!$limit) {
$limit = $this->getDefaultLimit();
}
$filter_phids = $request->getValue('filterPHIDs');
if (!$filter_phids) {
$filter_phids = array();
}
$query = id(new PhabricatorFeedQuery())
->setLimit($limit)
->setFilterPHIDs($filter_phids)
->setViewer($user);
$filter_phids = $request->getValue('filterPHIDs');
if ($filter_phids) {
$query->withFilterPHIDs($filter_phids);
}
$after = $request->getValue('after');
if (strlen($after)) {
$query->setAfterID($after);

View file

@ -5,8 +5,10 @@ final class PhabricatorFeedQuery
private $filterPHIDs;
private $chronologicalKeys;
private $rangeMin;
private $rangeMax;
public function setFilterPHIDs(array $phids) {
public function withFilterPHIDs(array $phids) {
$this->filterPHIDs = $phids;
return $this;
}
@ -16,50 +18,52 @@ final class PhabricatorFeedQuery
return $this;
}
public function withEpochInRange($range_min, $range_max) {
$this->rangeMin = $range_min;
$this->rangeMax = $range_max;
return $this;
}
public function newResultObject() {
return new PhabricatorFeedStoryData();
}
protected function loadPage() {
$story_table = new PhabricatorFeedStoryData();
$conn = $story_table->establishConnection('r');
$data = queryfx_all(
$conn,
'SELECT story.* FROM %T story %Q %Q %Q %Q %Q',
$story_table->getTableName(),
$this->buildJoinClause($conn),
$this->buildWhereClause($conn),
$this->buildGroupClause($conn),
$this->buildOrderClause($conn),
$this->buildLimitClause($conn));
return $data;
// NOTE: We return raw rows from this method, which is a little unusual.
return $this->loadStandardPageRows($this->newResultObject());
}
protected function willFilterPage(array $data) {
return PhabricatorFeedStory::loadAllFromRows($data, $this->getViewer());
}
protected function buildJoinClause(AphrontDatabaseConnection $conn_r) {
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
$joins = parent::buildJoinClauseParts($conn);
// NOTE: We perform this join unconditionally (even if we have no filter
// PHIDs) to omit rows which have no story references. These story data
// rows are notifications or realtime alerts.
$ref_table = new PhabricatorFeedStoryReference();
return qsprintf(
$conn_r,
$joins[] = qsprintf(
$conn,
'JOIN %T ref ON ref.chronologicalKey = story.chronologicalKey',
$ref_table->getTableName());
return $joins;
}
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
$where = array();
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->filterPHIDs) {
if ($this->filterPHIDs !== null) {
$where[] = qsprintf(
$conn_r,
$conn,
'ref.objectPHID IN (%Ls)',
$this->filterPHIDs);
}
if ($this->chronologicalKeys) {
if ($this->chronologicalKeys !== null) {
// NOTE: We want to use integers in the query so we can take advantage
// of keys, but can't use %d on 32-bit systems. Make sure all the keys
// are integers and then format them raw.
@ -73,21 +77,37 @@ final class PhabricatorFeedQuery
}
$where[] = qsprintf(
$conn_r,
$conn,
'ref.chronologicalKey IN (%Q)',
implode(', ', $keys));
}
$where[] = $this->buildPagingClause($conn_r);
// NOTE: We may not have 64-bit PHP, so do the shifts in MySQL instead.
// From EXPLAIN, it appears like MySQL is smart enough to compute the
// result and make use of keys to execute the query.
return $this->formatWhereClause($where);
if ($this->rangeMin !== null) {
$where[] = qsprintf(
$conn,
'ref.chronologicalKey >= (%d << 32)',
$this->rangeMin);
}
if ($this->rangeMax !== null) {
$where[] = qsprintf(
$conn,
'ref.chronologicalKey < (%d << 32)',
$this->rangeMax);
}
return $where;
}
protected function buildGroupClause(AphrontDatabaseConnection $conn_r) {
if ($this->filterPHIDs) {
return qsprintf($conn_r, 'GROUP BY ref.chronologicalKey');
protected function buildGroupClause(AphrontDatabaseConnection $conn) {
if ($this->filterPHIDs !== null) {
return qsprintf($conn, 'GROUP BY ref.chronologicalKey');
} else {
return qsprintf($conn_r, 'GROUP BY story.chronologicalKey');
return qsprintf($conn, 'GROUP BY story.chronologicalKey');
}
}
@ -95,6 +115,20 @@ final class PhabricatorFeedQuery
return array('key');
}
public function getBuiltinOrders() {
return array(
'newest' => array(
'vector' => array('key'),
'name' => pht('Creation (Newest First)'),
'aliases' => array('created'),
),
'oldest' => array(
'vector' => array('-key'),
'name' => pht('Creation (Oldest First)'),
),
);
}
public function getOrderableColumns() {
$table = ($this->filterPHIDs ? 'ref' : 'story');
return array(
@ -120,6 +154,10 @@ final class PhabricatorFeedQuery
return $item['chronologicalKey'];
}
protected function getPrimaryTableAlias() {
return 'story';
}
public function getQueryApplicationClass() {
return 'PhabricatorFeedApplication';
}

View file

@ -11,87 +11,99 @@ final class PhabricatorFeedSearchEngine
return 'PhabricatorFeedApplication';
}
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
$saved->setParameter(
'userPHIDs',
$this->readUsersFromRequest($request, 'users'));
$saved->setParameter(
'projectPHIDs',
array_values($request->getArr('projectPHIDs')));
$saved->setParameter(
'viewerProjects',
$request->getBool('viewerProjects'));
return $saved;
public function newQuery() {
return new PhabricatorFeedQuery();
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new PhabricatorFeedQuery());
protected function shouldShowOrderField() {
return false;
}
protected function buildCustomSearchFields() {
return array(
id(new PhabricatorUsersSearchField())
->setLabel(pht('Include Users'))
->setKey('userPHIDs'),
// NOTE: This query is not executed with EdgeLogic, so we can't use
// a fancy logical datasource.
id(new PhabricatorSearchDatasourceField())
->setDatasource(new PhabricatorProjectDatasource())
->setLabel(pht('Include Projects'))
->setKey('projectPHIDs'),
id(new PhabricatorSearchDateControlField())
->setLabel(pht('Occurs After'))
->setKey('rangeStart'),
id(new PhabricatorSearchDateControlField())
->setLabel(pht('Occurs Before'))
->setKey('rangeEnd'),
// NOTE: This is a legacy field retained only for backward
// compatibility. If the projects field used EdgeLogic, we could use
// `viewerprojects()` to execute an equivalent query.
id(new PhabricatorSearchCheckboxesField())
->setKey('viewerProjects')
->setOptions(
array(
'self' => pht('Include stories about projects I am a member of.'),
)),
);
}
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
$phids = array();
$user_phids = $saved->getParameter('userPHIDs');
if ($user_phids) {
$phids[] = $user_phids;
if ($map['userPHIDs']) {
$phids += array_fuse($map['userPHIDs']);
}
$proj_phids = $saved->getParameter('projectPHIDs');
if ($proj_phids) {
$phids[] = $proj_phids;
if ($map['projectPHIDs']) {
$phids += array_fuse($map['projectPHIDs']);
}
$viewer_projects = $saved->getParameter('viewerProjects');
// NOTE: This value may be `true` for older saved queries, or
// `array('self')` for newer ones.
$viewer_projects = $map['viewerProjects'];
if ($viewer_projects) {
$viewer = $this->requireViewer();
$projects = id(new PhabricatorProjectQuery())
->setViewer($viewer)
->withMemberPHIDs(array($viewer->getPHID()))
->execute();
$phids[] = mpull($projects, 'getPHID');
$phids += array_fuse(mpull($projects, 'getPHID'));
}
$phids = array_mergev($phids);
if ($phids) {
$query->setFilterPHIDs($phids);
$query->withFilterPHIDs($phids);
}
$range_min = $map['rangeStart'];
if ($range_min) {
$range_min = $range_min->getEpoch();
}
$range_max = $map['rangeEnd'];
if ($range_max) {
$range_max = $range_max->getEpoch();
}
if ($range_min && $range_max) {
if ($range_min > $range_max) {
throw new PhabricatorSearchConstraintException(
pht(
'The specified "Occurs Before" date is earlier in time than the '.
'specified "Occurs After" date, so this query can never match '.
'any results.'));
}
}
if ($range_min || $range_max) {
$query->withEpochInRange($range_min, $range_max);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
$user_phids = $saved_query->getParameter('userPHIDs', array());
$proj_phids = $saved_query->getParameter('projectPHIDs', array());
$viewer_projects = $saved_query->getParameter('viewerProjects');
$form
->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('users')
->setLabel(pht('Include Users'))
->setValue($user_phids))
->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorProjectDatasource())
->setName('projectPHIDs')
->setLabel(pht('Include Projects'))
->setValue($proj_phids))
->appendChild(
id(new AphrontFormCheckboxControl())
->addCheckbox(
'viewerProjects',
1,
pht('Include stories about projects I am a member of.'),
$viewer_projects));
}
protected function getURI($path) {
return '/feed/'.$path;
}
@ -117,7 +129,7 @@ final class PhabricatorFeedSearchEngine
case 'all':
return $query;
case 'projects':
return $query->setParameter('viewerProjects', true);
return $query->setParameter('viewerProjects', array('self'));
}
return parent::buildSavedQueryFromBuiltin($query_key);

View file

@ -44,7 +44,7 @@ final class PhabricatorFileInfoController extends PhabricatorFileController {
if ($ttl !== null) {
$ttl_tag = id(new PHUITagView())
->setType(PHUITagView::TYPE_SHADE)
->setShade(PHUITagView::COLOR_YELLOW)
->setColor(PHUITagView::COLOR_YELLOW)
->setName(pht('Temporary'));
$header->addTag($ttl_tag);
}
@ -53,7 +53,7 @@ final class PhabricatorFileInfoController extends PhabricatorFileController {
if ($partial) {
$partial_tag = id(new PHUITagView())
->setType(PHUITagView::TYPE_SHADE)
->setShade(PHUITagView::COLOR_ORANGE)
->setColor(PHUITagView::COLOR_ORANGE)
->setName(pht('Partial Upload'));
$header->addTag($partial_tag);
}

View file

@ -30,8 +30,8 @@ final class PhabricatorGuideListView extends AphrontView {
->setText(pht('Skip'))
->setTag('a')
->setHref($skip_href)
->setColor(PHUIButtonView::GREY);
$list_item->setLaunchButton($skip);
->setColor(PHUIButtonView::SIMPLE);
$list_item->setSideColumn($skip);
}
$list->addItem($list_item);
}

View file

@ -56,7 +56,7 @@ final class HarbormasterUnitSummaryView extends AphrontView {
$tag = id(new PHUITagView())
->setType(PHUITagView::TYPE_SHADE)
->setShade($tag_color)
->setColor($tag_color)
->setIcon($tag_icon)
->setName($tag_text);

View file

@ -125,6 +125,34 @@ final class ManiphestTaskTestCase extends PhabricatorTestCase {
9,
count($subpri),
pht('Expected subpriorities to be distributed.'));
// Move task 9 to the end.
$this->moveTask($viewer, $t[9], $t[1], true);
$tasks = $this->loadTasks($viewer, $auto_base);
$this->assertEqual(
array(8, 7, 6, 5, 4, 3, 2, 1, 9),
array_keys($tasks));
// Move task 3 to the beginning.
$this->moveTask($viewer, $t[3], $t[8], false);
$tasks = $this->loadTasks($viewer, $auto_base);
$this->assertEqual(
array(3, 8, 7, 6, 5, 4, 2, 1, 9),
array_keys($tasks));
// Move task 3 to the end.
$this->moveTask($viewer, $t[3], $t[9], true);
$tasks = $this->loadTasks($viewer, $auto_base);
$this->assertEqual(
array(8, 7, 6, 5, 4, 2, 1, 9, 3),
array_keys($tasks));
// Move task 5 to before task 4 (this is its current position).
$this->moveTask($viewer, $t[5], $t[4], false);
$tasks = $this->loadTasks($viewer, $auto_base);
$this->assertEqual(
array(8, 7, 6, 5, 4, 2, 1, 9, 3),
array_keys($tasks));
}
private function newTask(PhabricatorUser $viewer, $title) {

View file

@ -0,0 +1,44 @@
<?php
final class ManiphestPrioritySearchConduitAPIMethod
extends ManiphestConduitAPIMethod {
public function getAPIMethodName() {
return 'maniphest.priority.search';
}
public function getMethodSummary() {
return pht('Read information about task priorities.');
}
public function getMethodDescription() {
return pht(
'Returns information about the possible priorities for Maniphest '.
'tasks.');
}
protected function defineParamTypes() {
return array();
}
protected function defineReturnType() {
return 'map<string, wild>';
}
public function getRequiredScope() {
return self::SCOPE_ALWAYS;
}
protected function execute(ConduitAPIRequest $request) {
$config = ManiphestTaskPriority::getConfig();
$results = array();
foreach ($config as $code => $priority) {
$priority['value'] = $code;
$results[] = $priority;
}
return array('data' => $results);
}
}

View file

@ -335,9 +335,16 @@ dictionary with these keys:
"task", "feature", or "bug".
- `name` //Required string.// Human-readable name for this subtype, like
"Task", "Feature Request" or "Bug Report".
- `tag` //Optional string.// Tag text for this subtype.
- `color` //Optional string.// Display color for this subtype.
- `icon` //Optional string.// Icon for the subtype.
Each subtype must have a unique key, and you must define a subtype with
the key "%s", which is used as a default subtype.
The tag text (`tag`) is used to set the text shown in the subtype tag on list
views and workboards. If you do not configure it, the default subtype will have
no subtype tag and other subtypes will use their name as tag text.
EOTEXT
,
$subtype_default_key));

View file

@ -110,7 +110,7 @@ final class ManiphestTaskPriority extends ManiphestConstants {
return idx($config, 'disabled', false);
}
private static function getConfig() {
public static function getConfig() {
$config = PhabricatorEnv::getEnvConfig('maniphest.priorities');
krsort($config);
return $config;

View file

@ -97,7 +97,7 @@ final class ManiphestTaskStatus extends ManiphestConstants {
->setName($name)
->setIcon($icon)
->setType(PHUITagView::TYPE_SHADE)
->setShade($color);
->setColor($color);
return $tag;
}

View file

@ -233,13 +233,19 @@ final class ManiphestTaskDetailController extends ManiphestController {
ManiphestTaskPoints::getPointsLabel());
$tag = id(new PHUITagView())
->setName($points_name)
->setShade('blue')
->setColor(PHUITagView::COLOR_BLUE)
->setType(PHUITagView::TYPE_SHADE);
$view->addTag($tag);
}
}
$subtype = $task->newSubtypeObject();
if ($subtype && $subtype->hasTagView()) {
$subtype_tag = $subtype->newTagView();
$view->addTag($subtype_tag);
}
return $view;
}

View file

@ -384,8 +384,7 @@ final class ManiphestTransactionEditor
*/
public static function getAdjacentSubpriority(
ManiphestTask $dst,
$is_after,
$allow_recursion = true) {
$is_after) {
$query = id(new ManiphestTaskQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
@ -407,76 +406,25 @@ final class ManiphestTransactionEditor
// If we find an adjacent task, we average the two subpriorities and
// return the result.
if ($adjacent) {
$epsilon = 0.01;
$epsilon = 1.0;
// If the adjacent task has a subpriority that is identical or very
// close to the task we're looking at, we're going to move it and all
// tasks with the same subpriority a little farther down the subpriority
// scale.
if ($allow_recursion &&
(abs($adjacent->getSubpriority() - $base) < $epsilon)) {
$conn_w = $adjacent->establishConnection('w');
// close to the task we're looking at, we're going to spread out all
// the nearby tasks.
$min = ($adjacent->getSubpriority() - ($epsilon));
$max = ($adjacent->getSubpriority() + ($epsilon));
// Get all of the tasks with the similar subpriorities to the adjacent
// task, including the adjacent task itself.
$query = id(new ManiphestTaskQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withPriorities(array($adjacent->getPriority()))
->withSubpriorityBetween($min, $max);
if (!$is_after) {
$query->setOrderVector(array('-priority', '-subpriority', '-id'));
$adjacent_sub = $adjacent->getSubpriority();
if ((abs($adjacent_sub - $base) < $epsilon)) {
$base = self::disperseBlock(
$dst,
$epsilon * 2);
if ($is_after) {
$sub = $base - $epsilon;
} else {
$query->setOrderVector(array('priority', 'subpriority', 'id'));
}
$shift_all = $query->execute();
$shift_last = last($shift_all);
// Select the most extreme subpriority in the result set as the
// base value.
$shift_base = head($shift_all)->getSubpriority();
// Find the subpriority before or after the task at the end of the
// block.
list($shift_pri, $shift_sub) = self::getAdjacentSubpriority(
$shift_last,
$is_after,
$allow_recursion = false);
$delta = ($shift_sub - $shift_base);
$count = count($shift_all);
$shift = array();
$cursor = 1;
foreach ($shift_all as $shift_task) {
$shift_target = $shift_base + (($cursor / $count) * $delta);
$cursor++;
queryfx(
$conn_w,
'UPDATE %T SET subpriority = %f WHERE id = %d',
$adjacent->getTableName(),
$shift_target,
$shift_task->getID());
// If we're shifting the adjacent task, update it.
if ($shift_task->getID() == $adjacent->getID()) {
$adjacent->setSubpriority($shift_target);
}
// If we're shifting the original target task, update the base
// subpriority.
if ($shift_task->getID() == $dst->getID()) {
$base = $shift_target;
}
$sub = $base + $epsilon;
}
} else {
$sub = ($adjacent_sub + $base) / 2;
}
$sub = ($adjacent->getSubpriority() + $base) / 2;
} else {
// Otherwise, we take a step away from the target's subpriority and
// use that.
@ -490,6 +438,156 @@ final class ManiphestTransactionEditor
return array($dst->getPriority(), $sub);
}
/**
* Distribute a cluster of tasks with similar subpriorities.
*/
private static function disperseBlock(
ManiphestTask $task,
$spacing) {
$conn = $task->establishConnection('w');
// Find a block of subpriority space which is, on average, sparse enough
// to hold all the tasks that are inside it with a reasonable level of
// separation between them.
// We'll start by looking near the target task for a range of numbers
// which has more space available than tasks. For example, if the target
// task has subpriority 33 and we want to separate each task by at least 1,
// we might start by looking in the range [23, 43].
// If we find fewer than 20 tasks there, we have room to reassign them
// with the desired level of separation. We space them out, then we're
// done.
// However: if we find more than 20 tasks, we don't have enough room to
// distribute them. We'll widen our search and look in a bigger range,
// maybe [13, 53]. This range has more space, so if we find fewer than
// 40 tasks in this range we can spread them out. If we still find too
// many tasks, we keep widening the search.
$base = $task->getSubpriority();
$scale = 4.0;
while (true) {
$range = ($spacing * $scale) / 2.0;
$min = ($base - $range);
$max = ($base + $range);
$result = queryfx_one(
$conn,
'SELECT COUNT(*) N FROM %T WHERE priority = %d AND
subpriority BETWEEN %f AND %f',
$task->getTableName(),
$task->getPriority(),
$min,
$max);
$count = $result['N'];
if ($count < $scale) {
// We have found a block which we can make sparse enough, so bail and
// continue below with our selection.
break;
}
// This block had too many tasks for its size, so try again with a
// bigger block.
$scale *= 2.0;
}
$rows = queryfx_all(
$conn,
'SELECT id FROM %T WHERE priority = %d AND
subpriority BETWEEN %f AND %f
ORDER BY priority, subpriority, id',
$task->getTableName(),
$task->getPriority(),
$min,
$max);
$task_id = $task->getID();
$result = null;
// NOTE: In strict mode (which we encourage enabling) we can't structure
// this bulk update as an "INSERT ... ON DUPLICATE KEY UPDATE" unless we
// provide default values for ALL of the columns that don't have defaults.
// This is gross, but we may be moving enough rows that individual
// queries are unreasonably slow. An alternate construction which might
// be worth evaluating is to use "CASE". Another approach is to disable
// strict mode for this query.
$extra_columns = array(
'phid' => '""',
'authorPHID' => '""',
'status' => '""',
'priority' => 0,
'title' => '""',
'originalTitle' => '""',
'description' => '""',
'dateCreated' => 0,
'dateModified' => 0,
'mailKey' => '""',
'viewPolicy' => '""',
'editPolicy' => '""',
'ownerOrdering' => '""',
'spacePHID' => '""',
'bridgedObjectPHID' => '""',
'properties' => '""',
'points' => 0,
'subtype' => '""',
);
$defaults = implode(', ', $extra_columns);
$sql = array();
$offset = 0;
// Often, we'll have more room than we need in the range. Distribute the
// tasks evenly over the whole range so that we're less likely to end up
// with tasks spaced exactly the minimum distance apart, which may
// get shifted again later. We have one fewer space to distribute than we
// have tasks.
$divisor = (double)(count($rows) - 1.0);
if ($divisor > 0) {
$available_distance = (($max - $min) / $divisor);
} else {
$available_distance = 0.0;
}
foreach ($rows as $row) {
$subpriority = $min + ($offset * $available_distance);
// If this is the task that we're spreading out relative to, keep track
// of where it is ending up so we can return the new subpriority.
$id = $row['id'];
if ($id == $task_id) {
$result = $subpriority;
}
$sql[] = qsprintf(
$conn,
'(%d, %Q, %f)',
$id,
$defaults,
$subpriority);
$offset++;
}
foreach (PhabricatorLiskDAO::chunkSQL($sql) as $chunk) {
queryfx(
$conn,
'INSERT INTO %T (id, %Q, subpriority) VALUES %Q
ON DUPLICATE KEY UPDATE subpriority = VALUES(subpriority)',
$task->getTableName(),
implode(', ', array_keys($extra_columns)),
$chunk);
}
return $result;
}
protected function validateAllTransactions(
PhabricatorLiskDAO $object,
array $xactions) {

View file

@ -17,8 +17,6 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
private $dateCreatedBefore;
private $dateModifiedAfter;
private $dateModifiedBefore;
private $subpriorityMin;
private $subpriorityMax;
private $bridgedObjectPHIDs;
private $hasOpenParents;
private $hasOpenSubtasks;
@ -112,12 +110,6 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
return $this;
}
public function withSubpriorityBetween($min, $max) {
$this->subpriorityMin = $min;
$this->subpriorityMax = $max;
return $this;
}
public function withSubscribers(array $subscribers) {
$this->subscriberPHIDs = $subscribers;
return $this;
@ -408,20 +400,6 @@ final class ManiphestTaskQuery extends PhabricatorCursorPagedPolicyAwareQuery {
$this->subpriorities);
}
if ($this->subpriorityMin !== null) {
$where[] = qsprintf(
$conn,
'task.subpriority >= %f',
$this->subpriorityMin);
}
if ($this->subpriorityMax !== null) {
$where[] = qsprintf(
$conn,
'task.subpriority <= %f',
$this->subpriorityMax);
}
if ($this->bridgedObjectPHIDs !== null) {
$where[] = qsprintf(
$conn,

View file

@ -540,6 +540,11 @@ final class ManiphestTask extends ManiphestDAO
);
}
public function newSubtypeObject() {
$subtype_key = $this->getEditEngineSubtype();
$subtype_map = $this->newEditEngineSubtypeMap();
return idx($subtype_map, $subtype_key);
}
/* -( PhabricatorFulltextInterface )--------------------------------------- */

View file

@ -211,4 +211,14 @@ final class ManiphestTransaction
return parent::getNoEffectDescription();
}
public function renderSubtypeName($value) {
$object = $this->getObject();
$map = $object->newEditEngineSubtypeMap();
if (!isset($map[$value])) {
return $value;
}
return $map[$value]->getName();
}
}

View file

@ -32,6 +32,7 @@ final class ManiphestTaskSubtypeDatasource
$result = id(new PhabricatorTypeaheadResult())
->setIcon($subtype->getIcon())
->setColor($subtype->getColor())
->setPHID($key)
->setName($subtype->getName());

View file

@ -56,6 +56,9 @@ final class ManiphestTaskListView extends ManiphestView {
Javelin::initBehavior('maniphest-list-editor');
}
$subtype_map = id(new ManiphestTask())
->newEditEngineSubtypeMap();
foreach ($this->tasks as $task) {
$item = id(new PHUIObjectItemView())
->setUser($this->getUser())
@ -94,6 +97,13 @@ final class ManiphestTaskListView extends ManiphestView {
$item->addSigil('maniphest-task');
}
$subtype = $task->newSubtypeObject();
if ($subtype && $subtype->hasTagView()) {
$subtype_tag = $subtype->newTagView()
->setSlimShady(true);
$item->addAttribute($subtype_tag);
}
$project_handles = array_select_keys(
$handles,
array_reverse($task->getProjectPHIDs()));

View file

@ -50,6 +50,32 @@ final class ManiphestTaskPointsTransaction
}
}
public function getTitleForFeed() {
$old = $this->getOldValue();
$new = $this->getNewValue();
if ($old === null) {
return pht(
'%s set the point value for %s to %s.',
$this->renderAuthor(),
$this->renderObject(),
$this->renderNewValue());
} else if ($new === null) {
return pht(
'%s removed the point value for %s.',
$this->renderAuthor(),
$this->renderObject());
} else {
return pht(
'%s changed the point value for %s from %s to %s.',
$this->renderAuthor(),
$this->renderObject(),
$this->renderOldValue(),
$this->renderNewValue());
}
}
public function validateTransactions($object, array $xactions) {
$errors = array();

View file

@ -19,8 +19,8 @@ final class ManiphestTaskTitleTransaction
public function getActionName() {
$old = $this->getOldValue();
$new = $this->getNewValue();
if ($old === null) {
if (!strlen($old)) {
return pht('Created');
}
@ -29,7 +29,8 @@ final class ManiphestTaskTitleTransaction
public function getTitle() {
$old = $this->getOldValue();
if ($old === null) {
if (!strlen($old)) {
return pht(
'%s created this task.',
$this->renderAuthor());

View file

@ -3,14 +3,4 @@
abstract class ManiphestTaskTransactionType
extends PhabricatorModularTransactionType {
public function renderSubtypeName($value) {
$object = $this->getObject();
$map = $object->newEditEngineSubtypeMap();
if (!isset($map[$value])) {
return $value;
}
return $map[$value]->getName();
}
}

View file

@ -218,20 +218,39 @@ final class PhabricatorAppSearchEngine
$configure = id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-gears')
->setHref('/applications/view/'.get_class($application).'/')
->setText(pht('Configure'))
->setColor(PHUIButtonView::GREY);
$name = $application->getName();
if ($application->isPrototype()) {
$name = $name.' '.pht('(Prototype)');
}
$item = id(new PHUIObjectItemView())
->setHeader($name)
->setImageIcon($icon)
->setSubhead($description)
->setLaunchButton($configure);
->setSideColumn($configure);
if (!$application->isFirstParty()) {
$tag = id(new PHUITagView())
->setName(pht('Extension'))
->setIcon('fa-puzzle-piece')
->setColor(PHUITagView::COLOR_BLUE)
->setType(PHUITagView::TYPE_SHADE)
->setSlimShady(true);
$item->addAttribute($tag);
}
if ($application->isPrototype()) {
$prototype_tag = id(new PHUITagView())
->setName(pht('Prototype'))
->setIcon('fa-exclamation-circle')
->setColor(PHUITagView::COLOR_ORANGE)
->setType(PHUITagView::TYPE_SHADE)
->setSlimShady(true);
$item->addAttribute($prototype_tag);
}
$item->addAttribute($description);
if ($application->getBaseURI() && $application->isInstalled()) {
$item->setHref($application->getBaseURI());
@ -242,10 +261,6 @@ final class PhabricatorAppSearchEngine
$item->setDisabled(true);
}
if (!$application->isFirstParty()) {
$item->addAttribute(pht('Extension'));
}
$list->addItem($item);
}

View file

@ -51,6 +51,9 @@ final class PhabricatorNuanceApplication extends PhabricatorApplication {
$this->getQueryRoutePattern() => 'NuanceQueueListController',
$this->getEditRoutePattern('edit/') => 'NuanceQueueEditController',
'view/(?P<id>[1-9]\d*)/' => 'NuanceQueueViewController',
'work/(?P<id>[1-9]\d*)/' => 'NuanceQueueWorkController',
'action/(?P<queueID>[1-9]\d*)/(?P<action>[^/]+)/(?P<id>[1-9]\d*)/'
=> 'NuanceItemActionController',
),
),
'/action/' => array(

View file

@ -0,0 +1,113 @@
<?php
abstract class NuanceCommandImplementation
extends Phobject {
private $actor;
private $transactionQueue = array();
final public function setActor(PhabricatorUser $actor) {
$this->actor = $actor;
return $this;
}
final public function getActor() {
return $this->actor;
}
abstract public function getCommandName();
abstract public function canApplyToItem(NuanceItem $item);
public function canApplyImmediately(
NuanceItem $item,
NuanceItemCommand $command) {
return false;
}
abstract protected function executeCommand(
NuanceItem $item,
NuanceItemCommand $command);
final public function applyCommand(
NuanceItem $item,
NuanceItemCommand $command) {
$command_key = $command->getCommand();
$implementation_key = $this->getCommandKey();
if ($command_key !== $implementation_key) {
throw new Exception(
pht(
'This command implementation("%s") can not apply a command of a '.
'different type ("%s").',
$implementation_key,
$command_key));
}
if (!$this->canApplyToItem($item)) {
throw new Exception(
pht(
'This command implementation ("%s") can not be applied to an '.
'item of type "%s".',
$implementation_key,
$item->getItemType()));
}
$this->transactionQueue = array();
$command_type = NuanceItemCommandTransaction::TRANSACTIONTYPE;
$command_xaction = $this->newTransaction($command_type);
$result = $this->executeCommand($item, $command);
$xactions = $this->transactionQueue;
$this->transactionQueue = array();
$command_xaction->setNewValue(
array(
'command' => $command->getCommand(),
'parameters' => $command->getParameters(),
'result' => $result,
));
// TODO: Maybe preserve the actor's original content source?
$source = PhabricatorContentSource::newForSource(
PhabricatorDaemonContentSource::SOURCECONST);
$actor = $this->getActor();
id(new NuanceItemEditor())
->setActor($actor)
->setActingAsPHID($command->getAuthorPHID())
->setContentSource($source)
->setContinueOnMissingFields(true)
->setContinueOnNoEffect(true)
->applyTransactions($item, $xactions);
}
final public function getCommandKey() {
return $this->getPhobjectClassConstant('COMMANDKEY');
}
final public static function getAllCommands() {
return id(new PhutilClassMapQuery())
->setAncestorClass(__CLASS__)
->setUniqueMethod('getCommandKey')
->execute();
}
protected function newTransaction($type) {
$xaction = id(new NuanceItemTransaction())
->setTransactionType($type);
$this->transactionQueue[] = $xaction;
return $xaction;
}
protected function newStatusTransaction($status) {
return $this->newTransaction(NuanceItemStatusTransaction::TRANSACTIONTYPE)
->setNewValue($status);
}
}

View file

@ -0,0 +1,37 @@
<?php
final class NuanceItemCommandSpec
extends Phobject {
private $commandKey;
private $name;
private $icon;
public function setCommandKey($command_key) {
$this->commandKey = $command_key;
return $this;
}
public function getCommandKey() {
return $this->commandKey;
}
public function setName($name) {
$this->name = $name;
return $this;
}
public function getName() {
return $this->name;
}
public function setIcon($icon) {
$this->icon = $icon;
return $this;
}
public function getIcon() {
return $this->icon;
}
}

View file

@ -0,0 +1,29 @@
<?php
final class NuanceTrashCommand
extends NuanceCommandImplementation {
const COMMANDKEY = 'trash';
public function getCommandName() {
return pht('Throw in Trash');
}
public function canApplyToItem(NuanceItem $item) {
$type = $item->getImplementation();
return ($type instanceof NuanceFormItemType);
}
public function canApplyImmediately(
NuanceItem $item,
NuanceItemCommand $command) {
return true;
}
protected function executeCommand(
NuanceItem $item,
NuanceItemCommand $command) {
$this->newStatusTransaction(NuanceItem::STATUS_CLOSED);
}
}

View file

@ -6,6 +6,14 @@ final class NuanceItemActionController extends NuanceController {
$viewer = $this->getViewer();
$id = $request->getURIData('id');
if (!$request->validateCSRF()) {
return new Aphront400Response();
}
// NOTE: This controller can be reached from an individual item (usually
// by a user) or while working through a queue (usually by staff). When
// a command originates from a queue, the URI will have a queue ID.
$item = id(new NuanceItemQuery())
->setViewer($viewer)
->withIDs(array($id))
@ -14,13 +22,100 @@ final class NuanceItemActionController extends NuanceController {
return new Aphront404Response();
}
$cancel_uri = $item->getURI();
$queue_id = $request->getURIData('queueID');
$queue = null;
if ($queue_id) {
$queue = id(new NuanceQueueQuery())
->setViewer($viewer)
->withIDs(array($queue_id))
->executeOne();
if (!$queue) {
return new Aphront404Response();
}
$item_queue = $item->getQueue();
if (!$item_queue || ($item_queue->getPHID() != $queue->getPHID())) {
return $this->newDialog()
->setTitle(pht('Wrong Queue'))
->appendParagraph(
pht(
'You are trying to act on this item from the wrong queue: it '.
'is currently in a different queue.'))
->addCancelButton($cancel_uri);
}
}
$action = $request->getURIData('action');
$impl = $item->getImplementation();
$impl->setViewer($viewer);
$impl->setController($this);
return $impl->buildActionResponse($item, $action);
$executors = NuanceCommandImplementation::getAllCommands();
$executor = idx($executors, $action);
if (!$executor) {
return new Aphront404Response();
}
$executor = id(clone $executor)
->setActor($viewer);
if (!$executor->canApplyToItem($item)) {
return $this->newDialog()
->setTitle(pht('Command Not Supported'))
->appendParagraph(
pht(
'This item does not support the specified command ("%s").',
$action))
->addCancelButton($cancel_uri);
}
$command = NuanceItemCommand::initializeNewCommand()
->setItemPHID($item->getPHID())
->setAuthorPHID($viewer->getPHID())
->setCommand($action);
if ($queue) {
$command->setQueuePHID($queue->getPHID());
}
$command->save();
// If this command can be applied immediately, try to apply it now.
// In most cases, local commands (like closing an item) can be applied
// immediately.
// Commands that require making a call to a remote system (for example,
// to reply to a tweet or close a remote object) are usually done in the
// background so the user doesn't have to wait for the operation to
// complete before they can continue work.
$did_apply = false;
$immediate = $executor->canApplyImmediately($item, $command);
if ($immediate) {
// TODO: Move this stuff to a new Engine, and have the controller and
// worker both call into the Engine.
$worker = new NuanceItemUpdateWorker(array());
$did_apply = $worker->executeCommands($item, array($command));
}
// If this can't be applied immediately or we were unable to get a lock
// fast enough, do the update in the background instead.
if (!$did_apply) {
$item->scheduleUpdate();
}
if ($queue) {
$done_uri = $queue->getWorkURI();
} else {
$done_uri = $item->getURI();
}
return id(new AphrontRedirectResponse())
->setURI($done_uri);
}
}

View file

@ -26,14 +26,12 @@ final class NuanceItemViewController extends NuanceController {
$curtain = $this->buildCurtain($item);
$content = $this->buildContent($item);
$commands = $this->buildCommands($item);
$timeline = $this->buildTransactionTimeline(
$item,
new NuanceItemTransactionQuery());
$main = array(
$commands,
$content,
$timeline,
);
@ -91,36 +89,4 @@ final class NuanceItemViewController extends NuanceController {
return $impl->buildItemView($item);
}
private function buildCommands(NuanceItem $item) {
$viewer = $this->getViewer();
$commands = id(new NuanceItemCommandQuery())
->setViewer($viewer)
->withItemPHIDs(array($item->getPHID()))
->execute();
$commands = msort($commands, 'getID');
if (!$commands) {
return null;
}
$rows = array();
foreach ($commands as $command) {
$rows[] = array(
$command->getCommand(),
);
}
$table = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('Command'),
));
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Pending Commands'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($table);
}
}

View file

@ -70,6 +70,14 @@ final class NuanceQueueViewController
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Begin Work'))
->setIcon('fa-play-circle-o')
->setHref($this->getApplicationURI("queue/work/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
return $curtain;
}

View file

@ -0,0 +1,186 @@
<?php
final class NuanceQueueWorkController
extends NuanceQueueController {
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$queue = id(new NuanceQueueQuery())
->setViewer($viewer)
->withIDs(array($request->getURIData('id')))
->executeOne();
if (!$queue) {
return new Aphront404Response();
}
$title = $queue->getName();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Queues'), $this->getApplicationURI('queue/'));
$crumbs->addTextCrumb($queue->getName(), $queue->getURI());
$crumbs->addTextCrumb(pht('Work'));
$crumbs->setBorder(true);
// For now, just pick the first open item.
$items = id(new NuanceItemQuery())
->setViewer($viewer)
->withQueuePHIDs(
array(
$queue->getPHID(),
))
->withStatuses(
array(
NuanceItem::STATUS_OPEN,
))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->setLimit(5)
->execute();
if (!$items) {
return $this->newDialog()
->setTitle(pht('Queue Empty'))
->appendParagraph(
pht(
'This queue has no open items which you have permission to '.
'work on.'))
->addCancelButton($queue->getURI());
}
$item = head($items);
$curtain = $this->buildCurtain($queue, $item);
$timeline = $this->buildTransactionTimeline(
$item,
new NuanceItemTransactionQuery());
$timeline->setShouldTerminate(true);
$impl = $item->getImplementation()
->setViewer($viewer);
$commands = $this->buildCommands($item);
$work_content = $impl->buildItemWorkView($item);
$view = id(new PHUITwoColumnView())
->setCurtain($curtain)
->setMainColumn(
array(
$commands,
$work_content,
$timeline,
));
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->appendChild($view);
}
private function buildCurtain(NuanceQueue $queue, NuanceItem $item) {
$viewer = $this->getViewer();
$id = $queue->getID();
$curtain = $this->newCurtainView();
$impl = $item->getImplementation();
$commands = $impl->buildWorkCommands($item);
foreach ($commands as $command) {
$command_key = $command->getCommandKey();
$item_id = $item->getID();
$action_uri = "queue/action/{$id}/{$command_key}/{$item_id}/";
$action_uri = $this->getApplicationURI($action_uri);
$curtain->addAction(
id(new PhabricatorActionView())
->setName($command->getName())
->setIcon($command->getIcon())
->setHref($action_uri)
->setWorkflow(true));
}
$curtain->addAction(
id(new PhabricatorActionView())
->setType(PhabricatorActionView::TYPE_DIVIDER));
$curtain->addAction(
id(new PhabricatorActionView())
->setType(PhabricatorActionView::TYPE_LABEL)
->setName(pht('Queue Actions')));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Manage Queue'))
->setIcon('fa-cog')
->setHref($this->getApplicationURI("queue/view/{$id}/")));
return $curtain;
}
private function buildCommands(NuanceItem $item) {
$viewer = $this->getViewer();
$commands = id(new NuanceItemCommandQuery())
->setViewer($viewer)
->withItemPHIDs(array($item->getPHID()))
->withStatuses(
array(
NuanceItemCommand::STATUS_ISSUED,
NuanceItemCommand::STATUS_EXECUTING,
NuanceItemCommand::STATUS_FAILED,
))
->execute();
$commands = msort($commands, 'getID');
if (!$commands) {
return null;
}
$rows = array();
foreach ($commands as $command) {
$icon = $command->getStatusIcon();
$color = $command->getStatusColor();
$rows[] = array(
$command->getID(),
id(new PHUIIconView())
->setIcon($icon, $color),
$viewer->renderHandle($command->getAuthorPHID()),
$command->getCommand(),
phabricator_datetime($command->getDateCreated(), $viewer),
);
}
$table = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('ID'),
null,
pht('Actor'),
pht('Command'),
pht('Date'),
))
->setColumnClasses(
array(
null,
'icon',
null,
'pri',
'wide right',
));
return id(new PHUIObjectBoxView())
->setHeaderText(pht('Pending Commands'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setTable($table);
}
}

View file

@ -17,10 +17,9 @@ final class NuanceGitHubIssuesImportCursor
$container_key = null;
return NuanceItem::initializeNewItem()
return NuanceItem::initializeNewItem(NuanceGitHubEventItemType::ITEMTYPE)
->setStatus(NuanceItem::STATUS_IMPORTING)
->setSourcePHID($source->getPHID())
->setItemType(NuanceGitHubEventItemType::ITEMTYPE)
->setItemKey($item_key)
->setItemContainerKey($container_key)
->setItemProperty('api.type', 'issue')

View file

@ -36,10 +36,9 @@ final class NuanceGitHubRepositoryImportCursor
$container_key = "github.issue.{$issue_id}";
}
return NuanceItem::initializeNewItem()
return NuanceItem::initializeNewItem(NuanceGitHubEventItemType::ITEMTYPE)
->setStatus(NuanceItem::STATUS_IMPORTING)
->setSourcePHID($source->getPHID())
->setItemType(NuanceGitHubEventItemType::ITEMTYPE)
->setItemKey($item_key)
->setItemContainerKey($container_key)
->setItemProperty('api.type', 'repository')

View file

@ -14,104 +14,10 @@ final class NuanceItemEditor
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = NuanceItemTransaction::TYPE_OWNER;
$types[] = NuanceItemTransaction::TYPE_SOURCE;
$types[] = NuanceItemTransaction::TYPE_REQUESTOR;
$types[] = NuanceItemTransaction::TYPE_PROPERTY;
$types[] = NuanceItemTransaction::TYPE_QUEUE;
$types[] = NuanceItemTransaction::TYPE_COMMAND;
$types[] = PhabricatorTransactions::TYPE_EDGE;
$types[] = PhabricatorTransactions::TYPE_COMMENT;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
return $types;
}
protected function getCustomTransactionOldValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case NuanceItemTransaction::TYPE_REQUESTOR:
return $object->getRequestorPHID();
case NuanceItemTransaction::TYPE_SOURCE:
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);
return $object->getNuanceProperty($key);
case NuanceItemTransaction::TYPE_COMMAND:
return null;
}
return parent::getCustomTransactionOldValue($object, $xaction);
}
protected function getCustomTransactionNewValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case NuanceItemTransaction::TYPE_REQUESTOR:
case NuanceItemTransaction::TYPE_SOURCE:
case NuanceItemTransaction::TYPE_OWNER:
case NuanceItemTransaction::TYPE_PROPERTY:
case NuanceItemTransaction::TYPE_QUEUE:
case NuanceItemTransaction::TYPE_COMMAND:
return $xaction->getNewValue();
}
return parent::getCustomTransactionNewValue($object, $xaction);
}
protected function applyCustomInternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case NuanceItemTransaction::TYPE_REQUESTOR:
$object->setRequestorPHID($xaction->getNewValue());
break;
case NuanceItemTransaction::TYPE_SOURCE:
$object->setSourcePHID($xaction->getNewValue());
break;
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);
$object->setNuanceProperty($key, $xaction->getNewValue());
break;
case NuanceItemTransaction::TYPE_COMMAND:
break;
}
}
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case NuanceItemTransaction::TYPE_REQUESTOR:
case NuanceItemTransaction::TYPE_SOURCE:
case NuanceItemTransaction::TYPE_OWNER:
case NuanceItemTransaction::TYPE_PROPERTY:
case NuanceItemTransaction::TYPE_QUEUE:
case NuanceItemTransaction::TYPE_COMMAND:
return;
}
return parent::applyCustomExternalTransaction($object, $xaction);
}
}

View file

@ -75,7 +75,7 @@ final class NuanceQueueEditEngine
->setKey('name')
->setLabel(pht('Name'))
->setDescription(pht('Name of the queue.'))
->setTransactionType(NuanceQueueTransaction::TYPE_NAME)
->setTransactionType(NuanceQueueNameTransaction::TRANSACTIONTYPE)
->setIsRequired(true)
->setValue($object->getName()),
);

View file

@ -14,89 +14,10 @@ final class NuanceQueueEditor
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = NuanceQueueTransaction::TYPE_NAME;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
return $types;
}
protected function getCustomTransactionOldValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case NuanceQueueTransaction::TYPE_NAME:
return $object->getName();
}
return parent::getCustomTransactionOldValue($object, $xaction);
}
protected function getCustomTransactionNewValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case NuanceQueueTransaction::TYPE_NAME:
return $xaction->getNewValue();
}
return parent::getCustomTransactionNewValue($object, $xaction);
}
protected function applyCustomInternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case NuanceQueueTransaction::TYPE_NAME:
$object->setName($xaction->getNewValue());
break;
}
}
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case NuanceQueueTransaction::TYPE_NAME:
return;
}
return parent::applyCustomExternalTransaction($object, $xaction);
}
protected function validateTransaction(
PhabricatorLiskDAO $object,
$type,
array $xactions) {
$errors = parent::validateTransaction($object, $type, $xactions);
switch ($type) {
case NuanceQueueTransaction::TYPE_NAME:
$missing = $this->validateIsEmptyTextField(
$object->getName(),
$xactions);
if ($missing) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Required'),
pht('A queue must have a name.'),
nonempty(last($xactions), null));
$error->setIsMissingFieldError(true);
$errors[] = $error;
}
break;
}
return $errors;
}
}

View file

@ -96,14 +96,15 @@ final class NuanceSourceEditEngine
->setKey('name')
->setLabel(pht('Name'))
->setDescription(pht('Name of the source.'))
->setTransactionType(NuanceSourceTransaction::TYPE_NAME)
->setTransactionType(NuanceSourceNameTransaction::TRANSACTIONTYPE)
->setIsRequired(true)
->setValue($object->getName()),
id(new PhabricatorDatasourceEditField())
->setKey('defaultQueue')
->setLabel(pht('Default Queue'))
->setDescription(pht('Default queue.'))
->setTransactionType(NuanceSourceTransaction::TYPE_DEFAULT_QUEUE)
->setTransactionType(
NuanceSourceDefaultQueueTransaction::TRANSACTIONTYPE)
->setDatasource(new NuanceQueueDatasource())
->setSingleValue($object->getDefaultQueuePHID()),
);

View file

@ -18,111 +18,10 @@ final class NuanceSourceEditor
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = NuanceSourceTransaction::TYPE_NAME;
$types[] = NuanceSourceTransaction::TYPE_DEFAULT_QUEUE;
$types[] = PhabricatorTransactions::TYPE_EDGE;
$types[] = PhabricatorTransactions::TYPE_COMMENT;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
return $types;
}
protected function getCustomTransactionOldValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case NuanceSourceTransaction::TYPE_NAME:
return $object->getName();
case NuanceSourceTransaction::TYPE_DEFAULT_QUEUE:
return $object->getDefaultQueuePHID();
}
return parent::getCustomTransactionOldValue($object, $xaction);
}
protected function getCustomTransactionNewValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case NuanceSourceTransaction::TYPE_NAME:
case NuanceSourceTransaction::TYPE_DEFAULT_QUEUE:
return $xaction->getNewValue();
}
return parent::getCustomTransactionNewValue($object, $xaction);
}
protected function applyCustomInternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case NuanceSourceTransaction::TYPE_NAME:
$object->setName($xaction->getNewValue());
break;
case NuanceSourceTransaction::TYPE_DEFAULT_QUEUE:
$object->setDefaultQueuePHID($xaction->getNewValue());
break;
}
}
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case NuanceSourceTransaction::TYPE_NAME:
case NuanceSourceTransaction::TYPE_DEFAULT_QUEUE:
return;
}
return parent::applyCustomExternalTransaction($object, $xaction);
}
protected function validateTransaction(
PhabricatorLiskDAO $object,
$type,
array $xactions) {
$errors = parent::validateTransaction($object, $type, $xactions);
switch ($type) {
case NuanceSourceTransaction::TYPE_NAME:
$missing = $this->validateIsEmptyTextField(
$object->getName(),
$xactions);
if ($missing) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Required'),
pht('Source name is required.'),
nonempty(last($xactions), null));
$error->setIsMissingFieldError(true);
$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

@ -0,0 +1,54 @@
<?php
final class NuanceFormItemType
extends NuanceItemType {
const ITEMTYPE = 'form.item';
public function getItemTypeDisplayName() {
return pht('Form');
}
public function getItemDisplayName(NuanceItem $item) {
return pht('Complaint');
}
protected function newWorkCommands(NuanceItem $item) {
return array(
$this->newCommand('trash')
->setIcon('fa-trash')
->setName(pht('Throw In Trash')),
);
}
protected function newItemView(NuanceItem $item) {
$viewer = $this->getViewer();
$content = $item->getItemProperty('complaint');
$content_view = id(new PHUIRemarkupView($viewer, $content))
->setContextObject($item);
$content_section = id(new PHUIPropertyListView())
->addTextContent(
phutil_tag(
'div',
array(
'class' => 'phabricator-remarkup',
),
$content_view));
$content_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Complaint'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->appendChild($content_section);
return array(
$content_box,
);
}
protected function handleAction(NuanceItem $item, $action) {
return null;
}
}

View file

@ -329,6 +329,9 @@ final class NuanceGitHubEventItemType
NuanceItem $item,
NuanceItemCommand $command) {
// TODO: This code is no longer reachable, and has moved to
// CommandImplementation subclasses.
$action = $command->getCommand();
switch ($action) {
case 'sync':

View file

@ -32,6 +32,10 @@ abstract class NuanceItemType
return $this->newItemView($item);
}
final public function buildItemWorkView(NuanceItem $item) {
return $this->newItemView($item);
}
protected function newItemView(NuanceItem $item) {
return null;
}
@ -91,57 +95,15 @@ abstract class NuanceItemType
}
final public function buildActionResponse(NuanceItem $item, $action) {
$response = $this->handleAction($item, $action);
if ($response === null) {
return new Aphront404Response();
}
return $response;
return $this->handleAction($item, $action);
}
protected function handleAction(NuanceItem $item, $action) {
return null;
}
final public function applyCommand(
NuanceItem $item,
NuanceItemCommand $command) {
$result = $this->handleCommand($item, $command);
if ($result === null) {
return;
}
$xaction = id(new NuanceItemTransaction())
->setTransactionType(NuanceItemTransaction::TYPE_COMMAND)
->setNewValue(
array(
'command' => $command->getCommand(),
'parameters' => $command->getParameters(),
'result' => $result,
));
$viewer = $this->getViewer();
// TODO: Maybe preserve the actor's original content source?
$source = PhabricatorContentSource::newForSource(
PhabricatorDaemonContentSource::SOURCECONST);
$editor = id(new NuanceItemEditor())
->setActor($viewer)
->setActingAsPHID($command->getAuthorPHID())
->setContentSource($source)
->setContinueOnMissingFields(true)
->setContinueOnNoEffect(true)
->applyTransactions($item, array($xaction));
}
protected function handleCommand(
NuanceItem $item,
NuanceItemCommand $command) {
return null;
final public function buildWorkCommands(NuanceItem $item) {
return $this->newWorkCommands($item);
}
final protected function newContentSource(
@ -159,4 +121,8 @@ abstract class NuanceItemType
return id(new PhabricatorNuanceApplication())->getPHID();
}
protected function newCommand($command_key) {
return id(new NuanceItemCommandSpec())
->setCommandKey($command_key);
}
}

View file

@ -33,7 +33,7 @@ final class NuanceItemPHIDType extends PhabricatorPHIDType {
foreach ($handles as $phid => $handle) {
$item = $objects[$phid];
$handle->setName($item->getItemDisplayName());
$handle->setName($item->getDisplayName());
$handle->setURI($item->getURI());
}
}

View file

@ -5,6 +5,7 @@ final class NuanceItemCommandQuery
private $ids;
private $itemPHIDs;
private $statuses;
public function withIDs(array $ids) {
$this->ids = $ids;
@ -16,6 +17,11 @@ final class NuanceItemCommandQuery
return $this;
}
public function withStatuses(array $statuses) {
$this->statuses = $statuses;
return $this;
}
public function newResultObject() {
return new NuanceItemCommand();
}
@ -41,6 +47,13 @@ final class NuanceItemCommandQuery
$this->itemPHIDs);
}
if ($this->statuses !== null) {
$where[] = qsprintf(
$conn,
'status IN (%Ls)',
$this->statuses);
}
return $where;
}

View file

@ -6,9 +6,11 @@ final class NuanceItemQuery
private $ids;
private $phids;
private $sourcePHIDs;
private $queuePHIDs;
private $itemTypes;
private $itemKeys;
private $containerKeys;
private $statuses;
public function withIDs(array $ids) {
$this->ids = $ids;
@ -25,6 +27,11 @@ final class NuanceItemQuery
return $this;
}
public function withQueuePHIDs(array $queue_phids) {
$this->queuePHIDs = $queue_phids;
return $this;
}
public function withItemTypes(array $item_types) {
$this->itemTypes = $item_types;
return $this;
@ -35,6 +42,11 @@ final class NuanceItemQuery
return $this;
}
public function withStatuses(array $statuses) {
$this->statuses = $statuses;
return $this;
}
public function withItemContainerKeys(array $container_keys) {
$this->containerKeys = $container_keys;
return $this;
@ -49,13 +61,11 @@ final class NuanceItemQuery
}
protected function willFilterPage(array $items) {
$viewer = $this->getViewer();
$source_phids = mpull($items, 'getSourcePHID');
// 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())
->setViewer($viewer)
->withPHIDs($source_phids)
->execute();
$sources = mpull($sources, null, 'getPHID');
@ -81,6 +91,43 @@ final class NuanceItemQuery
$item->attachImplementation($type);
}
$queue_phids = array();
foreach ($items as $item) {
$queue_phid = $item->getQueuePHID();
if ($queue_phid) {
$queue_phids[$queue_phid] = $queue_phid;
}
}
if ($queue_phids) {
$queues = id(new NuanceQueueQuery())
->setViewer($viewer)
->withPHIDs($queue_phids)
->execute();
$queues = mpull($queues, null, 'getPHID');
} else {
$queues = array();
}
foreach ($items as $key => $item) {
$queue_phid = $item->getQueuePHID();
if (!$queue_phid) {
$item->attachQueue(null);
continue;
}
$queue = idx($queues, $queue_phid);
if (!$queue) {
unset($items[$key]);
$this->didRejectResult($item);
continue;
}
$item->attachQueue($queue);
}
return $items;
}
@ -94,6 +141,13 @@ final class NuanceItemQuery
$this->sourcePHIDs);
}
if ($this->queuePHIDs !== null) {
$where[] = qsprintf(
$conn,
'queuePHID IN (%Ls)',
$this->queuePHIDs);
}
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
@ -108,6 +162,13 @@ final class NuanceItemQuery
$this->phids);
}
if ($this->statuses !== null) {
$where[] = qsprintf(
$conn,
'status IN (%Ls)',
$this->statuses);
}
if ($this->itemTypes !== null) {
$where[] = qsprintf(
$conn,

View file

@ -72,6 +72,19 @@ final class NuanceItemSearchEngine
$impl->getItemTypeDisplayIcon(),
$impl->getItemTypeDisplayName());
$queue = $item->getQueue();
if ($queue) {
$view->addAttribute(
phutil_tag(
'a',
array(
'href' => $queue->getURI(),
),
$queue->getName()));
} else {
$view->addAttribute(phutil_tag('em', array(), pht('Not in Queue')));
}
$list->addItem($view);
}

View file

@ -39,6 +39,8 @@ final class NuancePhabricatorFormSourceDefinition
$content_source = PhabricatorContentSource::newFromRequest($request);
$item = $this->newItemFromProperties(
NuanceFormItemType::ITEMTYPE,
$viewer->getPHID(),
$properties,
$content_source);
@ -79,7 +81,7 @@ final class NuancePhabricatorFormSourceDefinition
NuanceItem $item,
PHUIPropertyListView $view) {
$complaint = $item->getNuanceProperty('complaint');
$complaint = $item->getItemProperty('complaint');
$complaint = new PHUIRemarkupView($viewer, $complaint);
$view->addSectionHeader(
pht('Complaint'), 'fa-exclamation-circle');

View file

@ -149,6 +149,8 @@ abstract class NuanceSourceDefinition extends Phobject {
}
protected function newItemFromProperties(
$item_type,
$author_phid,
array $properties,
PhabricatorContentSource $content_source) {
@ -157,29 +159,31 @@ abstract class NuanceSourceDefinition extends Phobject {
$actor = PhabricatorUser::getOmnipotentUser();
$source = $this->getSource();
$item = NuanceItem::initializeNewItem();
$item = NuanceItem::initializeNewItem($item_type);
$xactions = array();
$xactions[] = id(new NuanceItemTransaction())
->setTransactionType(NuanceItemTransaction::TYPE_SOURCE)
->setTransactionType(NuanceItemSourceTransaction::TRANSACTIONTYPE)
->setNewValue($source->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)
->setTransactionType(NuanceItemQueueTransaction::TRANSACTIONTYPE)
->setNewValue($source->getDefaultQueuePHID());
// TODO: Maybe this should all be modular transactions now?
foreach ($properties as $key => $property) {
$xactions[] = id(new NuanceItemTransaction())
->setTransactionType(NuanceItemTransaction::TYPE_PROPERTY)
->setTransactionType(NuanceItemPropertyTransaction::TRANSACTIONTYPE)
->setMetadataValue(NuanceItemTransaction::PROPERTY_KEY, $key)
->setNewValue($property);
}
$editor = id(new NuanceItemEditor())
->setActor($actor)
->setActingAsPHID($author_phid)
->setContentSource($content_source);
$editor->applyTransactions($item, $xactions);

View file

@ -9,7 +9,6 @@ final class NuanceItem
const STATUS_IMPORTING = 'importing';
const STATUS_ROUTING = 'routing';
const STATUS_OPEN = 'open';
const STATUS_ASSIGNED = 'assigned';
const STATUS_CLOSED = 'closed';
protected $status;
@ -23,11 +22,16 @@ final class NuanceItem
protected $data = array();
protected $mailKey;
private $queue = self::ATTACHABLE;
private $source = self::ATTACHABLE;
private $implementation = self::ATTACHABLE;
public static function initializeNewItem() {
public static function initializeNewItem($item_type) {
// TODO: Validate that the type is valid, and construct and attach it.
return id(new NuanceItem())
->setItemType($item_type)
->setStatus(self::STATUS_OPEN);
}
@ -42,7 +46,7 @@ final class NuanceItem
'requestorPHID' => 'phid?',
'queuePHID' => 'phid?',
'itemType' => 'text64',
'itemKey' => 'text64',
'itemKey' => 'text64?',
'itemContainerKey' => 'text64?',
'status' => 'text32',
'mailKey' => 'bytes20',
@ -172,6 +176,15 @@ final class NuanceItem
return $this;
}
public function getQueue() {
return $this->assertAttached($this->queue);
}
public function attachQueue(NuanceQueue $queue = null) {
$this->queue = $queue;
return $this;
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */

View file

@ -4,32 +4,86 @@ final class NuanceItemCommand
extends NuanceDAO
implements PhabricatorPolicyInterface {
const STATUS_ISSUED = 'issued';
const STATUS_EXECUTING = 'executing';
const STATUS_DONE = 'done';
const STATUS_FAILED = 'failed';
protected $itemPHID;
protected $authorPHID;
protected $queuePHID;
protected $command;
protected $parameters;
protected $status;
protected $parameters = array();
public static function initializeNewCommand() {
return new self();
return id(new self())
->setStatus(self::STATUS_ISSUED);
}
protected function getConfiguration() {
return array(
self::CONFIG_TIMESTAMPS => false,
self::CONFIG_SERIALIZATION => array(
'parameters' => self::SERIALIZATION_JSON,
),
self::CONFIG_COLUMN_SCHEMA => array(
'command' => 'text64',
'status' => 'text64',
'queuePHID' => 'phid?',
),
self::CONFIG_KEY_SCHEMA => array(
'key_item' => array(
'columns' => array('itemPHID'),
'key_pending' => array(
'columns' => array('itemPHID', 'status'),
),
),
) + parent::getConfiguration();
}
public static function getStatusMap() {
return array(
self::STATUS_ISSUED => array(
'name' => pht('Issued'),
'icon' => 'fa-clock-o',
'color' => 'bluegrey',
),
self::STATUS_EXECUTING => array(
'name' => pht('Executing'),
'icon' => 'fa-play',
'color' => 'green',
),
self::STATUS_DONE => array(
'name' => pht('Done'),
'icon' => 'fa-check',
'color' => 'blue',
),
self::STATUS_FAILED => array(
'name' => pht('Failed'),
'icon' => 'fa-times',
'color' => 'red',
),
);
}
private function getStatusSpec() {
$map = self::getStatusMap();
return idx($map, $this->getStatus(), array());
}
public function getStatusIcon() {
$spec = $this->getStatusSpec();
return idx($spec, 'icon', 'fa-question');
}
public function getStatusColor() {
$spec = $this->getStatusSpec();
return idx($spec, 'color', 'indigo');
}
public function getStatusName() {
$spec = $this->getStatusSpec();
return idx($spec, 'name', $this->getStatus());
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */

View file

@ -5,13 +5,6 @@ final class NuanceItemTransaction
const PROPERTY_KEY = 'property.key';
const TYPE_OWNER = 'nuance.item.owner';
const TYPE_REQUESTOR = 'nuance.item.requestor';
const TYPE_SOURCE = 'nuance.item.source';
const TYPE_PROPERTY = 'nuance.item.property';
const TYPE_QUEUE = 'nuance.item.queue';
const TYPE_COMMAND = 'nuance.item.command';
public function getApplicationTransactionType() {
return NuanceItemPHIDType::TYPECONST;
}
@ -20,61 +13,8 @@ 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));
case self::TYPE_COMMAND:
// TODO: Give item types a chance to render this properly.
return pht(
'%s applied command "%s" to this item.',
$this->renderHandleLink($author_phid),
idx($new, 'command'));
}
return parent::getTitle();
public function getBaseTransactionClass() {
return 'NuanceItemTransactionType';
}
}

View file

@ -43,6 +43,10 @@ final class NuanceQueue
return '/nuance/queue/view/'.$this->getID().'/';
}
public function getWorkURI() {
return '/nuance/queue/work/'.$this->getID().'/';
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */

View file

@ -2,8 +2,6 @@
final class NuanceQueueTransaction extends NuanceTransaction {
const TYPE_NAME = 'nuance.queue.name';
public function getApplicationTransactionType() {
return NuanceQueuePHIDType::TYPECONST;
}
@ -12,26 +10,8 @@ final class NuanceQueueTransaction extends NuanceTransaction {
return new NuanceQueueTransactionComment();
}
public function getTitle() {
$old = $this->getOldValue();
$new = $this->getNewValue();
$type = $this->getTransactionType();
$author_phid = $this->getAuthorPHID();
switch ($type) {
case PhabricatorTransactions::TYPE_CREATE:
return pht(
'%s created this queue.',
$this->renderHandleLink($author_phid));
case self::TYPE_NAME:
return pht(
'%s renamed this queue from "%s" to "%s".',
$this->renderHandleLink($author_phid),
$old,
$new);
}
return parent::getTitle();
public function getBaseTransactionClass() {
return 'NuanceQueueTransactionType';
}
}

View file

@ -3,9 +3,6 @@
final class NuanceSourceTransaction
extends NuanceTransaction {
const TYPE_NAME = 'source.name';
const TYPE_DEFAULT_QUEUE = 'source.queue.default';
public function getApplicationTransactionType() {
return NuanceSourcePHIDType::TYPECONST;
}
@ -14,63 +11,8 @@ final class NuanceSourceTransaction
return new NuanceSourceTransactionComment();
}
public function shouldHide() {
$old = $this->getOldValue();
$new = $this->getNewValue();
$type = $this->getTransactionType();
switch ($type) {
case self::TYPE_DEFAULT_QUEUE:
return !$old;
case self::TYPE_NAME:
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();
public function getBaseTransactionClass() {
return 'NuanceSourceTransactionType';
}
}

View file

@ -1,7 +1,7 @@
<?php
abstract class NuanceTransaction
extends PhabricatorApplicationTransaction {
extends PhabricatorModularTransaction {
public function getApplicationName() {
return 'nuance';

View file

@ -6,9 +6,7 @@ final class NuanceItemUpdateWorker
protected function doWork() {
$item_phid = $this->getTaskDataValue('itemPHID');
$hash = PhabricatorHash::digestForIndex($item_phid);
$lock_key = "nuance.item.{$hash}";
$lock = PhabricatorGlobalLock::newLock($lock_key);
$lock = $this->newLock($item_phid);
$lock->lock(1);
try {
@ -55,19 +53,109 @@ final class NuanceItemUpdateWorker
private function applyCommands(NuanceItem $item) {
$viewer = $this->getViewer();
$impl = $item->getImplementation();
$impl->setViewer($viewer);
$commands = id(new NuanceItemCommandQuery())
->setViewer($viewer)
->withItemPHIDs(array($item->getPHID()))
->withStatuses(
array(
NuanceItemCommand::STATUS_ISSUED,
))
->execute();
$commands = msort($commands, 'getID');
$this->executeCommandList($item, $commands);
}
public function executeCommands(NuanceItem $item, array $commands) {
if (!$commands) {
return true;
}
$item_phid = $item->getPHID();
$viewer = $this->getViewer();
$lock = $this->newLock($item_phid);
try {
$lock->lock(1);
} catch (PhutilLockException $ex) {
return false;
}
try {
$item = $this->loadItem($item_phid);
// Reload commands now that we have a lock, to make sure we don't
// execute any commands twice by mistake.
$commands = id(new NuanceItemCommandQuery())
->setViewer($viewer)
->withIDs(mpull($commands, 'getID'))
->execute();
$this->executeCommandList($item, $commands);
} catch (Exception $ex) {
$lock->unlock();
throw $ex;
}
$lock->unlock();
return true;
}
private function executeCommandList(NuanceItem $item, array $commands) {
$viewer = $this->getViewer();
$executors = NuanceCommandImplementation::getAllCommands();
foreach ($commands as $command) {
$impl->applyCommand($item, $command);
$command->delete();
if ($command->getItemPHID() !== $item->getPHID()) {
throw new Exception(
pht('Trying to apply a command to the wrong item!'));
}
if ($command->getStatus() !== NuanceItemCommand::STATUS_ISSUED) {
// Never execute commands which have already been issued.
continue;
}
$command
->setStatus(NuanceItemCommand::STATUS_EXECUTING)
->save();
try {
$command_key = $command->getCommand();
$executor = idx($executors, $command_key);
if (!$executor) {
throw new Exception(
pht(
'Unable to execute command "%s": this command does not have '.
'a recognized command implementation.',
$command_key));
}
$executor = clone $executor;
$executor
->setActor($viewer)
->applyCommand($item, $command);
$command
->setStatus(NuanceItemCommand::STATUS_DONE)
->save();
} catch (Exception $ex) {
$command
->setStatus(NuanceItemCommand::STATUS_FAILED)
->save();
throw $ex;
}
}
}
private function newLock($item_phid) {
$hash = PhabricatorHash::digestForIndex($item_phid);
$lock_key = "nuance.item.{$hash}";
return PhabricatorGlobalLock::newLock($lock_key);
}
}

View file

@ -0,0 +1,22 @@
<?php
final class NuanceItemCommandTransaction
extends NuanceItemTransactionType {
const TRANSACTIONTYPE = 'nuance.item.command';
public function generateOldValue($object) {
return null;
}
public function getTitle() {
$spec = $this->getNewValue();
$command_key = idx($spec, 'command', '???');
return pht(
'%s applied a "%s" command to this item.',
$this->renderAuthor(),
$command_key);
}
}

View file

@ -0,0 +1,27 @@
<?php
final class NuanceItemOwnerTransaction
extends NuanceItemTransactionType {
const TRANSACTIONTYPE = 'nuance.item.owner';
public function generateOldValue($object) {
return $object->getOwnerPHID();
}
public function applyInternalEffects($object, $value) {
$object->setOwnerPHID($value);
}
public function getTitle() {
// TODO: Assign, unassign strings probably need variants.
return pht(
'%s reassigned this item from %s to %s.',
$this->renderAuthor(),
$this->renderHandle($this->getOldValue()),
$this->renderHandle($this->getNewValue()));
}
}

View file

@ -0,0 +1,27 @@
<?php
final class NuanceItemPropertyTransaction
extends NuanceItemTransactionType {
const TRANSACTIONTYPE = 'nuance.item.property';
public function generateOldValue($object) {
$property_key = NuanceItemTransaction::PROPERTY_KEY;
$key = $this->getMetadataValue($property_key);
return $object->getItemProperty($key);
}
public function applyInternalEffects($object, $value) {
$property_key = NuanceItemTransaction::PROPERTY_KEY;
$key = $this->getMetadataValue($property_key);
$object->setItemProperty($key, $value);
}
public function getTitle() {
return pht(
'%s set a property on this item.',
$this->renderAuthor());
}
}

View file

@ -0,0 +1,25 @@
<?php
final class NuanceItemQueueTransaction
extends NuanceItemTransactionType {
const TRANSACTIONTYPE = 'nuance.item.queue';
public function generateOldValue($object) {
return $object->getQueuePHID();
}
public function applyInternalEffects($object, $value) {
$object->setQueuePHID($value);
}
public function getTitle() {
return pht(
'%s rerouted this item from %s to %s.',
$this->renderAuthor(),
$this->renderHandle($this->getOldValue()),
$this->renderHandle($this->getNewValue()));
}
}

View file

@ -0,0 +1,16 @@
<?php
final class NuanceItemRequestorTransaction
extends NuanceItemTransactionType {
const TRANSACTIONTYPE = 'nuance.item.requestor';
public function generateOldValue($object) {
return $object->getRequestorPHID();
}
public function applyInternalEffects($object, $value) {
$object->setRequestorPHID($value);
}
}

View file

@ -0,0 +1,16 @@
<?php
final class NuanceItemSourceTransaction
extends NuanceItemTransactionType {
const TRANSACTIONTYPE = 'nuance.item.source';
public function generateOldValue($object) {
return $object->getSourcePHID();
}
public function applyInternalEffects($object, $value) {
$object->setSourcePHID($value);
}
}

View file

@ -0,0 +1,25 @@
<?php
final class NuanceItemStatusTransaction
extends NuanceItemTransactionType {
const TRANSACTIONTYPE = 'nuance.item.status';
public function generateOldValue($object) {
return $object->getStatus();
}
public function applyInternalEffects($object, $value) {
$object->setStatus($value);
}
public function getTitle() {
return pht(
'%s changed the status of this item from %s to %s.',
$this->renderAuthor(),
$this->renderOldValue(),
$this->renderNewValue());
}
}

View file

@ -0,0 +1,4 @@
<?php
abstract class NuanceItemTransactionType
extends PhabricatorModularTransactionType {}

View file

@ -0,0 +1,47 @@
<?php
final class NuanceQueueNameTransaction
extends NuanceQueueTransactionType {
const TRANSACTIONTYPE = 'nuance.queue.name';
public function generateOldValue($object) {
return $object->getName();
}
public function applyInternalEffects($object, $value) {
$object->setName($value);
}
public function getTitle() {
return pht(
'%s renamed this queue from %s to %s.',
$this->renderAuthor(),
$this->renderOldValue(),
$this->renderNewValue());
}
public function validateTransactions($object, array $xactions) {
$errors = array();
if ($this->isEmptyTextTransaction($object->getName(), $xactions)) {
$errors[] = $this->newRequiredError(
pht('Queues must have a name.'));
}
$max_length = $object->getColumnMaximumByteLength('name');
foreach ($xactions as $xaction) {
$new_value = $xaction->getNewValue();
$new_length = strlen($new_value);
if ($new_length > $max_length) {
$errors[] = $this->newInvalidError(
pht(
'Queue names must not be longer than %s character(s).',
new PhutilNumber($max_length)));
}
}
return $errors;
}
}

View file

@ -0,0 +1,4 @@
<?php
abstract class NuanceQueueTransactionType
extends PhabricatorModularTransactionType {}

View file

@ -0,0 +1,42 @@
<?php
final class NuanceSourceDefaultQueueTransaction
extends NuanceSourceTransactionType {
const TRANSACTIONTYPE = 'source.queue.default';
public function generateOldValue($object) {
return $object->getDefaultQueuePHID();
}
public function applyInternalEffects($object, $value) {
$object->setDefaultQueuePHID($value);
}
public function getTitle() {
return pht(
'%s changed the default queue for this source from %s to %s.',
$this->renderAuthor(),
$this->renderOldHandle(),
$this->renderNewHandle());
}
public function validateTransactions($object, array $xactions) {
$errors = array();
if (!$object->getDefaultQueuePHID() && !$xactions) {
$errors[] = $this->newRequiredError(
pht('Sources must have a default queue.'));
}
foreach ($xactions as $xaction) {
if (!$xaction->getNewValue()) {
$errors[] = $this->newRequiredError(
pht('Sources must have a default queue.'));
}
}
return $errors;
}
}

View file

@ -0,0 +1,47 @@
<?php
final class NuanceSourceNameTransaction
extends NuanceSourceTransactionType {
const TRANSACTIONTYPE = 'source.name';
public function generateOldValue($object) {
return $object->getName();
}
public function applyInternalEffects($object, $value) {
$object->setName($value);
}
public function getTitle() {
return pht(
'%s renamed this source from %s to %s.',
$this->renderAuthor(),
$this->renderOldValue(),
$this->renderNewValue());
}
public function validateTransactions($object, array $xactions) {
$errors = array();
if ($this->isEmptyTextTransaction($object->getName(), $xactions)) {
$errors[] = $this->newRequiredError(
pht('Sources must have a name.'));
}
$max_length = $object->getColumnMaximumByteLength('name');
foreach ($xactions as $xaction) {
$new_value = $xaction->getNewValue();
$new_length = strlen($new_value);
if ($new_length > $max_length) {
$errors[] = $this->newInvalidError(
pht(
'Source names must not be longer than %s character(s).',
new PhutilNumber($max_length)));
}
}
return $errors;
}
}

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