diff --git a/resources/celerity/map.php b/resources/celerity/map.php --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -88,7 +88,7 @@ 'rsrc/css/application/phortune/phortune-credit-card-form.css' => '8391eb02', 'rsrc/css/application/phortune/phortune.css' => '9149f103', 'rsrc/css/application/phrequent/phrequent.css' => 'ffc185ad', - 'rsrc/css/application/phriction/phriction-document-css.css' => 'd1861e06', + 'rsrc/css/application/phriction/phriction-document-css.css' => '55446c91', 'rsrc/css/application/policy/policy-edit.css' => '815c66f7', 'rsrc/css/application/policy/policy-transaction-detail.css' => '82100a43', 'rsrc/css/application/policy/policy.css' => '957ea14c', @@ -807,7 +807,7 @@ 'phortune-credit-card-form-css' => '8391eb02', 'phortune-css' => '9149f103', 'phrequent-css' => 'ffc185ad', - 'phriction-document-css' => 'd1861e06', + 'phriction-document-css' => '55446c91', 'phui-action-panel-css' => '91c7b835', 'phui-badge-view-css' => '3baef8db', 'phui-big-info-view-css' => 'bd903741', diff --git a/src/applications/phriction/markup/PhrictionRemarkupRule.php b/src/applications/phriction/markup/PhrictionRemarkupRule.php --- a/src/applications/phriction/markup/PhrictionRemarkupRule.php +++ b/src/applications/phriction/markup/PhrictionRemarkupRule.php @@ -2,6 +2,8 @@ final class PhrictionRemarkupRule extends PhutilRemarkupRule { + const KEY_RULE_PHRICTION_LINK = 'phriction.link'; + public function getPriority() { return 175.0; } @@ -48,37 +50,123 @@ } } - $name = trim(idx($matches, 2, $link)); + $name = trim(idx($matches, 2, '')); if (empty($matches[2])) { - $name = explode('/', trim($name, '/')); - $name = end($name); + $name = null; } - $uri = new PhutilURI($link); - $slug = $uri->getPath(); - $fragment = $uri->getFragment(); - $slug = PhabricatorSlug::normalize($slug); - $slug = PhrictionDocument::getSlugURI($slug); - $href = (string)id(new PhutilURI($slug))->setFragment($fragment); - - $text_mode = $this->getEngine()->isTextMode(); - $mail_mode = $this->getEngine()->isHTMLMailMode(); - - if ($this->getEngine()->getState('toc')) { - $text = $name; - } else if ($text_mode || $mail_mode) { - return PhabricatorEnv::getProductionURI($href); - } else { - $text = $this->newTag( - 'a', - array( - 'href' => $href, - 'class' => 'phriction-link', - ), - $name); + // Link is now used for slug detection, so append a slash if one + // is needed. + $link = rtrim($link, '/').'/'; + + $engine = $this->getEngine(); + $token = $engine->storeText('x'); + $metadata = $engine->getTextMetadata( + self::KEY_RULE_PHRICTION_LINK, + array()); + $metadata[] = array( + 'token' => $token, + 'link' => $link, + 'explicitName' => $name, + ); + $engine->setTextMetadata(self::KEY_RULE_PHRICTION_LINK, $metadata); + + return $token; + } + + public function didMarkupText() { + $engine = $this->getEngine(); + $metadata = $engine->getTextMetadata( + self::KEY_RULE_PHRICTION_LINK, + array()); + + if (!$metadata) { + return; } - return $this->getEngine()->storeText($text); + $slugs = ipull($metadata, 'link'); + + // We have to make two queries here to distinguish between + // documents the user can't see, and documents that don't + // exist. + $visible_documents = id(new PhrictionDocumentQuery()) + ->setViewer($engine->getConfig('viewer')) + ->withSlugs($slugs) + ->needContent(true) + ->execute(); + $existant_documents = id(new PhrictionDocumentQuery()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->withSlugs($slugs) + ->execute(); + + $visible_documents = mpull($visible_documents, null, 'getSlug'); + $existant_documents = mpull($existant_documents, null, 'getSlug'); + + foreach ($metadata as $spec) { + $link = $spec['link']; + $name = $spec['explicitName']; + $class = 'phriction-link'; + + if (idx($existant_documents, $link) === null) { + // The target document doesn't exist. + if ($name === null) { + $name = explode('/', trim($link, '/')); + $name = end($name); + } + $class = 'phriction-link-missing'; + } else if (idx($visible_documents, $link) === null) { + // The document exists, but the user can't see it. + if ($name === null) { + $name = explode('/', trim($link, '/')); + $name = end($name); + } + $class = 'phriction-link-lock'; + } else { + if ($name === null) { + // Use the title of the document if no name is set. + $name = $visible_documents[$link] + ->getContent() + ->getTitle(); + } + } + + $uri = new PhutilURI($link); + $slug = $uri->getPath(); + $fragment = $uri->getFragment(); + $slug = PhabricatorSlug::normalize($slug); + $slug = PhrictionDocument::getSlugURI($slug); + $href = (string)id(new PhutilURI($slug))->setFragment($fragment); + + $text_mode = $this->getEngine()->isTextMode(); + $mail_mode = $this->getEngine()->isHTMLMailMode(); + + if ($this->getEngine()->getState('toc')) { + $text = $name; + } else if ($text_mode || $mail_mode) { + return PhabricatorEnv::getProductionURI($href); + } else { + if ($class === 'phriction-link-lock') { + $name = array( + $this->newTag( + 'i', + array( + 'class' => 'phui-icon-view phui-font-fa fa-lock', + ), + ''), + ' ', + $name, + ); + } + $text = $this->newTag( + 'a', + array( + 'href' => $href, + 'class' => $class, + ), + $name); + $this->getEngine()->overwriteStoredText($spec['token'], $text); + } + } } } diff --git a/webroot/rsrc/css/application/phriction/phriction-document-css.css b/webroot/rsrc/css/application/phriction/phriction-document-css.css --- a/webroot/rsrc/css/application/phriction/phriction-document-css.css +++ b/webroot/rsrc/css/application/phriction/phriction-document-css.css @@ -36,3 +36,13 @@ .phui-document-content .phriction-link { font-weight: bold; } + +.phui-document-content .phriction-link-missing { + font-weight: bold; + color: {$red}; +} + +.phui-document-content .phriction-link-lock { + font-weight: bold; + color: {$greytext}; +}