1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-09-20 17:28:51 +02: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:
epriestley 2013-10-01 18:03:09 -07:00
parent aac490180f
commit 742d45b625
3 changed files with 166 additions and 200 deletions

View file

@ -1,236 +1,188 @@
<?php <?php
/**
* @group markup
*/
final class PhabricatorRemarkupRuleEmbedFile final class PhabricatorRemarkupRuleEmbedFile
extends PhutilRemarkupRule { extends PhabricatorRemarkupRuleObject {
const KEY_RULE_EMBED_FILE = 'rule.embed.file';
const KEY_EMBED_FILE_PHIDS = 'phabricator.embedded-file-phids'; const KEY_EMBED_FILE_PHIDS = 'phabricator.embedded-file-phids';
public function apply($text) { protected function getObjectNamePrefix() {
return preg_replace_callback( return 'F';
"@{F(\d+)([^}]+?)?}@",
array($this, 'markupEmbedFile'),
$text);
} }
public function markupEmbedFile($matches) { protected function loadObjects(array $ids) {
$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];
}
$engine = $this->getEngine(); $engine = $this->getEngine();
if ($engine->isTextMode()) { $viewer = $engine->getConfig('viewer');
return $engine->storeText($file->getBestURI()); $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(''); protected function renderObjectEmbed($object, $handle, $options) {
$metadata_key = self::KEY_RULE_EMBED_FILE; $options = $this->getFileOptions($options) + array(
$metadata = $engine->getTextMetadata($metadata_key, array()); 'name' => $object->getName(),
$bundle = array('token' => $token); );
$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( $options = array(
'size' => 'thumb', 'size' => 'thumb',
'layout' => 'left', 'layout' => 'left',
'float' => false, 'float' => false,
'name' => null,
); );
if (!empty($matches[2])) { if ($option_string) {
$matches[2] = trim($matches[2], ', '); $option_string = trim($option_string, ', ');
$parser = new PhutilSimpleOptions(); $parser = new PhutilSimpleOptions();
$options = $parser->parse($matches[2]) + $options; $options = $parser->parse($option_string) + $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'),
);
} }
$metadata[$phid][] = $bundle; return $options;
$engine->setTextMetadata($metadata_key, $metadata);
return $token;
} }
public function didMarkupText() { private function renderImageFile(
$engine = $this->getEngine(); PhabricatorFile $file,
PhabricatorObjectHandle $handle,
array $options) {
$metadata_key = self::KEY_RULE_EMBED_FILE; require_celerity_resource('lightbox-attachment-css');
$metadata = $engine->getTextMetadata($metadata_key, array());
if (!$metadata) { $attrs = array();
return; $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(); $img = phutil_tag('img', $attrs);
foreach ($metadata as $phid => $bundles) {
foreach ($bundles as $data) {
$options = $data['options']; $embed = javelin_tag(
$meta = $data['meta']; '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'); switch ($options['layout']) {
$is_audio = idx($meta, 'audio'); 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') { if ($options['float']) {
$link = id(new PhabricatorFileLinkView()) switch ($options['layout']) {
->setFilePHID($meta['phid']) case 'center':
->setFileName($meta['name']) case 'inline':
->setFileDownloadURI($meta['dUri']) break;
->setFileViewURI($meta['uri']) case 'right':
->setFileViewable($meta['viewable']); $layout_class .= ' phabricator-remarkup-embed-float-right';
$embed = $link->render(); break;
$engine->overwriteStoredText($data['token'], $embed); case 'left':
continue; default:
} $layout_class .= ' phabricator-remarkup-embed-float-left';
break;
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);
} }
$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']);
} }
} }

View file

@ -846,6 +846,20 @@ final class PhabricatorFile extends PhabricatorFileDAO
return $this; 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 )-------------------------- */ /* -( PhabricatorPolicyInterface Implementation )-------------------------- */

View file

@ -41,7 +41,7 @@ final class PhabricatorMarkupEngine {
private $objects = array(); private $objects = array();
private $viewer; private $viewer;
private $version = 7; private $version = 8;
/* -( Markup Pipeline )---------------------------------------------------- */ /* -( Markup Pipeline )---------------------------------------------------- */