Initial commit - Typo3 11.5.41

This commit is contained in:
Matteo Gallo
2026-07-03 17:53:31 +02:00
commit 5ca4743197
6811 changed files with 568848 additions and 0 deletions

View File

@@ -0,0 +1,168 @@
<?php
namespace MiniFranske\FsMediaGallery\ContextMenu\ItemProviders;
use MiniFranske\FsMediaGallery\Service\Utility;
use TYPO3\CMS\Backend\ContextMenu\ItemProviders\AbstractProvider;
use TYPO3\CMS\Core\Resource\Folder;
use TYPO3\CMS\Core\Resource\FolderInterface;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
class FsMediaGalleryProvider extends AbstractProvider
{
protected $itemsConfiguration = [];
/**
* @return bool
*/
public function canHandle(): bool
{
return $this->table === 'sys_file';
}
/**
* @var Folder
*/
protected $folder;
/**
* @return int
*/
public function getPriority(): int
{
return 90;
}
/**
* Initialize file object
*/
protected function initialize()
{
parent::initialize();
$resource = GeneralUtility::makeInstance(ResourceFactory::class)
->retrieveFileOrFolderObject($this->identifier);
if ($resource instanceof Folder
&& in_array(
$resource->getRole(),
[FolderInterface::ROLE_DEFAULT, FolderInterface::ROLE_USERUPLOAD],
true
)
) {
$this->folder = $resource;
}
}
/**
* Adds the media album add/edit menu items
*
* @param array $items
* @return array
*/
public function addItems(array $items): array
{
$this->initialize();
if (!($this->folder instanceof Folder)) {
return $items;
}
/** @var \MiniFranske\FsMediaGallery\Service\Utility $utility */
$utility = GeneralUtility::makeInstance(Utility::class);
$mediaFolders = $utility->getStorageFolders();
if (count($mediaFolders) === 0) {
$this->itemsConfiguration['add-media-gallery'] = [
'label' => $this->sL('module.buttons.createAlbum'),
'iconIdentifier' => 'action-add-album',
'callbackAction' => 'missingMediaFolder',
'title' => $this->sL('module.alerts.firstCreateStorageFolder')
];
}
if (count($mediaFolders) > 0) {
$collections = $utility->findFileCollectionRecordsForFolder(
$this->folder->getStorage()->getUid(),
$this->folder->getIdentifier(),
array_keys($mediaFolders)
);
foreach ($collections as $collection) {
$this->itemsConfiguration['edit-media-gallery-' . $collection['uid']] = [
'label' => sprintf($this->sL('module.buttons.editAlbum'), $collection['title']),
'iconIdentifier' => 'action-edit-album',
'callbackAction' => 'mediaAlbum',
'uid' => $collection['uid'],
];
}
if (!count($collections)) {
foreach ($mediaFolders as $uid => $title) {
// Find parent album for auto setting parent album
$parentUid = 0;
$parents = $utility->findFileCollectionRecordsForFolder(
$this->folder->getStorage()->getUid(),
$this->folder->getParentFolder()->getIdentifier(),
[$uid]
);
// If parent(s) found we take the first one
if (count($parents)) {
$parentUid = $parents[0]['uid'];
}
$this->itemsConfiguration['add-media-gallery-' . $uid] = [
'label' => sprintf($this->sL('module.buttons.createAlbumIn'), $title),
'iconIdentifier' => 'action-add-album',
'callbackAction' => 'mediaAlbum',
'pid' => $uid,
'parentUid' => $parentUid,
];
}
}
}
if ($this->itemsConfiguration !== []) {
$items += $this->prepareItems(
['media_gallery_divider' => ['type' => 'divider']]
+ $this->itemsConfiguration
);
}
return $items;
}
/**
* @param string $itemName
* @return array
*/
protected function getAdditionalAttributes(string $itemName): array
{
$itemInfo = $this->itemsConfiguration[$itemName] ?? [];
return [
'data-callback-module' => 'TYPO3/CMS/FsMediaGallery/ContextMenuActions',
'data-album-record-uid' => $itemInfo['uid'] ?? 0,
'data-pid' => $itemInfo['pid'] ?? 0,
'data-parent-uid' => $itemInfo['parentUid'] ?? 0,
'data-title' => $itemInfo['title'] ?? ucfirst(trim(str_replace('_', ' ', $this->folder->getName()))),
'data-storage' => $this->folder->getStorage()->getUid(),
'data-folder' => $this->folder->getIdentifier(),
];
}
/**
* Get language string
*
* @param string $key
* @param string $languageFile
* @return string
*/
protected function sL($key, $languageFile = 'LLL:EXT:fs_media_gallery/Resources/Private/Language/locallang_be.xlf')
{
return $this->languageService->sL($languageFile . ':' . $key);
}
}

View File

