Skip to content

Commit

Permalink
[TASK] Open Source Release
Browse files Browse the repository at this point in the history
  • Loading branch information
ervaude committed Jun 29, 2019
0 parents commit 4039a4a
Show file tree
Hide file tree
Showing 15 changed files with 872 additions and 0 deletions.
80 changes: 80 additions & 0 deletions Classes/Authentication/PreviewUserAuthentication.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php
declare(strict_types = 1);
namespace B13\AuthorizedPreview\Authentication;

/*
* This file is part of TYPO3 CMS extension authorized_preview by b13.
*
* 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.
*/

use B13\AuthorizedPreview\Preview\PreviewUriBuilder;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
use TYPO3\CMS\Core\Type\Bitmask\Permission;

/**
* A custom BackendUserAuthentication that is allowed to view the current language
* Instantiated in \B13\AuthorizedPreview\Http\Middleware\Preview
*/
class PreviewUserAuthentication extends BackendUserAuthentication
{

/**
* @var SiteLanguage
*/
protected $siteLanguage = null;

/**
* PreviewUserAuthentication constructor.
*
* @param SiteLanguage|null $siteLanguage
*/
public function __construct(SiteLanguage $siteLanguage = null)
{
parent::__construct();
$this->name = PreviewUriBuilder::PARAMETER_NAME;
$this->siteLanguage = $siteLanguage ?? $GLOBALS['TYPO3_REQUEST']->getAttribute('language', null);
}

/**
* A preview user has read-only permissions, always.
*
* @param int $perms
* @return string
*/
public function getPagePermsClause($perms)
{
if ($perms === Permission::PAGE_SHOW) {
return '1=1';
}
return '0=1';
}

/**
* Has read permissions on the whole workspace, but nothing else
*
* @param array $row
* @return int
*/
public function calcPerms($row)
{
return Permission::PAGE_SHOW;
}

/**
* This user is always allowed to see the current language
*
* @param int $langValue
* @return bool
*/
public function checkLanguageAccess($langValue)
{
if ($this->siteLanguage === null) {
return false;
}
return (int)$langValue === $this->siteLanguage->getLanguageId();
}
}
105 changes: 105 additions & 0 deletions Classes/Controller/PreviewController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<?php
declare(strict_types=1);

namespace B13\AuthorizedPreview\Controller;

/*
* This file is part of TYPO3 CMS extension authorized_preview by b13.
*
* 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.
*/

use B13\AuthorizedPreview\Preview\SitePreview;
use B13\AuthorizedPreview\SiteWrapper;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use TYPO3\CMS\Backend\Template\ModuleTemplate;
use TYPO3\CMS\Core\Http\HtmlResponse;
use TYPO3\CMS\Core\Imaging\IconFactory;
use TYPO3\CMS\Core\Site\Entity\Site;
use TYPO3\CMS\Core\Site\SiteFinder;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Fluid\View\StandaloneView;

/**
* Class PreviewController
*/
class PreviewController
{
/**
* ModuleTemplate object
*
* @var ModuleTemplate
*/
protected $moduleTemplate;

/**
* @var StandaloneView
*/
protected $view;

/**
* @var ServerRequestInterface
*/
protected $request;

/**
* @var IconFactory
*/
protected $iconFactory;

/**
* Instantiate the form protection before a simulated user is initialized.
*/
public function __construct()
{
$this->moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
$this->iconFactory = GeneralUtility::makeInstance(IconFactory::class);
$this->initializeView('index');
}

/**
* @param string $templateName
*/
protected function initializeView(string $templateName)
{
$this->view = GeneralUtility::makeInstance(StandaloneView::class);
$this->view->setTemplate($templateName);
$this->view->setTemplateRootPaths(['EXT:authorized_preview/Resources/Private/Templates/Preview']);
}

/**
* @param ServerRequestInterface $request
* @return ResponseInterface the response with the content
*/
public function indexAction(ServerRequestInterface $request): ResponseInterface
{
$this->view->assign('sites', $this->getAllSites());

$sitePreview = SitePreview::createFromRequest($request);
if ($sitePreview->isValid()) {
$this->view->assign('sitePreview', $sitePreview);
}

$this->moduleTemplate->setContent($this->view->render());
return new HtmlResponse($this->moduleTemplate->renderContent());
}

/**
* @return Site[]
*/
protected function getAllSites(): array
{
$siteFinder = GeneralUtility::makeInstance(SiteFinder::class);
$sites = [];
foreach ($siteFinder->getAllSites() as $site) {
if (!($site instanceof Site)) {
continue;
}
$sites[] = GeneralUtility::makeInstance(SiteWrapper::class, $site);
}
return $sites;
}
}
153 changes: 153 additions & 0 deletions Classes/Http/Middleware/Preview.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
<?php
declare(strict_types = 1);
namespace B13\AuthorizedPreview\Http\Middleware;

