Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions Classes/Controller/TemplatesAjaxController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php
declare(strict_types=1);

namespace Psychomieze\AdminpanelExtended\Controller;

/*
* This file is part of the TYPO3 Adminpanel Initiative.
*
* For the full copyright and license information, please read the
* LICENSE file that was distributed with this source code.
*/

use Psr\Http\Message\ServerRequestInterface;
use Psr\Log\LoggerAwareInterface;
use Psr\Log\LoggerAwareTrait;
use Psychomieze\AdminpanelExtended\Modules\Fluid\Templates;
use Psychomieze\AdminpanelExtended\Service\ModuleDataService;
use TYPO3\CMS\Adminpanel\ModuleApi\ModuleData;
use TYPO3\CMS\Core\Http\JsonResponse;
use TYPO3\CMS\Core\Utility\GeneralUtility;

class TemplatesAjaxController implements LoggerAwareInterface
{
use LoggerAwareTrait;

public function getData(ServerRequestInterface $request): JsonResponse

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Function getData has a Cognitive Complexity of 7 (exceeds 5 allowed). Consider refactoring.

{
$queryParams = $request->getQueryParams();
$templateId = (string)($queryParams['templateId'] ?? '');
$requestId = (string)($queryParams['requestId'] ?? '');
$this->validateParameters($templateId, $requestId);
$moduleData = GeneralUtility::makeInstance(ModuleDataService::class)
->getModuleDataByRequestId(Templates::class, $requestId);
$templateData = [];
$statusCode = 404;

if ($moduleData instanceof ModuleData) {
$templateRecord = $moduleData['templates'][$templateId] ?? null;
if (is_array($templateRecord)) {
$absTemplatePath = GeneralUtility::getFileAbsFileName($templateRecord['path']);
if (GeneralUtility::isAllowedAbsPath($absTemplatePath) && file_exists($absTemplatePath)) {
$content = file_get_contents($absTemplatePath);
$statusCode = 200;
$templateData['templateId'] = $templateId;
$templateData['template'] = $content;
}
}
}

return new JsonResponse($templateData, $statusCode);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Returning type \TYPO3\CMS\Core\Http\JsonResponse but getData() is declared to return \Psr\Http\Message\ResponseInterface

}

private function validateParameters(string $templateId, string $requestId): void
{
if (!$templateId || !$requestId) {
throw new \InvalidArgumentException(
'Missing parameters, templateId and requestId need to be set.',
1561386190
);
}
}
}
97 changes: 97 additions & 0 deletions Classes/Modules/Fluid/TemplatePaths.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php
declare(strict_types=1);

namespace Psychomieze\AdminpanelExtended\Modules\Fluid;

/*
* This file is part of the TYPO3 Adminpanel Initiative.
*
* For the full copyright and license information, please read the
* LICENSE file that was distributed with this source code.
*/

use TYPO3\CMS\Core\Core\Environment;
use TYPO3\CMS\Core\Log\LogLevel;
use TYPO3\CMS\Core\Log\LogManager;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Utility\StringUtility;

class TemplatePaths extends \TYPO3\CMS\Fluid\View\TemplatePaths
{
/**
* @var \TYPO3\CMS\Core\Log\Logger
*/
private $logger;

/**
* @param array|string|NULL $packageNameOrArray
*/
public function __construct($packageNameOrArray = null)
{
parent::__construct($packageNameOrArray);

$this->logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__);
}

/**
* Attempts to resolve an absolute filename
* of a template (i.e. `templateRootPaths`)
* using a controller name, action and format.
*
* Works _backwards_ through template paths in
* order to achieve an "overlay"-type behavior
* where the last paths added are the first to
* be checked and the first path added acts as
* fallback if no other paths have the file.
*
* If the file does not exist in any path,
* including fallback path, `NULL` is returned.
*
* Path configurations filled from TypoScript
* is automatically recorded in the right
* order (see `fillFromTypoScriptArray`), but
* when manually setting the paths that should
* be checked, you as user must be aware of
* this reverse behavior (which you should
* already be, given that it is the same way
* TypoScript path configurations work).
*
* @param string $controller
* @param string $action
* @param string $format
* @return string|NULL
* @api
*/
public function resolveTemplateFileForControllerAndActionAndFormat($controller, $action, $format = null): ?string
{
$templateName = parent::resolveTemplateFileForControllerAndActionAndFormat(
$controller,
$action,
$format
);

if (is_string($templateName)) {
if (StringUtility::beginsWith($templateName, Environment::getExtensionsPath())) {
$path = str_replace(Environment::getExtensionsPath().DIRECTORY_SEPARATOR, 'EXT:', $templateName);
} elseif (StringUtility::beginsWith($templateName, Environment::getFrameworkBasePath())) {
$path = str_replace(Environment::getFrameworkBasePath().DIRECTORY_SEPARATOR, 'EXT:', $templateName);
}

$format = $format ?? $this->getFormat();
$identifier = uniqid("template-{$controller}-{$action}-{$format}-", false);

$this->logger->log(
LogLevel::DEBUG,
$identifier,
[
'path' => $path ?? $templateName,
'controller' => $controller,
'action' => $action,
'format' => $format,
]
);
}

return $templateName;
}
}
135 changes: 135 additions & 0 deletions Classes/Modules/Fluid/Templates.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
<?php
declare(strict_types=1);