@@ -0,0 +1,458 @@
<?php
namespace MiniFranske\FsMediaGallery\Controller;
/***************************************************************
* Copyright notice
*
* (c) 2014 Frans Saris <franssaris@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 3 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.
*
* 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!
***************************************************************/
use GeorgRinger\NumberedPagination\NumberedPagination;
use MiniFranske\FsMediaGallery\Pagination\ExtendedArrayPaginator;
use MiniFranske\FsMediaGallery\Utility\TypoScriptUtility;
use MiniFranske\FsMediaGallery\Domain\Repository\MediaAlbumRepository;
use Psr\Http\Message\ResponseInterface;
use TYPO3\CMS\Core\Pagination\ArrayPaginator;
use TYPO3\CMS\Core\Pagination\SimplePagination;
use TYPO3\CMS\Extbase\Http\ForwardResponse;
use TYPO3\CMS\Frontend\Controller\ErrorController;
use TYPO3\CMS\Core\Http\ImmediateResponseException;
use MiniFranske\FsMediaGallery\Domain\Model\MediaAlbum;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController;
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
/**
* MediaAlbumController
*/
class MediaAlbumController extends ActionController
{
/**
* mediaAlbumRepository
*
* @var \MiniFranske\FsMediaGallery\Domain\Repository\MediaAlbumRepository
*/
protected $mediaAlbumRepository;
/**
* Injects the Configuration Manager
*
* @param ConfigurationManagerInterface $configurationManager Instance of the Configuration Manager
* @return void
*/
public function injectConfigurationManager(ConfigurationManagerInterface $configurationManager)
{
$this->configurationManager = $configurationManager;
$frameworkSettings = $this->configurationManager->getConfiguration(
ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK,
'fsmediagallery',
'fsmediagallery_mediagallery'
);
$flexformSettings = $this->configurationManager->getConfiguration(
ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS
);
// merge Framework (TypoScript) and Flexform settings
if (isset($frameworkSettings['settings']['overrideFlexformSettingsIfEmpty'])) {
/** @var $typoScriptUtility \MiniFranske\FsMediaGallery\Utility\TypoScriptUtility */
$typoScriptUtility = GeneralUtility::makeInstance(TypoScriptUtility::class);
$mergedSettings = $typoScriptUtility->override($flexformSettings, $frameworkSettings);
$this->settings = $mergedSettings;
} else {
$this->settings = $flexformSettings;
}
/**
* sync persistence.storagePid=settings.startingpoint and persistence.recursive=settings.recursive
*/
// overwrite persistence.storagePid if settings.startingpoint is defined in flexform
if (!empty($this->settings['startingpoint'])) {
$frameworkSettings['persistence']['storagePid'] = $this->settings['startingpoint'];
// if settings.startingpoint is not set in flexform, use persistence.storagePid from TS
} elseif (!empty($frameworkSettings['persistence']['storagePid'])) {
$this->settings['startingpoint'] = $frameworkSettings['persistence']['storagePid'];
// startingpoint/storagePid is not set via TS nor flexforms > fallback to current pid
} else {
$this->settings['startingpoint'] = $frameworkSettings['persistence']['storagePid'] = $GLOBALS['TSFE']->id;
}
// set persistence.recursive if settings.recursive is defined in flexform
if (!empty($this->settings['recursive'])) {
$frameworkSettings['persistence']['recursive'] = $this->settings['recursive'];
// if settings.recursive is not set in flexform, use persistence.recursive from TS
} elseif (!empty($frameworkSettings['persistence']['recursive'])) {
$this->settings['recursive'] = $frameworkSettings['persistence']['recursive'];
// recursive is not set via TS nor flexforms
} else {
$this->settings['recursive'] = $frameworkSettings['persistence']['recursive'] = 0;
}
// write back altered configuration
$this->configurationManager->setConfiguration($frameworkSettings);
// check some settings
if (!isset($this->settings['list']['pagination']['itemsPerPage']) || $this->settings['list']['pagination']['itemsPerPage'] < 1) {
$this->settings['list']['pagination']['itemsPerPage'] = 12;
}
if (!isset($this->settings['album']['pagination']['itemsPerPage']) || $this->settings['album']['pagination']['itemsPerPage'] < 1) {
$this->settings['album']['pagination']['itemsPerPage'] = 12;
}
// correct resizeMode 's' set in flexforms (flexforms value '' is used for inherit/definition by TS)
if (isset($this->settings['list']['thumb']['resizeMode']) && $this->settings['list']['thumb']['resizeMode'] == 's') {
$this->settings['list']['thumb']['resizeMode'] = '';
}
if (isset($this->settings['album']['thumb']['resizeMode']) && $this->settings['album']['thumb']['resizeMode'] == 's') {
$this->settings['album']['thumb']['resizeMode'] = '';
}
if (isset($this->settings['detail']['asset']['resizeMode']) && $this->settings['detail']['asset']['resizeMode'] == 's') {
$this->settings['detail']['asset']['resizeMode'] = '';
}
if (isset($this->settings['random']['thumb']['resizeMode']) && $this->settings['random']['thumb']['resizeMode'] == 's') {
$this->settings['random']['thumb']['resizeMode'] = '';
}
}
/**
* Injects the MediaAlbumRepository
*
* @param \MiniFranske\FsMediaGallery\Domain\Repository\MediaAlbumRepository $mediaAlbumRepository
* @return void
*/
public function injectMediaAlbumRepository(
MediaAlbumRepository $mediaAlbumRepository
)
{
$this->mediaAlbumRepository = $mediaAlbumRepository;
if (!empty($this->settings['allowedAssetMimeTypes'])) {
$this->mediaAlbumRepository->setAllowedAssetMimeTypes(GeneralUtility::trimExplode(',',
$this->settings['allowedAssetMimeTypes']));
}
if (isset($this->settings['album']['assets']['orderBy'])) {
$this->mediaAlbumRepository->setAssetsOrderBy($this->settings['album']['assets']['orderBy']);
}
if (isset($this->settings['album']['assets']['orderDirection'])) {
$this->mediaAlbumRepository->setAssetsOrderDirection($this->settings['album']['assets']['orderDirection']);
}
}
/**
* Set album uid restrictions as defined in settings
* By setting this in the repository also the MediaAlbum::getAlbums()
* and MediaAlbum::getRandomAlbum() is restricted to these uids.
*/
protected function setAlbumUidRestrictions()
{
$mediaAlbumsUids = GeneralUtility::trimExplode(',', $this->settings['mediaAlbumsUids'], true);
$this->mediaAlbumRepository->setAlbumUids($mediaAlbumsUids);
$this->mediaAlbumRepository->setUseAlbumUidsAsExclude(!empty($this->settings['useAlbumFilterAsExclude']));
}
/**
* Index Action
* As switchableControllerActions can be limited in EM this function
* is needed as default action (with no output).
* It is set as default action in flexform to make sure the
* correct tabs/fields are shown when a new plugin is added.
*/
public function indexAction(): ResponseInterface
{
return $this->htmlResponse('<i>Please select a display mode in the plugin.</i>');
}
/**
* NestedList Action
* Displays a (nested) list of albums; default/show action in fs_media_gallery <= 1.0.0
*
* @param int $mediaAlbum (this is not directly mapped to an object to handle 404 on our own)
*/
public function nestedListAction(int $mediaAlbum = 0): ResponseInterface
{
$mediaAlbums = null;
$mediaAlbum = (int)$mediaAlbum ?: null;
$showBackLink = true;
$this->setAlbumUidRestrictions();
// Single view
if ($mediaAlbum) {
/** @var MediaAlbum $mediaAlbum */
$mediaAlbum = $this->mediaAlbumRepository->findByUid($mediaAlbum);
if (!$mediaAlbum) {
return $this->pageNotFound(LocalizationUtility::translate('no_album_found', 'fs_media_gallery'));
}
}
/**
* No album selected and album restriction set, find all "root" albums
* Albums without parent or with parent not selected as allowed
*/
if ($mediaAlbum === null && $this->mediaAlbumRepository->getAlbumUids() !== []) {
$mediaAlbums = [];
$all = $this->mediaAlbumRepository->findAll((bool)$this->settings['list']['hideEmptyAlbums'], $this->settings['list']['orderBy'], $this->settings['list']['orderDirection']);
/** @var MediaAlbum $album */
foreach ($all as $album) {
$parent = $album->getParentalbum();
if ($parent === null
|| (!$this->mediaAlbumRepository->getUseAlbumUidsAsExclude() && !in_array($parent->getUid(),
$this->mediaAlbumRepository->getAlbumUids()))
|| ($this->mediaAlbumRepository->getUseAlbumUidsAsExclude() && in_array($parent->getUid(),
$this->mediaAlbumRepository->getAlbumUids()))
) {
$mediaAlbums[] = $album;
}
}
} else {
$mediaAlbums = $this->mediaAlbumRepository->findByParentalbum($mediaAlbum,
$this->settings['list']['hideEmptyAlbums'], $this->settings['list']['orderBy'],
$this->settings['list']['orderDirection']);
}
// when only 1 album skip gallery view
if ($mediaAlbum === null && !empty($this->settings['list']['skipListWhenOnlyOneAlbum']) && count($mediaAlbums) === 1) {
$mediaAlbum = $mediaAlbums[0];
$mediaAlbums = $this->mediaAlbumRepository->findByParentalbum($mediaAlbum,
$this->settings['list']['hideEmptyAlbums'], $this->settings['list']['orderBy'],
$this->settings['list']['orderDirection']);
$showBackLink = false;
}
if ($mediaAlbum && $mediaAlbum->getParentalbum() && (
$this->mediaAlbumRepository->getAlbumUids() === []
||
(!$this->mediaAlbumRepository->getUseAlbumUidsAsExclude() && in_array($mediaAlbum->getParentalbum()->getUid(),
$this->mediaAlbumRepository->getAlbumUids()))
||
($this->mediaAlbumRepository->getUseAlbumUidsAsExclude() && !in_array($mediaAlbum->getParentalbum()->getUid(),
$this->mediaAlbumRepository->getAlbumUids()))
)
) {
$this->view->assign('parentAlbum', $mediaAlbum->getParentalbum());
}
$this->view->assign('showBackLink', $showBackLink);
$this->view->assign('mediaAlbums', $mediaAlbums);
$this->view->assign('mediaAlbum', $mediaAlbum);
if ($mediaAlbums) {
$this->view->assign('mediaAlbumsPagination', $this->getAlbumsPagination($mediaAlbums));
}
if ($mediaAlbum) {
$this->view->assign('mediaAlbumPagination', $this->getAlbumPagination($mediaAlbum));
}
return $this->htmlResponse();
}
/**
* FlatList Action
* Displays a (one-dimensional, flattened) list of albums
*
* @param int $mediaAlbum (this is not directly mapped to an object to handle 404 on our own)
*/
public function flatListAction(int $mediaAlbum = 0): ResponseInterface
{
$showBackLink = true;
if ($mediaAlbum) {
// if an album is given, display it
$mediaAlbum = $this->mediaAlbumRepository->findByUid($mediaAlbum);
if (!$mediaAlbum) {
return $this->pageNotFound(LocalizationUtility::translate('no_album_found', 'fs_media_gallery'));
}
$this->view->assign('displayMode', 'album');
$this->view->assign('mediaAlbum', $mediaAlbum);
} else {
// display the album list
$mediaAlbums = $this->mediaAlbumRepository->findAll($this->settings['list']['hideEmptyAlbums'],
$this->settings['list']['orderBy'], $this->settings['list']['orderDirection']);
$this->view->assign('displayMode', 'flatList');
$this->view->assign('mediaAlbums', $mediaAlbums);
$showBackLink = false;
}
$this->view->assign('showBackLink', $showBackLink);
if ($mediaAlbum) {
$this->view->assign('mediaAlbumPagination', $this->getAlbumPagination($mediaAlbum));
}
return $this->htmlResponse();
}
/**
* Show single Album (defined in FlexForm/TS) Action
* As showAlbumAction() displays any album by the given param this function gets its value from TS/Felxform
* This could be merged with showAlbumAction() if there is a way to determine which switchableControllerActions is defined in Felxform.
*/
public function showAlbumByConfigAction(): ResponseInterface
{
// get all request arguments (e.g. pagination widget)
$arguments = $this->request->getArguments();
// set album id from settings
$arguments['mediaAlbum'] = $this->settings['mediaAlbum'];
return (new ForwardResponse('showAlbum'))->withArguments($arguments);
}
/**
* Show single Album Action
*
* @param int $mediaAlbum (this is not directly mapped to an object to handle 404 on our own)
*/
public function showAlbumAction(int $mediaAlbum = null): ResponseInterface
{
$mediaAlbum = (int)$mediaAlbum ?: null;
if (empty($mediaAlbum)) {
$mediaAlbum = (int)$this->settings['mediaAlbum'];
}
// if album uid is set through settings (typoscript or flexform) we skip the storage check
$respectStorage = true;
if ((int)$this->settings['mediaAlbum'] === (int)$mediaAlbum) {
$respectStorage = false;
}
$mediaAlbum = $this->mediaAlbumRepository->findByUid($mediaAlbum, $respectStorage);
$this->view->assign('mediaAlbum', $mediaAlbum);
$this->view->assign('showBackLink', false);
$this->view->assign('mediaAlbumPagination', $this->getAlbumPagination($mediaAlbum));
return $this->htmlResponse();
}
/**
* Show single media asset from album
*
* @param \MiniFranske\FsMediaGallery\Domain\Model\MediaAlbum $mediaAlbum
* @param int $mediaAssetUid
* @TYPO3\CMS\Extbase\Annotation\IgnoreValidation("")
*/
public function showAssetAction(MediaAlbum $mediaAlbum, int $mediaAssetUid): ResponseInterface
{
if (isset($this->settings['album']['assets']['orderBy'])) {
$mediaAlbum->setAssetsOrderBy($this->settings['album']['assets']['orderBy']);
}
if (isset($this->settings['album']['assets']['orderDirection'])) {
$mediaAlbum->setAssetsOrderDirection($this->settings['album']['assets']['orderDirection']);
}
list($previousAsset, $mediaAsset, $nextAsset) = $mediaAlbum->getPreviousCurrentAndNext($mediaAssetUid);
if (!$mediaAsset) {
$message = LocalizationUtility::translate('asset_not_found', 'fs_media_gallery');
return $this->pageNotFound((empty($message) ? 'Asset not found.' : $message));
}
$this->view->assign('previousAsset', $previousAsset);
$this->view->assign('nextAsset', $nextAsset);
$this->view->assign('mediaAsset', $mediaAsset);
$this->view->assign('mediaAlbum', $mediaAlbum);
return $this->htmlResponse();
}
/**
* Show random media asset
*/
public function randomAssetAction(): ResponseInterface
{
$this->setAlbumUidRestrictions();
$mediaAlbum = $this->mediaAlbumRepository->findRandom();
$this->view->assign('mediaAlbum', $mediaAlbum);
return $this->htmlResponse();
}
/**
* If there were validation errors, we don't want to write details like
* "An error occurred while trying to call Tx_Community_Controller_UserController->updateAction()"
*
* @return string|boolean The flash message or FALSE if no flash message should be set
*/
protected function getErrorFlashMessage(): bool
{
return false;
}
/**
* Page not found wrapper
*
* @throws ImmediateResponseException
*/
protected function pageNotFound(string $message): ResponseInterface
{
if (!empty($GLOBALS['TSFE'])) {
$response = GeneralUtility::makeInstance(ErrorController::class)->pageNotFoundAction($GLOBALS['TYPO3_REQUEST'], $message);
throw new ImmediateResponseException($response);
}
return $this->htmlResponse($message);
}
private function getAlbumPagination(MediaAlbum $album): array
{
$paginationConfiguration = $this->settings['album']['pagination'] ?? [];
$itemsPerPage = (int)($paginationConfiguration['itemsPerPage'] ?? 10);
$maximumNumberOfLinks = (int)($paginationConfiguration['maximumNumberOfLinks'] ?? 0);
$currentPage = $this->request->hasArgument('currentPage') ? (int)$this->request->getArgument('currentPage') : 1;
$paginator = GeneralUtility::makeInstance(ExtendedArrayPaginator::class, $album->getAssets(), $currentPage, $itemsPerPage);
$paginationClass = $paginationConfiguration['class'] ?? SimplePagination::class;
if ($paginationClass === NumberedPagination::class && $maximumNumberOfLinks) {
$pagination = GeneralUtility::makeInstance(NumberedPagination::class, $paginator, $maximumNumberOfLinks);
} else {
$pagination = GeneralUtility::makeInstance(SimplePagination::class, $paginator);
}
return [
'currentPage' => $currentPage,
'paginator' => $paginator,
'pagination' => $pagination,
];
}
private function getAlbumsPagination(array $albums): array
{
$paginationConfiguration = $this->settings['list']['pagination'] ?? [];
$itemsPerPage = (int)($paginationConfiguration['itemsPerPage'] ?? 10);
$maximumNumberOfLinks = (int)($paginationConfiguration['maximumNumberOfLinks'] ?? 0);
$currentPage = $this->request->hasArgument('currentAlbumPage') ? (int)$this->request->getArgument('currentAlbumPage') : 1;
$paginator = GeneralUtility::makeInstance(ArrayPaginator::class, $albums, $currentPage, $itemsPerPage);
$paginationClass = $paginationConfiguration['class'] ?? SimplePagination::class;
if ($paginationClass === NumberedPagination::class && $maximumNumberOfLinks) {
$pagination = GeneralUtility::makeInstance(NumberedPagination::class, $paginator, $maximumNumberOfLinks);
} else {
$pagination = GeneralUtility::makeInstance(SimplePagination::class, $paginator);
}
return [
'currentPage' => $currentPage,
'paginator' => $paginator,
'pagination' => $pagination,
];
}
}

View File