/*
* This file is part of TYPO3 CMS extension authorized_preview by b13.
*
* 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.
*/

use B13\AuthorizedPreview\Authentication\PreviewUserAuthentication;
use B13\AuthorizedPreview\Preview\PreviewUriBuilder;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Context\Context;
use TYPO3\CMS\Core\Context\UserAspect;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Http\NormalizedParams;
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
use TYPO3\CMS\Core\Utility\GeneralUtility;

/**
* Middleware to detect "preview mode" so that a hidden language is shown in the frontend
*/
class Preview implements MiddlewareInterface
{
/**
*
* @param ServerRequestInterface $request
* @param RequestHandlerInterface $handler
* @return ResponseInterface
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$hash = $this->findHashInRequest($request);
if (empty($hash)) {
return $handler->handle($request);
}

$language = $request->getAttribute('language', null);
if ($language instanceof SiteLanguage && $language->isEnabled()) {
return $handler->handle($request);
}


$context = GeneralUtility::makeInstance(Context::class);
if (!$this->verifyHash($hash, $context, $language)) {
return $handler->handle($request);
}

// If the GET parameter PreviewUriBuilder::PARAMETER_NAME is set, then a cookie is set for the next request
if ($request->getQueryParams()[PreviewUriBuilder::PARAMETER_NAME] ?? false) {
$this->setCookie($hash, $request->getAttribute('normalizedParams'));
}
$previewUser = $this->initializePreviewUser();
if ($previewUser) {
$GLOBALS['BE_USER'] = $previewUser;
$this->setBackendUserAspect($context, $previewUser);
} else {
return $handler->handle($request);
}

return $handler->handle($request);
}

/**
* Looks for the PreviewUriBuilder::PARAMETER_NAME in the QueryParams and Cookies
*
* @param ServerRequestInterface $request
* @return string
*/
protected function findHashInRequest(ServerRequestInterface $request): string
{
return $request->getQueryParams()[PreviewUriBuilder::PARAMETER_NAME] ?? $request->getCookieParams()[PreviewUriBuilder::PARAMETER_NAME] ?? '';
}

/**
* Sets a cookie
*
* @param string $inputCode
* @param NormalizedParams $normalizedParams
*/
protected function setCookie(string $inputCode, NormalizedParams $normalizedParams)
{
setcookie(PreviewUriBuilder::PARAMETER_NAME, $inputCode, 0, $normalizedParams->getSitePath(), '', true, true);
}

/**
* Creates a preview user and sets the current page ID (for accessing the page)
*
* @return PreviewUserAuthentication
*/
protected function initializePreviewUser()
{
$previewUser = GeneralUtility::makeInstance(PreviewUserAuthentication::class);
$previewUser->setWebmounts([$GLOBALS['TSFE']->id]);
return $previewUser;
}

/**
* Register the backend user as aspect
*
* @param Context $context
* @param BackendUserAuthentication $user
*/
protected function setBackendUserAspect(Context $context, BackendUserAuthentication $user = null)
{
$context->setAspect('backend.user', GeneralUtility::makeInstance(UserAspect::class, $user));
}

/**
* Looks for the hash in the tx_authorized_preview
* Must not be expired yet.
*
* @param string $hash
* @param Context $context
* @param SiteLanguage $language
* @return bool
*/
protected function verifyHash(string $hash, Context $context, SiteLanguage $language): bool
{
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('tx_authorized_preview');
$row = $queryBuilder
->select('*')
->from('tx_authorized_preview')
->where(
$queryBuilder->expr()->eq(
'hash',
$queryBuilder->createNamedParameter($hash)
),
$queryBuilder->expr()->gt(
'endtime',
$queryBuilder->createNamedParameter($context->getPropertyFromAspect('date', 'timestamp'), \PDO::PARAM_INT)
)
)
->setMaxResults(1)
->execute()
->fetch();

if (empty($row)) {
return false;
}

$config = json_decode($row['config'], true);
return (int)$config['languageId'] === $language->getLanguageId();
}
}
Loading

0 comments on commit 4039a4a

Please sign in to comment.