namespace Psychomieze\AdminpanelExtended\Modules\Fluid;

/*
* This file is part of the TYPO3 Adminpanel Initiative.
*
* For the full copyright and license information, please read the
* LICENSE file that was distributed with this source code.
*/

use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Adminpanel\Log\InMemoryLogWriter;
use TYPO3\CMS\Adminpanel\ModuleApi\AbstractSubModule;
use TYPO3\CMS\Adminpanel\ModuleApi\ContentProviderInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\DataProviderInterface;
use TYPO3\CMS\Adminpanel\ModuleApi\ModuleData;
use TYPO3\CMS\Adminpanel\ModuleApi\ResourceProviderInterface;
use TYPO3\CMS\Backend\Routing\UriBuilder;
use TYPO3\CMS\Core\Log\LogRecord;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;

/**
* Fluid templates submodule
*/
class Templates extends AbstractSubModule implements ContentProviderInterface, DataProviderInterface, ResourceProviderInterface
{
/**
* Identifier for this module,
* for example "preview" or "cache"
*
* @return string
*/
public function getIdentifier(): string
{
return 'psychomieze_fluid_templates';
}

/**
* Module label
*
* @return string
*/
public function getLabel(): string
{
return $this->getLanguageService()->sL(
'LLL:EXT:adminpanel_extended/Resources/Private/Language/locallang_fluid.xlf:submodule.templates.label'
);
}

/**
* Main method for content generation of an admin panel module.
* Return content as HTML. For modules implementing the DataProviderInterface
* the "ModuleData" object is automatically filled with the stored data - if
* no data is given a "fresh" ModuleData object is injected.
*
* @param \TYPO3\CMS\Adminpanel\ModuleApi\ModuleData $data
* @return string
*/
public function getContent(ModuleData $data): string
{
$view = GeneralUtility::makeInstance(StandaloneView::class);

$view->setTemplatePathAndFilename('EXT:adminpanel_extended/Resources/Private/Templates/Fluid/Templates.html');
$view->assignMultiple($data->getArrayCopy());
$url = GeneralUtility::makeInstance(UriBuilder::class)->buildUriFromRoute(
'ajax_adminPanelExtended_templateData',
['requestId' => $data['requestId']]
);
$view->assign('templateDataUri', $url);

return $view->render();
}

/**
* @param \Psr\Http\Message\ServerRequestInterface $request
* @return \TYPO3\CMS\Adminpanel\ModuleApi\ModuleData
*/
public function getDataToStore(ServerRequestInterface $request): ModuleData
{
$log = InMemoryLogWriter::$log;
/** @var LogRecord[] $templateRecords */
$templateRecords = array_filter(
$log,
static function (LogRecord $entry) {
return $entry->getComponent() === 'Psychomieze.AdminpanelExtended.Modules.Fluid.TemplatePaths';
}
);

$templates = [];

foreach ($templateRecords as $logRecord) {
$templates[$logRecord->getMessage()] = $logRecord->getData();
}

$templates = array_unique($templates, SORT_REGULAR);

return new ModuleData([
'templates' => $templates,
'requestId' => $request->getAttribute('adminPanelRequestId')
]);
}

/**
* Returns a string array with javascript files that will be rendered after the module
*
* Example: return ['EXT:adminpanel/Resources/Public/JavaScript/Modules/Edit.js'];
*
* @return array
*/
public function getJavaScriptFiles(): array
{
return [
'EXT:adminpanel_extended/Resources/Public/JavaScript/Templates.js',
'EXT:adminpanel_extended/Resources/Public/JavaScript/vendor/prettify.js',
];
}

/**
* Returns a string array with css files that will be rendered after the module
*
* Example: return ['EXT:adminpanel/Resources/Public/JavaScript/Modules/Edit.css'];
*
* @return array
*/
public function getCssFiles(): array
{
return [
'EXT:adminpanel_extended/Resources/Public/Css/Templates.css',
'EXT:adminpanel_extended/Resources/Public/Css/vendor/desert.css'
];
}
}
4 changes: 4 additions & 0 deletions Configuration/Backend/AjaxRoutes.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@
'path' => '/adminpanelExtended/signals/data',
'target' => \Psychomieze\AdminpanelExtended\Controller\SignalsAjaxController::class . '::getData'
],
'adminPanelExtended_templateData' => [
'path' => '/adminpanelExtended/templates/data',
'target' => \Psychomieze\AdminpanelExtended\Controller\TemplatesAjaxController::class . '::getData'
],
];
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Currently in alpha phase!
- Show currently online users and info if someone is editing current page
- Show fluid global namespaces
- Show fluid preprocessors
- Show fluid templates that have been rendered with code highlighting

