1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-26 16:52:41 +01:00

Very basic Object Selector interface.

Summary: Interface for selecting objects to attach to other objects
(e.g., Maniphest tasks to Differential diffs and vice versa).

Test Plan: still rough

Reviewers:

CC:
This commit is contained in:
epriestley 2011-02-16 22:14:09 -08:00
parent 0b1450c5f9
commit 258e0cdded
14 changed files with 533 additions and 65 deletions

View file

@ -215,6 +215,16 @@ celerity_register_resource_map(array(
), ),
'disk' => '/rsrc/css/application/maniphest/transaction-detail.css', 'disk' => '/rsrc/css/application/maniphest/transaction-detail.css',
), ),
'phabricator-object-selector-css' =>
array(
'uri' => '/res/270ce107/rsrc/css/application/objectselector/object-selector.css',
'type' => 'css',
'requires' =>
array(
0 => 'aphront-dialog-view-css',
),
'disk' => '/rsrc/css/application/objectselector/object-selector.css',
),
'phabricator-core-buttons-css' => 'phabricator-core-buttons-css' =>
array( array(
'uri' => '/res/ee35ffe1/rsrc/css/core/buttons.css', 'uri' => '/res/ee35ffe1/rsrc/css/core/buttons.css',
@ -233,15 +243,6 @@ celerity_register_resource_map(array(
), ),
'disk' => '/rsrc/css/core/core.css', 'disk' => '/rsrc/css/core/core.css',
), ),
'phabricator-core-dialog-css' =>
array(
'uri' => '/res/f66cec41/rsrc/css/core/dialog.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/core/dialog.css',
),
'phabricator-remarkup-css' => 'phabricator-remarkup-css' =>
array( array(
'uri' => '/res/786989c3/rsrc/css/core/remarkup.css', 'uri' => '/res/786989c3/rsrc/css/core/remarkup.css',
@ -269,6 +270,16 @@ celerity_register_resource_map(array(
), ),
'disk' => '/rsrc/js/application/core/behavior-dark-console.js', 'disk' => '/rsrc/js/application/core/behavior-dark-console.js',
), ),
'javelin-behavior-phabricator-object-selector' =>
array(
'uri' => '/res/7f7eda6a/rsrc/js/application/core/behavior-object-selector.js',
'type' => 'js',
'requires' =>
array(
0 => 'javelin-lib-dev',
),
'disk' => '/rsrc/js/application/core/behavior-object-selector.js',
),
'javelin-behavior-aphront-basic-tokenizer' => 'javelin-behavior-aphront-basic-tokenizer' =>
array( array(
'uri' => '/res/8317d761/rsrc/js/application/core/behavior-tokenizer.js', 'uri' => '/res/8317d761/rsrc/js/application/core/behavior-tokenizer.js',

View file

@ -145,6 +145,8 @@ 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',
'ManiphestTaskSelectorController' => 'applications/maniphest/controller/taskselector',
'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',
@ -207,6 +209,7 @@ phutil_register_library_map(array(
'PhabricatorMetaMTAViewController' => 'applications/metamta/controller/view', 'PhabricatorMetaMTAViewController' => 'applications/metamta/controller/view',
'PhabricatorObjectHandle' => 'applications/phid/handle', 'PhabricatorObjectHandle' => 'applications/phid/handle',
'PhabricatorObjectHandleData' => 'applications/phid/handle/data', 'PhabricatorObjectHandleData' => 'applications/phid/handle/data',
'PhabricatorObjectSelectorDialog' => 'view/control/objectselector',
'PhabricatorPHID' => 'applications/phid/storage/phid', 'PhabricatorPHID' => 'applications/phid/storage/phid',
'PhabricatorPHIDAllocateController' => 'applications/phid/controller/allocate', 'PhabricatorPHIDAllocateController' => 'applications/phid/controller/allocate',
'PhabricatorPHIDController' => 'applications/phid/controller/base', 'PhabricatorPHIDController' => 'applications/phid/controller/base',
@ -377,6 +380,8 @@ phutil_register_library_map(array(
'ManiphestTaskDetailController' => 'ManiphestController', 'ManiphestTaskDetailController' => 'ManiphestController',
'ManiphestTaskListController' => 'ManiphestController', 'ManiphestTaskListController' => 'ManiphestController',
'ManiphestTaskListView' => 'AphrontView', 'ManiphestTaskListView' => 'AphrontView',
'ManiphestTaskSelectorController' => 'ManiphestController',
'ManiphestTaskSelectorSearchController' => 'ManiphestController',
'ManiphestTaskSummaryView' => 'AphrontView', 'ManiphestTaskSummaryView' => 'AphrontView',
'ManiphestTransaction' => 'ManiphestDAO', 'ManiphestTransaction' => 'ManiphestDAO',
'ManiphestTransactionDetailView' => 'AphrontView', 'ManiphestTransactionDetailView' => 'AphrontView',
@ -430,6 +435,7 @@ 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',

View file

@ -143,6 +143,8 @@ class AphrontDefaultApplicationConfiguration
'transaction/' => array( 'transaction/' => array(
'save/' => 'ManiphestTransactionSaveController', 'save/' => 'ManiphestTransactionSaveController',
), ),
'select/$' => 'ManiphestTaskSelectorController',
'select/search/$' => 'ManiphestTaskSelectorSearchController',
), ),
'/T(?P<id>\d+)$' => 'ManiphestTaskDetailController', '/T(?P<id>\d+)$' => 'ManiphestTaskDetailController',

View file

@ -0,0 +1,163 @@
<?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 ManiphestTaskSelectorController extends ManiphestController {
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$filter_id = celerity_generate_unique_node_id();
$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 =
'<table class="phabricator-object-selector-search">
<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>';
$attached_box =
'<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)
->setTitle('Manage Attached Tasks')
->setClass('phabricator-object-selector-dialog')
->appendChild($search_box)
->appendChild($result_box)
->appendChild($attached_box)
->addCancelButton('#')
->addSubmitButton('Save Tasks');
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>'.
*/

View file

@ -0,0 +1,18 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/response/dialog');
phutil_require_module('phabricator', 'applications/maniphest/controller/base');
phutil_require_module('phabricator', 'infrastructure/celerity/api');
phutil_require_module('phabricator', 'infrastructure/javelin/api');
phutil_require_module('phabricator', 'view/control/objectselector');
phutil_require_module('phutil', 'utils');
phutil_require_source('ManiphestTaskSelectorController.php');

View file

@ -0,0 +1,43 @@
<?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 ManiphestTaskSelectorSearchController extends ManiphestController {
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$query = new PhabricatorSearchQuery();
$query->setQuery($request->getStr('query'));
$query->setParameter('type', 'TASK');
$exec = new PhabricatorSearchMySQLExecutor();
$results = $exec->executeSearch($query);
$data = array();
foreach ($results as $result) {
$data[] = array(
'phid' => $result['phid'],
'name' => $result['documentTitle'],
'href' => '#',
);
}
return id(new AphrontAjaxResponse())->setContent($data);
}
}

View file

@ -0,0 +1,17 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/response/ajax');
phutil_require_module('phabricator', 'applications/maniphest/controller/base');
phutil_require_module('phabricator', 'applications/search/execute/mysql');
phutil_require_module('phabricator', 'applications/search/storage/query');
phutil_require_module('phutil', 'utils');
phutil_require_source('ManiphestTaskSelectorSearchController.php');

View file

@ -106,6 +106,7 @@ class PhabricatorSearchMySQLExecutor extends PhabricatorSearchExecutor {
'SELECT DISTINCT 'SELECT DISTINCT
document.phid, document.phid,
document.documentType, document.documentType,
document.documentTitle,
document.documentCreated FROM %T document %Q %Q %Q document.documentCreated FROM %T document %Q %Q %Q
LIMIT 50', LIMIT 50',
$t_doc, $t_doc,

View file

@ -0,0 +1,22 @@
<?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 PhabricatorObjectSelectorDialog extends AphrontDialogView {
}

View file

@ -0,0 +1,12 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'view/dialog');
phutil_require_source('PhabricatorObjectSelectorDialog.php');

View file

@ -24,6 +24,7 @@ class AphrontDialogView extends AphrontView {
private $submitURI; private $submitURI;
private $user; private $user;
private $hidden = array(); private $hidden = array();
private $class;
public function setUser(PhabricatorUser $user) { public function setUser(PhabricatorUser $user) {
$this->user = $user; $this->user = $user;
@ -59,6 +60,11 @@ class AphrontDialogView extends AphrontView {
return $this; return $this;
} }
public function setClass($class) {
$this->class = $class;
return $this;
}
final public function render() { final public function render() {
require_celerity_resource('aphront-dialog-view-css'); require_celerity_resource('aphront-dialog-view-css');
@ -103,10 +109,12 @@ class AphrontDialogView extends AphrontView {
} }
$hidden_inputs = implode("\n", $hidden_inputs); $hidden_inputs = implode("\n", $hidden_inputs);
$more = $this->class;
return javelin_render_tag( return javelin_render_tag(
'form', 'form',
array( array(
'class' => 'aphront-dialog-view', 'class' => 'aphront-dialog-view '.$more,
'action' => $this->submitURI, 'action' => $this->submitURI,
'method' => 'post', 'method' => 'post',
'sigil' => 'jx-dialog', 'sigil' => 'jx-dialog',

View file

@ -0,0 +1,81 @@
/**
* @provides phabricator-object-selector-css
* @requires aphront-dialog-view-css
*/
form.phabricator-object-selector-dialog {
width: 800px;
}
form.phabricator-object-selector-dialog .aphront-dialog-body {
padding: 0;
}
.phabricator-object-selector-search {
width: 100%;
background: #ededed;
}
.phabricator-object-selector-search td {
padding: 4px 8px;
}
td.phabricator-object-selector-search-text {
width: 100%;
}
.phabricator-object-selector-search-text input {
width: 100%;
}
.phabricator-object-selector-results {
position: relative;
height: 16em;
border: solid #bbbbbb;
border-width: 1px 0px;
overflow-y: scroll;
overflow-x: hidden;
}
.phabricator-object-selector-handle {
width: 100%;
background: #e9e9e9;
margin-bottom: 1px;
}
.phabricator-object-selector-handle td {
padding: 4px 1em;
}
.phabricator-object-selector-handle th {
padding: 4px 1em;
font-weight: bold;
vertical-align: middle;
width: 100%;
overflow: hidden;
}
.phabricator-object-selector-header {
padding: 2px;
border-bottom: 1px solid #d0d0d0;
margin-bottom: 16px;
color: #444444;
}
.phabricator-object-selector-attach-explicit {
padding: 4px;
background: #f3f3f3;
border: solid #bbbbbb;
border-width: 1px 0px;
}
.phabricator-object-selector-currently-attached {
background: #fff;
padding: 16px;
border: 1px solid #dddddd;
}
.phabricator-object-selector-current {
background: #ededed;
padding: 8px 8px;
}

View file

@ -1,55 +0,0 @@
/**
* @provides phabricator-core-dialog-css
*/
.jx-dialog {
display: block;
width: 480px;
padding: 8px;
background: #666;
margin: auto;
}
.jx-client-dialog {
position: absolute;
z-index: 6;
}
.jx-dialog .dialog-title {
background: #6d84b4;
border: none;
font-size: 15px;
font-weight: bold;
padding: 5px 12px 6px;
color: #ffffff;
}
.jx-dialog .dialog-body {
background: #ffffff;
padding: 16px 12px;
border: none;
overflow: hidden;
}
.jx-dialog .dialog-foot {
border: none;
background: #ededed;
padding: .5em;
text-align: right;
}
.jx-dialog button {
margin-left: 6px;
}
.jx-dialog input {
padding: 4px;
}
.jx-dialog .fields {
margin-top: 10px;
}
.jx-dialog input.block {
display: block;
margin: 3px 0 0 0;
}

View file

@ -0,0 +1,139 @@
/**
* @provides javelin-behavior-phabricator-object-selector
* @requires javelin-lib-dev
*/
JX.behavior('phabricator-object-selector', function(config) {
var n = 0;
var phids = {};
var handles = {};
var attach_list = {};
function onreceive(seq, r) {
if (seq != n) {
return;
}
var display = [];
attach_list = {};
for (var k in r) {
handles[r[k].phid] = r[k];
display.push(renderHandle(r[k], true));
}
if (!display.length) {
display = renderNote('No results.');
}
JX.DOM.setContent(JX.$(config.results), display);
}
function redrawAttached() {
var display = [];
for (var k in phids) {
display.push(renderHandle(handles[k], false));
}
if (!display.length) {
display = renderNote('Nothing attached.');
}
JX.DOM.setContent(JX.$(config.current), display);
}
function renderHandle(h, attach) {
var td = JX.$N('td');
var table = JX.$N(
'table',
{className: 'phabricator-object-selector-handle'},
JX.$N(
'tbody',
{},
[JX.$N('th', {}, h.name), td]));
var btn = JX.$N(
'a',
{className: 'button small grey'},
attach ? 'Attach' : 'Remove');
JX.Stratcom.addSigil(btn, 'object-attach-button');
JX.Stratcom.addData(btn, {handle : h, table : table});
if (attach) {
attach_list[h.phid] = btn;
}
JX.DOM.setContent(td, btn);
return table;
}
function renderNote(note) {
return JX.$N('div', {}, note);
}
function sendQuery() {
JX.DOM.setContent(JX.$(config.results), renderNote('Loading...'))
new JX.Request(config.uri, JX.bind(null, onreceive, ++n))
.setData({
filter: JX.$(config.filter).value,
query: JX.$(config.query).value
})
.send();
}
JX.DOM.listen(
JX.$(config.search),
'click',
null,
function(e) {
e.kill();
sendQuery();
});
JX.DOM.listen(
JX.$(config.results),
'click',
'object-attach-button',
function(e) {
e.kill();
var button = e.getNode('object-attach-button');
if (button.disabled) {
return;
}
var data = e.getNodeData('object-attach-button');
phids[data.handle.phid] = true;
JX.DOM.alterClass(button, 'disabled', true);
button.disabled = true;
redrawAttached();
});
JX.DOM.listen(
JX.$(config.current),
'click',
'object-attach-button',
function(e) {
e.kill();
var button = e.getNode('object-attach-button');
if (button.disabled) {
return;
}
var data = e.getNodeData('object-attach-button');
delete phids[data.handle.phid];
if (attach_list[data.handle.phid]) {
JX.DOM.alterClass(attach_list[data.handle.phid], 'disabled', false);
attach_list[data.handle.phid].disabled = false;
}
redrawAttached();
});
sendQuery();
redrawAttached();
});