@@ -0,0 +1,519 @@
<?php
namespace MiniFranske\FsMediaGallery\Domain\Model;
/***************************************************************
* Copyright notice
*
* (c) 2014 Frans Saris <franssaris@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 3 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.
*
* 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!
***************************************************************/
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
use TYPO3\CMS\Core\Resource\FileCollectionRepository;
use MiniFranske\FsMediaGallery\Domain\Repository\MediaAlbumRepository;
use TYPO3\CMS\Core\Resource\File;
use TYPO3\CMS\Core\Resource\FileInterface;
use TYPO3\CMS\Core\Resource\FileReference;
/**
* Media album
*/
class MediaAlbum extends AbstractEntity
{
/**
* fileCollectionRepository
*
* @var \TYPO3\CMS\Core\Resource\FileCollectionRepository
*/
protected $fileCollectionRepository;
/**
* mediaAlbumRepository
*
* @var \MiniFranske\FsMediaGallery\Domain\Repository\MediaAlbumRepository
*/
protected $mediaAlbumRepository;
/**
* @var array
*/
protected $assetCache;
/**
* @var array
*/
protected $allowedMimeTypes = [];
/**
* @var string
*/
protected $assetsOrderBy = '';
/**
* @var string
*/
protected $assetsOrderDirection = 'asc';
/**
* @var bool
*/
protected $excludeEmptyAlbums = false;
/**
* Assets
* An array of File or FileReference
*
* @var array
*/
protected $assets;
/**
* @var integer
*/
protected $assetsCount;
/**
* @var bool
*/
protected $hidden;
/**
* Title
*
* @var \string
*/
protected $title;
/**
* Description visible online
*
* @var \string
*/
protected $webdescription;
/**
* @var \MiniFranske\FsMediaGallery\Domain\Model\MediaAlbum|NULL
* @TYPO3\CMS\Extbase\Annotation\ORM\Lazy
*/
protected $parentalbum;
/**
* Main asset
*
* @var \TYPO3\CMS\Extbase\Domain\Model\FileReference
*/
protected $mainAsset;
/**
* Child albums
*
* @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\MiniFranske\FsMediaGallery\Domain\Model\MediaAlbum>
* @TYPO3\CMS\Extbase\Annotation\ORM\Lazy
*/
public $albumCache;
/**
* @var \DateTime
*/
protected $datetime;
public function injectFileCollectionRepository(FileCollectionRepository $fileCollectionRepository): void
{
$this->fileCollectionRepository = $fileCollectionRepository;
}
public function injectMediaAlbumRepository(MediaAlbumRepository $mediaAlbumRepository): void
{
$this->mediaAlbumRepository = $mediaAlbumRepository;
}
/**
* Set allowedMimeTypes
*
* @param array $allowedMimeTypes
*/
public function setAllowedMimeTypes($allowedMimeTypes)
{
$this->allowedMimeTypes = $allowedMimeTypes;
}
/**
* Get allowedMimeTypes
*
* @return array $allowedMimeTypes
*/
public function getAllowedMimeTypes()
{
return $this->allowedMimeTypes;
}
/**
* Get assetsOrderBy
*
* @return string
*/
public function getAssetsOrderBy()
{
return $this->assetsOrderBy;
}
/**
* Set assetsOrderBy
*
* @param string $assetsOrderBy
*/
public function setAssetsOrderBy($assetsOrderBy)
{
$this->assetsOrderBy = $assetsOrderBy;
}
/**
* Get assetsOrderDirection
*
* @return string
*/
public function getAssetsOrderDirection()
{
return $this->assetsOrderDirection;
}
/**
* Set assetsOrderDirection
*
* @param string $assetsOrderDirection
*/
public function setAssetsOrderDirection($assetsOrderDirection)
{
$this->assetsOrderDirection = strtolower($assetsOrderDirection);
}
/**
* Get excludeEmptyAlbums
*
* @return bool
*/
public function getExcludeEmptyAlbums()
{
return $this->excludeEmptyAlbums;
}
/**
* Set excludeEmptyAlbums
*
* @param bool $excludeEmptyAlbums
*/
public function setExcludeEmptyAlbums($excludeEmptyAlbums)
{
$this->excludeEmptyAlbums = (bool)$excludeEmptyAlbums;
}
/**
* Set hidden
*
* @param boolean $hidden
*/
public function setHidden($hidden)
{
$this->hidden = $hidden;
}
/**
* Get hidden
*
* @return boolean
*/
public function getHidden()
{
return $this->hidden;
}
/**
* Returns the title
*
* @return \string $title
*/
public function getTitle()
{
return $this->title;
}
/**
* Sets the title
*
* @param \string $title
* @return void
*/
public function setTitle($title)
{
$this->title = $title;
}
/**
* Returns the webdescription
*
* @return \string $webdescription
*/
public function getWebdescription()
{
return $this->webdescription;
}
/**
* Sets the webdescription
*
* @param \string $webdescription
* @return void
*/
public function setWebdescription($webdescription)
{
$this->webdescription = $webdescription;
}
/**
* Set parentalbum
*
* @param \MiniFranske\FsMediaGallery\Domain\Model\MediaAlbum $parentalbum
*/
public function setParentalbum(\MiniFranske\FsMediaGallery\Domain\Model\MediaAlbum $parentalbum)
{
$this->parentalbum = $parentalbum;
}
/**
* Get parentalbum
*
* @return \MiniFranske\FsMediaGallery\Domain\Model\MediaAlbum
*/
public function getParentalbum()
{
return $this->parentalbum;
}
/**
* @return File[]|FileReference[]
*/
public function getAssets()
{
if ($this->assetCache === null) {
try {
/** @var $fileCollection \TYPO3\CMS\Core\Resource\Collection\AbstractFileCollection */
$fileCollection = $this->fileCollectionRepository->findByUid($this->getUid());
$fileCollection->loadContents();
$files = $fileCollection->getItems();
// check if file has right mimeType
if (count($this->allowedMimeTypes) > 0) {
foreach ($files as $key => $fileObject) {
/** @var $fileObject File|FileReference */
if (!in_array($fileObject->getMimeType(), $this->allowedMimeTypes)) {
unset($files[$key]);
}
}
// reset keys
$files = array_values($files);
}
if (trim($this->assetsOrderBy) !== '') {
$files = $this->orderAssets($files, $this->assetsOrderBy, $this->assetsOrderDirection);
}
$this->assetCache = $files;
} catch (\Exception $exception) {
// failing albums get disabled
$this->setHidden(true);
$this->mediaAlbumRepository->update($this);
$this->assetCache = [];
}
}
return $this->assetCache;
}
/**
* Get asset by uid
*
* @param int $assetUid
* @return File|FileReference|NULL
*/
public function getAssetByUid($assetUid)
{
foreach ($assets = $this->getAssets() as $asset) {
/** @var $asset File|FileReference */
if ((int)$assetUid === (int)$asset->getUid()) {
return $asset;
}
}
return null;
}
/**
* Get pevious, current and next asset by assetUid
*
* @param $assetUid
* @return FileInterface[]
*/
public function getPreviousCurrentAndNext($assetUid)
{
$previous = $last = $current = $next = null;
foreach ($assets = $this->getAssets() as $asset) {
if ($current !== null) {
$next = $asset;
break;
}
$previous = $last;
$last = $asset;
/** @var $asset File|FileReference */
if ((int)$assetUid === (int)$asset->getUid()) {
$current = $asset;
}
}
return [$previous, $current, $next];
}
/**
* @return array
* @deprecated Will be removed in next major version 2.*
*/
public function getAssetsUids()
{
trigger_error('MediaAlbum::getAssetsUid is deprecated and will be removed with next major version 2.*. Use getAssets() as this method can not handle static file collections', E_USER_DEPRECATED);
$assetsUids = [];
foreach ($assets = $this->getAssets() as $asset) {
/** @var $asset FileInterface */
$assetsUids[] = $asset->getUid();
}
return $assetsUids;
}
/**
* Get assetsCount
*
* @return integer
*/
public function getAssetsCount()
{
if ($this->assetCache === null) {
return count($this->getAssets());
}
return count($this->assetCache);
}
/**
* Get child albums
*
* @return \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\MiniFranske\FsMediaGallery\Domain\Model\MediaAlbum>>
*/
public function getAlbums()
{
if ($this->albumCache === null) {
$this->albumCache = $this->mediaAlbumRepository->findByParentalbum($this, $this->excludeEmptyAlbums);
}
return $this->albumCache;
}
/**
* Get random child album
*
* @return MediaAlbum
*/
public function getRandomAlbum()
{
$albums = $this->getAlbums();
return $albums[rand(0, count($albums) - 1)];
}
/**
* @return File|FileReference
*/
public function getMainAsset()
{
$mainAsset = null;
if ($this->mainAsset) {
$mainAsset = $this->mainAsset->getOriginalResource();
} else {
$assets = $this->getAssets();
$mainAsset = $assets !== [] ? $assets[0] : null;
}
return $mainAsset;
}
/**
* @return File|FileReference
*/
public function getRandomAsset()
{
$assets = $this->getAssets();
// if there is an asset, return it
if (count($assets)) {
return $assets[rand(1, count($assets)) - 1];
} else {
// try to fetch it from child album
$randomAlbum = $this->getRandomAlbum();
if ($randomAlbum) {
return $randomAlbum->getRandomAsset();
}
// album and child album are empty
return null;
}
}
/**
* Get datetime
*
* @return \DateTime
*/
public function getDatetime()
{
return $this->datetime;
}
/**
* Set date time
*
* @param \DateTime $datetime datetime
* @return void
*/
public function setDatetime($datetime)
{
$this->datetime = $datetime;
}
/**
* @param FileInterface[] $files
* @param string $orderBy
* @param string $direction
* @return FileInterface[]
*/
protected function orderAssets($files, $orderBy, $direction)
{
usort($files, function ($a, $b) use ($orderBy, $direction) {
if ($orderBy === 'crdate') {
$compare = $a->getCreationTime() > $b->getCreationTime();
} elseif (in_array($orderBy, ['content_creation_date', 'content_modification_date'], true)) {
$compare = $a->getProperty($orderBy) > $b->getProperty($orderBy);
} else {
$compare = strnatcasecmp($a->getProperty($orderBy), $b->getProperty($orderBy));
}
return $direction === 'desc' ? -1 * $compare : $compare;
});
return $files;
}
}

View File

