This commit is contained in:
Philipp Dieter 2021-07-14 12:41:47 +02:00
commit 5a9b59e4ec
12 changed files with 405 additions and 35 deletions

View File

@ -21,6 +21,7 @@ use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Mvc\Controller\ActionController as BaseController; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController as BaseController;
use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder; use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder;
use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper; use TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper;
use TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager;
use TYPO3\CMS\Extbase\Property\PropertyMapper; use TYPO3\CMS\Extbase\Property\PropertyMapper;
use TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationBuilder; use TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationBuilder;
use TYPO3\CMS\Extbase\Service\ExtensionService; use TYPO3\CMS\Extbase\Service\ExtensionService;
@ -99,6 +100,11 @@ class ActionController extends BaseController
*/ */
protected $redirect = null; protected $redirect = null;
/**
* if to reload
*/
protected $reload = null;
/** /**
* errors * errors
*/ */
@ -244,6 +250,16 @@ class ActionController extends BaseController
); );
} }
/**
* returns an instance of uribuilder
*/
public function persistAll()
{
($this->objectManager->get(
PersistenceManager::class
))->persistAll();
}
/** /**
* shortcut * shortcut
* *
@ -406,6 +422,19 @@ class ActionController extends BaseController
return null; return null;
} }
/**
*
*/
protected function getDomainModelString($object)
{
$extensionName = $this->request->getControllerExtensionName();
$reflection = new \ReflectionClass($object);
return 'tx_' .
strtolower($this->request->getControllerExtensionName()) .
'_domain_model_' .
strtolower($reflection->getShortName());
}
/** /**
* gets error label based on field and keyword, uses predefined extensionkey * gets error label based on field and keyword, uses predefined extensionkey
*/ */
@ -424,6 +453,7 @@ class ActionController extends BaseController
protected function addValidationError( protected function addValidationError(
$field, $keyword, $overwrite = false $field, $keyword, $overwrite = false
) { ) {
$this->isValid = false;
$this->responseStatus = [400 => 'validationError']; $this->responseStatus = [400 => 'validationError'];
if (!array_key_exists($field, $this->errors) if (!array_key_exists($field, $this->errors)
|| $overwrite == true || $overwrite == true
@ -554,6 +584,7 @@ class ActionController extends BaseController
->getUriBuilder() ->getUriBuilder()
->reset() ->reset()
->setCreateAbsoluteUri(true) ->setCreateAbsoluteUri(true)
->setAddQueryString(true)
->setTargetPageType($this->ajaxPageType) ->setTargetPageType($this->ajaxPageType)
->setArguments(['cid' => $this->contentObjectUid]) ->setArguments(['cid' => $this->contentObjectUid])
->uriFor($this->request->getControllerActionName()); ->uriFor($this->request->getControllerActionName());
@ -667,9 +698,12 @@ class ActionController extends BaseController
* @param array $result * @param array $result
* @return void * @return void
*/ */
protected function returnFunction($result = [], $errorStatus = null) protected function returnFunction(
{ $result = [],
$this->setAjaxEnv(); $errorStatus = null,
$object = 'data'
) {
$this->setAjaxEnv($object);
if ($result == null) { if ($result == null) {
$result = []; $result = [];
} }
@ -706,6 +740,9 @@ class ActionController extends BaseController
if ($this->redirect) { if ($this->redirect) {
$result['redirect'] = $this->redirect; $result['redirect'] = $this->redirect;
} }
if ($this->reload) {
$result['reload'] = true;
}
return json_encode($result); return json_encode($result);
} }
$result = array_merge( $result = array_merge(

View File

@ -93,15 +93,18 @@ class ApiUtility
) { ) {
$rowResult[$attributeName] = $methodResult; $rowResult[$attributeName] = $methodResult;
} }
if (array_key_exists($rowClass, $mapping) }
&& array_key_exists($attributeName, $mapping[$rowClass]) // ---
) { if (array_key_exists($rowClass, $mapping)) {
$mappingFunction = $mapping[$rowClass][$attributeName]; foreach ($mapping[$rowClass] as $attributeName => $function) {
$rowResult[$attributeName] = $mappingFunction( $rowResult[$attributeName] = $function(
$methodResult, $rowResult[$attributeName],
$row $row
); );
} }
}
// ---
foreach ($propertieResults as $attributeName => $methodResult) {
if (gettype($methodResult) == 'object' if (gettype($methodResult) == 'object'
&& get_class($methodResult) == 'DateTime' && get_class($methodResult) == 'DateTime'
) { ) {

View File

@ -26,6 +26,49 @@ use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
*/ */
class MailUtility class MailUtility
{ {
/**
* Parse text with a simple "template system" to be used as data for
* sendMail function
*
* @param string $text
* @param array $markers
* @return array
*/
public static function parseContentTemplate(
$text,
$markers = []
) {
$textParts = explode("\r\n\r\n", $text);
$result = [];
foreach ($textParts as $textPart) {
$type = 'text';
if (substr($textPart, 0, 2) === '# ') {
$type = 'headline';
$textPart = substr($textPart, 2);
}
if (substr($textPart, 0, 3) === '## ') {
$type = 'headline2';
$textPart = substr($textPart, 3);
}
if (substr($textPart, 0, 4) === '### ') {
$type = 'headline3';
$textPart = substr($textPart, 4);
}
foreach ($markers as $markerName => $markerContent) {
$textPart = str_replace(
'###' . $markerName . '###',
$markerContent,
$textPart
);
}
$result[] = [
'type' => $type,
'data' => $textPart,
];
}
return $result;
}
/** /**
* tages maildata, builds html and text mails an decides where to send them * tages maildata, builds html and text mails an decides where to send them
* allows to intercept sender for testing * allows to intercept sender for testing
@ -89,11 +132,12 @@ class MailUtility
case 'text': case 'text':
case 'textbold': case 'textbold':
case 'headline': case 'headline':
case 'headline2':
case 'headline3':
$htmlRow = $row; $htmlRow = $row;
$htmlRow['data'] = preg_replace_callback( $htmlRow['data'] = preg_replace_callback(
'/\[.*\]/mU', '/\[.*\]/mU',
function($matches) { function($matches) {
foreach ($matches as $match) { foreach ($matches as $match) {
return preg_replace_callback( return preg_replace_callback(
'/\[(\S*)\s(.*)\]/mU', '/\[(\S*)\s(.*)\]/mU',
@ -196,6 +240,13 @@ class MailUtility
$htmlView->assign('domain', $domain); $htmlView->assign('domain', $domain);
$textBody = $textView->render(); $textBody = $textView->render();
$htmlBody = $htmlView->render(); $htmlBody = $htmlView->render();
if ($domain) {
$htmlBody = str_replace(
'src="/assets',
'src="' . $domain . '/assets',
$htmlBody
);
}
$mail->setBody($textBody); $mail->setBody($textBody);
$mail->addPart($htmlBody, 'text/html'); $mail->addPart($htmlBody, 'text/html');
$recipients = explode( $recipients = explode(

View File

@ -0,0 +1,87 @@
<?php
namespace Cjel\TemplatesAide\Utility;
/***
*
* This file is part of the "Templates Aide" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* (c) 2021 Philipp Dieter <philipp.dieter@attic-media.net>
*
***/
use Cjel\TemplatesAide\Utility\ObjectUtility;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
/**
*
*/
class ObjectUtility
{
/**
* fills object from array
*
* @return void
*/
public static function fromArray(
&$object, $data, $storageMapping = []
) {
$objectManager = GeneralUtility::makeInstance(
ObjectManager::class
);
$reflectionClass = new \ReflectionClass(get_class($object));
foreach ($data as $property => $value) {
$methodName = 'set' . ucfirst($property);
if (!$reflectionClass->hasMethod($methodName)) {
continue;
}
$method = $reflectionClass->getMethod($methodName);
$params = $method->getParameters();
$methodType = $params[0]->getType();
if (is_array($value)) {
if (array_key_exists($property, $storageMapping)) {
$storage = $object->_getProperty($property);
$storageUpdated = $objectManager->get(
ObjectStorage::class
);
foreach ($value as $row) {
$item = null;
if ($row['uid']) {
foreach ($storage as $storageIitem) {
if ($storageIitem->getUid() == $row['uid']) {
$item = $storageIitem;
}
}
$storageUpdated->attach($item);
}
if (!$item) {
$item = new $storageMapping[$property]();
$storageUpdated->attach($item);
}
self::fromArray($item, $row);
}
$object->_setProperty($property, $storageUpdated);
}
} else {
if ($methodType == null) {
$value = StringUtility::checkAndfixUtf8($value);
$object->_setProperty($property, $value);
} else {
$typeParts = explode('\\', (string)$methodType);
$typeParts[count($typeParts) - 2] = 'Repository';
$repositoryClass = join('\\', $typeParts);
$repositoryClass .= 'Repository';
if (class_exists($repositoryClass)) {
$repository = $objectManager->get($repositoryClass);
$relatedObject = $repository->findByUid($value);
$object->_setProperty($property, $relatedObject);
}
}
}
}
}
}

View File

@ -0,0 +1,50 @@
<?php
namespace Cjel\TemplatesAide\Utility;
/***
*
* This file is part of the "" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* (c) 2021 Philipp Dieter
*
***/
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Object\ObjectManager;
/**
* Utility to work with site config
*/
class SiteConfigUtility
{
/**
* Gets site config by typoscript path
*
* @var string $path
* @return string
*/
public static function getByPath($path)
{
$pathParts = explode('.', $path);
$objectManager = GeneralUtility::makeInstance(
ObjectManager::class
);
$configurationManager = $objectManager->get(
ConfigurationManagerInterface::class
);
$typoscript = $configurationManager->getConfiguration(
ConfigurationManagerInterface::CONFIGURATION_TYPE_FULL_TYPOSCRIPT
);
$typoscript = GeneralUtility::removeDotsFromTS($typoscript);
$siteConfig = $typoscript['config']['site'];
$current = &$siteConfig;
foreach ($pathParts as $key) {
$current = &$current[$key];
}
return $current;
}
}

View File

@ -64,4 +64,11 @@ class StringUtility
} }
return implode('', $pieces); return implode('', $pieces);
} }
public static function checkAndfixUtf8($string){
if (!mb_detect_encoding($string, 'UTF-8', true)) {
$string = mb_convert_encoding($string , 'UTF-8', 'ASCII');
}
return $string;
}
} }

View File

@ -0,0 +1,70 @@
<?php
namespace Cjel\TemplatesAide\Utility;
/***
*
* This file is part of the "Templates Aide" Extension for TYPO3 CMS.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* (c) 2021 Philipp Dieter <philipp.dieter@attic-media.net>
*
***/
use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
/**
*
*/
class TcaUtility
{
/**
* fills object from array
*
* @return void
*/
public static function configureSelect(
&$tca, $column, $options, $extensionKey = null
) {
foreach ($options as &$option) {
$translation = self::getTranslation(
'option.' . $option[0], $extensionKey
);
if ($translation) {
$option[0] = $translation;
}
}
$tca['columns'][$column]['config']['type'] = 'select';
$tca['columns'][$column]['config']['renderType'] = 'selectSingle';
$tca['columns'][$column]['config']['size'] = 6;
$tca['columns'][$column]['config']['appearance'] = [];
$tca['columns'][$column]['config']['items'] = $options;
}
/**
* shortcut to get translation
*
* @return void
*/
public static function getTranslation($key, $extensionKey)
{
if ($extensionKey) {
$translation = LocalizationUtility::translate(
$key,
$extensionKey
);
if ($translation) {
return $translation;
}
}
$translation = LocalizationUtility::translate(
$key,
'site_templates'
);
if ($translation) {
return $translation;
}
return null;
}
}

View File

@ -4,8 +4,9 @@ namespace Cjel\TemplatesAide\ViewHelpers;
use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Connection; use TYPO3\CMS\Core\Database\Connection;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
class DbTableViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper { class DbTableViewHelper extends \TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper {
/** /**
* As this ViewHelper renders HTML, the output must not be escaped. * As this ViewHelper renders HTML, the output must not be escaped.
@ -15,16 +16,34 @@ class DbTableViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHel
protected $escapeOutput = false; protected $escapeOutput = false;
/** /**
* @param string $table The filename * Initialize arguments
* @param string $fields The filename */
* @param string $where public function initializeArguments() {
* @param string $orderBy parent::initializeArguments();
$this->registerArgument('table', 'string', '', true);
$this->registerArgument('fields', 'array', '', true, ['*']);
$this->registerArgument('where', 'array', '', false);
$this->registerArgument('orderBy', 'array', '', false);
}
/**
* @param string $table
* @param array $fields
* @param array $where
* @param array $orderBy
* @return string HTML Content * @return string HTML Content
*/ */
public function render($table, $fields = ['*'], $where = [], $orderBy = []){ public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
) {
$table = $arguments['table'];
$fields = $arguments['fields'];
$where = $arguments['where'];
$orderBy = $arguments['orderBy'];
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table)->createQueryBuilder();
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('pages')->createQueryBuilder();
$whereExpressions = []; $whereExpressions = [];
if (!empty($where)) { if (!empty($where)) {

View File

@ -0,0 +1,30 @@
<?php
namespace Cjel\TemplatesAide\ViewHelpers;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
class DotsToBracketsViewHelper extends AbstractViewHelper
{
public static function renderStatic(
array $arguments,
\Closure $renderChildrenClosure,
RenderingContextInterface $renderingContext
) {
$parts = explode('.', $renderChildrenClosure());
$_ = '';
foreach ($parts as $part) {
$_ .= '[';
if (substr($part, 0, 1) === '#') {
$_ .= substr($part, 1);
} else {
$_ .= '\'';
$_ .= $part;
$_ .= '\'';
}
$_ .= ']';
}
return $_;
}
}

View File

@ -3,7 +3,7 @@
<f:then> <f:then>
</f:then> </f:then>
<f:else> <f:else>
<f:if condition="{row.type} == 'headline' || {row.type} == 'text'"> <f:if condition="{v:condition.string.contains(haystack: '{row.type}', needle: 'headline', then: '1')} || {row.type} == 'text'">
<!--[if mso | IE]> <!--[if mso | IE]>
<table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:640px;" width="640" > <table align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:640px;" width="640" >
<tr> <tr>
@ -13,7 +13,7 @@
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#ffffff;background-color:#ffffff;width:100%;"> <table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#ffffff;background-color:#ffffff;width:100%;">
<tbody> <tbody>
<tr> <tr>
<td style="direction:ltr;font-size:0px;padding:20px;text-align:center;"> <td style="direction:ltr;font-size:0px;padding:0 20px;text-align:center;">
<!--[if mso | IE]> <!--[if mso | IE]>
<table role="presentation" border="0" cellpadding="0" cellspacing="0"> <table role="presentation" border="0" cellpadding="0" cellspacing="0">
<tr> <tr>
@ -27,15 +27,21 @@
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style width="100%"> <table border="0" cellpadding="0" cellspacing="0" role="presentation" style width="100%">
<tr> <tr>
<td align="left" style="font-size:0px;padding:0;word-break:break-word;"> <td align="left" style="font-size:0px;padding:0;word-break:break-word;">
<div style="font-family:Arial, sans-serif;font-size:16px;line-height:1;text-align:left;color:#000000;"> <div style="font-family:Arial, sans-serif;font-size:16px;line-height:1.4;text-align:left;color:#000000;">
<f:if condition="{row.type} == 'headline'"> <f:switch expression="{row.type}">
<f:then> <f:case value="headline">
<h1>{row.data -> f:format.nl2br() -> f:format.raw()}</h1>
</f:case>
<f:case value="headline2">
<h2>{row.data -> f:format.nl2br() -> f:format.raw()}</h2> <h2>{row.data -> f:format.nl2br() -> f:format.raw()}</h2>
</f:then> </f:case>
<f:else> <f:case value="headline3">
<h3>{row.data -> f:format.nl2br() -> f:format.raw()}</h3>
</f:case>
<f:defaultCase>
<p>{row.data -> f:format.nl2br() -> f:format.raw()}</p> <p>{row.data -> f:format.nl2br() -> f:format.raw()}</p>
</f:else> </f:defaultCase>
</f:if> </f:switch>
</div> </div>
</td> </td>
</tr> </tr>

View File

@ -8,10 +8,12 @@
><v:format.trim> ><v:format.trim>
<v:condition.type.isArray value="{row.data}"> <v:condition.type.isArray value="{row.data}">
<f:then>{row.data.0}: {row.data.1}</f:then> <f:then>{row.data.0}: {row.data.1}</f:then>
<f:else><f:if condition="{row.type} == 'headline' || {row.type} == 'text'"><f:if condition="{row.type} == 'headline'"> <f:else><f:if condition="{v:condition.string.contains(haystack: '{row.type}', needle: 'headline', then: '1')} || {row.type} == 'text'"><f:switch expression="{row.type}">
<f:then>= {row.data} =</f:then> <f:case value="headline">= {row.data} =</f:case>
<f:else>{row.data}</f:else> <f:case value="headline2">== {row.data} ==</f:case>
</f:if></f:if></f:else> <f:case value="headline3">=== {row.data} ===</f:case>
<f:defaultCase>{row.data}</f:defaultCase>
</f:switch></f:if></f:else>
</v:condition.type.isArray> </v:condition.type.isArray>
</v:format.trim><f:if condition="{content.{rowI.cycle}.data}">{br}<f:if condition="{v:condition.type.isArray(value='{content.{rowI.index}.data}', then: '1')} && {v:condition.type.isArray(value='{content.{rowI.cycle}.data}', then: '1')}"><f:else>{br}</f:else></f:if></f:if><f:if condition="{row.type} == 'buttons'"> </v:format.trim><f:if condition="{content.{rowI.cycle}.data}">{br}<f:if condition="{v:condition.type.isArray(value='{content.{rowI.index}.data}', then: '1')} && {v:condition.type.isArray(value='{content.{rowI.cycle}.data}', then: '1')}"><f:else>{br}</f:else></f:if></f:if><f:if condition="{row.type} == 'buttons'">
<f:then>{br}{br}{row.targets.0.1}:{br}{row.targets.0.0}{br}{br}{row.targets.1.1}:{br}{row.targets.1.0}</f:then> <f:then>{br}{br}{row.targets.0.1}:{br}{row.targets.0.0}{br}{br}{row.targets.1.1}:{br}{row.targets.1.0}</f:then>

View File

@ -2,3 +2,11 @@
border: 1px solid #eee; border: 1px solid #eee;
height: auto !important; height: auto !important;
} }
textarea.formengine-textarea {
max-height: unset;
width: 100%;
resize: vertical;
}
.form-control-wrap {
max-width: unset !important;
}