2011-01-25 13:48:05 -08:00
|
|
|
<?php
|
|
|
|
|
|
2012-03-13 16:21:04 -07:00
|
|
|
final class AphrontFormTokenizerControl extends AphrontFormControl {
|
2011-01-25 13:48:05 -08:00
|
|
|
|
|
|
|
|
private $datasource;
|
2011-02-05 12:33:53 -08:00
|
|
|
private $disableBehavior;
|
2011-02-08 10:53:59 -08:00
|
|
|
private $limit;
|
Use Javelin placeholders and new sorting rules broadly; consolidate tokenizer construction code
Summary:
- We have three nearly-identical blocks of Tokenizer construction code; consolidate them into Prefab.
- Add placeholder support.
- Augment server-side stuff to specify placeholder text.
Test Plan: Verified behavior of Differential edit tokenizers, Differential comment tokenizers, Maniphest edit tokenizers, Maniphest comment tokenizers, Maniphest filter tokenizers, Differential filter tokenizers, Owners filter tokenizers, Owners edit tokenizers, Herald edit tokenizers, Audit filter tokenizers.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T772, T946
Differential Revision: https://secure.phabricator.com/D1844
2012-03-09 15:46:39 -08:00
|
|
|
private $placeholder;
|
2015-04-02 13:42:01 -07:00
|
|
|
private $handles;
|
2011-01-25 13:48:05 -08:00
|
|
|
|
2014-07-17 15:49:00 -07:00
|
|
|
public function setDatasource(PhabricatorTypeaheadDatasource $datasource) {
|
2011-01-25 13:48:05 -08:00
|
|
|
$this->datasource = $datasource;
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-04 22:45:42 -08:00
|
|
|
public function setDisableBehavior($disable) {
|
|
|
|
|
$this->disableBehavior = $disable;
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-25 13:48:05 -08:00
|
|
|
protected function getCustomControlClass() {
|
|
|
|
|
return 'aphront-form-control-tokenizer';
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-08 10:53:59 -08:00
|
|
|
public function setLimit($limit) {
|
|
|
|
|
$this->limit = $limit;
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
Use Javelin placeholders and new sorting rules broadly; consolidate tokenizer construction code
Summary:
- We have three nearly-identical blocks of Tokenizer construction code; consolidate them into Prefab.
- Add placeholder support.
- Augment server-side stuff to specify placeholder text.
Test Plan: Verified behavior of Differential edit tokenizers, Differential comment tokenizers, Maniphest edit tokenizers, Maniphest comment tokenizers, Maniphest filter tokenizers, Differential filter tokenizers, Owners filter tokenizers, Owners edit tokenizers, Herald edit tokenizers, Audit filter tokenizers.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T772, T946
Differential Revision: https://secure.phabricator.com/D1844
2012-03-09 15:46:39 -08:00
|
|
|
public function setPlaceholder($placeholder) {
|
|
|
|
|
$this->placeholder = $placeholder;
|
|
|
|
|
return $this;
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-02 13:42:01 -07:00
|
|
|
public function willRender() {
|
|
|
|
|
// Load the handles now so we'll get a bulk load later on when we actually
|
|
|
|
|
// render them.
|
|
|
|
|
$this->loadHandles();
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-25 13:48:05 -08:00
|
|
|
protected function renderInput() {
|
|
|
|
|
$name = $this->getName();
|
2015-03-31 14:10:32 -07:00
|
|
|
|
2015-04-02 13:42:01 -07:00
|
|
|
$handles = $this->loadHandles();
|
|
|
|
|
$handles = iterator_to_array($handles);
|
2013-09-12 13:03:14 -07:00
|
|
|
|
2011-02-04 22:45:42 -08:00
|
|
|
if ($this->getID()) {
|
|
|
|
|
$id = $this->getID();
|
|
|
|
|
} else {
|
|
|
|
|
$id = celerity_generate_unique_node_id();
|
|
|
|
|
}
|
2011-01-25 13:48:05 -08:00
|
|
|
|
2015-04-16 15:30:41 -07:00
|
|
|
$datasource = $this->datasource;
|
2015-04-18 08:38:47 -07:00
|
|
|
if (!$datasource) {
|
|
|
|
|
throw new Exception(
|
|
|
|
|
pht('You must set a datasource to use a TokenizerControl.'));
|
2015-04-17 07:00:43 -07:00
|
|
|
}
|
2015-04-18 08:38:47 -07:00
|
|
|
$datasource->setViewer($this->getUser());
|
2015-04-16 15:30:41 -07:00
|
|
|
|
2014-07-17 15:49:00 -07:00
|
|
|
$placeholder = null;
|
2014-07-10 16:18:04 -07:00
|
|
|
if (!strlen($this->placeholder)) {
|
2015-04-18 08:38:47 -07:00
|
|
|
$placeholder = $datasource->getPlaceholderText();
|
Use Javelin placeholders and new sorting rules broadly; consolidate tokenizer construction code
Summary:
- We have three nearly-identical blocks of Tokenizer construction code; consolidate them into Prefab.
- Add placeholder support.
- Augment server-side stuff to specify placeholder text.
Test Plan: Verified behavior of Differential edit tokenizers, Differential comment tokenizers, Maniphest edit tokenizers, Maniphest comment tokenizers, Maniphest filter tokenizers, Differential filter tokenizers, Owners filter tokenizers, Owners edit tokenizers, Herald edit tokenizers, Audit filter tokenizers.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T772, T946
Differential Revision: https://secure.phabricator.com/D1844
2012-03-09 15:46:39 -08:00
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:30:41 -07:00
|
|
|
$tokens = array();
|
|
|
|
|
$values = nonempty($this->getValue(), array());
|
|
|
|
|
foreach ($values as $value) {
|
|
|
|
|
if (isset($handles[$value])) {
|
|
|
|
|
$token = PhabricatorTypeaheadTokenView::newFromHandle($handles[$value]);
|
|
|
|
|
} else {
|
|
|
|
|
$token = null;
|
2015-04-18 08:38:47 -07:00
|
|
|
|
|
|
|
|
$function = $datasource->parseFunction($value);
|
|
|
|
|
if ($function) {
|
|
|
|
|
$token_list = $datasource->renderFunctionTokens(
|
|
|
|
|
$function['name'],
|
|
|
|
|
array($function['argv']));
|
|
|
|
|
$token = head($token_list);
|
2015-04-16 15:30:41 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!$token) {
|
|
|
|
|
$name = pht('Invalid Function: %s', $value);
|
|
|
|
|
$token = $datasource->newInvalidToken($name);
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-17 07:00:43 -07:00
|
|
|
$type = $token->getTokenType();
|
|
|
|
|
if ($type == PhabricatorTypeaheadTokenView::TYPE_INVALID) {
|
2015-04-16 15:30:41 -07:00
|
|
|
$token->setKey($value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$token->setInputName($this->getName());
|
|
|
|
|
$tokens[] = $token;
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-22 20:41:02 -07:00
|
|
|
$template = new AphrontTokenizerTemplateView();
|
|
|
|
|
$template->setName($name);
|
|
|
|
|
$template->setID($id);
|
2015-04-16 15:30:41 -07:00
|
|
|
$template->setValue($tokens);
|
2011-03-22 20:41:02 -07:00
|
|
|
|
Improve tokenizer sorting rules
Summary:
Currently, we sort all results alphabetically. This isn't ideal. Instead, sort them like this:
- If the viewing user appears in the list, always sort them first. This is common in a lot of contexts and some "Ben Evans" guy is sorting first on secure.phabricator.com and causing me no end of aggravation.
- If the tokens match a "priority" component (e.g., username), sort that before results which do not have a "priority" match.
- Within a group (self, priority, everything else) sort tokens alphabetically.
NOTE: I need to go add setUser() to all the tokenizers to make the "self" rule work, but that's trivial so I figured I'd get this out first.
Test Plan:
https://secure.phabricator.com/file/data/4s2a72l5hhyyqqkq4bnd/PHID-FILE-x2r6ubk7s7dz54kxmtwx/Screen_Shot_2012-03-07_at_9.18.03_AM.png
Previously, "aaaaaepriestley" (first alphabetic match) would sort before "epriestley" (the viewing user). Now, "epriestley" sorts first because that is the viewer.
https://secure.phabricator.com/file/data/rmnxgnafz42f23fsjwui/PHID-FILE-yrnn55jl3ysbntldq3af/Screen_Shot_2012-03-07_at_9.18.09_AM.png
Previously, "aaaagopher" (first alphabetic match) would sort before "banana" (the "priority" match). Now, "banana" sorts first because it priority matches on username.
Reviewers: btrahan
Reviewed By: btrahan
CC: aran, epriestley
Maniphest Tasks: T946
Differential Revision: https://secure.phabricator.com/D1807
2012-03-07 13:17:44 -08:00
|
|
|
$username = null;
|
|
|
|
|
if ($this->user) {
|
|
|
|
|
$username = $this->user->getUsername();
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-18 08:38:47 -07:00
|
|
|
$datasource_uri = $datasource->getDatasourceURI();
|
|
|
|
|
$browse_uri = $datasource->getBrowseURI();
|
|
|
|
|
if ($browse_uri) {
|
|
|
|
|
$template->setBrowseURI($browse_uri);
|
2014-07-10 16:18:04 -07:00
|
|
|
}
|
|
|
|
|
|
2011-02-04 22:45:42 -08:00
|
|
|
if (!$this->disableBehavior) {
|
|
|
|
|
Javelin::initBehavior('aphront-basic-tokenizer', array(
|
2015-04-17 07:00:43 -07:00
|
|
|
'id' => $id,
|
|
|
|
|
'src' => $datasource_uri,
|
|
|
|
|
'value' => mpull($tokens, 'getValue', 'getKey'),
|
|
|
|
|
'icons' => mpull($tokens, 'getIcon', 'getKey'),
|
|
|
|
|
'types' => mpull($tokens, 'getTokenType', 'getKey'),
|
2015-04-19 07:17:54 -07:00
|
|
|
'colors' => mpull($tokens, 'getColor', 'getKey'),
|
2015-04-17 07:00:43 -07:00
|
|
|
'limit' => $this->limit,
|
|
|
|
|
'username' => $username,
|
2014-07-10 16:18:04 -07:00
|
|
|
'placeholder' => $placeholder,
|
2015-04-16 13:37:12 -07:00
|
|
|
'browseURI' => $browse_uri,
|
2011-02-04 22:45:42 -08:00
|
|
|
));
|
|
|
|
|
}
|
2011-01-25 13:48:05 -08:00
|
|
|
|
2011-03-22 20:41:02 -07:00
|
|
|
return $template->render();
|
2011-01-25 13:48:05 -08:00
|
|
|
}
|
|
|
|
|
|
2015-04-02 13:42:01 -07:00
|
|
|
private function loadHandles() {
|
|
|
|
|
if ($this->handles === null) {
|
|
|
|
|
$viewer = $this->getUser();
|
|
|
|
|
if (!$viewer) {
|
|
|
|
|
throw new Exception(
|
|
|
|
|
pht(
|
|
|
|
|
'Call setUser() before rendering tokenizers. Use appendControl() '.
|
|
|
|
|
'on AphrontFormView to do this easily.'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$values = nonempty($this->getValue(), array());
|
2015-04-16 15:30:41 -07:00
|
|
|
|
|
|
|
|
$phids = array();
|
|
|
|
|
foreach ($values as $value) {
|
|
|
|
|
if (!PhabricatorTypeaheadDatasource::isFunctionToken($value)) {
|
|
|
|
|
$phids[] = $value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->handles = $viewer->loadHandles($phids);
|
2015-04-02 13:42:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->handles;
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-25 13:48:05 -08:00
|
|
|
}
|