@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Fix Diviner links to articles by title

Summary:
Ref T988. This fixes the biggest current problem with Diviner, which is dead links to articles.

In the new Diviner, articles can have both a "name" (derived from the file name, and used in the URI) and a "title" (optional, specified explicitly). For example, we have one document with the name "feedback" and the title "Give Feedback! Get Support!".

On disk, we want to use the name for the actual file where the text lives ("feedback.diviner"). We also want to use the name in the URI, to generate a clean URI and to allow us to retitle the document slightly without breaking links to it (for example, we renamed the "Backup" document to "Backups and Migrations").

However, when displaying the article we want to use the title.

Currently, you can //only// link to the name, not the title. This is inconvenient:

- We have a bunch of existing docs which link to titles.
- It's natural/intuitive to link to titles.
- Linking to titles makes it easier/cheaper to generate documentation, because we don't need to be able to resolve things at render time.

To remedy this, allow links to target either names or titles. If we miss on a name query, we'll do a title query. This is implemented with a slug hash to allow approximately correct titles (wrong case/spacing/punctuation, e.g.) and sidestep all the UTF8/column length issues.

(In the long run, atom resolution should theoretically be more sophistiated than it is now, and we should do render-time lookups on at least some documents to catch bad links. However, this is fairly complicated and a relatively advanced feature, and I think allowing links to titles is desirable no matter what.)

Test Plan: The user documentation book now has valid links to articles when the titles and names differ.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T988

Differential Revision: https://secure.phabricator.com/D8407

+66 -9
+2
resources/sql/autopatches/20140305.diviner.1.slugcol.sql
··· 1 + ALTER TABLE {$NAMESPACE}_diviner.diviner_livesymbol 2 + ADD titleSlugHash CHAR(12) COLLATE latin1_bin AFTER title;
+2
resources/sql/autopatches/20140305.diviner.2.slugkey.sql
··· 1 + ALTER TABLE {$NAMESPACE}_diviner.diviner_livesymbol 2 + ADD KEY `key_slug` (titleSlugHash);
+1
src/applications/diviner/atom/DivinerAtom.php
··· 197 197 } 198 198 199 199 $parts = array( 200 + $this->getBook(), 200 201 $this->getType(), 201 202 $this->getName(), 202 203 $this->getFile(),
+10
src/applications/diviner/atom/DivinerAtomRef.php
··· 109 109 return $this->title; 110 110 } 111 111 112 + public function getTitleSlug() { 113 + return self::normalizeTitleString($this->getTitle()); 114 + } 115 + 112 116 public function toDictionary() { 113 117 return array( 114 118 'book' => $this->getBook(), ··· 144 148 $obj->index = idx($dict, 'index'); 145 149 $obj->summary = idx($dict, 'summary'); 146 150 $obj->title = idx($dict, 'title'); 151 + 147 152 return $obj; 148 153 } 149 154 ··· 191 196 ); 192 197 193 198 return idx($alternates, $str, $str); 199 + } 200 + 201 + public static function normalizeTitleString($str) { 202 + $str = self::normalizeString($str); 203 + return phutil_utf8_strtolower($str); 194 204 } 195 205 196 206 }
+18 -9
src/applications/diviner/controller/DivinerFindController.php
··· 24 24 } 25 25 26 26 $query = id(new DivinerAtomQuery()) 27 - ->setViewer($viewer) 28 - ->withNames( 29 - array( 30 - $request->getStr('name'), 31 - // TODO: This could probably be more smartly normalized in the DB, 32 - // but just fake it for now. 33 - phutil_utf8_strtolower($request->getStr('name')), 34 - )); 27 + ->setViewer($viewer); 35 28 36 29 if ($book) { 37 30 $query->withBookPHIDs(array($book->getPHID())); ··· 47 40 $query->withTypes(array($type)); 48 41 } 49 42 50 - $atoms = $query->execute(); 43 + $name_query = clone $query; 44 + 45 + $name_query->withNames( 46 + array( 47 + $request->getStr('name'), 48 + // TODO: This could probably be more smartly normalized in the DB, 49 + // but just fake it for now. 50 + phutil_utf8_strtolower($request->getStr('name')), 51 + )); 52 + 53 + $atoms = $name_query->execute(); 54 + 55 + if (!$atoms) { 56 + $title_query = clone $query; 57 + $title_query->withTitles(array($request->getStr('name'))); 58 + $atoms = $title_query->execute(); 59 + } 51 60 52 61 if (!$atoms) { 53 62 return new Aphront404Response();
+20
src/applications/diviner/query/DivinerAtomQuery.php
··· 13 13 private $includeUndocumentable; 14 14 private $includeGhosts; 15 15 private $nodeHashes; 16 + private $titles; 16 17 17 18 private $needAtoms; 18 19 private $needExtends; ··· 55 56 56 57 public function withNodeHashes(array $hashes) { 57 58 $this->nodeHashes = $hashes; 59 + return $this; 60 + } 61 + 62 + public function withTitles($titles) { 63 + $this->titles = $titles; 58 64 return $this; 59 65 } 60 66 ··· 285 291 $conn_r, 286 292 'name IN (%Ls)', 287 293 $this->names); 294 + } 295 + 296 + if ($this->titles) { 297 + $hashes = array(); 298 + foreach ($this->titles as $title) { 299 + $slug = DivinerAtomRef::normalizeTitleString($title); 300 + $hash = PhabricatorHash::digestForIndex($slug); 301 + $hashes[] = $hash; 302 + } 303 + 304 + $where[] = qsprintf( 305 + $conn_r, 306 + 'titleSlugHash in (%Ls)', 307 + $hashes); 288 308 } 289 309 290 310 if ($this->contexts) {
+13
src/applications/diviner/storage/DivinerLiveSymbol.php
··· 13 13 protected $nodeHash; 14 14 15 15 protected $title; 16 + protected $titleSlugHash; 16 17 protected $groupName; 17 18 protected $summary; 18 19 protected $isDocumentable = 0; ··· 103 104 $title = $this->getName(); 104 105 } 105 106 return $title; 107 + } 108 + 109 + public function setTitle($value) { 110 + $this->writeField('title', $value); 111 + if (strlen($value)) { 112 + $slug = DivinerAtomRef::normalizeTitleString($value); 113 + $hash = PhabricatorHash::digestForIndex($slug); 114 + $this->titleSlugHash = $hash; 115 + } else { 116 + $this->titleSlugHash = null; 117 + } 118 + return $this; 106 119 } 107 120 108 121 public function attachExtends(array $extends) {