[FEATURE] Introduce handling of missing files 13/22913/8
authorFrans Saris <franssaris@gmail.com>
Wed, 7 Aug 2013 09:51:34 +0000 (11:51 +0200)
committerSteffen Ritter <info@rs-websystems.de>
Wed, 28 Aug 2013 11:08:34 +0000 (13:08 +0200)
To keep track of missing files we introduce a missing
flag is sys_files.
When indexer detects file is missing the flag is set
and reset when file is present again.

In backend a message is shown when a reference to a
missing file is used.

Resolves: #50827
Change-Id: Iaaf9455698507c5cd574f6928fa5a563a49ad121
Reviewed-on: https://review.typo3.org/22913
Reviewed-by: Steffen Ritter
Reviewed-by: Kai Ole Hartwig
Tested-by: Kai Ole Hartwig
Reviewed-by: Mattias Nilsson
Tested-by: Mattias Nilsson
Tested-by: Steffen Ritter
18 files changed:
typo3/sysext/backend/Classes/Controller/ContentElement/ElementInformationController.php
typo3/sysext/backend/Classes/Form/Element/InlineElement.php
typo3/sysext/backend/Classes/Form/FormEngine.php
typo3/sysext/backend/Classes/Utility/BackendUtility.php
typo3/sysext/core/Classes/Resource/AbstractFile.php
typo3/sysext/core/Classes/Resource/File.php
typo3/sysext/core/Classes/Resource/FileReference.php
typo3/sysext/core/Classes/Resource/FileRepository.php
typo3/sysext/core/Classes/Resource/Hook/FileInfoHook.php
typo3/sysext/core/Classes/Resource/ProcessedFile.php
typo3/sysext/core/Classes/Resource/ResourceStorage.php
typo3/sysext/core/Classes/Resource/Service/IndexerService.php
typo3/sysext/core/Classes/Resource/Utility/BackendUtility.php [new file with mode: 0644]
typo3/sysext/core/Configuration/TCA/sys_file.php
typo3/sysext/core/ext_tables.sql
typo3/sysext/filelist/Classes/FileList.php
typo3/sysext/lang/locallang_core.xlf
typo3/sysext/lang/locallang_tca.xlf

index b4735ef..4dfabd9 100644 (file)
@@ -251,30 +251,41 @@ class ElementInformationController {
                if (!$this->fileObject) {
                        return;
                }
+               $imageTag = '';
+               $downloadLink = '';
 
-               $fileExtension = $this->fileObject->getExtension();
-               if (GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $fileExtension)) {
-                       $thumbUrl = $this->fileObject->process(
-                               ProcessedFile::CONTEXT_IMAGEPREVIEW,
-                               array(
-                                       'width' => '400m',
-                                       'height' => '400m'
-                               )
-                       )->getPublicUrl(TRUE);
-               }
+               // check if file is marked as missing
+               if ($this->fileObject->isMissing()) {
+                       $flashMessage = \TYPO3\CMS\Core\Resource\Utility\BackendUtility::getFlashMessageForMissingFile($this->fileObject);
+                       $imageTag .= $flashMessage->render();
 
-               // Create thumbnail image?
-               if ($thumbUrl) {
-                       $imageTag = '<img src="' . $thumbUrl . '" ' .
-                                       'alt="' . htmlspecialchars(trim($this->fileObject->getName())) . '" ' .
-                                       'title="' . htmlspecialchars(trim($this->fileObject->getName())) . '" />';
-               }
+               } else {
+
+                       $fileExtension = $this->fileObject->getExtension();
+                       $thumbUrl = '';
+                       if (GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $fileExtension)) {
+                               $thumbUrl = $this->fileObject->process(
+                                       ProcessedFile::CONTEXT_IMAGEPREVIEW,
+                                       array(
+                                               'width' => '400m',
+                                               'height' => '400m'
+                                       )
+                               )->getPublicUrl(TRUE);
+                       }
+
+                       // Create thumbnail image?
+                       if ($thumbUrl) {
+                               $imageTag .= '<img src="' . $thumbUrl . '" ' .
+                                               'alt="' . htmlspecialchars(trim($this->fileObject->getName())) . '" ' .
+                                               'title="' . htmlspecialchars(trim($this->fileObject->getName())) . '" />';
+                       }
 
