diff --git a/src/applications/project/phid/PhabricatorProjectPHIDTypeProject.php b/src/applications/project/phid/PhabricatorProjectPHIDTypeProject.php index 97a81b1d0c..56165dd3cf 100644 --- a/src/applications/project/phid/PhabricatorProjectPHIDTypeProject.php +++ b/src/applications/project/phid/PhabricatorProjectPHIDTypeProject.php @@ -1,49 +1,97 @@ withPHIDs($phids); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $project = $objects[$phid]; $name = $project->getName(); $id = $project->getID(); $handle->setName($name); $handle->setObjectName('#'.rtrim($project->getPhrictionSlug(), '/')); $handle->setURI("/project/view/{$id}/"); } } + public static function getProjectMonogramPatternFragment() { + // NOTE: This explicitly does not match strings which contain only + // digits, because digit strings like "#123" are used to reference tasks at + // Facebook and are somewhat conventional in general. + return '[^\s.!,:;]*[^\s\d.!,:;]+[^\s.!,:;]*'; + } + public function canLoadNamedObject($name) { - // TODO: We should be able to load named projects by hashtag, e.g. "#yolo". - return false; + $fragment = self::getProjectMonogramPatternFragment(); + return preg_match('/^#'.$fragment.'$/i', $name); + } + + public function loadNamedObjects( + PhabricatorObjectQuery $query, + array $names) { + + // If the user types "#YoloSwag", we still want to match "#yoloswag", so + // we normalize, query, and then map back to the original inputs. + + $map = array(); + foreach ($names as $key => $slug) { + $map[$this->normalizeSlug(substr($slug, 1))][] = $slug; + } + + $projects = id(new PhabricatorProjectQuery()) + ->setViewer($query->getViewer()) + ->withPhrictionSlugs(array_keys($map)) + ->execute(); + + $result = array(); + foreach ($projects as $project) { + $slugs = array($project->getPhrictionSlug()); + foreach ($slugs as $slug) { + foreach ($map[$slug] as $original) { + $result[$original] = $project; + } + } + } + + return $result; } + private function normalizeSlug($slug) { + // NOTE: We're using phutil_utf8_strtolower() (and not PhabricatorSlug's + // normalize() method) because this normalization should be only somewhat + // liberal. We want "#YOLO" to match against "#yolo", but "#\\yo!!lo" + // should not. normalize() strips out most punctuation and leads to + // excessively aggressive matches. + + return phutil_utf8_strtolower($slug).'/'; + } + + } diff --git a/src/applications/project/remarkup/ProjectRemarkupRule.php b/src/applications/project/remarkup/ProjectRemarkupRule.php index 9acd489dd6..637f0ea505 100644 --- a/src/applications/project/remarkup/ProjectRemarkupRule.php +++ b/src/applications/project/remarkup/ProjectRemarkupRule.php @@ -1,60 +1,44 @@ getEngine()->getConfig('viewer'); - // If the user types "#YoloSwag", we still want to match "#yoloswag", so - // we normalize, query, and then map back to the original inputs. - - $map = array(); - foreach ($ids as $key => $slug) { - $map[$this->normalizeSlug($slug)][] = $slug; + // Put the "#" back on the front of these IDs. + $names = array(); + foreach ($ids as $id) { + $names[] = '#'.$id; } - $projects = id(new PhabricatorProjectQuery()) + // Issue a query by object name. + $query = id(new PhabricatorObjectQuery()) ->setViewer($viewer) - ->withPhrictionSlugs(array_keys($map)) - ->execute(); + ->withNames($names); + + $query->execute(); + $projects = $query->getNamedResults(); + // Slice the "#" off again. $result = array(); - foreach ($projects as $project) { - $slugs = array($project->getPhrictionSlug()); - foreach ($slugs as $slug) { - foreach ($map[$slug] as $original) { - $result[$original] = $project; - } - } + foreach ($projects as $name => $project) { + $result[substr($name, 1)] = $project; } - return $result; } - private function normalizeSlug($slug) { - // NOTE: We're using phutil_utf8_strtolower() (and not PhabricatorSlug's - // normalize() method) because this normalization should be only somewhat - // liberal. We want "#YOLO" to match against "#yolo", but "#\\yo!!lo" - // should not. normalize() strips out most punctuation and leads to - // excessively aggressive matches. - - return phutil_utf8_strtolower($slug).'/'; - } - }