Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/base/controller/PhabricatorController.php b/src/applications/base/controller/PhabricatorController.php
index e7ae1d0f8d..e015f3a67f 100644
--- a/src/applications/base/controller/PhabricatorController.php
+++ b/src/applications/base/controller/PhabricatorController.php
@@ -1,242 +1,246 @@
<?php
abstract class PhabricatorController extends AphrontController {
private $handles;
public function shouldRequireLogin() {
// If this install is configured to allow public resources and the
// controller works in public mode, allow the request through.
$is_public_allowed = PhabricatorEnv::getEnvConfig('policy.allow-public');
if ($is_public_allowed && $this->shouldAllowPublic()) {
return false;
}
return true;
}
public function shouldRequireAdmin() {
return false;
}
public function shouldRequireEnabledUser() {
return true;
}
public function shouldAllowPublic() {
return false;
}
public function shouldRequireEmailVerification() {
$need_verify = PhabricatorUserEmail::isEmailVerificationRequired();
$need_login = $this->shouldRequireLogin();
return ($need_login && $need_verify);
}
final public function willBeginExecution() {
$request = $this->getRequest();
$user = new PhabricatorUser();
$phusr = $request->getCookie('phusr');
$phsid = $request->getCookie('phsid');
if (strlen($phusr) && $phsid) {
$info = queryfx_one(
$user->establishConnection('r'),
'SELECT u.* FROM %T u JOIN %T s ON u.phid = s.userPHID
AND s.type LIKE %> AND s.sessionKey = %s',
$user->getTableName(),
'phabricator_session',
'web-',
$phsid);
if ($info) {
$user->loadFromArray($info);
}
}
$translation = $user->getTranslation();
if ($translation &&
$translation != PhabricatorEnv::getEnvConfig('translation.provider')) {
$translation = newv($translation, array());
PhutilTranslator::getInstance()
->setLanguage($translation->getLanguage())
->addTranslations($translation->getTranslations());
}
$request->setUser($user);
if ($user->getIsDisabled() && $this->shouldRequireEnabledUser()) {
$disabled_user_controller = new PhabricatorDisabledUserController(
$request);
return $this->delegateToController($disabled_user_controller);
}
$event = new PhabricatorEvent(
PhabricatorEventType::TYPE_CONTROLLER_CHECKREQUEST,
array(
'request' => $request,
'controller' => get_class($this),
));
$event->setUser($user);
PhutilEventEngine::dispatchEvent($event);
$checker_controller = $event->getValue('controller');
if ($checker_controller != get_class($this)) {
return $this->delegateToController($checker_controller);
}
if (PhabricatorEnv::getEnvConfig('darkconsole.enabled')) {
if ($user->getConsoleEnabled() ||
PhabricatorEnv::getEnvConfig('darkconsole.always-on')) {
$console = new DarkConsoleCore();
$request->getApplicationConfiguration()->setConsole($console);
}
}
if ($this->shouldRequireLogin() && !$user->getPHID()) {
$login_controller = new PhabricatorLoginController($request);
return $this->delegateToController($login_controller);
}
if ($this->shouldRequireEmailVerification()) {
$email = $user->loadPrimaryEmail();
if (!$email) {
throw new Exception(
"No primary email address associated with this account!");
}
if (!$email->getIsVerified()) {
$verify_controller = new PhabricatorMustVerifyEmailController($request);
return $this->delegateToController($verify_controller);
}
}
if ($this->shouldRequireAdmin() && !$user->getIsAdmin()) {
return new Aphront403Response();
}
}
public function buildStandardPageView() {
$view = new PhabricatorStandardPageView();
$view->setRequest($this->getRequest());
$view->setController($this);
return $view;
}
public function buildStandardPageResponse($view, array $data) {
$page = $this->buildStandardPageView();
$page->appendChild($view);
$response = new AphrontWebpageResponse();
$response->setContent($page->render());
return $response;
}
public function getApplicationURI($path = '') {
if (!$this->getCurrentApplication()) {
throw new Exception("No application!");
}
return $this->getCurrentApplication()->getBaseURI().ltrim($path, '/');
}
public function buildApplicationPage($view, array $options) {
$page = $this->buildStandardPageView();
$application = $this->getCurrentApplication();
if ($application) {
$page->setApplicationName($application->getName());
$page->setTitle(idx($options, 'title'));
if ($application->getTitleGlyph()) {
$page->setGlyph($application->getTitleGlyph());
}
}
if (!($view instanceof AphrontSideNavFilterView)) {
$nav = new AphrontSideNavFilterView();
$nav->appendChild($view);
$view = $nav;
}
if ($application) {
$view->setCurrentApplication($application);
}
$view->setUser($this->getRequest()->getUser());
$view->setFlexNav(true);
$view->setShowApplicationMenu(true);
$page->appendChild($view);
if (idx($options, 'device')) {
$page->setDeviceReady(true);
$view->appendChild($page->renderFooter());
}
$response = new AphrontWebpageResponse();
return $response->setContent($page->render());
}
public function didProcessRequest($response) {
$request = $this->getRequest();
$response->setRequest($request);
if ($response instanceof AphrontDialogResponse) {
if (!$request->isAjax()) {
$view = new PhabricatorStandardPageView();
$view->setRequest($request);
$view->setController($this);
$view->appendChild(
'<div style="padding: 2em 0;">'.
$response->buildResponseString().
'</div>');
$response = new AphrontWebpageResponse();
$response->setContent($view->render());
return $response;
} else {
return id(new AphrontAjaxResponse())
->setContent(array(
'dialog' => $response->buildResponseString(),
));
}
} else if ($response instanceof AphrontRedirectResponse) {
if ($request->isAjax()) {
return id(new AphrontAjaxResponse())
->setContent(
array(
'redirect' => $response->getURI(),
));
}
}
return $response;
}
protected function getHandle($phid) {
if (empty($this->handles[$phid])) {
throw new Exception(
"Attempting to access handle which wasn't loaded: {$phid}");
}
return $this->handles[$phid];
}
protected function loadHandles(array $phids) {
$phids = array_filter($phids);
$this->handles = $this->loadViewerHandles($phids);
return $this;
}
+ protected function getLoadedHandles() {
+ return $this->handles;
+ }
+
protected function loadViewerHandles(array $phids) {
return id(new PhabricatorObjectHandleData($phids))
->setViewer($this->getRequest()->getUser())
->loadHandles();
}
protected function renderHandlesForPHIDs(array $phids) {
$items = array();
foreach ($phids as $phid) {
$items[] = $this->getHandle($phid)->renderLink();
}
return implode('<br />', $items);
}
}
diff --git a/src/applications/ponder/controller/PonderQuestionViewController.php b/src/applications/ponder/controller/PonderQuestionViewController.php
index 47a0d08599..4a34a328f0 100644
--- a/src/applications/ponder/controller/PonderQuestionViewController.php
+++ b/src/applications/ponder/controller/PonderQuestionViewController.php
@@ -1,132 +1,132 @@
<?php
final class PonderQuestionViewController extends PonderController {
private $questionID;
public function willProcessRequest(array $data) {
$this->questionID = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$question = PonderQuestionQuery::loadSingle($user, $this->questionID);
if (!$question) {
return new Aphront404Response();
}
$question->attachRelated();
$question->attachVotes($user->getPHID());
$object_phids = array($user->getPHID(), $question->getAuthorPHID());
$answers = $question->getAnswers();
$comments = $question->getComments();
foreach ($comments as $comment) {
$object_phids[] = $comment->getAuthorPHID();
}
foreach ($answers as $answer) {
$object_phids[] = $answer->getAuthorPHID();
$comments = $answer->getComments();
foreach ($comments as $comment) {
$object_phids[] = $comment->getAuthorPHID();
}
}
$subscribers = PhabricatorSubscribersQuery::loadSubscribersForPHID(
$question->getPHID());
$object_phids = array_merge($object_phids, $subscribers);
- $handles = $this->loadViewerHandles($object_phids);
$this->loadHandles($object_phids);
+ $handles = $this->getLoadedHandles();
$detail_panel = new PonderQuestionDetailView();
$detail_panel
->setQuestion($question)
->setUser($user)
->setHandles($handles);
$responses_panel = new PonderAnswerListView();
$responses_panel
->setQuestion($question)
->setHandles($handles)
->setUser($user)
->setAnswers($answers);
$answer_add_panel = new PonderAddAnswerView();
$answer_add_panel
->setQuestion($question)
->setUser($user)
->setActionURI("/ponder/answer/add/");
$header = id(new PhabricatorHeaderView())
->setObjectName('Q'.$question->getID())
->setHeader($question->getTitle());
$actions = $this->buildActionListView($question);
$properties = $this->buildPropertyListView($question, $subscribers);
$nav = $this->buildSideNavView($question);
$nav->appendChild(
array(
$header,
$actions,
$properties,
$detail_panel,
$responses_panel,
$answer_add_panel
));
$nav->selectFilter(null);
return $this->buildApplicationPage(
$nav,
array(
'device' => true,
'title' => 'Q'.$question->getID().' '.$question->getTitle()
));
}
private function buildActionListView(PonderQuestion $question) {
$viewer = $this->getRequest()->getUser();
$view = new PhabricatorActionListView();
$view->setUser($viewer);
$view->setObject($question);
return $view;
}
private function buildPropertyListView(
PonderQuestion $question,
array $subscribers) {
$viewer = $this->getRequest()->getUser();
$view = new PhabricatorPropertyListView();
$view->addProperty(
pht('Author'),
$this->getHandle($question->getAuthorPHID())->renderLink());
$view->addProperty(
pht('Created'),
phabricator_datetime($question->getDateCreated(), $viewer));
if ($subscribers) {
foreach ($subscribers as $key => $subscriber) {
$subscribers[$key] = $this->getHandle($subscriber)->renderLink();
}
$subscribers = implode(', ', $subscribers);
}
$view->addProperty(
pht('Subscribers'),
nonempty($subscribers, '<em>'.pht('None').'</em>'));
return $view;
}
}
diff --git a/src/applications/ponder/search/PhabricatorSearchPonderIndexer.php b/src/applications/ponder/search/PhabricatorSearchPonderIndexer.php
index d2dcdefbc0..755b4f4b35 100644
--- a/src/applications/ponder/search/PhabricatorSearchPonderIndexer.php
+++ b/src/applications/ponder/search/PhabricatorSearchPonderIndexer.php
@@ -1,53 +1,66 @@
<?php
final class PhabricatorSearchPonderIndexer
extends PhabricatorSearchDocumentIndexer {
public static function indexQuestion(PonderQuestion $question) {
// note: we assume someone's already called attachrelated on $question
$doc = new PhabricatorSearchAbstractDocument();
$doc->setPHID($question->getPHID());
$doc->setDocumentType(PhabricatorPHIDConstants::PHID_TYPE_QUES);
$doc->setDocumentTitle($question->getTitle());
$doc->setDocumentCreated($question->getDateCreated());
$doc->setDocumentModified($question->getDateModified());
$doc->addField(
PhabricatorSearchField::FIELD_BODY,
$question->getContent());
$doc->addRelationship(
PhabricatorSearchRelationship::RELATIONSHIP_AUTHOR,
$question->getAuthorPHID(),
PhabricatorPHIDConstants::PHID_TYPE_USER,
$question->getDateCreated());
$comments = $question->getComments();
foreach ($comments as $curcomment) {
$doc->addField(
PhabricatorSearchField::FIELD_COMMENT,
$curcomment->getContent()
);
}
$answers = $question->getAnswers();
foreach ($answers as $curanswer) {
if (strlen($curanswer->getContent())) {
$doc->addField(
PhabricatorSearchField::FIELD_COMMENT,
$curanswer->getContent());
}
$answer_comments = $curanswer->getComments();
foreach ($answer_comments as $curcomment) {
$doc->addField(
PhabricatorSearchField::FIELD_COMMENT,
$curcomment->getContent()
);
}
}
+ $subscribers = PhabricatorSubscribersQuery::loadSubscribersForPHID(
+ $question->getPHID());
+ $handles = id(new PhabricatorObjectHandleData($subscribers))
+ ->loadHandles();
+
+ foreach ($handles as $phid => $handle) {
+ $doc->addRelationship(
+ PhabricatorSearchRelationship::RELATIONSHIP_SUBSCRIBER,
+ $phid,
+ $handle->getType(),
+ $question->getDateModified()); // Bogus timestamp.
+ }
+
self::reindexAbstractDocument($doc);
}
}
diff --git a/src/applications/search/controller/PhabricatorSearchController.php b/src/applications/search/controller/PhabricatorSearchController.php
index e0e61cafaa..4ffe4798aa 100644
--- a/src/applications/search/controller/PhabricatorSearchController.php
+++ b/src/applications/search/controller/PhabricatorSearchController.php
@@ -1,255 +1,273 @@
<?php
/**
* @group search
*/
final class PhabricatorSearchController
extends PhabricatorSearchBaseController {
private $key;
public function willProcessRequest(array $data) {
$this->key = idx($data, 'key');
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
if ($this->key) {
$query = id(new PhabricatorSearchQuery())->loadOneWhere(
'queryKey = %s',
$this->key);
if (!$query) {
return new Aphront404Response();
}
} else {
$query = new PhabricatorSearchQuery();
if ($request->isFormPost()) {
$query_str = $request->getStr('query');
$pref_jump = PhabricatorUserPreferences::PREFERENCE_SEARCHBAR_JUMP;
if ($request->getStr('jump') != 'no' &&
$user && $user->loadPreferences()->getPreference($pref_jump, 1)) {
$response = PhabricatorJumpNavHandler::jumpPostResponse($query_str);
} else {
$response = null;
}
if ($response) {
return $response;
} else {
$query->setQuery($query_str);
if ($request->getStr('scope')) {
switch ($request->getStr('scope')) {
case PhabricatorSearchScope::SCOPE_OPEN_REVISIONS:
$query->setParameter('open', 1);
$query->setParameter(
'type',
PhabricatorPHIDConstants::PHID_TYPE_DREV);
break;
case PhabricatorSearchScope::SCOPE_OPEN_TASKS:
$query->setParameter('open', 1);
$query->setParameter(
'type',
PhabricatorPHIDConstants::PHID_TYPE_TASK);
break;
case PhabricatorSearchScope::SCOPE_WIKI:
$query->setParameter(
'type',
PhabricatorPHIDConstants::PHID_TYPE_WIKI);
break;
case PhabricatorSearchScope::SCOPE_COMMITS:
$query->setParameter(
'type',
PhabricatorPHIDConstants::PHID_TYPE_CMIT);
break;
default:
break;
}
} else {
if (strlen($request->getStr('type'))) {
$query->setParameter('type', $request->getStr('type'));
}
if ($request->getArr('author')) {
$query->setParameter('author', $request->getArr('author'));
}
if ($request->getArr('owner')) {
$query->setParameter('owner', $request->getArr('owner'));
}
+ if ($request->getArr('subscribers')) {
+ $query->setParameter('subscribers',
+ $request->getArr('subscribers'));
+ }
+
if ($request->getInt('open')) {
$query->setParameter('open', $request->getInt('open'));
}
if ($request->getArr('project')) {
$query->setParameter('project', $request->getArr('project'));
}
}
$query->save();
return id(new AphrontRedirectResponse())
->setURI('/search/'.$query->getQueryKey().'/');
}
}
}
$options = array(
'' => 'All Documents',
) + PhabricatorSearchAbstractDocument::getSupportedTypes();
$status_options = array(
0 => 'Open and Closed Documents',
1 => 'Open Documents',
);
$phids = array_merge(
$query->getParameter('author', array()),
$query->getParameter('owner', array()),
+ $query->getParameter('subscribers', array()),
$query->getParameter('project', array())
);
$handles = $this->loadViewerHandles($phids);
$author_value = array_select_keys(
$handles,
$query->getParameter('author', array()));
$author_value = mpull($author_value, 'getFullName', 'getPHID');
$owner_value = array_select_keys(
$handles,
$query->getParameter('owner', array()));
$owner_value = mpull($owner_value, 'getFullName', 'getPHID');
+ $subscribers_value = array_select_keys(
+ $handles,
+ $query->getParameter('subscribers', array()));
+ $subscribers_value = mpull($subscribers_value, 'getFullName', 'getPHID');
+
$project_value = array_select_keys(
$handles,
$query->getParameter('project', array()));
$project_value = mpull($project_value, 'getFullName', 'getPHID');
$search_form = new AphrontFormView();
$search_form
->setUser($user)
->setAction('/search/')
->appendChild(
phutil_render_tag(
'input',
array(
'type' => 'hidden',
'name' => 'jump',
'value' => 'no',
)))
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Search')
->setName('query')
->setValue($query->getQuery()))
->appendChild(
id(new AphrontFormSelectControl())
->setLabel('Document Type')
->setName('type')
->setOptions($options)
->setValue($query->getParameter('type')))
->appendChild(
id(new AphrontFormSelectControl())
->setLabel('Document Status')
->setName('open')
->setOptions($status_options)
->setValue($query->getParameter('open')))
->appendChild(
id(new AphrontFormTokenizerControl())
->setName('author')
->setLabel('Author')
->setDatasource('/typeahead/common/users/')
->setValue($author_value))
->appendChild(
id(new AphrontFormTokenizerControl())
->setName('owner')
->setLabel('Owner')
->setDatasource('/typeahead/common/searchowner/')
->setValue($owner_value)
->setCaption(
'Tip: search for "Up For Grabs" to find unowned documents.'))
+ ->appendChild(
+ id(new AphrontFormTokenizerControl())
+ ->setName('subscribers')
+ ->setLabel('Subscribers')
+ ->setDatasource('/typeahead/common/users/')
+ ->setValue($subscribers_value))
->appendChild(
id(new AphrontFormTokenizerControl())
->setName('project')
->setLabel('Project')
->setDatasource('/typeahead/common/projects/')
->setValue($project_value))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Search'));
$search_panel = new AphrontPanelView();
$search_panel->setHeader('Search Phabricator');
$search_panel->appendChild($search_form);
require_celerity_resource('phabricator-search-results-css');
if ($query->getID()) {
$limit = 20;
$pager = new AphrontPagerView();
$pager->setURI($request->getRequestURI(), 'page');
$pager->setPageSize($limit);
$pager->setOffset($request->getInt('page'));
$query->setParameter('limit', $limit + 1);
$query->setParameter('offset', $pager->getOffset());
$engine = PhabricatorSearchEngineSelector::newSelector()->newEngine();
$results = $engine->executeSearch($query);
$results = $pager->sliceResults($results);
if (!$request->getInt('page')) {
$jump = PhabricatorPHID::fromObjectName($query->getQuery());
if ($jump) {
array_unshift($results, $jump);
}
}
if ($results) {
- $loader = new PhabricatorObjectHandleData($results);
+ $loader = id(new PhabricatorObjectHandleData($results))
+ ->setViewer($user);
$handles = $loader->loadHandles();
$objects = $loader->loadObjects();
$results = array();
foreach ($handles as $phid => $handle) {
- $view = new PhabricatorSearchResultView();
- $view->setHandle($handle);
- $view->setQuery($query);
- $view->setObject(idx($objects, $phid));
+ $view = id(new PhabricatorSearchResultView())
+ ->setHandle($handle)
+ ->setQuery($query)
+ ->setObject(idx($objects, $phid));
$results[] = $view->render();
}
$results =
'<div class="phabricator-search-result-list">'.
implode("\n", $results).
'<div class="search-results-pager">'.
$pager->render().
'</div>'.
'</div>';
} else {
$results =
'<div class="phabricator-search-result-list">'.
'<p class="phabricator-search-no-results">No search results.</p>'.
'</div>';
}
} else {
$results = null;
}
return $this->buildStandardPageResponse(
array(
$search_panel,
$results,
),
array(
'title' => 'Search Results',
));
}
}
diff --git a/src/applications/search/controller/PhabricatorSearchIndexController.php b/src/applications/search/controller/PhabricatorSearchIndexController.php
deleted file mode 100644
index c73928cb65..0000000000
--- a/src/applications/search/controller/PhabricatorSearchIndexController.php
+++ /dev/null
@@ -1,129 +0,0 @@
-<?php
-
-/**
- * @group search
- */
-final class PhabricatorSearchIndexController
- extends PhabricatorSearchBaseController {
-
- private $phid;
-
- public function shouldRequireAdmin() {
- // This basically shows you all the text of any object in the system, so
- // make it admin-only.
- return true;
- }
-
- public function willProcessRequest(array $data) {
- $this->phid = $data['phid'];
- }
-
- public function processRequest() {
-
- $engine = PhabricatorSearchEngineSelector::newSelector()->newEngine();
- $document = $engine->reconstructDocument($this->phid);
- if (!$document) {
- return new Aphront404Response();
- }
-
- $panels = array();
-
- $panel = new AphrontPanelView();
- $panel->setHeader('Abstract Document Index');
-
- $props = array(
- 'PHID' => phutil_escape_html($document->getPHID()),
- 'Title' => phutil_escape_html($document->getDocumentTitle()),
- 'Type' => phutil_escape_html($document->getDocumentType()),
- );
- $rows = array();
- foreach ($props as $name => $value) {
- $rows[] = array($name, $value);
- }
- $table = new AphrontTableView($rows);
- $table->setColumnClasses(
- array(
- 'header',
- '',
- ));
- $panel->appendChild($table);
- $panels[] = $panel;
-
-
- $panel = new AphrontPanelView();
- $panel->setHeader('Document Fields');
-
- $fields = $document->getFieldData();
- $rows = array();
- foreach ($fields as $field) {
- list($name, $corpus, $aux_phid) = $field;
- $rows[] = array(
- phutil_escape_html($name),
- phutil_escape_html(nonempty($aux_phid, null)),
- str_replace("\n", '<br />', phutil_escape_html($corpus)),
- );
- }
-
- $table = new AphrontTableView($rows);
- $table->setHeaders(
- array(
- 'Field',
- 'Aux PHID',
- 'Corpus',
- ));
- $table->setColumnClasses(
- array(
- '',
- '',
- 'wide',
- ));
- $panel->appendChild($table);
- $panels[] = $panel;
-
-
- $panel = new AphrontPanelView();
- $panel->setHeader('Document Relationships');
-
- $relationships = $document->getRelationshipData();
-
- $phids = ipull($relationships, 1);
- $handles = $this->loadViewerHandles($phids);
-
- $rows = array();
- foreach ($relationships as $relationship) {
- list($type, $phid, $rtype, $time) = $relationship;
- $rows[] = array(
- phutil_escape_html($type),
- phutil_escape_html($phid),
- phutil_escape_html($rtype),
- $handles[$phid]->renderLink(),
- );
- }
-
- $table = new AphrontTableView($rows);
- $table->setHeaders(
- array(
- 'Relationship',
- 'Related PHID',
- 'Related Type',
- 'Related Handle',
- ));
- $table->setColumnClasses(
- array(
- '',
- '',
- '',
- 'wide',
- ));
- $panel->appendChild($table);
- $panels[] = $panel;
-
-
- return $this->buildStandardPageResponse(
- $panels,
- array(
- 'title' => 'Raw Index',
- ));
- }
-
-}
diff --git a/src/applications/search/engine/PhabricatorSearchEngineElastic.php b/src/applications/search/engine/PhabricatorSearchEngineElastic.php
index ae3ae91515..aa5273312d 100644
--- a/src/applications/search/engine/PhabricatorSearchEngineElastic.php
+++ b/src/applications/search/engine/PhabricatorSearchEngineElastic.php
@@ -1,236 +1,237 @@
<?php
/**
* @group search
*/
final class PhabricatorSearchEngineElastic extends PhabricatorSearchEngine {
private $uri;
private $timeout;
public function __construct($uri) {
$this->uri = $uri;
}
public function setTimeout($timeout) {
$this->timeout = $timeout;
return $this;
}
public function getTimeout() {
return $this->timeout;
}
public function reindexAbstractDocument(
PhabricatorSearchAbstractDocument $doc) {
$type = $doc->getDocumentType();
$phid = $doc->getPHID();
$handle = PhabricatorObjectHandleData::loadOneHandle($phid);
// URL is not used internally but it can be useful externally.
$spec = array(
'title' => $doc->getDocumentTitle(),
'url' => PhabricatorEnv::getProductionURI($handle->getURI()),
'dateCreated' => $doc->getDocumentCreated(),
'_timestamp' => $doc->getDocumentModified(),
'field' => array(),
'relationship' => array(),
);
foreach ($doc->getFieldData() as $field) {
$spec['field'][] = array_combine(array('type', 'corpus', 'aux'), $field);
}
foreach ($doc->getRelationshipData() as $relationship) {
list($rtype, $to_phid, $to_type, $time) = $relationship;
$spec['relationship'][$rtype][] = array(
'phid' => $to_phid,
'phidType' => $to_type,
'when' => $time,
);
}
$this->executeRequest(
"/phabricator/{$type}/{$phid}/",
$spec,
$is_write = true);
}
public function reconstructDocument($phid) {
$type = phid_get_type($phid);
$response = $this->executeRequest("/phabricator/{$type}/{$phid}", array());
if (empty($response['exists'])) {
return null;
}
$hit = $response['_source'];
$doc = new PhabricatorSearchAbstractDocument();
$doc->setPHID($phid);
$doc->setDocumentType($response['_type']);
$doc->setDocumentTitle($hit['title']);
$doc->setDocumentCreated($hit['dateCreated']);
$doc->setDocumentModified($hit['_timestamp']);
foreach ($hit['field'] as $fdef) {
$doc->addField($fdef['type'], $fdef['corpus'], $fdef['aux']);
}
foreach ($hit['relationship'] as $rtype => $rships) {
foreach ($rships as $rship) {
$doc->addRelationship(
$rtype,
$rship['phid'],
$rship['phidType'],
$rship['when']);
}
}
return $doc;
}
private function buildSpec(PhabricatorSearchQuery $query) {
$spec = array();
$filter = array();
if ($query->getQuery()) {
$spec[] = array(
'field' => array(
'field.corpus' => $query->getQuery(),
),
);
}
$exclude = $query->getParameter('exclude');
if ($exclude) {
$filter[] = array(
'not' => array(
'ids' => array(
'values' => array($exclude),
),
),
);
}
$rel_mapping = array(
'author' => PhabricatorSearchRelationship::RELATIONSHIP_AUTHOR,
'open' => PhabricatorSearchRelationship::RELATIONSHIP_OPEN,
'owner' => PhabricatorSearchRelationship::RELATIONSHIP_OWNER,
+ 'subscribers' => PhabricatorSearchRelationship::RELATIONSHIP_SUBSCRIBER,
'project' => PhabricatorSearchRelationship::RELATIONSHIP_PROJECT,
'repository' => PhabricatorSearchRelationship::RELATIONSHIP_REPOSITORY,
);
foreach ($rel_mapping as $name => $field) {
$param = $query->getParameter($name);
if (is_array($param)) {
$should = array();
foreach ($param as $val) {
$should[] = array(
'text' => array(
"relationship.{$field}.phid" => array(
'query' => $val,
'type' => 'phrase',
),
),
);
}
// We couldn't solve it by minimum_number_should_match because it can
// match multiple owners without matching author.
$spec[] = array('bool' => array('should' => $should));
} else if ($param) {
$filter[] = array(
'exists' => array(
'field' => "relationship.{$field}.phid",
),
);
}
}
if ($spec) {
$spec = array('query' => array('bool' => array('must' => $spec)));
}
if ($filter) {
$filter = array('filter' => array('and' => $filter));
if ($spec) {
$spec = array(
'query' => array(
'filtered' => $spec + $filter,
),
);
} else {
$spec = $filter;
}
}
if (!$query->getQuery()) {
$spec['sort'] = array(
array('dateCreated' => 'desc'),
);
}
$spec['from'] = (int)$query->getParameter('offset', 0);
$spec['size'] = (int)$query->getParameter('limit', 25);
return $spec;
}
public function executeSearch(PhabricatorSearchQuery $query) {
$type = $query->getParameter('type');
if ($type) {
$uri = "/phabricator/{$type}/_search";
} else {
// Don't use '/phabricator/_search' for the case that there is something
// else in the index (for example if 'phabricator' is only an alias to
// some bigger index).
$types = PhabricatorSearchAbstractDocument::getSupportedTypes();
$uri = '/phabricator/' . implode(',', array_keys($types)) . '/_search';
}
try {
$response = $this->executeRequest($uri, $this->buildSpec($query));
} catch (HTTPFutureResponseStatusHTTP $ex) {
// elasticsearch probably uses Lucene query syntax:
// http://lucene.apache.org/core/3_6_1/queryparsersyntax.html
// Try literal search if operator search fails.
if (!$query->getQuery()) {
throw $ex;
}
$query = clone $query;
$query->setQuery(addcslashes($query->getQuery(), '+-&|!(){}[]^"~*?:\\'));
$response = $this->executeRequest($uri, $this->buildSpec($query));
}
$phids = ipull($response['hits']['hits'], '_id');
return $phids;
}
private function executeRequest($path, array $data, $is_write = false) {
$uri = new PhutilURI($this->uri);
$data = json_encode($data);
$uri->setPath($path);
$future = new HTTPSFuture($uri, $data);
if ($is_write) {
$future->setMethod('PUT');
}
if ($this->getTimeout()) {
$future->setTimeout($this->getTimeout());
}
list($body) = $future->resolvex();
if ($is_write) {
return null;
}
$body = json_decode($body, true);
if (!is_array($body)) {
throw new Exception("elasticsearch server returned invalid JSON!");
}
return $body;
}
}
diff --git a/src/applications/search/engine/PhabricatorSearchEngineMySQL.php b/src/applications/search/engine/PhabricatorSearchEngineMySQL.php
index 28e90ab1d9..cdc95be54e 100644
--- a/src/applications/search/engine/PhabricatorSearchEngineMySQL.php
+++ b/src/applications/search/engine/PhabricatorSearchEngineMySQL.php
@@ -1,314 +1,320 @@
<?php
/**
* @group search
*/
final class PhabricatorSearchEngineMySQL extends PhabricatorSearchEngine {
public function reindexAbstractDocument(
PhabricatorSearchAbstractDocument $doc) {
$phid = $doc->getPHID();
if (!$phid) {
throw new Exception("Document has no PHID!");
}
$store = new PhabricatorSearchDocument();
$store->setPHID($doc->getPHID());
$store->setDocumentType($doc->getDocumentType());
$store->setDocumentTitle($doc->getDocumentTitle());
$store->setDocumentCreated($doc->getDocumentCreated());
$store->setDocumentModified($doc->getDocumentModified());
$store->replace();
$conn_w = $store->establishConnection('w');
$field_dao = new PhabricatorSearchDocumentField();
queryfx(
$conn_w,
'DELETE FROM %T WHERE phid = %s',
$field_dao->getTableName(),
$phid);
foreach ($doc->getFieldData() as $field) {
list($ftype, $corpus, $aux_phid) = $field;
queryfx(
$conn_w,
'INSERT INTO %T (phid, phidType, field, auxPHID, corpus) '.
' VALUES (%s, %s, %s, %ns, %s)',
$field_dao->getTableName(),
$phid,
$doc->getDocumentType(),
$ftype,
$aux_phid,
$corpus);
}
$sql = array();
foreach ($doc->getRelationshipData() as $relationship) {
list($rtype, $to_phid, $to_type, $time) = $relationship;
$sql[] = qsprintf(
$conn_w,
'(%s, %s, %s, %s, %d)',
$phid,
$to_phid,
$rtype,
$to_type,
$time);
}
$rship_dao = new PhabricatorSearchDocumentRelationship();
queryfx(
$conn_w,
'DELETE FROM %T WHERE phid = %s',
$rship_dao->getTableName(),
$phid);
if ($sql) {
queryfx(
$conn_w,
'INSERT INTO %T'.
' (phid, relatedPHID, relation, relatedType, relatedTime) '.
' VALUES %Q',
$rship_dao->getTableName(),
implode(', ', $sql));
}
}
/**
* Rebuild the PhabricatorSearchAbstractDocument that was used to index
* an object out of the index itself. This is primarily useful for debugging,
* as it allows you to inspect the search index representation of a
* document.
*
* @param phid PHID of a document which exists in the search index.
* @return null|PhabricatorSearchAbstractDocument Abstract document object
* which corresponds to the original abstract document used to
* build the document index.
*/
public function reconstructDocument($phid) {
$dao_doc = new PhabricatorSearchDocument();
$dao_field = new PhabricatorSearchDocumentField();
$dao_relationship = new PhabricatorSearchDocumentRelationship();
$t_doc = $dao_doc->getTableName();
$t_field = $dao_field->getTableName();
$t_relationship = $dao_relationship->getTableName();
$doc = queryfx_one(
$dao_doc->establishConnection('r'),
'SELECT * FROM %T WHERE phid = %s',
$t_doc,
$phid);
if (!$doc) {
return null;
}
$fields = queryfx_all(
$dao_field->establishConnection('r'),
'SELECT * FROM %T WHERE phid = %s',
$t_field,
$phid);
$relationships = queryfx_all(
$dao_relationship->establishConnection('r'),
'SELECT * FROM %T WHERE phid = %s',
$t_relationship,
$phid);
$adoc = id(new PhabricatorSearchAbstractDocument())
->setPHID($phid)
->setDocumentType($doc['documentType'])
->setDocumentTitle($doc['documentTitle'])
->setDocumentCreated($doc['documentCreated'])
->setDocumentModified($doc['documentModified']);
foreach ($fields as $field) {
$adoc->addField(
$field['field'],
$field['corpus'],
$field['auxPHID']);
}
foreach ($relationships as $relationship) {
$adoc->addRelationship(
$relationship['relation'],
$relationship['relatedPHID'],
$relationship['relatedType'],
$relationship['relatedTime']);
}
return $adoc;
}
public function executeSearch(PhabricatorSearchQuery $query) {
$where = array();
$join = array();
$order = 'ORDER BY documentCreated DESC';
$dao_doc = new PhabricatorSearchDocument();
$dao_field = new PhabricatorSearchDocumentField();
$t_doc = $dao_doc->getTableName();
$t_field = $dao_field->getTableName();
$conn_r = $dao_doc->establishConnection('r');
$q = $query->getQuery();
if (strlen($q)) {
$join[] = qsprintf(
$conn_r,
"{$t_field} field ON field.phid = document.phid");
$where[] = qsprintf(
$conn_r,
'MATCH(corpus) AGAINST (%s IN BOOLEAN MODE)',
$q);
// When searching for a string, promote user listings above other
// listings.
$order = qsprintf(
$conn_r,
'ORDER BY
IF(documentType = %s, 0, 1) ASC,
MAX(MATCH(corpus) AGAINST (%s)) DESC',
'USER',
$q);
$field = $query->getParameter('field');
if ($field/* && $field != AdjutantQuery::FIELD_ALL*/) {
$where[] = qsprintf(
$conn_r,
'field.field = %s',
$field);
}
}
$exclude = $query->getParameter('exclude');
if ($exclude) {
$where[] = qsprintf($conn_r, 'document.phid != %s', $exclude);
}
if ($query->getParameter('type')) {
if (strlen($q)) {
// TODO: verify that this column actually does something useful in query
// plans once we have nontrivial amounts of data.
$where[] = qsprintf(
$conn_r,
'field.phidType = %s',
$query->getParameter('type'));
}
$where[] = qsprintf(
$conn_r,
'document.documentType = %s',
$query->getParameter('type'));
}
$join[] = $this->joinRelationship(
$conn_r,
$query,
'author',
PhabricatorSearchRelationship::RELATIONSHIP_AUTHOR);
$join[] = $this->joinRelationship(
$conn_r,
$query,
'open',
PhabricatorSearchRelationship::RELATIONSHIP_OPEN);
$join[] = $this->joinRelationship(
$conn_r,
$query,
'owner',
PhabricatorSearchRelationship::RELATIONSHIP_OWNER);
+ $join[] = $this->joinRelationship(
+ $conn_r,
+ $query,
+ 'subscribers',
+ PhabricatorSearchRelationship::RELATIONSHIP_SUBSCRIBER);
+
$join[] = $this->joinRelationship(
$conn_r,
$query,
'project',
PhabricatorSearchRelationship::RELATIONSHIP_PROJECT);
$join[] = $this->joinRelationship(
$conn_r,
$query,
'repository',
PhabricatorSearchRelationship::RELATIONSHIP_REPOSITORY);
$join = array_filter($join);
foreach ($join as $key => $clause) {
$join[$key] = ' JOIN '.$clause;
}
$join = implode(' ', $join);
if ($where) {
$where = 'WHERE '.implode(' AND ', $where);
} else {
$where = '';
}
$offset = (int)$query->getParameter('offset', 0);
$limit = (int)$query->getParameter('limit', 25);
$hits = queryfx_all(
$conn_r,
'SELECT
document.phid
FROM %T document
%Q
%Q
GROUP BY document.phid
%Q
LIMIT %d, %d',
$t_doc,
$join,
$where,
$order,
$offset,
$limit);
return ipull($hits, 'phid');
}
protected function joinRelationship(
AphrontDatabaseConnection $conn,
PhabricatorSearchQuery $query,
$field,
$type) {
$phids = $query->getParameter($field, array());
if (!$phids) {
return null;
}
$is_existence = false;
switch ($type) {
case PhabricatorSearchRelationship::RELATIONSHIP_OPEN:
$is_existence = true;
break;
}
$sql = qsprintf(
$conn,
'%T AS %C ON %C.phid = document.phid AND %C.relation = %s',
id(new PhabricatorSearchDocumentRelationship())->getTableName(),
$field,
$field,
$field,
$type);
if (!$is_existence) {
$sql .= qsprintf(
$conn,
' AND %C.relatedPHID in (%Ls)',
$field,
$phids);
}
return $sql;
}
}
diff --git a/src/applications/search/view/PhabricatorSearchResultView.php b/src/applications/search/view/PhabricatorSearchResultView.php
index c553e41cee..555f602d40 100644
--- a/src/applications/search/view/PhabricatorSearchResultView.php
+++ b/src/applications/search/view/PhabricatorSearchResultView.php
@@ -1,124 +1,118 @@
<?php
/**
* @group search
*/
final class PhabricatorSearchResultView extends AphrontView {
private $handle;
private $query;
private $object;
public function setHandle(PhabricatorObjectHandle $handle) {
$this->handle = $handle;
return $this;
}
public function setQuery(PhabricatorSearchQuery $query) {
$this->query = $query;
return $this;
}
public function setObject($object) {
$this->object = $object;
return $this;
}
public function render() {
$handle = $this->handle;
+ if (!$handle->isComplete()) {
+ return;
+ }
$type_name = nonempty($handle->getTypeName(), 'Document');
require_celerity_resource('phabricator-search-results-css');
$link = phutil_render_tag(
'a',
array(
'href' => $handle->getURI(),
),
PhabricatorEnv::getProductionURI($handle->getURI()));
$img = $handle->getImageURI();
if ($img) {
$img = phutil_render_tag(
'div',
array(
'class' => 'result-image',
'style' => "background-image: url('{$img}');",
),
'');
}
switch ($handle->getType()) {
case PhabricatorPHIDConstants::PHID_TYPE_CMIT:
$object_name = $handle->getName();
if ($this->object) {
$data = $this->object->getCommitData();
$summary = $data->getSummary();
if (strlen($summary)) {
$object_name = $handle->getName().': '.$data->getSummary();
}
}
break;
default:
$object_name = $handle->getFullName();
break;
}
- $index_link = phutil_render_tag(
- 'a',
- array(
- 'href' => '/search/index/'.$handle->getPHID().'/',
- 'style' => 'float: right',
- ),
- 'Examine Index');
-
return
'<div class="phabricator-search-result">'.
$img.
'<div class="result-desc">'.
- $index_link.
phutil_render_tag(
'a',
array(
'class' => 'result-name',
'href' => $handle->getURI(),
),
$this->emboldenQuery($object_name)).
'<div class="result-type">'.$type_name.' &middot; '.$link.'</div>'.
'</div>'.
'<div style="clear: both;"></div>'.
'</div>';
}
private function emboldenQuery($str) {
if (!$this->query) {
return phutil_escape_html($str);
}
$query = $this->query->getQuery();
$quoted_regexp = '/"([^"]*)"/';
$matches = array(1 => array());
preg_match_all($quoted_regexp, $query, $matches);
$quoted_queries = $matches[1];
$query = preg_replace($quoted_regexp, '', $query);
$query = preg_split('/\s+[+|]?/', $query);
$query = array_filter($query);
$query = array_merge($query, $quoted_queries);
$str = phutil_escape_html($str);
foreach ($query as $word) {
$word = phutil_escape_html($word);
$word = preg_quote($word, '/');
$word = preg_replace('/\\\\\*$/', '\w*', $word);
$str = preg_replace(
'/(?:^|\b)('.$word.')(?:\b|$)/i',
'<strong>\1</strong>',
$str);
}
return $str;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Nov 5, 10:53 PM (17 h, 57 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
321303
Default Alt Text
(45 KB)

Event Timeline