Commit afa88729 authored by Thomas Müller's avatar Thomas Müller
Browse files

Merge pull request #14857 from owncloud/preview-provider-registration-in-manager

Preview provider registration in manager
parents a45e45df 8f063538
......@@ -170,6 +170,7 @@ class ShareController extends Controller {
$shareTmpl['filename'] = $file;
$shareTmpl['directory_path'] = $linkItem['file_target'];
$shareTmpl['mimetype'] = Filesystem::getMimeType($originalSharePath);
$shareTmpl['previewSupported'] = \OC::$server->getPreviewManager()->isMimeSupported($shareTmpl['mimetype']);
$shareTmpl['dirToken'] = $linkItem['token'];
$shareTmpl['sharingToken'] = $token;
$shareTmpl['server2serversharing'] = Helper::isOutgoingServer2serverShareEnabled();
......
......@@ -19,11 +19,10 @@ OCP\Util::addScript('files', 'files');
OCP\Util::addScript('files', 'filelist');
OCP\Util::addscript('files', 'keyboardshortcuts');
$thumbSize=1024;
$previewSupported = OC\Preview::isMimeSupported($_['mimetype']) ? 'true' : 'false';
$thumbSize = 1024;
?>
<?php if ( \OC\Preview::isMimeSupported($_['mimetype'])): /* This enables preview images for links (e.g. on Facebook, Google+, ...)*/?>
<?php if ($_['previewSupported']): /* This enables preview images for links (e.g. on Facebook, Google+, ...)*/?>
<link rel="image_src" href="<?php p(OCP\Util::linkToRoute( 'core_ajax_public_preview', array('x' => $thumbSize, 'y' => $thumbSize, 'file' => $_['directory_path'], 't' => $_['dirToken']))); ?>" />
<?php endif; ?>
......@@ -38,7 +37,7 @@ $previewSupported = OC\Preview::isMimeSupported($_['mimetype']) ? 'true' : 'fals
<input type="hidden" name="sharingToken" value="<?php p($_['sharingToken']) ?>" id="sharingToken">
<input type="hidden" name="filename" value="<?php p($_['filename']) ?>" id="filename">
<input type="hidden" name="mimetype" value="<?php p($_['mimetype']) ?>" id="mimetype">
<input type="hidden" name="previewSupported" value="<?php p($previewSupported); ?>" id="previewSupported">
<input type="hidden" name="previewSupported" value="<?php p($_['previewSupported'] ? 'true' : 'false'); ?>" id="previewSupported">
<input type="hidden" name="mimetypeIcon" value="<?php p(OC_Helper::mimetypeIcon($_['mimetype'])); ?>" id="mimetypeIcon">
<input type="hidden" name="filesize" value="<?php p($_['nonHumanFileSize']); ?>" id="filesize">
<input type="hidden" name="maxSizeAnimateGif" value="<?php p($_['maxSizeAnimateGif']); ?>" id="maxSizeAnimateGif">
......
......@@ -158,6 +158,7 @@ class ShareControllerTest extends \Test\TestCase {
'fileSize' => '33 B',
'nonHumanFileSize' => 33,
'maxSizeAnimateGif' => 10,
'previewSupported' => true,
);
$csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
......
......@@ -29,13 +29,12 @@ if ($maxX === 0 || $maxY === 0) {
exit;
}
$preview = new \OC\Preview(\OC_User::getUser(), 'files');
$info = \OC\Files\Filesystem::getFileInfo($file);
if (!$info instanceof OCP\Files\FileInfo || !$always && !$preview->isAvailable($info)) {
if (!$info instanceof OCP\Files\FileInfo || !$always && !\OC::$server->getPreviewManager()->isAvailable($info)) {
\OC_Response::setStatus(404);
} else {
$preview = new \OC\Preview(\OC_User::getUser(), 'files');
$preview->setFile($file);
$preview->setMaxX($maxX);
$preview->setMaxY($maxY);
......
......@@ -95,7 +95,7 @@ class AvatarController extends Controller {
$avatar = $this->avatarManager->getAvatar($userId);
$image = $avatar->get($size);
if ($image instanceof \OC_Image) {
if ($image instanceof \OCP\IImage) {
$resp = new DataDisplayResponse($image->data(),
Http::STATUS_OK,
['Content-Type' => $image->mimeType()]);
......
......@@ -29,7 +29,7 @@ class Avatar implements \OCP\IAvatar {
/**
* get the users avatar
* @param int $size size in px of the avatar, avatars are square, defaults to 64
* @return boolean|\OC_Image containing the avatar or false if there's no image
* @return boolean|\OCP\IImage containing the avatar or false if there's no image
*/
public function get ($size = 64) {
if ($this->view->file_exists('avatar.jpg')) {
......@@ -57,14 +57,14 @@ class Avatar implements \OCP\IAvatar {
/**
* sets the users avatar
* @param \OC_Image|resource|string $data OC_Image, imagedata or path to set a new avatar
* @param \OCP\IImage|resource|string $data An image object, imagedata or path to set a new avatar
* @throws \Exception if the provided file is not a jpg or png image
* @throws \Exception if the provided image is not valid
* @throws \OC\NotSquareException if the image is not square
* @return void
*/
public function set ($data) {
if($data instanceOf OC_Image) {
if($data instanceOf \OCP\IImage) {
$img = $data;
$data = $img->data();
} else {
......
......@@ -15,7 +15,7 @@
/**
* Class for basic image manipulation
*/
class OC_Image {
class OC_Image implements \OCP\IImage {
protected $resource = false; // tmp resource.
protected $imageType = IMAGETYPE_PNG; // Default to png if file type isn't evident.
protected $mimeType = "image/png"; // Default to png
......@@ -285,7 +285,7 @@ class OC_Image {
/**
* @return null|string Returns the raw image data.
*/
function data() {
public function data() {
if (!$this->valid()) {
return null;
}
......@@ -949,6 +949,9 @@ class OC_Image {
return true;
}
/**
* Destroys the current image and resets the object
*/
public function destroy() {
if ($this->valid()) {
imagedestroy($this->resource);
......
......@@ -46,15 +46,10 @@ class Preview {
/**
* preview images object
*
* @var \OC_Image
* @var \OCP\IImage
*/
private $preview;
//preview providers
static private $providers = array();
static private $registeredProviders = array();
static private $enabledProviders = array();
/**
* @var \OCP\Files\FileInfo
*/
......@@ -95,11 +90,7 @@ class Preview {
$this->preview = null;
//check if there are preview backends
if (empty(self::$providers)) {
self::initProviders();
}
if (empty(self::$providers) && \OC::$server->getConfig()->getSystemValue('enable_previews', true)) {
if (!\OC::$server->getPreviewManager()->hasProviders() && \OC::$server->getConfig()->getSystemValue('enable_previews', true)) {
\OC_Log::write('core', 'No preview providers exist', \OC_Log::ERROR);
throw new \Exception('No preview providers');
}
......@@ -474,7 +465,7 @@ class Preview {
/**
* return a preview of a file
* @return \OC_Image
* @return \OCP\IImage
*/
public function getPreview() {
if (!is_null($this->preview) && $this->preview->valid()) {
......@@ -510,17 +501,24 @@ class Preview {
if (is_null($this->preview)) {
$preview = null;
foreach (self::$providers as $supportedMimeType => $provider) {
$previewProviders = \OC::$server->getPreviewManager()->getProviders();
foreach ($previewProviders as $supportedMimeType => $providers) {
if (!preg_match($supportedMimeType, $this->mimeType)) {
continue;
}
foreach ($providers as $closure) {
$provider = $closure();
if (!($provider instanceof \OCP\Preview\IProvider)) {
continue;
}
\OC_Log::write('core', 'Generating preview for "' . $file . '" with "' . get_class($provider) . '"', \OC_Log::DEBUG);
/** @var $provider Provider */
$preview = $provider->getThumbnail($file, $maxX, $maxY, $scalingUp, $this->fileView);
if (!($preview instanceof \OC_Image)) {
if (!($preview instanceof \OCP\IImage)) {
continue;
}
......@@ -540,7 +538,8 @@ class Preview {
$this->userView->file_put_contents($cachePath, $preview->data());
break;
break 2;
}
}
}
......@@ -565,7 +564,7 @@ class Preview {
if (is_null($this->preview)) {
$this->getPreview();
}
if ($this->preview instanceof \OC_Image) {
if ($this->preview instanceof \OCP\IImage) {
$this->preview->show($mimeType);
}
}
......@@ -581,8 +580,8 @@ class Preview {
$scalingUp = $this->getScalingUp();
$maxScaleFactor = $this->getMaxScaleFactor();
if (!($image instanceof \OC_Image)) {
\OC_Log::write('core', '$this->preview is not an instance of OC_Image', \OC_Log::DEBUG);
if (!($image instanceof \OCP\IImage)) {
\OC_Log::write('core', '$this->preview is not an instance of \OCP\IImage', \OC_Log::DEBUG);
return;
}
......@@ -685,146 +684,6 @@ class Preview {
}
}
/**
* register a new preview provider to be used
* @param string $class
* @param array $options
*/
public static function registerProvider($class, $options = array()) {
/**
* Only register providers that have been explicitly enabled
*
* The following providers are enabled by default:
* - OC\Preview\Image
* - OC\Preview\MP3
* - OC\Preview\TXT
* - OC\Preview\MarkDown
*
* The following providers are disabled by default due to performance or privacy concerns:
* - OC\Preview\MSOfficeDoc
* - OC\Preview\MSOffice2003
* - OC\Preview\MSOffice2007
* - OC\Preview\OpenDocument
* - OC\Preview\StarOffice
* - OC\Preview\SVG
* - OC\Preview\Movie
* - OC\Preview\PDF
* - OC\Preview\TIFF
* - OC\Preview\Illustrator
* - OC\Preview\Postscript
* - OC\Preview\Photoshop
* - OC\Preview\Font
*/
if(empty(self::$enabledProviders)) {
self::$enabledProviders = \OC::$server->getConfig()->getSystemValue('enabledPreviewProviders', array(
'OC\Preview\Image',
'OC\Preview\MP3',
'OC\Preview\TXT',
'OC\Preview\MarkDown',
));
}
if(in_array($class, self::$enabledProviders)) {
self::$registeredProviders[] = array('class' => $class, 'options' => $options);
}
}
/**
* create instances of all the registered preview providers
* @return void
*/
private static function initProviders() {
if (!\OC::$server->getConfig()->getSystemValue('enable_previews', true)) {
self::$providers = array();
return;
}
if (!empty(self::$providers)) {
return;
}
self::registerCoreProviders();
foreach (self::$registeredProviders as $provider) {
$class = $provider['class'];
$options = $provider['options'];
/** @var $object Provider */
$object = new $class($options);
self::$providers[$object->getMimeType()] = $object;
}
$keys = array_map('strlen', array_keys(self::$providers));
array_multisort($keys, SORT_DESC, self::$providers);
}
protected static function registerCoreProviders() {
self::registerProvider('OC\Preview\TXT');
self::registerProvider('OC\Preview\MarkDown');
self::registerProvider('OC\Preview\Image');
self::registerProvider('OC\Preview\MP3');
// SVG, Office and Bitmap require imagick
if (extension_loaded('imagick')) {
$checkImagick = new \Imagick();
$imagickProviders = array(
'SVG' => 'OC\Preview\SVG',
'TIFF' => 'OC\Preview\TIFF',
'PDF' => 'OC\Preview\PDF',
'AI' => 'OC\Preview\Illustrator',
'PSD' => 'OC\Preview\Photoshop',
'EPS' => 'OC\Preview\Postscript',
'TTF' => 'OC\Preview\Font',
);
foreach ($imagickProviders as $queryFormat => $provider) {
if (count($checkImagick->queryFormats($queryFormat)) === 1) {
self::registerProvider($provider);
}
}
if (count($checkImagick->queryFormats('PDF')) === 1) {
// Office previews are currently not supported on Windows
if (!\OC_Util::runningOnWindows() && \OC_Helper::is_function_enabled('shell_exec')) {
$officeFound = is_string(\OC::$server->getConfig()->getSystemValue('preview_libreoffice_path', null));
if (!$officeFound) {
//let's see if there is libreoffice or openoffice on this machine
$whichLibreOffice = shell_exec('command -v libreoffice');
$officeFound = !empty($whichLibreOffice);
if (!$officeFound) {
$whichOpenOffice = shell_exec('command -v openoffice');
$officeFound = !empty($whichOpenOffice);
}
}
if ($officeFound) {
self::registerProvider('OC\Preview\MSOfficeDoc');
self::registerProvider('OC\Preview\MSOffice2003');
self::registerProvider('OC\Preview\MSOffice2007');
self::registerProvider('OC\Preview\OpenDocument');
self::registerProvider('OC\Preview\StarOffice');
}
}
}
}
// Video requires avconv or ffmpeg and is therefor
// currently not supported on Windows.
if (!\OC_Util::runningOnWindows()) {
$avconvBinary = \OC_Helper::findBinaryPath('avconv');
$ffmpegBinary = ($avconvBinary) ? null : \OC_Helper::findBinaryPath('ffmpeg');
if ($avconvBinary || $ffmpegBinary) {
// FIXME // a bit hacky but didn't want to use subclasses
\OC\Preview\Movie::$avconvBinary = $avconvBinary;
\OC\Preview\Movie::$ffmpegBinary = $ffmpegBinary;
self::registerProvider('OC\Preview\Movie');
}
}
}
/**
* @param array $args
*/
......@@ -914,60 +773,6 @@ class Preview {
$preview->deleteAllPreviews();
}
/**
* Check if a preview can be generated for a file
*
* @param \OC\Files\FileInfo $file
* @return bool
*/
public static function isAvailable(\OC\Files\FileInfo $file) {
if (!\OC_Config::getValue('enable_previews', true)) {
return false;
}
$mount = $file->getMountPoint();
if ($mount and !$mount->getOption('previews', true)){
return false;
}
//check if there are preview backends
if (empty(self::$providers)) {
self::initProviders();
}
foreach (self::$providers as $supportedMimeType => $provider) {
/**
* @var \OC\Preview\Provider $provider
*/
if (preg_match($supportedMimeType, $file->getMimetype())) {
return $provider->isAvailable($file);
}
}
return false;
}
/**
* @param string $mimeType
* @return bool
*/
public static function isMimeSupported($mimeType) {
if (!\OC_Config::getValue('enable_previews', true)) {
return false;
}
//check if there are preview backends
if (empty(self::$providers)) {
self::initProviders();
}
foreach(self::$providers as $supportedMimetype => $provider) {
if(preg_match($supportedMimetype, $mimeType)) {
return true;
}
}
return false;
}
/**
* @param int $fileId
* @return string
......
......@@ -61,7 +61,7 @@ class Movie extends Provider {
* @param int $maxY
* @param string $absPath
* @param int $second
* @return bool|\OC_Image
* @return bool|\OCP\IImage
*/
private function generateThumbNail($maxX, $maxY, $absPath, $second) {
$tmpPath = \OC_Helper::tmpFile();
......
......@@ -39,8 +39,7 @@ class MP3 extends Provider {
/**
* Generates a default image when the file has no cover
*
* @return false|\OC_Image False if the default image is missing or invalid,
* otherwise the image is returned as \OC_Image
* @return bool|\OCP\IImage false if the default image is missing or invalid
*/
private function getNoCoverThumbnail() {
$icon = \OC::$SERVERROOT . '/core/img/filetypes/audio.png';
......
<?php
namespace OC\Preview;
abstract class Provider {
use OCP\Preview\IProvider;
abstract class Provider implements IProvider {
private $options;
public function __construct($options) {
/**
* Constructor
*
* @param array $options
*/
public function __construct(array $options = []) {
$this->options = $options;
}
......@@ -16,10 +23,10 @@ abstract class Provider {
/**
* Check if a preview can be generated for $path
*
* @param \OC\Files\FileInfo $file
* @param \OCP\Files\FileInfo $file
* @return bool
*/
public function isAvailable($file) {
public function isAvailable(\OCP\Files\FileInfo $file) {
return true;
}
......@@ -30,9 +37,7 @@ abstract class Provider {
* @param int $maxY The maximum Y size of the thumbnail. It can be smaller depending on the shape of the image
* @param bool $scalingup Disable/Enable upscaling of previews
* @param \OC\Files\View $fileview fileview object of user folder
* @return mixed
* false if no preview was generated
* OC_Image object of the preview
* @return bool|\OCP\IImage false if no preview was generated
*/
abstract public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview);
}
......@@ -18,7 +18,7 @@ class TXT extends Provider {
/**
* {@inheritDoc}
*/
public function isAvailable($file) {
public function isAvailable(\OCP\Files\FileInfo $file) {
return $file->getSize() > 5;
}
......
......@@ -8,10 +8,87 @@
*/
namespace OC;
use OCP\image;
use OCP\IPreview;
use OCP\Preview\IProvider;
class PreviewManager implements IPreview {
/** @var \OCP\IConfig */
protected $config;
/** @var bool */
protected $providerListDirty = false;
/** @var bool */
protected $registeredCoreProviders = false;
/** @var array */
protected $providers = [];
/** @var array mime type => support status */
protected $mimeTypeSupportMap = [];
/** @var array */
protected $defaultProviders;
/**
* Constructor
*
* @param \OCP\IConfig $config
*/
public function __construct(\OCP\IConfig $config) {
$this->config = $config;
}
/**
* In order to improve lazy loading a closure can be registered which will be
* called in case preview providers are actually requested
*
* $callable has to return an instance of \OCP\Preview\IProvider
*
* @param string $mimeTypeRegex Regex with the mime types that are supported by this provider
* @param \Closure $callable
* @return void
*/
public function registerProvider($mimeTypeRegex, \Closure $callable) {
if (!$this->config->getSystemValue('enable_previews', true)) {
return;
}
if (!isset($this->providers[$mimeTypeRegex])) {
$this->providers[$mimeTypeRegex] = [];
}
$this->providers[$mimeTypeRegex][] = $callable;
$this->providerListDirty = true;
}
/**
* Get all providers
* @return array
*/
public function getProviders() {
if (!$this->config->getSystemValue('enable_previews', true)) {
return [];
}
$this->registerCoreProviders();
if ($this->providerListDirty) {
$keys = array_map('strlen', array_keys($this->providers));
array_multisort($keys, SORT_DESC, $this->providers);
$this->providerListDirty = false;
}
return $this->providers;
}
/**
* Does the manager have any providers
* @return bool
*/
public function hasProviders() {
$this->registerCoreProviders();
return !empty($this->providers);
}
/**
* return a preview of a file
*
......@@ -19,9 +96,9 @@ class PreviewManager implements IPreview {
* @param int $maxX The maximum X size of the thumbnail. It can be smaller depending on the shape of the image
* @param int $maxY The maximum Y size of the thumbnail. It can be smaller depending on the shape of the image
* @param boolean $scaleUp Scale smaller images up to the thumbnail size or not. Might look ugly
* @return \OCP\Image
* @return \OCP\IImage
*/
function createPreview($file, $maxX = 100, $maxY = 75, $scaleUp = false) {
public function createPreview($file, $maxX = 100, $maxY = 75, $scaleUp = false) {
$preview = new \OC\Preview('', '/', $file, $maxX, $maxY, $scaleUp);
return $preview->getPreview();
}
......@@ -32,17 +109,198 @@ class PreviewManager implements IPreview {
* @param string $mimeType
* @return boolean
*/
function isMimeSupported($mimeType = '*') {
return \OC\Preview::isMimeSupported($mimeType);
public function isMimeSupported($mimeType = '*') {
if (!$this->config->getSystemValue('enable_previews', true)) {
return false;
}
if (isset($this->mimeTypeSupportMap[$mimeType])) {
return $this->mimeTypeSupportMap[$mimeType];
}
$this->registerCoreProviders();
$providerMimeTypes = array_keys($this->providers);
foreach ($providerMimeTypes as $supportedMimeType) {
if (preg_match($supportedMimeType, $mimeType)) {
$this->mimeTypeSupportMap[$mimeType] = true;
return true;
}
}