[PHP] Show the editor responsible for a page tree in the TYPO3 backend

[PHP] Show the editor responsible for a page tree in the TYPO3 backend

In the college and university sector in particular, it is important to indicate an editorially responsible person in the TYPO3 backend. You can contact them if you have any questions. You can easily add this function via your website package.

ext_tables.sql:

CREATE TABLE pages ( responsible_name varchar(255) DEFAULT '' NOT NULL, responsible_email varchar(255) DEFAULT '' NOT NULL, );

ext_localconf.php:

<?php defined('TYPO3_MODE') || die(); call_user_func( function () { /** * Show responsible person in backend page module */ $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/db_layout.php']['drawHeaderHook'][] = \Unitue\Project\Hooks\PageModule::class . '->manipulate'; } );

PageModule.php:

<?php declare(strict_types=1); namespace Unitue\Project\Hooks; use TYPO3\CMS\Backend\Controller\PageLayoutController; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Mvc\Exception\InvalidExtensionNameException; use TYPO3\CMS\Fluid\View\StandaloneView; use Unitue\Project\Utility\DatabaseUtility; use Unitue\Project\Utility\ObjectUtility; /** * Class PageModule */ class PageModule { /** * @var string */ protected $templatePathAndFile = 'EXT:project/Resources/Private/Templates/Hooks/ResponsiblePageModule.html'; /** * @param array $params * @param PageLayoutController $pageLayoutController * @return string * @throws InvalidExtensionNameException */ public function manipulate(array $params, PageLayoutController $pageLayoutController): string { unset($params); $pageIdentifier = $pageLayoutController->id; $properties = $this->getResponsiblePropertiesToPage($pageIdentifier); $properties = $properties + $this->getLastChangedPropertiesToPage($pageIdentifier); $properties = $properties + $this->getUserPropertiesToIdentifier($properties['userid']); return $this->renderMarkup($properties); } /** * @param int $pageIdentifier * @return array */ protected function getResponsiblePropertiesToPage(int $pageIdentifier): array { $properties = $this->getPropertiesToPage($pageIdentifier, ['responsible_name', 'responsible_email']); if (empty($properties['responsible_name']) && empty($properties['responsible_email'])) { $parentPageIdentifier = $this->getPropertiesToPage($pageIdentifier, ['pid'])['pid']; if ($parentPageIdentifier > 0) { $properties = $this->getResponsiblePropertiesToPage($parentPageIdentifier); } } return $properties; } /** * @param int $pageIdentifier * @param array $properties * @return array */ protected function getPropertiesToPage(int $pageIdentifier, array $properties): array { $queryBuilder = DatabaseUtility::getQueryBuilderForTable('pages', true); $rows = $queryBuilder ->select(...$properties) ->from('pages') ->where('uid=' . (int)$pageIdentifier) ->setMaxResults(1) ->execute() ->fetchAll(); return $rows[0]; } /** * @param int $pageIdentifier * @return array */ protected function getLastChangedPropertiesToPage(int $pageIdentifier): array { $contentElements = $this->getContentElementsToPage($pageIdentifier); $queryBuilder = DatabaseUtility::getQueryBuilderForTable('sys_log'); $rows = $queryBuilder ->select('userid', 'tstamp') ->from('sys_log') ->where('tablename="tt_content" and recuid in (' . implode(',', $contentElements) . ')') ->orderBy('tstamp', 'desc') ->setMaxResults(1) ->execute() ->fetchAll(); $properties = [ 'userid' => 0, 'tstamp' => 0 ]; if (!empty($rows[0])) { $properties = $rows[0]; } return $properties; } /** * @param int $userIdentifier * @return array */ protected function getUserPropertiesToIdentifier(int $userIdentifier): array { $queryBuilder = DatabaseUtility::getQueryBuilderForTable('be_users'); $rows = $queryBuilder ->select('username', 'realName', 'email') ->from('be_users') ->where('uid=' . (int)$userIdentifier) ->execute() ->fetchAll(); $properties = [ 'username' => '', 'realName' => '', 'email' => '' ]; if (!empty($rows[0])) { $properties = $rows[0]; } return $properties; } /** * @param int $pageIdentifier * @return array */ protected function getContentElementsToPage(int $pageIdentifier): array { $queryBuilder = DatabaseUtility::getQueryBuilderForTable('tt_content', true); $rows = $queryBuilder ->select('uid') ->from('tt_content') ->where('pid=' . (int)$pageIdentifier . ' and deleted=0') ->execute() ->fetchAll(); $contentElements = [0]; foreach ($rows as $row) { $contentElements[] = $row['uid']; } return $contentElements; } /** * @param array $properties * @return string * @throws InvalidExtensionNameException */ protected function renderMarkup(array $properties): string { /** @var StandaloneView $standaloneView */ $standaloneView = ObjectUtility::getObjectManager()->get(StandaloneView::class); $standaloneView->getRequest()->setControllerExtensionName('in2template'); $standaloneView->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName($this->templatePathAndFile)); $standaloneView->assignMultiple($properties); return $standaloneView->render(); } }

ResponsiblePageModule.html:

{namespace p=Unitue\Project\ViewHelpers} <f:if condition="{responsible_name} || {responsible_email}"> <div class="callout callout-warning"> <div class="media"> <div class="media-left"> <span class="fa-stack fa-lg callout-icon"> <i class="fa fa-circle fa-stack-2x"></i> <i class="fa fa-info fa-stack-1x"></i> </span> </div> <div class="media-body"> <h4 class="callout-title"> <f:translate key="LLL:EXT:project/Resources/Private/Language/Backend/locallang.xlf:pageModuleResponsible.responsiblename">Responsible</f:translate> </h4> <div class="callout-body"> <f:render section="ResponsibleName" arguments="{_all}" /> <f:render section="LastChange" arguments="{_all}" /> </div> </div> </div> </div> </f:if> <f:section name="ResponsibleName"> <p> <strong> <f:link.typolink parameter="{responsible_email}"> <f:if condition="{responsible_name}"> <f:then>{responsible_name}</f:then> <f:else>{responsible_email}</f:else> </f:if> </f:link.typolink> </strong> </p> </f:section> <f:section name="LastChange"> <f:if condition="{userid} > 0"> <p> <f:translate key="LLL:EXT:project/Resources/Private/Language/Backend/locallang.xlf:pageModuleResponsible.lastchangename">Last change</f:translate> <f:render section="UserName" arguments="{_all}" /> <span title="{f:format.date(format:'%d.%m.%Y %H:%M', date:'@{tstamp}')}">(<p:format.readableDate>{tstamp}</p:format.readableDate>)</span> </p> </f:if> </f:section> <f:section name="UserName"> <f:link.typolink parameter="{email}"> <f:if condition="{realName}"> <f:then>{realName}</f:then> <f:else>{username}</f:else> </f:if> </f:link.typolink> </f:section>

TYPO3: Finding unused files in fileadmin

Do you want to delete unused or orphaned files in fileadmin or another storage location? Unfortunately, there's no direct core functionality for this. But a small command in your site package can...

Go to news

TYPO3: Editors with individual user_upload folders

Perhaps you're familiar with this client requirement? Editors should be able to add videos using the "Add media by URL" button. But the files shouldn't be located in fileadmin/user_upload/, but rather...

Go to news

TYPO3: Finding pages in mixed mode

In TYPO3, Mixed Mode refers to translated pages that contain content only partially related to the corresponding content in the main language. This is indicated in the backend by an error message. But...

Go to news

Extbase Extensions: Think extensibility with data, site and language

Today, I have a small request for the TYPO3 extension authors out there: Make sure your extensions are extensible. This will also promote the distribution of the corresponding plugins.

Go to news

SQL: Show all tables sorted by size in descending order

Lately I've been using the SQL command more often to find out which tables in the TYPO3 database are the largest. I've published the snippet once.

Go to news

TYPO3 12 with CKEditor 5: Styles in a single selection

If you set a link in the RTE in TYPO3, you may have to choose between different link classes, for example to create buttons in the frontend. What's new in TYPO3 12 is that you can select not just one...

Go to news