first commit
This commit is contained in:
commit
1218dd1517
12716 files changed
+989233
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Backend\Tree\View;
|
||||
|
||||
/*
|
||||
* 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 TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Exception\SiteNotFoundException;
|
||||
use TYPO3\CMS\Core\Site\PseudoSiteFinder;
|
||||
use TYPO3\CMS\Core\Site\SiteFinder;
|
||||
use TYPO3\CMS\Core\Type\Bitmask\Permission;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Generate a page-tree, browsable.
|
||||
*/
|
||||
class BrowseTreeView extends AbstractTreeView
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $fieldArray = [
|
||||
'uid',
|
||||
'pid',
|
||||
'title',
|
||||
'doktype',
|
||||
'nav_title',
|
||||
'mount_pid',
|
||||
'php_tree_stop',
|
||||
't3ver_id',
|
||||
't3ver_state',
|
||||
'hidden',
|
||||
'starttime',
|
||||
'endtime',
|
||||
'fe_group',
|
||||
'module',
|
||||
'extendToSubpages',
|
||||
'nav_hide',
|
||||
't3ver_wsid',
|
||||
't3ver_move_id',
|
||||
'is_siteroot'
|
||||
];
|
||||
|
||||
/**
|
||||
* override to use this treeName
|
||||
* @var string
|
||||
*/
|
||||
public $treeName = 'browsePages';
|
||||
|
||||
/**
|
||||
* override to use this table
|
||||
* @var string
|
||||
*/
|
||||
public $table = 'pages';
|
||||
|
||||
/**
|
||||
* override to use this domIdPrefix
|
||||
* @var string
|
||||
*/
|
||||
public $domIdPrefix = 'pages';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $ext_showNavTitle = false;
|
||||
|
||||
/**
|
||||
* Initialize, setting what is necessary for browsing pages.
|
||||
* Using the current user.
|
||||
*
|
||||
* @param string $clause Additional clause for selecting pages.
|
||||
* @param string $orderByFields record ORDER BY field
|
||||
*/
|
||||
public function init($clause = '', $orderByFields = '')
|
||||
{
|
||||
$backendUser = $this->getBackendUser();
|
||||
// This will hide records from display - it has nothing to do with user rights!!
|
||||
$clauseExcludePidList = '';
|
||||
$pidList = $backendUser->getTSConfig()['options.']['hideRecords.']['pages'] ?? '';
|
||||
if (!empty($pidList)) {
|
||||
if ($pidList = implode(',', GeneralUtility::intExplode(',', $pidList))) {
|
||||
$clauseExcludePidList = ' AND pages.uid NOT IN (' . $pidList . ')';
|
||||
}
|
||||
}
|
||||
// This is very important for making trees of pages: Filtering out deleted pages, pages with no access to and sorting them correctly:
|
||||
parent::init(' AND deleted=0 AND sys_language_uid=0 AND ' . $backendUser->getPagePermsClause(Permission::PAGE_SHOW) . ' ' . $clause . $clauseExcludePidList, 'sorting');
|
||||
$this->title = $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'];
|
||||
$this->MOUNTS = $backendUser->returnWebmounts();
|
||||
if ($pidList) {
|
||||
// Remove mountpoint if explicitly set in options.hideRecords.pages (see above)
|
||||
$hideList = explode(',', $pidList);
|
||||
$this->MOUNTS = array_diff($this->MOUNTS, $hideList);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates title attribute content for pages.
|
||||
* Uses API function in \TYPO3\CMS\Backend\Utility\BackendUtility which will retrieve lots of useful information for pages.
|
||||
*
|
||||
* @param array $row The table row.
|
||||
* @return string
|
||||
*/
|
||||
public function getTitleAttrib($row)
|
||||
{
|
||||
return BackendUtility::titleAttribForPages($row, '1=1 ' . $this->clause, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapping the image tag, $icon, for the row, $row (except for mount points)
|
||||
*
|
||||
* @param string $icon The image tag for the icon
|
||||
* @param array $row The row for the current element
|
||||
* @return string The processed icon input value.
|
||||
* @internal
|
||||
*/
|
||||
public function wrapIcon($icon, $row)
|
||||
{
|
||||
// Wrap icon in click-menu link.
|
||||
$theIcon = '';
|
||||
if (!$this->ext_IconMode) {
|
||||
$theIcon = BackendUtility::wrapClickMenuOnIcon($icon, $this->treeName, $this->getId($row), 0);
|
||||
} elseif ($this->ext_IconMode === 'titlelink') {
|
||||
$aOnClick = 'return jumpTo(' . GeneralUtility::quoteJSvalue($this->getJumpToParam($row)) . ',this,'
|
||||
. GeneralUtility::quoteJSvalue($this->domIdPrefix . $this->getId($row)) . ',' . $this->bank . ');';
|
||||
$theIcon = '<a href="#" onclick="' . htmlspecialchars($aOnClick) . '">' . $icon . '</a>';
|
||||
}
|
||||
return $theIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title for the input record. If blank, a "no title" label (localized) will be returned.
|
||||
* Do NOT htmlspecialchar the string from this function - has already been done.
|
||||
*
|
||||
* @param array $row The input row array (where the key "title" is used for the title)
|
||||
* @param int $titleLen Title length (30)
|
||||
* @return string The title.
|
||||
*/
|
||||
public function getTitleStr($row, $titleLen = 30)
|
||||
{
|
||||
if ($this->ext_showNavTitle && isset($row['nav_title']) && trim($row['nav_title']) !== '') {
|
||||
$title = parent::getTitleStr(['title' => $row['nav_title']], $titleLen);
|
||||
} else {
|
||||
$title = parent::getTitleStr($row, $titleLen);
|
||||
}
|
||||
if (!empty($row['is_siteroot'])
|
||||
&& $this->getBackendUser()->getTSConfig()['options.']['pageTree.']['showDomainNameWithTitle'] ?? false
|
||||
) {
|
||||
$pageId = (int)$row['uid'];
|
||||
$siteFinder = GeneralUtility::makeInstance(SiteFinder::class);
|
||||
try {
|
||||
$site = $siteFinder->getSiteByRootPageId($pageId);
|
||||
$title .= ' [' . (string)$site->getBase() . ']';
|
||||
} catch (SiteNotFoundException $e) {
|
||||
// No site found, let's see if it is a legacy-pseudo-site
|
||||
$pseudoSiteFinder = GeneralUtility::makeInstance(PseudoSiteFinder::class);
|
||||
|
||||
try {
|
||||
$site = $pseudoSiteFinder->getSiteByRootPageId($pageId);
|
||||
$title .= ' [' . trim((string)$site->getBase(), '/') . ']';
|
||||
} catch (SiteNotFoundException $e) {
|
||||
// No pseudo-site found either
|
||||
}
|
||||
}
|
||||
}
|
||||
return $title;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Backend\Tree\View;
|
||||
|
||||
/*
|
||||
* 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 TYPO3\CMS\Backend\Routing\UriBuilder;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Local position map class when creating new Content Elements
|
||||
*
|
||||
* @internal This class is a TYPO3 Backend implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class ContentCreationPagePositionMap extends PagePositionMap
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $dontPrintPageInsertIcons = 1;
|
||||
|
||||
/**
|
||||
* Wrapping the title of the record - here we just return it.
|
||||
*
|
||||
* @param string $str The title value.
|
||||
* @param array $row The record row.
|
||||
* @return string Wrapped title string.
|
||||
*/
|
||||
public function wrapRecordTitle($str, $row)
|
||||
{
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create on-click event value.
|
||||
*
|
||||
* @param array $row The record.
|
||||
* @param string $vv Column position value.
|
||||
* @param int $moveUid Move uid
|
||||
* @param int $pid PID value.
|
||||
* @param int $sys_lang System language
|
||||
* @return string
|
||||
*/
|
||||
public function onClickInsertRecord($row, $vv, $moveUid, $pid, $sys_lang = 0)
|
||||
{
|
||||
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
|
||||
$location = (string)$uriBuilder->buildUriFromRoute('record_edit', [
|
||||
'edit[tt_content][' . (is_array($row) ? -$row['uid'] : $pid) . ']' => 'new',
|
||||
'defVals[tt_content][colPos]' => $vv,
|
||||
'defVals[tt_content][sys_language_uid]' => $sys_lang,
|
||||
'returnUrl' => GeneralUtility::_GP('returnUrl')
|
||||
]);
|
||||
return $this->clientContext . '.location.href=' . GeneralUtility::quoteJSvalue($location) . '+document.editForm.defValues.value; return false;';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Backend\Tree\View;
|
||||
|
||||
/*
|
||||
* 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 TYPO3\CMS\Core\Imaging\Icon;
|
||||
use TYPO3\CMS\Core\Imaging\IconFactory;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Position map class for content elements within the page module
|
||||
*
|
||||
* @internal This class is a TYPO3 Backend implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class ContentLayoutPagePositionMap extends PagePositionMap
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $dontPrintPageInsertIcons = 1;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $l_insertNewRecordHere = 'newContentElement';
|
||||
|
||||
/**
|
||||
* Wrapping the title of the record.
|
||||
*
|
||||
* @param string $str The title value.
|
||||
* @param array $row The record row.
|
||||
* @return string Wrapped title string.
|
||||
*/
|
||||
public function wrapRecordTitle($str, $row)
|
||||
{
|
||||
$aOnClick = 'jumpToUrl(' . GeneralUtility::quoteJSvalue($this->linkToCurrentModule(['edit_record' => 'tt_content:' . $row['uid']])) . ');return false;';
|
||||
return '<a href="#" onclick="' . htmlspecialchars($aOnClick) . '">' . $str . '</a>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapping the column header
|
||||
*
|
||||
* @param string $str Header value
|
||||
* @param string $vv Column info.
|
||||
* @return string
|
||||
* @see printRecordMap()
|
||||
*/
|
||||
public function wrapColumnHeader($str, $vv)
|
||||
{
|
||||
$aOnClick = 'jumpToUrl(' . GeneralUtility::quoteJSvalue($this->linkToCurrentModule(['edit_record' => '_EDIT_COL:' . $vv])) . ');return false;';
|
||||
return '<a href="#" onclick="' . htmlspecialchars($aOnClick) . '">' . $str . '</a>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create on-click event value.
|
||||
*
|
||||
* @param array $row The record.
|
||||
* @param string $vv Column position value.
|
||||
* @param int $moveUid Move uid
|
||||
* @param int $pid PID value.
|
||||
* @param int $sys_lang System language
|
||||
* @return string
|
||||
*/
|
||||
public function onClickInsertRecord($row, $vv, $moveUid, $pid, $sys_lang = 0)
|
||||
{
|
||||
if (is_array($row)) {
|
||||
$linkInformation = 'tt_content:new/-' . $row['uid'] . '/' . $row['colPos'];
|
||||
} else {
|
||||
$linkInformation = 'tt_content:new/' . $pid . '/' . $vv;
|
||||
}
|
||||
$location = $this->linkToCurrentModule(['edit_record' => $linkInformation]);
|
||||
return 'jumpToUrl(' . GeneralUtility::quoteJSvalue($location) . ');return false;';
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapping the record header (from getRecordHeader())
|
||||
*
|
||||
* @param string $str HTML content
|
||||
* @param array $row Record array.
|
||||
* @return string HTML content
|
||||
*/
|
||||
public function wrapRecordHeader($str, $row)
|
||||
{
|
||||
if ($row['uid'] == $this->moveUid) {
|
||||
/** @var IconFactory $iconFactory */
|
||||
$iconFactory = GeneralUtility::makeInstance(IconFactory::class);
|
||||
return $iconFactory->getIcon('status-status-current', Icon::SIZE_SMALL)->render() . $str;
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns URL to the current script.
|
||||
* In particular the "popView" and "new_unique_uid" Get vars are unset.
|
||||
*
|
||||
* @param array $params Parameters array, merged with global GET vars.
|
||||
* @return string URL
|
||||
*/
|
||||
protected function linkToCurrentModule($params)
|
||||
{
|
||||
unset($params['popView']);
|
||||
unset($params['new_unique_uid']);
|
||||
return GeneralUtility::linkThisScript($params);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Backend\Tree\View;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* Position map class for moving content elements.
|
||||
*
|
||||
* @internal This class is a TYPO3 Backend implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class ContentMovingPagePositionMap extends PagePositionMap
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $dontPrintPageInsertIcons = 1;
|
||||
|
||||
/**
|
||||
* Page tree implementation class name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $pageTreeClassName = PageTreeView::class;
|
||||
|
||||
/**
|
||||
* Wrapping page title.
|
||||
*
|
||||
* @param string $str Page title.
|
||||
* @param array $rec Page record (?)
|
||||
* @return string Wrapped title.
|
||||
*/
|
||||
public function linkPageTitle($str, $rec)
|
||||
{
|
||||
$url = \TYPO3\CMS\Core\Utility\GeneralUtility::linkThisScript(['uid' => (int)$rec['uid'], 'moveUid' => $this->moveUid]);
|
||||
return '<a href="' . htmlspecialchars($url) . '">' . $str . '</a>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapping the title of the record.
|
||||
*
|
||||
* @param string $str The title value.
|
||||
* @param array $row The record row.
|
||||
* @return string Wrapped title string.
|
||||
*/
|
||||
public function wrapRecordTitle($str, $row)
|
||||
{
|
||||
if ($this->moveUid == $row['uid']) {
|
||||
$str = '<strong>' . $str . '</strong>';
|
||||
}
|
||||
return parent::wrapRecordTitle($str, $row);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Backend\Tree\View;
|
||||
|
||||
/*
|
||||
* 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 TYPO3\CMS\Core\Resource\Folder;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\HttpUtility;
|
||||
use TYPO3\CMS\Recordlist\Tree\View\LinkParameterProviderInterface;
|
||||
|
||||
/**
|
||||
* Base extension class which generates the folder tree.
|
||||
* Used directly by the RTE.
|
||||
* also used for the linkpicker on files
|
||||
*
|
||||
* Browsable folder tree, used in Element Browser and RTE (for which it will be extended)
|
||||
* previously located inside typo3/class.browse_links.php
|
||||
*/
|
||||
class ElementBrowserFolderTreeView extends FolderTreeView
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $ext_IconMode = 'titlelink';
|
||||
|
||||
/**
|
||||
* @var LinkParameterProviderInterface
|
||||
*/
|
||||
protected $linkParameterProvider;
|
||||
|
||||
/**
|
||||
* @param LinkParameterProviderInterface $linkParameterProvider
|
||||
*/
|
||||
public function setLinkParameterProvider(LinkParameterProviderInterface $linkParameterProvider)
|
||||
{
|
||||
$this->linkParameterProvider = $linkParameterProvider;
|
||||
$this->thisScript = $linkParameterProvider->getScriptUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapping the folder icon
|
||||
*
|
||||
* @param string $icon The image tag for the icon
|
||||
* @param Folder $folderObject The row for the current element
|
||||
*
|
||||
* @return string The processed icon input value.
|
||||
* @internal
|
||||
*/
|
||||
public function wrapIcon($icon, $folderObject)
|
||||
{
|
||||
// Add title attribute to input icon tag
|
||||
$theFolderIcon = '';
|
||||
|
||||
// Wrap icon in link (in ElementBrowser only the "titlelink" is used).
|
||||
if ($this->ext_IconMode === 'titlelink') {
|
||||
$parameters = HttpUtility::buildQueryString(
|
||||
$this->linkParameterProvider->getUrlParameters(['identifier' => $folderObject->getCombinedIdentifier()])
|
||||
);
|
||||
$aOnClick = 'return jumpToUrl(' . GeneralUtility::quoteJSvalue($this->getThisScript() . $parameters) . ');';
|
||||
$theFolderIcon = '<a href="#" onclick="' . htmlspecialchars($aOnClick) . '">' . $icon . '</a>';
|
||||
}
|
||||
|
||||
return $theFolderIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapping the title in a link, if applicable.
|
||||
*
|
||||
* @param string $title Title, ready for output.
|
||||
* @param Folder $folderObject The record
|
||||
* @param int $bank Bank pointer (which mount point number)
|
||||
* @return string Wrapping title string.
|
||||
*/
|
||||
public function wrapTitle($title, $folderObject, $bank = 0)
|
||||
{
|
||||
$parameters = HttpUtility::buildQueryString(
|
||||
$this->linkParameterProvider->getUrlParameters(['identifier' => $folderObject->getCombinedIdentifier()])
|
||||
);
|
||||
return '<a href="#" onclick="return jumpToUrl(' . htmlspecialchars(GeneralUtility::quoteJSvalue($this->getThisScript() . $parameters)) . ');">' . $title . '</a>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if the input "record" contains a folder which can be linked.
|
||||
*
|
||||
* @param Folder $folderObject Object with information about the folder element. Contains keys like title, uid, path, _title
|
||||
* @return bool TRUE
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
|
||||
*/
|
||||
public function ext_isLinkable(Folder $folderObject)
|
||||
{
|
||||
trigger_error('This method is obsolete and will be removed in TYPO3 v10.0.', E_USER_DEPRECATED);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $cmd
|
||||
* @param bool $isOpen
|
||||
* @return string
|
||||
*/
|
||||
protected function renderPMIconAndLink($cmd, $isOpen)
|
||||
{
|
||||
if (static::class === __CLASS__) {
|
||||
return $this->PMiconATagWrap('', $cmd, !$isOpen);
|
||||
}
|
||||
return parent::renderPMIconAndLink($cmd, $isOpen);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the plus/minus icon in a link
|
||||
*
|
||||
* @param string $icon HTML string to wrap, probably an image tag.
|
||||
* @param string $cmd Command for 'PM' get var
|
||||
* @param bool|string $bMark If set, the link will have an anchor point (=$bMark) and a name attribute (=$bMark)
|
||||
* @param bool $isOpen check if the item has children
|
||||
* @return string Link-wrapped input string
|
||||
* @internal
|
||||
*/
|
||||
public function PM_ATagWrap($icon, $cmd, $bMark = '', $isOpen = false)
|
||||
{
|
||||
$anchor = $bMark ? '#' . $bMark : '';
|
||||
$name = $bMark ? ' name=' . $bMark : '';
|
||||
$urlParameters = $this->linkParameterProvider->getUrlParameters([]);
|
||||
$urlParameters['PM'] = $cmd;
|
||||
$aOnClick = 'return jumpToUrl(' . GeneralUtility::quoteJSvalue($this->getThisScript() . HttpUtility::buildQueryString($urlParameters)) . ',' . GeneralUtility::quoteJSvalue($anchor) . ');';
|
||||
return '<a href="#"' . htmlspecialchars($name) . ' onclick="' . htmlspecialchars($aOnClick) . '">' . $icon . '</a>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the plus/minus icon in a link
|
||||
*
|
||||
* @param string $icon HTML string to wrap, probably an image tag.
|
||||
* @param string $cmd Command for 'PM' get var
|
||||
* @param bool $isExpand Whether to be expanded
|
||||
* @return string Link-wrapped input string
|
||||
* @internal
|
||||
*/
|
||||
public function PMiconATagWrap($icon, $cmd, $isExpand = true)
|
||||
{
|
||||
if (empty($this->scope)) {
|
||||
$this->scope = [
|
||||
'class' => static::class,
|
||||
'script' => $this->thisScript,
|
||||
'browser' => $this->linkParameterProvider->getUrlParameters([]),
|
||||
];
|
||||
}
|
||||
|
||||
return parent::PMiconATagWrap($icon, $cmd, $isExpand);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Backend\Tree\View;
|
||||
|
||||
/*
|
||||
* 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 TYPO3\CMS\Core\LinkHandling\LinkService;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
use TYPO3\CMS\Core\Utility\HttpUtility;
|
||||
use TYPO3\CMS\Frontend\Page\PageRepository;
|
||||
use TYPO3\CMS\Recordlist\Tree\View\LinkParameterProviderInterface;
|
||||
|
||||
/**
|
||||
* Class which generates the selectable page tree
|
||||
*
|
||||
* Browsable tree, used in PagePositionMaps (move elements), the Link Wizard and the Database Browser (for which it will be extended)
|
||||
*/
|
||||
class ElementBrowserPageTreeView extends BrowseTreeView
|
||||
{
|
||||
/**
|
||||
* whether the page ID should be shown next to the title, activate through
|
||||
* userTSconfig (options.pageTree.showPageIdWithTitle)
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $ext_showPageId = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $ext_pArrPages = true;
|
||||
|
||||
/**
|
||||
* @var LinkParameterProviderInterface
|
||||
*/
|
||||
protected $linkParameterProvider;
|
||||
|
||||
/**
|
||||
* Constructor. Just calling init()
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->init();
|
||||
$this->clause = ' AND doktype <> ' . PageRepository::DOKTYPE_RECYCLER . $this->clause;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param LinkParameterProviderInterface $linkParameterProvider
|
||||
*/
|
||||
public function setLinkParameterProvider(LinkParameterProviderInterface $linkParameterProvider)
|
||||
{
|
||||
$this->linkParameterProvider = $linkParameterProvider;
|
||||
$this->thisScript = $linkParameterProvider->getScriptUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapping the title in a link, if applicable.
|
||||
*
|
||||
* @param string $title Title, (must be ready for output, that means it must be htmlspecialchars()'ed).
|
||||
* @param array $v The record
|
||||
* @param bool $ext_pArrPages (ignored)
|
||||
* @return string Wrapping title string.
|
||||
*/
|
||||
public function wrapTitle($title, $v, $ext_pArrPages = false)
|
||||
{
|
||||
if ($this->ext_isLinkable($v['doktype'], $v['uid'])) {
|
||||
$url = GeneralUtility::makeInstance(LinkService::class)->asString(['type' => LinkService::TYPE_PAGE, 'pageuid' => (int)$v['uid']]);
|
||||
return '<span class="list-tree-title"><a href="' . htmlspecialchars($url) . '" class="t3js-pageLink">' . $title . '</a></span>';
|
||||
}
|
||||
return '<span class="list-tree-title text-muted">' . $title . '</span>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the page navigation tree in HTML
|
||||
*
|
||||
* @param array|string $treeArr Tree array
|
||||
* @return string HTML output.
|
||||
*/
|
||||
public function printTree($treeArr = '')
|
||||
{
|
||||
$titleLen = (int)$GLOBALS['BE_USER']->uc['titleLen'];
|
||||
if (!is_array($treeArr)) {
|
||||
$treeArr = $this->tree;
|
||||
}
|
||||
$out = '';
|
||||
// We need to count the opened <ul>'s every time we dig into another level,
|
||||
// so we know how many we have to close when all children are done rendering
|
||||
$closeDepth = [];
|
||||
foreach ($treeArr as $treeItem) {
|
||||
if ($treeItem['isMountPointPath']) {
|
||||
$out .= '<li class="list-tree-path">' . $treeItem['title'] . '</li>';
|
||||
continue;
|
||||
}
|
||||
|
||||
$classAttr = $treeItem['row']['_CSSCLASS'];
|
||||
if ($treeItem['isFirst']) {
|
||||
$out .= '<ul class="list-tree">';
|
||||
}
|
||||
|
||||
// Add CSS classes to the list item
|
||||
if ($treeItem['hasSub']) {
|
||||
$classAttr .= ' list-tree-control-open';
|
||||
}
|
||||
|
||||
$selected = '';
|
||||
if ($this->linkParameterProvider->isCurrentlySelectedItem(['pid' => (int)$treeItem['row']['uid']])) {
|
||||
$selected = ' bg-success';
|
||||
$classAttr .= ' active';
|
||||
}
|
||||
$urlParameters = $this->linkParameterProvider->getUrlParameters(['pid' => (int)$treeItem['row']['uid']]);
|
||||
$aOnClick = 'return jumpToUrl(' . GeneralUtility::quoteJSvalue($this->getThisScript() . HttpUtility::buildQueryString($urlParameters)) . ');';
|
||||
$cEbullet = $this->ext_isLinkable($treeItem['row']['doktype'], $treeItem['row']['uid'])
|
||||
? '<a href="#" class="list-tree-show" onclick="' . htmlspecialchars($aOnClick) . '"><i class="fa fa-caret-square-o-right"></i></a>'
|
||||
: '';
|
||||
$out .= '
|
||||
<li' . ($classAttr ? ' class="' . trim($classAttr) . '"' : '') . '>
|
||||
<span class="list-tree-group' . $selected . '">
|
||||
' . $cEbullet . $treeItem['HTML'] . $this->wrapTitle($this->getTitleStr($treeItem['row'], $titleLen), $treeItem['row'], $this->ext_pArrPages) . '
|
||||
</span>
|
||||
';
|
||||
if (!$treeItem['hasSub']) {
|
||||
$out .= '</li>';
|
||||
}
|
||||
|
||||
// We have to remember if this is the last one
|
||||
// on level X so the last child on level X+1 closes the <ul>-tag
|
||||
if ($treeItem['isLast']) {
|
||||
$closeDepth[$treeItem['invertedDepth']] = 1;
|
||||
}
|
||||
// If this is the last one and does not have subitems, we need to close
|
||||
// the tree as long as the upper levels have last items too
|
||||
if ($treeItem['isLast'] && !$treeItem['hasSub']) {
|
||||
for ($i = $treeItem['invertedDepth']; $closeDepth[$i] == 1; $i++) {
|
||||
$closeDepth[$i] = 0;
|
||||
$out .= '</ul></li>';
|
||||
}
|
||||
}
|
||||
}
|
||||
return '<ul class="list-tree list-tree-root">' . $out . '</ul>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE if a doktype can be linked.
|
||||
*
|
||||
* @param int $doktype Doktype value to test
|
||||
* @param int $uid uid to test.
|
||||
* @return bool
|
||||
*/
|
||||
public function ext_isLinkable($doktype, $uid)
|
||||
{
|
||||
return $uid && $doktype < PageRepository::DOKTYPE_SPACER;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the plus/minus icon in a link
|
||||
*
|
||||
* @param string $icon HTML string to wrap, probably an image tag.
|
||||
* @param string $cmd Command for 'PM' get var
|
||||
* @param string $bMark If set, the link will have an anchor point (=$bMark) and a name attribute (=$bMark)
|
||||
* @param bool $isOpen
|
||||
* @return string Link-wrapped input string
|
||||
*/
|
||||
public function PM_ATagWrap($icon, $cmd, $bMark = '', $isOpen = false)
|
||||
{
|
||||
$anchor = $bMark ? '#' . $bMark : '';
|
||||
$name = $bMark ? ' name=' . $bMark : '';
|
||||
$urlParameters = $this->linkParameterProvider->getUrlParameters([]);
|
||||
$urlParameters['PM'] = $cmd;
|
||||
$aOnClick = 'return jumpToUrl(' . GeneralUtility::quoteJSvalue($this->getThisScript() . HttpUtility::buildQueryString($urlParameters)) . ',' . GeneralUtility::quoteJSvalue($anchor) . ');';
|
||||
return '<a class="list-tree-control ' . ($isOpen ? 'list-tree-control-open' : 'list-tree-control-closed')
|
||||
. '" href="#"' . htmlspecialchars($name) . ' onclick="' . htmlspecialchars($aOnClick) . '"><i class="fa"></i></a>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapping the image tag, $icon, for the row, $row
|
||||
*
|
||||
* @param string $icon The image tag for the icon
|
||||
* @param array $row The row for the current element
|
||||
* @return string The processed icon input value.
|
||||
*/
|
||||
public function wrapIcon($icon, $row)
|
||||
{
|
||||
if ($this->ext_showPageId) {
|
||||
$icon .= '[' . $row['uid'] . '] ';
|
||||
}
|
||||
return $icon;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,717 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Backend\Tree\View;
|
||||
|
||||
/*
|
||||
* 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 TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Imaging\Icon;
|
||||
use TYPO3\CMS\Core\Imaging\IconFactory;
|
||||
use TYPO3\CMS\Core\Localization\LanguageService;
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessage;
|
||||
use TYPO3\CMS\Core\Messaging\FlashMessageService;
|
||||
use TYPO3\CMS\Core\Resource\Folder;
|
||||
use TYPO3\CMS\Core\Resource\FolderInterface;
|
||||
use TYPO3\CMS\Core\Resource\InaccessibleFolder;
|
||||
use TYPO3\CMS\Core\Resource\ResourceStorage;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Generate a folder tree,
|
||||
* specially made for browsing folders in the File module
|
||||
*
|
||||
* @internal This class is a TYPO3 Backend implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class FolderTreeView extends AbstractTreeView
|
||||
{
|
||||
/**
|
||||
* The users' file Storages
|
||||
*
|
||||
* @var ResourceStorage[]
|
||||
*/
|
||||
protected $storages;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $storageHashNumbers;
|
||||
|
||||
/**
|
||||
* Indicates, whether the AJAX call was successful,
|
||||
* i.e. the requested page has been found
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $ajaxStatus = false;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $scope;
|
||||
|
||||
/**
|
||||
* @var IconFactory
|
||||
*/
|
||||
protected $iconFactory;
|
||||
|
||||
/**
|
||||
* override to not use a title attribute
|
||||
* @var string
|
||||
*/
|
||||
public $titleAttrib = '';
|
||||
|
||||
/**
|
||||
* override to use this treeName
|
||||
* does not need to be set in __construct()
|
||||
* @var string
|
||||
*/
|
||||
public $treeName = 'folder';
|
||||
|
||||
/**
|
||||
* override to use this domIdPrefix
|
||||
* @var string
|
||||
*/
|
||||
public $domIdPrefix = 'folder';
|
||||
|
||||
/**
|
||||
* Constructor function of the class
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->init();
|
||||
$this->storages = $this->BE_USER->getFileStorages();
|
||||
$this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the plus/minus icon for the browsable tree.
|
||||
*
|
||||
* @param Folder $folderObject Entry folder object
|
||||
* @param int $subFolderCounter The current entry number
|
||||
* @param int $totalSubFolders The total number of entries. If equal to $a, a "bottom" element is returned.
|
||||
* @param int $nextCount The number of sub-elements to the current element.
|
||||
* @param bool $isExpanded The element was expanded to render subelements if this flag is set.
|
||||
*
|
||||
* @return string Image tag with the plus/minus icon.
|
||||
* @internal
|
||||
* @see \TYPO3\CMS\Backend\Tree\View\PageTreeView::PMicon()
|
||||
*/
|
||||
public function PMicon($folderObject, $subFolderCounter, $totalSubFolders, $nextCount, $isExpanded)
|
||||
{
|
||||
$icon = '';
|
||||
if ($nextCount) {
|
||||
$cmd = $this->generateExpandCollapseParameter($this->bank, !$isExpanded, $folderObject);
|
||||
$icon = $this->PMiconATagWrap($icon, $cmd, !$isExpanded);
|
||||
}
|
||||
return $icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the plus/minus icon in a link
|
||||
*
|
||||
* @param string $icon HTML string to wrap, probably an image tag.
|
||||
* @param string $cmd Command for 'PM' get var
|
||||
* @param bool $isExpand Whether to be expanded
|
||||
* @return string Link-wrapped input string
|
||||
* @internal
|
||||
*/
|
||||
public function PMiconATagWrap($icon, $cmd, $isExpand = true)
|
||||
{
|
||||
if (empty($this->scope)) {
|
||||
$this->scope = [
|
||||
'class' => static::class,
|
||||
'script' => $this->thisScript,
|
||||
];
|
||||
}
|
||||
|
||||
if ($this->thisScript) {
|
||||
// Activates dynamic AJAX based tree
|
||||
$scopeData = json_encode($this->scope);
|
||||
$scopeHash = GeneralUtility::hmac($scopeData);
|
||||
$js = htmlspecialchars('Tree.load(' . GeneralUtility::quoteJSvalue($cmd) . ', ' . (int)$isExpand . ', this, ' . GeneralUtility::quoteJSvalue($scopeData) . ', ' . GeneralUtility::quoteJSvalue($scopeHash) . ');');
|
||||
return '<a class="list-tree-control' . (!$isExpand ? ' list-tree-control-open' : ' list-tree-control-closed') . '" onclick="' . $js . '"><i class="fa"></i></a>';
|
||||
}
|
||||
return $icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $cmd
|
||||
* @param bool $isOpen
|
||||
* @return string
|
||||
*/
|
||||
protected function renderPMIconAndLink($cmd, $isOpen)
|
||||
{
|
||||
$link = $this->thisScript ? ' href="' . htmlspecialchars($this->getThisScript() . 'PM=' . $cmd) . '"' : '';
|
||||
return '<a class="list-tree-control list-tree-control-' . ($isOpen ? 'open' : 'closed') . '"' . $link . '><i class="fa"></i></a>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapping the folder icon
|
||||
*
|
||||
* @param string $icon The image tag for the icon
|
||||
* @param Folder $folderObject The row for the current element
|
||||
*
|
||||
* @return string The processed icon input value.
|
||||
* @internal
|
||||
*/
|
||||
public function wrapIcon($icon, $folderObject)
|
||||
{
|
||||
// Add title attribute to input icon tag
|
||||
$theFolderIcon = '';
|
||||
// Wrap icon in click-menu link.
|
||||
if (!$this->ext_IconMode) {
|
||||
// Check storage access to wrap with click menu
|
||||
if (!$folderObject instanceof InaccessibleFolder) {
|
||||
$tableName = $this->getTableNameForClickMenu($folderObject);
|
||||
$theFolderIcon = BackendUtility::wrapClickMenuOnIcon($icon, $tableName, $folderObject->getCombinedIdentifier(), 'tree');
|
||||
}
|
||||
} elseif ($this->ext_IconMode === 'titlelink') {
|
||||
$aOnClick = 'return jumpTo(' . GeneralUtility::quoteJSvalue($this->getJumpToParam($folderObject)) . ',this,' . GeneralUtility::quoteJSvalue($this->domIdPrefix . $this->getId($folderObject)) . ',' . $this->bank . ');';
|
||||
$theFolderIcon = '<a href="#" onclick="' . htmlspecialchars($aOnClick) . '">' . $icon . '</a>';
|
||||
}
|
||||
return $theFolderIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapping $title in a-tags.
|
||||
*
|
||||
* @param string $title Title string
|
||||
* @param Folder $folderObject the folder record
|
||||
* @param int $bank Bank pointer (which mount point number)
|
||||
*
|
||||
* @return string
|
||||
* @internal
|
||||
*/
|
||||
public function wrapTitle($title, $folderObject, $bank = 0)
|
||||
{
|
||||
// Check storage access to wrap with click menu
|
||||
if ($folderObject instanceof InaccessibleFolder) {
|
||||
return $title;
|
||||
}
|
||||
$aOnClick = 'return jumpTo(' . GeneralUtility::quoteJSvalue($this->getJumpToParam($folderObject)) . ', this, ' . GeneralUtility::quoteJSvalue($this->domIdPrefix . $this->getId($folderObject)) . ', ' . $bank . ');';
|
||||
$tableName = $this->getTableNameForClickMenu($folderObject);
|
||||
$clickMenuParts = BackendUtility::wrapClickMenuOnIcon('', $tableName, $folderObject->getCombinedIdentifier(), 'tree', '', '', true);
|
||||
|
||||
return '<a href="#" title="' . htmlspecialchars(strip_tags($title)) . '" onclick="' . htmlspecialchars($aOnClick) . '" ' . GeneralUtility::implodeAttributes($clickMenuParts) . '>' . $title . '</a>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the id from the record - for folders, this is an md5 hash.
|
||||
*
|
||||
* @param Folder $folderObject The folder object
|
||||
*
|
||||
* @return int The "uid" field value.
|
||||
*/
|
||||
public function getId($folderObject)
|
||||
{
|
||||
return GeneralUtility::md5int($folderObject->getCombinedIdentifier());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns jump-url parameter value.
|
||||
*
|
||||
* @param Folder $folderObject The folder object
|
||||
*
|
||||
* @return string The jump-url parameter.
|
||||
*/
|
||||
public function getJumpToParam($folderObject)
|
||||
{
|
||||
return rawurlencode($folderObject->getCombinedIdentifier());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title for the input record. If blank, a "no title" label (localized) will be returned.
|
||||
* '_title' is used for setting an alternative title for folders.
|
||||
*
|
||||
* @param array $row The input row array (where the key "_title" is used for the title)
|
||||
* @param int $titleLen Title length (30)
|
||||
* @return string The title
|
||||
*/
|
||||
public function getTitleStr($row, $titleLen = 30)
|
||||
{
|
||||
return $row['_title'] ?? parent::getTitleStr($row, $titleLen);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value for the image "title" attribute
|
||||
*
|
||||
* @param Folder $folderObject The folder to be used
|
||||
*
|
||||
* @return string The attribute value (is htmlspecialchared() already)
|
||||
*/
|
||||
public function getTitleAttrib($folderObject)
|
||||
{
|
||||
return htmlspecialchars($folderObject->getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Will create and return the HTML code for a browsable tree of folders.
|
||||
* Is based on the mounts found in the internal array ->MOUNTS (set in the constructor)
|
||||
*
|
||||
* @return string HTML code for the browsable tree
|
||||
*/
|
||||
public function getBrowsableTree()
|
||||
{
|
||||
// Get stored tree structure AND updating it if needed according to incoming PM GET var.
|
||||
$this->initializePositionSaving();
|
||||
// Init done:
|
||||
$treeItems = [];
|
||||
// Traverse mounts:
|
||||
foreach ($this->storages as $storageObject) {
|
||||
$this->getBrowseableTreeForStorage($storageObject);
|
||||
// Add tree:
|
||||
$treeItems = array_merge($treeItems, $this->tree);
|
||||
}
|
||||
return $this->printTree($treeItems);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a tree for one storage
|
||||
*
|
||||
* @param ResourceStorage $storageObject
|
||||
*/
|
||||
public function getBrowseableTreeForStorage(ResourceStorage $storageObject)
|
||||
{
|
||||
// If there are filemounts, show each, otherwise just the rootlevel folder
|
||||
$fileMounts = $storageObject->getFileMounts();
|
||||
$rootLevelFolders = [];
|
||||
if (!empty($fileMounts)) {
|
||||
foreach ($fileMounts as $fileMountInfo) {
|
||||
$rootLevelFolders[] = [
|
||||
'folder' => $fileMountInfo['folder'],
|
||||
'name' => $fileMountInfo['title']
|
||||
];
|
||||
}
|
||||
} elseif ($this->BE_USER->isAdmin()) {
|
||||
$rootLevelFolders[] = [
|
||||
'folder' => $storageObject->getRootLevelFolder(),
|
||||
'name' => $storageObject->getName()
|
||||
];
|
||||
}
|
||||
// Clean the tree
|
||||
$this->reset();
|
||||
// Go through all "root level folders" of this tree (can be the rootlevel folder or any file mount points)
|
||||
foreach ($rootLevelFolders as $rootLevelFolderInfo) {
|
||||
/** @var Folder $rootLevelFolder */
|
||||
$rootLevelFolder = $rootLevelFolderInfo['folder'];
|
||||
$rootLevelFolderName = $rootLevelFolderInfo['name'];
|
||||
$folderHashSpecUID = GeneralUtility::md5int($rootLevelFolder->getCombinedIdentifier());
|
||||
$this->specUIDmap[$folderHashSpecUID] = $rootLevelFolder->getCombinedIdentifier();
|
||||
// Hash key
|
||||
$storageHashNumber = $this->getShortHashNumberForStorage($storageObject, $rootLevelFolder);
|
||||
// Set first:
|
||||
$this->bank = $storageHashNumber;
|
||||
$isOpen = $this->stored[$storageHashNumber][$folderHashSpecUID] || $this->expandFirst;
|
||||
// Set PM icon:
|
||||
$cmd = $this->generateExpandCollapseParameter($this->bank, !$isOpen, $rootLevelFolder);
|
||||
// Only show and link icon if storage is browseable
|
||||
if (!$storageObject->isBrowsable() || $this->getNumberOfSubfolders($rootLevelFolder) === 0) {
|
||||
$firstHtml = '';
|
||||
} else {
|
||||
$firstHtml = $this->renderPMIconAndLink($cmd, $isOpen);
|
||||
}
|
||||
// Mark a storage which is not online, as offline
|
||||
// maybe someday there will be a special icon for this
|
||||
if ($storageObject->isOnline() === false) {
|
||||
$rootLevelFolderName .= ' (' . $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_mod_file.xlf:sys_file_storage.isOffline') . ')';
|
||||
}
|
||||
// Preparing rootRec for the mount
|
||||
$icon = $this->iconFactory->getIconForResource($rootLevelFolder, Icon::SIZE_SMALL, null, ['mount-root' => true]);
|
||||
$firstHtml .= $this->wrapIcon($icon, $rootLevelFolder);
|
||||
$row = [
|
||||
'uid' => $folderHashSpecUID,
|
||||
'title' => $rootLevelFolderName,
|
||||
'path' => $rootLevelFolder->getCombinedIdentifier(),
|
||||
'folder' => $rootLevelFolder
|
||||
];
|
||||
// Add the storage root to ->tree
|
||||
$this->tree[] = [
|
||||
'HTML' => $firstHtml,
|
||||
'row' => $row,
|
||||
'bank' => $this->bank,
|
||||
// hasSub is TRUE when the root of the storage is expanded
|
||||
'hasSub' => $isOpen && $storageObject->isBrowsable(),
|
||||
'invertedDepth' => 1000,
|
||||
];
|
||||
// If the mount is expanded, go down:
|
||||
if ($isOpen && $storageObject->isBrowsable()) {
|
||||
// Set depth:
|
||||
$this->getFolderTree($rootLevelFolder, 999);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the data for the tree
|
||||
*
|
||||
* @param Folder $folderObject the folderobject
|
||||
* @param int $depth Max depth (recursivity limit)
|
||||
* @param string $type HTML-code prefix for recursive calls.
|
||||
*
|
||||
* @return int The count of items on the level
|
||||
* @see getBrowsableTree()
|
||||
*/
|
||||
public function getFolderTree(Folder $folderObject, $depth = 999, $type = '')
|
||||
{
|
||||
$depth = (int)$depth;
|
||||
|
||||
// This generates the directory tree
|
||||
/* array of \TYPO3\CMS\Core\Resource\Folder */
|
||||
if ($folderObject instanceof InaccessibleFolder) {
|
||||
$subFolders = [];
|
||||
} else {
|
||||
$subFolders = $folderObject->getSubfolders();
|
||||
$subFolders = \TYPO3\CMS\Core\Resource\Utility\ListUtility::resolveSpecialFolderNames($subFolders);
|
||||
uksort($subFolders, 'strnatcasecmp');
|
||||
}
|
||||
|
||||
$totalSubFolders = count($subFolders);
|
||||
$HTML = '';
|
||||
$subFolderCounter = 0;
|
||||
$treeKey = '';
|
||||
/** @var Folder $subFolder */
|
||||
foreach ($subFolders as $subFolderName => $subFolder) {
|
||||
$subFolderCounter++;
|
||||
// Reserve space.
|
||||
$this->tree[] = [];
|
||||
// Get the key for this space
|
||||
end($this->tree);
|
||||
$isLocked = $subFolder instanceof InaccessibleFolder;
|
||||
$treeKey = key($this->tree);
|
||||
$specUID = GeneralUtility::md5int($subFolder->getCombinedIdentifier());
|
||||
$this->specUIDmap[$specUID] = $subFolder->getCombinedIdentifier();
|
||||
$row = [
|
||||
'uid' => $specUID,
|
||||
'path' => $subFolder->getCombinedIdentifier(),
|
||||
'title' => $subFolderName,
|
||||
'folder' => $subFolder
|
||||
];
|
||||
// Make a recursive call to the next level
|
||||
if (!$isLocked && $depth > 1 && $this->expandNext($specUID)) {
|
||||
$nextCount = $this->getFolderTree($subFolder, $depth - 1, $type);
|
||||
// Set "did expand" flag
|
||||
$isOpen = 1;
|
||||
} else {
|
||||
$nextCount = $isLocked ? 0 : $this->getNumberOfSubfolders($subFolder);
|
||||
// Clear "did expand" flag
|
||||
$isOpen = 0;
|
||||
}
|
||||
// Set HTML-icons, if any:
|
||||
if ($this->makeHTML) {
|
||||
$HTML = $this->PMicon($subFolder, $subFolderCounter, $totalSubFolders, $nextCount, $isOpen);
|
||||
$type = '';
|
||||
|
||||
$role = $subFolder->getRole();
|
||||
if ($role !== FolderInterface::ROLE_DEFAULT) {
|
||||
$row['_title'] = '<strong>' . $subFolderName . '</strong>';
|
||||
}
|
||||
$icon = '<span title="' . htmlspecialchars($subFolderName) . '">'
|
||||
. $this->iconFactory->getIconForResource($subFolder, Icon::SIZE_SMALL, null, ['folder-open' => (bool)$isOpen])
|
||||
. '</span>';
|
||||
$HTML .= $this->wrapIcon($icon, $subFolder);
|
||||
}
|
||||
// Finally, add the row/HTML content to the ->tree array in the reserved key.
|
||||
$this->tree[$treeKey] = [
|
||||
'row' => $row,
|
||||
'HTML' => $HTML,
|
||||
'hasSub' => $nextCount && $this->expandNext($specUID),
|
||||
'isFirst' => $subFolderCounter == 1,
|
||||
'isLast' => false,
|
||||
'invertedDepth' => $depth,
|
||||
'bank' => $this->bank
|
||||
];
|
||||
}
|
||||
if ($subFolderCounter > 0) {
|
||||
$this->tree[$treeKey]['isLast'] = true;
|
||||
}
|
||||
return $totalSubFolders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the HTML code for displaying the structure found inside the ->tree array
|
||||
*
|
||||
* @param array|string $treeItems "tree-array" - if blank string, the internal ->tree array is used.
|
||||
* @return string The HTML code for the tree
|
||||
*/
|
||||
public function printTree($treeItems = '')
|
||||
{
|
||||
$doExpand = false;
|
||||
$doCollapse = false;
|
||||
$ajaxOutput = '';
|
||||
$titleLength = (int)$this->BE_USER->uc['titleLen'];
|
||||
if (!is_array($treeItems)) {
|
||||
$treeItems = $this->tree;
|
||||
}
|
||||
|
||||
if (empty($treeItems)) {
|
||||
$message = GeneralUtility::makeInstance(
|
||||
FlashMessage::class,
|
||||
$this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang.xlf:foldertreeview.noFolders.message'),
|
||||
$this->getLanguageService()->sL('LLL:EXT:backend/Resources/Private/Language/locallang.xlf:foldertreeview.noFolders.title'),
|
||||
FlashMessage::INFO
|
||||
);
|
||||
/** @var \TYPO3\CMS\Core\Messaging\FlashMessageService $flashMessageService */
|
||||
$flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
|
||||
/** @var \TYPO3\CMS\Core\Messaging\FlashMessageQueue $defaultFlashMessageQueue */
|
||||
$defaultFlashMessageQueue = $flashMessageService->getMessageQueueByIdentifier();
|
||||
$defaultFlashMessageQueue->enqueue($message);
|
||||
return $defaultFlashMessageQueue->renderFlashMessages();
|
||||
}
|
||||
|
||||
$expandedFolderHash = '';
|
||||
$invertedDepthOfAjaxRequestedItem = 0;
|
||||
$out = '<ul class="list-tree list-tree-root">';
|
||||
// Evaluate AJAX request
|
||||
if (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_AJAX) {
|
||||
list(, $expandCollapseCommand, $expandedFolderHash, ) = $this->evaluateExpandCollapseParameter();
|
||||
if ($expandCollapseCommand == 1) {
|
||||
$doExpand = true;
|
||||
} else {
|
||||
$doCollapse = true;
|
||||
}
|
||||
}
|
||||
// We need to count the opened <ul>'s every time we dig into another level,
|
||||
// so we know how many we have to close when all children are done rendering
|
||||
$closeDepth = [];
|
||||
foreach ($treeItems as $treeItem) {
|
||||
/** @var Folder $folderObject */
|
||||
$folderObject = $treeItem['row']['folder'];
|
||||
$classAttr = $treeItem['row']['_CSSCLASS'] ?? '';
|
||||
$folderIdentifier = $folderObject->getCombinedIdentifier();
|
||||
// this is set if the AJAX request has just opened this folder (via the PM command)
|
||||
$isExpandedFolderIdentifier = $expandedFolderHash == GeneralUtility::md5int($folderIdentifier);
|
||||
$idAttr = htmlspecialchars($this->domIdPrefix . $this->getId($folderObject) . '_' . $treeItem['bank']);
|
||||
$itemHTML = '';
|
||||
// If this item is the start of a new level,
|
||||
// then a new level <ul> is needed, but not in ajax mode
|
||||
if (!empty($treeItem['isFirst']) && !$doCollapse && !($doExpand && $isExpandedFolderIdentifier)) {
|
||||
$itemHTML = '<ul class="list-tree">';
|
||||
}
|
||||
// Add CSS classes to the list item
|
||||
if (!empty($treeItem['hasSub'])) {
|
||||
$classAttr .= ' list-tree-control-open';
|
||||
}
|
||||
$itemHTML .= '
|
||||
<li id="' . $idAttr . '" ' . ($classAttr ? ' class="' . trim($classAttr) . '"' : '') . '><span class="list-tree-group">' . $treeItem['HTML'] . $this->wrapTitle($this->getTitleStr($treeItem['row'], $titleLength), $folderObject, $treeItem['bank']) . '</span>';
|
||||
if (empty($treeItem['hasSub'])) {
|
||||
$itemHTML .= '</li>';
|
||||
}
|
||||
// We have to remember if this is the last one
|
||||
// on level X so the last child on level X+1 closes the <ul>-tag
|
||||
if (!empty($treeItem['isLast']) && !($doExpand && $isExpandedFolderIdentifier)) {
|
||||
$closeDepth[$treeItem['invertedDepth']] = 1;
|
||||
}
|
||||
// If this is the last one and does not have subitems, we need to close
|
||||
// the tree as long as the upper levels have last items too
|
||||
if (!empty($treeItem['isLast']) && empty($treeItem['hasSub']) && !$doCollapse && !($doExpand && $isExpandedFolderIdentifier)) {
|
||||
for ($i = $treeItem['invertedDepth']; !empty($closeDepth[$i]); $i++) {
|
||||
$closeDepth[$i] = 0;
|
||||
$itemHTML .= '</ul></li>';
|
||||
}
|
||||
}
|
||||
// Ajax request: collapse
|
||||
if ($doCollapse && $isExpandedFolderIdentifier) {
|
||||
$this->ajaxStatus = true;
|
||||
return $itemHTML;
|
||||
}
|
||||
// Ajax request: expand
|
||||
if ($doExpand && $isExpandedFolderIdentifier) {
|
||||
$ajaxOutput .= $itemHTML;
|
||||
$invertedDepthOfAjaxRequestedItem = $treeItem['invertedDepth'];
|
||||
} elseif ($invertedDepthOfAjaxRequestedItem) {
|
||||
if ($treeItem['invertedDepth'] && ($treeItem['invertedDepth'] < $invertedDepthOfAjaxRequestedItem)) {
|
||||
$ajaxOutput .= $itemHTML;
|
||||
} else {
|
||||
$this->ajaxStatus = true;
|
||||
return $ajaxOutput;
|
||||
}
|
||||
}
|
||||
$out .= $itemHTML;
|
||||
}
|
||||
// If this is an AJAX request, output directly
|
||||
if ($ajaxOutput) {
|
||||
$this->ajaxStatus = true;
|
||||
return $ajaxOutput;
|
||||
}
|
||||
// Finally close the first ul
|
||||
$out .= '</ul>';
|
||||
return $out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns table name for click menu
|
||||
*
|
||||
* @param Folder $folderObject
|
||||
* @return string
|
||||
*/
|
||||
protected function getTableNameForClickMenu(Folder $folderObject)
|
||||
{
|
||||
if (strpos($folderObject->getRole(), FolderInterface::ROLE_MOUNT) !== false) {
|
||||
$tableName = 'sys_filemounts';
|
||||
} elseif ($folderObject->getIdentifier() === $folderObject->getStorage()->getRootLevelFolder()->getIdentifier()) {
|
||||
$tableName = 'sys_file_storage';
|
||||
} else {
|
||||
$tableName = 'sys_file';
|
||||
}
|
||||
return $tableName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the number of directories in a file path.
|
||||
*
|
||||
* @param Folder $folderObject File path.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getNumberOfSubfolders(Folder $folderObject)
|
||||
{
|
||||
$subFolders = $folderObject->getSubfolders();
|
||||
return count($subFolders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get stored tree structure AND updating it if needed according to incoming PM GET var.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function initializePositionSaving()
|
||||
{
|
||||
// Get stored tree structure:
|
||||
$this->stored = unserialize($this->BE_USER->uc['browseTrees'][$this->treeName], ['allowed_classes' => false]);
|
||||
$this->getShortHashNumberForStorage();
|
||||
// PM action:
|
||||
// (If an plus/minus icon has been clicked,
|
||||
// the PM GET var is sent and we must update the stored positions in the tree):
|
||||
// 0: mount key, 1: set/clear boolean, 2: item ID (cannot contain "_"), 3: treeName
|
||||
list($storageHashNumber, $doExpand, $numericFolderHash, $treeName) = $this->evaluateExpandCollapseParameter();
|
||||
if ($treeName && $treeName == $this->treeName) {
|
||||
if (in_array($storageHashNumber, $this->storageHashNumbers)) {
|
||||
if ($doExpand == 1) {
|
||||
// Set
|
||||
$this->stored[$storageHashNumber][$numericFolderHash] = 1;
|
||||
} else {
|
||||
// Clear
|
||||
unset($this->stored[$storageHashNumber][$numericFolderHash]);
|
||||
}
|
||||
$this->savePosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to map md5-hash to shorter number
|
||||
*
|
||||
* @param ResourceStorage $storageObject
|
||||
* @param Folder $startingPointFolder
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
protected function getShortHashNumberForStorage(ResourceStorage $storageObject = null, Folder $startingPointFolder = null)
|
||||
{
|
||||
if (!$this->storageHashNumbers) {
|
||||
$this->storageHashNumbers = [];
|
||||
foreach ($this->storages as $storageUid => $storage) {
|
||||
$fileMounts = $storage->getFileMounts();
|
||||
if (!empty($fileMounts)) {
|
||||
foreach ($fileMounts as $fileMount) {
|
||||
$nkey = hexdec(substr(GeneralUtility::md5int($fileMount['folder']->getCombinedIdentifier()), 0, 4));
|
||||
$this->storageHashNumbers[$storageUid . $fileMount['folder']->getCombinedIdentifier()] = $nkey;
|
||||
}
|
||||
} else {
|
||||
$folder = $storage->getRootLevelFolder();
|
||||
$nkey = hexdec(substr(GeneralUtility::md5int($folder->getCombinedIdentifier()), 0, 4));
|
||||
$this->storageHashNumbers[$storageUid . $folder->getCombinedIdentifier()] = $nkey;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($storageObject) {
|
||||
if ($startingPointFolder) {
|
||||
return $this->storageHashNumbers[$storageObject->getUid() . $startingPointFolder->getCombinedIdentifier()];
|
||||
}
|
||||
return $this->storageHashNumbers[$storageObject->getUid()];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the values from the Expand/Collapse Parameter (&PM)
|
||||
* previously known as "PM" (plus/minus)
|
||||
* PM action:
|
||||
* (If an plus/minus icon has been clicked,
|
||||
* the PM GET var is sent and we must update the stored positions in the tree):
|
||||
* 0: mount key, 1: set/clear boolean, 2: item ID (cannot contain "_"), 3: treeName
|
||||
*
|
||||
* @param string $PM The "plus/minus" command
|
||||
* @return array
|
||||
*/
|
||||
protected function evaluateExpandCollapseParameter($PM = null)
|
||||
{
|
||||
if ($PM === null) {
|
||||
$PM = GeneralUtility::_GP('PM');
|
||||
// IE takes anchor as parameter
|
||||
if (($PMpos = strpos($PM, '#')) !== false) {
|
||||
$PM = substr($PM, 0, $PMpos);
|
||||
}
|
||||
}
|
||||
// Take the first three parameters
|
||||
list($mountKey, $doExpand, $folderIdentifier) = array_pad(explode('_', $PM, 3), 3, null);
|
||||
// In case the folder identifier contains "_", we just need to get the fourth/last parameter
|
||||
list($folderIdentifier, $treeName) = array_pad(GeneralUtility::revExplode('_', $folderIdentifier, 2), 2, null);
|
||||
return [
|
||||
$mountKey,
|
||||
$doExpand,
|
||||
$folderIdentifier,
|
||||
$treeName
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the "PM" string to sent to expand/collapse items
|
||||
*
|
||||
* @param string $mountKey The mount key / storage UID
|
||||
* @param bool $doExpand Whether to expand/collapse
|
||||
* @param Folder $folderObject The folder object
|
||||
* @param string $treeName The name of the tree
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function generateExpandCollapseParameter($mountKey = null, $doExpand = false, Folder $folderObject = null, $treeName = null)
|
||||
{
|
||||
$parts = [
|
||||
$mountKey ?? $this->bank,
|
||||
$doExpand == 1 ? 1 : 0,
|
||||
$folderObject !== null ? GeneralUtility::md5int($folderObject->getCombinedIdentifier()) : '',
|
||||
$treeName ?? $this->treeName
|
||||
];
|
||||
return implode('_', $parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the AJAX status.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getAjaxStatus()
|
||||
{
|
||||
return $this->ajaxStatus;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LanguageService
|
||||
*/
|
||||
protected function getLanguageService()
|
||||
{
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Backend\Tree\View;
|
||||
|
||||
/*
|
||||
* 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!
|
||||
*/
|
||||
|
||||
/**
|
||||
* Extension for the tree class that generates the tree of pages in the page-wizard mode
|
||||
*
|
||||
* @see \TYPO3\CMS\Backend\Controller\NewRecordController
|
||||
* @internal This class is a TYPO3 Backend implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class NewRecordPageTreeView extends PageTreeView
|
||||
{
|
||||
/**
|
||||
* Determines whether to expand a branch or not.
|
||||
* Here the branch is expanded if the current id matches the global id for the listing/new
|
||||
*
|
||||
* @param int $id The ID (page id) of the element
|
||||
* @return bool Returns TRUE if the IDs matches
|
||||
*/
|
||||
public function expandNext($id)
|
||||
{
|
||||
return $id == $GLOBALS['SOBE']->id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Backend\Tree\View;
|
||||
|
||||
/*
|
||||
* 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 TYPO3\CMS\Backend\Routing\UriBuilder;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Position map class for moving pages,
|
||||
* @internal This class is a TYPO3 Backend implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class PageMovingPagePositionMap extends PagePositionMap
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $l_insertNewPageHere = 'movePageToHere';
|
||||
|
||||
/**
|
||||
* Page tree implementation class name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $pageTreeClassName = PageTreeView::class;
|
||||
|
||||
/**
|
||||
* Creates the onclick event for the insert-icons.
|
||||
*
|
||||
* @param int $pid The pid.
|
||||
* @param int $newPagePID New page id.
|
||||
* @return string Onclick attribute content
|
||||
*/
|
||||
public function onClickEvent($pid, $newPagePID)
|
||||
{
|
||||
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
|
||||
return 'window.location.href=' . GeneralUtility::quoteJSvalue((string)$uriBuilder->buildUriFromRoute('tce_db', [
|
||||
'cmd[pages][' . $this->moveUid . '][' . $this->moveOrCopy . ']' => $pid,
|
||||
'redirect' => $this->R_URI,
|
||||
])) . ';return false;';
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapping page title.
|
||||
*
|
||||
* @param string $str Page title.
|
||||
* @param array $rec Page record (?)
|
||||
* @return string Wrapped title.
|
||||
*/
|
||||
public function linkPageTitle($str, $rec)
|
||||
{
|
||||
$url = GeneralUtility::linkThisScript(['uid' => (int)$rec['uid'], 'moveUid' => $this->moveUid]);
|
||||
return '<a href="' . htmlspecialchars($url) . '">' . $str . '</a>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap $t_code in bold IF the $dat uid matches $id
|
||||
*
|
||||
* @param string $t_code Title string
|
||||
* @param array $dat Information array with record array inside.
|
||||
* @param int $id The current id.
|
||||
* @return string The title string.
|
||||
*/
|
||||
public function boldTitle($t_code, $dat, $id)
|
||||
{
|
||||
return parent::boldTitle($t_code, $dat, $this->moveUid);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,663 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Backend\Tree\View;
|
||||
|
||||
/*
|
||||
* 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 TYPO3\CMS\Backend\Routing\UriBuilder;
|
||||
use TYPO3\CMS\Backend\Utility\BackendUtility;
|
||||
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
|
||||
use TYPO3\CMS\Core\Compatibility\PublicPropertyDeprecationTrait;
|
||||
use TYPO3\CMS\Core\Database\ConnectionPool;
|
||||
use TYPO3\CMS\Core\Database\Query\Restriction\BackendWorkspaceRestriction;
|
||||
use TYPO3\CMS\Core\Database\Query\Restriction\EndTimeRestriction;
|
||||
use TYPO3\CMS\Core\Database\Query\Restriction\HiddenRestriction;
|
||||
use TYPO3\CMS\Core\Database\Query\Restriction\StartTimeRestriction;
|
||||
use TYPO3\CMS\Core\Imaging\Icon;
|
||||
use TYPO3\CMS\Core\Imaging\IconFactory;
|
||||
use TYPO3\CMS\Core\Localization\LanguageService;
|
||||
use TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Position map class - generating a page tree / content element list which links for inserting (copy/move) of records.
|
||||
* Used for pages / tt_content element wizards of various kinds.
|
||||
* @internal This class is a TYPO3 Backend implementation and is not considered part of the Public TYPO3 API.
|
||||
*/
|
||||
class PagePositionMap
|
||||
{
|
||||
use PublicPropertyDeprecationTrait;
|
||||
|
||||
/**
|
||||
* Properties which have been moved to protected status from public
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $deprecatedPublicProperties = [
|
||||
'getModConfigCache' => 'Using $getModConfigCache of class PagePositionMap is discouraged. This property will be removed in TYPO3 v10.0.',
|
||||
'modConfigStr' => 'Using $$modConfigStr of class PagePositionMap is discouraged. This property will be removed in TYPO3 v10.0.',
|
||||
];
|
||||
|
||||
// EXTERNAL, static:
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $moveOrCopy = 'move';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $dontPrintPageInsertIcons = 0;
|
||||
|
||||
// How deep the position page tree will go.
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
public $depth = 2;
|
||||
|
||||
// Can be set to the sys_language uid to select content elements for.
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $cur_sys_language;
|
||||
|
||||
// INTERNAL, dynamic:
|
||||
// Request uri
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $R_URI = '';
|
||||
|
||||
// Element id.
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $elUid = '';
|
||||
|
||||
// tt_content element uid to move.
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $moveUid = '';
|
||||
|
||||
// Caching arrays:
|
||||
/**
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
|
||||
*/
|
||||
protected $getModConfigCache = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $checkNewPageCache = [];
|
||||
|
||||
// Label keys:
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $l_insertNewPageHere = 'insertNewPageHere';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $l_insertNewRecordHere = 'insertNewRecordHere';
|
||||
|
||||
/**
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
|
||||
*/
|
||||
protected $modConfigStr = 'mod.web_list.newPageWiz';
|
||||
|
||||
/**
|
||||
* Page tree implementation class name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $pageTreeClassName = ElementBrowserPageTreeView::class;
|
||||
|
||||
/**
|
||||
* @var IconFactory
|
||||
*/
|
||||
protected $iconFactory;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $clientContext;
|
||||
|
||||
/**
|
||||
* Constructor allowing to set pageTreeImplementation
|
||||
*
|
||||
* @param string $pageTreeClassName
|
||||
* @param string $clientContext JavaScript context of view client (either 'window' or 'list_frame')
|
||||
*/
|
||||
public function __construct(string $pageTreeClassName = null, string $clientContext = 'window')
|
||||
{
|
||||
if ($pageTreeClassName !== null) {
|
||||
$this->pageTreeClassName = $pageTreeClassName;
|
||||
}
|
||||
$this->clientContext = $clientContext;
|
||||
$this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
|
||||
}
|
||||
|
||||
/*************************************
|
||||
*
|
||||
* Page position map:
|
||||
*
|
||||
**************************************/
|
||||
/**
|
||||
* Creates a "position tree" based on the page tree.
|
||||
*
|
||||
* @param int $id Current page id
|
||||
* @param array $pageinfo Current page record.
|
||||
* @param string $perms_clause Page selection permission clause.
|
||||
* @param string $R_URI Current REQUEST_URI
|
||||
* @return string HTML code for the tree.
|
||||
*/
|
||||
public function positionTree($id, $pageinfo, $perms_clause, $R_URI)
|
||||
{
|
||||
// Make page tree object:
|
||||
/** @var \TYPO3\CMS\Backend\Tree\View\PageTreeView $pageTree */
|
||||
$pageTree = GeneralUtility::makeInstance($this->pageTreeClassName);
|
||||
$pageTree->init(' AND ' . $perms_clause);
|
||||
$pageTree->addField('pid');
|
||||
// Initialize variables:
|
||||
$this->R_URI = $R_URI;
|
||||
$this->elUid = $id;
|
||||
// Create page tree, in $this->depth levels.
|
||||
$pageTree->getTree($pageinfo['pid'], $this->depth);
|
||||
// Initialize variables:
|
||||
$saveLatestUid = [];
|
||||
$latestInvDepth = $this->depth;
|
||||
// Traverse the tree:
|
||||
$lines = [];
|
||||
foreach ($pageTree->tree as $cc => $dat) {
|
||||
if ($latestInvDepth > $dat['invertedDepth']) {
|
||||
$margin = 'style="margin-left: ' . ($dat['invertedDepth'] * 16 + 9) . 'px;"';
|
||||
$lines[] = '<ul class="list-tree" ' . $margin . '>';
|
||||
}
|
||||
// Make link + parameters.
|
||||
$latestInvDepth = $dat['invertedDepth'];
|
||||
$saveLatestUid[$latestInvDepth] = $dat;
|
||||
if (isset($pageTree->tree[$cc - 1])) {
|
||||
$prev_dat = $pageTree->tree[$cc - 1];
|
||||
// If current page, subpage?
|
||||
if ($prev_dat['row']['uid'] == $id) {
|
||||
// 1) It must be allowed to create a new page and 2) If there are subpages there is no need to render a subpage icon here - it'll be done over the subpages...
|
||||
if (!$this->dontPrintPageInsertIcons && $this->checkNewPageInPid($id) && !($prev_dat['invertedDepth'] > $pageTree->tree[$cc]['invertedDepth'])) {
|
||||
end($lines);
|
||||
$margin = 'style="margin-left: ' . (($dat['invertedDepth'] - 1) * 16 + 9) . 'px;"';
|
||||
$lines[] = '<ul class="list-tree" ' . $margin . '><li><span class="text-nowrap"><a href="#" onclick="' . htmlspecialchars($this->onClickEvent($id, $id)) . '"><i class="t3-icon fa fa-long-arrow-left" title="' . $this->insertlabel() . '"></i></a></span></li></ul>';
|
||||
}
|
||||
}
|
||||
// If going down
|
||||
if ($prev_dat['invertedDepth'] > $pageTree->tree[$cc]['invertedDepth']) {
|
||||
$prevPid = $pageTree->tree[$cc]['row']['pid'];
|
||||
} elseif ($prev_dat['invertedDepth'] < $pageTree->tree[$cc]['invertedDepth']) {
|
||||
// If going up
|
||||
// First of all the previous level should have an icon:
|
||||
if (!$this->dontPrintPageInsertIcons && $this->checkNewPageInPid($prev_dat['row']['pid'])) {
|
||||
$prevPid = -$prev_dat['row']['uid'];
|
||||
end($lines);
|
||||
$lines[] = '<li><span class="text-nowrap"><a href="#" onclick="' . htmlspecialchars($this->onClickEvent($prevPid, $prev_dat['row']['pid'])) . '"><i class="t3-icon fa fa-long-arrow-left" title="' . $this->insertlabel() . '"></i></a></span></li>';
|
||||
}
|
||||
// Then set the current prevPid
|
||||
$prevPid = -$prev_dat['row']['pid'];
|
||||
if ($prevPid !== $dat['row']['pid']) {
|
||||
$lines[] = '</ul>';
|
||||
}
|
||||
} else {
|
||||
// In on the same level
|
||||
$prevPid = -$prev_dat['row']['uid'];
|
||||
}
|
||||
} else {
|
||||
// First in the tree
|
||||
$prevPid = $dat['row']['pid'];
|
||||
}
|
||||
// print arrow on the same level
|
||||
if (!$this->dontPrintPageInsertIcons && $this->checkNewPageInPid($dat['row']['pid'])) {
|
||||
$lines[] = '<span class="text-nowrap"><a href="#" onclick="' . htmlspecialchars($this->onClickEvent($prevPid, $dat['row']['pid'])) . '"><i class="t3-icon fa fa-long-arrow-left" title="' . $this->insertlabel() . '"></i></a></span>';
|
||||
}
|
||||
// The line with the icon and title:
|
||||
$toolTip = BackendUtility::getRecordToolTip($dat['row'], 'pages');
|
||||
$icon = '<span ' . $toolTip . '>' . $this->iconFactory->getIconForRecord('pages', $dat['row'], Icon::SIZE_SMALL)->render() . '</span>';
|
||||
|
||||
$lines[] = '<span class="text-nowrap">' . $icon . $this->linkPageTitle($this->boldTitle(htmlspecialchars(GeneralUtility::fixed_lgd_cs($dat['row']['title'], $this->getBackendUser()->uc['titleLen'])), $dat, $id), $dat['row']) . '</span>';
|
||||
}
|
||||
// If the current page was the last in the tree:
|
||||
$prev_dat = end($pageTree->tree);
|
||||
if ($prev_dat['row']['uid'] == $id) {
|
||||
if (!$this->dontPrintPageInsertIcons && $this->checkNewPageInPid($id)) {
|
||||
$lines[] = '<ul class="list-tree" style="margin-left: 25px"><li><span class="text-nowrap"><a href="#" onclick="' . htmlspecialchars($this->onClickEvent($id, $id)) . '"><i class="t3-icon fa fa-long-arrow-left" title="' . $this->insertlabel() . '"></i></a></span></li></ul>';
|
||||
}
|
||||
}
|
||||
for ($a = $latestInvDepth; $a <= $this->depth; $a++) {
|
||||
$dat = $saveLatestUid[$a];
|
||||
$prevPid = -$dat['row']['uid'];
|
||||
if (!$this->dontPrintPageInsertIcons && $this->checkNewPageInPid($dat['row']['pid'])) {
|
||||
if ($latestInvDepth < $dat['invertedDepth']) {
|
||||
$lines[] = '</ul>';
|
||||
}
|
||||
$lines[] = '<span class="text-nowrap"><a href="#" onclick="' . htmlspecialchars($this->onClickEvent($prevPid, $dat['row']['pid'])) . '"><i class="t3-icon fa fa-long-arrow-left" title="' . $this->insertlabel() . '"></i></a></span>';
|
||||
}
|
||||
}
|
||||
|
||||
$code = '<ul class="list-tree">';
|
||||
|
||||
foreach ($lines as $line) {
|
||||
if ((strpos($line, '<ul') === 0) || (strpos($line, '</ul') === 0)) {
|
||||
$code .= $line;
|
||||
} else {
|
||||
$code .= '<li>' . $line . '</li>';
|
||||
}
|
||||
}
|
||||
|
||||
$code .= '</ul>';
|
||||
return $code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap $t_code in bold IF the $dat uid matches $id
|
||||
*
|
||||
* @param string $t_code Title string
|
||||
* @param array $dat Information array with record array inside.
|
||||
* @param int $id The current id.
|
||||
* @return string The title string.
|
||||
*/
|
||||
public function boldTitle($t_code, $dat, $id)
|
||||
{
|
||||
if ($dat['row']['uid'] == $id) {
|
||||
$t_code = '<strong>' . $t_code . '</strong>';
|
||||
}
|
||||
return $t_code;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the onclick event for the insert-icons.
|
||||
*
|
||||
* TSconfig mod.newPageWizard.override may contain an alternative module / route which can be
|
||||
* used instead of the normal create new page wizard.
|
||||
*
|
||||
* @param int $pid The pid.
|
||||
* @param int $newPagePID New page id.
|
||||
* @return string Onclick attribute content
|
||||
*/
|
||||
public function onClickEvent($pid, $newPagePID)
|
||||
{
|
||||
$TSconfig = BackendUtility::getPagesTSconfig($newPagePID)['mod.']['newPageWizard.'] ?? [];
|
||||
if (isset($TSconfig['override']) && !empty($TSconfig['override'])) {
|
||||
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
|
||||
$url = $uriBuilder->buildUriFromRoute(
|
||||
$TSconfig['override'],
|
||||
[
|
||||
'positionPid' => $pid,
|
||||
'newPageId' => $newPagePID,
|
||||
'cmd' => 'crPage',
|
||||
'returnUrl' => GeneralUtility::getIndpEnv('REQUEST_URI')
|
||||
]
|
||||
);
|
||||
return $this->clientContext . '.location.href=' . GeneralUtility::quoteJSvalue((string)$url) . ';';
|
||||
}
|
||||
$params = '&edit[pages][' . $pid . ']=new&returnNewPageId=1';
|
||||
return BackendUtility::editOnClick($params, '', $this->R_URI);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get label, htmlspecialchars()'ed
|
||||
*
|
||||
* @return string The localized label for "insert new page here
|
||||
*/
|
||||
public function insertlabel()
|
||||
{
|
||||
return htmlspecialchars($this->getLanguageService()->getLL($this->l_insertNewPageHere));
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapping page title.
|
||||
*
|
||||
* @param string $str Page title.
|
||||
* @param array $rec Page record (?)
|
||||
* @return string Wrapped title.
|
||||
*/
|
||||
public function linkPageTitle($str, $rec)
|
||||
{
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the user has permission to created pages inside of the $pid page.
|
||||
* Uses caching so only one regular lookup is made - hence you can call the function multiple times without worrying about performance.
|
||||
*
|
||||
* @param int $pid Page id for which to test.
|
||||
* @return bool
|
||||
*/
|
||||
public function checkNewPageInPid($pid)
|
||||
{
|
||||
if (!isset($this->checkNewPageCache[$pid])) {
|
||||
$pidInfo = BackendUtility::getRecord('pages', $pid);
|
||||
$this->checkNewPageCache[$pid] = $this->getBackendUser()->isAdmin() || $this->getBackendUser()->doesUserHaveAccess($pidInfo, 8);
|
||||
}
|
||||
return $this->checkNewPageCache[$pid];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns module configuration for a pid.
|
||||
*
|
||||
* @param int $pid Page id for which to get the module configuration.
|
||||
* @return array The properties of the module configuration for the page id.
|
||||
* @see onClickEvent()
|
||||
* @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0
|
||||
*/
|
||||
public function getModConfig($pid)
|
||||
{
|
||||
trigger_error('PagePositionMap->getModConfig() will be removed in TYPO3 v10.0.', E_USER_DEPRECATED);
|
||||
if (!isset($this->getModConfigCache[$pid])) {
|
||||
// Acquiring TSconfig for this PID:
|
||||
$this->getModConfigCache[$pid]['properties'] = BackendUtility::getPagesTSconfig($pid)['mod.']['web_list.']['newPageWiz.'] ?? [];
|
||||
}
|
||||
return $this->getModConfigCache[$pid]['properties'];
|
||||
}
|
||||
|
||||
/*************************************
|
||||
*
|
||||
* Content element positioning:
|
||||
*
|
||||
**************************************/
|
||||
/**
|
||||
* Creates HTML for inserting/moving content elements.
|
||||
*
|
||||
* @param int $pid page id onto which to insert content element.
|
||||
* @param int $moveUid Move-uid (tt_content element uid?)
|
||||
* @param string $colPosList List of columns to show
|
||||
* @param bool $showHidden If not set, then hidden/starttime/endtime records are filtered out.
|
||||
* @param string $R_URI Request URI
|
||||
* @return string HTML
|
||||
*/
|
||||
public function printContentElementColumns($pid, $moveUid, $colPosList, $showHidden, $R_URI)
|
||||
{
|
||||
$this->R_URI = $R_URI;
|
||||
$this->moveUid = $moveUid;
|
||||
$colPosArray = GeneralUtility::trimExplode(',', $colPosList, true);
|
||||
$lines = [];
|
||||
foreach ($colPosArray as $kk => $vv) {
|
||||
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tt_content');
|
||||
$queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(BackendWorkspaceRestriction::class));
|
||||
if ($showHidden) {
|
||||
$queryBuilder->getRestrictions()
|
||||
->removeByType(HiddenRestriction::class)
|
||||
->removeByType(StartTimeRestriction::class)
|
||||
->removeByType(EndTimeRestriction::class);
|
||||
}
|
||||
$queryBuilder
|
||||
->select('*')
|
||||
->from('tt_content')
|
||||
->where(
|
||||
$queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pid, \PDO::PARAM_INT)),
|
||||
$queryBuilder->expr()->eq('colPos', $queryBuilder->createNamedParameter($vv, \PDO::PARAM_INT))
|
||||
)
|
||||
->orderBy('sorting');
|
||||
|
||||
if ((string)$this->cur_sys_language !== '') {
|
||||
$queryBuilder->andWhere(
|
||||
$queryBuilder->expr()->eq(
|
||||
'sys_language_uid',
|
||||
$queryBuilder->createNamedParameter($this->cur_sys_language, \PDO::PARAM_INT)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
$res = $queryBuilder->execute();
|
||||
$lines[$vv] = [];
|
||||
$lines[$vv][] = $this->insertPositionIcon('', $vv, $kk, $moveUid, $pid);
|
||||
|
||||
while ($row = $res->fetch()) {
|
||||
BackendUtility::workspaceOL('tt_content', $row);
|
||||
if (is_array($row)) {
|
||||
$lines[$vv][] = $this->wrapRecordHeader($this->getRecordHeader($row), $row);
|
||||
$lines[$vv][] = $this->insertPositionIcon($row, $vv, $kk, $moveUid, $pid);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $this->printRecordMap($lines, $colPosArray, $pid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the table with the content columns
|
||||
*
|
||||
* @param array $lines Array with arrays of lines for each column
|
||||
* @param array $colPosArray Column position array
|
||||
* @param int $pid The id of the page
|
||||
* @return string HTML
|
||||
*/
|
||||
public function printRecordMap($lines, $colPosArray, $pid = 0)
|
||||
{
|
||||
$count = \TYPO3\CMS\Core\Utility\MathUtility::forceIntegerInRange(count($colPosArray), 1);
|
||||
$backendLayout = GeneralUtility::callUserFunction(\TYPO3\CMS\Backend\View\BackendLayoutView::class . '->getSelectedBackendLayout', $pid, $this);
|
||||
if (isset($backendLayout['__config']['backend_layout.'])) {
|
||||
$this->getLanguageService()->includeLLFile('EXT:backend/Resources/Private/Language/locallang_layout.xlf');
|
||||
$table = '<div class="table-fit"><table class="table table-condensed table-bordered table-vertical-top">';
|
||||
$colCount = (int)$backendLayout['__config']['backend_layout.']['colCount'];
|
||||
$rowCount = (int)$backendLayout['__config']['backend_layout.']['rowCount'];
|
||||
$table .= '<colgroup>';
|
||||
for ($i = 0; $i < $colCount; $i++) {
|
||||
$table .= '<col style="width:' . 100 / $colCount . '%"></col>';
|
||||
}
|
||||
$table .= '</colgroup>';
|
||||
$table .= '<tbody>';
|
||||
$tcaItems = GeneralUtility::callUserFunction(\TYPO3\CMS\Backend\View\BackendLayoutView::class . '->getColPosListItemsParsed', $pid, $this);
|
||||
// Cycle through rows
|
||||
for ($row = 1; $row <= $rowCount; $row++) {
|
||||
$rowConfig = $backendLayout['__config']['backend_layout.']['rows.'][$row . '.'];
|
||||
if (!isset($rowConfig)) {
|
||||
continue;
|
||||
}
|
||||
$table .= '<tr>';
|
||||
for ($col = 1; $col <= $colCount; $col++) {
|
||||
$columnConfig = $rowConfig['columns.'][$col . '.'];
|
||||
if (!isset($columnConfig)) {
|
||||
continue;
|
||||
}
|
||||
// Which tt_content colPos should be displayed inside this cell
|
||||
$columnKey = (int)$columnConfig['colPos'];
|
||||
$head = '';
|
||||
foreach ($tcaItems as $item) {
|
||||
if ($item[1] == $columnKey) {
|
||||
$head = htmlspecialchars($this->getLanguageService()->sL($item[0]));
|
||||
}
|
||||
}
|
||||
// Render the grid cell
|
||||
$table .= '<td'
|
||||
. (isset($columnConfig['colspan']) ? ' colspan="' . $columnConfig['colspan'] . '"' : '')
|
||||
. (isset($columnConfig['rowspan']) ? ' rowspan="' . $columnConfig['rowspan'] . '"' : '')
|
||||
. ' class="col-nowrap col-min'
|
||||
. (!isset($columnConfig['colPos']) ? ' warning' : '')
|
||||
. (isset($columnConfig['colPos']) && !$head ? ' danger' : '') . '">';
|
||||
// Render header
|
||||
$table .= '<p>';
|
||||
if (isset($columnConfig['colPos']) && $head) {
|
||||
$table .= '<strong>' . $this->wrapColumnHeader($head, '') . '</strong>';
|
||||
} elseif ($columnConfig['colPos']) {
|
||||
$table .= '<em>' . $this->wrapColumnHeader($this->getLanguageService()->getLL('noAccess'), '') . '</em>';
|
||||
} else {
|
||||
$table .= '<em>' . $this->wrapColumnHeader(($this->getLanguageService()->sL($columnConfig['name']) ?: '') . ' (' . $this->getLanguageService()->getLL('notAssigned') . ')', '') . '</em>';
|
||||
}
|
||||
$table .= '</p>';
|
||||
// Render lines
|
||||
if (isset($columnConfig['colPos']) && $head && !empty($lines[$columnKey])) {
|
||||
$table .= '<ul class="list-unstyled">';
|
||||
foreach ($lines[$columnKey] as $line) {
|
||||
$table .= '<li>' . $line . '</li>';
|
||||
}
|
||||
$table .= '</ul>';
|
||||
}
|
||||
$table .= '</td>';
|
||||
}
|
||||
$table .= '</tr>';
|
||||
}
|
||||
$table .= '</tbody>';
|
||||
$table .= '</table></div>';
|
||||
} else {
|
||||
// Traverse the columns here:
|
||||
$row = '';
|
||||
foreach ($colPosArray as $kk => $vv) {
|
||||
$row .= '<td class="col-nowrap col-min" width="' . round(100 / $count) . '%">';
|
||||
$row .= '<p><strong>' . $this->wrapColumnHeader(htmlspecialchars($this->getLanguageService()->sL(BackendUtility::getLabelFromItemlist('tt_content', 'colPos', $vv))), $vv) . '</strong></p>';
|
||||
if (!empty($lines[$vv])) {
|
||||
$row .= '<ul class="list-unstyled">';
|
||||
foreach ($lines[$vv] as $line) {
|
||||
$row .= '<li>' . $line . '</li>';
|
||||
}
|
||||
$row .= '</ul>';
|
||||
}
|
||||
$row .= '</td>';
|
||||
}
|
||||
$table = '
|
||||
|
||||
<!--
|
||||
Map of records in columns:
|
||||
-->
|
||||
<div class="table-fit">
|
||||
<table class="table table-condensed table-bordered table-vertical-top">
|
||||
<tr>' . $row . '</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
';
|
||||
}
|
||||
return $table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapping the column header
|
||||
*
|
||||
* @param string $str Header value
|
||||
* @param string $vv Column info.
|
||||
* @return string
|
||||
* @see printRecordMap()
|
||||
*/
|
||||
public function wrapColumnHeader($str, $vv)
|
||||
{
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a linked position icon.
|
||||
*
|
||||
* @param mixed $row Element row. If this is an array the link will cause an insert after this content element, otherwise
|
||||
* the link will insert at the first position in the column
|
||||
* @param string $vv Column position value.
|
||||
* @param int $kk Column key.
|
||||
* @param int $moveUid Move uid
|
||||
* @param int $pid PID value.
|
||||
* @return string
|
||||
*/
|
||||
public function insertPositionIcon($row, $vv, $kk, $moveUid, $pid)
|
||||
{
|
||||
if (is_array($row) && !empty($row['uid'])) {
|
||||
// Use record uid for the hash when inserting after this content element
|
||||
$uid = $row['uid'];
|
||||
} else {
|
||||
// No uid means insert at first position in the column
|
||||
$uid = '';
|
||||
}
|
||||
$cc = hexdec(substr(md5($uid . '-' . $vv . '-' . $kk), 0, 4));
|
||||
return '<a href="#" onclick="' . htmlspecialchars($this->onClickInsertRecord($row, $vv, $moveUid, $pid, $this->cur_sys_language)) . '" data-dismiss="modal">' . '<i class="t3-icon fa fa-long-arrow-left" name="mImgEnd' . $cc . '" title="' . htmlspecialchars($this->getLanguageService()->getLL($this->l_insertNewRecordHere)) . '"></i></a>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Create on-click event value.
|
||||
*
|
||||
* @param mixed $row The record. If this is not an array with the record data the insert will be for the first position
|
||||
* in the column
|
||||
* @param string $vv Column position value.
|
||||
* @param int $moveUid Move uid
|
||||
* @param int $pid PID value.
|
||||
* @param int $sys_lang System language (not used currently)
|
||||
* @return string
|
||||
*/
|
||||
public function onClickInsertRecord($row, $vv, $moveUid, $pid, $sys_lang = 0)
|
||||
{
|
||||
$uriBuilder = GeneralUtility::makeInstance(UriBuilder::class);
|
||||
if (is_array($row)) {
|
||||
$location = $uriBuilder->buildUriFromRoute('tce_db', [
|
||||
'cmd[tt_content][' . $moveUid . '][' . $this->moveOrCopy . ']' => '-' . $row['uid'],
|
||||
'redirect' => $this->R_URI,
|
||||
]);
|
||||
} else {
|
||||
$location = $uriBuilder->buildUriFromRoute('tce_db', [
|
||||
'cmd[tt_content][' . $moveUid . '][' . $this->moveOrCopy . ']' => $pid,
|
||||
'data[tt_content][' . $moveUid . '][colPos]' => $vv,
|
||||
'redirect' => $this->R_URI,
|
||||
]);
|
||||
}
|
||||
// returns to prev. page
|
||||
return $this->clientContext . '.location.href=' . GeneralUtility::quoteJSvalue((string)$location) . ';return false;';
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapping the record header (from getRecordHeader())
|
||||
*
|
||||
* @param string $str HTML content
|
||||
* @param string $row Record array.
|
||||
* @return string HTML content
|
||||
*/
|
||||
public function wrapRecordHeader($str, $row)
|
||||
{
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create record header (includes the record icon, record title etc.)
|
||||
*
|
||||
* @param array $row Record row.
|
||||
* @return string HTML
|
||||
*/
|
||||
public function getRecordHeader($row)
|
||||
{
|
||||
$iconFactory = GeneralUtility::makeInstance(IconFactory::class);
|
||||
$toolTip = BackendUtility::getRecordToolTip($row, 'tt_content');
|
||||
$line = '<span ' . $toolTip . ' title="' . BackendUtility::getRecordIconAltText($row, 'tt_content') . '">' . $iconFactory->getIconForRecord('tt_content', $row, Icon::SIZE_SMALL)->render() . '</span>';
|
||||
$line .= BackendUtility::getRecordTitle('tt_content', $row, true);
|
||||
return $this->wrapRecordTitle($line, $row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapping the title of the record.
|
||||
*
|
||||
* @param string $str The title value.
|
||||
* @param array $row The record row.
|
||||
* @return string Wrapped title string.
|
||||
*/
|
||||
public function wrapRecordTitle($str, $row)
|
||||
{
|
||||
return '<a href="' . htmlspecialchars(GeneralUtility::linkThisScript(['uid' => (int)$row['uid'], 'moveUid' => ''])) . '">' . $str . '</a>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the BackendUser
|
||||
*
|
||||
* @return BackendUserAuthentication
|
||||
*/
|
||||
protected function getBackendUser()
|
||||
{
|
||||
return $GLOBALS['BE_USER'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the LanguageService
|
||||
*
|
||||
* @return LanguageService
|
||||
*/
|
||||
protected function getLanguageService()
|
||||
{
|
||||
return $GLOBALS['LANG'];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
namespace TYPO3\CMS\Backend\Tree\View;
|
||||
|
||||
/*
|
||||
* 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 TYPO3\CMS\Core\Utility\GeneralUtility;
|
||||
|
||||
/**
|
||||
* Generate a page-tree, non-browsable.
|
||||
*/
|
||||
class PageTreeView extends AbstractTreeView
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public $fieldArray = [
|
||||
'uid',
|
||||
'pid',
|
||||
'is_siteroot',
|
||||
'title',
|
||||
'doktype',
|
||||
'nav_title',
|
||||
'mount_pid',
|
||||
'php_tree_stop',
|
||||
't3ver_id',
|
||||
't3ver_state',
|
||||
'hidden',
|
||||
'starttime',
|
||||
'endtime',
|
||||
'fe_group',
|
||||
'module',
|
||||
'extendToSubpages',
|
||||
'nav_hide'
|
||||
];
|
||||
|
||||
/**
|
||||
* override to use this treeName
|
||||
* @var string
|
||||
*/
|
||||
public $treeName = 'pages';
|
||||
|
||||
/**
|
||||
* override to use this table
|
||||
* @var string
|
||||
*/
|
||||
public $table = 'pages';
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $ext_showNavTitle = false;
|
||||
|
||||
/**
|
||||
* Init function
|
||||
* REMEMBER to feed a $clause which will filter out non-readable pages!
|
||||
*
|
||||
* @param string $clause Part of where query which will filter out non-readable pages.
|
||||
* @param string $orderByFields Record ORDER BY field
|
||||
*/
|
||||
public function init($clause = '', $orderByFields = '')
|
||||
{
|
||||
parent::init(' AND deleted=0 AND sys_language_uid=0 ' . $clause, 'sorting');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns TRUE/FALSE if the next level for $id should be expanded - and all levels should, so we always return 1.
|
||||
*
|
||||
* @param int $id ID (uid) to test for (see extending classes where this is checked against session data)
|
||||
* @return bool
|
||||
*/
|
||||
public function expandNext($id)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the plus/minus icon for the browsable tree.
|
||||
* In this case, there is no plus-minus icon displayed.
|
||||
*
|
||||
* @param array $row Record for the entry
|
||||
* @param int $a The current entry number
|
||||
* @param int $c The total number of entries. If equal to $a, a 'bottom' element is returned.
|
||||
* @param int $nextCount The number of sub-elements to the current element.
|
||||
* @param bool $isExpand The element was expanded to render subelements if this flag is set.
|
||||
* @return string Image tag with the plus/minus icon.
|
||||
* @internal
|
||||
* @see AbstractTreeView::PMicon()
|
||||
*/
|
||||
public function PMicon($row, $a, $c, $nextCount, $isExpand)
|
||||
{
|
||||
return '<span class="treeline-icon treeline-icon-join' . ($a == $c ? 'bottom' : '') . '"></span>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get stored tree structure AND updating it if needed according to incoming PM GET var.
|
||||
* - Here we just set it to nothing since we want to just render the tree, nothing more.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function initializePositionSaving()
|
||||
{
|
||||
$this->stored = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title for the input record. If blank, a "no title" label (localized) will be returned.
|
||||
* Do NOT htmlspecialchar the string from this function - has already been done.
|
||||
*
|
||||
* @param array $row The input row array (where the key "title" is used for the title)
|
||||
* @param int $titleLen Title length (30)
|
||||
* @return string The title.
|
||||
*/
|
||||
public function getTitleStr($row, $titleLen = 30)
|
||||
{
|
||||
$lang = $this->getLanguageService();
|
||||
if ($this->ext_showNavTitle && isset($row['nav_title']) && trim($row['nav_title']) !== '') {
|
||||
$title = '<span title="' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:title')) . ' '
|
||||
. htmlspecialchars(trim($row['title'])) . '">'
|
||||
. htmlspecialchars(GeneralUtility::fixed_lgd_cs($row['nav_title'], $titleLen))
|
||||
. '</span>';
|
||||
} else {
|
||||
$title = htmlspecialchars(GeneralUtility::fixed_lgd_cs($row['title'], $titleLen));
|
||||
if (isset($row['nav_title']) && trim($row['nav_title']) !== '') {
|
||||
$title = '<span title="'
|
||||
. htmlspecialchars($lang->sL('LLL:EXT:frontend/Resources/Private/Language/locallang_tca.xlf:pages.nav_title'))
|
||||
. ' ' . htmlspecialchars(trim($row['nav_title'])) . '">' . $title
|
||||
. '</span>';
|
||||
}
|
||||
$title = trim($row['title']) === ''
|
||||
? '<em>[' . htmlspecialchars($lang->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:labels.no_title')) . ']</em>'
|
||||
: $title;
|
||||
}
|
||||
return $title;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user