1
0
Fork 0
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:
epriestley 2011-05-16 11:43:39 -07:00
parent 3fdc115b54
commit 4b92b2cead
14 changed files with 339 additions and 162 deletions

View file

@ -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(

View file

@ -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',

View file

@ -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();

View 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();
}
}
}

View 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');

View file

@ -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);
}
}

View file

@ -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',
); );
} }

View file

@ -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();

View file

@ -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));
}
}

View file

@ -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');

View file

@ -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);
}
} }

View file

@ -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');

View file

@ -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);
} }

View file

@ -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;