-               // Display download link?
-               if ($this->fileObject->getPublicUrl()) {
-                       $downloadLink = '<a href="../' . $this->fileObject->getPublicUrl() . '" target="_blank">' .
-                                       $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xlf:download', TRUE) .
-                                       '</a>';
+                       // Display download link?
+                       if ($this->fileObject->getPublicUrl()) {
+                               $downloadLink .= '<a href="../' . $this->fileObject->getPublicUrl() . '" target="_blank">' .
+                                               $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xlf:download', TRUE) .
+                                               '</a>';
+                       }
                }
 
                return ($imageTag ? '<p>' . $imageTag . '</p>' : '') .
index 17daec2..0a04346 100644 (file)
@@ -551,29 +551,33 @@ class InlineElement {
                } else {
                        $recTitle = BackendUtility::getRecordTitle($foreign_table, $rec, TRUE);
                }
+
+               $altText = BackendUtility::getRecordIconAltText($rec, $foreign_table);
+               $iconImg = IconUtility::getSpriteIconForRecord($foreign_table, $rec, array('title' => htmlspecialchars($altText), 'id' => $objectId . '_icon'));
+               $label = '<span id="' . $objectId . '_label">' . $recTitle . '</span>';
+               $ctrl = $this->renderForeignRecordHeaderControl($parentUid, $foreign_table, $rec, $config, $isVirtualRecord);
+               $thumbnail = FALSE;
+
                // Renders a thumbnail for the header
                if (!empty($config['appearance']['headerThumbnail']['field'])) {
                        $fieldValue = $rec[$config['appearance']['headerThumbnail']['field']];
                        $firstElement = array_shift(GeneralUtility::trimExplode(',', $fieldValue));
                        $fileUid = array_pop(BackendUtility::splitTable_Uid($firstElement));
+
                        if (!empty($fileUid)) {
                                $fileObject = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance()->getFileObject($fileUid);
-                               if ($fileObject) {
+                               if ($fileObject && $fileObject->isMissing()) {
+                                       $flashMessage = \TYPO3\CMS\Core\Resource\Utility\BackendUtility::getFlashMessageForMissingFile($fileObject);
+                                       $thumbnail = $flashMessage->render();
+                               } elseif($fileObject) {
                                        $imageSetup = $config['appearance']['headerThumbnail'];
                                        unset($imageSetup['field']);
                                        $imageSetup = array_merge(array('width' => 64, 'height' => 64), $imageSetup);
                                        $imageUrl = $fileObject->process(\TYPO3\CMS\Core\Resource\ProcessedFile::CONTEXT_IMAGEPREVIEW, $imageSetup)->getPublicUrl(TRUE);
                                        $thumbnail = '<img src="' . $imageUrl . '" alt="' . htmlspecialchars($recTitle) . '">';
-                               } else {
-                                       $thumbnail = FALSE;
                                }
                        }
-
                }
-               $altText = BackendUtility::getRecordIconAltText($rec, $foreign_table);
-               $iconImg = IconUtility::getSpriteIconForRecord($foreign_table, $rec, array('title' => htmlspecialchars($altText), 'id' => $objectId . '_icon'));
-               $label = '<span id="' . $objectId . '_label">' . $recTitle . '</span>';
-               $ctrl = $this->renderForeignRecordHeaderControl($parentUid, $foreign_table, $rec, $config, $isVirtualRecord);
                $header = '<table>' . '<tr>' . (!empty($config['appearance']['headerThumbnail']['field']) && $thumbnail ?
                                '<td class="t3-form-field-header-inline-thumbnail" id="' . $objectId . '_thumbnailcontainer">' . $thumbnail . '</td>' :
                                '<td class="t3-form-field-header-inline-icon" id="' . $objectId . '_iconcontainer">' . $iconImg . '</td>') . '<td class="t3-form-field-header-inline-summary">' . $label . '</td>' . '<td clasS="t3-form-field-header-inline-ctrl">' . $ctrl . '</td>' . '</tr>' . '</table>';
index 3544865..e8a3798 100644 (file)
@@ -2365,14 +2365,19 @@ function ' . $evalData . '(value) {
                                        // FAL icon production
                                        if (MathUtility::canBeInterpretedAsInteger($imgP[0])) {
                                                $fileObject = $fileFactory->getFileObject($imgP[0]);
-                                               if (GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $fileObject->getExtension())) {
+
+                                               if ($fileObject->isMissing()) {
+                                                       $flashMessage = \TYPO3\CMS\Core\Resource\Utility\BackendUtility::getFlashMessageForMissingFile($fileObject);
+                                                       $imgs[] = $flashMessage->render();
+                                               } elseif (GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $fileObject->getExtension())) {
                                                        $imageUrl = $fileObject->process(\TYPO3\CMS\Core\Resource\ProcessedFile::CONTEXT_IMAGEPREVIEW, array())->getPublicUrl(TRUE);
                                                        $imgTag = '<img src="' . $imageUrl . '" alt="' . htmlspecialchars($fileObject->getName()) . '" />';
+                                                       $imgs[] = '<span class="nobr">' . $imgTag . htmlspecialchars($fileObject->getName()) . '</span>';
                                                } else {
                                                        // Icon
                                                        $imgTag = IconUtility::getSpriteIconForFile(strtolower($fileObject->getExtension()), array('title' => $fileObject->getName()));
+                                                       $imgs[] = '<span class="nobr">' . $imgTag . htmlspecialchars($fileObject->getName()) . '</span>';
                                                }
-                                               $imgs[] = '<span class="nobr">' . $imgTag . htmlspecialchars($fileObject->getName()) . '</span>';
                                        } else {
                                                $rowCopy = array();
                                                $rowCopy[$field] = $imgPath;
index b2e7eec..67f52cc 100644 (file)
@@ -1552,6 +1552,13 @@ class BackendUtility {
                        foreach ($referenceUids as $referenceUid) {
                                $fileReferenceObject = ResourceFactory::getInstance()->getFileReferenceObject($referenceUid['uid']);
                                $fileObject = $fileReferenceObject->getOriginalFile();
+
+                               if ($fileObject->isMissing()) {
+                                       $flashMessage = \TYPO3\CMS\Core\Resource\Utility\BackendUtility::getFlashMessageForMissingFile($fileObject);
+                                       $thumbData .= $flashMessage->render();
+                                       continue;
+                               }
+
                                // Web image
                                if (GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $fileReferenceObject->getExtension())) {
                                        $imageUrl = $fileObject->process(ProcessedFile::CONTEXT_IMAGEPREVIEW, array(
@@ -1584,17 +1591,18 @@ class BackendUtility {
                                        $fileName = trim($uploaddir . '/' . $theFile, '/');
                                        $fileObject = ResourceFactory::getInstance()->retrieveFileOrFolderObject($fileName);
                                        $fileExtension = $fileObject->getExtension();
+
+                                       if ($fileObject->isMissing()) {
+                                               $flashMessage = \TYPO3\CMS\Core\Resource\Utility\BackendUtility::getFlashMessageForMissingFile($fileObject);
+                                               $thumbData .= $flashMessage->render();
+                                               continue;
+                                       }
+
                                        if ($fileExtension == 'ttf' || GeneralUtility::inList($GLOBALS['TYPO3_CONF_VARS']['GFX']['imagefile_ext'], $fileExtension)) {
                                                $imageUrl = $fileObject->process(ProcessedFile::CONTEXT_IMAGEPREVIEW, array(
                                                        'width' => $sizeParts[0],
                                                        'height' => $sizeParts[1]
                                                ))->getPublicUrl(TRUE);
-                                               if (!$fileObject->checkActionPermission('read')) {
-                                                       /** @var \TYPO3\CMS\Core\Messaging\FlashMessage $flashMessage */
-                                                       $flashMessage = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\FlashMessage', $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.file_missing_text') . ' <abbr title="' . htmlspecialchars($fileObject->getName()) . '">' . htmlspecialchars($fileObject->getName()) . '</abbr>', $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.file_missing'), FlashMessage::ERROR);
-                                                       $thumbData .= $flashMessage->render();
-                                                       continue;
-                                               }
                                                $image = '<img src="' . htmlspecialchars($imageUrl) . '" hspace="2" border="0" title="' . htmlspecialchars($fileObject->getName()) . '"' . $tparams . ' alt="" />';
                                                if ($linkInfoPopup) {
                                                        $onClick = 'top.launchView(\'_FILE\', \'' . $fileName . '\',\'\',\'' . $backPath . '\');return false;';
@@ -4187,5 +4195,6 @@ class BackendUtility {
        static public function isRootLevelRestrictionIgnored($table) {
                return !empty($GLOBALS['TCA'][$table]['ctrl']['security']['ignoreRootLevelRestriction']);
        }
+
 }
 ?>
index a052583..8947b7c 100644 (file)
@@ -138,7 +138,12 @@ abstract class AbstractFile implements FileInterface {
         * @return mixed Property value
         */
        public function getProperty($key) {
-               return $this->properties[$key];
+
+               if ($this->hasProperty($key)) {
+                       return $this->properties[$key];
+               } else {
+                       return NULL;
+               }
        }
 
        /**
@@ -519,14 +524,14 @@ abstract class AbstractFile implements FileInterface {
         *
         * @param bool  $relativeToCurrentScript   Determines whether the URL returned should be relative to the current script, in case it is relative at all (only for the LocalDriver)
         *
-        * @throws \RuntimeException
-        * @return string
+        * @return null|string
         */
        public function getPublicUrl($relativeToCurrentScript = FALSE) {
                if ($this->deleted) {
-                       throw new \RuntimeException('File has been deleted.', 1329821485);
+                       return NULL;
+               } else {
+                       return $this->getStorage()->getPublicUrl($this, $relativeToCurrentScript);
                }
-               return $this->getStorage()->getPublicUrl($this, $relativeToCurrentScript);
        }
 
        /**
index e0418a6..f01c202 100644 (file)
@@ -341,7 +341,39 @@ class File extends AbstractFile {
                $this->indexable = $indexable;
        }
 
-}
+       /**
+        * @return boolean
+        */
+       public function isMissing() {
+               return (bool) $this->getProperty('missing');
+       }
 
+       /**
+        * @param boolean $missing
+        */
+       public function setMissing($missing) {
+               $this->updateProperties(array('missing' => $missing ? 1 : 0));
+       }
+
+       /**
+        * Returns a publicly accessible URL for this file
+        * When file is marked as missing or deleted no url is returned
+        *
+        * WARNING: Access to the file may be restricted by further means, e.g. some
+        * web-based authentication. You have to take care of this yourself.
+        *
+        * @param bool  $relativeToCurrentScript   Determines whether the URL returned should be relative to the current script, in case it is relative at all (only for the LocalDriver)
+        *
+        * @return string
+        */
+       public function getPublicUrl($relativeToCurrentScript = FALSE) {
+               if ($this->isMissing() || $this->deleted) {
+                       return FALSE;
+               } else {
+                       return $this->getStorage()->getPublicUrl($this, $relativeToCurrentScript);
+               }
+       }
+
+}
 
-?>
+?>
\ No newline at end of file
index eabca23..2a1b989 100644 (file)
@@ -325,6 +325,15 @@ class FileReference implements FileInterface {
                return (int) $this->originalFile->getType();
        }
 
+       /**
+        * Check if file is marked as missing by indexer
+        *
+        * @return boolean
+        */
+       public function isMissing() {
+               return (bool) $this->originalFile->getProperty('missing');
+       }
+
        /******************
         * CONTENTS RELATED
         ******************/
index a713036..41c3d6f 100644 (file)
@@ -151,7 +151,7 @@ class FileRepository extends AbstractRepository {
                $rows = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
                        '*',
                        $this->table,
-                       sprintf('storage=%u AND identifier LIKE %s AND NOT identifier LIKE %s',
+                       sprintf('storage=%u AND missing = 0 AND identifier LIKE %s AND NOT identifier LIKE %s',
                                        $storage,
                                        $GLOBALS['TYPO3_DB']->fullQuoteStr($GLOBALS['TYPO3_DB']->escapeStrForLike($identifier, $this->table) . '%', $this->table),
                                        $GLOBALS['TYPO3_DB']->fullQuoteStr($GLOBALS['TYPO3_DB']->escapeStrForLike($identifier, $this->table) . '%/%', $this->table)
index 27745cd..90c8477 100644 (file)
@@ -47,6 +47,10 @@ class FileInfoHook {
                        $processedFile = $fileObject->process(\TYPO3\CMS\Core\Resource\ProcessedFile::CONTEXT_IMAGEPREVIEW, array('width' => 150, 'height' => 150));
                        $previewImage = $processedFile->getPublicUrl(TRUE);
                        $content = '';
+                       if ($fileObject->isMissing()) {
+                               $flashMessage = \TYPO3\CMS\Core\Resource\Utility\BackendUtility::getFlashMessageForMissingFile($fileObject);
+                               $content .= $flashMessage->render();
+                       }
                        if ($previewImage) {
                                $content .= '<img src="' . htmlspecialchars($previewImage) . '" alt="" class="t3-tceforms-sysfile-imagepreview" />';
                        }
index 0176889..869c3b5 100644 (file)
@@ -446,6 +446,11 @@ class ProcessedFile extends AbstractFile {
        public function needsReprocessing() {
                $fileMustBeRecreated = FALSE;
 
+               // if original is missing we can not reprocess the file
+               if ($this->originalFile->isMissing()) {
+                       return FALSE;
+               }
+
                // processedFile does not exist
                if (!$this->usesOriginalFile() && !$this->exists()) {
                        $fileMustBeRecreated = TRUE;
index 763f6a0..52d484d 100644 (file)
@@ -590,17 +590,25 @@ class ResourceStorage {
                if (in_array($action, array('add', 'edit', 'write', 'upload', 'move', 'rename', 'unzip', 'remove'))) {
                        $isWriteCheck = TRUE;
                }
-               // Check 3: Check the capabilities of the storage (and the driver)
-               if ($isWriteCheck && !$this->isWritable()) {
-                       return FALSE;
+
+               $isMissing = FALSE;
+               if (!$isProcessedFile) {
+                       $isMissing = $file->isMissing();
                }
-               // Check 4: "File permissions" of the driver
-               $filePermissions = $this->driver->getFilePermissions($file);
-               if ($isReadCheck && !$filePermissions['r']) {
+
+               // Check 3: Check the capabilities of the storage (and the driver)
+               if ($isWriteCheck && ($isMissing || !$this->isWritable())) {
                        return FALSE;
                }
-               if ($isWriteCheck && !$filePermissions['w']) {
-                       return FALSE;
+               // Check 4: "File permissions" of the driver (only when file isn't marked as missing)
+               if (!$isMissing) {
+                       $filePermissions = $this->driver->getFilePermissions($file);
+                       if ($isReadCheck && !$filePermissions['r']) {
+                               return FALSE;
+                       }
+                       if ($isWriteCheck && !$filePermissions['w']) {
+                               return FALSE;
+                       }
                }
                return TRUE;
        }
index 4b69b1e..41307a6 100644 (file)
@@ -98,6 +98,7 @@ class IndexerService implements \TYPO3\CMS\Core\SingletonInterface {
                // be updated on the existing record
                if ($fileObject->isIndexed()) {
                        $fileInfo['deleted'] = 0;
+                       $fileInfo['missing'] = 0;
                        $GLOBALS['TYPO3_DB']->exec_UPDATEquery('sys_file', sprintf('uid = %d', $fileObject->getUid()), $fileInfo);
                } else {
                        // Check if a file has been moved outside of FAL -- we have some
@@ -109,6 +110,8 @@ class IndexerService implements \TYPO3\CMS\Core\SingletonInterface {
                                if (!$otherFile->exists()) {
                                        // @todo: create a log entry
                                        $movedFile = TRUE;
+                                       $fileInfo['deleted'] = 0;
+                                       $fileInfo['missing'] = 0;
                                        $otherFile->updateProperties($fileInfo);
                                        $this->getRepository()->update($otherFile);
                                        $fileInfo['uid'] = $otherFile->getUid();
@@ -177,16 +180,36 @@ class IndexerService implements \TYPO3\CMS\Core\SingletonInterface {
         */
        public function indexFilesInFolder(\TYPO3\CMS\Core\Resource\Folder $folder) {
                $numberOfIndexedFiles = 0;
+               $fileIdentifiers = array();
+
                // Index all files in this folder
                $fileObjects = $folder->getFiles();
+
                // emit signal
                $this->emitPreMultipleFilesIndexSignal($fileObjects);
                foreach ($fileObjects as $fileObject) {
                        $this->indexFile($fileObject);
+                       $fileIdentifiers[] = $fileObject->getIdentifier();
                        $numberOfIndexedFiles++;
                }
+
+               // check for deleted files (file not found during indexing are marked as missing)
+               foreach ($this->getRepository()->getFileIndexRecordsForFolder($folder) as $file) {
+                       if (!in_array($file['identifier'], $fileIdentifiers)) {
+                               /** @var $fileObject \TYPO3\CMS\Core\Resource\File */
+                               $fileObject = $this->getRepository()->findByIdentifier($file['uid']);
+                               $fileObject->setMissing(TRUE);
+                               $this->getRepository()->update($fileObject);
+                       }
+               }
+
                // emit signal
                $this->emitPostMultipleFilesIndexSignal($fileObjects);
+
+               // cleanup to prevent to much memory use on big folders
+               unset($fileObjects);
+               unset($fileIdentifiers);
+
                // Call this function recursively for each subfolder
                $subFolders = $folder->getSubfolders();
                foreach ($subFolders as $subFolder) {
diff --git a/typo3/sysext/core/Classes/Resource/Utility/BackendUtility.php b/typo3/sysext/core/Classes/Resource/Utility/BackendUtility.php
new file mode 100644 (file)
index 0000000..84ec73a
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+namespace TYPO3\CMS\Core\Resource\Utility;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Frans Saris <franssaris (at) gmail.com>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * Some Backend Utility functions for working with resources
+ */
+class BackendUtility {
+
+       /**
+        * Create a flash message for a file that is marked as missing
+        *
+        * @param \TYPO3\CMS\Core\Resource\AbstractFile $file
+        * @return \TYPO3\CMS\Core\Messaging\FlashMessage
+        */
+       static public function getFlashMessageForMissingFile(\TYPO3\CMS\Core\Resource\AbstractFile $file) {
+               /** @var \TYPO3\CMS\Core\Messaging\FlashMessage $flashMessage */
+               $flashMessage = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Messaging\\FlashMessage',
+                       $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.file_missing_text') .
+                       ' <abbr title="' . htmlspecialchars($file->getStorage()->getName() . ' :: '.$file->getIdentifier()) . '">' .
+                       htmlspecialchars($file->getName()) . '</abbr>',
+                       $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_core.xlf:warning.file_missing'),
+                       \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR
+               );
+
+               return $flashMessage;
+       }
+
+}
+
+?>
\ No newline at end of file
index 1ced99c..91689d7 100644 (file)
@@ -29,7 +29,7 @@ return array(
                ),
        ),
        'interface' => array(
-               'showRecordFieldList' => 'storage, name, description, alternative, type, mime_type, size, sha1'
+               'showRecordFieldList' => 'storage, name, description, alternative, type, mime_type, size, sha1, missing'
        ),
        'columns' => array(
                't3ver_label' => array(
@@ -154,6 +154,15 @@ return array(
                                'eval' => 'int',
                                'default' => 0
                        )
+               ),
+               'missing' => array(
+                       'exclude' => 0,
+                       'label' => 'LLL:EXT:lang/locallang_tca.xlf:sys_file.missing',
+                       'config' => array(
+                               'readOnly' => 1,
+                               'type' => 'check',
+                               'default' => '0'
+                       )
                )
        ),
        'types' => array(
index 762d53f..3b9efba 100644 (file)
@@ -285,6 +285,7 @@ CREATE TABLE sys_file (
        crdate int(11) DEFAULT '0' NOT NULL,
        cruser_id int(11) DEFAULT '0' NOT NULL,
        deleted tinyint(4) DEFAULT '0' NOT NULL,
+       missing tinyint(4) DEFAULT '0' NOT NULL,
 
        # Versioning fields
        t3ver_oid int(11) DEFAULT '0' NOT NULL,
index 75d3b93..42f7e85 100644 (file)
@@ -618,8 +618,12 @@ class FileList extends \TYPO3\CMS\Backend\RecordList\AbstractRecordList {
                                                        break;
                                                case 'file':
                                                        $theData[$field] = $this->linkWrapFile(htmlspecialchars($fileName), $fileObject);
+
+                                                       if ($fileObject->isMissing()) {
+                                                               $flashMessage = \TYPO3\CMS\Core\Resource\Utility\BackendUtility::getFlashMessageForMissingFile($fileObject);
+                                                               $theData[$field] .= $flashMessage->render();
                                                        // Thumbnails?
-                                                       if ($this->thumbs && $this->isImage($ext)) {
+                                                       } elseif ($this->thumbs && $this->isImage($ext)) {
                                                                $processedFile = $fileObject->process(\TYPO3\CMS\Core\Resource\ProcessedFile::CONTEXT_IMAGEPREVIEW, array());
                                                                if ($processedFile) {
                                                                        $thumbUrl = $processedFile->getPublicUrl(TRUE);
index e4de82f..57f9be1 100644 (file)
@@ -893,7 +893,7 @@ Would you like to save now in order to refresh the display?</source>
                                <source>File is missing!</source>
                        </trans-unit>
                        <trans-unit id="warning.file_missing_text" xml:space="preserve">
-                               <source>This content element references a file which seems to not exist:</source>
+                               <source>This file is marked as missing:</source>
                        </trans-unit>
                        <trans-unit id="error.formProtection.tokenInvalid" xml:space="preserve">
                                <source>Validating the security token of this form has failed. Please reload the form and submit it again.</source>
index 87d3f43..8df1740 100644 (file)
                        <trans-unit id="sys_file.alternative" xml:space="preserve">
                                <source>Alternative Text</source>
                        </trans-unit>
+                       <trans-unit id="sys_file.missing" xml:space="preserve">
+                               <source>Marked as missing</source>
+                       </trans-unit>
                        <trans-unit id="sys_file_reference" xml:space="preserve">
                                <source>File Reference</source>
                        </trans-unit>