mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-14 02:42:40 +01:00
Allow revisions to be edited from Maniphest
Summary: You can currently attach tasks to revisions from Differential, but not revisions to tasks from Maniphest. Allow editing from either side. This logic is kind of tricky but the alternative was massive code duplication. Test Plan: Added and removed revisions from maniphest. Added and removed tasks from differential. This should have no impact on the Facebook install since none of this is used there. Reviewed By: aran Reviewers: tomo, tuomaspelkonen, jungejason, aran CC: aran, epriestley Differential Revision: 288
This commit is contained in:
parent
3fdc115b54
commit
4b92b2cead
14 changed files with 339 additions and 162 deletions
|
@ -501,7 +501,7 @@ celerity_register_resource_map(array(
|
||||||
),
|
),
|
||||||
'javelin-behavior-phabricator-object-selector' =>
|
'javelin-behavior-phabricator-object-selector' =>
|
||||||
array(
|
array(
|
||||||
'uri' => '/res/fd4ce976/rsrc/js/application/core/behavior-object-selector.js',
|
'uri' => '/res/12d4d90d/rsrc/js/application/core/behavior-object-selector.js',
|
||||||
'type' => 'js',
|
'type' => 'js',
|
||||||
'requires' =>
|
'requires' =>
|
||||||
array(
|
array(
|
||||||
|
|
|
@ -59,6 +59,7 @@ phutil_register_library_map(array(
|
||||||
'AphrontQueryRecoverableException' => 'storage/exception/recoverable',
|
'AphrontQueryRecoverableException' => 'storage/exception/recoverable',
|
||||||
'AphrontRedirectException' => 'aphront/exception/redirect',
|
'AphrontRedirectException' => 'aphront/exception/redirect',
|
||||||
'AphrontRedirectResponse' => 'aphront/response/redirect',
|
'AphrontRedirectResponse' => 'aphront/response/redirect',
|
||||||
|
'AphrontReloadResponse' => 'aphront/response/reload',
|
||||||
'AphrontRequest' => 'aphront/request',
|
'AphrontRequest' => 'aphront/request',
|
||||||
'AphrontRequestFailureView' => 'view/page/failure',
|
'AphrontRequestFailureView' => 'view/page/failure',
|
||||||
'AphrontResponse' => 'aphront/response/base',
|
'AphrontResponse' => 'aphront/response/base',
|
||||||
|
@ -114,7 +115,6 @@ phutil_register_library_map(array(
|
||||||
'DatabaseConfigurationProvider' => 'applications/base/storage/configuration',
|
'DatabaseConfigurationProvider' => 'applications/base/storage/configuration',
|
||||||
'DifferentialAction' => 'applications/differential/constants/action',
|
'DifferentialAction' => 'applications/differential/constants/action',
|
||||||
'DifferentialAddCommentView' => 'applications/differential/view/addcomment',
|
'DifferentialAddCommentView' => 'applications/differential/view/addcomment',
|
||||||
'DifferentialAttachController' => 'applications/differential/controller/attach',
|
|
||||||
'DifferentialCCWelcomeMail' => 'applications/differential/mail/ccwelcome',
|
'DifferentialCCWelcomeMail' => 'applications/differential/mail/ccwelcome',
|
||||||
'DifferentialChangeType' => 'applications/differential/constants/changetype',
|
'DifferentialChangeType' => 'applications/differential/constants/changetype',
|
||||||
'DifferentialChangeset' => 'applications/differential/storage/changeset',
|
'DifferentialChangeset' => 'applications/differential/storage/changeset',
|
||||||
|
@ -255,7 +255,6 @@ phutil_register_library_map(array(
|
||||||
'ManiphestTaskListController' => 'applications/maniphest/controller/tasklist',
|
'ManiphestTaskListController' => 'applications/maniphest/controller/tasklist',
|
||||||
'ManiphestTaskListView' => 'applications/maniphest/view/tasklist',
|
'ManiphestTaskListView' => 'applications/maniphest/view/tasklist',
|
||||||
'ManiphestTaskPriority' => 'applications/maniphest/constants/priority',
|
'ManiphestTaskPriority' => 'applications/maniphest/constants/priority',
|
||||||
'ManiphestTaskSelectorSearchController' => 'applications/maniphest/controller/taskselectorsearch',
|
|
||||||
'ManiphestTaskStatus' => 'applications/maniphest/constants/status',
|
'ManiphestTaskStatus' => 'applications/maniphest/constants/status',
|
||||||
'ManiphestTaskSummaryView' => 'applications/maniphest/view/tasksummary',
|
'ManiphestTaskSummaryView' => 'applications/maniphest/view/tasksummary',
|
||||||
'ManiphestTransaction' => 'applications/maniphest/storage/transaction',
|
'ManiphestTransaction' => 'applications/maniphest/storage/transaction',
|
||||||
|
@ -434,6 +433,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorRepositoryType' => 'applications/repository/constants/repositorytype',
|
'PhabricatorRepositoryType' => 'applications/repository/constants/repositorytype',
|
||||||
'PhabricatorSQLPatchList' => 'infrastructure/setup/sql',
|
'PhabricatorSQLPatchList' => 'infrastructure/setup/sql',
|
||||||
'PhabricatorSearchAbstractDocument' => 'applications/search/index/abstractdocument',
|
'PhabricatorSearchAbstractDocument' => 'applications/search/index/abstractdocument',
|
||||||
|
'PhabricatorSearchAttachController' => 'applications/search/controller/attach',
|
||||||
'PhabricatorSearchBaseController' => 'applications/search/controller/base',
|
'PhabricatorSearchBaseController' => 'applications/search/controller/base',
|
||||||
'PhabricatorSearchController' => 'applications/search/controller/search',
|
'PhabricatorSearchController' => 'applications/search/controller/search',
|
||||||
'PhabricatorSearchDAO' => 'applications/search/storage/base',
|
'PhabricatorSearchDAO' => 'applications/search/storage/base',
|
||||||
|
@ -448,6 +448,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorSearchMySQLExecutor' => 'applications/search/execute/mysql',
|
'PhabricatorSearchMySQLExecutor' => 'applications/search/execute/mysql',
|
||||||
'PhabricatorSearchQuery' => 'applications/search/storage/query',
|
'PhabricatorSearchQuery' => 'applications/search/storage/query',
|
||||||
'PhabricatorSearchRelationship' => 'applications/search/constants/relationship',
|
'PhabricatorSearchRelationship' => 'applications/search/constants/relationship',
|
||||||
|
'PhabricatorSearchSelectController' => 'applications/search/controller/select',
|
||||||
'PhabricatorSetup' => 'infrastructure/setup',
|
'PhabricatorSetup' => 'infrastructure/setup',
|
||||||
'PhabricatorStandardPageView' => 'view/page/standard',
|
'PhabricatorStandardPageView' => 'view/page/standard',
|
||||||
'PhabricatorStatusController' => 'applications/status/base',
|
'PhabricatorStatusController' => 'applications/status/base',
|
||||||
|
@ -560,6 +561,7 @@ phutil_register_library_map(array(
|
||||||
'AphrontQueryRecoverableException' => 'AphrontQueryException',
|
'AphrontQueryRecoverableException' => 'AphrontQueryException',
|
||||||
'AphrontRedirectException' => 'AphrontException',
|
'AphrontRedirectException' => 'AphrontException',
|
||||||
'AphrontRedirectResponse' => 'AphrontResponse',
|
'AphrontRedirectResponse' => 'AphrontResponse',
|
||||||
|
'AphrontReloadResponse' => 'AphrontRedirectResponse',
|
||||||
'AphrontRequestFailureView' => 'AphrontView',
|
'AphrontRequestFailureView' => 'AphrontView',
|
||||||
'AphrontSideNavView' => 'AphrontView',
|
'AphrontSideNavView' => 'AphrontView',
|
||||||
'AphrontTableView' => 'AphrontView',
|
'AphrontTableView' => 'AphrontView',
|
||||||
|
@ -597,7 +599,6 @@ phutil_register_library_map(array(
|
||||||
'DarkConsoleServicesPlugin' => 'DarkConsolePlugin',
|
'DarkConsoleServicesPlugin' => 'DarkConsolePlugin',
|
||||||
'DarkConsoleXHProfPlugin' => 'DarkConsolePlugin',
|
'DarkConsoleXHProfPlugin' => 'DarkConsolePlugin',
|
||||||
'DifferentialAddCommentView' => 'AphrontView',
|
'DifferentialAddCommentView' => 'AphrontView',
|
||||||
'DifferentialAttachController' => 'DifferentialController',
|
|
||||||
'DifferentialCCWelcomeMail' => 'DifferentialReviewRequestMail',
|
'DifferentialCCWelcomeMail' => 'DifferentialReviewRequestMail',
|
||||||
'DifferentialChangeset' => 'DifferentialDAO',
|
'DifferentialChangeset' => 'DifferentialDAO',
|
||||||
'DifferentialChangesetDetailView' => 'AphrontView',
|
'DifferentialChangesetDetailView' => 'AphrontView',
|
||||||
|
@ -691,7 +692,6 @@ phutil_register_library_map(array(
|
||||||
'ManiphestTaskEditController' => 'ManiphestController',
|
'ManiphestTaskEditController' => 'ManiphestController',
|
||||||
'ManiphestTaskListController' => 'ManiphestController',
|
'ManiphestTaskListController' => 'ManiphestController',
|
||||||
'ManiphestTaskListView' => 'AphrontView',
|
'ManiphestTaskListView' => 'AphrontView',
|
||||||
'ManiphestTaskSelectorSearchController' => 'ManiphestController',
|
|
||||||
'ManiphestTaskSummaryView' => 'AphrontView',
|
'ManiphestTaskSummaryView' => 'AphrontView',
|
||||||
'ManiphestTransaction' => 'ManiphestDAO',
|
'ManiphestTransaction' => 'ManiphestDAO',
|
||||||
'ManiphestTransactionDetailView' => 'AphrontView',
|
'ManiphestTransactionDetailView' => 'AphrontView',
|
||||||
|
@ -849,6 +849,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorRepositorySvnCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
|
'PhabricatorRepositorySvnCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
|
||||||
'PhabricatorRepositorySvnCommitDiscoveryDaemon' => 'PhabricatorRepositoryCommitDiscoveryDaemon',
|
'PhabricatorRepositorySvnCommitDiscoveryDaemon' => 'PhabricatorRepositoryCommitDiscoveryDaemon',
|
||||||
'PhabricatorRepositorySvnCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
|
'PhabricatorRepositorySvnCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
|
||||||
|
'PhabricatorSearchAttachController' => 'PhabricatorSearchController',
|
||||||
'PhabricatorSearchBaseController' => 'PhabricatorController',
|
'PhabricatorSearchBaseController' => 'PhabricatorController',
|
||||||
'PhabricatorSearchController' => 'PhabricatorSearchBaseController',
|
'PhabricatorSearchController' => 'PhabricatorSearchBaseController',
|
||||||
'PhabricatorSearchDAO' => 'PhabricatorLiskDAO',
|
'PhabricatorSearchDAO' => 'PhabricatorLiskDAO',
|
||||||
|
@ -859,6 +860,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorSearchManiphestIndexer' => 'PhabricatorSearchDocumentIndexer',
|
'PhabricatorSearchManiphestIndexer' => 'PhabricatorSearchDocumentIndexer',
|
||||||
'PhabricatorSearchMySQLExecutor' => 'PhabricatorSearchExecutor',
|
'PhabricatorSearchMySQLExecutor' => 'PhabricatorSearchExecutor',
|
||||||
'PhabricatorSearchQuery' => 'PhabricatorSearchDAO',
|
'PhabricatorSearchQuery' => 'PhabricatorSearchDAO',
|
||||||
|
'PhabricatorSearchSelectController' => 'PhabricatorSearchController',
|
||||||
'PhabricatorStandardPageView' => 'AphrontPageView',
|
'PhabricatorStandardPageView' => 'AphrontPageView',
|
||||||
'PhabricatorStatusController' => 'PhabricatorController',
|
'PhabricatorStatusController' => 'PhabricatorController',
|
||||||
'PhabricatorTaskmasterDaemon' => 'PhabricatorDaemon',
|
'PhabricatorTaskmasterDaemon' => 'PhabricatorDaemon',
|
||||||
|
|
|
@ -104,7 +104,6 @@ class AphrontDefaultApplicationConfiguration
|
||||||
'edit/(?P<id>\d+)/$' => 'DifferentialInlineCommentEditController',
|
'edit/(?P<id>\d+)/$' => 'DifferentialInlineCommentEditController',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
'attach/(?P<id>\d+)/(?P<type>\w+)/$' => 'DifferentialAttachController',
|
|
||||||
'subscribe/(?P<action>add|rem)/(?P<id>\d+)/$'
|
'subscribe/(?P<action>add|rem)/(?P<id>\d+)/$'
|
||||||
=> 'DifferentialSubscribeController',
|
=> 'DifferentialSubscribeController',
|
||||||
),
|
),
|
||||||
|
@ -162,7 +161,6 @@ class AphrontDefaultApplicationConfiguration
|
||||||
'save/' => 'ManiphestTransactionSaveController',
|
'save/' => 'ManiphestTransactionSaveController',
|
||||||
'preview/(?P<id>\d+)/$' => 'ManiphestTransactionPreviewController',
|
'preview/(?P<id>\d+)/$' => 'ManiphestTransactionPreviewController',
|
||||||
),
|
),
|
||||||
'select/search/$' => 'ManiphestTaskSelectorSearchController',
|
|
||||||
),
|
),
|
||||||
|
|
||||||
'/T(?P<id>\d+)$' => 'ManiphestTaskDetailController',
|
'/T(?P<id>\d+)$' => 'ManiphestTaskDetailController',
|
||||||
|
@ -183,6 +181,10 @@ class AphrontDefaultApplicationConfiguration
|
||||||
'/search/' => array(
|
'/search/' => array(
|
||||||
'$' => 'PhabricatorSearchController',
|
'$' => 'PhabricatorSearchController',
|
||||||
'(?P<id>\d+)/$' => 'PhabricatorSearchController',
|
'(?P<id>\d+)/$' => 'PhabricatorSearchController',
|
||||||
|
'attach/(?P<phid>[^/]+)/(?P<type>\w+)/$'
|
||||||
|
=> 'PhabricatorSearchAttachController',
|
||||||
|
'select/(?P<type>\w+)/$'
|
||||||
|
=> 'PhabricatorSearchSelectController',
|
||||||
),
|
),
|
||||||
|
|
||||||
'/project/' => array(
|
'/project/' => array(
|
||||||
|
@ -351,6 +353,7 @@ class AphrontDefaultApplicationConfiguration
|
||||||
|
|
||||||
public function willSendResponse(AphrontResponse $response) {
|
public function willSendResponse(AphrontResponse $response) {
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
|
$response->setRequest($request);
|
||||||
if ($response instanceof AphrontDialogResponse) {
|
if ($response instanceof AphrontDialogResponse) {
|
||||||
if (!$request->isAjax()) {
|
if (!$request->isAjax()) {
|
||||||
$view = new PhabricatorStandardPageView();
|
$view = new PhabricatorStandardPageView();
|
||||||
|
|
37
src/aphront/response/reload/AphrontReloadResponse.php
Normal file
37
src/aphront/response/reload/AphrontReloadResponse.php
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2011 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When actions happen over a JX.Workflow, we may want to reload the page
|
||||||
|
* if the action is javascript-driven but redirect if it isn't. This preserves
|
||||||
|
* query parameters in the javascript case. A reload response behaves like
|
||||||
|
* a redirect response but causes a page reload when received via workflow.
|
||||||
|
*
|
||||||
|
* @group aphront
|
||||||
|
*/
|
||||||
|
class AphrontReloadResponse extends AphrontRedirectResponse {
|
||||||
|
|
||||||
|
public function getURI() {
|
||||||
|
if ($this->getRequest()->isAjax()) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return parent::getURI();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
12
src/aphront/response/reload/__init__.php
Normal file
12
src/aphront/response/reload/__init__.php
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'aphront/response/redirect');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('AphrontReloadResponse.php');
|
|
@ -1,120 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright 2011 Facebook, Inc.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class DifferentialAttachController extends DifferentialController {
|
|
||||||
|
|
||||||
private $id;
|
|
||||||
private $type;
|
|
||||||
|
|
||||||
public function willProcessRequest(array $data) {
|
|
||||||
$this->id = $data['id'];
|
|
||||||
$this->type = $data['type'];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function processRequest() {
|
|
||||||
|
|
||||||
$request = $this->getRequest();
|
|
||||||
$user = $request->getUser();
|
|
||||||
|
|
||||||
$revision = id(new DifferentialRevision())->load($this->id);
|
|
||||||
if (!$revision) {
|
|
||||||
return new Aphront404Response();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($request->isFormPost()) {
|
|
||||||
$phids = explode(';', $request->getStr('phids'));
|
|
||||||
$old_phids = $revision->getAttachedPHIDs(
|
|
||||||
PhabricatorPHIDConstants::PHID_TYPE_TASK);
|
|
||||||
|
|
||||||
if (($phids || $old_phids) && ($phids != $old_phids)) {
|
|
||||||
$tasks = id(new ManiphestTask())->loadAllWhere(
|
|
||||||
'phid in (%Ls)',
|
|
||||||
array_merge($phids, $old_phids));
|
|
||||||
$tasks = mpull($tasks, null, 'getPHID');
|
|
||||||
|
|
||||||
// Remove PHIDs which don't actually exist.
|
|
||||||
$phids = array_keys(array_select_keys($tasks, $phids));
|
|
||||||
|
|
||||||
$revision->setAttachedPHIDs($this->type, $phids);
|
|
||||||
$revision->save();
|
|
||||||
|
|
||||||
$editor = new ManiphestTransactionEditor();
|
|
||||||
$type = ManiphestTransactionType::TYPE_ATTACH;
|
|
||||||
foreach ($tasks as $task) {
|
|
||||||
$transaction = new ManiphestTransaction();
|
|
||||||
$transaction->setAuthorPHID($user->getPHID());
|
|
||||||
$transaction->setTransactionType($type);
|
|
||||||
$new = $task->getAttached();
|
|
||||||
if (empty($new[PhabricatorPHIDConstants::PHID_TYPE_DREV])) {
|
|
||||||
$new[PhabricatorPHIDConstants::PHID_TYPE_DREV] = array();
|
|
||||||
}
|
|
||||||
$rev_phid = $revision->getPHID();
|
|
||||||
if (in_array($task->getPHID(), $phids)) {
|
|
||||||
if (in_array($rev_phid, $task->getAttachedPHIDs(
|
|
||||||
PhabricatorPHIDConstants::PHID_TYPE_DREV))) {
|
|
||||||
// TODO: maybe the transaction editor should be responsible for
|
|
||||||
// this?
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$new[PhabricatorPHIDConstants::PHID_TYPE_DREV][$rev_phid] = array();
|
|
||||||
} else {
|
|
||||||
if (!in_array($rev_phid, $task->getAttachedPHIDs(
|
|
||||||
PhabricatorPHIDConstants::PHID_TYPE_DREV))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
unset($new[PhabricatorPHIDConstants::PHID_TYPE_DREV][$rev_phid]);
|
|
||||||
}
|
|
||||||
$transaction->setNewValue($new);
|
|
||||||
$editor->applyTransactions($task, array($transaction));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($request->isAjax()) {
|
|
||||||
return id(new AphrontRedirectResponse());
|
|
||||||
} else {
|
|
||||||
return id(new AphrontRedirectResponse())
|
|
||||||
->setURI('/D'.$revision->getID());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$phids = $revision->getAttachedPHIDs($this->type);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$handles = id(new PhabricatorObjectHandleData($phids))
|
|
||||||
->loadHandles();
|
|
||||||
|
|
||||||
$obj_dialog = new PhabricatorObjectSelectorDialog();
|
|
||||||
$obj_dialog
|
|
||||||
->setUser($user)
|
|
||||||
->setHandles($handles)
|
|
||||||
->setFilters(array(
|
|
||||||
'assigned' => 'Assigned to Me',
|
|
||||||
'created' => 'Created By Me',
|
|
||||||
'open' => 'All Open Tasks',
|
|
||||||
'all' => 'All Tasks',
|
|
||||||
))
|
|
||||||
->setCancelURI('#')
|
|
||||||
->setSearchURI('/maniphest/select/search/')
|
|
||||||
->setNoun('Tasks');
|
|
||||||
|
|
||||||
$dialog = $obj_dialog->buildDialog();
|
|
||||||
|
|
||||||
return id(new AphrontDialogResponse())->setDialog($dialog);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -464,7 +464,7 @@ class DifferentialRevisionViewController extends DifferentialController {
|
||||||
$links[] = array(
|
$links[] = array(
|
||||||
'class' => 'attach-maniphest',
|
'class' => 'attach-maniphest',
|
||||||
'name' => 'Edit Maniphest Tasks',
|
'name' => 'Edit Maniphest Tasks',
|
||||||
'href' => "/differential/attach/{$revision_id}/TASK/",
|
'href' => "/search/attach/{$revision_phid}/TASK/",
|
||||||
'sigil' => 'workflow',
|
'sigil' => 'workflow',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,9 +162,14 @@ class ManiphestTaskDetailController extends ManiphestController {
|
||||||
$action->setClass('action-edit');
|
$action->setClass('action-edit');
|
||||||
$actions[] = $action;
|
$actions[] = $action;
|
||||||
|
|
||||||
|
require_celerity_resource('phabricator-object-selector-css');
|
||||||
|
require_celerity_resource('javelin-behavior-phabricator-object-selector');
|
||||||
|
|
||||||
$action = new AphrontHeadsupActionView();
|
$action = new AphrontHeadsupActionView();
|
||||||
$action->setName('Edit Differential Revisions');
|
$action->setName('Edit Differential Revisions');
|
||||||
$action->setClass('action-attach unavailable');
|
$action->setURI('/search/attach/'.$task->getPHID().'/DREV/');
|
||||||
|
$action->setWorkflow(true);
|
||||||
|
$action->setClass('action-attach');
|
||||||
$actions[] = $action;
|
$actions[] = $action;
|
||||||
|
|
||||||
$action_list = new AphrontHeadsupActionListView();
|
$action_list = new AphrontHeadsupActionListView();
|
||||||
|
|
|
@ -0,0 +1,199 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2011 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class PhabricatorSearchAttachController extends PhabricatorSearchController {
|
||||||
|
|
||||||
|
private $phid;
|
||||||
|
private $type;
|
||||||
|
|
||||||
|
public function willProcessRequest(array $data) {
|
||||||
|
$this->phid = $data['phid'];
|
||||||
|
$this->type = $data['type'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function processRequest() {
|
||||||
|
|
||||||
|
$request = $this->getRequest();
|
||||||
|
$user = $request->getUser();
|
||||||
|
|
||||||
|
$handles = id(new PhabricatorObjectHandleData(array($this->phid)))
|
||||||
|
->loadHandles();
|
||||||
|
$handle = $handles[$this->phid];
|
||||||
|
|
||||||
|
$object_phid = $this->phid;
|
||||||
|
$object_type = $handle->getType();
|
||||||
|
$attach_type = $this->type;
|
||||||
|
|
||||||
|
|
||||||
|
// Load the object we're going to attach/detach stuff from. This is the
|
||||||
|
// object that triggered the action, e.g. the revision you clicked
|
||||||
|
// "Edit Maniphest Tasks" on.
|
||||||
|
$object = null;
|
||||||
|
switch ($object_type) {
|
||||||
|
case PhabricatorPHIDConstants::PHID_TYPE_DREV:
|
||||||
|
$object = id(new DifferentialRevision())->loadOneWhere(
|
||||||
|
'phid = %s',
|
||||||
|
$this->phid);
|
||||||
|
break;
|
||||||
|
case PhabricatorPHIDConstants::PHID_TYPE_TASK:
|
||||||
|
$object = id(new ManiphestTask())->loadOneWhere(
|
||||||
|
'phid = %s',
|
||||||
|
$this->phid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$object) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($request->isFormPost()) {
|
||||||
|
$phids = explode(';', $request->getStr('phids'));
|
||||||
|
$phids = array_filter($phids);
|
||||||
|
$phids = array_values($phids);
|
||||||
|
// sort() so that removing [X, Y] and then adding [Y, X] is correctly
|
||||||
|
// detected as a no-op.
|
||||||
|
sort($phids);
|
||||||
|
|
||||||
|
$old_phids = $object->getAttachedPHIDs($attach_type);
|
||||||
|
sort($old_phids);
|
||||||
|
|
||||||
|
if (($phids || $old_phids) && ($phids !== $old_phids)) {
|
||||||
|
|
||||||
|
// Load all the objects we're attaching or detaching from the main
|
||||||
|
// object.
|
||||||
|
switch ($attach_type) {
|
||||||
|
case PhabricatorPHIDConstants::PHID_TYPE_DREV:
|
||||||
|
$attach_objs = id(new DifferentialRevision())->loadAllWhere(
|
||||||
|
'phid IN (%Ls)',
|
||||||
|
array_merge($phids, $old_phids));
|
||||||
|
break;
|
||||||
|
case PhabricatorPHIDConstants::PHID_TYPE_TASK:
|
||||||
|
$attach_objs = id(new ManiphestTask())->loadAllWhere(
|
||||||
|
'phid IN (%Ls)',
|
||||||
|
array_merge($phids, $old_phids));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$attach_objs = mpull($attach_objs, null, 'getPHID');
|
||||||
|
|
||||||
|
// Remove PHIDs which don't actually exist, to prevent silliness.
|
||||||
|
$phids = array_keys(array_select_keys($attach_objs, $phids));
|
||||||
|
if ($phids) {
|
||||||
|
$phids = array_combine($phids, $phids);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the primary object.
|
||||||
|
switch ($object_type) {
|
||||||
|
case PhabricatorPHIDConstants::PHID_TYPE_DREV:
|
||||||
|
$object->setAttachedPHIDs($attach_type, $phids);
|
||||||
|
$object->save();
|
||||||
|
break;
|
||||||
|
case PhabricatorPHIDConstants::PHID_TYPE_TASK:
|
||||||
|
$this->applyTaskTransaction(
|
||||||
|
$object,
|
||||||
|
$attach_type,
|
||||||
|
$phids);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop through all of the attached/detached objects and update them.
|
||||||
|
foreach ($attach_objs as $phid => $attach_obj) {
|
||||||
|
$attached_phids = $attach_obj->getAttachedPHIDs($object_type);
|
||||||
|
// Figure out if we're attaching or detaching this object.
|
||||||
|
if (isset($phids[$phid])) {
|
||||||
|
$attached_phids[] = $object_phid;
|
||||||
|
} else {
|
||||||
|
$attached_phids = array_fill_keys($attached_phids, true);
|
||||||
|
unset($attached_phids[$object_phid]);
|
||||||
|
$attached_phids = array_keys($attached_phids);
|
||||||
|
}
|
||||||
|
switch ($attach_type) {
|
||||||
|
case PhabricatorPHIDConstants::PHID_TYPE_DREV:
|
||||||
|
$attach_obj->setAttachedPHIDs($object_type, $attached_phids);
|
||||||
|
$attach_obj->save();
|
||||||
|
break;
|
||||||
|
case PhabricatorPHIDConstants::PHID_TYPE_TASK:
|
||||||
|
$this->applyTaskTransaction(
|
||||||
|
$attach_obj,
|
||||||
|
$object_type,
|
||||||
|
$attached_phids);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return id(new AphrontReloadResponse())->setURI($handle->getURI());
|
||||||
|
} else {
|
||||||
|
$phids = $object->getAttachedPHIDs($attach_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($attach_type) {
|
||||||
|
case PhabricatorPHIDConstants::PHID_TYPE_DREV:
|
||||||
|
$noun = 'Revisions';
|
||||||
|
$selected = 'created';
|
||||||
|
break;
|
||||||
|
case PhabricatorPHIDConstants::PHID_TYPE_TASK:
|
||||||
|
$noun = 'Tasks';
|
||||||
|
$selected = 'assigned';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$handles = id(new PhabricatorObjectHandleData($phids))
|
||||||
|
->loadHandles();
|
||||||
|
|
||||||
|
$obj_dialog = new PhabricatorObjectSelectorDialog();
|
||||||
|
$obj_dialog
|
||||||
|
->setUser($user)
|
||||||
|
->setHandles($handles)
|
||||||
|
->setFilters(array(
|
||||||
|
'assigned' => 'Assigned to Me',
|
||||||
|
'created' => 'Created By Me',
|
||||||
|
'open' => 'All Open '.$noun,
|
||||||
|
'all' => 'All '.$noun,
|
||||||
|
))
|
||||||
|
->setSelectedFilter($selected)
|
||||||
|
->setCancelURI($handle->getURI())
|
||||||
|
->setSearchURI('/search/select/'.$attach_type.'/')
|
||||||
|
->setNoun($noun);
|
||||||
|
|
||||||
|
$dialog = $obj_dialog->buildDialog();
|
||||||
|
|
||||||
|
return id(new AphrontDialogResponse())->setDialog($dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function applyTaskTransaction(
|
||||||
|
ManiphestTask $task,
|
||||||
|
$attach_type,
|
||||||
|
array $new_phids) {
|
||||||
|
|
||||||
|
$user = $this->getRequest()->getUser();
|
||||||
|
|
||||||
|
$editor = new ManiphestTransactionEditor();
|
||||||
|
$type = ManiphestTransactionType::TYPE_ATTACH;
|
||||||
|
|
||||||
|
$transaction = new ManiphestTransaction();
|
||||||
|
$transaction->setAuthorPHID($user->getPHID());
|
||||||
|
$transaction->setTransactionType($type);
|
||||||
|
|
||||||
|
$new = $task->getAttached();
|
||||||
|
$new[$attach_type] = array_fill_keys($new_phids, array());
|
||||||
|
|
||||||
|
$transaction->setNewValue($new);
|
||||||
|
$editor->applyTransactions($task, array($transaction));
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,8 +8,7 @@
|
||||||
|
|
||||||
phutil_require_module('phabricator', 'aphront/response/404');
|
phutil_require_module('phabricator', 'aphront/response/404');
|
||||||
phutil_require_module('phabricator', 'aphront/response/dialog');
|
phutil_require_module('phabricator', 'aphront/response/dialog');
|
||||||
phutil_require_module('phabricator', 'aphront/response/redirect');
|
phutil_require_module('phabricator', 'aphront/response/reload');
|
||||||
phutil_require_module('phabricator', 'applications/differential/controller/base');
|
|
||||||
phutil_require_module('phabricator', 'applications/differential/storage/revision');
|
phutil_require_module('phabricator', 'applications/differential/storage/revision');
|
||||||
phutil_require_module('phabricator', 'applications/maniphest/constants/transactiontype');
|
phutil_require_module('phabricator', 'applications/maniphest/constants/transactiontype');
|
||||||
phutil_require_module('phabricator', 'applications/maniphest/editor/transaction');
|
phutil_require_module('phabricator', 'applications/maniphest/editor/transaction');
|
||||||
|
@ -17,9 +16,10 @@ phutil_require_module('phabricator', 'applications/maniphest/storage/task');
|
||||||
phutil_require_module('phabricator', 'applications/maniphest/storage/transaction');
|
phutil_require_module('phabricator', 'applications/maniphest/storage/transaction');
|
||||||
phutil_require_module('phabricator', 'applications/phid/constants');
|
phutil_require_module('phabricator', 'applications/phid/constants');
|
||||||
phutil_require_module('phabricator', 'applications/phid/handle/data');
|
phutil_require_module('phabricator', 'applications/phid/handle/data');
|
||||||
|
phutil_require_module('phabricator', 'applications/search/controller/search');
|
||||||
phutil_require_module('phabricator', 'view/control/objectselector');
|
phutil_require_module('phabricator', 'view/control/objectselector');
|
||||||
|
|
||||||
phutil_require_module('phutil', 'utils');
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
phutil_require_source('DifferentialAttachController.php');
|
phutil_require_source('PhabricatorSearchAttachController.php');
|
|
@ -16,7 +16,14 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class ManiphestTaskSelectorSearchController extends ManiphestController {
|
class PhabricatorSearchSelectController
|
||||||
|
extends PhabricatorSearchController {
|
||||||
|
|
||||||
|
private $type;
|
||||||
|
|
||||||
|
public function willProcessRequest(array $data) {
|
||||||
|
$this->type = $data['type'];
|
||||||
|
}
|
||||||
|
|
||||||
public function processRequest() {
|
public function processRequest() {
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
|
@ -26,16 +33,9 @@ class ManiphestTaskSelectorSearchController extends ManiphestController {
|
||||||
|
|
||||||
$query_str = $request->getStr('query');
|
$query_str = $request->getStr('query');
|
||||||
$matches = array();
|
$matches = array();
|
||||||
$task_ids = array();
|
|
||||||
|
|
||||||
// Collect all task IDs, e.g., T12 T651 T631, from the query string
|
|
||||||
preg_match_all('/\bT(\d+)\b/', $query_str, $matches);
|
|
||||||
if ($matches) {
|
|
||||||
$task_ids = $matches[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
$query->setQuery($query_str);
|
$query->setQuery($query_str);
|
||||||
$query->setParameter('type', PhabricatorPHIDConstants::PHID_TYPE_TASK);
|
$query->setParameter('type', $this->type);
|
||||||
|
|
||||||
switch ($request->getStr('filter')) {
|
switch ($request->getStr('filter')) {
|
||||||
case 'assigned':
|
case 'assigned':
|
||||||
|
@ -54,23 +54,8 @@ class ManiphestTaskSelectorSearchController extends ManiphestController {
|
||||||
$exec = new PhabricatorSearchMySQLExecutor();
|
$exec = new PhabricatorSearchMySQLExecutor();
|
||||||
$results = $exec->executeSearch($query);
|
$results = $exec->executeSearch($query);
|
||||||
|
|
||||||
$phids = array();
|
$phids = array_fill_keys(ipull($results, 'phid'), true);
|
||||||
|
$phids += $this->queryObjectNames($query_str);
|
||||||
foreach ($results as $result) {
|
|
||||||
$phids[$result['phid']] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do a separate query for task IDs if the query had them
|
|
||||||
if ($task_ids) {
|
|
||||||
$task_object = new ManiphestTask();
|
|
||||||
|
|
||||||
// It's OK to ignore filters, if user wants specific task IDs
|
|
||||||
$tasks = $task_object->loadAllWhere('id IN (%Ls)', $task_ids);
|
|
||||||
|
|
||||||
foreach ($tasks as $task) {
|
|
||||||
$phids[$task->getPHID()] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$phids = array_keys($phids);
|
$phids = array_keys($phids);
|
||||||
$handles = id(new PhabricatorObjectHandleData($phids))
|
$handles = id(new PhabricatorObjectHandleData($phids))
|
||||||
|
@ -84,4 +69,48 @@ class ManiphestTaskSelectorSearchController extends ManiphestController {
|
||||||
|
|
||||||
return id(new AphrontAjaxResponse())->setContent($data);
|
return id(new AphrontAjaxResponse())->setContent($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function queryObjectNames($query) {
|
||||||
|
|
||||||
|
$pattern = null;
|
||||||
|
switch ($this->type) {
|
||||||
|
case PhabricatorPHIDConstants::PHID_TYPE_TASK:
|
||||||
|
$pattern = '/\bT(\d+)\b/';
|
||||||
|
break;
|
||||||
|
case PhabricatorPHIDConstants::PHID_TYPE_DREV:
|
||||||
|
$pattern = '/\bD(\d+)\b/';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$pattern) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$matches = array();
|
||||||
|
preg_match_all($pattern, $query, $matches);
|
||||||
|
if (!$matches) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$object_ids = $matches[1];
|
||||||
|
if (!$object_ids) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($this->type) {
|
||||||
|
case PhabricatorPHIDConstants::PHID_TYPE_DREV:
|
||||||
|
$objects = id(new DifferentialRevision())->loadAllWhere(
|
||||||
|
'id IN (%Ld)',
|
||||||
|
$object_ids);
|
||||||
|
break;
|
||||||
|
case PhabricatorPHIDConstants::PHID_TYPE_TASK:
|
||||||
|
$objects = id(new ManiphestTask())->loadAllWhere(
|
||||||
|
'id IN (%Ld)',
|
||||||
|
$object_ids);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_fill_keys(mpull($objects, 'getPHID'), true);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -7,15 +7,16 @@
|
||||||
|
|
||||||
|
|
||||||
phutil_require_module('phabricator', 'aphront/response/ajax');
|
phutil_require_module('phabricator', 'aphront/response/ajax');
|
||||||
phutil_require_module('phabricator', 'applications/maniphest/controller/base');
|
phutil_require_module('phabricator', 'applications/differential/storage/revision');
|
||||||
phutil_require_module('phabricator', 'applications/maniphest/storage/task');
|
phutil_require_module('phabricator', 'applications/maniphest/storage/task');
|
||||||
phutil_require_module('phabricator', 'applications/phid/constants');
|
phutil_require_module('phabricator', 'applications/phid/constants');
|
||||||
phutil_require_module('phabricator', 'applications/phid/handle/data');
|
phutil_require_module('phabricator', 'applications/phid/handle/data');
|
||||||
phutil_require_module('phabricator', 'applications/phid/handle/view/selector');
|
phutil_require_module('phabricator', 'applications/phid/handle/view/selector');
|
||||||
|
phutil_require_module('phabricator', 'applications/search/controller/search');
|
||||||
phutil_require_module('phabricator', 'applications/search/execute/mysql');
|
phutil_require_module('phabricator', 'applications/search/execute/mysql');
|
||||||
phutil_require_module('phabricator', 'applications/search/storage/query');
|
phutil_require_module('phabricator', 'applications/search/storage/query');
|
||||||
|
|
||||||
phutil_require_module('phutil', 'utils');
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
phutil_require_source('ManiphestTaskSelectorSearchController.php');
|
phutil_require_source('PhabricatorSearchSelectController.php');
|
|
@ -25,6 +25,7 @@ class PhabricatorObjectSelectorDialog {
|
||||||
private $submitURI;
|
private $submitURI;
|
||||||
private $noun;
|
private $noun;
|
||||||
private $searchURI;
|
private $searchURI;
|
||||||
|
private $selectedFilter;
|
||||||
|
|
||||||
public function setUser($user) {
|
public function setUser($user) {
|
||||||
$this->user = $user;
|
$this->user = $user;
|
||||||
|
@ -36,6 +37,11 @@ class PhabricatorObjectSelectorDialog {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setSelectedFilter($selected_filter) {
|
||||||
|
$this->selectedFilter = $selected_filter;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function setHandles(array $handles) {
|
public function setHandles(array $handles) {
|
||||||
$this->handles = $handles;
|
$this->handles = $handles;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -78,7 +84,10 @@ class PhabricatorObjectSelectorDialog {
|
||||||
$options[] = phutil_render_tag(
|
$options[] = phutil_render_tag(
|
||||||
'option',
|
'option',
|
||||||
array(
|
array(
|
||||||
'value' => $key
|
'value' => $key,
|
||||||
|
'selected' => ($key == $this->selectedFilter)
|
||||||
|
? 'selected'
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
$label);
|
$label);
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,7 +164,7 @@ JX.behavior('phabricator-object-selector', function(config) {
|
||||||
|
|
||||||
JX.DOM.listen(
|
JX.DOM.listen(
|
||||||
JX.$(config.query),
|
JX.$(config.query),
|
||||||
'keydown',
|
['change', 'keydown', 'keyup', 'keypress'],
|
||||||
null,
|
null,
|
||||||
function(e) {
|
function(e) {
|
||||||
var cur_value = JX.$(config.query).value;
|
var cur_value = JX.$(config.query).value;
|
||||||
|
|
Loading…
Reference in a new issue