### Development

Expand All @@ -25,3 +26,7 @@ initiative, see <https://typo3.org/community/teams/typo3-development/initiatives
## Issues / Repository

Report issues and find the code at <https://github.com/TYPO3-Initiatives/adminpanel-extended>

## Misc

This project is highly inspired by the symfony profiler: <https://symfony.com/doc/current/profiler.html>
3 changes: 3 additions & 0 deletions Resources/Private/Language/locallang_fluid.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
<trans-unit id="submodule.general.priority">
<source>Priority</source>
</trans-unit>
<trans-unit id="submodule.templates.label">
<source>Templates</source>
</trans-unit>
</body>
</file>
</xliff>
29 changes: 29 additions & 0 deletions Resources/Private/Templates/Fluid/Templates.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<html xmlns:f="https://typo3.org/ns/TYPO3/CMS/Fluid/ViewHelpers" data-namespace-typo3-fluid="true">
<f:if condition="{templates}">
<f:then>
<table class="typo3-adminPanel-table typo3-adminPanel-table-debug">
<thead>
<tr>
<th scope="col" class="typo3-adminPanel-table-cell-key">Path</th>
<th scope="col">Controller</th>
<th scope="col">Action</th>
<th scope="col">Format</th>
</tr>
</thead>
<tbody>
<f:for each="{templates}" key="identifier" as="data">
<tr>
<th scope="row" class="typo3-adminPanel-table-cell-key">
<a href="#" data-typo3-role="template-link" data-typo3-ajax-url="{templateDataUri}&templateId={identifier}" data-typo3-template-id="{identifier}">{data.path}</a>
</th>
<td>{data.controller}</td>
<td>{data.action}</td>
<td>{data.format}</td>
</tr>
</f:for>
</tbody>
</table>
</f:then>
<f:else>No templates</f:else>
</f:if>
</html>
28 changes: 28 additions & 0 deletions Resources/Public/Css/Templates.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* This file is part of the TYPO3 Adminpanel Initiative.
*
* For the full copyright and license information, please read the
* LICENSE file that was distributed with this source code.
*/
#TSFE_ADMIN_PANEL_FORM.typo3-kidjls9dksoje.typo3-adminPanel pre.prettyprint {
background-color: #333;
}
#TSFE_ADMIN_PANEL_FORM.typo3-kidjls9dksoje.typo3-adminPanel pre.prettyprint *{
white-space: pre;
}
#TSFE_ADMIN_PANEL_FORM.typo3-kidjls9dksoje.typo3-adminPanel pre.prettyprint li.L0,
#TSFE_ADMIN_PANEL_FORM.typo3-kidjls9dksoje.typo3-adminPanel pre.prettyprint li.L1,
#TSFE_ADMIN_PANEL_FORM.typo3-kidjls9dksoje.typo3-adminPanel pre.prettyprint li.L2,
#TSFE_ADMIN_PANEL_FORM.typo3-kidjls9dksoje.typo3-adminPanel pre.prettyprint li.L3,
#TSFE_ADMIN_PANEL_FORM.typo3-kidjls9dksoje.typo3-adminPanel pre.prettyprint li.L5,
#TSFE_ADMIN_PANEL_FORM.typo3-kidjls9dksoje.typo3-adminPanel pre.prettyprint li.L6,
#TSFE_ADMIN_PANEL_FORM.typo3-kidjls9dksoje.typo3-adminPanel pre.prettyprint li.L7,
#TSFE_ADMIN_PANEL_FORM.typo3-kidjls9dksoje.typo3-adminPanel pre.prettyprint li.L8 {
list-style-type: decimal;
}
#TSFE_ADMIN_PANEL_FORM.typo3-kidjls9dksoje.typo3-adminPanel pre.prettyprint ol.linenums {
padding-left: 40px;
margin-top: 0;
margin-bottom: 0;
color: #AEAEAE;
}
Loading