diff --git a/src/applications/herald/controller/HeraldRuleController.php b/src/applications/herald/controller/HeraldRuleController.php --- a/src/applications/herald/controller/HeraldRuleController.php +++ b/src/applications/herald/controller/HeraldRuleController.php @@ -605,9 +605,12 @@ ); foreach ($sources as $key => $source) { + $source->setViewer($this->getViewer()); + $sources[$key] = array( 'uri' => $source->getDatasourceURI(), 'placeholder' => $source->getPlaceholderText(), + 'browseURI' => $source->getBrowseURI(), ); } diff --git a/src/applications/mailinglists/typeahead/PhabricatorMailingListDatasource.php b/src/applications/mailinglists/typeahead/PhabricatorMailingListDatasource.php --- a/src/applications/mailinglists/typeahead/PhabricatorMailingListDatasource.php +++ b/src/applications/mailinglists/typeahead/PhabricatorMailingListDatasource.php @@ -3,11 +3,6 @@ final class PhabricatorMailingListDatasource extends PhabricatorTypeaheadDatasource { - public function isBrowsable() { - // TODO: Make this browsable if we don't delete it before then. - return false; - } - public function getPlaceholderText() { return pht('Type a mailing list name...'); } @@ -20,11 +15,10 @@ $viewer = $this->getViewer(); $raw_query = $this->getRawQuery(); - $results = array(); + $query = id(new PhabricatorMailingListQuery()); + $lists = $this->executeQuery($query); - $lists = id(new PhabricatorMailingListQuery()) - ->setViewer($viewer) - ->execute(); + $results = array(); foreach ($lists as $list) { $results[] = id(new PhabricatorTypeaheadResult()) ->setName($list->getName()) @@ -32,7 +26,10 @@ ->setPHID($list->getPHID()); } - return $results; + // TODO: It would be slightly preferable to do this as part of the query, + // this is just simpler for the moment. + + return $this->filterResultsAgainstTokens($results); } } diff --git a/src/applications/maniphest/controller/ManiphestBatchEditController.php b/src/applications/maniphest/controller/ManiphestBatchEditController.php --- a/src/applications/maniphest/controller/ManiphestBatchEditController.php +++ b/src/applications/maniphest/controller/ManiphestBatchEditController.php @@ -65,7 +65,9 @@ $projects_source = new PhabricatorProjectDatasource(); $mailable_source = new PhabricatorMetaMTAMailableDatasource(); + $mailable_source->setViewer($user); $owner_source = new PhabricatorTypeaheadOwnerDatasource(); + $owner_source->setViewer($user); require_celerity_resource('maniphest-batch-editor'); Javelin::initBehavior( @@ -75,17 +77,20 @@ 'tokenizerTemplate' => $template, 'sources' => array( 'project' => array( - 'src' => $projects_source->getDatasourceURI(), - 'placeholder' => $projects_source->getPlaceholderText(), + 'src' => $projects_source->getDatasourceURI(), + 'placeholder' => $projects_source->getPlaceholderText(), + 'browseURI' => $projects_source->getBrowseURI(), ), 'owner' => array( - 'src' => $owner_source->getDatasourceURI(), - 'placeholder' => $owner_source->getPlaceholderText(), - 'limit' => 1, + 'src' => $owner_source->getDatasourceURI(), + 'placeholder' => $owner_source->getPlaceholderText(), + 'browseURI' => $owner_source->getBrowseURI(), + 'limit' => 1, ), - 'cc' => array( - 'src' => $mailable_source->getDatasourceURI(), - 'placeholder' => $mailable_source->getPlaceholderText(), + 'cc' => array( + 'src' => $mailable_source->getDatasourceURI(), + 'placeholder' => $mailable_source->getPlaceholderText(), + 'browseURI' => $mailable_source->getBrowseURI(), ), ), 'input' => 'batch-form-actions', diff --git a/src/applications/policy/rule/PhabricatorLegalpadSignaturePolicyRule.php b/src/applications/policy/rule/PhabricatorLegalpadSignaturePolicyRule.php --- a/src/applications/policy/rule/PhabricatorLegalpadSignaturePolicyRule.php +++ b/src/applications/policy/rule/PhabricatorLegalpadSignaturePolicyRule.php @@ -40,13 +40,7 @@ } public function getValueControlTemplate() { - $datasource = new LegalpadDocumentDatasource(); - - return array( - 'markup' => new AphrontTokenizerTemplateView(), - 'uri' => $datasource->getDatasourceURI(), - 'placeholder' => $datasource->getPlaceholderText(), - ); + return $this->getDatasourceTemplate(new LegalpadDocumentDatasource()); } public function getRuleOrder() { diff --git a/src/applications/policy/rule/PhabricatorPolicyRule.php b/src/applications/policy/rule/PhabricatorPolicyRule.php --- a/src/applications/policy/rule/PhabricatorPolicyRule.php +++ b/src/applications/policy/rule/PhabricatorPolicyRule.php @@ -22,6 +22,16 @@ return null; } + protected function getDatasourceTemplate( + PhabricatorTypeaheadDatasource $datasource) { + return array( + 'markup' => new AphrontTokenizerTemplateView(), + 'uri' => $datasource->getDatasourceURI(), + 'placeholder' => $datasource->getPlaceholderText(), + 'browseURI' => $datasource->getBrowseURI(), + ); + } + public function getRuleOrder() { return 500; } diff --git a/src/applications/policy/rule/PhabricatorProjectsPolicyRule.php b/src/applications/policy/rule/PhabricatorProjectsPolicyRule.php --- a/src/applications/policy/rule/PhabricatorProjectsPolicyRule.php +++ b/src/applications/policy/rule/PhabricatorProjectsPolicyRule.php @@ -38,13 +38,7 @@ } public function getValueControlTemplate() { - $projects_source = new PhabricatorProjectDatasource(); - - return array( - 'markup' => new AphrontTokenizerTemplateView(), - 'uri' => $projects_source->getDatasourceURI(), - 'placeholder' => $projects_source->getPlaceholderText(), - ); + return $this->getDatasourceTemplate(new PhabricatorProjectDatasource()); } public function getRuleOrder() { diff --git a/src/applications/policy/rule/PhabricatorUsersPolicyRule.php b/src/applications/policy/rule/PhabricatorUsersPolicyRule.php --- a/src/applications/policy/rule/PhabricatorUsersPolicyRule.php +++ b/src/applications/policy/rule/PhabricatorUsersPolicyRule.php @@ -20,13 +20,7 @@ } public function getValueControlTemplate() { - $users_datasource = new PhabricatorPeopleDatasource(); - - return array( - 'markup' => new AphrontTokenizerTemplateView(), - 'uri' => $users_datasource->getDatasourceURI(), - 'placeholder' => $users_datasource->getPlaceholderText(), - ); + return $this->getDatasourceTemplate(new PhabricatorPeopleDatasource()); } public function getRuleOrder() { diff --git a/src/view/control/AphrontTokenizerTemplateView.php b/src/view/control/AphrontTokenizerTemplateView.php --- a/src/view/control/AphrontTokenizerTemplateView.php +++ b/src/view/control/AphrontTokenizerTemplateView.php @@ -67,35 +67,40 @@ $content[] = $input; $content[] = phutil_tag('div', array('style' => 'clear: both;'), ''); - $container = phutil_tag( + $container = javelin_tag( 'div', array( 'id' => $id, 'class' => 'jx-tokenizer-container', + 'sigil' => 'tokenizer-container', ), $content); - $browse = null; + $icon = id(new PHUIIconView()) + ->setIconFont('fa-list-ul'); + + // TODO: This thing is ugly and the ugliness is not intentional. + // We have to give it text or PHUIButtonView collapses. It should likely + // just be an icon and look more integrated into the input. + $browse = id(new PHUIButtonView()) + ->setTag('a') + ->setIcon($icon) + ->addSigil('tokenizer-browse') + ->setColor(PHUIButtonView::GREY) + ->setSize(PHUIButtonView::SMALL) + ->setText(pht('Browse...')); + + $classes = array(); + $classes[] = 'jx-tokenizer-frame'; + if ($this->browseURI) { - $icon = id(new PHUIIconView()) - ->setIconFont('fa-list-ul'); - - // TODO: This thing is ugly and the ugliness is not intentional. - // We have to give it text or PHUIButtonView collapses. It should likely - // just be an icon and look more integrated into the input. - $browse = id(new PHUIButtonView()) - ->setTag('a') - ->setIcon($icon) - ->addSigil('tokenizer-browse') - ->setColor(PHUIButtonView::GREY) - ->setSize(PHUIButtonView::SMALL) - ->setText(pht('Browse...')); + $classes[] = 'has-browse'; } $frame = javelin_tag( 'table', array( - 'class' => 'jx-tokenizer-frame', + 'class' => implode(' ', $classes), 'sigil' => 'tokenizer-frame', ), phutil_tag( diff --git a/webroot/rsrc/css/aphront/tokenizer.css b/webroot/rsrc/css/aphront/tokenizer.css --- a/webroot/rsrc/css/aphront/tokenizer.css +++ b/webroot/rsrc/css/aphront/tokenizer.css @@ -109,7 +109,15 @@ width: 100%; } -.jx-tokenizer-frame-input { +.jx-tokenizer-frame .jx-tokenizer-frame-browse { + display: none; +} + +.has-browse .jx-tokenizer-frame-browse { + display: table-cell; +} + +.jx-tokenizer-frame td.jx-tokenizer-frame-input { width: 100%; } diff --git a/webroot/rsrc/externals/javelin/lib/control/tokenizer/Tokenizer.js b/webroot/rsrc/externals/javelin/lib/control/tokenizer/Tokenizer.js --- a/webroot/rsrc/externals/javelin/lib/control/tokenizer/Tokenizer.js +++ b/webroot/rsrc/externals/javelin/lib/control/tokenizer/Tokenizer.js @@ -85,6 +85,7 @@ } if (this._frame) { + JX.DOM.alterClass(this._frame, 'has-browse', !!this.getBrowseURI()); JX.DOM.listen( this._frame, 'click', diff --git a/webroot/rsrc/js/application/herald/HeraldRuleEditor.js b/webroot/rsrc/js/application/herald/HeraldRuleEditor.js --- a/webroot/rsrc/js/application/herald/HeraldRuleEditor.js +++ b/webroot/rsrc/js/application/herald/HeraldRuleEditor.js @@ -277,25 +277,21 @@ }, _newTokenizer : function(type) { - var template = JX.$N( - 'div', - JX.$H(this._config.template.markup)); - template = template.firstChild; - template.id = ''; - var tokenizerConfig = { - root : template, src : this._config.template.source[type].uri, placeholder: this._config.template.source[type].placeholder, + browseURI: this._config.template.source[type].browseURI, icons : this._config.template.icons, username : this._config.username }; - var build = JX.Prefab.buildTokenizer(tokenizerConfig); + var build = JX.Prefab.newTokenizerFromTemplate( + this._config.template.markup, + tokenizerConfig); build.tokenizer.start(); return [ - template, + build.node, function() { return build.tokenizer.getTokens(); }, diff --git a/webroot/rsrc/js/application/maniphest/behavior-batch-editor.js b/webroot/rsrc/js/application/maniphest/behavior-batch-editor.js --- a/webroot/rsrc/js/application/maniphest/behavior-batch-editor.js +++ b/webroot/rsrc/js/application/maniphest/behavior-batch-editor.js @@ -136,18 +136,14 @@ }); function build_tokenizer(tconfig) { - var template = JX.$N('div', JX.$H(config.tokenizerTemplate)).firstChild; - template.id = ''; - - var build_config = JX.copy({}, tconfig); - build_config.root = template; - - var built = JX.Prefab.buildTokenizer(build_config); + var built = JX.Prefab.newTokenizerFromTemplate( + config.tokenizerTemplate, + JX.copy({}, tconfig)); built.tokenizer.start(); return { object: built.tokenizer, - template: template + template: built.node }; } diff --git a/webroot/rsrc/js/application/policy/behavior-policy-rule-editor.js b/webroot/rsrc/js/application/policy/behavior-policy-rule-editor.js --- a/webroot/rsrc/js/application/policy/behavior-policy-rule-editor.js +++ b/webroot/rsrc/js/application/policy/behavior-policy-rule-editor.js @@ -118,17 +118,20 @@ switch (type) { case 'tokenizer': - node = JX.$H(template.markup).getNode(); - node.id = ''; - var options = { - root: node, src: template.uri, placeholder: template.placeholder, + browseURI: template.browseURI, limit: template.limit }; - var tokenizer = JX.Prefab.buildTokenizer(options).tokenizer; + var build = JX.Prefab.newTokenizerFromTemplate( + template.markup, + options); + + node = build.node; + + var tokenizer = build.tokenizer; tokenizer.start(); get_fn = function() { return JX.keys(tokenizer.getTokens()); }; diff --git a/webroot/rsrc/js/core/Prefab.js b/webroot/rsrc/js/core/Prefab.js --- a/webroot/rsrc/js/core/Prefab.js +++ b/webroot/rsrc/js/core/Prefab.js @@ -31,6 +31,17 @@ return select; }, + newTokenizerFromTemplate: function(markup, config) { + var template = JX.$H(markup).getFragment().firstChild; + var container = JX.DOM.find(template, 'div', 'tokenizer-container'); + + container.id = ''; + config.root = container; + + var build = JX.Prefab.buildTokenizer(config); + build.node = template; + return build; + }, /** * Build a Phabricator tokenizer out of a configuration with application