diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 69c0801f9a..8677f47b6e 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3295,6 +3295,7 @@ phutil_register_library_map(array( 'PhabricatorMarkupInterface' => 'infrastructure/markup/PhabricatorMarkupInterface.php', 'PhabricatorMarkupOneOff' => 'infrastructure/markup/PhabricatorMarkupOneOff.php', 'PhabricatorMarkupPreviewController' => 'infrastructure/markup/PhabricatorMarkupPreviewController.php', + 'PhabricatorMemeEngine' => 'applications/macro/engine/PhabricatorMemeEngine.php', 'PhabricatorMemeRemarkupRule' => 'applications/macro/markup/PhabricatorMemeRemarkupRule.php', 'PhabricatorMentionRemarkupRule' => 'applications/people/markup/PhabricatorMentionRemarkupRule.php', 'PhabricatorMentionableInterface' => 'applications/transactions/interface/PhabricatorMentionableInterface.php', @@ -8893,6 +8894,7 @@ phutil_register_library_map(array( 'PhabricatorMarkupInterface', ), 'PhabricatorMarkupPreviewController' => 'PhabricatorController', + 'PhabricatorMemeEngine' => 'Phobject', 'PhabricatorMemeRemarkupRule' => 'PhutilRemarkupRule', 'PhabricatorMentionRemarkupRule' => 'PhutilRemarkupRule', 'PhabricatorMercurialGraphStream' => 'PhabricatorRepositoryGraphStream', diff --git a/src/applications/macro/conduit/MacroCreateMemeConduitAPIMethod.php b/src/applications/macro/conduit/MacroCreateMemeConduitAPIMethod.php index e63d831a70..3c939f97d5 100644 --- a/src/applications/macro/conduit/MacroCreateMemeConduitAPIMethod.php +++ b/src/applications/macro/conduit/MacroCreateMemeConduitAPIMethod.php @@ -35,22 +35,15 @@ final class MacroCreateMemeConduitAPIMethod extends MacroConduitAPIMethod { protected function execute(ConduitAPIRequest $request) { $user = $request->getUser(); - $macro_name = $request->getValue('macroName'); - $upper_text = $request->getValue('upperText'); - $lower_text = $request->getValue('lowerText'); - - $uri = PhabricatorMacroMemeController::generateMacro( - $user, - $macro_name, - $upper_text, - $lower_text); - - if (!$uri) { - throw new ConduitException('ERR-NOT-FOUND'); - } + $file = id(new PhabricatorMemeEngine()) + ->setViewer($user) + ->setTemplate($request->getValue('macroName')) + ->setAboveText($request->getValue('upperText')) + ->setBelowText($request->getValue('lowerText')) + ->newAsset(); return array( - 'uri' => $uri, + 'uri' => $file->getViewURI(), ); } diff --git a/src/applications/macro/controller/PhabricatorMacroMemeController.php b/src/applications/macro/controller/PhabricatorMacroMemeController.php index 794e24e36d..eb1dcbeb2f 100644 --- a/src/applications/macro/controller/PhabricatorMacroMemeController.php +++ b/src/applications/macro/controller/PhabricatorMacroMemeController.php @@ -13,104 +13,18 @@ final class PhabricatorMacroMemeController $lower_text = $request->getStr('lowertext'); $viewer = $request->getViewer(); - $uri = self::generateMacro( - $viewer, - $macro_name, - $upper_text, - $lower_text); + $file = id(new PhabricatorMemeEngine()) + ->setViewer($viewer) + ->setTemplate($macro_name) + ->setAboveText($request->getStr('above')) + ->setBelowText($request->getStr('below')) + ->newAsset(); $content = array( - 'imageURI' => $uri, + 'imageURI' => $file->getViewURI(), ); return id(new AphrontAjaxResponse())->setContent($content); } - public static function generateMacro( - PhabricatorUser $viewer, - $macro_name, - $upper_text, - $lower_text) { - - $macro = id(new PhabricatorMacroQuery()) - ->setViewer($viewer) - ->withNames(array($macro_name)) - ->needFiles(true) - ->executeOne(); - if (!$macro) { - return false; - } - $file = $macro->getFile(); - - $upper_text = phutil_utf8_strtoupper($upper_text); - $lower_text = phutil_utf8_strtoupper($lower_text); - - $hash = PhabricatorHash::digestForIndex( - phutil_json_encode( - array( - 'kind' => 'meme', - 'upper' => $upper_text, - 'lower' => $lower_text, - ))); - - $xfile = self::loadTransformedFile($viewer, $file->getPHID(), $hash); - if ($xfile) { - return $xfile->getViewURI(); - } - - $transformer = new PhabricatorImageTransformer(); - - $new_file = $transformer->executeMemeTransform( - $file, - $upper_text, - $lower_text); - - $xfile = id(new PhabricatorTransformedFile()) - ->setOriginalPHID($file->getPHID()) - ->setTransformedPHID($new_file->getPHID()) - ->setTransform($hash); - - try { - $caught = null; - - $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); - try { - $xfile->save(); - } catch (Exception $ex) { - $caught = $ex; - } - unset($unguarded); - - if ($caught) { - throw $caught; - } - - return $new_file->getViewURI(); - } catch (AphrontDuplicateKeyQueryException $ex) { - $xfile = self::loadTransformedFile($viewer, $file->getPHID(), $hash); - if (!$xfile) { - throw $ex; - } - return $xfile->getViewURI(); - } - } - - private static function loadTransformedFile( - PhabricatorUser $viewer, - $file_phid, - $hash) { - - $xform = id(new PhabricatorTransformedFile())->loadOneWhere( - 'originalPHID = %s AND transform = %s', - $file_phid, - $hash); - if (!$xform) { - return null; - } - - return id(new PhabricatorFileQuery()) - ->setViewer($viewer) - ->withPHIDs(array($xform->getTransformedPHID())) - ->executeOne(); - } } diff --git a/src/applications/macro/engine/PhabricatorMemeEngine.php b/src/applications/macro/engine/PhabricatorMemeEngine.php new file mode 100644 index 0000000000..21a9b44e99 --- /dev/null +++ b/src/applications/macro/engine/PhabricatorMemeEngine.php @@ -0,0 +1,163 @@ +viewer = $viewer; + return $this; + } + + public function getViewer() { + return $this->viewer; + } + + public function setTemplate($template) { + $this->template = $template; + return $this; + } + + public function getTemplate() { + return $this->template; + } + + public function setAboveText($above_text) { + $this->aboveText = $above_text; + return $this; + } + + public function getAboveText() { + return $this->aboveText; + } + + public function setBelowText($below_text) { + $this->belowText = $below_text; + return $this; + } + + public function getBelowText() { + return $this->belowText; + } + + public function getGenerateURI() { + return id(new PhutilURI('/macro/meme/')) + ->alter('macro', $this->getTemplate()) + ->alter('above', $this->getAboveText()) + ->alter('below', $this->getBelowText()); + } + + public function newAsset() { + $cache = $this->loadCachedFile(); + if ($cache) { + return $cache; + } + + $template = $this->loadTemplateFile(); + if (!$template) { + throw new Exception( + pht( + 'Template "%s" is not a valid template.', + $template)); + } + + $hash = $this->newTransformHash(); + + $transformer = new PhabricatorImageTransformer(); + $asset = $transformer->executeMemeTransform( + $template, + $this->getAboveText(), + $this->getBelowText()); + + $xfile = id(new PhabricatorTransformedFile()) + ->setOriginalPHID($template->getPHID()) + ->setTransformedPHID($asset->getPHID()) + ->setTransform($hash); + + try { + $caught = null; + + $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); + try { + $xfile->save(); + } catch (Exception $ex) { + $caught = $ex; + } + unset($unguarded); + + if ($caught) { + throw $caught; + } + + return $asset; + } catch (AphrontDuplicateKeyQueryException $ex) { + $xfile = $this->loadCachedFile(); + if (!$xfile) { + throw $ex; + } + return $xfile; + } + } + + private function newTransformHash() { + $properties = array( + 'kind' => 'meme', + 'above' => phutil_utf8_strtoupper($this->getAboveText()), + 'below' => phutil_utf8_strtoupper($this->getBelowText()), + ); + + $properties = phutil_json_encode($properties); + + return PhabricatorHash::digestForIndex($properties); + } + + public function loadCachedFile() { + $viewer = $this->getViewer(); + + $template_file = $this->loadTemplateFile(); + if (!$template_file) { + return null; + } + + $hash = $this->newTransformHash(); + + $xform = id(new PhabricatorTransformedFile())->loadOneWhere( + 'originalPHID = %s AND transform = %s', + $template_file->getPHID(), + $hash); + if (!$xform) { + return null; + } + + return id(new PhabricatorFileQuery()) + ->setViewer($viewer) + ->withPHIDs(array($xform->getTransformedPHID())) + ->executeOne(); + } + + private function loadTemplateFile() { + if ($this->templateFile === null) { + $viewer = $this->getViewer(); + $template = $this->getTemplate(); + + $macro = id(new PhabricatorMacroQuery()) + ->setViewer($viewer) + ->withNames(array($template)) + ->needFiles(true) + ->executeOne(); + if (!$macro) { + return null; + } + + $this->templateFile = $macro->getFile(); + } + + return $this->templateFile; + } + +} diff --git a/src/applications/macro/markup/PhabricatorMemeRemarkupRule.php b/src/applications/macro/markup/PhabricatorMemeRemarkupRule.php index 278bd403cb..cdee10676c 100644 --- a/src/applications/macro/markup/PhabricatorMemeRemarkupRule.php +++ b/src/applications/macro/markup/PhabricatorMemeRemarkupRule.php @@ -29,10 +29,14 @@ final class PhabricatorMemeRemarkupRule extends PhutilRemarkupRule { $parser = new PhutilSimpleOptions(); $options = $parser->parse($matches[1]) + $options; - $uri = id(new PhutilURI('/macro/meme/')) - ->alter('macro', $options['src']) - ->alter('uppertext', $options['above']) - ->alter('lowertext', $options['below']); + $engine = id(new PhabricatorMemeEngine()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->setTemplate($options['src']) + ->setAboveText($options['above']) + ->setBelowText($options['below']); + + $asset = $engine->loadCachedFile(); + $uri = $engine->getGenerateURI(); if ($this->getEngine()->isHTMLMailMode()) { $uri = PhabricatorEnv::getProductionURI($uri); @@ -50,10 +54,20 @@ final class PhabricatorMemeRemarkupRule extends PhutilRemarkupRule { $options['above'], $options['below']); - $img = id(new PHUIRemarkupImageView()) - ->setURI($uri) - ->addClass('phabricator-remarkup-macro') - ->setAlt($alt_text); + if ($asset) { + $img = $this->newTag( + 'img', + array( + 'src' => $asset->getViewURI(), + 'class' => 'phabricator-remarkup-macro', + 'alt' => $alt_text, + )); + } else { + $img = id(new PHUIRemarkupImageView()) + ->setURI($uri) + ->addClass('phabricator-remarkup-macro') + ->setAlt($alt_text); + } } return $this->getEngine()->storeText($img);