@@ -0,0 +1,407 @@
<?php
namespace MiniFranske\FsMediaGallery\Domain\Repository;
/***************************************************************
* Copyright notice
*
* (c) 2014 Frans Saris <franssaris@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 3 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.
*
* 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!
***************************************************************/
use TYPO3\CMS\Extbase\Persistence\Repository;
use TYPO3\CMS\Extbase\Persistence\QueryInterface;
use MiniFranske\FsMediaGallery\Domain\Model\MediaAlbum;
/**
* MediaAlbumRepository
*/
class MediaAlbumRepository extends Repository
{
/**
* @var array default ordering
*/
protected $defaultOrderings = [
'sorting' => QueryInterface::ORDER_ASCENDING,
'crdate' => QueryInterface::ORDER_DESCENDING,
];
/**
* @var array
*/
protected $allowedAssetMimeTypes = [];
/**
* @var array
*/
protected $albumUids = [];
/**
* @var bool
*/
protected $useAlbumUidsAsExclude = false;
/**
* @var string
*/
protected $assetsOrderBy = '';
/**
* @var string
*/
protected $assetsOrderDirection = 'asc';
/**
* Set allowedAssetMimeTypes
*
* @param array $allowedAssetMimeTypes
*/
public function setAllowedAssetMimeTypes($allowedAssetMimeTypes)
{
$this->allowedAssetMimeTypes = $allowedAssetMimeTypes;
}
/**
* Get allowedAssetMimeTypes
*
* @return array $allowedAssetMimeTypes
*/
public function getAllowedAssetMimeTypes()
{
return $this->allowedAssetMimeTypes;
}
/**
* Get allowedAlbumUids
*
* @return array
*/
public function getAlbumUids()
{
return $this->albumUids;
}
/**
* Set allowedAlbumUids
*
* @param array $albumUids
*/
public function setAlbumUids($albumUids)
{
$this->albumUids = $albumUids;
}
/**
* Get useAlbumUidsAsExclude
*
* @return boolean
*/
public function getUseAlbumUidsAsExclude()
{
return $this->useAlbumUidsAsExclude;
}
/**
* Set useAlbumUidsAsExclude
*
* @param boolean $useAlbumUidsAsExclude
*/
public function setUseAlbumUidsAsExclude($useAlbumUidsAsExclude)
{
$this->useAlbumUidsAsExclude = $useAlbumUidsAsExclude;
}
/**
* Get assetsOrderBy
*
* @return string
*/
public function getAssetsOrderBy()
{
return $this->assetsOrderBy;
}
/**
* Set assetsOrderBy
*
* @param string $assetsOrderBy
*/
public function setAssetsOrderBy($assetsOrderBy)
{
$this->assetsOrderBy = $assetsOrderBy;
}
/**
* Get assetsOrderDirection
*
* @return string
*/
public function getAssetsOrderDirection()
{
return $this->assetsOrderDirection;
}
/**
* Set assetsOrderDirection
*
* @param string $assetsOrderDirection
*/
public function setAssetsOrderDirection($assetsOrderDirection)
{
$this->assetsOrderDirection = $assetsOrderDirection;
}
/**
* Get random sub album
*
* @param MediaAlbum|bool $parent parent MediaAlbum, FALSE for parent = 0 or NULL for no restriction by parent
* @return \MiniFranske\FsMediaGallery\Domain\Model\MediaAlbum|NULL
*/
public function findRandom($parent = null)
{
/** @var \TYPO3\CMS\Extbase\Persistence\Generic\Query $query */
$query = $this->createQuery();
$constraints = [];
// restrict by parent album
if ($parent !== null) {
$constraints[] = $query->equals('parentalbum', $parent ? $parent->getUid() : 0);
}
// restrict by given uids
if ($this->albumUids !== []) {
if ($this->useAlbumUidsAsExclude) {
$constraints[] = $query->logicalNot($query->in('uid', $this->albumUids));
} else {
$constraints[] = $query->in('uid', $this->albumUids);
}
}
$query->matching($query->logicalAnd($constraints));
$mediaAlbums = $query->execute()->toArray();
/** @var \MiniFranske\FsMediaGallery\Domain\Model\MediaAlbum $mediaAlbum */
$mediaAlbum = null;
if ($mediaAlbums) {
$mediaAlbum = $mediaAlbums[array_rand($mediaAlbums, 1)];
// set allowed asset mime types
$mediaAlbum->setAllowedMimeTypes($this->allowedAssetMimeTypes);
// set assets order
$mediaAlbum->setAssetsOrderBy($this->assetsOrderBy);
$mediaAlbum->setAssetsOrderDirection($this->assetsOrderDirection);
}
return $mediaAlbum;
}
/**
* Find albums by parent album
*
* @param MediaAlbum $parentAlbum
* @param boolean $excludeEmptyAlbums
* @param string $orderBy Sort albums by: datetime|crdate|sorting
* @param string $orderDirection Sort order: asc|desc
* @return MediaAlbum[]
*/
public function findByParentAlbum(
MediaAlbum $parentAlbum = null,
$excludeEmptyAlbums = true,
$orderBy = 'sorting',
$orderDirection = 'desc'
) {
$excludeEmptyAlbums = filter_var($excludeEmptyAlbums, FILTER_VALIDATE_BOOLEAN);
$query = $this->createQuery();
$constraints = [];
$constraints[] = $query->equals('parentalbum', $parentAlbum ?: 0);
if ($this->albumUids !== []) {
if ($this->useAlbumUidsAsExclude) {
$constraints[] = $query->logicalNot($query->in('uid', $this->albumUids));
} else {
$constraints[] = $query->in('uid', $this->albumUids);
}
}
$query->matching($query->logicalAnd($constraints));
$query->setOrderings($this->getOrderingsSettings($orderBy, $orderDirection));
$mediaAlbums = $query->execute()->toArray();
foreach ($mediaAlbums as $key => $mediaAlbum) {
/** @var $mediaAlbum \MiniFranske\FsMediaGallery\Domain\Model\MediaAlbum */
// set allowed asset mime types
$mediaAlbum->setAllowedMimeTypes($this->allowedAssetMimeTypes);
// set assets order
$mediaAlbum->setAssetsOrderBy($this->assetsOrderBy);
$mediaAlbum->setAssetsOrderDirection($this->assetsOrderDirection);
$mediaAlbum->setExcludeEmptyAlbums($excludeEmptyAlbums);
// exclude if album is empty
if (
$excludeEmptyAlbums
&&
$mediaAlbum->getAssetsCount() === 0
&&
count($mediaAlbum->getAlbums()) === 0
) {
unset($mediaAlbums[$key]);
}
}
// Reset array keys and return albums
return array_values($mediaAlbums);
}
/**
* Find album by Uid
*
* @param integer $uid The identifier of the MediaAlbum to find
* @param bool $respectStorage possibility to disable storage restriction
* @return MediaAlbum|NULL The matching media album if found, otherwise NULL
*/
public function findByUid($uid, $respectStorage = true)
{
$query = $this->createQuery();
$query->getQuerySettings()->setRespectSysLanguage(false);
if (!$respectStorage) {
$query->getQuerySettings()->setRespectStoragePage(false);
}
$constraints = [$query->equals('uid', (int)$uid)];
if ($this->albumUids !== []) {
if ($this->useAlbumUidsAsExclude) {
$constraints[] = $query->logicalNot($query->in('uid', $this->albumUids));
} else {
$constraints[] = $query->in('uid', $this->albumUids);
}
}
/** @var $mediaAlbum \MiniFranske\FsMediaGallery\Domain\Model\MediaAlbum */
$mediaAlbum = $query->matching($query->logicalAnd($constraints))->execute()->getFirst();
if ($mediaAlbum) {
// set allowed asset mime types
$mediaAlbum->setAllowedMimeTypes($this->allowedAssetMimeTypes);
// set assets order
$mediaAlbum->setAssetsOrderBy($this->assetsOrderBy);
$mediaAlbum->setAssetsOrderDirection($this->assetsOrderDirection);
}
return $mediaAlbum;
}
/**
* Find all albums
*
* @param boolean $excludeEmptyAlbums
* @param string $orderBy Sort albums by: datetime|crdate|sorting
* @param string $orderDirection Sort order: asc|desc
* @return MediaAlbum[]
*/
public function findAll($excludeEmptyAlbums = true, $orderBy = 'datetime', $orderDirection = 'desc')
{
$excludeEmptyAlbums = filter_var($excludeEmptyAlbums, FILTER_VALIDATE_BOOLEAN);
$query = $this->createQuery();
$query->setOrderings($this->getOrderingsSettings($orderBy, $orderDirection));
$query->getQuerySettings()->setRespectSysLanguage(false);
if ($this->albumUids !== []) {
if ($this->useAlbumUidsAsExclude) {
$query->matching($query->logicalNot($query->in('uid', $this->albumUids)));
} else {
$query->matching($query->in('uid', $this->albumUids));
}
}
$mediaAlbums = $query->execute()->toArray();
foreach ($mediaAlbums as $key => $mediaAlbum) {
/** @var $mediaAlbum \MiniFranske\FsMediaGallery\Domain\Model\MediaAlbum */
// set allowed asset mime types
$mediaAlbum->setAllowedMimeTypes($this->allowedAssetMimeTypes);
// set assets order
$mediaAlbum->setAssetsOrderBy($this->assetsOrderBy);
$mediaAlbum->setAssetsOrderDirection($this->assetsOrderDirection);
$mediaAlbum->setExcludeEmptyAlbums($excludeEmptyAlbums);
// exclude if album is empty
if (
$excludeEmptyAlbums
&&
$mediaAlbum->getAssetsCount() === 0
&&
count($mediaAlbum->getAlbums()) === 0
) {
unset($mediaAlbums[$key]);
}
}
// Reset array keys and return albums
return array_values($mediaAlbums);
}
/**
* Get orderings settings. Returns an array like:
* array(
* 'foo' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_ASCENDING,
* 'bar' => \TYPO3\CMS\Extbase\Persistence\QueryInterface::ORDER_DESCENDING
* )
*
* @param string $orderBy Sort albums by: datetime|crdate|sorting
* @param string $orderDirection Sort order: asc|desc
* @return array Orderings settings used by \TYPO3\CMS\Extbase\Persistence\QueryInterface->setOrderings()
*/
protected function getOrderingsSettings($orderBy = 'sorting', $orderDirection = 'asc')
{
// check orderDirection
if ($orderDirection === 'asc') {
$orderDirection = QueryInterface::ORDER_ASCENDING;
} else {
$orderDirection = QueryInterface::ORDER_DESCENDING;
}
// set $orderingsSettings by orderBy and orderDirection
switch ($orderBy) {
case 'datetime':
$orderingsSettings = [
'datetime' => $orderDirection,
'crdate' => $orderDirection
];
break;
case 'crdate':
$orderingsSettings = ['crdate' => $orderDirection];
break;
default:
// sorting
$orderingsSettings = [
'sorting' => $orderDirection,
'crdate' => $orderDirection
];
}
return $orderingsSettings;
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace MiniFranske\FsMediaGallery\Hooks;
/***************************************************************
* Copyright notice
*
* (c) 2014 Frans Saris <franssaris@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.
*
* 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!
***************************************************************/
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Backend\Controller\BackendController;
use TYPO3\CMS\Core\Page\PageRenderer;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* This class adds Filelist related JavaScript to the backend
*/
class BackendControllerHook
{
/**
* Adds Filelist JavaScript used e.g. by context menu
*
* @param array $configuration
* @param BackendController $backendController
*/
public function addJavaScript(array $configuration, BackendController $backendController)
{
$pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
/** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
$pageRenderer->addInlineSetting('FileEdit', 'moduleUrl', (string)$uriBuilder->buildUriFromRoute('file_edit'));
$pageRenderer->addInlineSetting('FileCreate', 'moduleUrl', (string)$uriBuilder->buildUriFromRoute('file_upload'));
}
}

View File

@@ -0,0 +1,78 @@
<?php
namespace MiniFranske\FsMediaGallery\Hooks;
/***************************************************************
* Copyright notice
*
* (c) 2014 Frans Saris <franssaris@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.
*
* 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!
***************************************************************/
use MiniFranske\FsMediaGallery\Service\AbstractBeAlbumButtons;
use TYPO3\CMS\Backend\Template\Components\ButtonBar;
use TYPO3\CMS\Core\Imaging\Icon;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Hook to add extra button to DocHeaderButtons in file list
*/
class DocHeaderButtonsHook extends AbstractBeAlbumButtons
{
protected function createLink(string $title, string $shortTitle, Icon $icon, string $url, bool $addReturnUrl = true): array
{
return [
'title' => $title,
'icon' => $icon,
'url' => $url . ($addReturnUrl ? '&returnUrl=' . rawurlencode($_SERVER['REQUEST_URI']) : '')
];
}
/**
* Add media album buttons to file list
*/
public function moduleTemplateDocHeaderGetButtons(array $params, ButtonBar $buttonBar): array
{
$buttons = $params['buttons'];
if (GeneralUtility::_GP('M') === 'file_FilelistList'
|| GeneralUtility::_GP('route') === '/file/FilelistList/'
|| GeneralUtility::_GP('route') === '/module/file/FilelistList'
) {
foreach ($this->generateButtons((string)GeneralUtility::_GP('id')) as $buttonInfo) {
$button = $buttonBar->makeLinkButton();
$button->setShowLabelText(true);
$button->setIcon($buttonInfo['icon']);
$button->setTitle($buttonInfo['title']);
if (strpos($buttonInfo['url'], 'alert') === 0) {
// using CSS class to trigger confirmation in modal box
$button->setClasses('t3js-modal-trigger')
->setDataAttributes([
'severity' => 'warning',
'title' => $buttonInfo['title'],
'bs-content' => htmlspecialchars(substr($buttonInfo['url'], 6)),
]);
} else {
$button->setHref($buttonInfo['url']);
}
$buttons['left'][2][] = $button;
}
}
return $buttons;
}
}

View File

@@ -0,0 +1,138 @@
<?php
namespace MiniFranske\FsMediaGallery\Hooks;
/* *
* This script is part of the TYPO3 project. *
* *
* It is free software; you can redistribute it and/or modify it under *
* the terms of the GNU Lesser General Public License, either version 3 *
* of the License, or (at your option) any later version. *
* *
* The TYPO3 project - inspiring people to share! *
* */
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* ItemsProcFuncHook
*/
class ItemsProcFuncHook
{
/**
* Sets the available actions for settings.switchableControllerActions
*
* @param array &$config
* @return void
*/
public function getItemsForSwitchableControllerActions(array &$config)
{
$availableActions = [
'nestedList' => 'MediaAlbum->nestedList;MediaAlbum->showAsset',
'flatList' => 'MediaAlbum->flatList;MediaAlbum->showAlbum;MediaAlbum->showAsset',
'showAlbumByParam' => 'MediaAlbum->showAlbum;MediaAlbum->showAsset',
'showAlbumByConfig' => 'MediaAlbum->showAlbumByConfig;MediaAlbum->showAsset',
'randomAsset' => 'MediaAlbum->randomAsset',
];
$extConf = $this->getExtensionConfiguration();;
$allowedActions = [
// index action is always allowed
// this is needed to make sure the correct tabs/fields are shown in
// flexform when a new plugin is added
'index' => 'MediaAlbum->index',
];
$allowedActionsFromExtConf = [];
if (!empty($extConf['allowedActionsInFlexforms'])) {
$allowedActionsFromExtConf = GeneralUtility::trimExplode(',', $extConf['allowedActionsInFlexforms']);
}
foreach ($allowedActionsFromExtConf as $allowedActionFromExtConf) {
if (!empty($availableActions[$allowedActionFromExtConf])) {
$allowedActions[$allowedActionFromExtConf] = $availableActions[$allowedActionFromExtConf];
}
}
// check items; allow all actions if something went wrong (no action except of "indexAction" is allowed)
if (count($allowedActions) > 1) {
foreach ($config['items'] as $key => $item) {
if (!in_array($item[1], $allowedActions)) {
unset($config['items'][$key]);
}
}
}
}
/**
* Sets the available options for settings.list.orderBy
*
* @param array &$config
* @return void
*/
public function getItemsForListOrderBy(array &$config)
{
$availableOptions = ['datetime', 'crdate', 'sorting'];
$extConf = $this->getExtensionConfiguration();;
$allowedOptions = [];
$allowedOptionsFromExtConf = [];
if (!empty($extConf['list']['orderOptions'])) {
$allowedOptionsFromExtConf = GeneralUtility::trimExplode(',', $extConf['list']['orderOptions']);
}
foreach ($allowedOptionsFromExtConf as $allowedOptionFromExtConf) {
if (in_array($allowedOptionFromExtConf, $availableOptions)) {
$allowedOptions[] = $allowedOptionFromExtConf;
}
}
foreach ($config['items'] as $key => $item) {
// check items; empty value (inherit from TS) is always allowed
if (!empty($item[1]) && !in_array($item[1], $allowedOptions)) {
unset($config['items'][$key]);
}
}
}
/**
* Sets the available options for settings.album.assets.orderBy
*
* @param array &$config
* @return void
*/
public function getItemsForAssetsOrderBy(array &$config)
{
// default set
$allowedOptions = ['name', 'crdate', 'title', 'content_creation_date', 'content_modification_date'];
$availableOptions = [];
$extConf = $this->getExtensionConfiguration();;
if (!empty($extConf['asset']['orderOptions'])) {
$allowedOptions = GeneralUtility::trimExplode(',', $extConf['asset']['orderOptions']);
}
// check if field exists in TCA of sys_file or sys_file_metadata
foreach ($allowedOptions as $key => $option) {
if (
$option === 'crdate'
||
!empty($GLOBALS['TCA']['sys_file']['columns'][$option])
||
!empty($GLOBALS['TCA']['sys_file_metadata']['columns'][$option])
) {
$availableOptions[] = $option;
}
}
// @todo: add option to add custom options to the item list
// use label from TCA
foreach ($config['items'] as $key => $item) {
// check items; empty value (inherit from TS) is always allowed
if (!empty($item[1]) && !in_array($item[1], $availableOptions)) {
unset($config['items'][$key]);
}
}
}
/**
* Get extension configuration
*/
protected function getExtensionConfiguration() {
return GeneralUtility::makeInstance(
ExtensionConfiguration::class
)->get('fs_media_gallery');
}
}

View File

@@ -0,0 +1,250 @@
<?php
namespace MiniFranske\FsMediaGallery\Hooks;
/*
* This source file is proprietary property of Beech Applications B.V.
* Date: 28-09-2016
* All code (c) Beech Applications B.V. all rights reserved
*/
use TYPO3\CMS\Backend\Utility\BackendUtility as BackendUtilityCore;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Database\Connection;
/**
* Class PageLayoutView
*/
class PageLayoutView
{
/**
* @var string
*/
const LLPATH = 'LLL:EXT:fs_media_gallery/Resources/Private/Language/locallang_be.xlf:';
/**
* @var array
*/
private $summaryData = [];
/**
* @var array
*/
private $flexFormData = [];
public function getExtensionSummary(array $params): string
{
/** @var \TYPO3\CMS\Backend\View\PageLayoutView $pageLayoutView */
$pageLayoutView = $params['pObj'];
$row = $params['row'];
$this->flexFormData = GeneralUtility::xml2array($row['pi_flexform']);
$action = $this->getAction();
$result = '<strong>' . $pageLayoutView->linkEditContent($this->getLanguageService()->sL(self::LLPATH . 'mediagallery.title'), $row) . '</strong><br>';
$result .= $this->getDisplayMode($action);
$result .= '<hr>';
if (in_array($action, ['showAlbum', 'showAlbumByConfig'], true)) {
$this->addSelectedAlbumToSettingsSummary();
} else {
$this->addSelectedAlbumsToSettingsSummary();
}
$this->addStartingPointToSettingsSummary();
$result .= $this->renderSettingsAsTable();
return $result;
}
private function getAction(): string
{
// if flexForm data is found
$actions = $this->getFieldFromFlexform('switchableControllerActions');
$action = '';
if (!empty($actions)) {
$actionList = GeneralUtility::trimExplode(';', $actions);
// translate the first action into its translation
$action = str_replace('MediaAlbum->', '', $actionList[0]);
}
return $action;
}
private function getDisplayMode(string $action): string
{
switch ($action) {
case 'showalbum';
$actionTranslationKey = 'showAlbumByParam';
break;
default:
$actionTranslationKey = $action;
}
return $this->getLanguageService()->sL(self::LLPATH . 'flexforms.mediagallery.switchableControllerActions.I.' . $actionTranslationKey);
}
/**
* Get field value from flexform configuration,
* including checks if flexform configuration is available
*
* @param string $key name of the key
* @param string $sheet name of the sheet
* @return string|NULL if nothing found, value if found
*/
private function getFieldFromFlexform(string $key, string $sheet = 'general'): ?string
{
$flexform = $this->flexFormData;
if (isset($flexform['data'])) {
$flexform = $flexform['data'];
if (is_array($flexform) && is_array($flexform[$sheet]) && is_array($flexform[$sheet]['lDEF'])
&& is_array($flexform[$sheet]['lDEF'][$key]) && isset($flexform[$sheet]['lDEF'][$key]['vDEF'])
) {
return $flexform[$sheet]['lDEF'][$key]['vDEF'];
}
}
return null;
}
private function addSelectedAlbumToSettingsSummary(): void
{
$albumUid = (int)$this->getFieldFromFlexform('settings.mediaAlbum');
if ((int)$albumUid > 0) {
// Album record
$rowSysFileCollectionRecords = $this->getDatabaseConnection()->select(['*'], 'sys_file_collection', [
'uid' => (int)$albumUid,
'deleted' => 0,
])->fetchAllAssociative();
$albums = [];
foreach ($rowSysFileCollectionRecords as $record) {
$albums[] = htmlspecialchars(BackendUtilityCore::getRecordTitle('sys_file_collection', $record));
}
$this->summaryData[] = [
$this->getLanguageService()->sL(self::LLPATH . 'flexforms.mediagallery.mediaAlbum') .
'<br />',
implode(', ', $albums)
];
}
}
private function addSelectedAlbumsToSettingsSummary(): void
{
$filterMode = '';
$albums = [];
$albumUids = GeneralUtility::intExplode(',', $this->getFieldFromFlexform('settings.mediaAlbumsUids'), true);
if (count($albumUids) > 0) {
// Filter mode
$selectedFilerMode = $this->getFieldFromFlexform('settings.useAlbumFilterAsExclude');
if ($selectedFilerMode !== '') {
$filterMode = $this->getLanguageService()->sL(self::LLPATH . 'flexforms.general.I.inherit');
$filterMode = '<span style="font-weight:normal;font-style:italic">(' . htmlspecialchars($filterMode) . ')</span>';
}
// Album records
$q = $this->getDatabaseConnection()->createQueryBuilder();
$q->getRestrictions()
->removeAll()
->add(GeneralUtility::makeInstance(DeletedRestriction::class));
$quotedIdentifiers = $q->createNamedParameter($albumUids, Connection::PARAM_INT_ARRAY);
$q->select('*')
->from('sys_file_collection')
->where(
$q->expr()->in('uid', $quotedIdentifiers)
);
$rowSysFileCollectionRecords = $q->execute()->fetchAllAssociative();
foreach ((array)$rowSysFileCollectionRecords as $record) {
$albums[] = htmlspecialchars(BackendUtilityCore::getRecordTitle('sys_file_collection', $record));
}
$this->summaryData[] = [
$this->getLanguageService()->sL(self::LLPATH . 'flexforms.mediagallery.mediaAlbumsUids') .
'<br />' . $filterMode,
implode(', ', $albums)
];
}
}
private function addStartingPointToSettingsSummary(): void
{
$value = $this->getFieldFromFlexform('settings.startingpoint');
if (!empty($value)) {
$pagesOut = [];
$q = $this->getDatabaseConnection('pages')->createQueryBuilder();
$quotedIdentifiers = $q->createNamedParameter(GeneralUtility::intExplode(',', $value, true), Connection::PARAM_INT_ARRAY);
$q->select('*')
->from('pages')
->where(
$q->expr()->in('uid', $quotedIdentifiers)
);
$rawPagesRecords = $q->execute()->fetchAllAssociative();
foreach ((array)$rawPagesRecords as $page) {
$pagesOut[] = htmlspecialchars(BackendUtilityCore::getRecordTitle('pages',
$page)) . ' (' . $page['uid'] . ')';
}
$recursiveLevel = (int)$this->getFieldFromFlexform('settings.recursive');
$recursiveLevelText = '';
if ($recursiveLevel === 250) {
$recursiveLevelText = $this->getLanguageService()->sL('LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:recursive.I.5');
} elseif ($recursiveLevel > 0) {
$recursiveLevelText = $this->getLanguageService()->sL('LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:recursive.I.' . $recursiveLevel);
}
if (!empty($recursiveLevelText)) {
$recursiveLevelText = '<br />' .
$this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.recursive') . ' ' .
$recursiveLevelText;
}
$this->summaryData[] = [
$this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_general.xlf:LGL.startingpoint'),
implode(', ', $pagesOut) . $recursiveLevelText
];
}
}
/**
* Render the settings as table for Web>Page module
*
* System settings are displayed in mono font
*/
private function renderSettingsAsTable(): string
{
if (count($this->summaryData) == 0) {
return '';
}
$content = '';
foreach ($this->summaryData as $line) {
$content .= '<strong>' . $line[0] . '</strong>' . ' ' . $line[1] . '<br />';
}
return '<pre style="white-space:normal">' . $content . '</pre>';
}
private function getLanguageService(): LanguageService
{
return $GLOBALS['LANG'];
}
private function getDatabaseConnection(string $table = 'sys_file_collection'): Connection
{
return GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table);
}
}

View File

@@ -0,0 +1,81 @@
<?php
namespace MiniFranske\FsMediaGallery\Hooks;
/***************************************************************
* Copyright notice
*
* (c) 2014 Frans Saris <franssaris@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.
*
* 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!
***************************************************************/
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Backend\Utility\BackendUtility;
/**
* Hooks called after sys_file_collection is added/updated/deleted
*/
class ProcessDatamapHook
{
/**
* Trigger updateFolderTree after change in sys_file_collection
*
* @param string $status
* @param string $table
* @param $id
* @param array $fieldArray
* @param \TYPO3\CMS\Core\DataHandling\DataHandler $dataHandler
*/
public function processDatamap_afterDatabaseOperations(
$status,
$table,
$id,
array $fieldArray,
DataHandler $dataHandler
) {
if ($table === 'sys_file_collection') {
BackendUtility::setUpdateSignal('updateFolderTree');
}
}
/**
* Trigger updateFolderTree after a sys_file_collection record is deleted
*
* @param string $command
* @param string $table
* @param int $id
* @param mixed $value
* @param \TYPO3\CMS\Core\DataHandling\DataHandler $dataHandler
* @param mixed $pasteUpdate
* @param array $pasteDatamap
*/
public function processCmdmap_postProcess(
$command,
$table,
$id,
$value,
DataHandler $dataHandler,
$pasteUpdate,
array $pasteDatamap
) {
if ($table === 'sys_file_collection') {
BackendUtility::setUpdateSignal('updateFolderTree');
}
}
}

View File

@@ -0,0 +1,241 @@
<?php
namespace MiniFranske\FsMediaGallery\Listeners;
/***************************************************************
* Copyright notice
*
* (c) 2014 Frans Saris <franssaris@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.
*
* 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!
***************************************************************/
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Configuration\ExtensionConfiguration;
use TYPO3\CMS\Core\Resource\Event\AfterFileAddedEvent;
use TYPO3\CMS\Core\Resource\Event\AfterFileCopiedEvent;
use TYPO3\CMS\Core\Resource\Event\AfterFileCreatedEvent;
use TYPO3\CMS\Core\Resource\Event\AfterFileDeletedEvent;
use TYPO3\CMS\Core\Resource\Event\AfterFileMovedEvent;
use TYPO3\CMS\Core\Resource\Event\AfterFileRenamedEvent;
use TYPO3\CMS\Core\Resource\Event\AfterFileReplacedEvent;
use TYPO3\CMS\Core\Resource\Event\AfterFolderAddedEvent;
use TYPO3\CMS\Core\Resource\Event\AfterFolderDeletedEvent;
use TYPO3\CMS\Core\Resource\Event\AfterFolderMovedEvent;
use TYPO3\CMS\Core\Resource\Event\BeforeFolderDeletedEvent;
use TYPO3\CMS\Core\Resource\Event\BeforeFolderMovedEvent;
use MiniFranske\FsMediaGallery\Service\Utility;
use TYPO3\CMS\Core\Resource\Folder;
use TYPO3\CMS\Core\Resource\FolderInterface;
/**
* Slots that pick up signals after (re)moving folders to update mediagallery record (sys_file_collection)
*/
final class FolderChangedListener
{
protected $folderMapping = [];
/**
* @var Utility
*/
private $utilityService;
/**
* @var ExtensionConfiguration
*/
private $extensionConfiguration;
public function __construct(Utility $utilityService, ExtensionConfiguration $extensionConfiguration)
{
$this->utilityService = $utilityService;
$this->extensionConfiguration = $extensionConfiguration;
}
/**
* Get sub folder structure of folder before is gets moved
* Is needed to update sys_file_collection records when move was successful
*/
public function preFolderMove(BeforeFolderMovedEvent $event): void
{
$this->folderMapping[$event->getFolder()->getCombinedIdentifier()] = $this->getSubFolderIdentifiers($event->getFolder());
}
/**
* Update sys_file_collection records when folder is moved
*/
public function postFolderMove(AfterFolderMovedEvent $event): void
{
if (!$event->getTargetFolder()) {
return;
}
$newFolder = $event->getTargetFolder();
$oldStorageUid = $event->getFolder()->getStorage()->getUid();
$newStorageUid = $newFolder->getStorage()->getUid();
$newParentId = null;
// If this is a subFolder find new parent album
if ($newFolder->getParentFolder() !== $newFolder) {
$newParentId = $this->utilityService->findFileCollectionRecordsForFolder(
$newStorageUid,
$event->getFolder()->getParentFolder()->getIdentifier()
)[0]['parentalbum'] ?? null;
}
$this->utilityService->updateFolderRecord(
$oldStorageUid,
$event->getFolder()->getIdentifier(),
$newStorageUid,
$newFolder->getIdentifier(),
$newParentId
);
if (!empty($this->folderMapping[$event->getFolder()->getCombinedIdentifier()])) {
$newMapping = $this->getSubFolderIdentifiers($newFolder);
foreach ($this->folderMapping[$event->getFolder()->getCombinedIdentifier()] as $key => $folderInfo) {
$this->utilityService->updateFolderRecord(
$oldStorageUid,
$folderInfo[1],
$newStorageUid,
$newMapping[$key][1]
);
}
}
}
/**
* Get sub folder structure of folder before is gets deleted
* Is needed to update sys_file_collection records when delete was successful
*/
public function preFolderDelete(BeforeFolderDeletedEvent $event): void
{
$this->folderMapping[$event->getFolder()->getCombinedIdentifier()] = $this->getSubFolderIdentifiers($event->getFolder());
}
/**
* Update sys_file_collection records when folder is deleted
*/
public function postFolderDelete(AfterFolderDeletedEvent $event): void
{
$folder = $event->getFolder();
$storageUid = $folder->getStorage()->getUid();
$this->utilityService->deleteFolderRecord($storageUid, $folder->getIdentifier());
foreach ($this->folderMapping[$folder->getCombinedIdentifier()] as $folderInfo) {
$this->utilityService->deleteFolderRecord($storageUid, $folderInfo[1]);
}
$this->clearMediaGalleryPageCache($folder);
}
/**
* Auto creates a file collection to the first parentCollection found of the current folder,
* when no collection is found nothing is created
*/
public function postFolderAdd(AfterFolderAddedEvent $event): void
{
if (!$this->getConfigFlag('enableAutoCreateFileCollection')) {
return;
}
$folder = $event->getFolder();
$mediaFolders = $this->utilityService->getStorageFolders();
if (count($mediaFolders) === 0) {
return;
}
foreach ($mediaFolders as $uid => $title) {
$parents = $this->utilityService->getFirstParentCollections($folder, $uid);
if (count($parents) === 0) {
continue;
}
// Take the first parent found
$parentUid = $parents[0]['uid'];
$this->utilityService->createFolderRecord(
ucfirst(trim(str_replace('_', ' ', $folder->getName()))),
$uid,
$folder->getStorage()->getUid(),
$folder->getIdentifier(),
$parentUid
);
$this->clearMediaGalleryPageCache($folder);
}
}
public function postFileAdd(AfterFileAddedEvent $event): void
{
$this->clearMediaGalleryPageCache($event->getFolder());
}
public function postFileCreate(AfterFileCreatedEvent $event): void
{
$this->clearMediaGalleryPageCache($event->getFolder());
}
public function postFileCopy(AfterFileCopiedEvent $event): void
{
$this->clearMediaGalleryPageCache($event->getFolder());
}
public function postFileMove(AfterFileMovedEvent $event)
{
$this->clearMediaGalleryPageCache($event->getOriginalFolder());
$this->clearMediaGalleryPageCache($event->getFolder());
}
public function postFileDelete(AfterFileDeletedEvent $event): void
{
$this->clearMediaGalleryPageCache($event->getFile()->getParentFolder());
}
public function postFileRename(AfterFileRenamedEvent $event): void
{
$this->clearMediaGalleryPageCache($event->getFile()->getParentFolder());
}
public function postFileReplace(AfterFileReplacedEvent $event): void
{
$this->clearMediaGalleryPageCache($event->getFile()->getParentFolder());
}
private function clearMediaGalleryPageCache(FolderInterface $folder): void
{
if ($this->getBackendUser() && $this->getConfigFlag('clearCacheAfterFileChange')) {
$this->utilityService->clearMediaGalleryPageCache($folder);
}
}
private function getSubFolderIdentifiers(Folder $folder): array
{
$folderIdentifiers = [];
foreach ($folder->getSubfolders() as $subFolder) {
$folderIdentifiers[] = [$subFolder->getHashedIdentifier(), $subFolder->getIdentifier()];
$folderIdentifiers = array_merge($folderIdentifiers, $this->getSubFolderIdentifiers($subFolder));
}
return $folderIdentifiers;
}
private function getConfigFlag(string $flag): bool
{
return (bool)($this->extensionConfiguration->get('fs_media_gallery')[$flag] ?? false);
}
private function getBackendUser(): ?BackendUserAuthentication
{
return $GLOBALS['BE_USER'] ?? null;
}
}

View File

@@ -0,0 +1,100 @@
<?php
namespace MiniFranske\FsMediaGallery\Listeners;
/***************************************************************
* Copyright notice
*
* (c) 2016 Frans Saris <franssaris@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.
*
* 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!
***************************************************************/
use MiniFranske\FsMediaGallery\Service\Utility;
use TYPO3\CMS\Core\Imaging\Event\ModifyIconForResourcePropertiesEvent;
use TYPO3\CMS\Core\Resource\FolderInterface;
use TYPO3\CMS\Core\Resource\Folder;
/**
* Class IconFactory
*/
class IconFactoryListener
{
/**
* @var array
*/
static private $mediaFolders;
/**
* @var Utility
*/
private $utilityService;
public function __construct(Utility $utilityService)
{
$this->utilityService = $utilityService;
}
public function buildIconForResource(
ModifyIconForResourcePropertiesEvent $event
): void
{
$folderObject = $event->getResource();
if (!($folderObject instanceof Folder)
|| !in_array($folderObject->getRole(), [FolderInterface::ROLE_DEFAULT, FolderInterface::ROLE_USERUPLOAD])
) {
return;
}
$mediaFolders = $this->getMediaFolders();
if ($mediaFolders === []) {
return;
}
$collections = $this->utilityService->findFileCollectionRecordsForFolder(
$folderObject->getStorage()->getUid(),
$folderObject->getIdentifier(),
array_keys($mediaFolders)
);
if (!$collections) {
return;
}
$event->setIconIdentifier('tcarecords-sys_file_collection-folder');
$hidden = true;
foreach ($collections as $collection) {
if ((int)$collection['hidden'] === 0) {
$hidden = false;
break;
}
}
if ($hidden) {
$event->setOverlayIdentifier('overlay-hidden');
}
}
private function getMediaFolders(): array
{
if (self::$mediaFolders === null) {
self::$mediaFolders = $this->utilityService->getStorageFolders();
}
return self::$mediaFolders;
}
}

View File

@@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
namespace MiniFranske\FsMediaGallery\Pagination;
use TYPO3\CMS\Core\Pagination\AbstractPaginator;
class ExtendedArrayPaginator extends AbstractPaginator
{
/**
* @var array
*/
private $items;
/**
* @var array
*/
private $paginatedItems = [];
/**
* @var array
*/
private $itemsBefore = [];
/**
* @var array
*/
private $itemsAfter = [];
public function __construct(
array $items,
int $currentPageNumber = 1,
int $itemsPerPage = 10
)
{
$this->items = $items;
$this->setCurrentPageNumber($currentPageNumber);
$this->setItemsPerPage($itemsPerPage);
$this->updateInternalState();
}
/**
* @return iterable|array
*/
public function getPaginatedItems(): iterable
{
return $this->paginatedItems;
}
protected function updatePaginatedItems(int $itemsPerPage, int $offset): void
{
$this->itemsBefore = array_slice($this->items, 0, $offset);
$this->paginatedItems = array_slice($this->items, $offset, $itemsPerPage);
$this->itemsAfter = array_slice($this->items, $offset + $itemsPerPage);
}
protected function getTotalAmountOfItems(): int
{
return count($this->items);
}
protected function getAmountOfItemsOnCurrentPage(): int
{
return count($this->paginatedItems);
}
public function getItemsBefore(): iterable
{
return $this->itemsBefore;
}
public function getItemsAfter(): iterable
{
return $this->itemsAfter;
}
}

View File

@@ -0,0 +1,185 @@
<?php
namespace MiniFranske\FsMediaGallery\Service;
/***************************************************************
* Copyright notice
*
* (c) 2014 Frans Saris <franssaris@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.
*
* 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!
***************************************************************/
use TYPO3\CMS\Core\Localization\LanguageService;
use TYPO3\CMS\Core\Resource\FolderInterface;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Resource\Exception\ResourceDoesNotExistException;
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Core\Imaging\Icon;
use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Resource\Folder;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Abstract utility class for classes that want to add album add/edit buttons
* somewhere like a ClickMenuOptions class.
*/
abstract class AbstractBeAlbumButtons
{
/**
* Generate album add/edit buttons for click menu or toolbar
*/
protected function generateButtons(string $combinedIdentifier): array
{
$buttons = [];
// In some folder copy/move actions in file list a invalid id is passed
try {
/** @var $file \TYPO3\CMS\Core\Resource\Folder */
$folder = GeneralUtility::makeInstance(ResourceFactory::class)
->retrieveFileOrFolderObject($combinedIdentifier);
} catch (ResourceDoesNotExistException $exception) {
$folder = null;
}
if ($folder instanceof Folder &&
in_array(
$folder->getRole(),
[FolderInterface::ROLE_DEFAULT, FolderInterface::ROLE_USERUPLOAD]
)
) {
/** @var Utility $utility */
$utility = GeneralUtility::makeInstance(Utility::class);
$mediaFolders = $utility->getStorageFolders();
if (count($mediaFolders)) {
$collections = $utility->findFileCollectionRecordsForFolder(
$folder->getStorage()->getUid(),
$folder->getIdentifier(),
array_keys($mediaFolders)
);
foreach ($collections as $collection) {
$buttons[] = $this->createLink(
sprintf($this->sL('module.buttons.editAlbum'), $collection['title']),
sprintf(
$this->sL('module.buttons.editAlbum'),
mb_strimwidth($collection['title'], 0,12, '...')
),
$this->getIcon('edit-album'),
$this->buildEditUrl($collection['uid'])
);
}
if (!count($collections)) {
foreach ($mediaFolders as $uid => $title) {
// find parent album for auto setting parent album
$parentUid = 0;
$parents = $utility->findFileCollectionRecordsForFolder(
$folder->getStorage()->getUid(),
$folder->getParentFolder()->getIdentifier(),
[$uid]
);
// if parent(s) found we take the first one
if (count($parents)) {
$parentUid = $parents[0]['uid'];
}
$buttons[] = $this->createLink(
sprintf($this->sL('module.buttons.createAlbumIn'), $title),
sprintf(
$this->sL('module.buttons.createAlbumIn'),
mb_strimwidth($title, 0, 12, '...')
),
$this->getIcon('add-album'),
$this->buildAddUrl($uid, $parentUid, $folder)
);
}
}
// show hint button for admin users
// todo: make this better so it can also be used for editors with enough rights to create a storageFolder
} elseif ($GLOBALS['BE_USER']->isAdmin()) {
$buttons[] = $this->createLink(
$this->sL('module.buttons.createAlbum'),
$this->sL('module.buttons.createAlbum'),
$this->getIcon('add-album'),
'alert:' . $this->sL('module.alerts.firstCreateStorageFolder'),
false
);
}
}
return $buttons;
}
protected function buildEditUrl(int $mediaAlbumUid): string
{
return GeneralUtility::makeInstance(UriBuilder::class)->buildUriFromRoute('record_edit', [
'edit' => [
'sys_file_collection' => [
$mediaAlbumUid => 'edit'
]
],
'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
]);
}
/**
* Build Add new media album url
*/
protected function buildAddUrl(int $pid, int $parentAlbumUid, Folder $folder): string
{
return GeneralUtility::makeInstance(UriBuilder::class)->buildUriFromRoute('record_edit', [
'edit' => [
'sys_file_collection' => [
$pid => 'new'
]
],
'defVals' => [
'sys_file_collection' => [
'parentalbum' => $parentAlbumUid,
'title' => ucfirst(trim(str_replace('_', ' ', $folder->getName()))),
'storage' => $folder->getStorage()->getUid(),
'folder' => $folder->getIdentifier(),
'type' => 'folder',
]
],
'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
]);
}
abstract protected function createLink(string $title, string $shortTitle, Icon $icon, string $url, bool $addReturnUrl = true): array;
protected function getIcon(string $name): Icon
{
$iconFactory = GeneralUtility::makeInstance(IconFactory::class);
return $iconFactory->getIcon('action-' . $name, Icon::SIZE_SMALL);
}
protected function getLangService(): LanguageService
{
return $GLOBALS['LANG'];
}
/**
* Get language string
*/
protected function sL(string $key, string $languageFile = 'LLL:EXT:fs_media_gallery/Resources/Private/Language/locallang_be.xlf'): string
{
return $this->getLangService()->sL($languageFile . ':' . $key);
}
}

View File

@@ -0,0 +1,258 @@
<?php
/*
* This source file is proprietary of Beech Applications bv.
* Created by: Ruud Silvrants
* Date: 30/04/2019
* All code (c) Beech Applications bv. all rights reserverd
*/
namespace MiniFranske\FsMediaGallery\Service;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
use TYPO3\CMS\Core\DataHandling\SlugHelper;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Class SlugService
* @package MiniFranske\FsMediaGallery\Service
*/
class SlugService
{
protected $tableName = 'sys_file_collection';
protected $slugFieldName = 'slug';
/**
* @return bool
*/
public function typo3SupportsSlugs(): bool
{
return class_exists(SlugHelper::class);
}
/**
* @return int
*/
public function countOfSlugUpdates(): int
{
/** @var QueryBuilder $queryBuilder */
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->tableName);
$queryBuilder->getRestrictions()->removeAll();
$elementCount = $queryBuilder->count('uid')
->from($this->tableName)
->where(
$queryBuilder->expr()->orX(
$queryBuilder->expr()->eq(
$this->slugFieldName,
$queryBuilder->createNamedParameter('', \PDO::PARAM_STR)
),
$queryBuilder->expr()->isNull($this->slugFieldName)
)
)
->execute()->fetchOne();
return $elementCount;
}
/**
* @return array
*/
public function performUpdateSlugs(): array
{
$databaseQueries = [];
/** @var Connection $connection */
$connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($this->tableName);
/** @var QueryBuilder $queryBuilder */
$queryBuilder = $connection->createQueryBuilder();
$queryBuilder->getRestrictions()->removeAll();
$fieldConfig = $GLOBALS['TCA'][$this->tableName]['columns'][$this->slugFieldName]['config'];
/** @var SlugHelper $slugHelper */
$slugHelper = GeneralUtility::makeInstance(SlugHelper::class, $this->tableName, $this->slugFieldName,
$fieldConfig);
$statement = $queryBuilder->select('*')
->from($this->tableName)
->where(
$queryBuilder->expr()->orX(
$queryBuilder->expr()->eq(
$this->slugFieldName,
$queryBuilder->createNamedParameter('', \PDO::PARAM_STR)
),
$queryBuilder->expr()->isNull($this->slugFieldName)
)
)
->execute();
while ($record = $statement->fetchAssociative()) {
//Use the core slughelper which is also used in the BE form
$slug = $slugHelper->generate($record, $record['pid']);
/** @var QueryBuilder $queryBuilder */
$queryBuilder = $connection->createQueryBuilder();
$queryBuilder->update($this->tableName)
->where(
$queryBuilder->expr()->eq(
'uid',
$queryBuilder->createNamedParameter($record['uid'], \PDO::PARAM_INT)
)
)
->set($this->slugFieldName, $slug);
$databaseQueries[] = $queryBuilder->getSQL();
$queryBuilder->execute();
}
return $databaseQueries;
}
/**
* Count valid entries from EXT:realurl table tx_realurl_uniqalias which can be migrated
* Checks also for existance of third party extension table 'tx_realurl_uniqalias'
* EXT:realurl requires not to be installed
*
* @return int
*/
public function countOfRealurlAliasMigrations(): int
{
$elementCount = 0;
// Check if table 'tx_realurl_uniqalias' exists
/** @var QueryBuilder $queryBuilder */
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('tx_realurl_uniqalias');
$schemaManager = $queryBuilder->getConnection()->getSchemaManager();
if ($schemaManager->tablesExist(['tx_realurl_uniqalias']) === true) {
// Count valid aliases
$queryBuilder->getRestrictions()->removeAll();
$elementCount = $queryBuilder->selectLiteral('COUNT(DISTINCT ' . $this->tableName . '.uid)')
->from('tx_realurl_uniqalias')
->join(
'tx_realurl_uniqalias',
$this->tableName,
$this->tableName,
$queryBuilder->expr()->eq(
'tx_realurl_uniqalias.value_id',
$queryBuilder->quoteIdentifier($this->tableName . '.uid')
)
)
->where(
$queryBuilder->expr()->andX(
$queryBuilder->expr()->orX(
$queryBuilder->expr()->eq(
$this->tableName . '.' . $this->slugFieldName,
$queryBuilder->createNamedParameter('', \PDO::PARAM_STR)
),
$queryBuilder->expr()->isNull($this->tableName . '.' . $this->slugFieldName)
),
$queryBuilder->expr()->eq(
$this->tableName . '.sys_language_uid',
'tx_realurl_uniqalias.lang'
),
$queryBuilder->expr()->eq(
'tx_realurl_uniqalias.tablename',
$queryBuilder->createNamedParameter($this->tableName, \PDO::PARAM_STR)
),
$queryBuilder->expr()->orX(
$queryBuilder->expr()->eq(
'tx_realurl_uniqalias.expire',
$queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
),
$queryBuilder->expr()->gte(
'tx_realurl_uniqalias.expire',
$queryBuilder->createNamedParameter($GLOBALS['ACCESS_TIME'], \PDO::PARAM_INT)
)
)
)
)
->execute()->fetchOne(0);
}
return $elementCount;
}
/**
* Perform migration of EXT:realurl unique alias into fs_media_gallery slugs
*
* @return array
*/
public function performRealurlAliasMigration(): array
{
$databaseQueries = [];
// Check if table 'tx_realurl_uniqalias' exists
/** @var QueryBuilder $queryBuilderForRealurl */
$queryBuilderForRealurl = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('tx_realurl_uniqalias');
$schemaManager = $queryBuilderForRealurl->getConnection()->getSchemaManager();
if ($schemaManager->tablesExist(['tx_realurl_uniqalias']) === true) {
/** @var Connection $connection */
$connection = GeneralUtility::makeInstance(ConnectionPool::class)
->getConnectionForTable($this->tableName);
$queryBuilder = $connection->createQueryBuilder();
// Get entries to update
$statement = $queryBuilder
->selectLiteral(
'DISTINCT ' . $this->tableName . '.uid, tx_realurl_uniqalias.value_alias, ' . $this->tableName . '.uid'
)
->from($this->tableName)
->join(
$this->tableName,
'tx_realurl_uniqalias',
'tx_realurl_uniqalias',
$queryBuilder->expr()->eq(
$this->tableName . '.uid',
$queryBuilder->quoteIdentifier('tx_realurl_uniqalias.value_id')
)
)
->where(
$queryBuilder->expr()->andX(
$queryBuilder->expr()->orX(
$queryBuilder->expr()->eq(
$this->tableName . '.' . $this->slugFieldName,
$queryBuilder->createNamedParameter('', \PDO::PARAM_STR)
),
$queryBuilder->expr()->isNull($this->tableName . '.' . $this->slugFieldName)
),
$queryBuilder->expr()->eq(
$this->tableName . '.sys_language_uid',
'tx_realurl_uniqalias.lang'
),
$queryBuilder->expr()->eq(
'tx_realurl_uniqalias.tablename',
$queryBuilder->createNamedParameter($this->tableName, \PDO::PARAM_STR)
),
$queryBuilder->expr()->orX(
$queryBuilder->expr()->eq(
'tx_realurl_uniqalias.expire',
$queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
),
$queryBuilder->expr()->gte(
'tx_realurl_uniqalias.expire',
$queryBuilder->createNamedParameter($GLOBALS['ACCESS_TIME'], \PDO::PARAM_INT)
)
)
)
)
->execute();
// Update entries
while ($record = $statement->fetchAssociative()) {
$slug = (string)$record['value_alias'];
$queryBuilder = $connection->createQueryBuilder();
$queryBuilder->update($this->tableName)
->where(
$queryBuilder->expr()->eq(
'uid',
$queryBuilder->createNamedParameter($record['uid'], \PDO::PARAM_INT)
)
)
->set($this->slugFieldName, $slug);
$databaseQueries[] = $queryBuilder->getSQL();
$queryBuilder->execute();
}
}
return $databaseQueries;
}
}

View File

@@ -0,0 +1,259 @@
<?php
namespace MiniFranske\FsMediaGallery\Service;
/***************************************************************
* Copyright notice
*
* (c) 2014 Frans Saris <franssaris@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.
*
* 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!
***************************************************************/
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
use TYPO3\CMS\Core\DataHandling\DataHandler;
use TYPO3\CMS\Core\DataHandling\SlugHelper;
use TYPO3\CMS\Core\Resource\Folder;
use TYPO3\CMS\Core\Resource\FolderInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* Utility class
*/
class Utility implements SingletonInterface
{
/**
* Get storage folders marked as media gallery
*
* @return array
*/
public function getStorageFolders()
{
$pages = [];
if ($this->getBeUser()) {
$q = $this->getDatabaseConnection()->createQueryBuilder();
$q->getRestrictions()
->removeAll()
->add(GeneralUtility::makeInstance(DeletedRestriction::class));
$quotedIdentifiers = $q->createNamedParameter(['mediagal'], Connection::PARAM_STR_ARRAY);
$q->select('uid', 'title')
->from('pages')
->where(
$q->expr()->andX(
$q->expr()->eq('doktype', 254),
$q->expr()->in('module', $quotedIdentifiers)
)
)
->orderBy('title');
$statement = $q->execute();
while ($row = $statement->fetchAssociative()) {
if (BackendUtility::readPageAccess($row['uid'], $this->getBeUser()->getPagePermsClause(1))) {
$pages[$row['uid']] = $row['title'];
}
}
}
return $pages;
}
/**
* Clear pageCache defined at the storage of the collection/album
*
* @param FolderInterface $folder
*/
public function clearMediaGalleryPageCache(FolderInterface $folder)
{
$collections = $this->findFileCollectionRecordsForFolder(
$folder->getStorage()->getUid(),
$folder->getIdentifier(),
array_keys($this->getStorageFolders())
);
if (!$collections) {
return;
}
/** @var DataHandler $tce */
$tce = GeneralUtility::makeInstance(DataHandler::class);
$tce->start([], []);
foreach ($collections as $collection) {
$pageConfig = BackendUtility::getPagesTSconfig($collection['pid']);
if (!empty($pageConfig['TCEMAIN.']['clearCacheCmd'])) {
$clearCacheCommands = GeneralUtility::trimExplode(',', $pageConfig['TCEMAIN.']['clearCacheCmd'], true);
$clearCacheCommands = array_unique($clearCacheCommands);
foreach ($clearCacheCommands as $clearCacheCommand) {
$tce->clear_cacheCmd($clearCacheCommand);
}
}
}
}
/**
* Gets the first parentCollections of the given folder and mediaFolderUid(storagepid)
*/
public function getFirstParentCollections(Folder $folder, int $mediaFolderUid): array
{
$parentCollection = [];
$evalPermissions = $folder->getStorage()->getEvaluatePermissions();
$folder->getStorage()->setEvaluatePermissions(false);
// If not root folder (for root folder parent === folder)
if ($folder->getParentFolder()->getIdentifier() !== $folder->getIdentifier()) {
$parentCollection = $this->findFileCollectionRecordsForFolder(
$folder->getStorage()->getUid(),
$folder->getParentFolder()->getIdentifier(),
[$mediaFolderUid]
);
if (!count($parentCollection)) {
$parentCollection = $this->getFirstParentCollections($folder->getParentFolder(), $mediaFolderUid);
}
}
$folder->getStorage()->setEvaluatePermissions($evalPermissions);
return $parentCollection;
}
/**
* Update file_collection record after move/rename folder
*/
public function updateFolderRecord(int $oldStorageUid, string $oldIdentifier, int $newStorageUid, string $newIdentifier, ?int $newParentAlbum = 0): void
{
$updatedData = [
'storage' => $newStorageUid,
'folder' => $newIdentifier
];
if ($newParentAlbum !== null) {
$updatedData['parentalbum'] = $newParentAlbum;
}
$this->getDatabaseConnection()->update(
'sys_file_collection',
$updatedData,
[
'storage' => (int)$oldStorageUid,
'folder' => $oldIdentifier,
]
);
}
/**
* Delete file_collection when folder is deleted
*
* @param int $storageUid
* @param string $identifier
*/
public function deleteFolderRecord($storageUid, $identifier)
{
$this->getDatabaseConnection()->update(
'sys_file_collection',
['deleted' => 1],
['folder' => $identifier, 'storage' => $storageUid]
);
}
/**
* Creates a folderRecord (sys_file_collection)
*
* @param string $title The title of the folder(album_name)
* @param int $collectionStoragePid The pid of the collection/mediaStorage
* @param int $storageUid The uid of the storage (fileStorage)
* @param string $identifier The identifier of the folder
* @param int $parentAlbum The uid of the parentAlbum
*/
public function createFolderRecord($title, $collectionStoragePid, $storageUid, $identifier, $parentAlbum = 0)
{
$folderRecord = [
'pid' => (int)$collectionStoragePid,
'deleted' => 0,
'hidden' => 0,
'type' => 'folder',
'storage' => (int)$storageUid,
'folder' => $identifier,
'title' => $title,
'parentalbum' => (int)$parentAlbum,
'webdescription' => '',
];
// Create slug
$slugTCAConfig = $GLOBALS['TCA']['sys_file_collection']['columns']['slug']['config'];
/** @var SlugHelper $slugHelper */
$slugHelper = GeneralUtility::makeInstance(
SlugHelper::class,
'sys_file_collection',
'slug',
$slugTCAConfig
);
$slug = $slugHelper->generate($folderRecord, $collectionStoragePid);
$folderRecord['slug'] = $slug;
$this->getDatabaseConnection()->insert('sys_file_collection', $folderRecord);
}
/**
* @param null|int[] $pids
*/
public function findFileCollectionRecordsForFolder(int $storageUid, string $folder, ?array $pids = null): ?array
{
$q = $this->getDatabaseConnection()->createQueryBuilder();
$q->getRestrictions()
->removeAll()
->add(GeneralUtility::makeInstance(DeletedRestriction::class));
$q->select('uid', 'pid', 'title', 'type', 'hidden', 'parentalbum')
->from('sys_file_collection')
->where(
$q->expr()->andX(
$q->expr()->eq('storage', $q->createNamedParameter($storageUid, \PDO::PARAM_INT)),
$q->expr()->eq('folder', $q->createNamedParameter($folder))
)
);
if (is_array($pids) && count($pids) > 0) {
$q->andWhere(
$q->expr()->in('pid', $pids)
);
}
return $q->execute()->fetchAllAssociative();
}
protected function getDatabaseConnection(string $table = 'sys_file_collection'): Connection
{
return GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table);
}
protected function getBeUser(): ?BackendUserAuthentication
{
return $GLOBALS['BE_USER'] ?? null;
}
}

View File

@@ -0,0 +1,111 @@
<?php
declare(strict_types=1);
namespace MiniFranske\FsMediaGallery\Updates;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use MiniFranske\FsMediaGallery\Service\SlugService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Install\Updates\AbstractUpdate;
use TYPO3\CMS\Install\Updates\DatabaseUpdatedPrerequisite;
use TYPO3\CMS\Install\Updates\UpgradeWizardInterface;
/**
* Migrate EXT:realurl unique alias into album slugs
*
* If a lot of similar titles are used it might be a good a idea
* to migrate the unique aliases from realurl to be sure that the same alias is used
*
* Requires existence of DB table tx_realurl_uniqalias, but EXT:realurl requires not to be installed
* Will only appear if missing slugs found between realurl and fs_media_gallery, respecting language and expire date from realurl
* Converts title into slug
*/
class PopulateMedialAlbumsSlug implements UpgradeWizardInterface
{
/** @var SlugService */
protected $slugService;
public function __construct()
{
$this->slugService = GeneralUtility::makeInstance(SlugService::class);
}
/**
* Get title
*
* @return string
*/
public function getTitle(): string
{
return 'Fill empty field "slug" of EXT:fs_media_gallery records';
}
/**
* Get description
*
* @return string Longer description of this updater
*/
public function getDescription(): string
{
return 'Fill empty field "slug" of EXT:fs_media_gallery records';
}
/**
* @return string Unique identifier of this updater
*/
public function getIdentifier(): string
{
return 'populateMedialAlbumsSlug';
}
public function getPrerequisites(): array
{
return [
DatabaseUpdatedPrerequisite::class,
];
}
/**
* Checks if an update is needed
*
* @return bool Whether an update is needed (TRUE) or not (FALSE)
*/
public function updateNecessary(): bool
{
if (!$this->slugService->typo3SupportsSlugs()) {
return false;
}
$elementCount = $this->slugService->countOfSlugUpdates();
return (bool)$elementCount;
}
/**
* Performs the database update
*
* @return bool
*/
public function executeUpdate(): bool
{
$queries = $this->slugService->performUpdateSlugs();
if (!empty($queries)) {
foreach ($queries as $query) {
$databaseQueries[] = $query;
}
return true;
}
return false;
}
}

View File

@@ -0,0 +1,110 @@
<?php
declare(strict_types=1);
namespace MiniFranske\FsMediaGallery\Updates;
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
use MiniFranske\FsMediaGallery\Service\SlugService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Install\Updates\AbstractUpdate;
use TYPO3\CMS\Install\Updates\DatabaseUpdatedPrerequisite;
use TYPO3\CMS\Install\Updates\UpgradeWizardInterface;
/**
* Migrate EXT:realurl unique alias into album slugs
*
* If a lot of similar titles are used it might be a good a idea
* to migrate the unique aliases from realurl to be sure that the same alias is used
*
* Requires existence of DB table tx_realurl_uniqalias, but EXT:realurl requires not to be installed
* Will only appear if missing slugs found between realurl and fs_media_gallery, respecting language and expire date from realurl
* Converts title into slug
*/
class RealurlAliasMediaAlbumsSlug implements UpgradeWizardInterface
{
/** @var SlugService */
protected $slugService;
public function __construct()
{
$this->slugService = GeneralUtility::makeInstance(SlugService::class);
}
/**
* Get title
*
* @return string
*/
public function getTitle(): string
{
return 'Migrate realurl alias to slug field "slug" of EXT:fs_media_gallery records';
}
/**
* Get description
*
* @return string Longer description of this updater
*/
public function getDescription(): string
{
return 'Migrates EXT:realurl unique alias values into empty slug field "slug" of EXT:fs_media_gallery records.';
}
/**
* @return string Unique identifier of this updater
*/
public function getIdentifier(): string
{
return 'realurlAliasMediaAlbumsSlug';
}
public function getPrerequisites(): array
{
return [
DatabaseUpdatedPrerequisite::class,
];
}
/**
* Checks if an update is needed
*
* @return bool Whether an update is needed (TRUE) or not (FALSE)
*/
public function updateNecessary(): bool
{
if (!$this->slugService->typo3SupportsSlugs()) {
return false;
}
$elementCount = $this->slugService->countOfRealurlAliasMigrations();
return (bool)$elementCount;
}
/**
* Performs the database update
* @return bool
*/
public function executeUpdate(): bool
{
$queries = $this->slugService->performRealurlAliasMigration();
if (!empty($queries)) {
foreach ($queries as $query) {
$databaseQueries[] = $query;
}
return true;
}
return false;
}
}

View File

@@ -0,0 +1,123 @@
<?php
namespace MiniFranske\FsMediaGallery\Utility;
/* *
* Inspired by Tx_News_Utility_TypoScript *
* *
* This script is part of the TYPO3 project. *
* *
* It is free software; you can redistribute it and/or modify it under *
* the terms of the GNU Lesser General Public License, either version 3 *
* of the License, or (at your option) any later version. *
* *
* The TYPO3 project - inspiring people to share! *
* */
use TYPO3\CMS\Core\SingletonInterface;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
* TypoScript Utility class
*/
class TypoScriptUtility implements SingletonInterface
{
/**
* @param array $base
* @param array $overload
* @return array
*/
public function override(array $base, array $overload)
{
$validFields = GeneralUtility::trimExplode(',', $overload['settings']['overrideFlexformSettingsIfEmpty'], true);
foreach ($validFields as $fieldName) {
// Multilevel field
if (strpos($fieldName, '.') !== false) {
$keyAsArray = explode('.', $fieldName);
$foundInCurrentTs = $this->getValue($base, $keyAsArray);
if (is_string($foundInCurrentTs) && strlen($foundInCurrentTs) === 0) {
$foundInOriginal = $this->getValue($overload['settings'], $keyAsArray);
if ($foundInOriginal) {
$base = $this->setValue($base, $keyAsArray, $foundInOriginal);
}
}
} else {
// if flexform setting is empty and value is available in TS
if ((!isset($base[$fieldName]) || (strlen($base[$fieldName]) === 0))
&& isset($overload['settings'][$fieldName])
) {
$base[$fieldName] = $overload['settings'][$fieldName];
}
}
}
return $base;
}
/**
* Get value from array by path
*
* @param array $data
* @param array $path
* @return array|null
*/
protected function getValue(array $data, array $path)
{
$found = true;
for ($x = 0; ($x < count($path) && $found); $x++) {
$key = $path[$x];
if (isset($data[$key])) {
$data = $data[$key];
} else {
$found = false;
}
}
if ($found) {
return $data;
}
return null;
}
/**
* Set value in array by path
*
* @param array $array
* @param $path
* @param $value
* @return array
*/
protected function setValue(array $array, $path, $value)
{
$this->setValueByReference($array, $path, $value);
$final = array_merge_recursive([], $array);
return $final;
}
/**
* Set value by reference
*
* @param array $array
* @param array $path
* @param $value
*/
private function setValueByReference(array &$array, array $path, $value)
{
while (count($path) > 1) {
$key = array_shift($path);
if (!isset($array[$key])) {
$array[$key] = [];
}
$array = &$array[$key];
}
$key = reset($path);
$array[$key] = $value;
}
}

View File

@@ -0,0 +1,117 @@
<?php
namespace MiniFranske\FsMediaGallery\ViewHelpers\Embed;
/* *
* This script is part of the TYPO3 project. *
* *
* It is free software; you can redistribute it and/or modify it under *
* the terms of the GNU Lesser General Public License, either version 3 *
* of the License, or (at your option) any later version. *
* *
* The TYPO3 project - inspiring people to share! *
* */
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Core\Http\ApplicationType;
use TYPO3\CMS\Core\Page\PageRenderer;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
/**
* Embed JavaScript view helper.
*/
class JavaScriptViewHelper extends AbstractViewHelper
{
/**
* @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
*/
protected $configurationManager;
public function injectConfigurationManager(ConfigurationManagerInterface $configurationManager): void
{
$this->configurationManager = $configurationManager;
}
/**
* Initialize arguments
*
* @return void
*/
public function initializeArguments()
{
$this->registerArgument('name', 'string',
'If empty, a combination of plugin name and the uid of the cObj is used.');
$this->registerArgument('moveToFooter', 'boolean',
'If TRUE, adds the script to the document footer by PageRenderer->addJsFooterInlineCode().');
}
/**
* Renders child nodes as inline JavaScript content or adds it to page footer
*
* @return string The rendered script content; if moveToFooter is TRUE the script content is added by PageRenderer->addJsFooterInlineCode() and an empty string is returned
*/
public function render()
{
$content = $this->renderChildren();
if (!is_string($content)) {
return $content;
}
if (empty($this->arguments['name'])) {
$blockName = 'tx_fsmediagallery';
if ($cObj = $this->configurationManager->getContentObject()) {
$blockName .= '.' . $cObj->data['uid'];
}
} else {
$blockName = (string)$this->arguments['name'];
}
if (!empty($this->arguments['moveToFooter']) && $this->getApplicationType() === 'FE') {
// add JS inline code to footer
$this->getPageRenderer()->addJsFooterInlineCode(
$blockName,
$content,
$GLOBALS['TSFE']->config['config']['compressJs']
);
return '';
} else {
$lb = "\n";
return '<script type="text/javascript">' . $lb . '/*<![CDATA[*/' . $lb .
'/*' . $blockName . '*/' . $lb . $content . $lb . '/*]]>*/' . $lb . '</script>';
}
}
/**
* @return PageRenderer
*/
protected function getPageRenderer() {
if(class_exists(\TYPO3\CMS\Core\Page\PageRenderer::class)) {
$pageRenderer = GeneralUtility::makeInstance(PageRenderer::class);
} elseif (method_exists($GLOBALS['TSFE'], 'getPageRenderer')) {
$pageRenderer = $GLOBALS['TSFE']->getPageRenderer();
} else {
$pageRenderer = null;
}
return $pageRenderer;
}
/**
* String 'FE' if in FrontendApplication, 'BE' otherwise (also in CLI without request object)
*
* @internal
*/
public function getApplicationType(): string
{
if (
($GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface &&
ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend()
) {
return 'FE';
}
return 'BE';
}
}

View File

@@ -0,0 +1,76 @@
<?php
namespace MiniFranske\FsMediaGallery\ViewHelpers;
/***************************************************************
* Copyright notice
*
* (c) 2014 Frans Saris <franssaris@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 3 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.
*
* 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!
***************************************************************/
use TYPO3\CMS\Core\Resource\File;
use TYPO3\CMS\Core\Resource\FileInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
/**
* File title viewHelper
*/
class FileDescriptionViewHelper extends AbstractViewHelper
{
/**
* Initialize arguments.
*
* @api
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('file', File::class, 'File', true);
}
/**
* Get title of a File
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$file = $arguments['file'];
if (is_callable([$file, 'getOriginalResource'])) {
// Get the original file from the Extbase model
$file = $file->getOriginalResource();
}
if (!$file instanceof FileInterface) {
return null;
}
if ($file->getProperty('description')) {
return $file->getProperty('description');
}
}
}

View File

@@ -0,0 +1,77 @@
<?php
namespace MiniFranske\FsMediaGallery\ViewHelpers;
/***************************************************************
* Copyright notice
*
* (c) 2014 Frans Saris <franssaris@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 3 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.
*
* 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!
***************************************************************/
use TYPO3\CMS\Core\Resource\FileInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
/**
* File title viewHelper
*/
class FileTitleViewHelper extends AbstractViewHelper
{
/**
* Initialize arguments.
*
* @throws \TYPO3Fluid\Fluid\Core\ViewHelper\Exception
*/
public function initializeArguments()
{
parent::initializeArguments();
$this->registerArgument('file', FileInterface::class, 'File', true);
}
/**
* Get title of a File
*
* @param array $arguments
* @param \Closure $renderChildrenClosure
* @param RenderingContextInterface $renderingContext
* @return string
*/
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$file = $arguments['file'];
if (is_callable([$file, 'getOriginalResource'])) {
// Get the original file from the Extbase model
$file = $file->getOriginalResource();
}
if (!$file instanceof FileInterface) {
return null;
}
if ($file->getProperty('title')) {
return $file->getProperty('title');
} else {
return str_ireplace('_', ' ', $file->getNameWithoutExtension());
}
}
}