mirror of
https://we.phorge.it/source/phorge.git
synced 2025-01-10 23:01:04 +01:00
Modernize file embed Remarkup rule
Summary: Ref T603. Make this rule properly policy-aware, and extend from `PhabricatorRemarkupRuleObject`. Test Plan: - Embedded an image, tested all options (name, link, float, layout, size). - Used lightbox to view several images. - Embedded a text file, tested all options (name). - Embedded audio, tested all options (loop, autoplay). - Attached a file via comment to a task, verified edge was created. - Attached a file via comment to a conpherence, verified edge was created. - Viewed old files, verified remarkup version bump rendered them correctly. Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T603 Differential Revision: https://secure.phabricator.com/D7192
This commit is contained in:
parent
aac490180f
commit
742d45b625
3 changed files with 166 additions and 200 deletions
|
@ -1,236 +1,188 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @group markup
|
||||
*/
|
||||
final class PhabricatorRemarkupRuleEmbedFile
|
||||
extends PhutilRemarkupRule {
|
||||
extends PhabricatorRemarkupRuleObject {
|
||||
|
||||
const KEY_RULE_EMBED_FILE = 'rule.embed.file';
|
||||
const KEY_EMBED_FILE_PHIDS = 'phabricator.embedded-file-phids';
|
||||
|
||||
public function apply($text) {
|
||||
return preg_replace_callback(
|
||||
"@{F(\d+)([^}]+?)?}@",
|
||||
array($this, 'markupEmbedFile'),
|
||||
$text);
|
||||
protected function getObjectNamePrefix() {
|
||||
return 'F';
|
||||
}
|
||||
|
||||
public function markupEmbedFile($matches) {
|
||||
|
||||
$file = null;
|
||||
if ($matches[1]) {
|
||||
// TODO: This is pretty inefficient if there are a bunch of files.
|
||||
// TODO: (T603) This isn't policy-aware and should be extending
|
||||
// PhabricatorRemarkupRuleObject.
|
||||
$file = id(new PhabricatorFile())->load($matches[1]);
|
||||
}
|
||||
|
||||
if (!$file) {
|
||||
return $matches[0];
|
||||
}
|
||||
|
||||
protected function loadObjects(array $ids) {
|
||||
$engine = $this->getEngine();
|
||||
|
||||
if ($engine->isTextMode()) {
|
||||
return $engine->storeText($file->getBestURI());
|
||||
$viewer = $engine->getConfig('viewer');
|
||||
$objects = id(new PhabricatorFileQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs($ids)
|
||||
->execute();
|
||||
|
||||
$phids_key = self::KEY_EMBED_FILE_PHIDS;
|
||||
$phids = $engine->getTextMetadata($phids_key, array());
|
||||
foreach (mpull($objects, 'getPHID') as $phid) {
|
||||
$phids[] = $phid;
|
||||
}
|
||||
$engine->setTextMetadata($phids_key, $phids);
|
||||
|
||||
$phid = $file->getPHID();
|
||||
return $objects;
|
||||
}
|
||||
|
||||
$token = $engine->storeText('');
|
||||
$metadata_key = self::KEY_RULE_EMBED_FILE;
|
||||
$metadata = $engine->getTextMetadata($metadata_key, array());
|
||||
$bundle = array('token' => $token);
|
||||
protected function renderObjectEmbed($object, $handle, $options) {
|
||||
$options = $this->getFileOptions($options) + array(
|
||||
'name' => $object->getName(),
|
||||
);
|
||||
|
||||
$is_viewable_image = $object->isViewableImage();
|
||||
$is_audio = $object->isAudio();
|
||||
$force_link = ($options['layout'] == 'link');
|
||||
|
||||
$options['viewable'] = ($is_viewable_image || $is_audio);
|
||||
|
||||
if ($is_viewable_image && !$force_link) {
|
||||
return $this->renderImageFile($object, $handle, $options);
|
||||
} else if ($is_audio && !$force_link) {
|
||||
return $this->renderAudioFile($object, $handle, $options);
|
||||
} else {
|
||||
return $this->renderFileLink($object, $handle, $options);
|
||||
}
|
||||
}
|
||||
|
||||
private function getFileOptions($option_string) {
|
||||
$options = array(
|
||||
'size' => 'thumb',
|
||||
'layout' => 'left',
|
||||
'float' => false,
|
||||
'name' => null,
|
||||
);
|
||||
|
||||
if (!empty($matches[2])) {
|
||||
$matches[2] = trim($matches[2], ', ');
|
||||
if ($option_string) {
|
||||
$option_string = trim($option_string, ', ');
|
||||
$parser = new PhutilSimpleOptions();
|
||||
$options = $parser->parse($matches[2]) + $options;
|
||||
}
|
||||
$file_name = coalesce($options['name'], $file->getName());
|
||||
$options['name'] = $file_name;
|
||||
|
||||
$is_viewable_image = $file->isViewableImage();
|
||||
$is_audio = $file->isAudio();
|
||||
|
||||
$attrs = array();
|
||||
if ($is_viewable_image) {
|
||||
switch ((string)$options['size']) {
|
||||
case 'full':
|
||||
$attrs['src'] = $file->getBestURI();
|
||||
$options['image_class'] = null;
|
||||
$file_data = $file->getMetadata();
|
||||
$height = idx($file_data, PhabricatorFile::METADATA_IMAGE_HEIGHT);
|
||||
if ($height) {
|
||||
$attrs['height'] = $height;
|
||||
}
|
||||
$width = idx($file_data, PhabricatorFile::METADATA_IMAGE_WIDTH);
|
||||
if ($width) {
|
||||
$attrs['width'] = $width;
|
||||
}
|
||||
break;
|
||||
case 'thumb':
|
||||
default:
|
||||
$attrs['src'] = $file->getPreview220URI();
|
||||
$dimensions =
|
||||
PhabricatorImageTransformer::getPreviewDimensions($file, 220);
|
||||
$attrs['width'] = $dimensions['sdx'];
|
||||
$attrs['height'] = $dimensions['sdy'];
|
||||
$options['image_class'] = 'phabricator-remarkup-embed-image';
|
||||
break;
|
||||
}
|
||||
}
|
||||
$bundle['attrs'] = $attrs;
|
||||
$bundle['options'] = $options;
|
||||
|
||||
$bundle['meta'] = array(
|
||||
'phid' => $file->getPHID(),
|
||||
'viewable' => $is_viewable_image,
|
||||
'audio' => $is_audio,
|
||||
'uri' => $file->getBestURI(),
|
||||
'dUri' => $file->getDownloadURI(),
|
||||
'name' => $options['name'],
|
||||
'mime' => $file->getMimeType(),
|
||||
);
|
||||
|
||||
if ($is_audio) {
|
||||
$bundle['meta'] += array(
|
||||
'autoplay' => idx($options, 'autoplay'),
|
||||
'loop' => idx($options, 'loop'),
|
||||
);
|
||||
$options = $parser->parse($option_string) + $options;
|
||||
}
|
||||
|
||||
$metadata[$phid][] = $bundle;
|
||||
$engine->setTextMetadata($metadata_key, $metadata);
|
||||
|
||||
return $token;
|
||||
return $options;
|
||||
}
|
||||
|
||||
public function didMarkupText() {
|
||||
$engine = $this->getEngine();
|
||||
private function renderImageFile(
|
||||
PhabricatorFile $file,
|
||||
PhabricatorObjectHandle $handle,
|
||||
array $options) {
|
||||
|
||||
$metadata_key = self::KEY_RULE_EMBED_FILE;
|
||||
$metadata = $engine->getTextMetadata($metadata_key, array());
|
||||
require_celerity_resource('lightbox-attachment-css');
|
||||
|
||||
if (!$metadata) {
|
||||
return;
|
||||
$attrs = array();
|
||||
$image_class = null;
|
||||
switch ((string)$options['size']) {
|
||||
case 'full':
|
||||
$attrs += array(
|
||||
'src' => $file->getBestURI(),
|
||||
'width' => $file->getImageWidth(),
|
||||
'height' => $file->getImageHeight(),
|
||||
);
|
||||
break;
|
||||
case 'thumb':
|
||||
default:
|
||||
$attrs['src'] = $file->getPreview220URI();
|
||||
$dimensions =
|
||||
PhabricatorImageTransformer::getPreviewDimensions($file, 220);
|
||||
$attrs['width'] = $dimensions['sdx'];
|
||||
$attrs['height'] = $dimensions['sdy'];
|
||||
$image_class = 'phabricator-remarkup-embed-image';
|
||||
break;
|
||||
}
|
||||
|
||||
$file_phids = array();
|
||||
foreach ($metadata as $phid => $bundles) {
|
||||
foreach ($bundles as $data) {
|
||||
$img = phutil_tag('img', $attrs);
|
||||
|
||||
$options = $data['options'];
|
||||
$meta = $data['meta'];
|
||||
$embed = javelin_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $file->getBestURI(),
|
||||
'class' => $image_class,
|
||||
'sigil' => 'lightboxable',
|
||||
'meta' => array(
|
||||
'phid' => $file->getPHID(),
|
||||
'uri' => $file->getBestURI(),
|
||||
'viewable' => true,
|
||||
),
|
||||
),
|
||||
$img);
|
||||
|
||||
$is_image = idx($meta, 'viewable');
|
||||
$is_audio = idx($meta, 'audio');
|
||||
switch ($options['layout']) {
|
||||
case 'right':
|
||||
case 'center':
|
||||
case 'inline':
|
||||
case 'left':
|
||||
$layout_class = 'phabricator-remarkup-embed-layout-'.$options['layout'];
|
||||
break;
|
||||
default:
|
||||
$layout_class = 'phabricator-remarkup-embed-layout-left';
|
||||
break;
|
||||
}
|
||||
|
||||
if ((!$is_image && !$is_audio) || $options['layout'] == 'link') {
|
||||
$link = id(new PhabricatorFileLinkView())
|
||||
->setFilePHID($meta['phid'])
|
||||
->setFileName($meta['name'])
|
||||
->setFileDownloadURI($meta['dUri'])
|
||||
->setFileViewURI($meta['uri'])
|
||||
->setFileViewable($meta['viewable']);
|
||||
$embed = $link->render();
|
||||
$engine->overwriteStoredText($data['token'], $embed);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($is_audio) {
|
||||
if (idx($options, 'autoplay')) {
|
||||
$preload = 'auto';
|
||||
$autoplay = 'autoplay';
|
||||
} else {
|
||||
$preload = 'none';
|
||||
$autoplay = null;
|
||||
}
|
||||
|
||||
$link = phutil_tag(
|
||||
'audio',
|
||||
array(
|
||||
'controls' => 'controls',
|
||||
'preload' => $preload,
|
||||
'autoplay' => $autoplay,
|
||||
'loop' => idx($options, 'loop') ? 'loop' : null,
|
||||
),
|
||||
phutil_tag(
|
||||
'source',
|
||||
array(
|
||||
'src' => $meta['uri'],
|
||||
'type' => $meta['mime'],
|
||||
)));
|
||||
$engine->overwriteStoredText($data['token'], $link);
|
||||
continue;
|
||||
}
|
||||
|
||||
require_celerity_resource('lightbox-attachment-css');
|
||||
$img = phutil_tag('img', $data['attrs']);
|
||||
|
||||
$embed = javelin_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $meta['uri'],
|
||||
'class' => $options['image_class'],
|
||||
'sigil' => 'lightboxable',
|
||||
'mustcapture' => true,
|
||||
'meta' => $meta,
|
||||
),
|
||||
$img);
|
||||
|
||||
$layout_class = null;
|
||||
switch ($options['layout']) {
|
||||
case 'right':
|
||||
case 'center':
|
||||
case 'inline':
|
||||
case 'left':
|
||||
$layout_class = 'phabricator-remarkup-embed-layout-'.
|
||||
$options['layout'];
|
||||
break;
|
||||
default:
|
||||
$layout_class = 'phabricator-remarkup-embed-layout-left';
|
||||
break;
|
||||
}
|
||||
|
||||
if ($options['float']) {
|
||||
switch ($options['layout']) {
|
||||
case 'center':
|
||||
case 'inline':
|
||||
break;
|
||||
case 'right':
|
||||
$layout_class .= ' phabricator-remarkup-embed-float-right';
|
||||
break;
|
||||
case 'left':
|
||||
default:
|
||||
$layout_class .= ' phabricator-remarkup-embed-float-left';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($layout_class) {
|
||||
$embed = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => $layout_class,
|
||||
),
|
||||
$embed);
|
||||
}
|
||||
|
||||
$engine->overwriteStoredText($data['token'], $embed);
|
||||
if ($options['float']) {
|
||||
switch ($options['layout']) {
|
||||
case 'center':
|
||||
case 'inline':
|
||||
break;
|
||||
case 'right':
|
||||
$layout_class .= ' phabricator-remarkup-embed-float-right';
|
||||
break;
|
||||
case 'left':
|
||||
default:
|
||||
$layout_class .= ' phabricator-remarkup-embed-float-left';
|
||||
break;
|
||||
}
|
||||
$file_phids[] = $phid;
|
||||
}
|
||||
$engine->setTextMetadata(self::KEY_EMBED_FILE_PHIDS, $file_phids);
|
||||
$engine->setTextMetadata($metadata_key, array());
|
||||
|
||||
return phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => $layout_class,
|
||||
),
|
||||
$embed);
|
||||
}
|
||||
|
||||
private function renderAudioFile(
|
||||
PhabricatorFile $file,
|
||||
PhabricatorObjectHandle $handle,
|
||||
array $options) {
|
||||
|
||||
if (idx($options, 'autoplay')) {
|
||||
$preload = 'auto';
|
||||
$autoplay = 'autoplay';
|
||||
} else {
|
||||
$preload = 'none';
|
||||
$autoplay = null;
|
||||
}
|
||||
|
||||
return phutil_tag(
|
||||
'audio',
|
||||
array(
|
||||
'controls' => 'controls',
|
||||
'preload' => $preload,
|
||||
'autoplay' => $autoplay,
|
||||
'loop' => idx($options, 'loop') ? 'loop' : null,
|
||||
),
|
||||
phutil_tag(
|
||||
'source',
|
||||
array(
|
||||
'src' => $file->getBestURI(),
|
||||
'type' => $file->getMimeType(),
|
||||
)));
|
||||
}
|
||||
|
||||
private function renderFileLink(
|
||||
PhabricatorFile $file,
|
||||
PhabricatorObjectHandle $handle,
|
||||
array $options) {
|
||||
|
||||
return id(new PhabricatorFileLinkView())
|
||||
->setFilePHID($file->getPHID())
|
||||
->setFileName($options['name'])
|
||||
->setFileDownloadURI($file->getDownloadURI())
|
||||
->setFileViewURI($file->getBestURI())
|
||||
->setFileViewable($options['viewable']);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -846,6 +846,20 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function getImageHeight() {
|
||||
if (!$this->isViewableImage()) {
|
||||
return null;
|
||||
}
|
||||
return idx($this->metadata, self::METADATA_IMAGE_HEIGHT);
|
||||
}
|
||||
|
||||
public function getImageWidth() {
|
||||
if (!$this->isViewableImage()) {
|
||||
return null;
|
||||
}
|
||||
return idx($this->metadata, self::METADATA_IMAGE_WIDTH);
|
||||
}
|
||||
|
||||
|
||||
/* -( PhabricatorPolicyInterface Implementation )-------------------------- */
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ final class PhabricatorMarkupEngine {
|
|||
|
||||
private $objects = array();
|
||||
private $viewer;
|
||||
private $version = 7;
|
||||
private $version = 8;
|
||||
|
||||
|
||||
/* -( Markup Pipeline )---------------------------------------------------- */
|
||||
|
|
Loading…
Reference in a new issue