| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  | <?php | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | final class PhabricatorApplicationSearchController | 
					
						
							|  |  |  |   extends PhabricatorSearchBaseController { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   private $searchEngine; | 
					
						
							|  |  |  |   private $navigation; | 
					
						
							|  |  |  |   private $queryKey; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   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( | 
					
						
							|  |  |  |         "You must delegate to this controller, not invoke it directly."); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $engine = $this->getSearchEngine(); | 
					
						
							|  |  |  |     if (!$engine) { | 
					
						
							|  |  |  |       throw new Exception( | 
					
						
							|  |  |  |         "Call setEngine() before delegating to this controller!"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $nav = $this->getNavigation(); | 
					
						
							|  |  |  |     if (!$nav) { | 
					
						
							|  |  |  |       throw new Exception( | 
					
						
							|  |  |  |         "Call setNavigation() before delegating to this controller!"); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $engine->setViewer($this->getRequest()->getUser()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $parent = $this->getDelegatingController(); | 
					
						
							|  |  |  |     $interface = 'PhabricatorApplicationSearchResultsControllerInterface'; | 
					
						
							|  |  |  |     if (!$parent instanceof $interface) { | 
					
						
							|  |  |  |       throw new Exception( | 
					
						
							|  |  |  |         "Delegating controller must implement '{$interface}'."); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   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(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ($request->isFormPost()) { | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:37 -07:00
										 |  |  |       $saved_query = $engine->buildSavedQueryFromRequest($request); | 
					
						
							|  |  |  |       $this->saveQuery($saved_query); | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |       return id(new AphrontRedirectResponse())->setURI( | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:37 -07:00
										 |  |  |         $engine->getQueryResultsPageURI($saved_query->getQueryKey())); | 
					
						
							| 
									
										
										
										
											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)) { | 
					
						
							|  |  |  |       $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); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $nav->selectFilter( | 
					
						
							|  |  |  |       'query/'.$saved_query->getQueryKey(), | 
					
						
							|  |  |  |       'query/advanced'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $form = id(new AphrontFormView()) | 
					
						
							|  |  |  |       ->setNoShading(true) | 
					
						
							|  |  |  |       ->setUser($user); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $engine->buildSearchForm($form, $saved_query); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-30 17:32:12 -07:00
										 |  |  |     $errors = $engine->getErrors(); | 
					
						
							|  |  |  |     if ($errors) { | 
					
						
							|  |  |  |       $run_query = false; | 
					
						
							|  |  |  |       $errors = id(new AphrontErrorView()) | 
					
						
							|  |  |  |         ->setTitle(pht('Query Errors')) | 
					
						
							|  |  |  |         ->setErrors($errors); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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...')); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $form->appendChild($submit); | 
					
						
							|  |  |  |     $filter_view = id(new AphrontListFilterView())->appendChild($form); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ($run_query && $named_query) { | 
					
						
							|  |  |  |       if ($named_query->getIsBuiltin()) { | 
					
						
							|  |  |  |         $description = pht( | 
					
						
							|  |  |  |           'Showing results for query "%s".', | 
					
						
							|  |  |  |           $named_query->getQueryName()); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         $description = pht( | 
					
						
							|  |  |  |           'Showing results for saved query "%s".', | 
					
						
							|  |  |  |           $named_query->getQueryName()); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       $filter_view->setCollapsed( | 
					
						
							|  |  |  |         pht('Edit Query...'), | 
					
						
							|  |  |  |         pht('Hide Query'), | 
					
						
							|  |  |  |         $description, | 
					
						
							|  |  |  |         $this->getApplicationURI('query/advanced/?query='.$query_key)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $nav->appendChild($filter_view); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if ($run_query) { | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:37 -07:00
										 |  |  |       $query = $engine->buildQueryFromSavedQuery($saved_query); | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |       $pager = new AphrontCursorPagerView(); | 
					
						
							|  |  |  |       $pager->readFromRequest($request); | 
					
						
							| 
									
										
										
										
											2013-07-01 12:36:34 -07:00
										 |  |  |       $pager->setPageSize($engine->getPageSize($saved_query)); | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:37 -07:00
										 |  |  |       $objects = $query->setViewer($request->getUser()) | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |         ->executeWithCursorPager($pager); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-07-03 05:46:04 -07:00
										 |  |  |       $list = $parent->renderResultsList($objects, $saved_query); | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |       $nav->appendChild($list); | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:37 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |       // TODO: This is a bit hacky.
 | 
					
						
							|  |  |  |       if ($list instanceof PhabricatorObjectItemListView) { | 
					
						
							| 
									
										
										
										
											2013-07-01 12:36:34 -07:00
										 |  |  |         $list->setNoDataString(pht("No results found for this query.")); | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:37 -07:00
										 |  |  |         $list->setPager($pager); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         $nav->appendChild($pager); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-30 17:32:12 -07:00
										 |  |  |     if ($errors) { | 
					
						
							|  |  |  |       $nav->appendChild($errors); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |     if ($named_query) { | 
					
						
							|  |  |  |       $title = pht('Query: %s', $named_query->getQueryName()); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       $title = pht('Advanced Search'); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $crumbs = $parent | 
					
						
							|  |  |  |       ->buildApplicationCrumbs() | 
					
						
							|  |  |  |       ->addCrumb( | 
					
						
							|  |  |  |         id(new PhabricatorCrumbView()) | 
					
						
							|  |  |  |           ->setName(pht("Search"))); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $nav->setCrumbs($crumbs); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return $this->buildApplicationPage( | 
					
						
							|  |  |  |       $nav, | 
					
						
							|  |  |  |       array( | 
					
						
							|  |  |  |         'title' => $title, | 
					
						
							|  |  |  |         'device' => true, | 
					
						
							|  |  |  |       )); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   private function processEditRequest() { | 
					
						
							|  |  |  |     $parent = $this->getDelegatingController(); | 
					
						
							|  |  |  |     $request = $this->getRequest(); | 
					
						
							|  |  |  |     $user = $request->getUser(); | 
					
						
							|  |  |  |     $engine = $this->getSearchEngine(); | 
					
						
							|  |  |  |     $nav = $this->getNavigation(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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-05-30 14:09:02 -07:00
										 |  |  |     $list = new PhabricatorObjectItemListView(); | 
					
						
							|  |  |  |     $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-05-30 14:09:02 -07:00
										 |  |  |       $item = id(new PhabricatorObjectItemView()) | 
					
						
							|  |  |  |         ->setHeader($named_query->getQueryName()) | 
					
						
							| 
									
										
										
										
											2013-06-05 05:28:25 -07:00
										 |  |  |         ->setHref($engine->getQueryResultsPageURI($key)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if ($named_query->getIsBuiltin() && $named_query->getIsDisabled()) { | 
					
						
							|  |  |  |         $icon = 'new'; | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         $icon = 'delete'; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       $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()) { | 
					
						
							|  |  |  |           $item->addIcon('delete-grey', pht('Disabled')); | 
					
						
							| 
									
										
										
										
											2013-07-12 11:31:20 -07:00
										 |  |  |           $item->setDisabled(true); | 
					
						
							| 
									
										
										
										
											2013-06-05 05:28:25 -07:00
										 |  |  |         } else { | 
					
						
							|  |  |  |           $item->addIcon('lock-grey', pht('Builtin')); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |       } else { | 
					
						
							|  |  |  |         $item->addAction( | 
					
						
							| 
									
										
										
										
											2013-06-05 08:41:43 -07:00
										 |  |  |           id(new PHUIListItemView()) | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  |             ->setIcon('edit') | 
					
						
							| 
									
										
										
										
											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() | 
					
						
							|  |  |  |       ->addCrumb( | 
					
						
							|  |  |  |         id(new PhabricatorCrumbView()) | 
					
						
							|  |  |  |           ->setName(pht("Saved Queries")) | 
					
						
							|  |  |  |           ->setHref($engine->getQueryManagementURI())); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $nav->selectFilter('query/edit'); | 
					
						
							|  |  |  |     $nav->setCrumbs($crumbs); | 
					
						
							|  |  |  |     $nav->appendChild($list); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return $parent->buildApplicationPage( | 
					
						
							|  |  |  |       $nav, | 
					
						
							|  |  |  |       array( | 
					
						
							|  |  |  |         'title' => pht("Saved Queries"), | 
					
						
							|  |  |  |         'device' => true, | 
					
						
							|  |  |  |       )); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:37 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   private function saveQuery(PhabricatorSavedQuery $query) { | 
					
						
							|  |  |  |     $query->setEngineClassName(get_class($this->getSearchEngine())); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); | 
					
						
							|  |  |  |     try { | 
					
						
							|  |  |  |       $query->save(); | 
					
						
							|  |  |  |     } catch (AphrontQueryDuplicateKeyException $ex) { | 
					
						
							|  |  |  |       // Ignore, this is just a repeated search.
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     unset($unguarded); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-31 10:50:49 -07:00
										 |  |  |   protected function buildApplicationMenu() { | 
					
						
							|  |  |  |     return $this->getDelegatingController()->buildApplicationMenu(); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-05-30 14:09:02 -07:00
										 |  |  | } |