Commit 32e2c40d authored by Carsten  Rose's avatar Carsten Rose
Browse files

Feature 5425 / thumbnail: rewrite - secure thumbnails are now rendered on...

Feature 5425 / thumbnail: rewrite - secure thumbnails are now rendered on first access, not when 'AS _thumbnail' is called.
parent 7037ad96
...@@ -361,16 +361,18 @@ A download might be: ...@@ -361,16 +361,18 @@ A download might be:
* an export of a T3-'XML'-Page converted to Excel, * an export of a T3-'XML'-Page converted to Excel,
* a converted HTML page to PDF, * a converted HTML page to PDF,
* a PDF file, concatenated on single PDF files and/or converted HTML page to PDF. * a PDF file, concatenated on single PDF files and/or converted HTML page to PDF.
* a thumbnail, streamed from cache dir of if not present/recent rendered on request.
'api/download.php' will be called with a SIP (no other vars used). The SIP contains: 'api/download.php' will be called with a SIP (no other vars used). The SIP contains:
* DOWNLOAD_EXPORT_FILENAME - any target filename, if none given take DOWNLOAD_OUTPUT_PDF ('output.pdf'). * DOWNLOAD_EXPORT_FILENAME - any target filename, if none given take DOWNLOAD_OUTPUT_PDF ('output.pdf').
* DONWLOAD_MODE - file / pdf / excel / zip. If not specified: * DONWLOAD_MODE - file / pdf / excel / zip / thumbnail. If not specified:
a) 'file' is the default, if only one source is given and if that is a file. a) 'file' is the default, if only one source is given and if that is a file.
b) 'pdf' is the default, if there are multiple TOKEN_URL, TOKEN_URL_PARAM, TOKEN_FILE in SIP_DOWNLOAD_PARAMETER found. b) 'pdf' is the default, if there are multiple TOKEN_URL, TOKEN_URL_PARAM, TOKEN_FILE in SIP_DOWNLOAD_PARAMETER found.
* SIP_DOWNLOAD_PARAMETER (base64 encoded) - contains all parameter to source elements.
* SIP_DOWNLOAD_PARAMETER (base64 encoded): all DONWLOAD_MODE but 'thumbnail' - contains all parameter to source elements.
Format: <format 1>:<element 1>|<format 2>:<element 2>|...|<format n>:<element n>| Format: <format 1>:<element 1>|<format 2>:<element 2>|...|<format n>:<element n>|
<format>: TOKEN_URL, TOKEN_URL_PARAM, TOKEN_FILE <format>: TOKEN_URL, TOKEN_URL_PARAM, TOKEN_FILE, TOKEN_THUMBNAIL_DIMENSION
<element>: depending on the token - see below <element>: depending on the token - see below
URL: a) 'u:http://w3c.org', b) 'u:w3c.org/', c) 'u:w3c.org/2017/index.php?issue=23' URL: a) 'u:http://w3c.org', b) 'u:w3c.org/', c) 'u:w3c.org/2017/index.php?issue=23'
...@@ -379,6 +381,14 @@ A download might be: ...@@ -379,6 +381,14 @@ A download might be:
* In URL_PARAM extra parameter used by `wkhtmltopdf` can be specified. All Parameter, starting with '-' * In URL_PARAM extra parameter used by `wkhtmltopdf` can be specified. All Parameter, starting with '-'
will be extracted from the regular URL_PARAM and instead forwarded as options to `wkhtmlpdf` will be extracted from the regular URL_PARAM and instead forwarded as options to `wkhtmlpdf`
* SIP_DOWNLOAD_PARAMETER (base64 encoded): *DONWLOAD_MODE:thumbnail*
* T:<pathFilename Source>
* W:<dimension>
* r:<render mode>
* Render the thumbnail
Download.php will be called with the SIP. After decoding the SIP, the base64 encoded parameter are used with
DONWLOAD_MODE=file and SIP_DOWNLOAD_PARAMETER=F:<thumbnail>
* The base64 encoding is necessary: * The base64 encoding is necessary:
* to deliver multiple elements with the same token (e.g. multiple PDF files to concatenate). * to deliver multiple elements with the same token (e.g. multiple PDF files to concatenate).
......
...@@ -52,3 +52,8 @@ Form ...@@ -52,3 +52,8 @@ Form
Inside of a form with the new feature #5422 {{COLUMN '...' AS _thumbnail}} Inside of a form with the new feature #5422 {{COLUMN '...' AS _thumbnail}}
Workflow
--------
Report creates a SIP:
\ No newline at end of file
...@@ -163,6 +163,8 @@ Thumbnails will be rendered via GraphicsMagick (http://www.graphicsmagick.org/) ...@@ -163,6 +163,8 @@ Thumbnails will be rendered via GraphicsMagick (http://www.graphicsmagick.org/)
The Typo3 grafic eco-system is not used at all by QFQ. The Typo3 grafic eco-system is not used at all by QFQ.
Usage: `column-thumbnail`_.
Setup Setup
----- -----
...@@ -1674,6 +1676,10 @@ General ...@@ -1674,6 +1676,10 @@ General
* Saving the form will update the existing record. * Saving the form will update the existing record.
* E.g.: `http://example.com/index.php?id=home&form=Person&r=123` * E.g.: `http://example.com/index.php?id=home&form=Person&r=123`
* Providing additional parameter:
Often, it is necessary to store additional, for the user not visible, parameter in a record. See `form-magic`_.
* Display a form: * Display a form:
* Create a QFQ tt_content record on a Typo 3 page. * Create a QFQ tt_content record on a Typo 3 page.
...@@ -2778,7 +2784,9 @@ Type: datetime ...@@ -2778,7 +2784,9 @@ Type: datetime
Type: extra Type: extra
^^^^^^^^^^^ ^^^^^^^^^^^
* Element is not shown in the browser. * The element is not transferred to the the browser.
* The element behaves like, and can be used as, a HTML hidden input element - with the advantage that the element never
leaves the server and therefore can't be manipulated by a user.
* The element can be used to define / precalculate values for a column, which do not already exist as a native *FormElement*. * The element can be used to define / precalculate values for a column, which do not already exist as a native *FormElement*.
* The element is build / computed on form load and saved alongside with the SIP parameter of the current form. * The element is build / computed on form load and saved alongside with the SIP parameter of the current form.
* Access the value without specifying any store (default store priority is sufficient). * Access the value without specifying any store (default store priority is sufficient).
...@@ -3559,6 +3567,57 @@ See also `copy-form`_. ...@@ -3559,6 +3567,57 @@ See also `copy-form`_.
* *recordDestinationTable* - table where the new records will be copied to. * *recordDestinationTable* - table where the new records will be copied to.
* *translateIdColumn* - columnname to update references of newly created id's. * *translateIdColumn* - columnname to update references of newly created id's.
.. _form-magic:
Form Magic
----------
Parameter
'''''''''
* Table column `id`: QFQ expect that each table, which will be loaded in a form, contains an autoincrement column of name `id`.
It's not necessary to create a FormElement `id` in a form - but it won't disturb.
* Parameter (one or more) in the SIP url, which exist as a column in the form table (SIP parameter name is equal to a table column name),
will be automatically saved in the record. This acts as 'hidden magic'.
Example: A slave record (e.g. an address of a person) has to be assigned to a master record (a person). Just give the
`pId` in the link who calls the address form. The following creates a 'new' button for an address for all persons, and
the pId will be automatically saved in the address table: ::
SELECT CONCAT('{{pageAlias:T}}&form=address&r=0&pId=', p.id) AS _pagen FROM Person AS p
Such parameter, which the form expects to be in the SIP url, should be specified in Form.permitNew and/or Form.permitEdit.
It's only a check for the webmaster, not to forgot a parameter in a SIP url.
* FormElement.type = subrecord
Subrecord's will automatically create `new`, `edit` and `delete` links. To inject parameter in those automatically created
links, use `FormElement.parameter.detail` . See `subrecord-option`.
* FormElement.type = extra
If a table column should be saved with a specific value, and the value should not be shown to the user, the FE.type='extra'
will do the job. The value could be static or calculated on the fly. Often it's easier to specify such a parameter/value
in the SIP url, but if the form is called from multiple places, an `extra` element is more suitable.
Variables
'''''''''
* Form.parameter.fillStoreVar / FormElement.parameter.fillStoreVar
A SQL statement will fill STORE_VARS. Such values can be used during form load and/or save.
Action
''''''
* Action FE
Via `FormElement.parameter.requiredList` an element can be enabled / disabled, depending of a user provided input
in one of the specified required FEs.
.. _multi-language-form: .. _multi-language-form:
Multi Language Form Multi Language Form
...@@ -5841,15 +5900,15 @@ Column: _thumbnail ...@@ -5841,15 +5900,15 @@ Column: _thumbnail
^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^
A thumbnail of the file `T:<pathFilename>` will be rendered and saved with the given pixel size as specified via A thumbnail of the file `T:<pathFilename>` will be rendered and saved with the given pixel size as specified via
`W:<dimension>`. The file is only rendered once and will be rerendered, if the source file is newer than the thumbnail `W:<dimension>`. The file is only rendered once and subsequent access is delivered via a local cache. The will be
or if the thumbnail dimension changes. rendered again, if the source file is newer than the thumbnail or if the thumbnail dimension changes.
The thumbnail pathFilename is a MD5 hash of the pathFilename plus the dimension. The thumbnail pathFilename is a MD5 hash of the pathFilename plus the dimension information.
From multi page files like PDFs, the first page is used. From multi page files like PDFs, the first page is used as the thumbnail.
All file formats, which GraphicsMagick 'convert' (http://www.graphicsmagick.org/formats.html) supports, can be All file formats, which GraphicsMagick 'convert' (http://www.graphicsmagick.org/formats.html) supports, can be
used. Office file formats are not supported. Due to speed and quality reasons, SVG files will be converted by inkscape. used. Office file formats are not supported. Due to speed and quality reasons, SVG files will be converted by inkscape.
If a file format is not known, QFQ tries to show a corresponding file type image provided by Typo3 - such an image ist not If a file format is not known, QFQ tries to show a corresponding file type image provided by Typo3 - such an image is not
scaled. scaled.
In `config.qfq.ini`_ the exact location of `convert` and `inkscape` can be configured (optional) as well as the directory In `config.qfq.ini`_ the exact location of `convert` and `inkscape` can be configured (optional) as well as the directory
...@@ -5869,6 +5928,10 @@ names for the cached thumbnails. ...@@ -5869,6 +5928,10 @@ names for the cached thumbnails.
| r | r:7 | Render Mode. Default 'r:0'. With 'r:7' only the url will be delivered. | | r | r:7 | Render Mode. Default 'r:0'. With 'r:7' only the url will be delivered. |
+-------+--------------------------------+----------------------------------------------------------------------------+ +-------+--------------------------------+----------------------------------------------------------------------------+
The render mode '7' is useful, if the URL of the thumbnail have to be used in another way than the provided html-'<img>'
tag. Something like `<body style="background-image:url(bgimage.jpg)">` could be solved with
`SELECT "<body style="background-image:url(", 'T:fileadmin/file3.pdf' AS _thumbnail, ')">'`
Example: :: Example: ::
# SIP protected, IMG tag, thumbnail width 150px # SIP protected, IMG tag, thumbnail width 150px
...@@ -5897,8 +5960,13 @@ older than 1 year: :: ...@@ -5897,8 +5960,13 @@ older than 1 year: ::
find /path/to/files -type f -mtime +365 -delete find /path/to/files -type f -mtime +365 -delete
Pre render Render
'''''''''' ''''''
`Public` thumbnails are rendered at the time when the T3 QFQ record is executed. `Secure` thumbnails are rendered when the
'download.php?s=...' is called. The difference is, that the 'public' thumbnails blocks the page load until all thumbnails
are rendered, instead the `secure` thumbnails are loaded asynchonous via the browser - the main page is already delivered to
browser, all thumbnails appearing after a time.
A way to *pre render* thumbnails, is a periodically called (hidden) T3 page, which iterates over all new uploaded files and A way to *pre render* thumbnails, is a periodically called (hidden) T3 page, which iterates over all new uploaded files and
triggers the rendering via column `_thumbnail`. triggers the rendering via column `_thumbnail`.
......
...@@ -1128,6 +1128,8 @@ const EXISTING_PATH_FILE_NAME = '_existingPathFileName'; ...@@ -1128,6 +1128,8 @@ const EXISTING_PATH_FILE_NAME = '_existingPathFileName';
const THUMBNAIL_WIDTH_DEFAULT = '150x'; const THUMBNAIL_WIDTH_DEFAULT = '150x';
const THUMBNAIL_UNKNOWN_TYPE = 'typo3/sysext/frontend/Resources/Public/Icons/FileIcons/'; const THUMBNAIL_UNKNOWN_TYPE = 'typo3/sysext/frontend/Resources/Public/Icons/FileIcons/';
const THUMBNAIL_MAX_SECONDS = 60; const THUMBNAIL_MAX_SECONDS = 60;
const THUMBNAIL_PREPARE = 'prepare';
const THUMBNAIL_VIA_DOWNLOAD = 'secureFile';
//SENDMAIL //SENDMAIL
const SENDMAIL_TOKEN_RECEIVER = 't'; const SENDMAIL_TOKEN_RECEIVER = 't';
...@@ -1240,6 +1242,7 @@ const DOWNLOAD_MODE_FILE = 'file'; ...@@ -1240,6 +1242,7 @@ const DOWNLOAD_MODE_FILE = 'file';
const DOWNLOAD_MODE_PDF = 'pdf'; const DOWNLOAD_MODE_PDF = 'pdf';
const DOWNLOAD_MODE_EXCEL = 'excel'; const DOWNLOAD_MODE_EXCEL = 'excel';
const DOWNLOAD_MODE_ZIP = 'zip'; const DOWNLOAD_MODE_ZIP = 'zip';
const DOWNLOAD_MODE_THUMBNAIL = 'thumbnail';
const DOWNLOAD_EXPORT_FILENAME = '_exportFilename'; const DOWNLOAD_EXPORT_FILENAME = '_exportFilename';
const TMP_FILE_PREFIX = 'qfq.temp.'; // temporary filename on server of single export file const TMP_FILE_PREFIX = 'qfq.temp.'; // temporary filename on server of single export file
const DOWNLOAD_OUTPUT_FILENAME = 'output'; const DOWNLOAD_OUTPUT_FILENAME = 'output';
......
...@@ -1402,9 +1402,10 @@ class Support { ...@@ -1402,9 +1402,10 @@ class Support {
* *
* @param string $cmd : command to start * @param string $cmd : command to start
* *
* @return string The content that is displayed on the website * @param int $rc
* @return string The content that is displayed on the website
*/ */
public function myExec($cmd) { public static function qfqExec($cmd, &$rc = 0) {
exec($cmd, $arr, $rc); exec($cmd, $arr, $rc);
...@@ -1415,5 +1416,4 @@ class Support { ...@@ -1415,5 +1416,4 @@ class Support {
return ($output); return ($output);
} }
} }
\ No newline at end of file
...@@ -125,25 +125,4 @@ class Token { ...@@ -125,25 +125,4 @@ class Token {
return $width . $height; return $width . $height;
} }
/**
* Executes the Command in $cmd
* RC: if RC==0 Returns Output, else 'RC - Output'
*
* @param string $cmd : command to start
*
* @param int $rc
* @return string The content that is displayed on the website
*/
public static function qfqExec($cmd, &$rc = 0) {
exec($cmd, $arr, $rc);
$output = implode('<br>', $arr);
if ($rc != 0) {
$output = $rc . " - " . $output;
}
return ($output);
}
} }
\ No newline at end of file
...@@ -20,7 +20,7 @@ require_once(__DIR__ . '/../helper/Logger.php'); ...@@ -20,7 +20,7 @@ require_once(__DIR__ . '/../helper/Logger.php');
require_once(__DIR__ . '/../helper/Sanitize.php'); require_once(__DIR__ . '/../helper/Sanitize.php');
require_once(__DIR__ . '/../helper/HelperFile.php'); require_once(__DIR__ . '/../helper/HelperFile.php');
require_once(__DIR__ . '/../report/Html2Pdf.php'); require_once(__DIR__ . '/../report/Html2Pdf.php');
//require_once(__DIR__ . '/Link.php'); require_once(__DIR__ . '/Thumbnail.php');
//require_once(__DIR__ . '/Sendmail.php'); //require_once(__DIR__ . '/Sendmail.php');
require_once(__DIR__ . '/../exceptions/DownloadException.php'); require_once(__DIR__ . '/../exceptions/DownloadException.php');
//require_once(__DIR__ . '/../Evaluate.php'); //require_once(__DIR__ . '/../Evaluate.php');
...@@ -150,16 +150,6 @@ class Download { ...@@ -150,16 +150,6 @@ class Download {
$rcMimetype = mime_content_type($filename); $rcMimetype = mime_content_type($filename);
// See #4303 / Bug: Download von doc/docx-Dateien - the following approach makes more trouble than it helps. Removed completely.
// Main motivation: protect against hardcoded filename extension in FE.parameter.fileDestination
// In case there is a wrong filenameextension on the outputFilename: extend it.
// $ext = '.' . substr($rcMimetype, strrpos($rcMimetype, '/') + 1); // very very dirty way of getting an extension - only valid for a limited set of mimetypes
// $len = strlen($ext);
// if (substr($outputFilename, 0 - $len) != $ext) {
// $outputFilename .= $ext;
// }
return $outputFilename; return $outputFilename;
} }
...@@ -172,6 +162,7 @@ class Download { ...@@ -172,6 +162,7 @@ class Download {
private function outputFile($file, $outputFilename) { private function outputFile($file, $outputFilename) {
$length = filesize($file); $length = filesize($file);
$outputFilename = $this->targetFilenameExtension($file, $outputFilename, $mimetype); $outputFilename = $this->targetFilenameExtension($file, $outputFilename, $mimetype);
$outputFilename = Sanitize::safeFilename($outputFilename); // be sure that there are no problematic chars in the filename. E.g. MacOS X don't like spaces for downloads. $outputFilename = Sanitize::safeFilename($outputFilename); // be sure that there are no problematic chars in the filename. E.g. MacOS X don't like spaces for downloads.
...@@ -290,6 +281,14 @@ class Download { ...@@ -290,6 +281,14 @@ class Download {
} }
$downloadMode = $vars[DOWNLOAD_MODE]; $downloadMode = $vars[DOWNLOAD_MODE];
if ($downloadMode == DOWNLOAD_MODE_THUMBNAIL) {
// Fake $vars control array.
$pathFilenameThumbnail = $this->doThumbnail($vars[SIP_DOWNLOAD_PARAMETER]);
$downloadMode = DOWNLOAD_MODE_FILE;
$vars[SIP_DOWNLOAD_PARAMETER] = TOKEN_FILE . ':' . $pathFilenameThumbnail;
}
$elements = explode(PARAM_DELIMITER, $vars[SIP_DOWNLOAD_PARAMETER]); $elements = explode(PARAM_DELIMITER, $vars[SIP_DOWNLOAD_PARAMETER]);
// Get all files // Get all files
...@@ -355,6 +354,19 @@ class Download { ...@@ -355,6 +354,19 @@ class Download {
return $filename; return $filename;
} }
/**
* @param string $urlParam
* @return string
* @throws UserReportException
*/
private function doThumbnail($urlParam) {
$thumbnail = new Thumbnail();
$pathFilenameThumbnail = $thumbnail->process($urlParam, THUMBNAIL_VIA_DOWNLOAD);
return $pathFilenameThumbnail;
}
/** /**
* Process download as requested in $vars. Output is either directly send to the browser, or a file which has to be deleted later. * Process download as requested in $vars. Output is either directly send to the browser, or a file which has to be deleted later.
* *
...@@ -374,7 +386,6 @@ class Download { ...@@ -374,7 +386,6 @@ class Download {
return $this->doElements($vars, $outputMode); return $this->doElements($vars, $outputMode);
} }
}
}
...@@ -1059,7 +1059,7 @@ EOF; ...@@ -1059,7 +1059,7 @@ EOF;
$this->thumbnail = new Thumbnail(); $this->thumbnail = new Thumbnail();
} }
return $this->thumbnail->getImageTag($str); return $this->thumbnail->process($str);
} }
/** /**
......
...@@ -649,7 +649,7 @@ class Report { ...@@ -649,7 +649,7 @@ class Report {
case COLUMN_EXEC: case COLUMN_EXEC:
$rc = ''; $rc = '';
$content .= Token::qfqExec($columnValue, $rc); $content .= Support::qfqExec($columnValue, $rc);
break; break;
// Uppercase 'P' // Uppercase 'P'
...@@ -700,7 +700,7 @@ class Report { ...@@ -700,7 +700,7 @@ class Report {
if ($this->thumbnail == null) { if ($this->thumbnail == null) {
$this->thumbnail = new Thumbnail(); $this->thumbnail = new Thumbnail();
} }
$content .= $this->thumbnail->getImageTag($columnValue); $content .= $this->thumbnail->process($columnValue);
break; break;
case COLUMN_NL2BR: case COLUMN_NL2BR:
......
...@@ -49,12 +49,12 @@ class Thumbnail { ...@@ -49,12 +49,12 @@ class Thumbnail {
* Argument 's' is optional. Defaults to 's:1' * Argument 's' is optional. Defaults to 's:1'
* *
* @param string $str * @param string $str
* @param string $modeRender
* @return string * @return string
*
* @throws UserFormException * @throws UserFormException
* @throws UserReportException * @throws UserReportException
*/ */
public function getImageTag($str) { public function process($str, $modeRender = THUMBNAIL_PREPARE) {
$control = Token::explodeTokenString($str); $control = Token::explodeTokenString($str);
Support::setIfNotSet($control, TOKEN_SIP); Support::setIfNotSet($control, TOKEN_SIP);
...@@ -73,20 +73,27 @@ class Thumbnail { ...@@ -73,20 +73,27 @@ class Thumbnail {
$dir = ($control[TOKEN_SIP] == "1") ? $this->thumbnailDirSecure : $this->thumbnailDirPublic; $dir = ($control[TOKEN_SIP] == "1") ? $this->thumbnailDirSecure : $this->thumbnailDirPublic;
$pathFilenameThumbnail = Support::joinPath($dir, md5($pathFilenameSource . $control[TOKEN_THUMBNAIL_DIMENSION]) . '.png'); $pathFilenameThumbnail = Support::joinPath($dir, md5($pathFilenameSource . $control[TOKEN_THUMBNAIL_DIMENSION]) . '.png');
$pathFilenameThumbnail = $this->doThumbnail($pathFilenameSource, $pathFilenameThumbnail, $control[TOKEN_THUMBNAIL_DIMENSION]); // Check if the file has to exist now.
if ($modeRender == THUMBNAIL_VIA_DOWNLOAD || $control[TOKEN_SIP] != "1") {
$pathFilenameThumbnail = $this->getOrCreateThumbnail($pathFilenameSource, $pathFilenameThumbnail, $control, $modeRender);
}
return $this->buildImageTag($pathFilenameThumbnail, $control); return $this->buildImageTag($str, $modeRender, $control, $pathFilenameThumbnail);
} }
/** /**
* Creates a thumbnail (saved under $pathFilenameThumbnail) based on $pathFilenameSource.
* Returns the pathFilename of the thumbnail.
*
* @param string $pathFilenameSource * @param string $pathFilenameSource
* @param string $pathFilenameThumbnail * @param string $pathFilenameThumbnail
* @param string $dimension * @param array $control
* @param string $modeRender DOWNLOAD_RENDER_AUTO | DOWNLOAD_RENDER_NOW
* @return string * @return string
* @throws UserFormException * @throws UserFormException
* @throws UserReportException * @throws UserReportException
*/ */
private function doThumbnail($pathFilenameSource, $pathFilenameThumbnail, $dimension) { private function getOrCreateThumbnail($pathFilenameSource, $pathFilenameThumbnail, array $control, $modeRender) {
$debugMode = false; $debugMode = false;
$statSource = stat($pathFilenameSource); $statSource = stat($pathFilenameSource);
...@@ -96,20 +103,36 @@ class Thumbnail { ...@@ -96,20 +103,36 @@ class Thumbnail {
throw new UserFormException('File not found: "' . OnString::strrstr($pathFilenameSource, '/') . '"', ERROR_IO_FILE_NOT_FOUND); throw new UserFormException('File not found: "' . OnString::strrstr($pathFilenameSource, '/') . '"', ERROR_IO_FILE_NOT_FOUND);
} }
if (isset($statThumbnail['size']) && $statThumbnail['size'] == 0) { // thumbnail already exist?
if ($statThumbnail !== false) {
// Another process already creating this thumbnail? Just wait until it's finished.
if ($statThumbnail['size'] == 0) {
if (time() - $statThumbnail['mtime'] > THUMBNAIL_MAX_SECONDS) {
unlink($pathFilenameThumbnail); // remove old empty files: this gives a chance to rerender the image on the next call.
return 'brokenimage';
}
$max = THUMBNAIL_MAX_SECONDS;
while ($statThumbnail['size'] == 0 && $max-- > 0) {
sleep(1);
clearstatcache();
$statThumbnail = stat($pathFilenameThumbnail);
}
sleep(1); // additional time to be sure that the whole file is written.
}
// If the thumbnail has not been rendered during the last 60 secondes, something is wrong. // Check if the file is recent.
if (time() - $statThumbnail['mtime'] > THUMBNAIL_MAX_SECONDS) { if ($statSource['mtime'] < $statThumbnail['mtime']) {
$debugMode = true; return $pathFilenameThumbnail;
// throw new UserFormException('Thumbnail rendering takes longer than ' . THUMBNAIL_MAX_SECONDS . 's for file: ' .
// OnString::strrstr($pathFilenameSource,'/'), ERROR_THUMBNAIL_RENDER);
} else {
return $pathFilenameThumbnail; // There seems to be a thumbnail rendering already running. User needs to refresh page.
} }
} }
if ($statThumbnail === false || $statThumbnail['mtime'] < $statSource['mtime']) { // Render thumbnail: either it's a) public thumbnail or b) requested via download.php
$pathFilenameThumbnail = $this->createThumbnail($pathFilenameSource, $pathFilenameThumbnail, $dimension, $debugMode); if (($modeRender == THUMBNAIL_PREPARE && $control[TOKEN_SIP] != '1') || ($modeRender == THUMBNAIL_VIA_DOWNLOAD)) {
$pathFilenameThumbnail = $this->createThumbnail($pathFilenameSource, $pathFilenameThumbnail, $control[TOKEN_THUMBNAIL_DIMENSION], $debugMode);
} }
return $pathFilenameThumbnail; return $pathFilenameThumbnail;
...@@ -132,7 +155,6 @@ class Thumbnail { ...@@ -132,7 +155,6 @@ class Thumbnail {
private function createThumbnail($pathFilenameSource, $pathFilenameThumbnail, $dimension, $debugMode) { private function createThumbnail($pathFilenameSource, $pathFilenameThumbnail, $dimension, $debugMode) {
$outputInkscape = ''; $outputInkscape = '';
$cmdInkscape = ''; $cmdInkscape = '';
$background = $debugMode ? '&' : '';
// Indicates a running thumbnail rendering process. // Indicates a running thumbnail rendering process.
if (false === touch($pathFilenameThumbnail)) { if (false === touch($pathFilenameThumbnail)) {
...@@ -149,8 +171,9 @@ class Thumbnail { ...@@ -149,8 +171,9 @@ class Thumbnail {
// SVG files are best to thumbnail via 'inkscape' // SVG files are best to thumbnail via 'inkscape'
if ($ext == 'svg' && $this->inkscape != '') { if ($ext == 'svg' && $this->inkscape != '') {
$inkscapeDimension = Token::explodeDimension($dimension); $inkscapeDimension = Token::explodeDimension($dimension);
$cmdInkscape = $this->inkscape . " --export-area-drawing --without-gui $inkscapeDimension --export-png $pathFilenameThumbnail $pathFilenameSource $background"; // Automatically cut white border: --export-area-drawing
$outputInkscape = Token::qfqExec($cmdInkscape, $rc); $cmdInkscape = $this->inkscape . " --without-gui $inkscapeDimension --export-png $pathFilenameThumbnail $pathFilenameSource";
$outputInkscape = Support::qfqExec($cmdInkscape, $rc);
if ($rc == 0) {