diff --git a/src/infrastructure/markup/PhabricatorMarkupEngine.php b/src/infrastructure/markup/PhabricatorMarkupEngine.php --- a/src/infrastructure/markup/PhabricatorMarkupEngine.php +++ b/src/infrastructure/markup/PhabricatorMarkupEngine.php @@ -46,6 +46,8 @@ private $engineCaches = array(); private $auxiliaryConfig = array(); + private static $engineStack = array(); + /* -( Markup Pipeline )---------------------------------------------------- */ @@ -103,6 +105,24 @@ * @task markup */ public function process() { + self::$engineStack[] = $this; + + try { + $result = $this->execute(); + } finally { + array_pop(self::$engineStack); + } + + return $result; + } + + public static function isRenderingEmbeddedContent() { + // See T13678. This prevents cycles when rendering embedded content that + // itself has remarkup fields. + return (count(self::$engineStack) > 1); + } + + private function execute() { $keys = array(); foreach ($this->objects as $key => $info) { if (!isset($info['markup'])) { diff --git a/src/infrastructure/markup/rule/PhabricatorObjectRemarkupRule.php b/src/infrastructure/markup/rule/PhabricatorObjectRemarkupRule.php --- a/src/infrastructure/markup/rule/PhabricatorObjectRemarkupRule.php +++ b/src/infrastructure/markup/rule/PhabricatorObjectRemarkupRule.php @@ -126,6 +126,12 @@ return $this->renderObjectTagForMail($name, $href, $handle); } + // See T13678. If we're already rendering embedded content, render a + // default reference instead to avoid cycles. + if (PhabricatorMarkupEngine::isRenderingEmbeddedContent()) { + return $this->renderDefaultObjectEmbed($object, $handle); + } + return $this->renderObjectEmbed($object, $handle, $options); } @@ -133,6 +139,12 @@ $object, PhabricatorObjectHandle $handle, $options) { + return $this->renderDefaultObjectEmbed($object, $handle); + } + + final protected function renderDefaultObjectEmbed( + $object, + PhabricatorObjectHandle $handle) { $name = $handle->getFullName(); $href = $handle->getURI();