diff --git a/resources/sql/patches/030.imagemacro.sql b/resources/sql/patches/030.imagemacro.sql new file mode 100644 index 0000000000..8e90fc26d5 --- /dev/null +++ b/resources/sql/patches/030.imagemacro.sql @@ -0,0 +1,5 @@ +CREATE TABLE phabricator_file.`file_imagemacro` ( + `id` int unsigned NOT NULL auto_increment PRIMARY KEY, + `filePHID` varchar(64) NOT NULL, + `name` varchar(255) NOT NULL +); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index b3ec17f3d2..e5685d3268 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -287,6 +287,7 @@ phutil_register_library_map(array( 'PhabricatorFile' => 'applications/files/storage/file', 'PhabricatorFileController' => 'applications/files/controller/base', 'PhabricatorFileDAO' => 'applications/files/storage/base', + 'PhabricatorFileImageMacro' => 'applications/files/storage/imagemacro', 'PhabricatorFileListController' => 'applications/files/controller/list', 'PhabricatorFileStorageBlob' => 'applications/files/storage/storageblob', 'PhabricatorFileURI' => 'applications/files/uri', @@ -356,6 +357,7 @@ phutil_register_library_map(array( 'PhabricatorRedirectController' => 'applications/base/controller/redirect', 'PhabricatorRemarkupRuleDifferential' => 'infrastructure/markup/remarkup/markuprule/differential', 'PhabricatorRemarkupRuleDiffusion' => 'infrastructure/markup/remarkup/markuprule/diffusion', + 'PhabricatorRemarkupRuleImageMacro' => 'infrastructure/markup/remarkup/markuprule/imagemacro', 'PhabricatorRemarkupRuleManiphest' => 'infrastructure/markup/remarkup/markuprule/maniphest', 'PhabricatorRepository' => 'applications/repository/storage/repository', 'PhabricatorRepositoryArcanistProject' => 'applications/repository/storage/arcanistproject', @@ -672,6 +674,7 @@ phutil_register_library_map(array( 'PhabricatorFile' => 'PhabricatorFileDAO', 'PhabricatorFileController' => 'PhabricatorController', 'PhabricatorFileDAO' => 'PhabricatorLiskDAO', + 'PhabricatorFileImageMacro' => 'PhabricatorFileDAO', 'PhabricatorFileListController' => 'PhabricatorFileController', 'PhabricatorFileStorageBlob' => 'PhabricatorFileDAO', 'PhabricatorFileUploadController' => 'PhabricatorFileController', @@ -733,6 +736,7 @@ phutil_register_library_map(array( 'PhabricatorRedirectController' => 'PhabricatorController', 'PhabricatorRemarkupRuleDifferential' => 'PhutilRemarkupRule', 'PhabricatorRemarkupRuleDiffusion' => 'PhutilRemarkupRule', + 'PhabricatorRemarkupRuleImageMacro' => 'PhutilRemarkupRule', 'PhabricatorRemarkupRuleManiphest' => 'PhutilRemarkupRule', 'PhabricatorRepository' => 'PhabricatorRepositoryDAO', 'PhabricatorRepositoryArcanistProject' => 'PhabricatorRepositoryDAO', diff --git a/src/applications/differential/parser/markup/DifferentialMarkupEngineFactory.php b/src/applications/differential/parser/markup/DifferentialMarkupEngineFactory.php index 1eefbb60a1..a65712a83a 100644 --- a/src/applications/differential/parser/markup/DifferentialMarkupEngineFactory.php +++ b/src/applications/differential/parser/markup/DifferentialMarkupEngineFactory.php @@ -30,6 +30,7 @@ class DifferentialMarkupEngineFactory { $rules[] = new PhabricatorRemarkupRuleDifferential(); $rules[] = new PhabricatorRemarkupRuleDiffusion(); $rules[] = new PhabricatorRemarkupRuleManiphest(); + $rules[] = new PhabricatorRemarkupRuleImageMacro(); $rules[] = new PhutilRemarkupRuleEscapeHTML(); $rules[] = new PhutilRemarkupRuleMonospace(); diff --git a/src/applications/differential/parser/markup/__init__.php b/src/applications/differential/parser/markup/__init__.php index 7bc7a32d5a..4df4f32823 100644 --- a/src/applications/differential/parser/markup/__init__.php +++ b/src/applications/differential/parser/markup/__init__.php @@ -8,6 +8,7 @@ phutil_require_module('phabricator', 'infrastructure/markup/remarkup/markuprule/differential'); phutil_require_module('phabricator', 'infrastructure/markup/remarkup/markuprule/diffusion'); +phutil_require_module('phabricator', 'infrastructure/markup/remarkup/markuprule/imagemacro'); phutil_require_module('phabricator', 'infrastructure/markup/remarkup/markuprule/maniphest'); phutil_require_module('phutil', 'markup/engine/remarkup'); diff --git a/src/applications/files/storage/file/PhabricatorFile.php b/src/applications/files/storage/file/PhabricatorFile.php index 4a900cdb0f..7ba0b05e23 100644 --- a/src/applications/files/storage/file/PhabricatorFile.php +++ b/src/applications/files/storage/file/PhabricatorFile.php @@ -121,6 +121,23 @@ class PhabricatorFile extends PhabricatorFileDAO { return $file; } + public static function newFromFileDownload($uri, $name) { + $uri = new PhutilURI($uri); + $timeout = stream_context_create( + array( + 'http' => array( + 'timeout' => 5, + ), + )); + + $file_data = @file_get_contents($uri, false, $timeout); + if ($file_data === false) { + return null; + } + + return self::newFromFileData($file_data, array('name' => $name)); + } + public static function normalizeFileName($file_name) { return preg_replace('/[^a-zA-Z0-9.~_-]/', '_', $file_name); } diff --git a/src/applications/files/storage/file/__init__.php b/src/applications/files/storage/file/__init__.php index 1c82529ee4..c2533cf792 100644 --- a/src/applications/files/storage/file/__init__.php +++ b/src/applications/files/storage/file/__init__.php @@ -16,6 +16,7 @@ phutil_require_module('phabricator', 'infrastructure/env'); phutil_require_module('phutil', 'filesystem'); phutil_require_module('phutil', 'filesystem/tempfile'); phutil_require_module('phutil', 'future/exec'); +phutil_require_module('phutil', 'parser/uri'); phutil_require_module('phutil', 'utils'); diff --git a/src/applications/files/storage/imagemacro/PhabricatorFileImageMacro.php b/src/applications/files/storage/imagemacro/PhabricatorFileImageMacro.php new file mode 100644 index 0000000000..85825151e2 --- /dev/null +++ b/src/applications/files/storage/imagemacro/PhabricatorFileImageMacro.php @@ -0,0 +1,45 @@ + false, + ) + parent::getConfiguration(); + } + + static public function newFromImageURI($uri, $file_name, $image_macro_name) { + $file = PhabricatorFile::newFromFileDownload($uri, $file_name); + + if (!$file) { + return null; + } + + $image_macro = new PhabricatorFileImageMacro(); + $image_macro->setName($image_macro_name); + $image_macro->setFilePHID($file->getPHID()); + $image_macro->save(); + + return $image_macro; + } +} + diff --git a/src/applications/files/storage/imagemacro/__init__.php b/src/applications/files/storage/imagemacro/__init__.php new file mode 100644 index 0000000000..613481dc3c --- /dev/null +++ b/src/applications/files/storage/imagemacro/__init__.php @@ -0,0 +1,13 @@ +loadAll(); + foreach ($rows as $row) { + $this->images[$row->getName()] = $row->getFilePHID(); + } + $this->images[self::RANDOM_IMAGE_NAME] = ''; + } + + public function apply($text) { + $this->hash = 0; + + return preg_replace_callback( + '@\b([a-zA-Z0-9_\-]+)\b@U', + array($this, 'markupImageMacro'), + $text); + } + + /** + * Silly function for generating some 'randomness' based on the + * words in the text + */ + private function updateHash($word) { + // Simple Jenkins hash + for ($ii = 0; $ii < strlen($word); $ii++) { + $this->hash += ord($word[$ii]); + $this->hash += ($this->hash << 10); + $this->hash ^= ($this->hash >> 6); + } + } + + public function markupImageMacro($matches) { + // Update the hash that is used for defining each 'randomon' image. This way + // each 'randomon' image will be different, but they won't change when the + // text is updated. + $this->updateHash($matches[1]); + + if (array_key_exists($matches[1], $this->images)) { + if ($matches[1] === self::RANDOM_IMAGE_NAME) { + $keys = array_keys($this->images); + $phid = $this->images[$keys[$this->hash % count($this->images)]]; + } else { + $phid = $this->images[$matches[1]]; + } + + + $img = phutil_render_tag( + 'img', + array( + 'src' => PhabricatorFileURI::getViewURIForPHID($phid), + 'alt' => $matches[1], + 'title' => $matches[1]), + null); + return $this->getEngine()->storeText($img); + } else { + return $matches[1]; + } + } + +} diff --git a/src/infrastructure/markup/remarkup/markuprule/imagemacro/__init__.php b/src/infrastructure/markup/remarkup/markuprule/imagemacro/__init__.php new file mode 100644 index 0000000000..d742a178a2 --- /dev/null +++ b/src/infrastructure/markup/remarkup/markuprule/imagemacro/__init__.php @@ -0,0 +1,17 @@ +