diff --git a/resources/sql/autopatches/20141106.uniqdrafts.php b/resources/sql/autopatches/20141106.uniqdrafts.php index 31d6a53cb7..85b05f9ced 100644 --- a/resources/sql/autopatches/20141106.uniqdrafts.php +++ b/resources/sql/autopatches/20141106.uniqdrafts.php @@ -1,30 +1,3 @@ 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. diff --git a/resources/sql/autopatches/20170522.nuance.01.itemkey.sql b/resources/sql/autopatches/20170522.nuance.01.itemkey.sql new file mode 100644 index 0000000000..75118205ce --- /dev/null +++ b/resources/sql/autopatches/20170522.nuance.01.itemkey.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_nuance.nuance_item + MODIFY itemKey VARCHAR(64) COLLATE {$COLLATE_TEXT}; diff --git a/resources/sql/autopatches/20170524.nuance.01.command.sql b/resources/sql/autopatches/20170524.nuance.01.command.sql new file mode 100644 index 0000000000..529756e748 --- /dev/null +++ b/resources/sql/autopatches/20170524.nuance.01.command.sql @@ -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); diff --git a/resources/sql/autopatches/20170524.nuance.02.commandstatus.sql b/resources/sql/autopatches/20170524.nuance.02.commandstatus.sql new file mode 100644 index 0000000000..14f57af053 --- /dev/null +++ b/resources/sql/autopatches/20170524.nuance.02.commandstatus.sql @@ -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 = ''; diff --git a/resources/sql/autopatches/20170526.dropdifferentialdrafts.sql b/resources/sql/autopatches/20170526.dropdifferentialdrafts.sql new file mode 100644 index 0000000000..057bcb0d90 --- /dev/null +++ b/resources/sql/autopatches/20170526.dropdifferentialdrafts.sql @@ -0,0 +1 @@ +DROP TABLE {$NAMESPACE}_differential.differential_draft; diff --git a/resources/sql/autopatches/20170526.milestones.php b/resources/sql/autopatches/20170526.milestones.php new file mode 100644 index 0000000000..2e30ac4775 --- /dev/null +++ b/resources/sql/autopatches/20170526.milestones.php @@ -0,0 +1,11 @@ +getPHID(), + array( + 'force' => true, + )); +} diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index cbf324c847..ce3cc27784 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -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', diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php index 91d3a9e336..3ba3aa1e70 100644 --- a/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php +++ b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php @@ -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) { diff --git a/src/applications/calendar/view/PHUIUserAvailabilityView.php b/src/applications/calendar/view/PHUIUserAvailabilityView.php index 7ab0ace77a..f4f9696ba9 100644 --- a/src/applications/calendar/view/PHUIUserAvailabilityView.php +++ b/src/applications/calendar/view/PHUIUserAvailabilityView.php @@ -29,7 +29,7 @@ final class PHUIUserAvailabilityView $away_tag = id(new PHUITagView()) ->setType(PHUITagView::TYPE_SHADE) - ->setShade($color) + ->setColor($color) ->setName($name) ->setDotColor($color); diff --git a/src/applications/conpherence/controller/ConpherenceController.php b/src/applications/conpherence/controller/ConpherenceController.php index 90328cca04..9d7473e159 100644 --- a/src/applications/conpherence/controller/ConpherenceController.php +++ b/src/applications/conpherence/controller/ConpherenceController.php @@ -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); diff --git a/src/applications/daemon/garbagecollector/PhabricatorDaemonLogGarbageCollector.php b/src/applications/daemon/garbagecollector/PhabricatorDaemonLogGarbageCollector.php index 3ff26e3db7..fd7de9dd33 100644 --- a/src/applications/daemon/garbagecollector/PhabricatorDaemonLogGarbageCollector.php +++ b/src/applications/daemon/garbagecollector/PhabricatorDaemonLogGarbageCollector.php @@ -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); } diff --git a/src/applications/daemon/storage/PhabricatorDaemonLog.php b/src/applications/daemon/storage/PhabricatorDaemonLog.php index 2e61da3872..d3ac485ea2 100644 --- a/src/applications/daemon/storage/PhabricatorDaemonLog.php +++ b/src/applications/daemon/storage/PhabricatorDaemonLog.php @@ -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(); } diff --git a/src/applications/dashboard/controller/PhabricatorDashboardViewController.php b/src/applications/dashboard/controller/PhabricatorDashboardViewController.php index 07faee8f05..41441f09f4 100644 --- a/src/applications/dashboard/controller/PhabricatorDashboardViewController.php +++ b/src/applications/dashboard/controller/PhabricatorDashboardViewController.php @@ -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); diff --git a/src/applications/differential/constants/DifferentialRevisionStatus.php b/src/applications/differential/constants/DifferentialRevisionStatus.php index 4f332838ac..38e6a2dd49 100644 --- a/src/applications/differential/constants/DifferentialRevisionStatus.php +++ b/src/applications/differential/constants/DifferentialRevisionStatus.php @@ -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; diff --git a/src/applications/differential/storage/DifferentialDraft.php b/src/applications/differential/storage/DifferentialDraft.php deleted file mode 100644 index 7dbe2f68bd..0000000000 --- a/src/applications/differential/storage/DifferentialDraft.php +++ /dev/null @@ -1,23 +0,0 @@ - array( - 'draftKey' => 'text64', - ), - self::CONFIG_KEY_SCHEMA => array( - 'key_unique' => array( - 'columns' => array('objectPHID', 'authorPHID', 'draftKey'), - 'unique' => true, - ), - ), - ) + parent::getConfiguration(); - } - -} diff --git a/src/applications/differential/xaction/DifferentialRevisionAcceptTransaction.php b/src/applications/differential/xaction/DifferentialRevisionAcceptTransaction.php index 7d1f1a92ef..9b6aad3ad1 100644 --- a/src/applications/differential/xaction/DifferentialRevisionAcceptTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionAcceptTransaction.php @@ -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( diff --git a/src/applications/differential/xaction/DifferentialRevisionReviewTransaction.php b/src/applications/differential/xaction/DifferentialRevisionReviewTransaction.php index d44de8706c..c30514b9ab 100644 --- a/src/applications/differential/xaction/DifferentialRevisionReviewTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionReviewTransaction.php @@ -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); diff --git a/src/applications/diffusion/controller/DiffusionController.php b/src/applications/diffusion/controller/DiffusionController.php index a018eb3dbb..a296761bdf 100644 --- a/src/applications/diffusion/controller/DiffusionController.php +++ b/src/applications/diffusion/controller/DiffusionController.php @@ -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; diff --git a/src/applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php b/src/applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php index 22c03145e0..12faa6799d 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryManagePanelsController.php @@ -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(); diff --git a/src/applications/diffusion/management/DiffusionRepositoryActionsManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryActionsManagementPanel.php index 1f47a68de3..83697e52e3 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryActionsManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryActionsManagementPanel.php @@ -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') diff --git a/src/applications/diffusion/management/DiffusionRepositoryAutomationManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryAutomationManagementPanel.php index 14182a0444..b37aa05ecf 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryAutomationManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryAutomationManagementPanel.php @@ -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) { diff --git a/src/applications/diffusion/management/DiffusionRepositoryBasicsManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryBasicsManagementPanel.php index 45c2ca142a..a5e09e40d3 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryBasicsManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryBasicsManagementPanel.php @@ -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); diff --git a/src/applications/diffusion/management/DiffusionRepositoryBranchesManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryBranchesManagementPanel.php index b3e7926cfc..aa2bb49ccb 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryBranchesManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryBranchesManagementPanel.php @@ -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'), diff --git a/src/applications/diffusion/management/DiffusionRepositoryDocumentationManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryDocumentationManagementPanel.php index ca2c577b38..9d067cd90b 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryDocumentationManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryDocumentationManagementPanel.php @@ -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'); diff --git a/src/applications/diffusion/management/DiffusionRepositoryHistoryManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryHistoryManagementPanel.php index c1a812dca0..4ce2367f20 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryHistoryManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryHistoryManagementPanel.php @@ -21,5 +21,8 @@ final class DiffusionRepositoryHistoryManagementPanel return $this->newTimeline(); } + public function buildManagementPanelCurtain() { + return null; + } } diff --git a/src/applications/diffusion/management/DiffusionRepositoryManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryManagementPanel.php index 34047cdb89..0098fe1df9 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryManagementPanel.php @@ -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() { diff --git a/src/applications/diffusion/management/DiffusionRepositoryPoliciesManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryPoliciesManagementPanel.php index 081869fbaa..6b2972e30c 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryPoliciesManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryPoliciesManagementPanel.php @@ -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, diff --git a/src/applications/diffusion/management/DiffusionRepositoryStagingManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryStagingManagementPanel.php index 79b716e82a..bab7e72857 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryStagingManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryStagingManagementPanel.php @@ -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) { diff --git a/src/applications/diffusion/management/DiffusionRepositoryStatusManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryStatusManagementPanel.php index 324338443d..23b2cd1684 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryStatusManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryStatusManagementPanel.php @@ -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'), diff --git a/src/applications/diffusion/management/DiffusionRepositoryStorageManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryStorageManagementPanel.php index 9cf210632d..3d6733d5d3 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryStorageManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryStorageManagementPanel.php @@ -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; } } diff --git a/src/applications/diffusion/management/DiffusionRepositorySubversionManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositorySubversionManagementPanel.php index c6b236d5b9..5b9384d195 100644 --- a/src/applications/diffusion/management/DiffusionRepositorySubversionManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositorySubversionManagementPanel.php @@ -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'), diff --git a/src/applications/diffusion/management/DiffusionRepositorySymbolsManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositorySymbolsManagementPanel.php index 996473f4f1..22d8780077 100644 --- a/src/applications/diffusion/management/DiffusionRepositorySymbolsManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositorySymbolsManagementPanel.php @@ -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) { diff --git a/src/applications/diffusion/management/DiffusionRepositoryURIsManagementPanel.php b/src/applications/diffusion/management/DiffusionRepositoryURIsManagementPanel.php index 333c2f03c8..4fe9e00475 100644 --- a/src/applications/diffusion/management/DiffusionRepositoryURIsManagementPanel.php +++ b/src/applications/diffusion/management/DiffusionRepositoryURIsManagementPanel.php @@ -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); } } diff --git a/src/applications/diffusion/view/DiffusionCommitListView.php b/src/applications/diffusion/view/DiffusionCommitListView.php new file mode 100644 index 0000000000..b3f2a9fb0c --- /dev/null +++ b/src/applications/diffusion/view/DiffusionCommitListView.php @@ -0,0 +1,160 @@ +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; + } + +} diff --git a/src/applications/feed/conduit/FeedQueryConduitAPIMethod.php b/src/applications/feed/conduit/FeedQueryConduitAPIMethod.php index 661b70a7ab..bddc4f5921 100644 --- a/src/applications/feed/conduit/FeedQueryConduitAPIMethod.php +++ b/src/applications/feed/conduit/FeedQueryConduitAPIMethod.php @@ -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); diff --git a/src/applications/feed/query/PhabricatorFeedQuery.php b/src/applications/feed/query/PhabricatorFeedQuery.php index 13cfb266ed..ae1da3aba0 100644 --- a/src/applications/feed/query/PhabricatorFeedQuery.php +++ b/src/applications/feed/query/PhabricatorFeedQuery.php @@ -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'; } diff --git a/src/applications/feed/query/PhabricatorFeedSearchEngine.php b/src/applications/feed/query/PhabricatorFeedSearchEngine.php index b8caf60ae7..d17c756524 100644 --- a/src/applications/feed/query/PhabricatorFeedSearchEngine.php +++ b/src/applications/feed/query/PhabricatorFeedSearchEngine.php @@ -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); diff --git a/src/applications/files/controller/PhabricatorFileInfoController.php b/src/applications/files/controller/PhabricatorFileInfoController.php index 061790aa31..f30421c132 100644 --- a/src/applications/files/controller/PhabricatorFileInfoController.php +++ b/src/applications/files/controller/PhabricatorFileInfoController.php @@ -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); } diff --git a/src/applications/guides/view/PhabricatorGuideListView.php b/src/applications/guides/view/PhabricatorGuideListView.php index 384caf15ee..04ab7c3058 100644 --- a/src/applications/guides/view/PhabricatorGuideListView.php +++ b/src/applications/guides/view/PhabricatorGuideListView.php @@ -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); } diff --git a/src/applications/harbormaster/view/HarbormasterUnitSummaryView.php b/src/applications/harbormaster/view/HarbormasterUnitSummaryView.php index 28f79ee181..e5a66a3eb8 100644 --- a/src/applications/harbormaster/view/HarbormasterUnitSummaryView.php +++ b/src/applications/harbormaster/view/HarbormasterUnitSummaryView.php @@ -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); diff --git a/src/applications/maniphest/__tests__/ManiphestTaskTestCase.php b/src/applications/maniphest/__tests__/ManiphestTaskTestCase.php index 5f5c198a84..1e571190d7 100644 --- a/src/applications/maniphest/__tests__/ManiphestTaskTestCase.php +++ b/src/applications/maniphest/__tests__/ManiphestTaskTestCase.php @@ -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) { diff --git a/src/applications/maniphest/conduit/ManiphestPrioritySearchConduitAPIMethod.php b/src/applications/maniphest/conduit/ManiphestPrioritySearchConduitAPIMethod.php new file mode 100644 index 0000000000..eab06fb780 --- /dev/null +++ b/src/applications/maniphest/conduit/ManiphestPrioritySearchConduitAPIMethod.php @@ -0,0 +1,44 @@ +'; + } + + 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); + } + +} diff --git a/src/applications/maniphest/config/PhabricatorManiphestConfigOptions.php b/src/applications/maniphest/config/PhabricatorManiphestConfigOptions.php index 4a1927c5a8..9d927ffca1 100644 --- a/src/applications/maniphest/config/PhabricatorManiphestConfigOptions.php +++ b/src/applications/maniphest/config/PhabricatorManiphestConfigOptions.php @@ -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)); diff --git a/src/applications/maniphest/constants/ManiphestTaskPriority.php b/src/applications/maniphest/constants/ManiphestTaskPriority.php index 16a35a9e83..dd2ab69c69 100644 --- a/src/applications/maniphest/constants/ManiphestTaskPriority.php +++ b/src/applications/maniphest/constants/ManiphestTaskPriority.php @@ -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; diff --git a/src/applications/maniphest/constants/ManiphestTaskStatus.php b/src/applications/maniphest/constants/ManiphestTaskStatus.php index 5734892f0a..6781fb7724 100644 --- a/src/applications/maniphest/constants/ManiphestTaskStatus.php +++ b/src/applications/maniphest/constants/ManiphestTaskStatus.php @@ -97,7 +97,7 @@ final class ManiphestTaskStatus extends ManiphestConstants { ->setName($name) ->setIcon($icon) ->setType(PHUITagView::TYPE_SHADE) - ->setShade($color); + ->setColor($color); return $tag; } diff --git a/src/applications/maniphest/controller/ManiphestTaskDetailController.php b/src/applications/maniphest/controller/ManiphestTaskDetailController.php index 5384825de8..92fe703f91 100644 --- a/src/applications/maniphest/controller/ManiphestTaskDetailController.php +++ b/src/applications/maniphest/controller/ManiphestTaskDetailController.php @@ -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; } diff --git a/src/applications/maniphest/editor/ManiphestTransactionEditor.php b/src/applications/maniphest/editor/ManiphestTransactionEditor.php index c9e17cd4de..1b780549dc 100644 --- a/src/applications/maniphest/editor/ManiphestTransactionEditor.php +++ b/src/applications/maniphest/editor/ManiphestTransactionEditor.php @@ -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) { diff --git a/src/applications/maniphest/query/ManiphestTaskQuery.php b/src/applications/maniphest/query/ManiphestTaskQuery.php index 4cf47f40cb..704a67f548 100644 --- a/src/applications/maniphest/query/ManiphestTaskQuery.php +++ b/src/applications/maniphest/query/ManiphestTaskQuery.php @@ -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, diff --git a/src/applications/maniphest/storage/ManiphestTask.php b/src/applications/maniphest/storage/ManiphestTask.php index 16937da2f2..7cf9d3353f 100644 --- a/src/applications/maniphest/storage/ManiphestTask.php +++ b/src/applications/maniphest/storage/ManiphestTask.php @@ -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 )--------------------------------------- */ diff --git a/src/applications/maniphest/storage/ManiphestTransaction.php b/src/applications/maniphest/storage/ManiphestTransaction.php index 0d27ddc5da..e2739c1d09 100644 --- a/src/applications/maniphest/storage/ManiphestTransaction.php +++ b/src/applications/maniphest/storage/ManiphestTransaction.php @@ -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(); + } + } diff --git a/src/applications/maniphest/typeahead/ManiphestTaskSubtypeDatasource.php b/src/applications/maniphest/typeahead/ManiphestTaskSubtypeDatasource.php index 2678e946b4..c061c694e4 100644 --- a/src/applications/maniphest/typeahead/ManiphestTaskSubtypeDatasource.php +++ b/src/applications/maniphest/typeahead/ManiphestTaskSubtypeDatasource.php @@ -32,6 +32,7 @@ final class ManiphestTaskSubtypeDatasource $result = id(new PhabricatorTypeaheadResult()) ->setIcon($subtype->getIcon()) + ->setColor($subtype->getColor()) ->setPHID($key) ->setName($subtype->getName()); diff --git a/src/applications/maniphest/view/ManiphestTaskListView.php b/src/applications/maniphest/view/ManiphestTaskListView.php index 6f714d0c5c..de6b386ac8 100644 --- a/src/applications/maniphest/view/ManiphestTaskListView.php +++ b/src/applications/maniphest/view/ManiphestTaskListView.php @@ -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())); diff --git a/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php b/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php index 6d8548fdb4..b1c20af9e6 100644 --- a/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php +++ b/src/applications/maniphest/xaction/ManiphestTaskPointsTransaction.php @@ -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(); diff --git a/src/applications/maniphest/xaction/ManiphestTaskTitleTransaction.php b/src/applications/maniphest/xaction/ManiphestTaskTitleTransaction.php index 5fade77d07..506817b0fc 100644 --- a/src/applications/maniphest/xaction/ManiphestTaskTitleTransaction.php +++ b/src/applications/maniphest/xaction/ManiphestTaskTitleTransaction.php @@ -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()); diff --git a/src/applications/maniphest/xaction/ManiphestTaskTransactionType.php b/src/applications/maniphest/xaction/ManiphestTaskTransactionType.php index 699ef11631..c59de163c6 100644 --- a/src/applications/maniphest/xaction/ManiphestTaskTransactionType.php +++ b/src/applications/maniphest/xaction/ManiphestTaskTransactionType.php @@ -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(); - } - } diff --git a/src/applications/meta/query/PhabricatorAppSearchEngine.php b/src/applications/meta/query/PhabricatorAppSearchEngine.php index b609a8b137..ee938abbbb 100644 --- a/src/applications/meta/query/PhabricatorAppSearchEngine.php +++ b/src/applications/meta/query/PhabricatorAppSearchEngine.php @@ -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); } diff --git a/src/applications/nuance/application/PhabricatorNuanceApplication.php b/src/applications/nuance/application/PhabricatorNuanceApplication.php index cf18bc389d..cd268dd95e 100644 --- a/src/applications/nuance/application/PhabricatorNuanceApplication.php +++ b/src/applications/nuance/application/PhabricatorNuanceApplication.php @@ -51,6 +51,9 @@ final class PhabricatorNuanceApplication extends PhabricatorApplication { $this->getQueryRoutePattern() => 'NuanceQueueListController', $this->getEditRoutePattern('edit/') => 'NuanceQueueEditController', 'view/(?P[1-9]\d*)/' => 'NuanceQueueViewController', + 'work/(?P[1-9]\d*)/' => 'NuanceQueueWorkController', + 'action/(?P[1-9]\d*)/(?P[^/]+)/(?P[1-9]\d*)/' + => 'NuanceItemActionController', ), ), '/action/' => array( diff --git a/src/applications/nuance/command/NuanceCommandImplementation.php b/src/applications/nuance/command/NuanceCommandImplementation.php new file mode 100644 index 0000000000..2b47e40654 --- /dev/null +++ b/src/applications/nuance/command/NuanceCommandImplementation.php @@ -0,0 +1,113 @@ +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); + } + +} diff --git a/src/applications/nuance/command/NuanceItemCommandSpec.php b/src/applications/nuance/command/NuanceItemCommandSpec.php new file mode 100644 index 0000000000..d449e54833 --- /dev/null +++ b/src/applications/nuance/command/NuanceItemCommandSpec.php @@ -0,0 +1,37 @@ +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; + } + +} diff --git a/src/applications/nuance/command/NuanceTrashCommand.php b/src/applications/nuance/command/NuanceTrashCommand.php new file mode 100644 index 0000000000..b5ac851539 --- /dev/null +++ b/src/applications/nuance/command/NuanceTrashCommand.php @@ -0,0 +1,29 @@ +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); + } + +} diff --git a/src/applications/nuance/controller/NuanceItemActionController.php b/src/applications/nuance/controller/NuanceItemActionController.php index c64ac5f6ac..39ae8fd995 100644 --- a/src/applications/nuance/controller/NuanceItemActionController.php +++ b/src/applications/nuance/controller/NuanceItemActionController.php @@ -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); } } diff --git a/src/applications/nuance/controller/NuanceItemViewController.php b/src/applications/nuance/controller/NuanceItemViewController.php index 7ef5d06682..a902dc3b06 100644 --- a/src/applications/nuance/controller/NuanceItemViewController.php +++ b/src/applications/nuance/controller/NuanceItemViewController.php @@ -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); - } - } diff --git a/src/applications/nuance/controller/NuanceQueueViewController.php b/src/applications/nuance/controller/NuanceQueueViewController.php index 8f4e85565a..f5284f6bfa 100644 --- a/src/applications/nuance/controller/NuanceQueueViewController.php +++ b/src/applications/nuance/controller/NuanceQueueViewController.php @@ -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; } diff --git a/src/applications/nuance/controller/NuanceQueueWorkController.php b/src/applications/nuance/controller/NuanceQueueWorkController.php new file mode 100644 index 0000000000..8703a69334 --- /dev/null +++ b/src/applications/nuance/controller/NuanceQueueWorkController.php @@ -0,0 +1,186 @@ +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); + } + +} diff --git a/src/applications/nuance/cursor/NuanceGitHubIssuesImportCursor.php b/src/applications/nuance/cursor/NuanceGitHubIssuesImportCursor.php index d250b1d02d..151a5876ba 100644 --- a/src/applications/nuance/cursor/NuanceGitHubIssuesImportCursor.php +++ b/src/applications/nuance/cursor/NuanceGitHubIssuesImportCursor.php @@ -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') diff --git a/src/applications/nuance/cursor/NuanceGitHubRepositoryImportCursor.php b/src/applications/nuance/cursor/NuanceGitHubRepositoryImportCursor.php index 72aca276d2..018b3aa8c1 100644 --- a/src/applications/nuance/cursor/NuanceGitHubRepositoryImportCursor.php +++ b/src/applications/nuance/cursor/NuanceGitHubRepositoryImportCursor.php @@ -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') diff --git a/src/applications/nuance/editor/NuanceItemEditor.php b/src/applications/nuance/editor/NuanceItemEditor.php index 45288052c2..b41ca77563 100644 --- a/src/applications/nuance/editor/NuanceItemEditor.php +++ b/src/applications/nuance/editor/NuanceItemEditor.php @@ -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); - } - } diff --git a/src/applications/nuance/editor/NuanceQueueEditEngine.php b/src/applications/nuance/editor/NuanceQueueEditEngine.php index 049916adbe..12f5c7b517 100644 --- a/src/applications/nuance/editor/NuanceQueueEditEngine.php +++ b/src/applications/nuance/editor/NuanceQueueEditEngine.php @@ -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()), ); diff --git a/src/applications/nuance/editor/NuanceQueueEditor.php b/src/applications/nuance/editor/NuanceQueueEditor.php index cb3ead2417..2a18188f98 100644 --- a/src/applications/nuance/editor/NuanceQueueEditor.php +++ b/src/applications/nuance/editor/NuanceQueueEditor.php @@ -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; - } - - } diff --git a/src/applications/nuance/editor/NuanceSourceEditEngine.php b/src/applications/nuance/editor/NuanceSourceEditEngine.php index 18d27863ac..eac751c3a5 100644 --- a/src/applications/nuance/editor/NuanceSourceEditEngine.php +++ b/src/applications/nuance/editor/NuanceSourceEditEngine.php @@ -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()), ); diff --git a/src/applications/nuance/editor/NuanceSourceEditor.php b/src/applications/nuance/editor/NuanceSourceEditor.php index 5fbc02b962..b56b183f9e 100644 --- a/src/applications/nuance/editor/NuanceSourceEditor.php +++ b/src/applications/nuance/editor/NuanceSourceEditor.php @@ -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; - } - } diff --git a/src/applications/nuance/item/NuanceFormItemType.php b/src/applications/nuance/item/NuanceFormItemType.php new file mode 100644 index 0000000000..cbdde0e89c --- /dev/null +++ b/src/applications/nuance/item/NuanceFormItemType.php @@ -0,0 +1,54 @@ +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; + } + +} diff --git a/src/applications/nuance/item/NuanceGitHubEventItemType.php b/src/applications/nuance/item/NuanceGitHubEventItemType.php index b7b90690b7..b8bfb3ccab 100644 --- a/src/applications/nuance/item/NuanceGitHubEventItemType.php +++ b/src/applications/nuance/item/NuanceGitHubEventItemType.php @@ -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': diff --git a/src/applications/nuance/item/NuanceItemType.php b/src/applications/nuance/item/NuanceItemType.php index 74555494d0..3a65ad0195 100644 --- a/src/applications/nuance/item/NuanceItemType.php +++ b/src/applications/nuance/item/NuanceItemType.php @@ -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); + } } diff --git a/src/applications/nuance/phid/NuanceItemPHIDType.php b/src/applications/nuance/phid/NuanceItemPHIDType.php index ee51633ea9..771b398419 100644 --- a/src/applications/nuance/phid/NuanceItemPHIDType.php +++ b/src/applications/nuance/phid/NuanceItemPHIDType.php @@ -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()); } } diff --git a/src/applications/nuance/query/NuanceItemCommandQuery.php b/src/applications/nuance/query/NuanceItemCommandQuery.php index cb20610187..27137cf8f6 100644 --- a/src/applications/nuance/query/NuanceItemCommandQuery.php +++ b/src/applications/nuance/query/NuanceItemCommandQuery.php @@ -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; } diff --git a/src/applications/nuance/query/NuanceItemQuery.php b/src/applications/nuance/query/NuanceItemQuery.php index 5bb0ec70ec..834e81ca72 100644 --- a/src/applications/nuance/query/NuanceItemQuery.php +++ b/src/applications/nuance/query/NuanceItemQuery.php @@ -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, diff --git a/src/applications/nuance/query/NuanceItemSearchEngine.php b/src/applications/nuance/query/NuanceItemSearchEngine.php index 2f0951b4e0..0868d7551a 100644 --- a/src/applications/nuance/query/NuanceItemSearchEngine.php +++ b/src/applications/nuance/query/NuanceItemSearchEngine.php @@ -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); } diff --git a/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php b/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php index e7c7212590..2eaf5781dc 100644 --- a/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php +++ b/src/applications/nuance/source/NuancePhabricatorFormSourceDefinition.php @@ -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'); diff --git a/src/applications/nuance/source/NuanceSourceDefinition.php b/src/applications/nuance/source/NuanceSourceDefinition.php index 99d79f8f31..2c4ebc65e5 100644 --- a/src/applications/nuance/source/NuanceSourceDefinition.php +++ b/src/applications/nuance/source/NuanceSourceDefinition.php @@ -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); diff --git a/src/applications/nuance/storage/NuanceItem.php b/src/applications/nuance/storage/NuanceItem.php index 2eadcdb2d9..09a106ca7a 100644 --- a/src/applications/nuance/storage/NuanceItem.php +++ b/src/applications/nuance/storage/NuanceItem.php @@ -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 )------------------------- */ diff --git a/src/applications/nuance/storage/NuanceItemCommand.php b/src/applications/nuance/storage/NuanceItemCommand.php index bda6860ff5..21d3792ff2 100644 --- a/src/applications/nuance/storage/NuanceItemCommand.php +++ b/src/applications/nuance/storage/NuanceItemCommand.php @@ -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 )----------------------------------------- */ diff --git a/src/applications/nuance/storage/NuanceItemTransaction.php b/src/applications/nuance/storage/NuanceItemTransaction.php index 77471fcdb9..5579183a66 100644 --- a/src/applications/nuance/storage/NuanceItemTransaction.php +++ b/src/applications/nuance/storage/NuanceItemTransaction.php @@ -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'; } } diff --git a/src/applications/nuance/storage/NuanceQueue.php b/src/applications/nuance/storage/NuanceQueue.php index 9691a42e2f..f0ba5bb45c 100644 --- a/src/applications/nuance/storage/NuanceQueue.php +++ b/src/applications/nuance/storage/NuanceQueue.php @@ -43,6 +43,10 @@ final class NuanceQueue return '/nuance/queue/view/'.$this->getID().'/'; } + public function getWorkURI() { + return '/nuance/queue/work/'.$this->getID().'/'; + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ diff --git a/src/applications/nuance/storage/NuanceQueueTransaction.php b/src/applications/nuance/storage/NuanceQueueTransaction.php index 44309c2ae6..1561db80e5 100644 --- a/src/applications/nuance/storage/NuanceQueueTransaction.php +++ b/src/applications/nuance/storage/NuanceQueueTransaction.php @@ -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'; } + } diff --git a/src/applications/nuance/storage/NuanceSourceTransaction.php b/src/applications/nuance/storage/NuanceSourceTransaction.php index 0b18c81184..0e264876dd 100644 --- a/src/applications/nuance/storage/NuanceSourceTransaction.php +++ b/src/applications/nuance/storage/NuanceSourceTransaction.php @@ -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'; } } diff --git a/src/applications/nuance/storage/NuanceTransaction.php b/src/applications/nuance/storage/NuanceTransaction.php index 5e9ae656ef..0cdb98ff27 100644 --- a/src/applications/nuance/storage/NuanceTransaction.php +++ b/src/applications/nuance/storage/NuanceTransaction.php @@ -1,7 +1,7 @@ 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); + } + } diff --git a/src/applications/nuance/xaction/NuanceItemCommandTransaction.php b/src/applications/nuance/xaction/NuanceItemCommandTransaction.php new file mode 100644 index 0000000000..6aa1d6473f --- /dev/null +++ b/src/applications/nuance/xaction/NuanceItemCommandTransaction.php @@ -0,0 +1,22 @@ +getNewValue(); + $command_key = idx($spec, 'command', '???'); + + return pht( + '%s applied a "%s" command to this item.', + $this->renderAuthor(), + $command_key); + } + +} diff --git a/src/applications/nuance/xaction/NuanceItemOwnerTransaction.php b/src/applications/nuance/xaction/NuanceItemOwnerTransaction.php new file mode 100644 index 0000000000..381371c8f3 --- /dev/null +++ b/src/applications/nuance/xaction/NuanceItemOwnerTransaction.php @@ -0,0 +1,27 @@ +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())); + } + +} diff --git a/src/applications/nuance/xaction/NuanceItemPropertyTransaction.php b/src/applications/nuance/xaction/NuanceItemPropertyTransaction.php new file mode 100644 index 0000000000..3367a6a0cf --- /dev/null +++ b/src/applications/nuance/xaction/NuanceItemPropertyTransaction.php @@ -0,0 +1,27 @@ +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()); + } + +} diff --git a/src/applications/nuance/xaction/NuanceItemQueueTransaction.php b/src/applications/nuance/xaction/NuanceItemQueueTransaction.php new file mode 100644 index 0000000000..8ccbdb7797 --- /dev/null +++ b/src/applications/nuance/xaction/NuanceItemQueueTransaction.php @@ -0,0 +1,25 @@ +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())); + } + + +} diff --git a/src/applications/nuance/xaction/NuanceItemRequestorTransaction.php b/src/applications/nuance/xaction/NuanceItemRequestorTransaction.php new file mode 100644 index 0000000000..a19ef45197 --- /dev/null +++ b/src/applications/nuance/xaction/NuanceItemRequestorTransaction.php @@ -0,0 +1,16 @@ +getRequestorPHID(); + } + + public function applyInternalEffects($object, $value) { + $object->setRequestorPHID($value); + } + +} diff --git a/src/applications/nuance/xaction/NuanceItemSourceTransaction.php b/src/applications/nuance/xaction/NuanceItemSourceTransaction.php new file mode 100644 index 0000000000..e637485990 --- /dev/null +++ b/src/applications/nuance/xaction/NuanceItemSourceTransaction.php @@ -0,0 +1,16 @@ +getSourcePHID(); + } + + public function applyInternalEffects($object, $value) { + $object->setSourcePHID($value); + } + +} diff --git a/src/applications/nuance/xaction/NuanceItemStatusTransaction.php b/src/applications/nuance/xaction/NuanceItemStatusTransaction.php new file mode 100644 index 0000000000..48c5a16e8c --- /dev/null +++ b/src/applications/nuance/xaction/NuanceItemStatusTransaction.php @@ -0,0 +1,25 @@ +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()); + } + + +} diff --git a/src/applications/nuance/xaction/NuanceItemTransactionType.php b/src/applications/nuance/xaction/NuanceItemTransactionType.php new file mode 100644 index 0000000000..cc03b31ac4 --- /dev/null +++ b/src/applications/nuance/xaction/NuanceItemTransactionType.php @@ -0,0 +1,4 @@ +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; + } + +} diff --git a/src/applications/nuance/xaction/NuanceQueueTransactionType.php b/src/applications/nuance/xaction/NuanceQueueTransactionType.php new file mode 100644 index 0000000000..5662e3619f --- /dev/null +++ b/src/applications/nuance/xaction/NuanceQueueTransactionType.php @@ -0,0 +1,4 @@ +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; + } + +} diff --git a/src/applications/nuance/xaction/NuanceSourceNameTransaction.php b/src/applications/nuance/xaction/NuanceSourceNameTransaction.php new file mode 100644 index 0000000000..e33b60551c --- /dev/null +++ b/src/applications/nuance/xaction/NuanceSourceNameTransaction.php @@ -0,0 +1,47 @@ +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; + } + +} diff --git a/src/applications/nuance/xaction/NuanceSourceTransactionType.php b/src/applications/nuance/xaction/NuanceSourceTransactionType.php new file mode 100644 index 0000000000..5a26149cf8 --- /dev/null +++ b/src/applications/nuance/xaction/NuanceSourceTransactionType.php @@ -0,0 +1,4 @@ +isArchived()) { $tag = id(new PHUITagView()) ->setName(pht('Archived')) - ->setShade(PHUITagView::COLOR_INDIGO) + ->setColor(PHUITagView::COLOR_INDIGO) ->setType(PHUITagView::TYPE_OBJECT); $hovercard->addTag($tag); } diff --git a/src/applications/people/application/PhabricatorPeopleApplication.php b/src/applications/people/application/PhabricatorPeopleApplication.php index f2bb70e423..dde82f1d3a 100644 --- a/src/applications/people/application/PhabricatorPeopleApplication.php +++ b/src/applications/people/application/PhabricatorPeopleApplication.php @@ -70,6 +70,8 @@ final class PhabricatorPeopleApplication extends PhabricatorApplication { 'PhabricatorPeopleProfileTasksController', 'commits/(?P[1-9]\d*)/' => 'PhabricatorPeopleProfileCommitsController', + 'revisions/(?P[1-9]\d*)/' => + 'PhabricatorPeopleProfileRevisionsController', 'picture/(?P[1-9]\d*)/' => 'PhabricatorPeopleProfilePictureController', 'manage/(?P[1-9]\d*)/' => diff --git a/src/applications/people/controller/PhabricatorPeopleProfileCommitsController.php b/src/applications/people/controller/PhabricatorPeopleProfileCommitsController.php index f2adf4b23d..c18c5f4d96 100644 --- a/src/applications/people/controller/PhabricatorPeopleProfileCommitsController.php +++ b/src/applications/people/controller/PhabricatorPeopleProfileCommitsController.php @@ -13,10 +13,6 @@ final class PhabricatorPeopleProfileCommitsController ->needProfile(true) ->needProfileImage(true) ->needAvailability(true) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - )) ->executeOne(); if (!$user) { return new Aphront404Response(); @@ -60,26 +56,15 @@ final class PhabricatorPeopleProfileCommitsController $commits = id(new DiffusionCommitQuery()) ->setViewer($viewer) ->withAuthorPHIDs(array($user->getPHID())) - ->needAuditRequests(true) ->needCommitData(true) - ->needDrafts(true) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - )) ->setLimit(100) ->execute(); - $list = id(new PhabricatorAuditListView()) + $list = id(new DiffusionCommitListView()) ->setViewer($viewer) ->setCommits($commits) ->setNoDataString(pht('No recent commits.')); - $view = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Recent Commits')) - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) - ->appendChild($list); - - return $view; + return $list; } } diff --git a/src/applications/people/controller/PhabricatorPeopleProfileRevisionsController.php b/src/applications/people/controller/PhabricatorPeopleProfileRevisionsController.php new file mode 100644 index 0000000000..adb2a60e5d --- /dev/null +++ b/src/applications/people/controller/PhabricatorPeopleProfileRevisionsController.php @@ -0,0 +1,82 @@ +getViewer(); + $id = $request->getURIData('id'); + + $user = id(new PhabricatorPeopleQuery()) + ->setViewer($viewer) + ->withIDs(array($id)) + ->needProfile(true) + ->needProfileImage(true) + ->needAvailability(true) + ->executeOne(); + if (!$user) { + return new Aphront404Response(); + } + + $class = 'PhabricatorDifferentialApplication'; + if (!PhabricatorApplication::isClassInstalledForViewer($class, $viewer)) { + return new Aphront404Response(); + } + + $this->setUser($user); + $title = array(pht('Recent Revisions'), $user->getUsername()); + $header = $this->buildProfileHeader(); + $commits = $this->buildRevisionsView($user); + + $crumbs = $this->buildApplicationCrumbs(); + $crumbs->addTextCrumb(pht('Recent Revisions')); + $crumbs->setBorder(true); + + $nav = $this->getProfileMenu(); + $nav->selectFilter(PhabricatorPeopleProfileMenuEngine::ITEM_REVISIONS); + + $view = id(new PHUITwoColumnView()) + ->setHeader($header) + ->addClass('project-view-home') + ->addClass('project-view-people-home') + ->setFooter(array( + $commits, + )); + + return $this->newPage() + ->setTitle($title) + ->setCrumbs($crumbs) + ->setNavigation($nav) + ->appendChild($view); + } + + private function buildRevisionsView(PhabricatorUser $user) { + $viewer = $this->getViewer(); + + $revisions = id(new DifferentialRevisionQuery()) + ->setViewer($viewer) + ->withAuthors(array($user->getPHID())) + ->needFlags(true) + ->needDrafts(true) + ->needReviewers(true) + ->setLimit(100) + ->execute(); + + $list = id(new DifferentialRevisionListView()) + ->setUser($viewer) + ->setNoBox(true) + ->setRevisions($revisions) + ->setNoDataString(pht('No recent revisions.')); + + $object_phids = $list->getRequiredHandlePHIDs(); + $handles = $this->loadViewerHandles($object_phids); + $list->setHandles($handles); + + $view = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Recent Revisions')) + ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) + ->appendChild($list); + + return $view; + } +} diff --git a/src/applications/people/controller/PhabricatorPeopleProfileTasksController.php b/src/applications/people/controller/PhabricatorPeopleProfileTasksController.php index 52c88d89fa..47cc605bb8 100644 --- a/src/applications/people/controller/PhabricatorPeopleProfileTasksController.php +++ b/src/applications/people/controller/PhabricatorPeopleProfileTasksController.php @@ -13,10 +13,6 @@ final class PhabricatorPeopleProfileTasksController ->needProfile(true) ->needProfileImage(true) ->needAvailability(true) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - )) ->executeOne(); if (!$user) { return new Aphront404Response(); diff --git a/src/applications/people/controller/PhabricatorPeopleProfileViewController.php b/src/applications/people/controller/PhabricatorPeopleProfileViewController.php index 9db106081d..5dce872c4b 100644 --- a/src/applications/people/controller/PhabricatorPeopleProfileViewController.php +++ b/src/applications/people/controller/PhabricatorPeopleProfileViewController.php @@ -28,8 +28,21 @@ final class PhabricatorPeopleProfileViewController $name = $user->getUsername(); $feed = $this->buildPeopleFeed($user, $viewer); + + $view_all = id(new PHUIButtonView()) + ->setTag('a') + ->setIcon( + id(new PHUIIconView()) + ->setIcon('fa-list-ul')) + ->setText(pht('View All')) + ->setHref('/feed/?userPHIDs='.$user->getPHID()); + + $feed_header = id(new PHUIHeaderView()) + ->setHeader(pht('Recent Activity')) + ->addActionLink($view_all); + $feed = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Recent Activity')) + ->setHeader($feed_header) ->addClass('project-view-feed') ->appendChild($feed); @@ -229,7 +242,7 @@ final class PhabricatorPeopleProfileViewController $viewer) { $query = new PhabricatorFeedQuery(); - $query->setFilterPHIDs( + $query->withFilterPHIDs( array( $user->getPHID(), )); diff --git a/src/applications/people/engine/PhabricatorPeopleProfileMenuEngine.php b/src/applications/people/engine/PhabricatorPeopleProfileMenuEngine.php index e5f25eb4a0..6d6d239f5b 100644 --- a/src/applications/people/engine/PhabricatorPeopleProfileMenuEngine.php +++ b/src/applications/people/engine/PhabricatorPeopleProfileMenuEngine.php @@ -9,6 +9,7 @@ final class PhabricatorPeopleProfileMenuEngine const ITEM_BADGES = 'people.badges'; const ITEM_TASKS = 'people.tasks'; const ITEM_COMMITS = 'people.commits'; + const ITEM_REVISIONS = 'people.revisions'; protected function isMenuEngineConfigurable() { return false; @@ -52,6 +53,16 @@ final class PhabricatorPeopleProfileMenuEngine ->setMenuItemKey(PhabricatorPeopleTasksProfileMenuItem::MENUITEMKEY); } + $have_differential = PhabricatorApplication::isClassInstalledForViewer( + 'PhabricatorDifferentialApplication', + $viewer); + if ($have_differential) { + $items[] = $this->newItem() + ->setBuiltinKey(self::ITEM_REVISIONS) + ->setMenuItemKey( + PhabricatorPeopleRevisionsProfileMenuItem::MENUITEMKEY); + } + $have_diffusion = PhabricatorApplication::isClassInstalledForViewer( 'PhabricatorDiffusionApplication', $viewer); diff --git a/src/applications/people/menuitem/PhabricatorPeopleRevisionsProfileMenuItem.php b/src/applications/people/menuitem/PhabricatorPeopleRevisionsProfileMenuItem.php new file mode 100644 index 0000000000..499fc1d7f4 --- /dev/null +++ b/src/applications/people/menuitem/PhabricatorPeopleRevisionsProfileMenuItem.php @@ -0,0 +1,59 @@ +getMenuItemProperty('name'); + + if (strlen($name)) { + return $name; + } + + return $this->getDefaultName(); + } + + public function buildEditEngineFields( + PhabricatorProfileMenuItemConfiguration $config) { + return array( + id(new PhabricatorTextEditField()) + ->setKey('name') + ->setLabel(pht('Name')) + ->setPlaceholder($this->getDefaultName()) + ->setValue($config->getMenuItemProperty('name')), + ); + } + + protected function newNavigationMenuItems( + PhabricatorProfileMenuItemConfiguration $config) { + + $user = $config->getProfileObject(); + $id = $user->getID(); + + $item = $this->newItem() + ->setHref("/people/revisions/{$id}/") + ->setName($this->getDisplayName($config)) + ->setIcon('fa-gear'); + + return array( + $item, + ); + } + +} diff --git a/src/applications/people/view/PhabricatorUserCardView.php b/src/applications/people/view/PhabricatorUserCardView.php index 4f4f15a33d..f1fc515f88 100644 --- a/src/applications/people/view/PhabricatorUserCardView.php +++ b/src/applications/people/view/PhabricatorUserCardView.php @@ -85,7 +85,7 @@ final class PhabricatorUserCardView extends AphrontTagView { ->setType(PHUITagView::TYPE_SHADE); if ($tag_shade !== null) { - $tag->setShade($tag_shade); + $tag->setColor($tag_shade); } $body = array(); diff --git a/src/applications/phame/query/PhameBlogSearchEngine.php b/src/applications/phame/query/PhameBlogSearchEngine.php index d006745780..3d23a9763d 100644 --- a/src/applications/phame/query/PhameBlogSearchEngine.php +++ b/src/applications/phame/query/PhameBlogSearchEngine.php @@ -97,8 +97,9 @@ final class PhameBlogSearchEngine $button = id(new PHUIButtonView()) ->setTag('a') ->setText('New Post') - ->setHref($this->getApplicationURI('/post/edit/?blog='.$id)); - $item->setLaunchButton($button); + ->setHref($this->getApplicationURI('/post/edit/?blog='.$id)) + ->setColor(PHUIButtonView::SIMPLE); + $item->setSideColumn($button); } $list->addItem($item); diff --git a/src/applications/phid/PhabricatorObjectHandle.php b/src/applications/phid/PhabricatorObjectHandle.php index 49255fd9e8..1e6812b53b 100644 --- a/src/applications/phid/PhabricatorObjectHandle.php +++ b/src/applications/phid/PhabricatorObjectHandle.php @@ -407,8 +407,8 @@ final class PhabricatorObjectHandle public function renderTag() { return id(new PHUITagView()) - ->setType(PHUITagView::TYPE_OBJECT) - ->setShade($this->getTagColor()) + ->setType(PHUITagView::TYPE_SHADE) + ->setColor($this->getTagColor()) ->setIcon($this->getIcon()) ->setHref($this->getURI()) ->setName($this->getLinkName()); diff --git a/src/applications/phid/view/PHUIHandleTagListView.php b/src/applications/phid/view/PHUIHandleTagListView.php index c4ef3762c3..abae359a16 100644 --- a/src/applications/phid/view/PHUIHandleTagListView.php +++ b/src/applications/phid/view/PHUIHandleTagListView.php @@ -121,8 +121,8 @@ final class PHUIHandleTagListView extends AphrontTagView { private function newPlaceholderTag() { return id(new PHUITagView()) - ->setType(PHUITagView::TYPE_OBJECT) - ->setShade(PHUITagView::COLOR_DISABLED) + ->setType(PHUITagView::TYPE_SHADE) + ->setColor(PHUITagView::COLOR_DISABLED) ->setSlimShady($this->slim); } diff --git a/src/applications/phriction/conduit/PhrictionCreateConduitAPIMethod.php b/src/applications/phriction/conduit/PhrictionCreateConduitAPIMethod.php index 4b05874410..fc28b53b66 100644 --- a/src/applications/phriction/conduit/PhrictionCreateConduitAPIMethod.php +++ b/src/applications/phriction/conduit/PhrictionCreateConduitAPIMethod.php @@ -50,7 +50,7 @@ final class PhrictionCreateConduitAPIMethod extends PhrictionConduitAPIMethod { ->setTransactionType(PhrictionDocumentTitleTransaction::TRANSACTIONTYPE) ->setNewValue($request->getValue('title')); $xactions[] = id(new PhrictionTransaction()) - ->setTransactionType(PhrictionTransaction::TYPE_CONTENT) + ->setTransactionType(PhrictionDocumentContentTransaction::TRANSACTIONTYPE) ->setNewValue($request->getValue('content')); $editor = id(new PhrictionTransactionEditor()) diff --git a/src/applications/phriction/conduit/PhrictionEditConduitAPIMethod.php b/src/applications/phriction/conduit/PhrictionEditConduitAPIMethod.php index d4c2b63b5a..70c02d376a 100644 --- a/src/applications/phriction/conduit/PhrictionEditConduitAPIMethod.php +++ b/src/applications/phriction/conduit/PhrictionEditConduitAPIMethod.php @@ -45,7 +45,7 @@ final class PhrictionEditConduitAPIMethod extends PhrictionConduitAPIMethod { ->setTransactionType(PhrictionDocumentTitleTransaction::TRANSACTIONTYPE) ->setNewValue($request->getValue('title')); $xactions[] = id(new PhrictionTransaction()) - ->setTransactionType(PhrictionTransaction::TYPE_CONTENT) + ->setTransactionType(PhrictionDocumentContentTransaction::TRANSACTIONTYPE) ->setNewValue($request->getValue('content')); $editor = id(new PhrictionTransactionEditor()) diff --git a/src/applications/phriction/controller/PhrictionDeleteController.php b/src/applications/phriction/controller/PhrictionDeleteController.php index 58dc06a65b..b061e89756 100644 --- a/src/applications/phriction/controller/PhrictionDeleteController.php +++ b/src/applications/phriction/controller/PhrictionDeleteController.php @@ -26,7 +26,8 @@ final class PhrictionDeleteController extends PhrictionController { if ($request->isFormPost()) { $xactions = array(); $xactions[] = id(new PhrictionTransaction()) - ->setTransactionType(PhrictionTransaction::TYPE_DELETE) + ->setTransactionType( + PhrictionDocumentDeleteTransaction::TRANSACTIONTYPE) ->setNewValue(true); $editor = id(new PhrictionTransactionEditor()) diff --git a/src/applications/phriction/controller/PhrictionEditController.php b/src/applications/phriction/controller/PhrictionEditController.php index 1917348c24..17d11fdf3c 100644 --- a/src/applications/phriction/controller/PhrictionEditController.php +++ b/src/applications/phriction/controller/PhrictionEditController.php @@ -136,7 +136,8 @@ final class PhrictionEditController ->setTransactionType(PhrictionDocumentTitleTransaction::TRANSACTIONTYPE) ->setNewValue($title); $xactions[] = id(new PhrictionTransaction()) - ->setTransactionType(PhrictionTransaction::TYPE_CONTENT) + ->setTransactionType( + PhrictionDocumentContentTransaction::TRANSACTIONTYPE) ->setNewValue($content_text); $xactions[] = id(new PhrictionTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY) @@ -178,7 +179,8 @@ final class PhrictionEditController PhrictionDocumentTitleTransaction::TRANSACTIONTYPE), true); $e_content = nonempty( - $ex->getShortMessage(PhrictionTransaction::TYPE_CONTENT), + $ex->getShortMessage( + PhrictionDocumentContentTransaction::TRANSACTIONTYPE), true); // if we're not supposed to process the content version error, then diff --git a/src/applications/phriction/editor/PhrictionTransactionEditor.php b/src/applications/phriction/editor/PhrictionTransactionEditor.php index 9ab91d5db9..1920b4c718 100644 --- a/src/applications/phriction/editor/PhrictionTransactionEditor.php +++ b/src/applications/phriction/editor/PhrictionTransactionEditor.php @@ -85,10 +85,6 @@ final class PhrictionTransactionEditor public function getTransactionTypes() { $types = parent::getTransactionTypes(); - $types[] = PhrictionTransaction::TYPE_CONTENT; - $types[] = PhrictionTransaction::TYPE_DELETE; - $types[] = PhrictionTransaction::TYPE_MOVE_AWAY; - $types[] = PhabricatorTransactions::TYPE_EDGE; $types[] = PhabricatorTransactions::TYPE_COMMENT; $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; @@ -97,54 +93,18 @@ final class PhrictionTransactionEditor return $types; } - protected function getCustomTransactionOldValue( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case PhrictionTransaction::TYPE_CONTENT: - if ($this->getIsNewObject()) { - return null; - } - return $this->getOldContent()->getContent(); - case PhrictionTransaction::TYPE_DELETE: - case PhrictionTransaction::TYPE_MOVE_AWAY: - return null; - } - } - - protected function getCustomTransactionNewValue( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case PhrictionTransaction::TYPE_CONTENT: - case PhrictionTransaction::TYPE_DELETE: - return $xaction->getNewValue(); - case PhrictionTransaction::TYPE_MOVE_AWAY: - $document = $xaction->getNewValue(); - $dict = array( - 'id' => $document->getID(), - 'phid' => $document->getPHID(), - 'content' => $document->getContent()->getContent(), - 'title' => $document->getContent()->getTitle(), - ); - return $dict; - } - } - protected function shouldApplyInitialEffects( PhabricatorLiskDAO $object, array $xactions) { foreach ($xactions as $xaction) { switch ($xaction->getTransactionType()) { - case PhrictionDocumentTitleTransaction::TRANSACTIONTYPE: - case PhrictionTransaction::TYPE_CONTENT: - case PhrictionTransaction::TYPE_DELETE: - case PhrictionDocumentMoveToTransaction::TRANSACTIONTYPE: - case PhrictionTransaction::TYPE_MOVE_AWAY: - return true; + case PhrictionDocumentTitleTransaction::TRANSACTIONTYPE: + case PhrictionDocumentContentTransaction::TRANSACTIONTYPE: + case PhrictionDocumentDeleteTransaction::TRANSACTIONTYPE: + case PhrictionDocumentMoveToTransaction::TRANSACTIONTYPE: + case PhrictionDocumentMoveAwayTransaction::TRANSACTIONTYPE: + return true; } } return parent::shouldApplyInitialEffects($object, $xactions); @@ -158,37 +118,21 @@ final class PhrictionTransactionEditor $this->setNewContent($this->buildNewContentTemplate($object)); } - protected function applyCustomInternalTransaction( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case PhrictionTransaction::TYPE_CONTENT: - $object->setStatus(PhrictionDocumentStatus::STATUS_EXISTS); - return; - case PhrictionTransaction::TYPE_MOVE_AWAY: - $object->setStatus(PhrictionDocumentStatus::STATUS_MOVED); - return; - case PhrictionTransaction::TYPE_DELETE: - $object->setStatus(PhrictionDocumentStatus::STATUS_DELETED); - return; - } - } - protected function expandTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { $xactions = parent::expandTransaction($object, $xaction); switch ($xaction->getTransactionType()) { - case PhrictionTransaction::TYPE_CONTENT: + case PhrictionDocumentContentTransaction::TRANSACTIONTYPE: if ($this->getIsNewObject()) { break; } $content = $xaction->getNewValue(); if ($content === '') { $xactions[] = id(new PhrictionTransaction()) - ->setTransactionType(PhrictionTransaction::TYPE_DELETE) + ->setTransactionType( + PhrictionDocumentDeleteTransaction::TRANSACTIONTYPE) ->setNewValue(true) ->setMetadataValue('contentDelete', true); } @@ -210,31 +154,6 @@ final class PhrictionTransactionEditor return $xactions; } - protected function applyCustomExternalTransaction( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case PhrictionTransaction::TYPE_CONTENT: - $this->getNewContent()->setContent($xaction->getNewValue()); - break; - case PhrictionTransaction::TYPE_DELETE: - $this->getNewContent()->setContent(''); - $this->getNewContent()->setChangeType( - PhrictionChangeType::CHANGE_DELETE); - break; - case PhrictionTransaction::TYPE_MOVE_AWAY: - $dict = $xaction->getNewValue(); - $this->getNewContent()->setContent(''); - $this->getNewContent()->setChangeType( - PhrictionChangeType::CHANGE_MOVE_AWAY); - $this->getNewContent()->setChangeRef($dict['id']); - break; - default: - break; - } - } - protected function applyFinalEffects( PhabricatorLiskDAO $object, array $xactions) { @@ -244,9 +163,9 @@ final class PhrictionTransactionEditor switch ($xaction->getTransactionType()) { case PhrictionDocumentTitleTransaction::TRANSACTIONTYPE: case PhrictionDocumentMoveToTransaction::TRANSACTIONTYPE: - case PhrictionTransaction::TYPE_CONTENT: - case PhrictionTransaction::TYPE_DELETE: - case PhrictionTransaction::TYPE_MOVE_AWAY: + case PhrictionDocumentMoveAwayTransaction::TRANSACTIONTYPE: + case PhrictionDocumentDeleteTransaction::TRANSACTIONTYPE: + case PhrictionDocumentContentTransaction::TRANSACTIONTYPE: $save_content = true; break; default: @@ -289,7 +208,8 @@ final class PhrictionTransactionEditor ->setNewValue(PhabricatorSlug::getDefaultTitle($slug)) ->setMetadataValue('stub:create:phid', $object->getPHID()); $stub_xactions[] = id(new PhrictionTransaction()) - ->setTransactionType(PhrictionTransaction::TYPE_CONTENT) + ->setTransactionType( + PhrictionDocumentContentTransaction::TRANSACTIONTYPE) ->setNewValue('') ->setMetadataValue('stub:create:phid', $object->getPHID()); $stub_xactions[] = id(new PhrictionTransaction()) @@ -313,7 +233,8 @@ final class PhrictionTransactionEditor if ($this->moveAwayDocument !== null) { $move_away_xactions = array(); $move_away_xactions[] = id(new PhrictionTransaction()) - ->setTransactionType(PhrictionTransaction::TYPE_MOVE_AWAY) + ->setTransactionType( + PhrictionDocumentMoveAwayTransaction::TRANSACTIONTYPE) ->setNewValue($object); $sub_editor = id(new PhrictionTransactionEditor()) ->setActor($this->getActor()) @@ -326,7 +247,7 @@ final class PhrictionTransactionEditor // Compute the content diff URI for the publishing phase. foreach ($xactions as $xaction) { switch ($xaction->getTransactionType()) { - case PhrictionTransaction::TYPE_CONTENT: + case PhrictionDocumentContentTransaction::TRANSACTIONTYPE: $uri = id(new PhutilURI('/phriction/diff/'.$object->getID().'/')) ->alter('l', $this->getOldContent()->getVersion()) ->alter('r', $this->getNewContent()->getVersion()); @@ -450,29 +371,12 @@ final class PhrictionTransactionEditor foreach ($xactions as $xaction) { switch ($type) { - case PhrictionTransaction::TYPE_CONTENT: + case PhrictionDocumentContentTransaction::TRANSACTIONTYPE: if ($xaction->getMetadataValue('stub:create:phid')) { continue; } - $missing = false; - if ($this->getIsNewObject()) { - $content = $object->getContent()->getContent(); - $missing = $this->validateIsEmptyTextField( - $content, - $xactions); - } - - if ($missing) { - $error = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Required'), - pht('Document content is required.'), - nonempty(last($xactions), null)); - - $error->setIsMissingFieldError(true); - $errors[] = $error; - } else if ($this->getProcessContentVersionError()) { + if ($this->getProcessContentVersionError()) { $error = $this->validateContentVersion($object, $type, $xaction); if ($error) { $this->setProcessContentVersionError(false); @@ -490,7 +394,6 @@ final class PhrictionTransactionEditor $errors = array_merge($errors, $ancestry_errors); } } - break; case PhrictionDocumentMoveToTransaction::TRANSACTIONTYPE: @@ -536,35 +439,6 @@ final class PhrictionTransactionEditor } break; - case PhrictionTransaction::TYPE_DELETE: - switch ($object->getStatus()) { - case PhrictionDocumentStatus::STATUS_DELETED: - if ($xaction->getMetadataValue('contentDelete')) { - $e_text = pht( - 'This document is already deleted. You must specify '. - 'content to re-create the document and make further edits.'); - } else { - $e_text = pht( - 'An already deleted document can not be deleted.'); - } - break; - case PhrictionDocumentStatus::STATUS_MOVED: - $e_text = pht('A moved document can not be deleted.'); - break; - case PhrictionDocumentStatus::STATUS_STUB: - $e_text = pht('A stub document can not be deleted.'); - break; - default: - break 2; - } - - $error = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Can not delete document.'), - $e_text, - $xaction); - $errors[] = $error; - break; } } diff --git a/src/applications/phriction/storage/PhrictionTransaction.php b/src/applications/phriction/storage/PhrictionTransaction.php index d3d78ff665..2ce9d38a08 100644 --- a/src/applications/phriction/storage/PhrictionTransaction.php +++ b/src/applications/phriction/storage/PhrictionTransaction.php @@ -3,10 +3,6 @@ final class PhrictionTransaction extends PhabricatorModularTransaction { - const TYPE_CONTENT = 'content'; - const TYPE_DELETE = 'delete'; - const TYPE_MOVE_AWAY = 'move-away'; - const MAILTAG_TITLE = 'phriction-title'; const MAILTAG_CONTENT = 'phriction-content'; const MAILTAG_DELETE = 'phriction-delete'; @@ -34,7 +30,7 @@ final class PhrictionTransaction $new = $this->getNewValue(); switch ($this->getTransactionType()) { case PhrictionDocumentMoveToTransaction::TRANSACTIONTYPE: - case self::TYPE_MOVE_AWAY: + case PhrictionDocumentMoveAwayTransaction::TRANSACTIONTYPE: $phids[] = $new['phid']; break; case PhrictionDocumentTitleTransaction::TRANSACTIONTYPE: @@ -47,36 +43,10 @@ final class PhrictionTransaction return $phids; } - public function getRemarkupBlocks() { - $blocks = parent::getRemarkupBlocks(); - - switch ($this->getTransactionType()) { - case self::TYPE_CONTENT: - $blocks[] = $this->getNewValue(); - break; - } - - return $blocks; - } - - public function shouldHide() { - switch ($this->getTransactionType()) { - case self::TYPE_CONTENT: - if ($this->getOldValue() === null) { - return true; - } else { - return false; - } - break; - } - - return parent::shouldHide(); - } - public function shouldHideForMail(array $xactions) { switch ($this->getTransactionType()) { case PhrictionDocumentMoveToTransaction::TRANSACTIONTYPE: - case self::TYPE_MOVE_AWAY: + case PhrictionDocumentMoveAwayTransaction::TRANSACTIONTYPE: return true; case PhrictionDocumentTitleTransaction::TRANSACTIONTYPE: return $this->getMetadataValue('stub:create:phid', false); @@ -87,7 +57,7 @@ final class PhrictionTransaction public function shouldHideForFeed() { switch ($this->getTransactionType()) { case PhrictionDocumentMoveToTransaction::TRANSACTIONTYPE: - case self::TYPE_MOVE_AWAY: + case PhrictionDocumentMoveAwayTransaction::TRANSACTIONTYPE: return true; case PhrictionDocumentTitleTransaction::TRANSACTIONTYPE: return $this->getMetadataValue('stub:create:phid', false); @@ -95,119 +65,6 @@ final class PhrictionTransaction return parent::shouldHideForFeed(); } - public function getActionStrength() { - switch ($this->getTransactionType()) { - case self::TYPE_CONTENT: - return 1.3; - case self::TYPE_DELETE: - return 1.5; - case self::TYPE_MOVE_AWAY: - return 1.0; - } - - return parent::getActionStrength(); - } - - public function getActionName() { - $old = $this->getOldValue(); - $new = $this->getNewValue(); - - switch ($this->getTransactionType()) { - case self::TYPE_CONTENT: - return pht('Edited'); - case self::TYPE_DELETE: - return pht('Deleted'); - case self::TYPE_MOVE_AWAY: - return pht('Moved Away'); - } - - return parent::getActionName(); - } - - public function getIcon() { - $old = $this->getOldValue(); - $new = $this->getNewValue(); - - switch ($this->getTransactionType()) { - case self::TYPE_CONTENT: - return 'fa-pencil'; - case self::TYPE_DELETE: - return 'fa-times'; - case self::TYPE_MOVE_AWAY: - return 'fa-arrows'; - } - - return parent::getIcon(); - } - - - public function getTitle() { - $author_phid = $this->getAuthorPHID(); - - $old = $this->getOldValue(); - $new = $this->getNewValue(); - - switch ($this->getTransactionType()) { - case self::TYPE_CONTENT: - return pht( - '%s edited the document content.', - $this->renderHandleLink($author_phid)); - - case self::TYPE_DELETE: - return pht( - '%s deleted this document.', - $this->renderHandleLink($author_phid)); - - case self::TYPE_MOVE_AWAY: - return pht( - '%s moved this document to %s', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($new['phid'])); - - } - - return parent::getTitle(); - } - - public function getTitleForFeed() { - $author_phid = $this->getAuthorPHID(); - $object_phid = $this->getObjectPHID(); - - $old = $this->getOldValue(); - $new = $this->getNewValue(); - - switch ($this->getTransactionType()) { - - case self::TYPE_CONTENT: - return pht( - '%s edited the content of %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid)); - - case self::TYPE_DELETE: - return pht( - '%s deleted %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid)); - - } - return parent::getTitleForFeed(); - } - - public function hasChangeDetails() { - switch ($this->getTransactionType()) { - case self::TYPE_CONTENT: - return true; - } - return parent::hasChangeDetails(); - } - - public function renderChangeDetails(PhabricatorUser $viewer) { - return $this->renderTextCorpusChangeDetails( - $viewer, - $this->getOldValue(), - $this->getNewValue()); - } public function getMailTags() { $tags = array(); @@ -215,10 +72,10 @@ final class PhrictionTransaction case PhrictionDocumentTitleTransaction::TRANSACTIONTYPE: $tags[] = self::MAILTAG_TITLE; break; - case self::TYPE_CONTENT: + case PhrictionDocumentContentTransaction::TRANSACTIONTYPE: $tags[] = self::MAILTAG_CONTENT; break; - case self::TYPE_DELETE: + case PhrictionDocumentDeleteTransaction::TRANSACTIONTYPE: $tags[] = self::MAILTAG_DELETE; break; case PhabricatorTransactions::TYPE_SUBSCRIBERS: diff --git a/src/applications/phriction/xaction/PhrictionDocumentContentTransaction.php b/src/applications/phriction/xaction/PhrictionDocumentContentTransaction.php new file mode 100644 index 0000000000..a051546824 --- /dev/null +++ b/src/applications/phriction/xaction/PhrictionDocumentContentTransaction.php @@ -0,0 +1,95 @@ +getEditor()->getIsNewObject()) { + return null; + } + return $object->getContent()->getContent(); + } + + public function generateNewValue($object, $value) { + return $value; + } + + public function applyInternalEffects($object, $value) { + $object->setStatus(PhrictionDocumentStatus::STATUS_EXISTS); + } + + public function applyExternalEffects($object, $value) { + $this->getEditor()->getNewContent()->setContent($value); + } + + public function shouldHide() { + if ($this->getOldValue() === null) { + return true; + } else { + return false; + } + } + + public function getActionStrength() { + return 1.3; + } + + public function getActionName() { + return pht('Edited'); + } + + public function getTitle() { + return pht( + '%s edited the content of this document.', + $this->renderAuthor()); + } + + public function getTitleForFeed() { + return pht( + '%s edited the content of %s.', + $this->renderAuthor(), + $this->renderObject()); + } + + public function hasChangeDetailView() { + return true; + } + + public function getMailDiffSectionHeader() { + return pht('CHANGES TO DOCUMENT CONTENT'); + } + + public function newChangeDetailView() { + $viewer = $this->getViewer(); + + return id(new PhabricatorApplicationTransactionTextDiffDetailView()) + ->setViewer($viewer) + ->setOldText($this->getOldValue()) + ->setNewText($this->getNewValue()); + } + + public function newRemarkupChanges() { + $changes = array(); + + $changes[] = $this->newRemarkupChange() + ->setOldValue($this->getOldValue()) + ->setNewValue($this->getNewValue()); + + return $changes; + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + $content = $object->getContent()->getContent(); + if ($this->isEmptyTextTransaction($content, $xactions)) { + $errors[] = $this->newRequiredError( + pht('Documents must have content.')); + } + + return $errors; + } + +} diff --git a/src/applications/phriction/xaction/PhrictionDocumentDeleteTransaction.php b/src/applications/phriction/xaction/PhrictionDocumentDeleteTransaction.php new file mode 100644 index 0000000000..25cc7c2b28 --- /dev/null +++ b/src/applications/phriction/xaction/PhrictionDocumentDeleteTransaction.php @@ -0,0 +1,86 @@ +setStatus(PhrictionDocumentStatus::STATUS_DELETED); + } + + public function applyExternalEffects($object, $value) { + $this->getEditor()->getNewContent()->setContent(''); + $this->getEditor()->getNewContent()->setChangeType( + PhrictionChangeType::CHANGE_DELETE); + } + + public function getActionStrength() { + return 1.5; + } + + public function getActionName() { + return pht('Deleted'); + } + + public function getTitle() { + return pht( + '%s deleted this document.', + $this->renderAuthor()); + } + + public function getTitleForFeed() { + return pht( + '%s deleted %s.', + $this->renderAuthor(), + $this->renderObject()); + } + + public function validateTransactions($object, array $xactions) { + $errors = array(); + + $e_text = null; + foreach ($xactions as $xaction) { + switch ($object->getStatus()) { + case PhrictionDocumentStatus::STATUS_DELETED: + if ($xaction->getMetadataValue('contentDelete')) { + $e_text = pht( + 'This document is already deleted. You must specify '. + 'content to re-create the document and make further edits.'); + } else { + $e_text = pht( + 'An already deleted document can not be deleted.'); + } + break; + case PhrictionDocumentStatus::STATUS_MOVED: + $e_text = pht('A moved document can not be deleted.'); + break; + case PhrictionDocumentStatus::STATUS_STUB: + $e_text = pht('A stub document can not be deleted.'); + break; + default: + break; + } + + if ($e_text !== null) { + $errors[] = $this->newInvalidError($e_text); + } + + } + + return $errors; + } + + public function getIcon() { + return 'fa-trash-o'; + } + + public function getColor() { + return 'red'; + } + +} diff --git a/src/applications/phriction/xaction/PhrictionDocumentMoveAwayTransaction.php b/src/applications/phriction/xaction/PhrictionDocumentMoveAwayTransaction.php new file mode 100644 index 0000000000..c827f7337d --- /dev/null +++ b/src/applications/phriction/xaction/PhrictionDocumentMoveAwayTransaction.php @@ -0,0 +1,62 @@ + $document->getID(), + 'phid' => $document->getPHID(), + 'content' => $document->getContent()->getContent(), + 'title' => $document->getContent()->getTitle(), + ); + return $dict; + } + + public function applyInternalEffects($object, $value) { + $object->setStatus(PhrictionDocumentStatus::STATUS_MOVED); + } + + public function applyExternalEffects($object, $value) { + $dict = $value; + $this->getEditor()->getNewContent()->setContent(''); + $this->getEditor()->getNewContent()->setChangeType( + PhrictionChangeType::CHANGE_MOVE_AWAY); + $this->getEditor()->getNewContent()->setChangeRef($dict['id']); + } + + public function getActionName() { + return pht('Moved Away'); + } + + public function getTitle() { + $new = $this->getNewValue(); + + return pht( + '%s moved this document to %s', + $this->renderAuthor(), + $this->renderHandleLink($new['phid'])); + } + + public function getTitleForFeed() { + $new = $this->getNewValue(); + + return pht( + '%s moved %s to %s', + $this->renderAuthor(), + $this->renderObject(), + $this->renderHandleLink($new['phid'])); + } + + public function getIcon() { + return 'fa-arrows'; + } + +} diff --git a/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php b/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php index 0bec2fe53c..d9356357b3 100644 --- a/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php +++ b/src/applications/project/__tests__/PhabricatorProjectCoreTestCase.php @@ -1447,11 +1447,13 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase { if ($parent) { if ($is_milestone) { $xactions[] = id(new PhabricatorProjectTransaction()) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_MILESTONE) + ->setTransactionType( + PhabricatorProjectMilestoneTransaction::TRANSACTIONTYPE) ->setNewValue($parent->getPHID()); } else { $xactions[] = id(new PhabricatorProjectTransaction()) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_PARENT) + ->setTransactionType( + PhabricatorProjectParentTransaction::TRANSACTIONTYPE) ->setNewValue($parent->getPHID()); } } diff --git a/src/applications/project/controller/PhabricatorProjectBoardBackgroundController.php b/src/applications/project/controller/PhabricatorProjectBoardBackgroundController.php index 99260d1770..be4049bb73 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardBackgroundController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardBackgroundController.php @@ -37,7 +37,8 @@ final class PhabricatorProjectBoardBackgroundController $xactions = array(); $xactions[] = id(new PhabricatorProjectTransaction()) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_BACKGROUND) + ->setTransactionType( + PhabricatorProjectWorkboardBackgroundTransaction::TRANSACTIONTYPE) ->setNewValue($background_key); id(new PhabricatorProjectTransactionEditor()) diff --git a/src/applications/project/controller/PhabricatorProjectBoardDisableController.php b/src/applications/project/controller/PhabricatorProjectBoardDisableController.php index 0440ff9eff..fc850640f1 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardDisableController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardDisableController.php @@ -33,7 +33,8 @@ final class PhabricatorProjectBoardDisableController $xactions = array(); $xactions[] = id(new PhabricatorProjectTransaction()) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_HASWORKBOARD) + ->setTransactionType( + PhabricatorProjectWorkboardTransaction::TRANSACTIONTYPE) ->setNewValue(0); id(new PhabricatorProjectTransactionEditor()) diff --git a/src/applications/project/controller/PhabricatorProjectBoardImportController.php b/src/applications/project/controller/PhabricatorProjectBoardImportController.php index 988084d3ee..c344bc0af0 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardImportController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardImportController.php @@ -61,8 +61,18 @@ final class PhabricatorProjectBoardImportController ->setProperties($import_column->getProperties()) ->save(); } + $xactions = array(); + $xactions[] = id(new PhabricatorProjectTransaction()) + ->setTransactionType( + PhabricatorProjectWorkboardTransaction::TRANSACTIONTYPE) + ->setNewValue(1); - $project->setHasWorkboard(1)->save(); + id(new PhabricatorProjectTransactionEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true) + ->applyTransactions($project, $xactions); $table->saveTransaction(); diff --git a/src/applications/project/controller/PhabricatorProjectBoardViewController.php b/src/applications/project/controller/PhabricatorProjectBoardViewController.php index da1f0ccb95..8405ec677f 100644 --- a/src/applications/project/controller/PhabricatorProjectBoardViewController.php +++ b/src/applications/project/controller/PhabricatorProjectBoardViewController.php @@ -331,7 +331,7 @@ final class PhabricatorProjectBoardViewController $count_tag = id(new PHUITagView()) ->setType(PHUITagView::TYPE_SHADE) - ->setShade(PHUITagView::COLOR_BLUE) + ->setColor(PHUITagView::COLOR_BLUE) ->addSigil('column-points') ->setName( javelin_tag( @@ -713,14 +713,6 @@ final class PhabricatorProjectBoardViewController ->setDisabled(!$can_edit) ->setWorkflow(true); - $background_uri = $this->getApplicationURI("board/{$id}/background/"); - $manage_items[] = id(new PhabricatorActionView()) - ->setIcon('fa-paint-brush') - ->setName(pht('Change Background Color')) - ->setHref($background_uri) - ->setDisabled(!$can_edit) - ->setWorkflow(false); - if ($show_hidden) { $hidden_uri = $this->getURIWithState() ->setQueryParam('hidden', null); @@ -738,6 +730,17 @@ final class PhabricatorProjectBoardViewController ->setName($hidden_text) ->setHref($hidden_uri); + $manage_items[] = id(new PhabricatorActionView()) + ->setType(PhabricatorActionView::TYPE_DIVIDER); + + $background_uri = $this->getApplicationURI("board/{$id}/background/"); + $manage_items[] = id(new PhabricatorActionView()) + ->setIcon('fa-paint-brush') + ->setName(pht('Change Background Color')) + ->setHref($background_uri) + ->setDisabled(!$can_edit) + ->setWorkflow(false); + $manage_uri = $this->getApplicationURI("board/{$id}/manage/"); $manage_items[] = id(new PhabricatorActionView()) ->setIcon('fa-gear') @@ -963,7 +966,18 @@ final class PhabricatorProjectBoardViewController ->setProjectPHID($project->getPHID()) ->save(); - $project->setHasWorkboard(1)->save(); + $xactions = array(); + $xactions[] = id(new PhabricatorProjectTransaction()) + ->setTransactionType( + PhabricatorProjectWorkboardTransaction::TRANSACTIONTYPE) + ->setNewValue(1); + + id(new PhabricatorProjectTransactionEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true) + ->applyTransactions($project, $xactions); return id(new AphrontRedirectResponse()) ->setURI($board_uri); @@ -1047,7 +1061,8 @@ final class PhabricatorProjectBoardViewController $xactions = array(); $xactions[] = id(new PhabricatorProjectTransaction()) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_HASWORKBOARD) + ->setTransactionType( + PhabricatorProjectWorkboardTransaction::TRANSACTIONTYPE) ->setNewValue(1); id(new PhabricatorProjectTransactionEditor()) diff --git a/src/applications/project/controller/PhabricatorProjectController.php b/src/applications/project/controller/PhabricatorProjectController.php index 45e2f193af..7c344b0b8e 100644 --- a/src/applications/project/controller/PhabricatorProjectController.php +++ b/src/applications/project/controller/PhabricatorProjectController.php @@ -166,4 +166,12 @@ abstract class PhabricatorProjectController extends PhabricatorController { ->buildResponse(); } + public function renderHashtags(array $tags) { + $result = array(); + foreach ($tags as $key => $tag) { + $result[] = '#'.$tag; + } + return implode(', ', $result); + } + } diff --git a/src/applications/project/controller/PhabricatorProjectDefaultController.php b/src/applications/project/controller/PhabricatorProjectDefaultController.php index 0246f33f43..8f42ff9736 100644 --- a/src/applications/project/controller/PhabricatorProjectDefaultController.php +++ b/src/applications/project/controller/PhabricatorProjectDefaultController.php @@ -32,7 +32,7 @@ final class PhabricatorProjectDefaultController $button = pht('Save Default Filter'); $xaction_value = $request->getStr('filter'); - $xaction_type = PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER; + $xaction_type = PhabricatorProjectFilterTransaction::TRANSACTIONTYPE; break; case 'sort': $title = pht('Set Board Default Order'); @@ -43,7 +43,7 @@ final class PhabricatorProjectDefaultController $button = pht('Save Default Order'); $xaction_value = $request->getStr('order'); - $xaction_type = PhabricatorProjectTransaction::TYPE_DEFAULT_SORT; + $xaction_type = PhabricatorProjectSortTransaction::TRANSACTIONTYPE; break; default: return new Aphront404Response(); diff --git a/src/applications/project/controller/PhabricatorProjectLockController.php b/src/applications/project/controller/PhabricatorProjectLockController.php index b9b56bde10..a746911d45 100644 --- a/src/applications/project/controller/PhabricatorProjectLockController.php +++ b/src/applications/project/controller/PhabricatorProjectLockController.php @@ -49,7 +49,8 @@ final class PhabricatorProjectLockController } $xactions[] = id(new PhabricatorProjectTransaction()) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_LOCKED) + ->setTransactionType( + PhabricatorProjectLockTransaction::TRANSACTIONTYPE) ->setNewValue($new_value); $editor = id(new PhabricatorProjectTransactionEditor()) diff --git a/src/applications/project/controller/PhabricatorProjectManageController.php b/src/applications/project/controller/PhabricatorProjectManageController.php index 17b9030658..2c76c63606 100644 --- a/src/applications/project/controller/PhabricatorProjectManageController.php +++ b/src/applications/project/controller/PhabricatorProjectManageController.php @@ -138,6 +138,12 @@ final class PhabricatorProjectManageController pht('Looks Like'), $viewer->renderHandle($project->getPHID())->setAsTag(true)); + $slugs = $project->getSlugs(); + $tags = mpull($slugs, 'getSlug'); + + $view->addProperty( + pht('Hashtags'), + $this->renderHashtags($tags)); $field_list = PhabricatorCustomField::getObjectFields( $project, @@ -147,5 +153,4 @@ final class PhabricatorProjectManageController return $view; } - } diff --git a/src/applications/project/controller/PhabricatorProjectMoveController.php b/src/applications/project/controller/PhabricatorProjectMoveController.php index 68915664cb..801acbb90e 100644 --- a/src/applications/project/controller/PhabricatorProjectMoveController.php +++ b/src/applications/project/controller/PhabricatorProjectMoveController.php @@ -148,6 +148,9 @@ final class PhabricatorProjectMoveController list($pri, $sub) = ManiphestTransactionEditor::getAdjacentSubpriority( $task, $is_after); + + // If we find a priority on the first try, don't keep going. + break; } $xactions = array(); diff --git a/src/applications/project/controller/PhabricatorProjectProfileController.php b/src/applications/project/controller/PhabricatorProjectProfileController.php index 32dbec3c0f..1f59398930 100644 --- a/src/applications/project/controller/PhabricatorProjectProfileController.php +++ b/src/applications/project/controller/PhabricatorProjectProfileController.php @@ -73,16 +73,28 @@ final class PhabricatorProjectProfileController $stories = id(new PhabricatorFeedQuery()) ->setViewer($viewer) - ->setFilterPHIDs( + ->withFilterPHIDs( array( $project->getPHID(), )) ->setLimit(50) ->execute(); + $view_all = id(new PHUIButtonView()) + ->setTag('a') + ->setIcon( + id(new PHUIIconView()) + ->setIcon('fa-list-ul')) + ->setText(pht('View All')) + ->setHref('/feed/?projectPHIDs='.$project->getPHID()); + + $feed_header = id(new PHUIHeaderView()) + ->setHeader(pht('Recent Activity')) + ->addActionLink($view_all); + $feed = $this->renderStories($stories); $feed = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Recent Activity')) + ->setHeader($feed_header) ->addClass('project-view-feed') ->appendChild($feed); @@ -135,7 +147,7 @@ final class PhabricatorProjectProfileController } $header = id(new PHUIHeaderView()) - ->setHeader(pht('Properties')); + ->setHeader(pht('Details')); $view = id(new PHUIObjectBoxView()) ->setHeader($header) diff --git a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php index 2f86afc1ac..393c4569f6 100644 --- a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php +++ b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php @@ -30,118 +30,9 @@ final class PhabricatorProjectTransactionEditor $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; $types[] = PhabricatorTransactions::TYPE_JOIN_POLICY; - $types[] = PhabricatorProjectTransaction::TYPE_LOCKED; - $types[] = PhabricatorProjectTransaction::TYPE_PARENT; - $types[] = PhabricatorProjectTransaction::TYPE_MILESTONE; - $types[] = PhabricatorProjectTransaction::TYPE_HASWORKBOARD; - $types[] = PhabricatorProjectTransaction::TYPE_DEFAULT_SORT; - $types[] = PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER; - $types[] = PhabricatorProjectTransaction::TYPE_BACKGROUND; - return $types; } - protected function getCustomTransactionOldValue( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_LOCKED: - return (int)$object->getIsMembershipLocked(); - case PhabricatorProjectTransaction::TYPE_HASWORKBOARD: - return (int)$object->getHasWorkboard(); - case PhabricatorProjectTransaction::TYPE_PARENT: - case PhabricatorProjectTransaction::TYPE_MILESTONE: - return null; - case PhabricatorProjectTransaction::TYPE_DEFAULT_SORT: - return $object->getDefaultWorkboardSort(); - case PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER: - return $object->getDefaultWorkboardFilter(); - case PhabricatorProjectTransaction::TYPE_BACKGROUND: - return $object->getWorkboardBackgroundColor(); - } - - return parent::getCustomTransactionOldValue($object, $xaction); - } - - protected function getCustomTransactionNewValue( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_LOCKED: - case PhabricatorProjectTransaction::TYPE_PARENT: - case PhabricatorProjectTransaction::TYPE_MILESTONE: - case PhabricatorProjectTransaction::TYPE_DEFAULT_SORT: - case PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER: - return $xaction->getNewValue(); - case PhabricatorProjectTransaction::TYPE_HASWORKBOARD: - return (int)$xaction->getNewValue(); - case PhabricatorProjectTransaction::TYPE_BACKGROUND: - $value = $xaction->getNewValue(); - if (!strlen($value)) { - return null; - } - return $value; - } - - return parent::getCustomTransactionNewValue($object, $xaction); - } - - protected function applyCustomInternalTransaction( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_LOCKED: - $object->setIsMembershipLocked($xaction->getNewValue()); - return; - case PhabricatorProjectTransaction::TYPE_PARENT: - $object->setParentProjectPHID($xaction->getNewValue()); - return; - case PhabricatorProjectTransaction::TYPE_MILESTONE: - $number = $object->getParentProject()->loadNextMilestoneNumber(); - $object->setMilestoneNumber($number); - $object->setParentProjectPHID($xaction->getNewValue()); - return; - case PhabricatorProjectTransaction::TYPE_HASWORKBOARD: - $object->setHasWorkboard($xaction->getNewValue()); - return; - case PhabricatorProjectTransaction::TYPE_DEFAULT_SORT: - $object->setDefaultWorkboardSort($xaction->getNewValue()); - return; - case PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER: - $object->setDefaultWorkboardFilter($xaction->getNewValue()); - return; - case PhabricatorProjectTransaction::TYPE_BACKGROUND: - $object->setWorkboardBackgroundColor($xaction->getNewValue()); - return; - } - - return parent::applyCustomInternalTransaction($object, $xaction); - } - - protected function applyCustomExternalTransaction( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - $old = $xaction->getOldValue(); - $new = $xaction->getNewValue(); - - switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_LOCKED: - case PhabricatorProjectTransaction::TYPE_PARENT: - case PhabricatorProjectTransaction::TYPE_MILESTONE: - case PhabricatorProjectTransaction::TYPE_HASWORKBOARD: - case PhabricatorProjectTransaction::TYPE_DEFAULT_SORT: - case PhabricatorProjectTransaction::TYPE_DEFAULT_FILTER: - case PhabricatorProjectTransaction::TYPE_BACKGROUND: - return; - } - - return parent::applyCustomExternalTransaction($object, $xaction); - } - protected function validateAllTransactions( PhabricatorLiskDAO $object, array $xactions) { @@ -153,8 +44,8 @@ final class PhabricatorProjectTransactionEditor $parent_xaction = null; foreach ($xactions as $xaction) { switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_PARENT: - case PhabricatorProjectTransaction::TYPE_MILESTONE: + case PhabricatorProjectParentTransaction::TRANSACTIONTYPE: + case PhabricatorProjectMilestoneTransaction::TRANSACTIONTYPE: if ($xaction->getNewValue() === null) { continue; } @@ -216,95 +107,6 @@ final class PhabricatorProjectTransactionEditor return $errors; } - protected function validateTransaction( - PhabricatorLiskDAO $object, - $type, - array $xactions) { - - $errors = parent::validateTransaction($object, $type, $xactions); - - switch ($type) { - case PhabricatorProjectTransaction::TYPE_PARENT: - case PhabricatorProjectTransaction::TYPE_MILESTONE: - if (!$xactions) { - break; - } - - $xaction = last($xactions); - - $parent_phid = $xaction->getNewValue(); - if (!$parent_phid) { - continue; - } - - if (!$this->getIsNewObject()) { - $errors[] = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Invalid'), - pht( - 'You can only set a parent or milestone project when creating a '. - 'project for the first time.'), - $xaction); - break; - } - - $projects = id(new PhabricatorProjectQuery()) - ->setViewer($this->requireActor()) - ->withPHIDs(array($parent_phid)) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->execute(); - if (!$projects) { - $errors[] = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Invalid'), - pht( - 'Parent or milestone project PHID ("%s") must be the PHID of a '. - 'valid, visible project which you have permission to edit.', - $parent_phid), - $xaction); - break; - } - - $project = head($projects); - - if ($project->isMilestone()) { - $errors[] = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Invalid'), - pht( - 'Parent or milestone project PHID ("%s") must not be a '. - 'milestone. Milestones may not have subprojects or milestones.', - $parent_phid), - $xaction); - break; - } - - $limit = PhabricatorProject::getProjectDepthLimit(); - if ($project->getProjectDepth() >= ($limit - 1)) { - $errors[] = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Invalid'), - pht( - 'You can not create a subproject or mielstone under this parent '. - 'because it would nest projects too deeply. The maximum '. - 'nesting depth of projects is %s.', - new PhutilNumber($limit)), - $xaction); - break; - } - - $object->attachParentProject($project); - break; - } - - return $errors; - } - - protected function requireCapabilities( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { @@ -320,7 +122,7 @@ final class PhabricatorProjectTransactionEditor $object, PhabricatorPolicyCapability::CAN_EDIT); return; - case PhabricatorProjectTransaction::TYPE_LOCKED: + case PhabricatorProjectLockTransaction::TRANSACTIONTYPE: PhabricatorPolicyFilter::requireCapability( $this->requireActor(), newv($this->getEditorApplicationClass(), array()), @@ -466,8 +268,8 @@ final class PhabricatorProjectTransactionEditor break; } break; - case PhabricatorProjectTransaction::TYPE_PARENT: - case PhabricatorProjectTransaction::TYPE_MILESTONE: + case PhabricatorProjectParentTransaction::TRANSACTIONTYPE: + case PhabricatorProjectMilestoneTransaction::TRANSACTIONTYPE: $materialize = true; $new_parent = $object->getParentProject(); break; @@ -642,7 +444,7 @@ final class PhabricatorProjectTransactionEditor $is_milestone = $object->isMilestone(); foreach ($xactions as $xaction) { switch ($xaction->getTransactionType()) { - case PhabricatorProjectTransaction::TYPE_MILESTONE: + case PhabricatorProjectMilestoneTransaction::TRANSACTIONTYPE: if ($xaction->getNewValue() !== null) { $is_milestone = true; } diff --git a/src/applications/project/engine/PhabricatorProjectEditEngine.php b/src/applications/project/engine/PhabricatorProjectEditEngine.php index cc377bed44..a7702c345f 100644 --- a/src/applications/project/engine/PhabricatorProjectEditEngine.php +++ b/src/applications/project/engine/PhabricatorProjectEditEngine.php @@ -202,7 +202,8 @@ final class PhabricatorProjectEditEngine pht('Choose a parent project to create a subproject beneath.')) ->setConduitTypeDescription(pht('PHID of the parent project.')) ->setAliases(array('parentPHID')) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_PARENT) + ->setTransactionType( + PhabricatorProjectParentTransaction::TRANSACTIONTYPE) ->setHandleParameterType(new AphrontPHIDHTTPParameterType()) ->setSingleValue($parent_phid) ->setIsReorderable(false) @@ -217,7 +218,8 @@ final class PhabricatorProjectEditEngine pht('Choose a parent project to create a new milestone for.')) ->setConduitTypeDescription(pht('PHID of the parent project.')) ->setAliases(array('milestonePHID')) - ->setTransactionType(PhabricatorProjectTransaction::TYPE_MILESTONE) + ->setTransactionType( + PhabricatorProjectMilestoneTransaction::TRANSACTIONTYPE) ->setHandleParameterType(new AphrontPHIDHTTPParameterType()) ->setSingleValue($milestone_phid) ->setIsReorderable(false) @@ -244,7 +246,8 @@ final class PhabricatorProjectEditEngine id(new PhabricatorIconSetEditField()) ->setKey('icon') ->setLabel(pht('Icon')) - ->setTransactionType(PhabricatorProjectIconTransaction::TRANSACTIONTYPE) + ->setTransactionType( + PhabricatorProjectIconTransaction::TRANSACTIONTYPE) ->setIconSet(new PhabricatorProjectIconSet()) ->setDescription(pht('Project icon.')) ->setConduitDescription(pht('Change the project icon.')) diff --git a/src/applications/project/engineextension/PhabricatorProjectsMembershipIndexEngineExtension.php b/src/applications/project/engineextension/PhabricatorProjectsMembershipIndexEngineExtension.php index 567f5b749e..5acfaf913a 100644 --- a/src/applications/project/engineextension/PhabricatorProjectsMembershipIndexEngineExtension.php +++ b/src/applications/project/engineextension/PhabricatorProjectsMembershipIndexEngineExtension.php @@ -34,29 +34,30 @@ final class PhabricatorProjectsMembershipIndexEngineExtension } private function materializeProject(PhabricatorProject $project) { - if ($project->isMilestone()) { - return; - } - $material_type = PhabricatorProjectMaterializedMemberEdgeType::EDGECONST; $member_type = PhabricatorProjectProjectHasMemberEdgeType::EDGECONST; $project_phid = $project->getPHID(); - $descendants = id(new PhabricatorProjectQuery()) - ->setViewer($this->getViewer()) - ->withAncestorProjectPHIDs(array($project->getPHID())) - ->withIsMilestone(false) - ->withHasSubprojects(false) - ->execute(); - $descendant_phids = mpull($descendants, 'getPHID'); - - if ($descendant_phids) { - $source_phids = $descendant_phids; - $has_subprojects = true; - } else { - $source_phids = array($project->getPHID()); + if ($project->isMilestone()) { + $source_phids = array($project->getParentProjectPHID()); $has_subprojects = false; + } else { + $descendants = id(new PhabricatorProjectQuery()) + ->setViewer($this->getViewer()) + ->withAncestorProjectPHIDs(array($project->getPHID())) + ->withIsMilestone(false) + ->withHasSubprojects(false) + ->execute(); + $descendant_phids = mpull($descendants, 'getPHID'); + + if ($descendant_phids) { + $source_phids = $descendant_phids; + $has_subprojects = true; + } else { + $source_phids = array($project->getPHID()); + $has_subprojects = false; + } } $conn_w = $project->establishConnection('w'); diff --git a/src/applications/project/query/PhabricatorProjectSearchEngine.php b/src/applications/project/query/PhabricatorProjectSearchEngine.php index df13278812..452085fa9f 100644 --- a/src/applications/project/query/PhabricatorProjectSearchEngine.php +++ b/src/applications/project/query/PhabricatorProjectSearchEngine.php @@ -211,7 +211,7 @@ final class PhabricatorProjectSearchEngine $options[$color] = array( id(new PHUITagView()) ->setType(PHUITagView::TYPE_SHADE) - ->setShade($color) + ->setColor($color) ->setName($name), ); } diff --git a/src/applications/project/storage/PhabricatorProjectTransaction.php b/src/applications/project/storage/PhabricatorProjectTransaction.php index b0d442ae57..158c2480c0 100644 --- a/src/applications/project/storage/PhabricatorProjectTransaction.php +++ b/src/applications/project/storage/PhabricatorProjectTransaction.php @@ -3,14 +3,6 @@ final class PhabricatorProjectTransaction extends PhabricatorModularTransaction { - const TYPE_LOCKED = 'project:locked'; - const TYPE_PARENT = 'project:parent'; - const TYPE_MILESTONE = 'project:milestone'; - const TYPE_HASWORKBOARD = 'project:hasworkboard'; - const TYPE_DEFAULT_SORT = 'project:sort'; - const TYPE_DEFAULT_FILTER = 'project:filter'; - const TYPE_BACKGROUND = 'project:background'; - // NOTE: This is deprecated, members are just a normal edge now. const TYPE_MEMBERS = 'project:members'; @@ -62,24 +54,12 @@ final class PhabricatorProjectTransaction return parent::shouldHide(); } - public function shouldHideForFeed() { - switch ($this->getTransactionType()) { - case self::TYPE_HASWORKBOARD: - case self::TYPE_DEFAULT_SORT: - case self::TYPE_DEFAULT_FILTER: - case self::TYPE_BACKGROUND: - return true; - } - - return parent::shouldHideForFeed(); - } - public function shouldHideForMail(array $xactions) { switch ($this->getTransactionType()) { - case self::TYPE_HASWORKBOARD: - case self::TYPE_DEFAULT_SORT: - case self::TYPE_DEFAULT_FILTER: - case self::TYPE_BACKGROUND: + case PhabricatorProjectWorkboardTransaction::TRANSACTIONTYPE: + case PhabricatorProjectSortTransaction::TRANSACTIONTYPE: + case PhabricatorProjectFilterTransaction::TRANSACTIONTYPE: + case PhabricatorProjectWorkboardBackgroundTransaction::TRANSACTIONTYPE: return true; } @@ -87,16 +67,7 @@ final class PhabricatorProjectTransaction } public function getIcon() { - $old = $this->getOldValue(); - $new = $this->getNewValue(); - switch ($this->getTransactionType()) { - case self::TYPE_LOCKED: - if ($new) { - return 'fa-lock'; - } else { - return 'fa-unlock'; - } case self::TYPE_MEMBERS: return 'fa-user'; } @@ -115,18 +86,6 @@ final class PhabricatorProjectTransaction '%s created this project.', $this->renderHandleLink($author_phid)); - case self::TYPE_LOCKED: - if ($new) { - return pht( - "%s locked this project's membership.", - $author_handle); - } else { - return pht( - "%s unlocked this project's membership.", - $author_handle); - } - break; - case self::TYPE_MEMBERS: $add = array_diff($new, $old); $rem = array_diff($old, $new); @@ -165,64 +124,11 @@ final class PhabricatorProjectTransaction } } break; - - case self::TYPE_HASWORKBOARD: - if ($new) { - return pht( - '%s enabled the workboard for this project.', - $author_handle); - } else { - return pht( - '%s disabled the workboard for this project.', - $author_handle); - } - - case self::TYPE_DEFAULT_SORT: - return pht( - '%s changed the default sort order for the project workboard.', - $author_handle); - - case self::TYPE_DEFAULT_FILTER: - return pht( - '%s changed the default filter for the project workboard.', - $author_handle); - - case self::TYPE_BACKGROUND: - return pht( - '%s changed the background color of the project workboard.', - $author_handle); } return parent::getTitle(); } - public function getTitleForFeed() { - $author_phid = $this->getAuthorPHID(); - $object_phid = $this->getObjectPHID(); - $author_handle = $this->renderHandleLink($author_phid); - $object_handle = $this->renderHandleLink($object_phid); - - $old = $this->getOldValue(); - $new = $this->getNewValue(); - - switch ($this->getTransactionType()) { - case self::TYPE_LOCKED: - if ($new) { - return pht( - '%s locked membership for %s.', - $author_handle, - $object_handle); - } else { - return pht( - '%s unlocked membership for %s.', - $author_handle, - $object_handle); - } - } - - return parent::getTitleForFeed(); - } - public function getMailTags() { $tags = array(); switch ($this->getTransactionType()) { @@ -247,7 +153,7 @@ final class PhabricatorProjectTransaction } break; case PhabricatorProjectStatusTransaction::TRANSACTIONTYPE: - case self::TYPE_LOCKED: + case PhabricatorProjectLockTransaction::TRANSACTIONTYPE: default: $tags[] = self::MAILTAG_OTHER; break; diff --git a/src/applications/project/view/ProjectBoardTaskCard.php b/src/applications/project/view/ProjectBoardTaskCard.php index ba5213a623..1a6807ec45 100644 --- a/src/applications/project/view/ProjectBoardTaskCard.php +++ b/src/applications/project/view/ProjectBoardTaskCard.php @@ -100,7 +100,7 @@ final class ProjectBoardTaskCard extends Phobject { if ($points !== null) { $points_tag = id(new PHUITagView()) ->setType(PHUITagView::TYPE_SHADE) - ->setShade(PHUITagView::COLOR_GREY) + ->setColor(PHUITagView::COLOR_GREY) ->setSlimShady(true) ->setName($points) ->addClass('phui-workcard-points'); @@ -108,6 +108,13 @@ final class ProjectBoardTaskCard extends Phobject { } } + $subtype = $task->newSubtypeObject(); + if ($subtype && $subtype->hasTagView()) { + $subtype_tag = $subtype->newTagView() + ->setSlimShady(true); + $card->addAttribute($subtype_tag); + } + if ($task->isClosed()) { $icon = ManiphestTaskStatus::getStatusIcon($task->getStatus()); $icon = id(new PHUIIconView()) diff --git a/src/applications/project/xaction/PhabricatorProjectFilterTransaction.php b/src/applications/project/xaction/PhabricatorProjectFilterTransaction.php new file mode 100644 index 0000000000..d3d94fbb74 --- /dev/null +++ b/src/applications/project/xaction/PhabricatorProjectFilterTransaction.php @@ -0,0 +1,26 @@ +getDefaultWorkboardFilter(); + } + + public function applyInternalEffects($object, $value) { + $object->setDefaultWorkboardFilter($value); + } + + public function getTitle() { + return pht( + '%s changed the default filter for the project workboard.', + $this->renderAuthor()); + } + + public function shouldHide() { + return true; + } + +} diff --git a/src/applications/project/xaction/PhabricatorProjectLockTransaction.php b/src/applications/project/xaction/PhabricatorProjectLockTransaction.php new file mode 100644 index 0000000000..42551dfb2c --- /dev/null +++ b/src/applications/project/xaction/PhabricatorProjectLockTransaction.php @@ -0,0 +1,56 @@ +getIsMembershipLocked(); + } + + public function applyInternalEffects($object, $value) { + $object->setIsMembershipLocked($value); + } + + public function getTitle() { + $new = $this->getNewValue(); + + if ($new) { + return pht( + "%s locked this project's membership.", + $this->renderAuthor()); + } else { + return pht( + "%s unlocked this project's membership.", + $this->renderAuthor()); + } + } + + public function getTitleForFeed() { + $new = $this->getNewValue(); + + if ($new) { + return pht( + '%s locked %s membership.', + $this->renderAuthor(), + $this->renderObject()); + } else { + return pht( + '%s unlocked %s membership.', + $this->renderAuthor(), + $this->renderObject()); + } + } + + public function getIcon() { + $new = $this->getNewValue(); + + if ($new) { + return 'fa-lock'; + } else { + return 'fa-unlock'; + } + } + +} diff --git a/src/applications/project/xaction/PhabricatorProjectMilestoneTransaction.php b/src/applications/project/xaction/PhabricatorProjectMilestoneTransaction.php new file mode 100644 index 0000000000..74ac77b55d --- /dev/null +++ b/src/applications/project/xaction/PhabricatorProjectMilestoneTransaction.php @@ -0,0 +1,31 @@ +setViewer($this->getActor()) + ->withPHIDs(array($parent_phid)) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->executeOne(); + + $object->attachParentProject($project); + + $number = $object->getParentProject()->loadNextMilestoneNumber(); + $object->setMilestoneNumber($number); + $object->setParentProjectPHID($value); + } + +} diff --git a/src/applications/project/xaction/PhabricatorProjectParentTransaction.php b/src/applications/project/xaction/PhabricatorProjectParentTransaction.php new file mode 100644 index 0000000000..6fc850ddde --- /dev/null +++ b/src/applications/project/xaction/PhabricatorProjectParentTransaction.php @@ -0,0 +1,29 @@ +setViewer($this->getActor()) + ->withPHIDs(array($parent_phid)) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->executeOne(); + + $object->attachParentProject($project); + + $object->setParentProjectPHID($value); + } + +} diff --git a/src/applications/project/xaction/PhabricatorProjectSortTransaction.php b/src/applications/project/xaction/PhabricatorProjectSortTransaction.php new file mode 100644 index 0000000000..f070f6dd19 --- /dev/null +++ b/src/applications/project/xaction/PhabricatorProjectSortTransaction.php @@ -0,0 +1,26 @@ +getDefaultWorkboardSort(); + } + + public function applyInternalEffects($object, $value) { + $object->setDefaultWorkboardSort($value); + } + + public function getTitle() { + return pht( + '%s changed the default sort order for the project workboard.', + $this->renderAuthor()); + } + + public function shouldHide() { + return true; + } + +} diff --git a/src/applications/project/xaction/PhabricatorProjectTypeTransaction.php b/src/applications/project/xaction/PhabricatorProjectTypeTransaction.php new file mode 100644 index 0000000000..7bba2c5289 --- /dev/null +++ b/src/applications/project/xaction/PhabricatorProjectTypeTransaction.php @@ -0,0 +1,71 @@ +getNewValue(); + if (!$parent_phid) { + return $errors; + } + + if (!$this->getEditor()->getIsNewObject()) { + $errors[] = $this->newInvalidError( + pht( + 'You can only set a parent or milestone project when creating a '. + 'project for the first time.')); + return $errors; + } + + $projects = id(new PhabricatorProjectQuery()) + ->setViewer($this->getActor()) + ->withPHIDs(array($parent_phid)) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->execute(); + if (!$projects) { + $errors[] = $this->newInvalidError( + pht( + 'Parent or milestone project PHID ("%s") must be the PHID of a '. + 'valid, visible project which you have permission to edit.', + $parent_phid)); + return $errors; + } + + $project = head($projects); + + if ($project->isMilestone()) { + $errors[] = $this->newInvalidError( + pht( + 'Parent or milestone project PHID ("%s") must not be a '. + 'milestone. Milestones may not have subprojects or milestones.', + $parent_phid)); + return $errors; + } + + $limit = PhabricatorProject::getProjectDepthLimit(); + if ($project->getProjectDepth() >= ($limit - 1)) { + $errors[] = $this->newInvalidError( + pht( + 'You can not create a subproject or milestone under this parent '. + 'because it would nest projects too deeply. The maximum '. + 'nesting depth of projects is %s.', + new PhutilNumber($limit))); + return $errors; + } + + return $errors; + } + +} diff --git a/src/applications/project/xaction/PhabricatorProjectWorkboardBackgroundTransaction.php b/src/applications/project/xaction/PhabricatorProjectWorkboardBackgroundTransaction.php new file mode 100644 index 0000000000..620959b0a2 --- /dev/null +++ b/src/applications/project/xaction/PhabricatorProjectWorkboardBackgroundTransaction.php @@ -0,0 +1,26 @@ +getWorkboardBackgroundColor(); + } + + public function applyInternalEffects($object, $value) { + $object->setWorkboardBackgroundColor($value); + } + + public function getTitle() { + return pht( + '%s changed the background color of the project workboard.', + $this->renderAuthor()); + } + + public function shouldHide() { + return true; + } + +} diff --git a/src/applications/project/xaction/PhabricatorProjectWorkboardTransaction.php b/src/applications/project/xaction/PhabricatorProjectWorkboardTransaction.php new file mode 100644 index 0000000000..b16a1921e0 --- /dev/null +++ b/src/applications/project/xaction/PhabricatorProjectWorkboardTransaction.php @@ -0,0 +1,38 @@ +getHasWorkboard(); + } + + public function generateNewValue($object, $value) { + return (int)$value; + } + + public function applyInternalEffects($object, $value) { + $object->setHasWorkboard($value); + } + + public function getTitle() { + $new = $this->getNewValue(); + + if ($new) { + return pht( + '%s enabled the workboard for this project.', + $this->renderAuthor()); + } else { + return pht( + '%s disabled the workboard for this project.', + $this->renderAuthor()); + } + } + + public function shouldHide() { + return true; + } + +} diff --git a/src/applications/search/query/PhabricatorFulltextToken.php b/src/applications/search/query/PhabricatorFulltextToken.php index d42c15e9e6..4edeb098a9 100644 --- a/src/applications/search/query/PhabricatorFulltextToken.php +++ b/src/applications/search/query/PhabricatorFulltextToken.php @@ -64,7 +64,7 @@ final class PhabricatorFulltextToken extends Phobject { $tag = id(new PHUITagView()) ->setType(PHUITagView::TYPE_SHADE) - ->setShade($shade) + ->setColor($shade) ->setName($token->getValue()); if ($tip !== null) { diff --git a/src/applications/transactions/editengine/PhabricatorEditEngineSubtype.php b/src/applications/transactions/editengine/PhabricatorEditEngineSubtype.php index ff14ae239b..df367955af 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngineSubtype.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngineSubtype.php @@ -8,6 +8,9 @@ final class PhabricatorEditEngineSubtype private $key; private $name; + private $icon; + private $tagText; + private $color; public function setKey($key) { $this->key = $key; @@ -27,8 +30,48 @@ final class PhabricatorEditEngineSubtype return $this->name; } + public function setIcon($icon) { + $this->icon = $icon; + return $this; + } + public function getIcon() { - return 'fa-drivers-license-o'; + return $this->icon; + } + + public function setTagText($text) { + $this->tagText = $text; + return $this; + } + + public function getTagText() { + return $this->tagText; + } + + public function setColor($color) { + $this->color = $color; + return $this; + } + + public function getColor() { + return $this->color; + } + + public function hasTagView() { + return (bool)strlen($this->getTagText()); + } + + public function newTagView() { + $view = id(new PHUITagView()) + ->setType(PHUITagView::TYPE_OUTLINE) + ->setName($this->getTagText()); + + $color = $this->getColor(); + if ($color) { + $view->setColor($color); + } + + return $view; } public static function validateSubtypeKey($subtype) { @@ -72,6 +115,9 @@ final class PhabricatorEditEngineSubtype array( 'key' => 'string', 'name' => 'string', + 'tag' => 'optional string', + 'color' => 'optional string', + 'icon' => 'optional string', )); $key = $value['key']; @@ -113,9 +159,27 @@ final class PhabricatorEditEngineSubtype $key = $entry['key']; $name = $entry['name']; - $map[$key] = id(new self()) + $tag_text = idx($entry, 'tag'); + if ($tag_text === null) { + if ($key != self::SUBTYPE_DEFAULT) { + $tag_text = phutil_utf8_strtoupper($name); + } + } + + $color = idx($entry, 'color', 'blue'); + $icon = idx($entry, 'icon', 'fa-drivers-license-o'); + + $subtype = id(new self()) ->setKey($key) - ->setName($name); + ->setName($name) + ->setTagText($tag_text) + ->setIcon($icon); + + if ($color) { + $subtype->setColor($color); + } + + $map[$key] = $subtype; } return $map; diff --git a/src/applications/uiexample/examples/PHUIButtonExample.php b/src/applications/uiexample/examples/PHUIButtonExample.php index ae699eb7a3..900125cbc2 100644 --- a/src/applications/uiexample/examples/PHUIButtonExample.php +++ b/src/applications/uiexample/examples/PHUIButtonExample.php @@ -131,9 +131,6 @@ final class PHUIButtonExample extends PhabricatorUIExample { ); $colors = array( PHUIButtonView::SIMPLE, - PHUIButtonView::SIMPLE_YELLOW, - PHUIButtonView::SIMPLE_GREY, - PHUIButtonView::SIMPLE_BLUE, ); $column = array(); foreach ($colors as $color) { diff --git a/src/applications/uiexample/examples/PHUIIconExample.php b/src/applications/uiexample/examples/PHUIIconExample.php index 8e01d6e16a..1bf96ce8e5 100644 --- a/src/applications/uiexample/examples/PHUIIconExample.php +++ b/src/applications/uiexample/examples/PHUIIconExample.php @@ -130,6 +130,23 @@ final class PHUIIconExample extends PhabricatorUIExample { ->addClass('mmr'); } + $circles = array('fa-gear', 'fa-recycle'); + $colors = array('green', 'pink', 'red', 'sky', 'violet'); + foreach ($circles as $circle) { + $states = PHUIIconCircleView::getStateMap(); + foreach ($states as $state => $name) { + $i = array_rand($colors); + $circleview[] = + id(new PHUIIconCircleView()) + ->setIcon($circle) + ->setSize(PHUIIconCircleView::SMALL) + ->setState($state) + ->setColor($colors[$i]) + ->setHref('#') + ->addClass('mmr'); + } + } + $squares = array('fa-briefcase', 'fa-code', 'fa-globe', 'fa-home'); $squareview = array(); foreach ($squares as $icon) { diff --git a/src/applications/uiexample/examples/PHUIObjectItemListExample.php b/src/applications/uiexample/examples/PHUIObjectItemListExample.php index 4a75ceede1..6bc521dca2 100644 --- a/src/applications/uiexample/examples/PHUIObjectItemListExample.php +++ b/src/applications/uiexample/examples/PHUIObjectItemListExample.php @@ -202,7 +202,7 @@ final class PHUIObjectItemListExample extends PhabricatorUIExample { $list->addItem( id(new PHUIObjectItemView()) ->setHeader(pht('Ace of Hearts')) - ->setSubHead( + ->setDescription( pht('This is a powerful card in the game "Hearts".')) ->setHref('#') ->addAttribute(pht('Suit: Hearts')) @@ -330,6 +330,8 @@ final class PHUIObjectItemListExample extends PhabricatorUIExample { $list->addItem( id(new PHUIObjectItemView()) ->setImageURI($default_project->getViewURI()) + ->setImageHref('#') + ->setHref('$$$') ->setHeader(pht('Default Project Profile Image')) ->setGrippable(true) ->addAttribute(pht('This is the default project profile image.'))); diff --git a/src/applications/uiexample/examples/PHUITagExample.php b/src/applications/uiexample/examples/PHUITagExample.php index 155fe4e9da..027be2f9d8 100644 --- a/src/applications/uiexample/examples/PHUITagExample.php +++ b/src/applications/uiexample/examples/PHUITagExample.php @@ -162,15 +162,15 @@ final class PHUITagExample extends PhabricatorUIExample { $tags = array(); foreach ($shades as $shade) { $tags[] = id(new PHUITagView()) - ->setType(PHUITagView::TYPE_OBJECT) - ->setShade($shade) + ->setType(PHUITagView::TYPE_SHADE) + ->setColor($shade) ->setIcon('fa-tags') ->setName(ucwords($shade)) ->setHref('#'); $tags[] = hsprintf(' '); $tags[] = id(new PHUITagView()) - ->setType(PHUITagView::TYPE_OBJECT) - ->setShade($shade) + ->setType(PHUITagView::TYPE_SHADE) + ->setColor($shade) ->setSlimShady(true) ->setIcon('fa-tags') ->setName(ucwords($shade)) @@ -182,6 +182,26 @@ final class PHUITagExample extends PhabricatorUIExample { ->appendChild($tags) ->addPadding(PHUI::PADDING_LARGE); + $outlines = PHUITagView::getOutlines(); + $tags = array(); + foreach ($outlines as $outline) { + $tags[] = id(new PHUITagView()) + ->setType(PHUITagView::TYPE_OUTLINE) + ->setColor($outline) + ->setName($outline); + $tags[] = hsprintf(' '); + $tags[] = id(new PHUITagView()) + ->setType(PHUITagView::TYPE_OUTLINE) + ->setColor($outline) + ->setSlimShady(true) + ->setName($outline); + $tags[] = hsprintf('

'); + } + + $content5 = id(new PHUIBoxView()) + ->appendChild($tags) + ->addPadding(PHUI::PADDING_LARGE); + $box = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Inline')) ->appendChild($intro); @@ -202,6 +222,10 @@ final class PHUITagExample extends PhabricatorUIExample { ->setHeaderText(pht('Shades')) ->appendChild($content4); - return array($box, $box1, $box2, $box3, $box4); + $box5 = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Outlines')) + ->appendChild($content5); + + return array($box, $box1, $box2, $box3, $box4, $box5); } } diff --git a/src/docs/user/userguide/diffusion_existing.diviner b/src/docs/user/userguide/diffusion_existing.diviner index b5b1fbdf76..bf09d9ef1f 100644 --- a/src/docs/user/userguide/diffusion_existing.diviner +++ b/src/docs/user/userguide/diffusion_existing.diviner @@ -44,8 +44,8 @@ Importing Repositories There are two primary ways to import an existing repository: **Observe First**: In Git or Mercurial, you can observe the repository first. -Once the import completes, disable the **Observe** URI to automatically convert -it into a hosted repository. +Once the import completes, change the "I/O Type" on the **Observe** URI to +"No I/O" mode to automatically convert it into a hosted repository. **Push to Empty Repository**: Create an activate an empty repository, then push all of your changes to the empty repository. diff --git a/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php b/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php index 821834431a..a42efa932a 100644 --- a/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php +++ b/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php @@ -148,7 +148,7 @@ final class PHUIDiffInlineCommentDetailView ->setType(PHUITagView::TYPE_SHADE) ->setName(pht('Unsubmitted')) ->setSlimShady(true) - ->setShade(PHUITagView::COLOR_RED) + ->setColor(PHUITagView::COLOR_RED) ->addClass('mml inline-draft-text'); } @@ -383,7 +383,7 @@ final class PHUIDiffInlineCommentDetailView ->setType(PHUITagView::TYPE_SHADE) ->setName(pht('Author')) ->setSlimShady(true) - ->setShade(PHUITagView::COLOR_YELLOW) + ->setColor(PHUITagView::COLOR_YELLOW) ->addClass('mml'); } } diff --git a/src/infrastructure/markup/PhabricatorMarkupEngine.php b/src/infrastructure/markup/PhabricatorMarkupEngine.php index ad380414f6..0c56d9ed41 100644 --- a/src/infrastructure/markup/PhabricatorMarkupEngine.php +++ b/src/infrastructure/markup/PhabricatorMarkupEngine.php @@ -42,7 +42,7 @@ final class PhabricatorMarkupEngine extends Phobject { private $objects = array(); private $viewer; private $contextObject; - private $version = 16; + private $version = 17; private $engineCaches = array(); private $auxiliaryConfig = array(); diff --git a/src/infrastructure/markup/rule/PhabricatorNavigationRemarkupRule.php b/src/infrastructure/markup/rule/PhabricatorNavigationRemarkupRule.php index 458ee5b834..3fc3373001 100644 --- a/src/infrastructure/markup/rule/PhabricatorNavigationRemarkupRule.php +++ b/src/infrastructure/markup/rule/PhabricatorNavigationRemarkupRule.php @@ -60,7 +60,7 @@ final class PhabricatorNavigationRemarkupRule extends PhutilRemarkupRule { $tag = id(new PHUITagView()) ->setType(PHUITagView::TYPE_SHADE) - ->setShade($item_color) + ->setColor($item_color) ->setName($item_name); if ($item['icon']) { diff --git a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php index 2bd374d5db..3ef2a72e6f 100644 --- a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php +++ b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php @@ -1811,11 +1811,16 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery case PhabricatorQueryConstraint::OPERATOR_NOT: case PhabricatorQueryConstraint::OPERATOR_AND: case PhabricatorQueryConstraint::OPERATOR_OR: - case PhabricatorQueryConstraint::OPERATOR_ANCESTOR: if (count($list) > 1) { return true; } break; + case PhabricatorQueryConstraint::OPERATOR_ANCESTOR: + // NOTE: We must always group query results rows when using an + // "ANCESTOR" operator because a single task may be related to + // two different descendants of a particular ancestor. For + // discussion, see T12753. + return true; case PhabricatorQueryConstraint::OPERATOR_NULL: return true; } diff --git a/src/view/phui/PHUIButtonView.php b/src/view/phui/PHUIButtonView.php index ee6975357c..ac3f8aabe0 100644 --- a/src/view/phui/PHUIButtonView.php +++ b/src/view/phui/PHUIButtonView.php @@ -5,11 +5,7 @@ final class PHUIButtonView extends AphrontTagView { const GREEN = 'green'; const GREY = 'grey'; const DISABLED = 'disabled'; - const SIMPLE = 'simple'; - const SIMPLE_YELLOW = 'simple simple-yellow'; - const SIMPLE_GREY = 'simple simple-grey'; - const SIMPLE_BLUE = 'simple simple-blue'; const SMALL = 'small'; const BIG = 'big'; diff --git a/src/view/phui/PHUIHeaderView.php b/src/view/phui/PHUIHeaderView.php index 0cd8379b42..ab576c64d5 100644 --- a/src/view/phui/PHUIHeaderView.php +++ b/src/view/phui/PHUIHeaderView.php @@ -131,7 +131,7 @@ final class PHUIHeaderView extends AphrontTagView { $tag = id(new PHUITagView()) ->setName($name) ->setIcon($icon) - ->setShade($color) + ->setColor($color) ->setType(PHUITagView::TYPE_SHADE); return $this->addProperty(self::PROPERTY_STATUS, $tag); diff --git a/src/view/phui/PHUIIconCircleView.php b/src/view/phui/PHUIIconCircleView.php index 083818969f..11e2a2ac3b 100644 --- a/src/view/phui/PHUIIconCircleView.php +++ b/src/view/phui/PHUIIconCircleView.php @@ -6,10 +6,22 @@ final class PHUIIconCircleView extends AphrontTagView { private $icon; private $color; private $size; + private $state; const SMALL = 'circle-small'; const MEDIUM = 'circle-medium'; + const STATE_FAIL = 'fa-times-circle'; + const STATE_INFO = 'fa-info-circle'; + const STATE_STOP = 'fa-stop-circle'; + const STATE_START = 'fa-play-circle'; + const STATE_PAUSE = 'fa-pause-circle'; + const STATE_SUCCESS = 'fa-check-circle'; + const STATE_WARNING = 'fa-exclamation-circle'; + const STATE_PLUS = 'fa-plus-circle'; + const STATE_MINUS = 'fa-minus-circle'; + const STATE_UNKNOWN = 'fa-question-circle'; + public function setHref($href) { $this->href = $href; return $this; @@ -30,6 +42,11 @@ final class PHUIIconCircleView extends AphrontTagView { return $this; } + public function setState($state) { + $this->state = $state; + return $this; + } + protected function getTagName() { $tag = 'span'; if ($this->href) { @@ -54,6 +71,10 @@ final class PHUIIconCircleView extends AphrontTagView { $classes[] = $this->size; } + if ($this->state) { + $classes[] = 'phui-icon-circle-state'; + } + return array( 'href' => $this->href, 'class' => $classes, @@ -61,8 +82,32 @@ final class PHUIIconCircleView extends AphrontTagView { } protected function getTagContent() { + $state = null; + if ($this->state) { + $state = id(new PHUIIconView()) + ->setIcon($this->state.' '.$this->color) + ->addClass('phui-icon-circle-state-icon'); + } + return id(new PHUIIconView()) - ->setIcon($this->icon); + ->setIcon($this->icon) + ->addClass('phui-icon-circle-icon') + ->appendChild($state); + } + + public static function getStateMap() { + return array( + self::STATE_FAIL => pht('Failure'), + self::STATE_INFO => pht('Information'), + self::STATE_STOP => pht('Stop'), + self::STATE_START => pht('Start'), + self::STATE_PAUSE => pht('Pause'), + self::STATE_SUCCESS => pht('Success'), + self::STATE_WARNING => pht('Warning'), + self::STATE_PLUS => pht('Plus'), + self::STATE_MINUS => pht('Minus'), + self::STATE_UNKNOWN => pht('Unknown'), + ); } } diff --git a/src/view/phui/PHUIObjectItemView.php b/src/view/phui/PHUIObjectItemView.php index 6d193081d2..070d436cf6 100644 --- a/src/view/phui/PHUIObjectItemView.php +++ b/src/view/phui/PHUIObjectItemView.php @@ -19,13 +19,15 @@ final class PHUIObjectItemView extends AphrontTagView { private $headIcons = array(); private $disabled; private $imageURI; + private $imageHref; private $imageIcon; private $titleText; private $badge; private $countdownNum; private $countdownNoun; - private $launchButton; + private $sideColumn; private $coverImage; + private $description; public function setDisabled($disabled) { $this->disabled = $disabled; @@ -126,6 +128,11 @@ final class PHUIObjectItemView extends AphrontTagView { return $this; } + public function setImageHref($image_href) { + $this->imageHref = $image_href; + return $this; + } + public function getImageURI() { return $this->imageURI; } @@ -148,6 +155,11 @@ final class PHUIObjectItemView extends AphrontTagView { return $this; } + public function setDescription($description) { + $this->description = $description; + return $this; + } + public function setEpoch($epoch) { $date = phabricator_datetime($epoch, $this->getUser()); $this->addIcon('none', $date); @@ -217,9 +229,8 @@ final class PHUIObjectItemView extends AphrontTagView { return $this; } - public function setLaunchButton(PHUIButtonView $button) { - $button->setSize(PHUIButtonView::SMALL); - $this->launchButton = $button; + public function setSideColumn($column) { + $this->sideColumn = $column; return $this; } @@ -334,6 +345,23 @@ final class PHUIObjectItemView extends AphrontTagView { ), $this->header); + $description_tag = null; + if ($this->description) { + $decription_id = celerity_generate_unique_node_id(); + $description_tag = id(new PHUITagView()) + ->setIcon('fa-ellipsis-h') + ->addClass('phui-oi-description-tag') + ->setType(PHUITagView::TYPE_SHADE) + ->setColor(PHUITagView::COLOR_GREY) + ->addSigil('jx-toggle-class') + ->setSlimShady(true) + ->setMetaData(array( + 'map' => array( + $decription_id => 'phui-oi-description-reveal', + ), + )); + } + // Wrap the header content in a with the "slippery" sigil. This // prevents us from beginning a drag if you click the text (like "T123"), // but not if you click the white space after the header. @@ -351,6 +379,7 @@ final class PHUIObjectItemView extends AphrontTagView { $this->headIcons, $header_name, $header_link, + $description_tag, ))); $icons = array(); @@ -453,6 +482,16 @@ final class PHUIObjectItemView extends AphrontTagView { $this->subhead); } + if ($this->description) { + $subhead = phutil_tag( + 'div', + array( + 'class' => 'phui-oi-subhead phui-oi-description', + 'id' => $decription_id, + ), + $this->description); + } + if ($icons) { $icons = phutil_tag( 'div', @@ -541,11 +580,12 @@ final class PHUIObjectItemView extends AphrontTagView { $this->getImageIcon()); } - if ($image && $this->href) { + if ($image && (strlen($this->href) || strlen($this->imageHref))) { + $image_href = ($this->imageHref) ? $this->imageHref : $this->href; $image = phutil_tag( 'a', array( - 'href' => $this->href, + 'href' => $image_href, ), $image); } @@ -611,14 +651,15 @@ final class PHUIObjectItemView extends AphrontTagView { )); } - if ($this->launchButton) { + /* Fixed width, right column container. */ + if ($this->sideColumn) { $column2 = phutil_tag( 'div', array( - 'class' => 'phui-oi-col2 phui-oi-launch-button', + 'class' => 'phui-oi-col2 phui-oi-side-column', ), array( - $this->launchButton, + $this->sideColumn, )); } diff --git a/src/view/phui/PHUITagView.php b/src/view/phui/PHUITagView.php index ae80fc1731..1811f8f1a9 100644 --- a/src/view/phui/PHUITagView.php +++ b/src/view/phui/PHUITagView.php @@ -6,6 +6,7 @@ final class PHUITagView extends AphrontTagView { const TYPE_OBJECT = 'object'; const TYPE_STATE = 'state'; const TYPE_SHADE = 'shade'; + const TYPE_OUTLINE = 'outline'; const COLOR_RED = 'red'; const COLOR_ORANGE = 'orange'; @@ -15,6 +16,8 @@ final class PHUITagView extends AphrontTagView { const COLOR_VIOLET = 'violet'; const COLOR_GREEN = 'green'; const COLOR_BLACK = 'black'; + const COLOR_SKY = 'sky'; + const COLOR_FIRE = 'fire'; const COLOR_GREY = 'grey'; const COLOR_WHITE = 'white'; const COLOR_PINK = 'pink'; @@ -29,6 +32,7 @@ final class PHUITagView extends AphrontTagView { private $href; private $name; private $phid; + private $color; private $backgroundColor; private $dotColor; private $closed; @@ -41,6 +45,7 @@ final class PHUITagView extends AphrontTagView { $this->type = $type; switch ($type) { case self::TYPE_SHADE: + case self::TYPE_OUTLINE: break; case self::TYPE_OBJECT: $this->setBackgroundColor(self::COLOR_OBJECT); @@ -52,8 +57,20 @@ final class PHUITagView extends AphrontTagView { return $this; } + /** + * This method has been deprecated, use @{method:setColor} instead. + * + * @deprecated + */ public function setShade($shade) { - $this->shade = $shade; + phlog( + pht('Deprecated call to setShade(), use setColor() instead.')); + $this->color = $shade; + return $this; + } + + public function setColor($color) { + $this->color = $color; return $this; } @@ -109,12 +126,16 @@ final class PHUITagView extends AphrontTagView { 'phui-tag-type-'.$this->type, ); - if ($this->shade) { + if ($this->color) { + $classes[] = 'phui-tag-'.$this->color; + } + + if ($this->slimShady) { + $classes[] = 'phui-tag-slim'; + } + + if ($this->type == self::TYPE_SHADE) { $classes[] = 'phui-tag-shade'; - $classes[] = 'phui-tag-shade-'.$this->shade; - if ($this->slimShady) { - $classes[] = 'phui-tag-shade-slim'; - } } if ($this->icon) { @@ -240,6 +261,32 @@ final class PHUITagView extends AphrontTagView { return idx(self::getShadeMap(), $shade, $shade); } + public static function getOutlines() { + return array_keys(self::getOutlineMap()); + } + + public static function getOutlineMap() { + return array( + self::COLOR_RED => pht('Red'), + self::COLOR_ORANGE => pht('Orange'), + self::COLOR_YELLOW => pht('Yellow'), + self::COLOR_BLUE => pht('Blue'), + self::COLOR_INDIGO => pht('Indigo'), + self::COLOR_VIOLET => pht('Violet'), + self::COLOR_GREEN => pht('Green'), + self::COLOR_GREY => pht('Grey'), + self::COLOR_PINK => pht('Pink'), + self::COLOR_SKY => pht('Sky'), + self::COLOR_FIRE => pht('Fire'), + self::COLOR_BLACK => pht('Black'), + self::COLOR_DISABLED => pht('Disabled'), + ); + } + + public static function getOutlineName($outline) { + return idx(self::getOutlineMap(), $outline, $outline); + } + public function setExternal($external) { $this->external = $external; diff --git a/webroot/rsrc/css/aphront/typeahead-browse.css b/webroot/rsrc/css/aphront/typeahead-browse.css index 625354bb71..7439d299e7 100644 --- a/webroot/rsrc/css/aphront/typeahead-browse.css +++ b/webroot/rsrc/css/aphront/typeahead-browse.css @@ -64,19 +64,3 @@ input.typeahead-browse-input { margin-top: 1px; margin-left: 6px; } - -.typeahead-browse-item .phabricator-main-search-typeahead-result { - margin: 2px 0; - padding: 0 8px; -} - -.typeahead-browse-item .phabricator-main-search-typeahead-result.has-image { - padding-left: 48px; -} - -.typeahead-browse-item - .phabricator-main-search-typeahead-result.result-closed - .result-name { - text-decoration: line-through; - color: {$lightgreytext}; -} diff --git a/webroot/rsrc/css/application/base/main-menu-view.css b/webroot/rsrc/css/application/base/main-menu-view.css index 28148ae71a..eb5cfad8c7 100644 --- a/webroot/rsrc/css/application/base/main-menu-view.css +++ b/webroot/rsrc/css/application/base/main-menu-view.css @@ -309,6 +309,22 @@ a.phabricator-core-user-menu .caret:before { color: {$darkgreytext}; } +.phabricator-main-search-typeahead-result.result-closed { + opacity: .8; + -webkit-filter: grayscale(100%); + filter: grayscale(100%); +} + +.phabricator-main-search-typeahead-result.result-closed + .result-name { + text-decoration: line-through; + color: {$lightgreytext}; +} + +.phabricator-main-search-typeahead-result.has-image { + padding-left: 48px; +} + .phabricator-main-search-typeahead-result .result-type { color: {$lightgreytext}; font-size: {$smallestfontsize}; diff --git a/webroot/rsrc/css/application/diffusion/diffusion-history.css b/webroot/rsrc/css/application/diffusion/diffusion-history.css new file mode 100644 index 0000000000..b340cd913d --- /dev/null +++ b/webroot/rsrc/css/application/diffusion/diffusion-history.css @@ -0,0 +1,20 @@ +/** + * @provides diffusion-history-css + */ + +.diffusion-history-list .phui-oi-link { + color: {$darkbluetext}; + font-size: {$biggerfontsize}; +} + +.diffusion-history-list .phui-oi-attribute .phui-tag-core { + border-color: transparent; +} + +.diffusion-history-message { + background-color: {$bluebackground}; + padding: 16px; + margin: 4px 0; + border-radius: 5px; + color: {$darkbluetext}; +} diff --git a/webroot/rsrc/css/application/search/search-results.css b/webroot/rsrc/css/application/search/search-results.css index a1837fa391..0f0a31a1b9 100644 --- a/webroot/rsrc/css/application/search/search-results.css +++ b/webroot/rsrc/css/application/search/search-results.css @@ -26,6 +26,6 @@ margin: 0 2px; } -.phui-fulltext-tokens .phui-tag-view.phui-tag-shade-grey { +.phui-fulltext-tokens .phui-tag-view.phui-tag-grey { opacity: 0.5; } diff --git a/webroot/rsrc/css/phui/object-item/phui-oi-list-view.css b/webroot/rsrc/css/phui/object-item/phui-oi-list-view.css index 26b0781e8d..788b0e73ec 100644 --- a/webroot/rsrc/css/phui/object-item/phui-oi-list-view.css +++ b/webroot/rsrc/css/phui/object-item/phui-oi-list-view.css @@ -285,6 +285,31 @@ ul.phui-oi-list-view { padding: 0 8px 6px; } +.phui-oi-description { + display: none; +} + +.phui-oi-description.phui-oi-description-reveal { + display: block; +} + +.phui-oi-description-tag { + margin-left: 4px; +} + +.phui-oi-description-tag:hover .phui-tag-core { + cursor: pointer; + background: {$darkgreybackground}; +} + +.phui-oi-description-tag .phui-tag-core { + border: none; +} + +.phui-oi-description-tag.phui-tag-view .phui-icon-view { + margin: 2px; +} + /* - Attribute List ------------------------------------------------------------ @@ -625,13 +650,13 @@ ul.phui-oi-list-view .phui-oi-selected /* - Launcher Button -------------------------------------------------------- */ -.phui-oi-col2.phui-oi-launch-button { +.phui-oi-col2.phui-oi-side-column { text-align: right; vertical-align: middle; padding-right: 4px; } -.device-phone .phui-oi-col2.phui-oi-launch-button { +.device-phone .phui-oi-col2.phui-oi-side-column { padding: 0 8px 8px; text-align: left; } diff --git a/webroot/rsrc/css/phui/phui-button.css b/webroot/rsrc/css/phui/phui-button.css index 09f2233a6c..e9d446a0ac 100644 --- a/webroot/rsrc/css/phui/phui-button.css +++ b/webroot/rsrc/css/phui/phui-button.css @@ -91,8 +91,8 @@ input[type="submit"].simple, a.simple, a.simple:visited { background: #fff; - color: {$blue}; - border: 1px solid {$blue}; + color: {$bluetext}; + border: 1px solid {$lightblueborder}; } a.simple.current { @@ -103,7 +103,7 @@ button.simple .phui-icon-view, input[type="submit"].simple .phui-icon-view, a.simple .phui-icon-view, a.simple:visited .phui-icon-view { - color: {$blue}; + color: {$lightbluetext}; } a.disabled, @@ -145,10 +145,10 @@ button.green:hover { a.button.simple:hover, button.simple:hover { - background-color: {$blue}; + background-color: {$lightblue}; background-image: linear-gradient(to bottom, {$blue}, {$blue}); color: #fff; - transition: 0.1s; + transition: 0s; } a.button.simple:hover .phui-icon-view, diff --git a/webroot/rsrc/css/phui/phui-document-pro.css b/webroot/rsrc/css/phui/phui-document-pro.css index 0aef43ed90..a4c05a0ecd 100644 --- a/webroot/rsrc/css/phui/phui-document-pro.css +++ b/webroot/rsrc/css/phui/phui-document-pro.css @@ -75,7 +75,8 @@ a.button.phui-document-toc { display: inline-block; height: 16px; width: 20px; - padding: 3px 8px 4px 8px; + padding-left: 8px; + padding-right: 8px; } .phui-document-view-pro .phui-document-toc-list { @@ -83,7 +84,7 @@ a.button.phui-document-toc { border: 1px solid {$lightgreyborder}; border-radius: 3px; box-shadow: {$dropshadow}; - width: 200px; + width: 260px; position: absolute; z-index: 30; background-color: #fff; @@ -105,6 +106,7 @@ a.button.phui-document-toc { .phui-document-toc-open .phui-document-toc { background-color: {$blue}; + border-color: {$blue}; } .phui-document-toc-open .phui-document-toc .phui-icon-view { @@ -112,17 +114,38 @@ a.button.phui-document-toc { } .phui-document-view-pro .phui-document-toc-content { - margin: 4px 12px; + margin: 8px 16px; } .phui-document-view-pro .phui-document-toc-header { font-weight: bold; color: {$bluetext}; margin-bottom: 8px; + text-transform: uppercase; + font-size: {$smallerfontsize}; } .phui-document-view-pro .phui-document-toc-content li { - margin: 4px 8px; + margin: 4px 8px 4px 0; +} + +.phui-document-view-pro .phui-document-toc-content a { + padding: 2px 0; + display: block; + text-decoration: none; + color: {$darkbluetext}; + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; +} + +.phui-document-view-pro .phui-document-toc-content a:hover { + color: {$anchor}; + text-decoration: underline; +} + +.phui-document-view-pro .phui-document-toc-content li + ul { + margin: 4px 0 4px 8px; } .phui-document-view-pro .phui-document-content .phabricator-remarkup { diff --git a/webroot/rsrc/css/phui/phui-header-view.css b/webroot/rsrc/css/phui/phui-header-view.css index 5c24d5575e..470e821544 100644 --- a/webroot/rsrc/css/phui/phui-header-view.css +++ b/webroot/rsrc/css/phui/phui-header-view.css @@ -353,7 +353,7 @@ body .phui-header-shell.phui-bleed-header vertical-align: top; } -.phui-header-view .phui-tag-shade-indigo a { +.phui-header-view .phui-tag-indigo a { color: {$sh-indigotext}; } diff --git a/webroot/rsrc/css/phui/phui-icon.css b/webroot/rsrc/css/phui/phui-icon.css index 22b33bca11..627b7632fe 100644 --- a/webroot/rsrc/css/phui/phui-icon.css +++ b/webroot/rsrc/css/phui/phui-icon.css @@ -57,6 +57,7 @@ img.phui-image-disabled { cursor: pointer; background: transparent; padding: 0; + position: relative; } .phui-icon-circle.circle-medium { @@ -65,7 +66,21 @@ img.phui-image-disabled { border-radius: 36px; } -.phui-icon-circle .phui-icon-view { +.phui-icon-circle.phui-icon-circle-state { + border-color: transparent; + background-color: {$bluebackground}; +} + +.phui-icon-circle.phui-icon-circle-state .phui-icon-circle-icon { + color: {$bluetext}; + font-size: 16px; +} + +a.phui-icon-circle.phui-icon-circle-state:hover { + border-color: transparent !important; +} + +.phui-icon-circle .phui-icon-circle-icon { height: 24px; width: 24px; font-size: 11px; @@ -74,7 +89,7 @@ img.phui-image-disabled { cursor: pointer; } -.phui-icon-circle.circle-medium .phui-icon-view { +.phui-icon-circle.circle-medium .phui-icon-circle-icon { font-size: 18px; line-height: 36px; } @@ -129,6 +144,21 @@ a.phui-icon-circle.hover-red:hover .phui-icon-view { color: {$red}; } +a.phui-icon-circle .phui-icon-view.phui-icon-circle-state-icon { + position: absolute; + width: 14px; + height: 14px; + display: inline-block; + font-size: 12px; + right: -3px; + top: -4px; + text-shadow: + -1px -1px 0 #fff, + 1px -1px 0 #fff, + -1px 1px 0 #fff, + 1px 1px 0 #fff; +} + /* - Icon in a Square ------------------------------------------------------- */ .phui-icon-view.phui-icon-square { diff --git a/webroot/rsrc/css/phui/phui-tag-view.css b/webroot/rsrc/css/phui/phui-tag-view.css index d5b2710d7b..56b5badf8d 100644 --- a/webroot/rsrc/css/phui/phui-tag-view.css +++ b/webroot/rsrc/css/phui/phui-tag-view.css @@ -161,225 +161,233 @@ a.phui-tag-view:hover */ -.phui-tag-shade { +.phui-tag-view.phui-tag-type-shade { font-weight: normal; } -.phui-tag-shade .phui-icon-view { +.phui-tag-view.phui-tag-type-shade .phui-icon-view { font-size: 12px; } -.phui-tag-shade-slim .phui-icon-view { + +/* - Slim Tags ----------------------------------------------------------------- + + A thinner tag for object list, workboards. + +*/ + +.phui-tag-slim .phui-icon-view { font-size: 11px; } -.phui-tag-shade-slim .phui-tag-core { +.phui-tag-slim .phui-tag-core { font-size: {$smallerfontsize}; } + /* - Red -------------------------------------------------------------------- */ -.phui-tag-shade-red .phui-tag-core, +.phui-tag-red .phui-tag-core, .jx-tokenizer-token.red { background: {$sh-redbackground}; border-color: {$sh-lightredborder}; color: {$sh-redtext}; } -.phui-tag-shade-red .phui-icon-view, +.phui-tag-red .phui-icon-view, .jx-tokenizer-token.red .phui-icon-view, .jx-tokenizer-token.red .jx-tokenizer-x { color: {$sh-redicon}; } -a.phui-tag-view:hover.phui-tag-shade-red .phui-tag-core, +a.phui-tag-view:hover.phui-tag-red .phui-tag-core, .jx-tokenizer-token.red:hover { border-color: {$sh-redborder}; } /* - Orange ----------------------------------------------------------------- */ -.phui-tag-shade-orange .phui-tag-core, +.phui-tag-orange .phui-tag-core, .jx-tokenizer-token.orange { background: {$sh-orangebackground}; border-color: {$sh-lightorangeborder}; color: {$sh-orangetext}; } -.phui-tag-shade-orange .phui-icon-view, +.phui-tag-orange .phui-icon-view, .jx-tokenizer-token.orange .phui-icon-view, .jx-tokenizer-token.orange .jx-tokenizer-x { color: {$sh-orangeicon}; } -a.phui-tag-view:hover.phui-tag-shade-orange .phui-tag-core, +a.phui-tag-view:hover.phui-tag-orange .phui-tag-core, .jx-tokenizer-token.orange:hover { border-color: {$sh-orangeborder}; } /* - Yellow ----------------------------------------------------------------- */ -.phui-tag-shade-yellow .phui-tag-core, +.phui-tag-yellow .phui-tag-core, .jx-tokenizer-token.yellow { background: {$sh-yellowbackground}; border-color: {$sh-lightyellowborder}; color: {$sh-yellowtext}; } -.phui-tag-shade-yellow .phui-icon-view, +.phui-tag-yellow .phui-icon-view, .jx-tokenizer-token.yellow .phui-icon-view, .jx-tokenizer-token.yellow .jx-tokenizer-x { color: {$sh-yellowicon}; } -a.phui-tag-view:hover.phui-tag-shade-yellow .phui-tag-core, +a.phui-tag-view:hover.phui-tag-yellow .phui-tag-core, .jx-tokenizer-token.yellow:hover { border-color: {$sh-yellowborder}; } /* - Blue ------------------------------------------------------------------- */ -.phui-tag-shade-blue .phui-tag-core, +.phui-tag-blue .phui-tag-core, .jx-tokenizer-token.blue { background: {$sh-bluebackground}; border-color: {$sh-lightblueborder}; color: {$sh-bluetext}; } -.phui-tag-shade-blue .phui-icon-view, +.phui-tag-blue .phui-icon-view, .jx-tokenizer-token.blue .phui-icon-view, .jx-tokenizer-token.blue .jx-tokenizer-x { color: {$sh-blueicon}; } -a.phui-tag-view:hover.phui-tag-shade-blue .phui-tag-core, +a.phui-tag-view:hover.phui-tag-blue .phui-tag-core, .jx-tokenizer-token.blue:hover { border-color: {$sh-blueborder}; } /* - Sky ------------------------------------------------------------------- */ -.phui-tag-shade-sky .phui-tag-core, +.phui-tag-sky .phui-tag-core, .jx-tokenizer-token.sky { background: #E0F0FA; border-color: {$sh-lightblueborder}; color: {$sh-bluetext}; } -.phui-tag-shade-sky .phui-icon-view, +.phui-tag-sky .phui-icon-view, .jx-tokenizer-token.sky .phui-icon-view, .jx-tokenizer-token.sky .jx-tokenizer-x { color: {$sh-blueicon}; } -a.phui-tag-view:hover.phui-tag-shade-sky .phui-tag-core, +a.phui-tag-view:hover.phui-tag-sky .phui-tag-core, .jx-tokenizer-token.sky:hover { border-color: {$sh-blueborder}; } /* - Indigo ----------------------------------------------------------------- */ -.phui-tag-shade-indigo .phui-tag-core, +.phui-tag-indigo .phui-tag-core, .jx-tokenizer-token.indigo { background: {$sh-indigobackground}; border-color: {$sh-lightindigoborder}; color: {$sh-indigotext}; } -.phui-tag-shade-indigo .phui-icon-view, +.phui-tag-indigo .phui-icon-view, .jx-tokenizer-token.indigo .phui-icon-view, .jx-tokenizer-token.indigo .jx-tokenizer-x { color: {$sh-indigoicon}; } -a.phui-tag-view:hover.phui-tag-shade-indigo .phui-tag-core, +a.phui-tag-view:hover.phui-tag-indigo .phui-tag-core, .jx-tokenizer-token.indigo:hover { border-color: {$sh-indigoborder}; } /* - Green ------------------------------------------------------------------ */ -.phui-tag-shade-green .phui-tag-core, +.phui-tag-green .phui-tag-core, .jx-tokenizer-token.green { background: {$sh-greenbackground}; border-color: {$sh-lightgreenborder}; color: {$sh-greentext}; } -.phui-tag-shade-green .phui-icon-view, +.phui-tag-green .phui-icon-view, .jx-tokenizer-token.green .phui-icon-view, .jx-tokenizer-token.green .jx-tokenizer-x { color: {$sh-greenicon}; } -a.phui-tag-view:hover.phui-tag-shade-green .phui-tag-core, +a.phui-tag-view:hover.phui-tag-green .phui-tag-core, .jx-tokenizer-token.green:hover { border-color: {$sh-greenborder}; } /* - Violet ----------------------------------------------------------------- */ -.phui-tag-shade-violet .phui-tag-core, +.phui-tag-violet .phui-tag-core, .jx-tokenizer-token.violet { background: {$sh-violetbackground}; border-color: {$sh-lightvioletborder}; color: {$sh-violettext}; } -.phui-tag-shade-violet .phui-icon-view, +.phui-tag-violet .phui-icon-view, .jx-tokenizer-token.violet .phui-icon-view, .jx-tokenizer-token.violet .jx-tokenizer-x { color: {$sh-violeticon}; } -a.phui-tag-view:hover.phui-tag-shade-violet .phui-tag-core, +a.phui-tag-view:hover.phui-tag-violet .phui-tag-core, .jx-tokenizer-token.violet:hover { border-color: {$sh-violetborder}; } /* - Pink ------------------------------------------------------------------- */ -.phui-tag-shade-pink .phui-tag-core, +.phui-tag-pink .phui-tag-core, .jx-tokenizer-token.pink { background: {$sh-pinkbackground}; border-color: {$sh-lightpinkborder}; color: {$sh-pinktext}; } -.phui-tag-shade-pink .phui-icon-view, +.phui-tag-pink .phui-icon-view, .jx-tokenizer-token.pink .phui-icon-view, .jx-tokenizer-token.pink .jx-tokenizer-x { color: {$sh-pinkicon}; } -a.phui-tag-view:hover.phui-tag-shade-pink .phui-tag-core, +a.phui-tag-view:hover.phui-tag-pink .phui-tag-core, .jx-tokenizer-token.pink:hover { border-color: {$sh-pinkborder}; } /* - Grey ------------------------------------------------------------------- */ -.phui-tag-shade-grey .phui-tag-core, +.phui-tag-grey .phui-tag-core, .jx-tokenizer-token.grey { background: {$sh-greybackground}; border-color: {$sh-lightgreyborder}; color: {$sh-greytext}; } -.phui-tag-shade-grey .phui-icon-view, +.phui-tag-grey .phui-icon-view, .jx-tokenizer-token.grey .phui-icon-view, .jx-tokenizer-token.grey .jx-tokenizer-x { color: {$sh-greyicon}; } -a.phui-tag-view:hover.phui-tag-shade-grey .phui-tag-core, +a.phui-tag-view:hover.phui-tag-grey .phui-tag-core, .jx-tokenizer-token.grey:hover { border-color: {$sh-greyborder}; } /* - Checkered -------------------------------------------------------------- */ -.phui-tag-shade-checkered .phui-tag-core, +.phui-tag-checkered .phui-tag-core, .jx-tokenizer-token.checkered { background: url(/rsrc/image/checker_lighter.png); border-style: dashed; @@ -388,13 +396,13 @@ a.phui-tag-view:hover.phui-tag-shade-grey .phui-tag-core, text-shadow: 1px 1px #fff; } -.phui-tag-shade-checkered .phui-icon-view, +.phui-tag-checkered .phui-icon-view, .jx-tokenizer-token.checkered .phui-icon-view, .jx-tokenizer-token.checkered .jx-tokenizer-x { color: {$sh-greyicon}; } -a.phui-tag-view:hover.phui-tag-shade-checkered .phui-tag-core, +a.phui-tag-view:hover.phui-tag-checkered .phui-tag-core, .jx-tokenizer-token.checkered:hover { border-style: solid; border-color: {$sh-greyborder}; @@ -402,16 +410,101 @@ a.phui-tag-view:hover.phui-tag-shade-checkered .phui-tag-core, /* - Disabled --------------------------------------------------------------- */ -.phui-tag-shade-disabled .phui-tag-core { +.phui-tag-disabled .phui-tag-core { background-color: {$sh-disabledbackground}; border-color: {$sh-lightdisabledborder}; color: {$sh-disabledtext}; } -.phui-tag-shade-disabled .phui-icon-view { +.phui-tag-disabled .phui-icon-view { color: {$sh-disabledicon}; } -a.phui-tag-view:hover.phui-tag-shade-disabled .phui-tag-core { +a.phui-tag-view:hover.phui-tag-disabled .phui-tag-core { border-color: {$sh-disabledborder}; } + +/* - Outline Tags -------------------------------------------------------------- + + Basic Tag with a bold border and white background + +*/ + +.phui-tag-type-outline { + text-transform: uppercase; + font-weight: normal; +} + +.phui-tag-view.phui-tag-type-outline .phui-tag-core { + background: #fff; + padding: 0 6px 1px 6px; +} + +.phui-tag-slim.phui-tag-type-outline .phui-tag-core { + font-size: {$smallestfontsize}; +} + +.phui-tag-type-outline.phui-tag-red .phui-tag-core { + color: {$red}; + border-color: {$red}; +} + +.phui-tag-type-outline.phui-tag-orange .phui-tag-core { + color: {$orange}; + border-color: {$orange}; +} + +.phui-tag-type-outline.phui-tag-yellow .phui-tag-core { + color: {$yellow}; + border-color: {$yellow}; +} + +.phui-tag-type-outline.phui-tag-green .phui-tag-core { + color: {$green}; + border-color: {$green}; +} + +.phui-tag-type-outline.phui-tag-blue .phui-tag-core { + color: {$blue}; + border-color: {$blue}; +} + +.phui-tag-type-outline.phui-tag-indigo .phui-tag-core { + color: {$indigo}; + border-color: {$indigo}; +} + +.phui-tag-type-outline.phui-tag-violet .phui-tag-core { + color: {$violet}; + border-color: {$violet}; +} + +.phui-tag-type-outline.phui-tag-grey .phui-tag-core { + color: {$bluetext}; + border-color: {$bluetext}; +} + +.phui-tag-type-outline.phui-tag-disabled .phui-tag-core { + color: {$lightgreytext}; + border-color: {$lightgreytext}; +} + +.phui-tag-type-outline.phui-tag-pink .phui-tag-core { + color: {$pink}; + border-color: {$pink}; +} + +.phui-tag-type-outline.phui-tag-sky .phui-tag-core { + color: {$sky}; + border-color: {$sky}; +} + +.phui-tag-type-outline.phui-tag-fire .phui-tag-core { + color: {$fire}; + border-color: {$fire}; +} + +.phui-tag-type-outline.phui-tag-black .phui-tag-core { + color: #000; + border-color: #000; +} diff --git a/webroot/rsrc/js/application/projects/WorkboardColumn.js b/webroot/rsrc/js/application/projects/WorkboardColumn.js index 81fba84077..9973648593 100644 --- a/webroot/rsrc/js/application/projects/WorkboardColumn.js +++ b/webroot/rsrc/js/application/projects/WorkboardColumn.js @@ -285,9 +285,9 @@ JX.install('WorkboardColumn', { JX.DOM.alterClass(panel, 'project-panel-over-limit', over_limit); var color_map = { - 'phui-tag-shade-disabled': (total_points === 0), - 'phui-tag-shade-blue': (total_points > 0 && !over_limit), - 'phui-tag-shade-red': (over_limit) + 'phui-tag-disabled': (total_points === 0), + 'phui-tag-blue': (total_points > 0 && !over_limit), + 'phui-tag-red': (over_limit) }; for (var c in color_map) { diff --git a/webroot/rsrc/js/core/behavior-search-typeahead.js b/webroot/rsrc/js/core/behavior-search-typeahead.js index fc752a6079..ddcc2e6aaa 100644 --- a/webroot/rsrc/js/core/behavior-search-typeahead.js +++ b/webroot/rsrc/js/core/behavior-search-typeahead.js @@ -45,6 +45,10 @@ JX.behavior('phabricator-search-typeahead', function(config) { JX.$N('span', {className: 'result-type'}, object.type) ]); + if (object.closed) { + JX.DOM.alterClass(render, 'result-closed', true); + } + object.display = render; return object;