| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  | <?php | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | final class PhabricatorApplicationSearchController | 
					
						
							|  |  |  |   extends PhabricatorSearchBaseController { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   private $searchEngine; | 
					
						
							|  |  |  |   private $navigation; | 
					
						
							|  |  |  |   private $queryKey; | 
					
						
							| 
									
										
											  
											
												Move edit/deactivate operations onto project view page in Releeph
Summary:
Ref T3092.
Releeph's objects basically go like this:
  - At the top level, we have Projects (like "www" or "libphutil")
  - Each project has Branches (like "LATEST" or "v1.1.3")
  - Each branch has Requests (like pull requests, e.g. "please merge commit X into branch Y (in project Z)")
Currently, there's no real "project detail" or "branch detail" page. Instead, we have a search results page for their contained objects. That is, the "project detail" page shows a list of branches in the project, using ApplicationSearch.
This means that operations like "edit" and "deactivate" are one level up, on the respective list pages.
Instead, move details onto the detail pages. This gives us more room for actions and information, and simplifies the list views.
Basically, these are "detail pages" where the object content is a search interface. We do something simliar to this in Phame right now, although it's messier there (no ApplicationSearch yet).
@chad, you might have some ideas here. Roughly, the design question is "How should we present an object's detail view when its content is really a search interface (Phame Blog for Posts, Releeph Project for Branches)?"
I think the simple approach I've taken here (see screenshot) gives us reasonable results, but overall it's something we haven't done much or done too much thinking about, I think.
Test Plan: {F54774}
Reviewers: btrahan
Reviewed By: btrahan
CC: chad, aran
Maniphest Tasks: T3092
Differential Revision: https://secure.phabricator.com/D6771
											
										 
											2013-08-19 18:30:30 -07:00
										 |  |  |   private $preface; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   public function setPreface($preface) { | 
					
						
							|  |  |  |     $this->preface = $preface; | 
					
						
							|  |  |  |     return $this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   public function getPreface() { | 
					
						
							|  |  |  |     return $this->preface; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   public function setQueryKey($query_key) { | 
					
						
							|  |  |  |     $this->queryKey = $query_key; | 
					
						
							|  |  |  |     return $this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   protected function getQueryKey() { | 
					
						
							|  |  |  |     return $this->queryKey; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   public function setNavigation(AphrontSideNavFilterView $navigation) { | 
					
						
							|  |  |  |     $this->navigation = $navigation; | 
					
						
							|  |  |  |     return $this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   protected function getNavigation() { | 
					
						
							|  |  |  |     return $this->navigation; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   public function setSearchEngine( | 
					
						
							|  |  |  |     PhabricatorApplicationSearchEngine $search_engine) { | 
					
						
							|  |  |  |     $this->searchEngine = $search_engine; | 
					
						
							|  |  |  |     return $this; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   protected function getSearchEngine() { | 
					
						
							|  |  |  |     return $this->searchEngine; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   protected function validateDelegatingController() { | 
					
						
							|  |  |  |     $parent = $this->getDelegatingController(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!$parent) { | 
					
						
							|  |  |  |       throw new Exception( | 
					
						
							| 
									
										
										
										
											2015-03-01 09:41:00 +11:00
										 |  |  |         pht('You must delegate to this controller, not invoke it directly.')); | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $engine = $this->getSearchEngine(); | 
					
						
							|  |  |  |     if (!$engine) { | 
					
						
							| 
									
										
										
										
											2015-05-14 07:53:52 +10:00
										 |  |  |       throw new PhutilInvalidStateException('setEngine'); | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $engine->setViewer($this->getRequest()->getUser()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $parent = $this->getDelegatingController(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   public function processRequest() { | 
					
						
							|  |  |  |     $this->validateDelegatingController(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $key = $this->getQueryKey(); | 
					
						
							|  |  |  |     if ($key == 'edit') { | 
					
						
							|  |  |  |       return $this->processEditRequest(); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       return $this->processSearchRequest(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   private function processSearchRequest() { | 
					
						
							|  |  |  |     $parent = $this->getDelegatingController(); | 
					
						
							|  |  |  |     $request = $this->getRequest(); | 
					
						
							|  |  |  |     $user = $request->getUser(); | 
					
						
							|  |  |  |     $engine = $this->getSearchEngine(); | 
					
						
							|  |  |  |     $nav = $this->getNavigation(); | 
					
						
							| 
									
										
											  
											
												Make mobile navigation work properly by default in more cases
Summary:
Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are:
  - Don't load named queries multiple times on search pages.
  - Don't require extra code to get standard navigation right on mobile.
  - Reduce the amount of boilerplate in ListControllers.
  - Reduce the amount of boilerplate around navigation/menus in all controllers.
Specifically, here's what this does:
  - The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment.
    - It uses this to automatically add crumb actions to the application menu.
    - It uses this to automatically reuse one SearchEngine instead of running queries multiple times.
  - The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc).
  - SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff.
  - Building menus is generally simpler.
Test Plan:
  - Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu.
  - Edited saved queries.
  - Tested Differential, Maniphest (no changes).
  - Verified the paste pages don't run any duplicate NamedQuery queries.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T5752
Differential Revision: https://secure.phabricator.com/D14382
											
										 
											2015-11-02 12:06:28 -08:00
										 |  |  |     if (!$nav) { | 
					
						
							|  |  |  |       $nav = $this->buildNavigation(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if ($request->isFormPost()) { | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:37 -07:00
										 |  |  |       $saved_query = $engine->buildSavedQueryFromRequest($request); | 
					
						
							| 
									
										
										
										
											2014-05-20 11:42:05 -07:00
										 |  |  |       $engine->saveQuery($saved_query); | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |       return id(new AphrontRedirectResponse())->setURI( | 
					
						
							| 
									
										
										
										
											2013-09-17 11:30:39 -07:00
										 |  |  |         $engine->getQueryResultsPageURI($saved_query->getQueryKey()).'#R'); | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $named_query = null; | 
					
						
							|  |  |  |     $run_query = true; | 
					
						
							|  |  |  |     $query_key = $this->queryKey; | 
					
						
							|  |  |  |     if ($this->queryKey == 'advanced') { | 
					
						
							|  |  |  |       $run_query = false; | 
					
						
							|  |  |  |       $query_key = $request->getStr('query'); | 
					
						
							| 
									
										
										
										
											2013-06-05 18:58:50 -07:00
										 |  |  |     } else if (!strlen($this->queryKey)) { | 
					
						
							| 
									
										
										
										
											2013-11-27 12:18:10 -08:00
										 |  |  |       $found_query_data = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-21 16:21:48 -08:00
										 |  |  |       if ($request->isHTTPGet() || $request->isQuicksand()) { | 
					
						
							| 
									
										
											  
											
												Allow construction of ApplicationSearch queries with GET
Summary:
Ref T3775 (discussion here). Ref T2625.
T3775 presents two problems:
  # Existing tools which linked to `/differential/active/epriestley/` (that is, put a username in the URL) can't generate search links now.
  # Humans can't edit the URL anymore, either.
I think (1) is an actual issue, and this fixes it. I think (2) is pretty fluff, and this doesn't really try to fix it, although it probably improves it.
The fix for (1) is:
  - Provide a helper to read a parameter containing either a list of user PHIDs or a list of usernames, so `/?users[]=PHID-USER-xyz` (from a tokenizer) and `/?users=alincoln,htaft` (from an external program) are equivalent inputs.
  - Rename all the form parameters to be more digestable (`authorPHIDs` -> `authors`). Almost all of them were in this form already anyway. This just gives us `?users=alincoln` instead of `userPHIDs=alincoln`.
  - Inside ApplicationSearch, if a request has no query associated with it but does have query parameters, build a query from the request instead of issuing the user's default query. Basically, this means that `/differential/` runs the default query, while `/differential/?users=x` runs a custom query.
Test Plan: {F56612}
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T2625, T3775
Differential Revision: https://secure.phabricator.com/D6840
											
										 
											2013-08-29 11:52:29 -07:00
										 |  |  |         // If this is a GET request and it has some query data, don't
 | 
					
						
							| 
									
										
										
										
											2013-11-27 12:18:10 -08:00
										 |  |  |         // do anything unless it's only before= or after=. We'll build and
 | 
					
						
							|  |  |  |         // execute a query from it below. This allows external tools to build
 | 
					
						
							|  |  |  |         // URIs like "/query/?users=a,b".
 | 
					
						
							|  |  |  |         $pt_data = $request->getPassthroughRequestData(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-19 12:43:05 -08:00
										 |  |  |         $exempt = array( | 
					
						
							|  |  |  |           'before' => true, | 
					
						
							|  |  |  |           'after' => true, | 
					
						
							|  |  |  |           'nux' => true, | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-27 12:18:10 -08:00
										 |  |  |         foreach ($pt_data as $pt_key => $pt_value) { | 
					
						
							| 
									
										
										
										
											2015-12-19 12:43:05 -08:00
										 |  |  |           if (isset($exempt[$pt_key])) { | 
					
						
							|  |  |  |             continue; | 
					
						
							| 
									
										
										
										
											2013-11-27 12:18:10 -08:00
										 |  |  |           } | 
					
						
							| 
									
										
										
										
											2015-12-19 12:43:05 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |           $found_query_data = true; | 
					
						
							|  |  |  |           break; | 
					
						
							| 
									
										
										
										
											2013-11-27 12:18:10 -08:00
										 |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (!$found_query_data) { | 
					
						
							| 
									
										
											  
											
												Allow construction of ApplicationSearch queries with GET
Summary:
Ref T3775 (discussion here). Ref T2625.
T3775 presents two problems:
  # Existing tools which linked to `/differential/active/epriestley/` (that is, put a username in the URL) can't generate search links now.
  # Humans can't edit the URL anymore, either.
I think (1) is an actual issue, and this fixes it. I think (2) is pretty fluff, and this doesn't really try to fix it, although it probably improves it.
The fix for (1) is:
  - Provide a helper to read a parameter containing either a list of user PHIDs or a list of usernames, so `/?users[]=PHID-USER-xyz` (from a tokenizer) and `/?users=alincoln,htaft` (from an external program) are equivalent inputs.
  - Rename all the form parameters to be more digestable (`authorPHIDs` -> `authors`). Almost all of them were in this form already anyway. This just gives us `?users=alincoln` instead of `userPHIDs=alincoln`.
  - Inside ApplicationSearch, if a request has no query associated with it but does have query parameters, build a query from the request instead of issuing the user's default query. Basically, this means that `/differential/` runs the default query, while `/differential/?users=x` runs a custom query.
Test Plan: {F56612}
Reviewers: btrahan
Reviewed By: btrahan
CC: aran
Maniphest Tasks: T2625, T3775
Differential Revision: https://secure.phabricator.com/D6840
											
										 
											2013-08-29 11:52:29 -07:00
										 |  |  |         // Otherwise, there's no query data so just run the user's default
 | 
					
						
							|  |  |  |         // query for this application.
 | 
					
						
							|  |  |  |         $query_key = head_key($engine->loadEnabledNamedQueries()); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ($engine->isBuiltinQuery($query_key)) { | 
					
						
							|  |  |  |       $saved_query = $engine->buildSavedQueryFromBuiltin($query_key); | 
					
						
							| 
									
										
										
										
											2013-06-05 18:58:50 -07:00
										 |  |  |       $named_query = idx($engine->loadEnabledNamedQueries(), $query_key); | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |     } else if ($query_key) { | 
					
						
							|  |  |  |       $saved_query = id(new PhabricatorSavedQueryQuery()) | 
					
						
							|  |  |  |         ->setViewer($user) | 
					
						
							|  |  |  |         ->withQueryKeys(array($query_key)) | 
					
						
							|  |  |  |         ->executeOne(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (!$saved_query) { | 
					
						
							|  |  |  |         return new Aphront404Response(); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-05 18:58:50 -07:00
										 |  |  |       $named_query = idx($engine->loadEnabledNamedQueries(), $query_key); | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |     } else { | 
					
						
							|  |  |  |       $saved_query = $engine->buildSavedQueryFromRequest($request); | 
					
						
							| 
									
										
										
										
											2013-12-16 12:30:51 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |       // Save the query to generate a query key, so "Save Custom Query..." and
 | 
					
						
							|  |  |  |       // other features like Maniphest's "Export..." work correctly.
 | 
					
						
							| 
									
										
										
										
											2014-05-20 11:42:05 -07:00
										 |  |  |       $engine->saveQuery($saved_query); | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $nav->selectFilter( | 
					
						
							| 
									
										
										
										
											2013-12-26 12:30:36 -08:00
										 |  |  |       'query/'.$saved_query->getQueryKey(), | 
					
						
							|  |  |  |       'query/advanced'); | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     $form = id(new AphrontFormView()) | 
					
						
							| 
									
										
										
										
											2014-08-01 17:22:24 -07:00
										 |  |  |       ->setUser($user) | 
					
						
							|  |  |  |       ->setAction($request->getPath()); | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     $engine->buildSearchForm($form, $saved_query); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-30 17:32:12 -07:00
										 |  |  |     $errors = $engine->getErrors(); | 
					
						
							|  |  |  |     if ($errors) { | 
					
						
							|  |  |  |       $run_query = false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |     $submit = id(new AphrontFormSubmitControl()) | 
					
						
							|  |  |  |       ->setValue(pht('Execute Query')); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-31 10:51:20 -07:00
										 |  |  |     if ($run_query && !$named_query && $user->isLoggedIn()) { | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |       $submit->addCancelButton( | 
					
						
							|  |  |  |         '/search/edit/'.$saved_query->getQueryKey().'/', | 
					
						
							|  |  |  |         pht('Save Custom Query...')); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-16 12:27:12 -07:00
										 |  |  |     // TODO: A "Create Dashboard Panel" action goes here somewhere once
 | 
					
						
							|  |  |  |     // we sort out T5307.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |     $form->appendChild($submit); | 
					
						
							| 
									
										
											  
											
												Make mobile navigation work properly by default in more cases
Summary:
Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are:
  - Don't load named queries multiple times on search pages.
  - Don't require extra code to get standard navigation right on mobile.
  - Reduce the amount of boilerplate in ListControllers.
  - Reduce the amount of boilerplate around navigation/menus in all controllers.
Specifically, here's what this does:
  - The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment.
    - It uses this to automatically add crumb actions to the application menu.
    - It uses this to automatically reuse one SearchEngine instead of running queries multiple times.
  - The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc).
  - SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff.
  - Building menus is generally simpler.
Test Plan:
  - Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu.
  - Edited saved queries.
  - Tested Differential, Maniphest (no changes).
  - Verified the paste pages don't run any duplicate NamedQuery queries.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T5752
Differential Revision: https://secure.phabricator.com/D14382
											
										 
											2015-11-02 12:06:28 -08:00
										 |  |  |     $body = array(); | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
											  
											
												Move edit/deactivate operations onto project view page in Releeph
Summary:
Ref T3092.
Releeph's objects basically go like this:
  - At the top level, we have Projects (like "www" or "libphutil")
  - Each project has Branches (like "LATEST" or "v1.1.3")
  - Each branch has Requests (like pull requests, e.g. "please merge commit X into branch Y (in project Z)")
Currently, there's no real "project detail" or "branch detail" page. Instead, we have a search results page for their contained objects. That is, the "project detail" page shows a list of branches in the project, using ApplicationSearch.
This means that operations like "edit" and "deactivate" are one level up, on the respective list pages.
Instead, move details onto the detail pages. This gives us more room for actions and information, and simplifies the list views.
Basically, these are "detail pages" where the object content is a search interface. We do something simliar to this in Phame right now, although it's messier there (no ApplicationSearch yet).
@chad, you might have some ideas here. Roughly, the design question is "How should we present an object's detail view when its content is really a search interface (Phame Blog for Posts, Releeph Project for Branches)?"
I think the simple approach I've taken here (see screenshot) gives us reasonable results, but overall it's something we haven't done much or done too much thinking about, I think.
Test Plan: {F54774}
Reviewers: btrahan
Reviewed By: btrahan
CC: chad, aran
Maniphest Tasks: T3092
Differential Revision: https://secure.phabricator.com/D6771
											
										 
											2013-08-19 18:30:30 -07:00
										 |  |  |     if ($this->getPreface()) { | 
					
						
							| 
									
										
											  
											
												Make mobile navigation work properly by default in more cases
Summary:
Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are:
  - Don't load named queries multiple times on search pages.
  - Don't require extra code to get standard navigation right on mobile.
  - Reduce the amount of boilerplate in ListControllers.
  - Reduce the amount of boilerplate around navigation/menus in all controllers.
Specifically, here's what this does:
  - The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment.
    - It uses this to automatically add crumb actions to the application menu.
    - It uses this to automatically reuse one SearchEngine instead of running queries multiple times.
  - The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc).
  - SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff.
  - Building menus is generally simpler.
Test Plan:
  - Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu.
  - Edited saved queries.
  - Tested Differential, Maniphest (no changes).
  - Verified the paste pages don't run any duplicate NamedQuery queries.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T5752
Differential Revision: https://secure.phabricator.com/D14382
											
										 
											2015-11-02 12:06:28 -08:00
										 |  |  |       $body[] = $this->getPreface(); | 
					
						
							| 
									
										
											  
											
												Move edit/deactivate operations onto project view page in Releeph
Summary:
Ref T3092.
Releeph's objects basically go like this:
  - At the top level, we have Projects (like "www" or "libphutil")
  - Each project has Branches (like "LATEST" or "v1.1.3")
  - Each branch has Requests (like pull requests, e.g. "please merge commit X into branch Y (in project Z)")
Currently, there's no real "project detail" or "branch detail" page. Instead, we have a search results page for their contained objects. That is, the "project detail" page shows a list of branches in the project, using ApplicationSearch.
This means that operations like "edit" and "deactivate" are one level up, on the respective list pages.
Instead, move details onto the detail pages. This gives us more room for actions and information, and simplifies the list views.
Basically, these are "detail pages" where the object content is a search interface. We do something simliar to this in Phame right now, although it's messier there (no ApplicationSearch yet).
@chad, you might have some ideas here. Roughly, the design question is "How should we present an object's detail view when its content is really a search interface (Phame Blog for Posts, Releeph Project for Branches)?"
I think the simple approach I've taken here (see screenshot) gives us reasonable results, but overall it's something we haven't done much or done too much thinking about, I think.
Test Plan: {F54774}
Reviewers: btrahan
Reviewed By: btrahan
CC: chad, aran
Maniphest Tasks: T3092
Differential Revision: https://secure.phabricator.com/D6771
											
										 
											2013-08-19 18:30:30 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
											
												[Redesign] Put all ApplicationSearch results in an ObjectBox
Summary:
Ref T8099. In most cases we return either an ObjectList or AphrontTable, and can pretty up the UI in ApplicationSearch. There are a few edge cases, like  PeopleUserLog, that can be cleanup up individually in the future, but look fine for now.
Also added 'setNotice' for AphrontTable for a few cases where we want to convey addtional information.
TODO: Seems we always pass a Pager Object, which tries to get displayed, I'll redesign that interaction in the future, probably by passing the Pager to the ObjectBox
Test Plan: Went throught most/all ApplicationSearch panels I could find, even edge cases look better.
Reviewers: btrahan, epriestley
Reviewed By: epriestley
Subscribers: Korvin, epriestley
Maniphest Tasks: T8099
Differential Revision: https://secure.phabricator.com/D12989
											
										 
											2015-05-24 09:13:58 -07:00
										 |  |  |     if ($named_query) { | 
					
						
							|  |  |  |       $title = $named_query->getQueryName(); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       $title = pht('Advanced Search'); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-07-07 11:52:06 -07:00
										 |  |  |     $header = id(new PHUIHeaderView()) | 
					
						
							| 
									
										
										
										
											2016-08-01 12:06:35 -07:00
										 |  |  |       ->setHeader($title) | 
					
						
							|  |  |  |       ->setProfileHeader(true); | 
					
						
							| 
									
										
										
										
											2015-07-07 11:52:06 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     $box = id(new PHUIObjectBoxView()) | 
					
						
							| 
									
										
										
										
											2016-08-01 12:06:35 -07:00
										 |  |  |       ->setHeader($header) | 
					
						
							|  |  |  |       ->addClass('application-search-results'); | 
					
						
							| 
									
										
										
										
											2015-05-27 11:41:50 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if ($run_query || $named_query) { | 
					
						
							|  |  |  |       $box->setShowHide( | 
					
						
							|  |  |  |         pht('Edit Query'), | 
					
						
							|  |  |  |         pht('Hide Query'), | 
					
						
							|  |  |  |         $form, | 
					
						
							| 
									
										
										
										
											2015-06-16 13:10:24 -07:00
										 |  |  |         $this->getApplicationURI('query/advanced/?query='.$query_key), | 
					
						
							|  |  |  |         (!$named_query ? true : false)); | 
					
						
							| 
									
										
										
										
											2015-05-27 11:41:50 -07:00
										 |  |  |     } else { | 
					
						
							|  |  |  |       $box->setForm($form); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												Make mobile navigation work properly by default in more cases
Summary:
Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are:
  - Don't load named queries multiple times on search pages.
  - Don't require extra code to get standard navigation right on mobile.
  - Reduce the amount of boilerplate in ListControllers.
  - Reduce the amount of boilerplate around navigation/menus in all controllers.
Specifically, here's what this does:
  - The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment.
    - It uses this to automatically add crumb actions to the application menu.
    - It uses this to automatically reuse one SearchEngine instead of running queries multiple times.
  - The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc).
  - SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff.
  - Building menus is generally simpler.
Test Plan:
  - Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu.
  - Edited saved queries.
  - Tested Differential, Maniphest (no changes).
  - Verified the paste pages don't run any duplicate NamedQuery queries.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T5752
Differential Revision: https://secure.phabricator.com/D14382
											
										 
											2015-11-02 12:06:28 -08:00
										 |  |  |     $body[] = $box; | 
					
						
							| 
									
										
										
										
											2016-07-27 09:07:29 -07:00
										 |  |  |     $more_crumbs = null; | 
					
						
							| 
									
										
										
										
											2015-12-19 12:43:05 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |     if ($run_query) { | 
					
						
							| 
									
										
										
										
											2016-05-16 08:09:11 -07:00
										 |  |  |       $exec_errors = array(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-05-27 11:41:50 -07:00
										 |  |  |       $box->setAnchor( | 
					
						
							|  |  |  |         id(new PhabricatorAnchorView()) | 
					
						
							|  |  |  |           ->setAnchorName('R')); | 
					
						
							| 
									
										
										
										
											2013-09-17 11:30:39 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-16 15:30:41 -07:00
										 |  |  |       try { | 
					
						
							| 
									
										
										
										
											2015-12-19 12:43:05 -08:00
										 |  |  |         $engine->setRequest($request); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-16 15:30:41 -07:00
										 |  |  |         $query = $engine->buildQueryFromSavedQuery($saved_query); | 
					
						
							| 
									
										
										
										
											2013-11-22 12:34:52 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-16 15:30:41 -07:00
										 |  |  |         $pager = $engine->newPagerForSavedQuery($saved_query); | 
					
						
							|  |  |  |         $pager->readFromRequest($request); | 
					
						
							| 
									
										
										
										
											2014-02-03 12:52:19 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-16 15:30:41 -07:00
										 |  |  |         $objects = $engine->executeQuery($query, $pager); | 
					
						
							| 
									
										
										
										
											2014-02-03 12:52:19 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-19 12:43:05 -08:00
										 |  |  |         $force_nux = $request->getBool('nux'); | 
					
						
							|  |  |  |         if (!$objects || $force_nux) { | 
					
						
							|  |  |  |           $nux_view = $this->renderNewUserView($engine, $force_nux); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           $nux_view = null; | 
					
						
							| 
									
										
										
										
											2015-04-16 15:30:41 -07:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:37 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-19 12:43:05 -08:00
										 |  |  |         if ($nux_view) { | 
					
						
							|  |  |  |           $box->appendChild($nux_view); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           $list = $engine->renderResults($objects, $saved_query); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           if (!($list instanceof PhabricatorApplicationSearchResultView)) { | 
					
						
							|  |  |  |             throw new Exception( | 
					
						
							|  |  |  |               pht( | 
					
						
							|  |  |  |                 'SearchEngines must render a "%s" object, but this engine '. | 
					
						
							|  |  |  |                 '(of class "%s") rendered something else.', | 
					
						
							|  |  |  |                 'PhabricatorApplicationSearchResultView', | 
					
						
							|  |  |  |                 get_class($engine))); | 
					
						
							| 
									
										
										
										
											2015-06-19 11:46:20 +01:00
										 |  |  |           } | 
					
						
							| 
									
										
										
										
											2015-04-16 15:30:41 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-19 12:43:05 -08:00
										 |  |  |           if ($list->getActions()) { | 
					
						
							|  |  |  |             foreach ($list->getActions() as $action) { | 
					
						
							|  |  |  |               $header->addActionLink($action); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2015-06-19 11:46:20 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-19 12:43:05 -08:00
										 |  |  |           if ($list->getObjectList()) { | 
					
						
							|  |  |  |             $box->setObjectList($list->getObjectList()); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           if ($list->getTable()) { | 
					
						
							|  |  |  |             $box->setTable($list->getTable()); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           if ($list->getInfoView()) { | 
					
						
							|  |  |  |             $box->setInfoView($list->getInfoView()); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |           if ($list->getContent()) { | 
					
						
							|  |  |  |             $box->appendChild($list->getContent()); | 
					
						
							|  |  |  |           } | 
					
						
							| 
									
										
										
										
											2015-06-19 11:46:20 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 09:07:29 -07:00
										 |  |  |           $result_header = $list->getHeader(); | 
					
						
							|  |  |  |           if ($result_header) { | 
					
						
							|  |  |  |             $box->setHeader($result_header); | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           $more_crumbs = $list->getCrumbs(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-19 12:43:05 -08:00
										 |  |  |           if ($pager->willShowPagingControls()) { | 
					
						
							|  |  |  |             $pager_box = id(new PHUIBoxView()) | 
					
						
							| 
									
										
										
										
											2016-08-01 12:06:35 -07:00
										 |  |  |               ->setColor(PHUIBoxView::GREY) | 
					
						
							|  |  |  |               ->addClass('application-search-pager') | 
					
						
							| 
									
										
										
										
											2015-12-19 12:43:05 -08:00
										 |  |  |               ->appendChild($pager); | 
					
						
							|  |  |  |             $body[] = $pager_box; | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2015-04-16 15:30:41 -07:00
										 |  |  |       } catch (PhabricatorTypeaheadInvalidTokenException $ex) { | 
					
						
							| 
									
										
										
										
											2016-05-16 08:09:11 -07:00
										 |  |  |         $exec_errors[] = pht( | 
					
						
							| 
									
										
										
										
											2015-04-16 15:30:41 -07:00
										 |  |  |           'This query specifies an invalid parameter. Review the '. | 
					
						
							|  |  |  |           'query parameters and correct errors.'); | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:37 -07:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2016-05-16 08:09:11 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |       // The engine may have encountered additional errors during rendering;
 | 
					
						
							|  |  |  |       // merge them in and show everything.
 | 
					
						
							|  |  |  |       foreach ($engine->getErrors() as $error) { | 
					
						
							|  |  |  |         $exec_errors[] = $error; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       $errors = $exec_errors; | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-04-16 15:30:41 -07:00
										 |  |  |     if ($errors) { | 
					
						
							| 
									
										
										
										
											2015-05-27 11:41:50 -07:00
										 |  |  |       $box->setFormErrors($errors, pht('Query Errors')); | 
					
						
							| 
									
										
										
										
											2013-05-30 17:32:12 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |     $crumbs = $parent | 
					
						
							| 
									
										
										
										
											2016-08-01 12:06:35 -07:00
										 |  |  |       ->buildApplicationCrumbs() | 
					
						
							|  |  |  |       ->setBorder(true); | 
					
						
							| 
									
										
										
										
											2016-07-27 09:07:29 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     if ($more_crumbs) { | 
					
						
							|  |  |  |       $query_uri = $engine->getQueryResultsPageURI($saved_query->getQueryKey()); | 
					
						
							|  |  |  |       $crumbs->addTextCrumb($title, $query_uri); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       foreach ($more_crumbs as $crumb) { | 
					
						
							|  |  |  |         $crumbs->addCrumb($crumb); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       $crumbs->addTextCrumb($title); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 12:06:35 -07:00
										 |  |  |     require_celerity_resource('application-search-view-css'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												Make mobile navigation work properly by default in more cases
Summary:
Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are:
  - Don't load named queries multiple times on search pages.
  - Don't require extra code to get standard navigation right on mobile.
  - Reduce the amount of boilerplate in ListControllers.
  - Reduce the amount of boilerplate around navigation/menus in all controllers.
Specifically, here's what this does:
  - The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment.
    - It uses this to automatically add crumb actions to the application menu.
    - It uses this to automatically reuse one SearchEngine instead of running queries multiple times.
  - The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc).
  - SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff.
  - Building menus is generally simpler.
Test Plan:
  - Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu.
  - Edited saved queries.
  - Tested Differential, Maniphest (no changes).
  - Verified the paste pages don't run any duplicate NamedQuery queries.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T5752
Differential Revision: https://secure.phabricator.com/D14382
											
										 
											2015-11-02 12:06:28 -08:00
										 |  |  |     return $this->newPage() | 
					
						
							|  |  |  |       ->setApplicationMenu($this->buildApplicationMenu()) | 
					
						
							|  |  |  |       ->setTitle(pht('Query: %s', $title)) | 
					
						
							|  |  |  |       ->setCrumbs($crumbs) | 
					
						
							|  |  |  |       ->setNavigation($nav) | 
					
						
							| 
									
										
										
										
											2016-08-01 12:06:35 -07:00
										 |  |  |       ->appendChild($body) | 
					
						
							|  |  |  |       ->addClass('application-search-view'); | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   private function processEditRequest() { | 
					
						
							|  |  |  |     $parent = $this->getDelegatingController(); | 
					
						
							|  |  |  |     $request = $this->getRequest(); | 
					
						
							|  |  |  |     $user = $request->getUser(); | 
					
						
							|  |  |  |     $engine = $this->getSearchEngine(); | 
					
						
							| 
									
										
											  
											
												Make mobile navigation work properly by default in more cases
Summary:
Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are:
  - Don't load named queries multiple times on search pages.
  - Don't require extra code to get standard navigation right on mobile.
  - Reduce the amount of boilerplate in ListControllers.
  - Reduce the amount of boilerplate around navigation/menus in all controllers.
Specifically, here's what this does:
  - The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment.
    - It uses this to automatically add crumb actions to the application menu.
    - It uses this to automatically reuse one SearchEngine instead of running queries multiple times.
  - The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc).
  - SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff.
  - Building menus is generally simpler.
Test Plan:
  - Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu.
  - Edited saved queries.
  - Tested Differential, Maniphest (no changes).
  - Verified the paste pages don't run any duplicate NamedQuery queries.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T5752
Differential Revision: https://secure.phabricator.com/D14382
											
										 
											2015-11-02 12:06:28 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |     $nav = $this->getNavigation(); | 
					
						
							| 
									
										
											  
											
												Make mobile navigation work properly by default in more cases
Summary:
Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are:
  - Don't load named queries multiple times on search pages.
  - Don't require extra code to get standard navigation right on mobile.
  - Reduce the amount of boilerplate in ListControllers.
  - Reduce the amount of boilerplate around navigation/menus in all controllers.
Specifically, here's what this does:
  - The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment.
    - It uses this to automatically add crumb actions to the application menu.
    - It uses this to automatically reuse one SearchEngine instead of running queries multiple times.
  - The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc).
  - SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff.
  - Building menus is generally simpler.
Test Plan:
  - Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu.
  - Edited saved queries.
  - Tested Differential, Maniphest (no changes).
  - Verified the paste pages don't run any duplicate NamedQuery queries.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T5752
Differential Revision: https://secure.phabricator.com/D14382
											
										 
											2015-11-02 12:06:28 -08:00
										 |  |  |     if (!$nav) { | 
					
						
							|  |  |  |       $nav = $this->buildNavigation(); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-05 05:28:25 -07:00
										 |  |  |     $named_queries = $engine->loadAllNamedQueries(); | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-05 16:22:27 -07:00
										 |  |  |     $list_id = celerity_generate_unique_node_id(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-09 14:14:34 -07:00
										 |  |  |     $list = new PHUIObjectItemListView(); | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |     $list->setUser($user); | 
					
						
							| 
									
										
										
										
											2013-06-05 16:22:27 -07:00
										 |  |  |     $list->setID($list_id); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Javelin::initBehavior( | 
					
						
							|  |  |  |       'search-reorder-queries', | 
					
						
							|  |  |  |       array( | 
					
						
							|  |  |  |         'listID' => $list_id, | 
					
						
							|  |  |  |         'orderURI' => '/search/order/'.get_class($engine).'/', | 
					
						
							|  |  |  |       )); | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     foreach ($named_queries as $named_query) { | 
					
						
							| 
									
										
										
										
											2013-06-05 05:28:25 -07:00
										 |  |  |       $class = get_class($engine); | 
					
						
							|  |  |  |       $key = $named_query->getQueryKey(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-09 14:14:34 -07:00
										 |  |  |       $item = id(new PHUIObjectItemView()) | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |         ->setHeader($named_query->getQueryName()) | 
					
						
							| 
									
										
										
										
											2013-06-05 05:28:25 -07:00
										 |  |  |         ->setHref($engine->getQueryResultsPageURI($key)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if ($named_query->getIsBuiltin() && $named_query->getIsDisabled()) { | 
					
						
							| 
									
										
										
										
											2014-05-12 10:08:32 -07:00
										 |  |  |         $icon = 'fa-plus'; | 
					
						
							| 
									
										
										
										
											2013-06-05 05:28:25 -07:00
										 |  |  |       } else { | 
					
						
							| 
									
										
										
										
											2014-05-12 10:08:32 -07:00
										 |  |  |         $icon = 'fa-times'; | 
					
						
							| 
									
										
										
										
											2013-06-05 05:28:25 -07:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       $item->addAction( | 
					
						
							| 
									
										
										
										
											2013-06-05 08:41:43 -07:00
										 |  |  |         id(new PHUIListItemView()) | 
					
						
							| 
									
										
										
										
											2013-06-05 05:28:25 -07:00
										 |  |  |           ->setIcon($icon) | 
					
						
							|  |  |  |           ->setHref('/search/delete/'.$key.'/'.$class.'/') | 
					
						
							|  |  |  |           ->setWorkflow(true)); | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |       if ($named_query->getIsBuiltin()) { | 
					
						
							| 
									
										
										
										
											2013-06-05 05:28:25 -07:00
										 |  |  |         if ($named_query->getIsDisabled()) { | 
					
						
							| 
									
										
										
										
											2014-05-12 10:08:32 -07:00
										 |  |  |           $item->addIcon('fa-times lightgreytext', pht('Disabled')); | 
					
						
							| 
									
										
										
										
											2013-07-12 11:31:20 -07:00
										 |  |  |           $item->setDisabled(true); | 
					
						
							| 
									
										
										
										
											2013-06-05 05:28:25 -07:00
										 |  |  |         } else { | 
					
						
							| 
									
										
										
										
											2014-05-12 10:08:32 -07:00
										 |  |  |           $item->addIcon('fa-lock lightgreytext', pht('Builtin')); | 
					
						
							| 
									
										
										
										
											2013-06-05 05:28:25 -07:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |       } else { | 
					
						
							|  |  |  |         $item->addAction( | 
					
						
							| 
									
										
										
										
											2013-06-05 08:41:43 -07:00
										 |  |  |           id(new PHUIListItemView()) | 
					
						
							| 
									
										
										
										
											2014-05-12 10:08:32 -07:00
										 |  |  |             ->setIcon('fa-pencil') | 
					
						
							| 
									
										
										
										
											2013-06-05 05:28:25 -07:00
										 |  |  |             ->setHref('/search/edit/'.$key.'/')); | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-05 16:22:27 -07:00
										 |  |  |       $item->setGrippable(true); | 
					
						
							|  |  |  |       $item->addSigil('named-query'); | 
					
						
							|  |  |  |       $item->setMetadata( | 
					
						
							|  |  |  |         array( | 
					
						
							|  |  |  |           'queryKey' => $named_query->getQueryKey(), | 
					
						
							|  |  |  |         )); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |       $list->addItem($item); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $list->setNoDataString(pht('No saved queries.')); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $crumbs = $parent | 
					
						
							|  |  |  |       ->buildApplicationCrumbs() | 
					
						
							| 
									
										
										
										
											2016-08-01 12:06:35 -07:00
										 |  |  |       ->addTextCrumb(pht('Saved Queries'), $engine->getQueryManagementURI()) | 
					
						
							|  |  |  |       ->setBorder(true); | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     $nav->selectFilter('query/edit'); | 
					
						
							| 
									
										
										
											
												[Redesign] Put all ApplicationSearch results in an ObjectBox
Summary:
Ref T8099. In most cases we return either an ObjectList or AphrontTable, and can pretty up the UI in ApplicationSearch. There are a few edge cases, like  PeopleUserLog, that can be cleanup up individually in the future, but look fine for now.
Also added 'setNotice' for AphrontTable for a few cases where we want to convey addtional information.
TODO: Seems we always pass a Pager Object, which tries to get displayed, I'll redesign that interaction in the future, probably by passing the Pager to the ObjectBox
Test Plan: Went throught most/all ApplicationSearch panels I could find, even edge cases look better.
Reviewers: btrahan, epriestley
Reviewed By: epriestley
Subscribers: Korvin, epriestley
Maniphest Tasks: T8099
Differential Revision: https://secure.phabricator.com/D12989
											
										 
											2015-05-24 09:13:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 12:06:35 -07:00
										 |  |  |     $header = id(new PHUIHeaderView()) | 
					
						
							|  |  |  |       ->setHeader(pht('Saved Queries')) | 
					
						
							|  |  |  |       ->setProfileHeader(true); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
											
												[Redesign] Put all ApplicationSearch results in an ObjectBox
Summary:
Ref T8099. In most cases we return either an ObjectList or AphrontTable, and can pretty up the UI in ApplicationSearch. There are a few edge cases, like  PeopleUserLog, that can be cleanup up individually in the future, but look fine for now.
Also added 'setNotice' for AphrontTable for a few cases where we want to convey addtional information.
TODO: Seems we always pass a Pager Object, which tries to get displayed, I'll redesign that interaction in the future, probably by passing the Pager to the ObjectBox
Test Plan: Went throught most/all ApplicationSearch panels I could find, even edge cases look better.
Reviewers: btrahan, epriestley
Reviewed By: epriestley
Subscribers: Korvin, epriestley
Maniphest Tasks: T8099
Differential Revision: https://secure.phabricator.com/D12989
											
										 
											2015-05-24 09:13:58 -07:00
										 |  |  |     $box = id(new PHUIObjectBoxView()) | 
					
						
							| 
									
										
										
										
											2016-08-01 12:06:35 -07:00
										 |  |  |       ->setHeader($header) | 
					
						
							|  |  |  |       ->setObjectList($list) | 
					
						
							|  |  |  |       ->addClass('application-search-results'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     require_celerity_resource('application-search-view-css'); | 
					
						
							| 
									
										
										
											
												[Redesign] Put all ApplicationSearch results in an ObjectBox
Summary:
Ref T8099. In most cases we return either an ObjectList or AphrontTable, and can pretty up the UI in ApplicationSearch. There are a few edge cases, like  PeopleUserLog, that can be cleanup up individually in the future, but look fine for now.
Also added 'setNotice' for AphrontTable for a few cases where we want to convey addtional information.
TODO: Seems we always pass a Pager Object, which tries to get displayed, I'll redesign that interaction in the future, probably by passing the Pager to the ObjectBox
Test Plan: Went throught most/all ApplicationSearch panels I could find, even edge cases look better.
Reviewers: btrahan, epriestley
Reviewed By: epriestley
Subscribers: Korvin, epriestley
Maniphest Tasks: T8099
Differential Revision: https://secure.phabricator.com/D12989
											
										 
											2015-05-24 09:13:58 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
											  
											
												Make mobile navigation work properly by default in more cases
Summary:
Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are:
  - Don't load named queries multiple times on search pages.
  - Don't require extra code to get standard navigation right on mobile.
  - Reduce the amount of boilerplate in ListControllers.
  - Reduce the amount of boilerplate around navigation/menus in all controllers.
Specifically, here's what this does:
  - The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment.
    - It uses this to automatically add crumb actions to the application menu.
    - It uses this to automatically reuse one SearchEngine instead of running queries multiple times.
  - The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc).
  - SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff.
  - Building menus is generally simpler.
Test Plan:
  - Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu.
  - Edited saved queries.
  - Tested Differential, Maniphest (no changes).
  - Verified the paste pages don't run any duplicate NamedQuery queries.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T5752
Differential Revision: https://secure.phabricator.com/D14382
											
										 
											2015-11-02 12:06:28 -08:00
										 |  |  |     return $this->newPage() | 
					
						
							|  |  |  |       ->setApplicationMenu($this->buildApplicationMenu()) | 
					
						
							|  |  |  |       ->setTitle(pht('Saved Queries')) | 
					
						
							|  |  |  |       ->setCrumbs($crumbs) | 
					
						
							|  |  |  |       ->setNavigation($nav) | 
					
						
							| 
									
										
										
										
											2016-08-01 12:06:35 -07:00
										 |  |  |       ->addClass('application-search-view') | 
					
						
							| 
									
										
											  
											
												Make mobile navigation work properly by default in more cases
Summary:
Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are:
  - Don't load named queries multiple times on search pages.
  - Don't require extra code to get standard navigation right on mobile.
  - Reduce the amount of boilerplate in ListControllers.
  - Reduce the amount of boilerplate around navigation/menus in all controllers.
Specifically, here's what this does:
  - The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment.
    - It uses this to automatically add crumb actions to the application menu.
    - It uses this to automatically reuse one SearchEngine instead of running queries multiple times.
  - The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc).
  - SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff.
  - Building menus is generally simpler.
Test Plan:
  - Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu.
  - Edited saved queries.
  - Tested Differential, Maniphest (no changes).
  - Verified the paste pages don't run any duplicate NamedQuery queries.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T5752
Differential Revision: https://secure.phabricator.com/D14382
											
										 
											2015-11-02 12:06:28 -08:00
										 |  |  |       ->appendChild($box); | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:37 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-16 07:41:26 +11:00
										 |  |  |   public function buildApplicationMenu() { | 
					
						
							| 
									
										
											  
											
												Make mobile navigation work properly by default in more cases
Summary:
Fixes T5752. This obsoletes a bunch of old patterns and I'll follow up on those with a big "go do a bunch of mechanical code changes" task. Major goals are:
  - Don't load named queries multiple times on search pages.
  - Don't require extra code to get standard navigation right on mobile.
  - Reduce the amount of boilerplate in ListControllers.
  - Reduce the amount of boilerplate around navigation/menus in all controllers.
Specifically, here's what this does:
  - The StandardPage is now a smarter/more structured object with `setNavigation()` and `setCrumbs()` methods. More rendering decisions are delayed until the last possible moment.
    - It uses this to automatically add crumb actions to the application menu.
    - It uses this to automatically reuse one SearchEngine instead of running queries multiple times.
  - The new preferred way to build responses is `$this->newPage()` (like `$this->newDialog()`), which has structured methods for adding stuff (`setTitle()`, etc).
  - SearchEngine exposes a new convenience method so you don't have to do all the controller delegation stuff.
  - Building menus is generally simpler.
Test Plan:
  - Tested paste list, view, edit, comment, raw controllers for functionality, mobile menu, crumbs, navigation menu.
  - Edited saved queries.
  - Tested Differential, Maniphest (no changes).
  - Verified the paste pages don't run any duplicate NamedQuery queries.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T5752
Differential Revision: https://secure.phabricator.com/D14382
											
										 
											2015-11-02 12:06:28 -08:00
										 |  |  |     $menu = $this->getDelegatingController() | 
					
						
							|  |  |  |       ->buildApplicationMenu(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ($menu instanceof PHUIApplicationMenuView) { | 
					
						
							|  |  |  |       $menu->setSearchEngine($this->getSearchEngine()); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return $menu; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   private function buildNavigation() { | 
					
						
							|  |  |  |     $viewer = $this->getViewer(); | 
					
						
							|  |  |  |     $engine = $this->getSearchEngine(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $nav = id(new AphrontSideNavFilterView()) | 
					
						
							|  |  |  |       ->setUser($viewer) | 
					
						
							|  |  |  |       ->setBaseURI(new PhutilURI($this->getApplicationURI())); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $engine->addNavigationItems($nav->getMenu()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return $nav; | 
					
						
							| 
									
										
										
										
											2013-05-31 10:50:49 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-12-19 12:43:05 -08:00
										 |  |  |   private function renderNewUserView( | 
					
						
							|  |  |  |     PhabricatorApplicationSearchEngine $engine, | 
					
						
							|  |  |  |     $force_nux) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Don't render NUX if the user has clicked away from the default page.
 | 
					
						
							|  |  |  |     if (strlen($this->getQueryKey())) { | 
					
						
							|  |  |  |       return null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Don't put NUX in panels because it would be weird.
 | 
					
						
							|  |  |  |     if ($engine->isPanelContext()) { | 
					
						
							|  |  |  |       return null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Try to render the view itself first, since this should be very cheap
 | 
					
						
							|  |  |  |     // (just returning some text).
 | 
					
						
							|  |  |  |     $nux_view = $engine->renderNewUserView(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (!$nux_view) { | 
					
						
							|  |  |  |       return null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $query = $engine->newQuery(); | 
					
						
							|  |  |  |     if (!$query) { | 
					
						
							|  |  |  |       return null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Try to load any object at all. If we can, the application has seen some
 | 
					
						
							|  |  |  |     // use so we just render the normal view.
 | 
					
						
							|  |  |  |     if (!$force_nux) { | 
					
						
							|  |  |  |       $object = $query | 
					
						
							|  |  |  |         ->setViewer(PhabricatorUser::getOmnipotentUser()) | 
					
						
							|  |  |  |         ->setLimit(1) | 
					
						
							|  |  |  |         ->execute(); | 
					
						
							|  |  |  |       if ($object) { | 
					
						
							|  |  |  |         return null; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return $nux_view; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  | } |