mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-22 23:02:42 +01:00
Move object-selector closable to being usable.
Summary: Test Plan: Reviewers: CC:
This commit is contained in:
parent
258e0cdded
commit
eec3e8e3aa
32 changed files with 653 additions and 210 deletions
|
@ -18,7 +18,7 @@ celerity_register_resource_map(array(
|
||||||
),
|
),
|
||||||
'aphront-dialog-view-css' =>
|
'aphront-dialog-view-css' =>
|
||||||
array(
|
array(
|
||||||
'uri' => '/res/a05107ae/rsrc/css/aphront/dialog-view.css',
|
'uri' => '/res/c8324e86/rsrc/css/aphront/dialog-view.css',
|
||||||
'type' => 'css',
|
'type' => 'css',
|
||||||
'requires' =>
|
'requires' =>
|
||||||
array(
|
array(
|
||||||
|
@ -217,7 +217,7 @@ celerity_register_resource_map(array(
|
||||||
),
|
),
|
||||||
'phabricator-object-selector-css' =>
|
'phabricator-object-selector-css' =>
|
||||||
array(
|
array(
|
||||||
'uri' => '/res/270ce107/rsrc/css/application/objectselector/object-selector.css',
|
'uri' => '/res/52a7e289/rsrc/css/application/objectselector/object-selector.css',
|
||||||
'type' => 'css',
|
'type' => 'css',
|
||||||
'requires' =>
|
'requires' =>
|
||||||
array(
|
array(
|
||||||
|
@ -272,7 +272,7 @@ celerity_register_resource_map(array(
|
||||||
),
|
),
|
||||||
'javelin-behavior-phabricator-object-selector' =>
|
'javelin-behavior-phabricator-object-selector' =>
|
||||||
array(
|
array(
|
||||||
'uri' => '/res/7f7eda6a/rsrc/js/application/core/behavior-object-selector.js',
|
'uri' => '/res/e849ced6/rsrc/js/application/core/behavior-object-selector.js',
|
||||||
'type' => 'js',
|
'type' => 'js',
|
||||||
'requires' =>
|
'requires' =>
|
||||||
array(
|
array(
|
||||||
|
@ -455,7 +455,7 @@ celerity_register_resource_map(array(
|
||||||
), array (
|
), array (
|
||||||
'packages' =>
|
'packages' =>
|
||||||
array (
|
array (
|
||||||
'4f907a28' =>
|
'aa43d409' =>
|
||||||
array (
|
array (
|
||||||
'name' => 'core.pkg.css',
|
'name' => 'core.pkg.css',
|
||||||
'symbols' =>
|
'symbols' =>
|
||||||
|
@ -474,7 +474,7 @@ celerity_register_resource_map(array(
|
||||||
11 => 'phabricator-remarkup-css',
|
11 => 'phabricator-remarkup-css',
|
||||||
12 => 'syntax-highlighting-css',
|
12 => 'syntax-highlighting-css',
|
||||||
),
|
),
|
||||||
'uri' => '/res/pkg/4f907a28/core.pkg.css',
|
'uri' => '/res/pkg/aa43d409/core.pkg.css',
|
||||||
'type' => 'css',
|
'type' => 'css',
|
||||||
),
|
),
|
||||||
'2525bbc7' =>
|
'2525bbc7' =>
|
||||||
|
@ -511,19 +511,19 @@ celerity_register_resource_map(array(
|
||||||
),
|
),
|
||||||
'reverse' =>
|
'reverse' =>
|
||||||
array (
|
array (
|
||||||
'phabricator-core-css' => '4f907a28',
|
'phabricator-core-css' => 'aa43d409',
|
||||||
'phabricator-core-buttons-css' => '4f907a28',
|
'phabricator-core-buttons-css' => 'aa43d409',
|
||||||
'phabricator-standard-page-view' => '4f907a28',
|
'phabricator-standard-page-view' => 'aa43d409',
|
||||||
'aphront-dialog-view-css' => '4f907a28',
|
'aphront-dialog-view-css' => 'aa43d409',
|
||||||
'aphront-form-view-css' => '4f907a28',
|
'aphront-form-view-css' => 'aa43d409',
|
||||||
'aphront-panel-view-css' => '4f907a28',
|
'aphront-panel-view-css' => 'aa43d409',
|
||||||
'aphront-side-nav-view-css' => '4f907a28',
|
'aphront-side-nav-view-css' => 'aa43d409',
|
||||||
'aphront-table-view-css' => '4f907a28',
|
'aphront-table-view-css' => 'aa43d409',
|
||||||
'aphront-tokenizer-control-css' => '4f907a28',
|
'aphront-tokenizer-control-css' => 'aa43d409',
|
||||||
'aphront-typeahead-control-css' => '4f907a28',
|
'aphront-typeahead-control-css' => 'aa43d409',
|
||||||
'phabricator-directory-css' => '4f907a28',
|
'phabricator-directory-css' => 'aa43d409',
|
||||||
'phabricator-remarkup-css' => '4f907a28',
|
'phabricator-remarkup-css' => 'aa43d409',
|
||||||
'syntax-highlighting-css' => '4f907a28',
|
'syntax-highlighting-css' => 'aa43d409',
|
||||||
'differential-core-view-css' => '2525bbc7',
|
'differential-core-view-css' => '2525bbc7',
|
||||||
'differential-changeset-view-css' => '2525bbc7',
|
'differential-changeset-view-css' => '2525bbc7',
|
||||||
'differential-revision-detail-css' => '2525bbc7',
|
'differential-revision-detail-css' => '2525bbc7',
|
||||||
|
|
|
@ -89,6 +89,7 @@ phutil_register_library_map(array(
|
||||||
'DarkConsoleXHProfPluginAPI' => 'aphront/console/plugin/xhprof/api',
|
'DarkConsoleXHProfPluginAPI' => 'aphront/console/plugin/xhprof/api',
|
||||||
'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',
|
||||||
|
@ -191,6 +192,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorFileURI' => 'applications/files/uri',
|
'PhabricatorFileURI' => 'applications/files/uri',
|
||||||
'PhabricatorFileUploadController' => 'applications/files/controller/upload',
|
'PhabricatorFileUploadController' => 'applications/files/controller/upload',
|
||||||
'PhabricatorFileViewController' => 'applications/files/controller/view',
|
'PhabricatorFileViewController' => 'applications/files/controller/view',
|
||||||
|
'PhabricatorHandleObjectSelectorDataView' => 'applications/phid/handle/view/selector',
|
||||||
'PhabricatorLiskDAO' => 'applications/base/storage/lisk',
|
'PhabricatorLiskDAO' => 'applications/base/storage/lisk',
|
||||||
'PhabricatorLoginController' => 'applications/auth/controller/login',
|
'PhabricatorLoginController' => 'applications/auth/controller/login',
|
||||||
'PhabricatorLogoutController' => 'applications/auth/controller/logout',
|
'PhabricatorLogoutController' => 'applications/auth/controller/logout',
|
||||||
|
@ -341,6 +343,7 @@ 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',
|
||||||
|
@ -435,7 +438,6 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorMetaMTAMailingListsController' => 'PhabricatorMetaMTAController',
|
'PhabricatorMetaMTAMailingListsController' => 'PhabricatorMetaMTAController',
|
||||||
'PhabricatorMetaMTASendController' => 'PhabricatorMetaMTAController',
|
'PhabricatorMetaMTASendController' => 'PhabricatorMetaMTAController',
|
||||||
'PhabricatorMetaMTAViewController' => 'PhabricatorMetaMTAController',
|
'PhabricatorMetaMTAViewController' => 'PhabricatorMetaMTAController',
|
||||||
'PhabricatorObjectSelectorDialog' => 'AphrontDialogView',
|
|
||||||
'PhabricatorPHID' => 'PhabricatorPHIDDAO',
|
'PhabricatorPHID' => 'PhabricatorPHIDDAO',
|
||||||
'PhabricatorPHIDAllocateController' => 'PhabricatorPHIDController',
|
'PhabricatorPHIDAllocateController' => 'PhabricatorPHIDController',
|
||||||
'PhabricatorPHIDController' => 'PhabricatorController',
|
'PhabricatorPHIDController' => 'PhabricatorController',
|
||||||
|
|
|
@ -92,6 +92,7 @@ class AphrontDefaultApplicationConfiguration
|
||||||
'edit/(?P<id>\d+)/$' => 'DifferentialInlineCommentEditController',
|
'edit/(?P<id>\d+)/$' => 'DifferentialInlineCommentEditController',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
'attach/(?P<id>\d+)/(?P<type>\w+)/$' => 'DifferentialAttachController',
|
||||||
),
|
),
|
||||||
|
|
||||||
'/res/' => array(
|
'/res/' => array(
|
||||||
|
@ -183,6 +184,21 @@ class AphrontDefaultApplicationConfiguration
|
||||||
'<code>'.phutil_escape_html((string)$ex).'</code>'.
|
'<code>'.phutil_escape_html((string)$ex).'</code>'.
|
||||||
'</div>';
|
'</div>';
|
||||||
|
|
||||||
|
if ($this->getRequest()->isAjax()) {
|
||||||
|
$dialog = new AphrontDialogView();
|
||||||
|
$dialog
|
||||||
|
->setTitle('Exception!')
|
||||||
|
->setClass('aphront-exception-dialog')
|
||||||
|
->setUser($this->getRequest()->getUser())
|
||||||
|
->appendChild($content)
|
||||||
|
->addCancelButton('/');
|
||||||
|
|
||||||
|
$response = new AphrontDialogResponse();
|
||||||
|
$response->setDialog($dialog);
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
|
||||||
$view = new PhabricatorStandardPageView();
|
$view = new PhabricatorStandardPageView();
|
||||||
$view->setRequest($this->getRequest());
|
$view->setRequest($this->getRequest());
|
||||||
$view->appendChild($content);
|
$view->appendChild($content);
|
||||||
|
|
|
@ -9,8 +9,10 @@
|
||||||
phutil_require_module('phabricator', 'aphront/applicationconfiguration');
|
phutil_require_module('phabricator', 'aphront/applicationconfiguration');
|
||||||
phutil_require_module('phabricator', 'aphront/request');
|
phutil_require_module('phabricator', 'aphront/request');
|
||||||
phutil_require_module('phabricator', 'aphront/response/ajax');
|
phutil_require_module('phabricator', 'aphront/response/ajax');
|
||||||
|
phutil_require_module('phabricator', 'aphront/response/dialog');
|
||||||
phutil_require_module('phabricator', 'aphront/response/webpage');
|
phutil_require_module('phabricator', 'aphront/response/webpage');
|
||||||
phutil_require_module('phabricator', 'applications/base/controller/404');
|
phutil_require_module('phabricator', 'applications/base/controller/404');
|
||||||
|
phutil_require_module('phabricator', 'view/dialog');
|
||||||
phutil_require_module('phabricator', 'view/page/failure');
|
phutil_require_module('phabricator', 'view/page/failure');
|
||||||
phutil_require_module('phabricator', 'view/page/standard');
|
phutil_require_module('phabricator', 'view/page/standard');
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
<?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('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['DREV'])) {
|
||||||
|
$new['DREV'] = array();
|
||||||
|
}
|
||||||
|
$rev_phid = $revision->getPHID();
|
||||||
|
if (in_array($task->getPHID(), $phids)) {
|
||||||
|
if (in_array($rev_phid, $task->getAttachedPHIDs('DREV'))) {
|
||||||
|
// TODO: maybe the transaction editor should be responsible for
|
||||||
|
// this?
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$new['DREV'][$rev_phid] = array();
|
||||||
|
} else {
|
||||||
|
if (!in_array($rev_phid, $task->getAttachedPHIDs('DREV'))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
unset($new['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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
24
src/applications/differential/controller/attach/__init__.php
Normal file
24
src/applications/differential/controller/attach/__init__.php
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'aphront/response/404');
|
||||||
|
phutil_require_module('phabricator', 'aphront/response/dialog');
|
||||||
|
phutil_require_module('phabricator', 'aphront/response/redirect');
|
||||||
|
phutil_require_module('phabricator', 'applications/differential/controller/base');
|
||||||
|
phutil_require_module('phabricator', 'applications/differential/storage/revision');
|
||||||
|
phutil_require_module('phabricator', 'applications/maniphest/constants/transactiontype');
|
||||||
|
phutil_require_module('phabricator', 'applications/maniphest/editor/transaction');
|
||||||
|
phutil_require_module('phabricator', 'applications/maniphest/storage/task');
|
||||||
|
phutil_require_module('phabricator', 'applications/maniphest/storage/transaction');
|
||||||
|
phutil_require_module('phabricator', 'applications/phid/handle/data');
|
||||||
|
phutil_require_module('phabricator', 'view/control/objectselector');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('DifferentialAttachController.php');
|
|
@ -64,6 +64,11 @@ class DifferentialRevisionViewController extends DifferentialController {
|
||||||
$user->getPHID(),
|
$user->getPHID(),
|
||||||
),
|
),
|
||||||
mpull($comments, 'getAuthorPHID'));
|
mpull($comments, 'getAuthorPHID'));
|
||||||
|
foreach ($revision->getAttached() as $type => $phids) {
|
||||||
|
foreach ($phids as $phid => $info) {
|
||||||
|
$object_phids[] = $phid;
|
||||||
|
}
|
||||||
|
}
|
||||||
$object_phids = array_unique($object_phids);
|
$object_phids = array_unique($object_phids);
|
||||||
|
|
||||||
$handles = id(new PhabricatorObjectHandleData($object_phids))
|
$handles = id(new PhabricatorObjectHandleData($object_phids))
|
||||||
|
@ -229,6 +234,15 @@ class DifferentialRevisionViewController extends DifferentialController {
|
||||||
$umsg = DifferentialRevisionUpdateHistoryView::getDiffUnitMessage($diff);
|
$umsg = DifferentialRevisionUpdateHistoryView::getDiffUnitMessage($diff);
|
||||||
$properties['Unit'] = $ustar.' '.$umsg;
|
$properties['Unit'] = $ustar.' '.$umsg;
|
||||||
|
|
||||||
|
$tasks = $revision->getAttachedPHIDs('TASK');
|
||||||
|
if ($tasks) {
|
||||||
|
$links = array();
|
||||||
|
foreach ($tasks as $task_phid) {
|
||||||
|
$links[] = $handles[$task_phid]->renderLink();
|
||||||
|
}
|
||||||
|
$properties['Maniphest Tasks'] = implode('<br />', $links);
|
||||||
|
}
|
||||||
|
|
||||||
return $properties;
|
return $properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,6 +279,16 @@ class DifferentialRevisionViewController extends DifferentialController {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
require_celerity_resource('phabricator-object-selector-css');
|
||||||
|
require_celerity_resource('javelin-behavior-phabricator-object-selector');
|
||||||
|
|
||||||
|
$links[] = array(
|
||||||
|
'class' => 'attach-maniphest',
|
||||||
|
'name' => 'Edit Maniphest Tasks',
|
||||||
|
'href' => "/differential/attach/{$revision_id}/TASK/",
|
||||||
|
'sigil' => 'workflow',
|
||||||
|
);
|
||||||
|
|
||||||
$links[] = array(
|
$links[] = array(
|
||||||
'class' => 'transcripts-metamta',
|
'class' => 'transcripts-metamta',
|
||||||
'name' => 'MetaMTA Transcripts',
|
'name' => 'MetaMTA Transcripts',
|
||||||
|
|
|
@ -22,6 +22,7 @@ phutil_require_module('phabricator', 'applications/differential/view/revisiondet
|
||||||
phutil_require_module('phabricator', 'applications/differential/view/revisionupdatehistory');
|
phutil_require_module('phabricator', 'applications/differential/view/revisionupdatehistory');
|
||||||
phutil_require_module('phabricator', 'applications/draft/storage/draft');
|
phutil_require_module('phabricator', 'applications/draft/storage/draft');
|
||||||
phutil_require_module('phabricator', 'applications/phid/handle/data');
|
phutil_require_module('phabricator', 'applications/phid/handle/data');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/celerity/api');
|
||||||
phutil_require_module('phabricator', 'view/form/error');
|
phutil_require_module('phabricator', 'view/form/error');
|
||||||
|
|
||||||
phutil_require_module('phutil', 'markup');
|
phutil_require_module('phutil', 'markup');
|
||||||
|
|
|
@ -32,6 +32,7 @@ class DifferentialRevision extends DifferentialDAO {
|
||||||
protected $dateCommitted;
|
protected $dateCommitted;
|
||||||
|
|
||||||
protected $lineCount;
|
protected $lineCount;
|
||||||
|
protected $attached = array();
|
||||||
|
|
||||||
private $relationships;
|
private $relationships;
|
||||||
|
|
||||||
|
@ -44,9 +45,21 @@ class DifferentialRevision extends DifferentialDAO {
|
||||||
public function getConfiguration() {
|
public function getConfiguration() {
|
||||||
return array(
|
return array(
|
||||||
self::CONFIG_AUX_PHID => true,
|
self::CONFIG_AUX_PHID => true,
|
||||||
|
self::CONFIG_SERIALIZATION => array(
|
||||||
|
'attached' => self::SERIALIZATION_JSON,
|
||||||
|
),
|
||||||
) + parent::getConfiguration();
|
) + parent::getConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getAttachedPHIDs($type) {
|
||||||
|
return array_keys(idx($this->attached, $type, array()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setAttachedPHIDs($type, array $phids) {
|
||||||
|
$this->attached[$type] = array_fill_keys($phids, array());
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function generatePHID() {
|
public function generatePHID() {
|
||||||
return PhabricatorPHID::generateNewPHID('DREV');
|
return PhabricatorPHID::generateNewPHID('DREV');
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,13 +65,12 @@ final class DifferentialRevisionDetailView extends AphrontView {
|
||||||
} else {
|
} else {
|
||||||
$tag = 'a';
|
$tag = 'a';
|
||||||
}
|
}
|
||||||
$actions[] = phutil_render_tag(
|
$name = $action['name'];
|
||||||
|
unset($action['name']);
|
||||||
|
$actions[] = javelin_render_tag(
|
||||||
$tag,
|
$tag,
|
||||||
array(
|
$action,
|
||||||
'href' => idx($action, 'href'),
|
phutil_escape_html($name));
|
||||||
'class' => idx($action, 'class'),
|
|
||||||
),
|
|
||||||
phutil_escape_html($action['name']));
|
|
||||||
}
|
}
|
||||||
$actions = implode("\n", $actions);
|
$actions = implode("\n", $actions);
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,10 @@
|
||||||
|
|
||||||
|
|
||||||
phutil_require_module('phabricator', 'infrastructure/celerity/api');
|
phutil_require_module('phabricator', 'infrastructure/celerity/api');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
|
||||||
phutil_require_module('phabricator', 'view/base');
|
phutil_require_module('phabricator', 'view/base');
|
||||||
|
|
||||||
phutil_require_module('phutil', 'markup');
|
phutil_require_module('phutil', 'markup');
|
||||||
phutil_require_module('phutil', 'utils');
|
|
||||||
|
|
||||||
|
|
||||||
phutil_require_source('DifferentialRevisionDetailView.php');
|
phutil_require_source('DifferentialRevisionDetailView.php');
|
||||||
|
|
|
@ -24,6 +24,8 @@ final class ManiphestTransactionType {
|
||||||
const TYPE_CCS = 'ccs';
|
const TYPE_CCS = 'ccs';
|
||||||
const TYPE_PRIORITY = 'priority';
|
const TYPE_PRIORITY = 'priority';
|
||||||
|
|
||||||
|
const TYPE_ATTACH = 'attach';
|
||||||
|
|
||||||
public static function getTransactionTypeMap() {
|
public static function getTransactionTypeMap() {
|
||||||
return array(
|
return array(
|
||||||
self::TYPE_NONE => 'Comment',
|
self::TYPE_NONE => 'Comment',
|
||||||
|
|
|
@ -54,6 +54,13 @@ class ManiphestTaskDetailController extends ManiphestController {
|
||||||
$phids[$task->getAuthorPHID()] = true;
|
$phids[$task->getAuthorPHID()] = true;
|
||||||
$phids = array_keys($phids);
|
$phids = array_keys($phids);
|
||||||
|
|
||||||
|
$attached = $task->getAttached();
|
||||||
|
foreach ($attached as $type => $list) {
|
||||||
|
foreach ($list as $phid => $info) {
|
||||||
|
$phids[$phid] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$handles = id(new PhabricatorObjectHandleData($phids))
|
$handles = id(new PhabricatorObjectHandleData($phids))
|
||||||
->loadHandles();
|
->loadHandles();
|
||||||
|
|
||||||
|
@ -86,6 +93,16 @@ class ManiphestTaskDetailController extends ManiphestController {
|
||||||
|
|
||||||
$dict['Author'] = $handles[$task->getAuthorPHID()]->renderLink();
|
$dict['Author'] = $handles[$task->getAuthorPHID()]->renderLink();
|
||||||
|
|
||||||
|
if (idx($attached, 'DREV')) {
|
||||||
|
$revs = idx($attached, 'DREV');
|
||||||
|
$rev_links = array();
|
||||||
|
foreach ($revs as $rev => $info) {
|
||||||
|
$rev_links[] = $handles[$rev]->renderLink();
|
||||||
|
}
|
||||||
|
$rev_links = implode(', ', $rev_links);
|
||||||
|
$dict['Revisions'] = $rev_links;
|
||||||
|
}
|
||||||
|
|
||||||
$dict['Description'] =
|
$dict['Description'] =
|
||||||
'<div class="maniphest-task-description">'.
|
'<div class="maniphest-task-description">'.
|
||||||
'<div class="phabricator-remarkup">'.
|
'<div class="phabricator-remarkup">'.
|
||||||
|
|
|
@ -22,142 +22,29 @@ class ManiphestTaskSelectorController extends ManiphestController {
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
$user = $request->getUser();
|
$user = $request->getUser();
|
||||||
|
|
||||||
$filter_id = celerity_generate_unique_node_id();
|
$phids = $request->getArr('phids');
|
||||||
$query_id = celerity_generate_unique_node_id();
|
|
||||||
$search_id = celerity_generate_unique_node_id();
|
|
||||||
$results_id = celerity_generate_unique_node_id();
|
|
||||||
$current_id = celerity_generate_unique_node_id();
|
|
||||||
|
|
||||||
$search_box =
|
$handles = id(new PhabricatorObjectHandleData($phids))
|
||||||
'<table class="phabricator-object-selector-search">
|
->loadHandles();
|
||||||
<tr>
|
|
||||||
<td class="phabricator-object-selector-search-filter">
|
|
||||||
<select id="'.$filter_id.'">
|
|
||||||
<option>Assigned To Me</option>
|
|
||||||
<option>Created By Me</option>
|
|
||||||
<option>All Open Tasks</option>
|
|
||||||
<option>All Tasks</option>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
<td class="phabricator-object-selector-search-text">
|
|
||||||
<input type="text" id="'.$query_id.'" />
|
|
||||||
</td>
|
|
||||||
<td class="phabricator-object-selector-search-button">
|
|
||||||
<a href="#" class="button" id="'.$search_id.'">Search</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>';
|
|
||||||
$result_box =
|
|
||||||
'<div class="phabricator-object-selector-results" id="'.$results_id.'">'.
|
|
||||||
|
|
||||||
'</div>';
|
$obj_dialog = new PhabricatorObjectSelectorDialog();
|
||||||
$attached_box =
|
$obj_dialog
|
||||||
'<div class="phabricator-object-selector-current">'.
|
|
||||||
'<div class="phabricator-object-selector-currently-attached">'.
|
|
||||||
'<div class="phabricator-object-selector-header">'.
|
|
||||||
'Currently Attached Tasks'.
|
|
||||||
'</div>'.
|
|
||||||
'<div id="'.$current_id.'">'.
|
|
||||||
'</div>'.
|
|
||||||
'</div>'.
|
|
||||||
'</div>';
|
|
||||||
|
|
||||||
require_celerity_resource('phabricator-object-selector-css');
|
|
||||||
|
|
||||||
Javelin::initBehavior(
|
|
||||||
'phabricator-object-selector',
|
|
||||||
array(
|
|
||||||
'filter' => $filter_id,
|
|
||||||
'query' => $query_id,
|
|
||||||
'search' => $search_id,
|
|
||||||
'results' => $results_id,
|
|
||||||
'current' => $current_id,
|
|
||||||
'uri' => '/maniphest/select/search/',
|
|
||||||
));
|
|
||||||
|
|
||||||
$dialog = new PhabricatorObjectSelectorDialog();
|
|
||||||
$dialog
|
|
||||||
->setUser($user)
|
->setUser($user)
|
||||||
->setTitle('Manage Attached Tasks')
|
->setHandles($handles)
|
||||||
->setClass('phabricator-object-selector-dialog')
|
->setFilters(array(
|
||||||
->appendChild($search_box)
|
'assigned' => 'Assigned to Me',
|
||||||
->appendChild($result_box)
|
'created' => 'Created By Me',
|
||||||
->appendChild($attached_box)
|
'open' => 'All Open Tasks',
|
||||||
->addCancelButton('#')
|
'all' => 'All Tasks',
|
||||||
->addSubmitButton('Save Tasks');
|
))
|
||||||
|
->setCancelURI('#')
|
||||||
|
->setSearchURI('/maniphest/select/search/')
|
||||||
|
->setNoun('Tasks');
|
||||||
|
|
||||||
|
$dialog = $obj_dialog->buildDialog();
|
||||||
|
|
||||||
|
|
||||||
return id(new AphrontDialogResponse())->setDialog($dialog);
|
return id(new AphrontDialogResponse())->setDialog($dialog);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
'<table class="phabricator-object-selector-handle">
|
|
||||||
<tr>
|
|
||||||
<th>
|
|
||||||
<input type="checkbox" />
|
|
||||||
</th>
|
|
||||||
<td>
|
|
||||||
<a href="#">T20: Internet Attack Internets</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>'.
|
|
||||||
'<table class="phabricator-object-selector-handle">
|
|
||||||
<tr>
|
|
||||||
<th>
|
|
||||||
<input type="checkbox" />
|
|
||||||
</th>
|
|
||||||
<td>
|
|
||||||
<a href="#">T21: Internet Attack Internets</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>'.
|
|
||||||
'<table class="phabricator-object-selector-handle">
|
|
||||||
<tr>
|
|
||||||
<th>
|
|
||||||
<input type="checkbox" />
|
|
||||||
</th>
|
|
||||||
<td>
|
|
||||||
<a href="#">T22: Internet Attack Internets</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>'.
|
|
||||||
'more results<br />'.
|
|
||||||
'more results<br />'.
|
|
||||||
'more results<br />'.
|
|
||||||
'more results<br />'.
|
|
||||||
'more results<br />'.
|
|
||||||
'more results<br />'.
|
|
||||||
'more results<br />'.
|
|
||||||
'more results<br />'.
|
|
||||||
'more results<br />'.
|
|
||||||
'more results<br />'.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
'<table class="phabricator-object-selector-handle">
|
|
||||||
<tr>
|
|
||||||
<th>
|
|
||||||
<input type="checkbox" />
|
|
||||||
</th>
|
|
||||||
<td>
|
|
||||||
<a href="#">T22: Internet Attack Internets</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>'.
|
|
||||||
'<table class="phabricator-object-selector-handle">
|
|
||||||
<tr>
|
|
||||||
<th>
|
|
||||||
<input type="checkbox" />
|
|
||||||
</th>
|
|
||||||
<td>
|
|
||||||
<a href="#">T22: Internet Attack Internets</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>'.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
|
@ -8,8 +8,7 @@
|
||||||
|
|
||||||
phutil_require_module('phabricator', 'aphront/response/dialog');
|
phutil_require_module('phabricator', 'aphront/response/dialog');
|
||||||
phutil_require_module('phabricator', 'applications/maniphest/controller/base');
|
phutil_require_module('phabricator', 'applications/maniphest/controller/base');
|
||||||
phutil_require_module('phabricator', 'infrastructure/celerity/api');
|
phutil_require_module('phabricator', 'applications/phid/handle/data');
|
||||||
phutil_require_module('phabricator', 'infrastructure/javelin/api');
|
|
||||||
phutil_require_module('phabricator', 'view/control/objectselector');
|
phutil_require_module('phabricator', 'view/control/objectselector');
|
||||||
|
|
||||||
phutil_require_module('phutil', 'utils');
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
|
@ -28,14 +28,15 @@ class ManiphestTaskSelectorSearchController extends ManiphestController {
|
||||||
|
|
||||||
$exec = new PhabricatorSearchMySQLExecutor();
|
$exec = new PhabricatorSearchMySQLExecutor();
|
||||||
$results = $exec->executeSearch($query);
|
$results = $exec->executeSearch($query);
|
||||||
|
$results = ipull($results, 'phid');
|
||||||
|
|
||||||
|
$handles = id(new PhabricatorObjectHandleData($results))
|
||||||
|
->loadHandles();
|
||||||
|
|
||||||
$data = array();
|
$data = array();
|
||||||
foreach ($results as $result) {
|
foreach ($handles as $handle) {
|
||||||
$data[] = array(
|
$view = new PhabricatorHandleObjectSelectorDataView($handle);
|
||||||
'phid' => $result['phid'],
|
$data[] = $view->renderData();
|
||||||
'name' => $result['documentTitle'],
|
|
||||||
'href' => '#',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return id(new AphrontAjaxResponse())->setContent($data);
|
return id(new AphrontAjaxResponse())->setContent($data);
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
|
|
||||||
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/maniphest/controller/base');
|
||||||
|
phutil_require_module('phabricator', 'applications/phid/handle/data');
|
||||||
|
phutil_require_module('phabricator', 'applications/phid/handle/view/selector');
|
||||||
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');
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,9 @@ class ManiphestTransactionEditor {
|
||||||
case ManiphestTransactionType::TYPE_PRIORITY:
|
case ManiphestTransactionType::TYPE_PRIORITY:
|
||||||
$old = $task->getPriority();
|
$old = $task->getPriority();
|
||||||
break;
|
break;
|
||||||
|
case ManiphestTransactionType::TYPE_ATTACH:
|
||||||
|
$old = $task->getAttached();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Exception('Unknown action type.');
|
throw new Exception('Unknown action type.');
|
||||||
}
|
}
|
||||||
|
@ -78,6 +81,9 @@ class ManiphestTransactionEditor {
|
||||||
case ManiphestTransactionType::TYPE_PRIORITY:
|
case ManiphestTransactionType::TYPE_PRIORITY:
|
||||||
$task->setPriority($new);
|
$task->setPriority($new);
|
||||||
break;
|
break;
|
||||||
|
case ManiphestTransactionType::TYPE_ATTACH:
|
||||||
|
$task->setAttached($new);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Exception('Unknown action type.');
|
throw new Exception('Unknown action type.');
|
||||||
}
|
}
|
||||||
|
@ -120,7 +126,6 @@ class ManiphestTransactionEditor {
|
||||||
$handles = id(new PhabricatorObjectHandleData($phids))
|
$handles = id(new PhabricatorObjectHandleData($phids))
|
||||||
->loadHandles();
|
->loadHandles();
|
||||||
|
|
||||||
|
|
||||||
$view = new ManiphestTransactionDetailView();
|
$view = new ManiphestTransactionDetailView();
|
||||||
$view->setTransactionGroup($transactions);
|
$view->setTransactionGroup($transactions);
|
||||||
$view->setHandles($handles);
|
$view->setHandles($handles);
|
||||||
|
|
|
@ -29,18 +29,22 @@ class ManiphestTask extends ManiphestDAO {
|
||||||
protected $title;
|
protected $title;
|
||||||
protected $description;
|
protected $description;
|
||||||
|
|
||||||
protected $relatedPHIDs;
|
protected $attached = array();
|
||||||
|
|
||||||
public function getConfiguration() {
|
public function getConfiguration() {
|
||||||
return array(
|
return array(
|
||||||
self::CONFIG_AUX_PHID => true,
|
self::CONFIG_AUX_PHID => true,
|
||||||
self::CONFIG_SERIALIZATION => array(
|
self::CONFIG_SERIALIZATION => array(
|
||||||
'ccPHIDs' => self::SERIALIZATION_JSON,
|
'ccPHIDs' => self::SERIALIZATION_JSON,
|
||||||
'relatedPHIDs' => self::SERIALIZATION_JSON,
|
'attached' => self::SERIALIZATION_JSON,
|
||||||
),
|
),
|
||||||
) + parent::getConfiguration();
|
) + parent::getConfiguration();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getAttachedPHIDs($type) {
|
||||||
|
return array_keys(idx($this->attached, $type, array()));
|
||||||
|
}
|
||||||
|
|
||||||
public function generatePHID() {
|
public function generatePHID() {
|
||||||
return PhabricatorPHID::generateNewPHID('TASK');
|
return PhabricatorPHID::generateNewPHID('TASK');
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,22 @@ class ManiphestTransaction extends ManiphestDAO {
|
||||||
$phids[] = $this->getOldValue();
|
$phids[] = $this->getOldValue();
|
||||||
$phids[] = $this->getNewValue();
|
$phids[] = $this->getNewValue();
|
||||||
break;
|
break;
|
||||||
|
case ManiphestTransactionType::TYPE_ATTACH:
|
||||||
|
$old = $this->getOldValue();
|
||||||
|
$new = $this->getNewValue();
|
||||||
|
if (!is_array($old)) {
|
||||||
|
$old = array();
|
||||||
|
}
|
||||||
|
if (!is_array($new)) {
|
||||||
|
$new = array();
|
||||||
|
}
|
||||||
|
$val = array_merge(array_values($old), array_values($new));
|
||||||
|
foreach ($val as $stuff) {
|
||||||
|
foreach ($stuff as $phid => $ignored) {
|
||||||
|
$phids[] = $phid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
$phids[] = $this->getAuthorPHID();
|
$phids[] = $this->getAuthorPHID();
|
||||||
|
|
|
@ -104,9 +104,13 @@ class ManiphestTransactionDetailView extends AphrontView {
|
||||||
$comments = $comment_transaction->getCache();
|
$comments = $comment_transaction->getCache();
|
||||||
if (!strlen($comments)) {
|
if (!strlen($comments)) {
|
||||||
$comments = $comment_transaction->getComments();
|
$comments = $comment_transaction->getComments();
|
||||||
|
if (strlen($comments)) {
|
||||||
$comments = $this->markupEngine->markupText($comments);
|
$comments = $this->markupEngine->markupText($comments);
|
||||||
$transaction->setCache($comments);
|
$comment_transaction->setCache($comments);
|
||||||
$transaction->save();
|
if ($comment_transaction->getID()) {
|
||||||
|
$comment_transaction->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$comment_block =
|
$comment_block =
|
||||||
'<div class="maniphest-transaction-comments phabricator-remarkup">'.
|
'<div class="maniphest-transaction-comments phabricator-remarkup">'.
|
||||||
|
@ -225,8 +229,39 @@ class ManiphestTransactionDetailView extends AphrontView {
|
||||||
'"'.$new_name.'"';
|
'"'.$new_name.'"';
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case ManiphestTransactionType::TYPE_ATTACH:
|
||||||
|
$old = nonempty($old, array());
|
||||||
|
$new = nonempty($new, array());
|
||||||
|
|
||||||
|
$old = array_keys(idx($old, 'DREV', array()));
|
||||||
|
$new = array_keys(idx($new, 'DREV', array()));
|
||||||
|
$added = array_diff($new, $old);
|
||||||
|
$removed = array_diff($old, $new);
|
||||||
|
|
||||||
|
$add_desc = $this->renderHandles($added);
|
||||||
|
$rem_desc = $this->renderHandles($removed);
|
||||||
|
|
||||||
|
if ($added && !$removed) {
|
||||||
|
$verb = 'Attached';
|
||||||
|
if (count($added) == 1) {
|
||||||
|
$desc = 'attached Differential Revision: '.$add_desc;
|
||||||
|
} else {
|
||||||
|
$desc = 'attached Differential Revisions: '.$add_desc;
|
||||||
|
}
|
||||||
|
} else if ($removed && !$added) {
|
||||||
|
$verb = 'Detached';
|
||||||
|
if (count($removed) == 1) {
|
||||||
|
$desc = 'detached Differential Revision: '.$rem_desc;
|
||||||
|
} else {
|
||||||
|
$desc = 'detached Differential Revisions: '.$rem_desc;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$desc = 'changed attached Differential Revisions, added: '.$add_desc.
|
||||||
|
'removed: '.$rem_desc;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return ' brazenly '.$type."'d";
|
return array($type, ' brazenly '.$type."'d", $classes);
|
||||||
}
|
}
|
||||||
|
|
||||||
return array($verb, $desc, $classes);
|
return array($verb, $desc, $classes);
|
||||||
|
|
|
@ -90,12 +90,21 @@ class PhabricatorObjectHandle {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function renderLink() {
|
public function renderLink() {
|
||||||
|
|
||||||
|
switch ($this->getType()) {
|
||||||
|
case 'USER':
|
||||||
|
$name = $this->getName();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$name = $this->getFullName();
|
||||||
|
}
|
||||||
|
|
||||||
return phutil_render_tag(
|
return phutil_render_tag(
|
||||||
'a',
|
'a',
|
||||||
array(
|
array(
|
||||||
'href' => $this->getURI(),
|
'href' => $this->getURI(),
|
||||||
),
|
),
|
||||||
phutil_escape_html($this->getName()));
|
phutil_escape_html($name));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,6 +115,7 @@ class PhabricatorObjectHandleData {
|
||||||
$handle->setType($type);
|
$handle->setType($type);
|
||||||
$handle->setName($rev->getTitle());
|
$handle->setName($rev->getTitle());
|
||||||
$handle->setURI('/D'.$rev->getID());
|
$handle->setURI('/D'.$rev->getID());
|
||||||
|
$handle->setFullName('D'.$rev->getID().': '.$rev->getTitle());
|
||||||
}
|
}
|
||||||
$handles[$phid] = $handle;
|
$handles[$phid] = $handle;
|
||||||
}
|
}
|
||||||
|
@ -138,6 +139,7 @@ class PhabricatorObjectHandleData {
|
||||||
$handle->setType($type);
|
$handle->setType($type);
|
||||||
$handle->setName($task->getTitle());
|
$handle->setName($task->getTitle());
|
||||||
$handle->setURI('/T'.$task->getID());
|
$handle->setURI('/T'.$task->getID());
|
||||||
|
$handle->setFullName('T'.$task->getID().': '.$task->getTitle());
|
||||||
}
|
}
|
||||||
$handles[$phid] = $handle;
|
$handles[$phid] = $handle;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?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 PhabricatorHandleObjectSelectorDataView {
|
||||||
|
|
||||||
|
private $handle;
|
||||||
|
|
||||||
|
public function __construct($handle) {
|
||||||
|
$this->handle = $handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function renderData() {
|
||||||
|
$handle = $this->handle;
|
||||||
|
return array(
|
||||||
|
'phid' => $handle->getPHID(),
|
||||||
|
'name' => $handle->getFullName(),
|
||||||
|
'href' => $handle->getURI(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
10
src/applications/phid/handle/view/selector/__init__.php
Normal file
10
src/applications/phid/handle/view/selector/__init__.php
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhabricatorHandleObjectSelectorDataView.php');
|
|
@ -733,6 +733,10 @@ abstract class LiskDAO {
|
||||||
$this->getID(),
|
$this->getID(),
|
||||||
'version',
|
'version',
|
||||||
$this->getVersion());
|
$this->getVersion());
|
||||||
|
if ($conn->getAffectedRows() !== 1) {
|
||||||
|
throw new AphrontQueryObjectMissingException($use_locks);
|
||||||
|
}
|
||||||
|
$this->setVersion($this->getVersion() + 1);
|
||||||
} else {
|
} else {
|
||||||
$conn->query(
|
$conn->query(
|
||||||
'UPDATE %T SET %Q WHERE %C = %d',
|
'UPDATE %T SET %Q WHERE %C = %d',
|
||||||
|
@ -740,14 +744,9 @@ abstract class LiskDAO {
|
||||||
$map,
|
$map,
|
||||||
$this->getIDKeyForUse(),
|
$this->getIDKeyForUse(),
|
||||||
$this->getID());
|
$this->getID());
|
||||||
}
|
// We can't detect a missing object because updating an object without
|
||||||
|
// changing any values doesn't affect rows. We could jiggle timestamps
|
||||||
if ($conn->getAffectedRows() !== 1) {
|
// to catch this for objects which track them if we wanted.
|
||||||
throw new AphrontQueryObjectMissingException($use_locks);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($use_locks) {
|
|
||||||
$this->setVersion($this->getVersion() + 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->didWriteData();
|
$this->didWriteData();
|
||||||
|
|
|
@ -16,7 +16,149 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class PhabricatorObjectSelectorDialog extends AphrontDialogView {
|
class PhabricatorObjectSelectorDialog {
|
||||||
|
|
||||||
|
private $user;
|
||||||
|
private $filters = array();
|
||||||
|
private $handles = array();
|
||||||
|
private $cancelURI;
|
||||||
|
private $submitURI;
|
||||||
|
private $noun;
|
||||||
|
private $searchURI;
|
||||||
|
|
||||||
|
public function setUser($user) {
|
||||||
|
$this->user = $user;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setFilters(array $filters) {
|
||||||
|
$this->filters = $filters;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setHandles(array $handles) {
|
||||||
|
$this->handles = $handles;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCancelURI($cancel_uri) {
|
||||||
|
$this->cancelURI = $cancel_uri;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSubmitURI($submit_uri) {
|
||||||
|
$this->submitURI = $submit_uri;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSearchURI($search_uri) {
|
||||||
|
$this->searchURI = $search_uri;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setNoun($noun) {
|
||||||
|
$this->noun = $noun;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buildDialog() {
|
||||||
|
$user = $this->user;
|
||||||
|
|
||||||
|
$filter_id = celerity_generate_unique_node_id();
|
||||||
|
$query_id = celerity_generate_unique_node_id();
|
||||||
|
$results_id = celerity_generate_unique_node_id();
|
||||||
|
$current_id = celerity_generate_unique_node_id();
|
||||||
|
$search_id = celerity_generate_unique_node_id();
|
||||||
|
$form_id = celerity_generate_unique_node_id();
|
||||||
|
|
||||||
|
require_celerity_resource('phabricator-object-selector-css');
|
||||||
|
|
||||||
|
$options = array();
|
||||||
|
foreach ($this->filters as $key => $label) {
|
||||||
|
$options[] = phutil_render_tag(
|
||||||
|
'option',
|
||||||
|
array(
|
||||||
|
'value' => $key
|
||||||
|
),
|
||||||
|
$label);
|
||||||
|
}
|
||||||
|
$options = implode("\n", $options);
|
||||||
|
|
||||||
|
$search_box = phabricator_render_form(
|
||||||
|
$user,
|
||||||
|
array(
|
||||||
|
'method' => 'POST',
|
||||||
|
'action' => $this->submitURI,
|
||||||
|
'id' => $search_id,
|
||||||
|
),
|
||||||
|
'<table class="phabricator-object-selector-search">
|
||||||
|
<tr>
|
||||||
|
<td class="phabricator-object-selector-search-filter">
|
||||||
|
<select id="'.$filter_id.'">'.
|
||||||
|
$options.
|
||||||
|
'</select>
|
||||||
|
</td>
|
||||||
|
<td class="phabricator-object-selector-search-text">
|
||||||
|
<input type="text" id="'.$query_id.'" />
|
||||||
|
</td>
|
||||||
|
<td class="phabricator-object-selector-search-button">
|
||||||
|
<button>Search</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>');
|
||||||
|
$result_box =
|
||||||
|
'<div class="phabricator-object-selector-results" id="'.$results_id.'">'.
|
||||||
|
'</div>';
|
||||||
|
$attached_box =
|
||||||
|
'<div class="phabricator-object-selector-current">'.
|
||||||
|
'<div class="phabricator-object-selector-currently-attached">'.
|
||||||
|
'<div class="phabricator-object-selector-header">'.
|
||||||
|
'Currently Attached '.$this->noun.
|
||||||
|
'</div>'.
|
||||||
|
'<div id="'.$current_id.'">'.
|
||||||
|
'</div>'.
|
||||||
|
'</div>'.
|
||||||
|
'</div>';
|
||||||
|
|
||||||
|
|
||||||
|
$dialog = new AphrontDialogView();
|
||||||
|
$dialog
|
||||||
|
->setUser($this->user)
|
||||||
|
->setTitle('Manage Attached '.$this->noun)
|
||||||
|
->setClass('phabricator-object-selector-dialog')
|
||||||
|
->appendChild($search_box)
|
||||||
|
->appendChild($result_box)
|
||||||
|
->appendChild($attached_box)
|
||||||
|
->setRenderDialogAsDiv()
|
||||||
|
->setFormID($form_id)
|
||||||
|
->addSubmitButton('Save '.$this->noun);
|
||||||
|
|
||||||
|
if ($this->cancelURI) {
|
||||||
|
$dialog->addCancelButton($this->cancelURI);
|
||||||
|
}
|
||||||
|
|
||||||
|
$handle_views = array();
|
||||||
|
foreach ($this->handles as $phid => $handle) {
|
||||||
|
$view = new PhabricatorHandleObjectSelectorDataView($handle);
|
||||||
|
$handle_views[$phid] = $view->renderData();
|
||||||
|
}
|
||||||
|
$dialog->addHiddenInput('phids', implode(';', array_keys($this->handles)));
|
||||||
|
|
||||||
|
|
||||||
|
Javelin::initBehavior(
|
||||||
|
'phabricator-object-selector',
|
||||||
|
array(
|
||||||
|
'filter' => $filter_id,
|
||||||
|
'query' => $query_id,
|
||||||
|
'search' => $search_id,
|
||||||
|
'results' => $results_id,
|
||||||
|
'current' => $current_id,
|
||||||
|
'form' => $form_id,
|
||||||
|
'uri' => $this->searchURI,
|
||||||
|
'handles' => $handle_views,
|
||||||
|
));
|
||||||
|
|
||||||
|
return $dialog;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,13 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'applications/phid/handle/view/selector');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/celerity/api');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/javelin/api');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
|
||||||
phutil_require_module('phabricator', 'view/dialog');
|
phutil_require_module('phabricator', 'view/dialog');
|
||||||
|
|
||||||
|
phutil_require_module('phutil', 'markup');
|
||||||
|
|
||||||
|
|
||||||
phutil_require_source('PhabricatorObjectSelectorDialog.php');
|
phutil_require_source('PhabricatorObjectSelectorDialog.php');
|
||||||
|
|
|
@ -25,6 +25,8 @@ class AphrontDialogView extends AphrontView {
|
||||||
private $user;
|
private $user;
|
||||||
private $hidden = array();
|
private $hidden = array();
|
||||||
private $class;
|
private $class;
|
||||||
|
private $renderAsForm = true;
|
||||||
|
private $formID;
|
||||||
|
|
||||||
public function setUser(PhabricatorUser $user) {
|
public function setUser(PhabricatorUser $user) {
|
||||||
$this->user = $user;
|
$this->user = $user;
|
||||||
|
@ -56,7 +58,7 @@ class AphrontDialogView extends AphrontView {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addHiddenInput($key, $value) {
|
public function addHiddenInput($key, $value) {
|
||||||
$this->hidden[$key] = $value;
|
$this->hidden[] = array($key, $value);
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,6 +67,17 @@ class AphrontDialogView extends AphrontView {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setRenderDialogAsDiv() {
|
||||||
|
// TODO: This API is awkward.
|
||||||
|
$this->renderAsForm = false;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setFormID($id) {
|
||||||
|
$this->formID = $id;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
final public function render() {
|
final public function render() {
|
||||||
require_celerity_resource('aphront-dialog-view-css');
|
require_celerity_resource('aphront-dialog-view-css');
|
||||||
|
|
||||||
|
@ -90,39 +103,52 @@ class AphrontDialogView extends AphrontView {
|
||||||
),
|
),
|
||||||
'Cancel');
|
'Cancel');
|
||||||
}
|
}
|
||||||
|
$buttons = implode('', $buttons);
|
||||||
|
|
||||||
if (!$this->user) {
|
if (!$this->user) {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
"You must call setUser() when rendering an AphrontDialogView.");
|
"You must call setUser() when rendering an AphrontDialogView.");
|
||||||
}
|
}
|
||||||
$csrf = $this->user->getCSRFToken();
|
|
||||||
|
$more = $this->class;
|
||||||
|
|
||||||
|
$attributes = array(
|
||||||
|
'class' => 'aphront-dialog-view '.$more,
|
||||||
|
'sigil' => 'jx-dialog',
|
||||||
|
);
|
||||||
|
|
||||||
|
$form_attributes = array(
|
||||||
|
'action' => $this->submitURI,
|
||||||
|
'method' => 'post',
|
||||||
|
'id' => $this->formID,
|
||||||
|
);
|
||||||
|
|
||||||
$hidden_inputs = array();
|
$hidden_inputs = array();
|
||||||
foreach ($this->hidden as $key => $value) {
|
foreach ($this->hidden as $desc) {
|
||||||
$hidden_inputs[] = phutil_render_tag(
|
list($key, $value) = $desc;
|
||||||
|
$hidden_inputs[] = javelin_render_tag(
|
||||||
'input',
|
'input',
|
||||||
array(
|
array(
|
||||||
'type' => 'hidden',
|
'type' => 'hidden',
|
||||||
'name' => $key,
|
'name' => $key,
|
||||||
'value' => $value,
|
'value' => $value,
|
||||||
|
'sigil' => 'aphront-dialog-application-input'
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
$hidden_inputs = implode("\n", $hidden_inputs);
|
$hidden_inputs = implode("\n", $hidden_inputs);
|
||||||
|
$hidden_inputs =
|
||||||
$more = $this->class;
|
|
||||||
|
|
||||||
return javelin_render_tag(
|
|
||||||
'form',
|
|
||||||
array(
|
|
||||||
'class' => 'aphront-dialog-view '.$more,
|
|
||||||
'action' => $this->submitURI,
|
|
||||||
'method' => 'post',
|
|
||||||
'sigil' => 'jx-dialog',
|
|
||||||
),
|
|
||||||
'<input type="hidden" name="__form__" value="1" />'.
|
|
||||||
'<input type="hidden" name="__csrf__" value="'.$csrf.'" />'.
|
|
||||||
'<input type="hidden" name="__dialog__" value="1" />'.
|
'<input type="hidden" name="__dialog__" value="1" />'.
|
||||||
$hidden_inputs.
|
$hidden_inputs;
|
||||||
|
|
||||||
|
|
||||||
|
if (!$this->renderAsForm) {
|
||||||
|
$buttons = phabricator_render_form(
|
||||||
|
$this->user,
|
||||||
|
$form_attributes,
|
||||||
|
$hidden_inputs.$buttons);
|
||||||
|
}
|
||||||
|
|
||||||
|
$content =
|
||||||
'<div class="aphront-dialog-head">'.
|
'<div class="aphront-dialog-head">'.
|
||||||
phutil_escape_html($this->title).
|
phutil_escape_html($this->title).
|
||||||
'</div>'.
|
'</div>'.
|
||||||
|
@ -130,9 +156,22 @@ class AphrontDialogView extends AphrontView {
|
||||||
$this->renderChildren().
|
$this->renderChildren().
|
||||||
'</div>'.
|
'</div>'.
|
||||||
'<div class="aphront-dialog-tail">'.
|
'<div class="aphront-dialog-tail">'.
|
||||||
implode('', $buttons).
|
$buttons.
|
||||||
'<div style="clear: both;"></div>'.
|
'<div style="clear: both;"></div>'.
|
||||||
'</div>');
|
'</div>';
|
||||||
|
|
||||||
|
if ($this->renderAsForm) {
|
||||||
|
return phabricator_render_form(
|
||||||
|
$this->user,
|
||||||
|
$form_attributes + $attributes,
|
||||||
|
$hidden_inputs.
|
||||||
|
$content);
|
||||||
|
} else {
|
||||||
|
return javelin_render_tag(
|
||||||
|
'div',
|
||||||
|
$attributes,
|
||||||
|
$content);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,3 +58,11 @@
|
||||||
height: 100%;
|
height: 100%;
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.aphront-exception-dialog {
|
||||||
|
width: 95%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aphront-exception-dialog .aphront-dialog-head {
|
||||||
|
background: #aa0000;
|
||||||
|
}
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
* @requires aphront-dialog-view-css
|
* @requires aphront-dialog-view-css
|
||||||
*/
|
*/
|
||||||
|
|
||||||
form.phabricator-object-selector-dialog {
|
.phabricator-object-selector-dialog {
|
||||||
width: 800px;
|
width: 960px;
|
||||||
}
|
}
|
||||||
|
|
||||||
form.phabricator-object-selector-dialog .aphront-dialog-body {
|
.phabricator-object-selector-dialog .aphront-dialog-body {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ td.phabricator-object-selector-search-text {
|
||||||
|
|
||||||
.phabricator-object-selector-results {
|
.phabricator-object-selector-results {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 16em;
|
height: 24em;
|
||||||
border: solid #bbbbbb;
|
border: solid #bbbbbb;
|
||||||
border-width: 1px 0px;
|
border-width: 1px 0px;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
|
@ -79,3 +79,10 @@ td.phabricator-object-selector-search-text {
|
||||||
background: #ededed;
|
background: #ededed;
|
||||||
padding: 8px 8px;
|
padding: 8px 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.object-selector-nothing {
|
||||||
|
padding: 1em;
|
||||||
|
color: #888888;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
|
@ -7,9 +7,17 @@ JX.behavior('phabricator-object-selector', function(config) {
|
||||||
var n = 0;
|
var n = 0;
|
||||||
|
|
||||||
var phids = {};
|
var phids = {};
|
||||||
var handles = {};
|
var handles = config.handles;
|
||||||
|
for (var k in handles) {
|
||||||
|
phids[k] = true;
|
||||||
|
}
|
||||||
var attach_list = {};
|
var attach_list = {};
|
||||||
|
|
||||||
|
var phid_input = JX.DOM.find(
|
||||||
|
JX.$(config.form),
|
||||||
|
'input',
|
||||||
|
'aphront-dialog-application-input');
|
||||||
|
|
||||||
function onreceive(seq, r) {
|
function onreceive(seq, r) {
|
||||||
if (seq != n) {
|
if (seq != n) {
|
||||||
return;
|
return;
|
||||||
|
@ -41,18 +49,26 @@ JX.behavior('phabricator-object-selector', function(config) {
|
||||||
}
|
}
|
||||||
|
|
||||||
JX.DOM.setContent(JX.$(config.current), display);
|
JX.DOM.setContent(JX.$(config.current), display);
|
||||||
|
phid_input.value = JX.keys(phids).join(';');
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderHandle(h, attach) {
|
function renderHandle(h, attach) {
|
||||||
|
|
||||||
|
var link = JX.$N(
|
||||||
|
'a',
|
||||||
|
{href : h.uri, target : '_blank'},
|
||||||
|
h.name);
|
||||||
|
|
||||||
var td = JX.$N('td');
|
var td = JX.$N('td');
|
||||||
|
|
||||||
var table = JX.$N(
|
var table = JX.$N(
|
||||||
'table',
|
'table',
|
||||||
{className: 'phabricator-object-selector-handle'},
|
{className: 'phabricator-object-selector-handle'},
|
||||||
JX.$N(
|
JX.$N(
|
||||||
'tbody',
|
'tbody',
|
||||||
{},
|
{},
|
||||||
[JX.$N('th', {}, h.name), td]));
|
[JX.$N('th', {}, link), td]));
|
||||||
|
|
||||||
var btn = JX.$N(
|
var btn = JX.$N(
|
||||||
'a',
|
'a',
|
||||||
{className: 'button small grey'},
|
{className: 'button small grey'},
|
||||||
|
@ -62,6 +78,10 @@ JX.behavior('phabricator-object-selector', function(config) {
|
||||||
JX.Stratcom.addData(btn, {handle : h, table : table});
|
JX.Stratcom.addData(btn, {handle : h, table : table});
|
||||||
if (attach) {
|
if (attach) {
|
||||||
attach_list[h.phid] = btn;
|
attach_list[h.phid] = btn;
|
||||||
|
if (h.phid in phids) {
|
||||||
|
JX.DOM.alterClass(btn, 'disabled', true);
|
||||||
|
btn.disabled = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JX.DOM.setContent(td, btn);
|
JX.DOM.setContent(td, btn);
|
||||||
|
@ -70,7 +90,7 @@ JX.behavior('phabricator-object-selector', function(config) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderNote(note) {
|
function renderNote(note) {
|
||||||
return JX.$N('div', {}, note);
|
return JX.$N('div', {className : 'object-selector-nothing'}, note);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendQuery() {
|
function sendQuery() {
|
||||||
|
@ -85,7 +105,7 @@ JX.behavior('phabricator-object-selector', function(config) {
|
||||||
|
|
||||||
JX.DOM.listen(
|
JX.DOM.listen(
|
||||||
JX.$(config.search),
|
JX.$(config.search),
|
||||||
'click',
|
'submit',
|
||||||
null,
|
null,
|
||||||
function(e) {
|
function(e) {
|
||||||
e.kill();
|
e.kill();
|
||||||
|
|
Loading…
Reference in a new issue