@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class PhabricatorMainMenuSearchView extends AphrontView {
4
5 const DEFAULT_APPLICATION_ICON = 'fa-dot-circle-o';
6
7 private $id;
8 private $application;
9
10 public function setApplication(PhabricatorApplication $application) {
11 $this->application = $application;
12 return $this;
13 }
14
15 public function getApplication() {
16 return $this->application;
17 }
18
19 public function getID() {
20 if (!$this->id) {
21 $this->id = celerity_generate_unique_node_id();
22 }
23 return $this->id;
24 }
25
26 public function render() {
27 $viewer = $this->getViewer();
28
29 $target_id = celerity_generate_unique_node_id();
30 $search_id = $this->getID();
31 $button_id = celerity_generate_unique_node_id();
32 $selector_id = celerity_generate_unique_node_id();
33 $application_id = celerity_generate_unique_node_id();
34
35 $input = phutil_tag(
36 'input',
37 array(
38 'type' => 'text',
39 'name' => 'query',
40 'id' => $search_id,
41 'autocomplete' => 'off',
42 'autocorrect' => 'off',
43 'autocapitalize' => 'off',
44 'spellcheck' => 'false',
45 'aria-label' => pht('Search'),
46 ));
47
48 $target = javelin_tag(
49 'div',
50 array(
51 'id' => $target_id,
52 'class' => 'phabricator-main-menu-search-target',
53 ),
54 '');
55
56 $search_datasource = new PhabricatorSearchDatasource();
57 $scope_key = PhabricatorSearchScopeSetting::SETTINGKEY;
58
59 Javelin::initBehavior(
60 'phabricator-search-typeahead',
61 array(
62 'id' => $target_id,
63 'input' => $search_id,
64 'button' => $button_id,
65 'selectorID' => $selector_id,
66 'applicationID' => $application_id,
67 'defaultApplicationIcon' => self::DEFAULT_APPLICATION_ICON,
68 'appScope' => PhabricatorSearchController::SCOPE_CURRENT_APPLICATION,
69 'src' => $search_datasource->getDatasourceURI(),
70 'limit' => 10,
71 'placeholder' => pht('Search'),
72 'scopeUpdateURI' => '/settings/adjust/?key='.$scope_key,
73 ));
74
75 $primary_input = phutil_tag(
76 'input',
77 array(
78 'type' => 'hidden',
79 'name' => 'search:primary',
80 'value' => 'true',
81 ));
82
83 $search_text = javelin_tag(
84 'span',
85 array(
86 'aural' => true,
87 ),
88 pht('Search'));
89
90 $selector = $this->buildModeSelector($selector_id, $application_id);
91
92 $form = phabricator_form(
93 $viewer,
94 array(
95 'action' => '/search/',
96 'method' => 'POST',
97 ),
98 phutil_tag(
99 'div',
100 array(
101 'class' => 'phabricator-main-menu-search-container',
102 ),
103 array(
104 $input,
105 phutil_tag(
106 'button',
107 array(
108 'id' => $button_id,
109 'class' => 'phui-icon-view phui-font-fa fa-search',
110 ),
111 $search_text),
112 $selector,
113 $primary_input,
114 $target,
115 )));
116
117 return $form;
118 }
119
120 public static function getGlobalSearchScopeItems(
121 PhabricatorUser $viewer,
122 ?PhabricatorApplication $application = null,
123 $global_only = false) {
124
125 $items = array();
126 $items[] = array(
127 'name' => pht('Global Search'),
128 );
129
130 $items[] = array(
131 'icon' => 'fa-globe',
132 'name' => pht('All Items'),
133 'value' => 'all',
134 );
135
136 if ($application && $application->getApplicationSearchDocumentTypes()) {
137 $application_icon = $application->getIcon();
138 $application_name = $application->getName();
139 if (!$application_icon) {
140 $application_icon = self::DEFAULT_APPLICATION_ICON;
141 }
142 $items[] = array(
143 'icon' => $application_icon,
144 'name' => pht('In %s Only', $application_name),
145 'value' => PhabricatorSearchController::SCOPE_CURRENT_APPLICATION,
146 );
147 }
148
149 $items[] = array(
150 'name' => pht('Saved Global Queries'),
151 );
152
153
154 $engine = id(new PhabricatorSearchApplicationSearchEngine())
155 ->setViewer($viewer);
156 $engine_queries = $engine->loadEnabledNamedQueries();
157 foreach ($engine_queries as $query) {
158 $query_key = $query->getQueryKey();
159 if ($query_key == 'all') {
160 // Skip the builtin "All" query since it's redundant with the default
161 // setting.
162 continue;
163 }
164
165 // In the global "Settings" panel, we don't want to offer personal
166 // queries the viewer may have saved.
167 if ($global_only) {
168 if (!$query->isGlobal()) {
169 continue;
170 }
171 }
172
173 $query_name = $query->getQueryName();
174
175 $items[] = array(
176 'icon' => 'fa-certificate',
177 'name' => $query_name,
178 'value' => $query_key,
179 );
180 }
181
182 $items[] = array(
183 'name' => pht('Documentation'),
184 );
185
186 $items[] = array(
187 'icon' => 'fa-book',
188 'name' => pht('User Guide: Search'),
189 'href' => PhabricatorEnv::getDoclink('Search User Guide'),
190 );
191
192 return $items;
193 }
194
195 private function buildModeSelector($selector_id, $application_id) {
196 $viewer = $this->getViewer();
197
198 $items = self::getGlobalSearchScopeItems($viewer, $this->getApplication());
199
200 $scope_key = PhabricatorSearchScopeSetting::SETTINGKEY;
201 $current_value = $viewer->getUserSetting($scope_key);
202
203 $current_icon = 'fa-globe';
204 foreach ($items as $item) {
205 if (idx($item, 'value') == $current_value) {
206 $current_icon = $item['icon'];
207 break;
208 }
209 }
210
211 $application = $this->getApplication();
212
213 $application_value = null;
214 if ($application) {
215 $application_value = get_class($application);
216 }
217
218 $selector = id(new PHUIButtonView())
219 ->setID($selector_id)
220 ->addClass('phabricator-main-menu-search-dropdown')
221 ->addSigil('global-search-dropdown')
222 ->setMetadata(
223 array(
224 'items' => $items,
225 'icon' => $current_icon,
226 'value' => $current_value,
227 ))
228 ->setIcon(
229 id(new PHUIIconView())
230 ->addSigil('global-search-dropdown-icon')
231 ->setIcon($current_icon))
232 ->setAuralLabel(pht('Configure Global Search'))
233 ->setDropdown(true);
234
235 $input = javelin_tag(
236 'input',
237 array(
238 'type' => 'hidden',
239 'sigil' => 'global-search-dropdown-input',
240 'name' => 'search:scope',
241 'value' => $current_value,
242 ));
243
244 $application_input = javelin_tag(
245 'input',
246 array(
247 'type' => 'hidden',
248 'id' => $application_id,
249 'sigil' => 'global-search-dropdown-app',
250 'name' => 'search:application',
251 'value' => $application_value,
252 ));
253
254 return array($selector, $input, $application_input);
255 }
256
257}