I created (my first) extension with one viewhelper.
Oops, an error occurred!
Could not analyse class:My\Mlv\ViewHelpers\Format\ReplacenewlinesViewHelper maybe not loaded or no autoloader?
In use (with news):
{namespace m=My\Mlv\ViewHelpers}
{newsItem.bodytext -> m:format.replacenewlines()}
Directory tree of extension:
typo3conf/ext/mlv
ext_emconf.php (copied from another ext)
/Classes
/ViewHelpers
/Format
ReplaceNewLinesViewHelper.php
ReplaceNewLinesViewHelper.php:
<?php
namespace My\Mlv\ViewHelpers\Format;
/**
* Replaces newlines in plain text with <br> tags.
*
* #author johndoe33
* #package Mlv
* #subpackage ViewHelpers\Format
*/
class ReplaceNewLinesViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper {
/**
* Replaces newlines in plain text with <br> tags.
*
* #param string $content
* #return string
*/
public function render($content = NULL) {
if (NULL === $content) {
$content = $this->renderChildren();
}
$content = str_replace( "\n", '<br>', $content );
return $content;
}
}
You need to use camel case in the view helper invocation:
{newsItem.bodytext -> m:format.replaceNewLines()}
Furthermore you may need to define an autoload definition in your ext_emconf.php if you're using TYPO3 >=7.6 (reinstall the extension after doing so):
'autoload' => array(
'psr-4' => array('My\\Mlv\\' => 'Classes')
)
For more information see: http://insight.helhum.io/post/130876393595/how-to-configure-class-loading-for-extensions-in
Related
I need to add a custom validator to the datepicker field. By default, this field comes without any validators.
I've already made the validator settings visible in the TCA of tx_powermail_domain_model_field and added my custom validator as usual.
Now I need the attributes data-parsley-customXXX and data-parsley-error-message added to the HTML input field which is usually done via the the viewhelper in ValidationDataAttributeViewHelper.php:
https://github.com/einpraegsam/powermail/blob/develop/Classes/ViewHelpers/Validation/ValidationDataAttributeViewHelper.php#L342
https://github.com/einpraegsam/powermail/blob/develop/Classes/ViewHelpers/Validation/ValidationDataAttributeViewHelper.php#L348
This is the code I need to extend:
https://github.com/einpraegsam/powermail/blob/develop/Classes/ViewHelpers/Validation/DatepickerDataAttributeViewHelper.php#L32
I found a solution for my problem. As suggested in the comment it's possible to extend the Viewhelper:
ext_localconf.php:
$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects'][\In2code\Powermail\ViewHelpers\Validation\DatepickerDataAttributeViewHelper::class] = [
'className' => \Vendor\MyExt\Powermail\ViewHelpers\Validation\DatepickerDataAttributeViewHelper::class
];
myext/Classes/Powermail/ViewHelpers/Validation/DatepickerDataAttributeViewHelper.php
<?php
declare(strict_types=1);
namespace Vendor\MyExt\Powermail\ViewHelpers\Validation;
use In2code\Powermail\Domain\Model\Field;
use In2code\Powermail\Utility\LocalizationUtility;
class DatepickerDataAttributeViewHelper extends \In2code\Powermail\ViewHelpers\Validation\DatepickerDataAttributeViewHelper
{
/**
* Returns Data Attribute Array Datepicker settings (FE + BE)
*
* #return array for data attributes
*/
public function render(): array
{
/** #var Field $field */
$field = $this->arguments['field'];
$additionalAttributes = $this->arguments['additionalAttributes'];
$value = $this->arguments['value'];
$additionalAttributes['data-datepicker-force'] =
$this->settings['misc']['datepicker']['forceJavaScriptDatePicker'];
$additionalAttributes['data-datepicker-settings'] = $this->getDatepickerSettings($field);
$additionalAttributes['data-datepicker-months'] = $this->getMonthNames();
$additionalAttributes['data-datepicker-days'] = $this->getDayNames();
$additionalAttributes['data-datepicker-format'] = $this->getFormat($field);
if ($value) {
$additionalAttributes['data-date-value'] = $value;
}
if ($field->getValidation() && $this->isClientValidationEnabled()) {
$value = 1;
if ($field->getValidationConfiguration()) {
$value = $field->getValidationConfiguration();
}
$additionalAttributes['data-parsley-custom' . $field->getValidation()] = $value;
$additionalAttributes['data-parsley-error-message'] =
LocalizationUtility::translate('validationerror_validation.' . $field->getValidation());
}
$this->addMandatoryAttributes($additionalAttributes, $field);
return $additionalAttributes;
}
}
I want to add the subheader to the ctype 'textmedia' (TYPO3 9.5 & 10.4).
I followed this stackoverflow answer:
TYPO3 8 show layout selection in backend preview for textmedia
to register my Hook
typo3conf/ext/my-extension/Classes/Hooks/PageLayoutView/TextMediaCustomPreviewRenderer.php
Then I added the subheader
<?php
namespace aaa\bbb\Hooks\PageLayoutView;
use \TYPO3\CMS\Backend\View\PageLayoutViewDrawItemHookInterface;
use \TYPO3\CMS\Backend\View\PageLayoutView;
/**
* Contains a preview rendering for the page module of CType="textmedia"
*/
class TextMediaCustomPreviewRenderer implements PageLayoutViewDrawItemHookInterface
{
/**
* Preprocesses the preview rendering of a content element of type "textmedia"
*
* #param \TYPO3\CMS\Backend\View\PageLayoutView $parentObject Calling parent object
* #param bool $drawItem Whether to draw the item using the default functionality
* #param string $headerContent Header content
* #param string $subheaderContent Subheader content
* #param string $itemContent Item content
* #param array $row Record row of tt_content
*/
public function preProcess(
PageLayoutView &$parentObject,
&$drawItem,
&$headerContent,
&$subheaderContent,
&$itemContent,
array &$row
) {
if ($row['CType'] === 'textmedia') {
if ($row['bodytext']) {
$itemContent .= $parentObject->linkEditContent($parentObject->renderText($row['bodytext']), $row) . '<br />';
}
if ($row['assets']) {
$itemContent .= $parentObject->linkEditContent($parentObject->getThumbCodeUnlinked($row, 'tt_content', 'assets'), $row) . '<br />';
$fileReferences = BackendUtility::resolveFileReferences('tt_content', 'assets', $row);
if (!empty($fileReferences)) {
$linkedContent = '';
foreach ($fileReferences as $fileReference) {
$description = $fileReference->getDescription();
if ($description !== null && $description !== '') {
$linkedContent .= htmlspecialchars($description) . '<br />';
}
}
$itemContent .= $parentObject->linkEditContent($linkedContent, $row);
unset($linkedContent);
}
}
$drawItem = false;
}
}
}
I get the errror:
Fatal error: Declaration of aaaa\bbb\Hooks\PageLayoutView\TextMediaCustomPreviewRenderer::preProcess(TYPO3\CMS\Backend\View\PageLayoutView &$parentObject, &$drawItem, &$headerContent, &$subheaderContent, &$itemContent, array &$row) must be compatible with TYPO3\CMS\Backend\View\PageLayoutViewDrawItemHookInterface::preProcess(TYPO3\CMS\Backend\View\PageLayoutView &$parentObject, &$drawItem, &$headerContent, &$itemContent, array &$row) in /kunden/1111/rp-hosting/2222/333/typo3cms/projekt1/typo3conf/ext/my-sitepackage/Classes/Hooks/PageLayoutView/TextMediaCustomPreviewRenderer.php on line 23
What do I have to do to make it compatible with
TYPO3\CMS\Backend\View\PageLayoutViewDrawItemHookInterface::preProcess(TYPO3\CMS\Backend\View\PageLayoutView &$parentObject, &$drawItem, &$headerContent, &$itemContent, array &$row)
You got it a bit wrong. You do not add the subheader in your process() arguments. In general, the arguments must be the same as the class that they extend. You can add the subheader inside the if ($row['CType'] === 'textmedia') {} by adding the value in the itemContent
$itemContent .= $row['subheader'];
Personally i would avoid to do it this way. My preferred choice is to call the StandAlone View and assign a template for preview. Is easier to maintain and program it.
With TYPO3 9.5 I try to manage the URLs from my extension with a custom aspect. The url-generation works but there is a cHash appended. First, I don't want this cHash (it's unnecessary here) and second the cHash breaks the functionality of the link (I get a cHash-comparsion-error).
I read https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/9.5/Feature-86365-RoutingEnhancersAndAspects.html#impact another time about the dynamic parameters and cHash. To remove cHash I should add aspects for all placeholders. I have just one placeholder and an aspect for this, but cHash is present.
raw-url looks like this:
&tx_psoabilling_pi1[action]=showband&tx_psoabilling_pi1[controller]=Band&tx_psoabilling_pi1[band]=564&cHash=jkg24hwek8ufhqwezweklfzh
it's rendered to something like this:
2019-thisisaname?cHash=o28z3hkwejghweuhzlk
Here the part from config.yaml:
PsoabillingPlugin:
type: Extbase
extension: Psoabilling
plugin: Pi1
routes:
- routePath: '/{yearandbandname}'
_controller: 'Band::showband'
_arguments:
yearandbandname: band
defaultController: 'Band::listyear'
aspects:
yearandbandname:
type: BandAndYearMapper
BandAndYearMapper.php
<?php
namespace EnzephaloN\ThemePsoa\Routing\Aspect;
use TYPO3\CMS\Core\Routing\Aspect\PersistedMappableAspectInterface;
use TYPO3\CMS\Core\Site\SiteLanguageAwareTrait;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Database\ConnectionPool;
class BandAndYearMapper implements PersistedMappableAspectInterface{
use SiteLanguageAwareTrait;
/**
* #param string $value
* #return string|null
*/
public function generate(string $value): ?string{
if($uid=intval($value)){
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_psoabilling_domain_model_band');
$statement = $queryBuilder
->select('b.uid', 'b.name', 'y.year')
->from('tx_psoabilling_domain_model_band','b')
->leftJoin('b', 'tx_psoabilling_domain_model_year', 'y', 'b.year = y.uid')
->where(
$queryBuilder->expr()->eq('b.uid', $queryBuilder->createNamedParameter($uid))
)
->execute();
if($record = $statement->fetch()){
if(is_array($record)){
return $record['year']. "-" .str_replace(" ","-",trim(strtolower($record['name'])));
}
}
}
return null;
}
/**
* #param string $value
* #return string|null
*/
public function resolve(string $value): ?string{
$year = substr($value, 0, 4);
$name = str_replace("-", " ", substr($value, 5));
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_psoabilling_domain_model_band');
$statement = $queryBuilder
->select('b.uid')
->from('tx_psoabilling_domain_model_band','b')
->leftJoin('b', 'tx_psoabilling_domain_model_year', 'y', 'b.year = y.uid')
->where(
$queryBuilder->expr()->andX(
$queryBuilder->expr()->eq('y.year', $queryBuilder->createNamedParameter($year)),
$queryBuilder->expr()->orX(
$queryBuilder->expr()->like('b.name', $queryBuilder->createNamedParameter($name)),
$queryBuilder->expr()->like('b.name', $queryBuilder->createNamedParameter(substr($value, 5))), // if there was a - inside the bandname
$queryBuilder->expr()->like('b.name', $queryBuilder->createNamedParameter(substr($value, 5, strpos($name, " ")-1).'%')) // just find by beginning
)
)
)
->execute();
if($record = $statement->fetch()){
return (string)$record['uid'];
}
return null;
}
}
How can the cHash be removed??
Like Susi wrote, switching to StaticMappableAspectInterface was the solution. The class now starts with:
<?php
namespace EnzephaloN\ThemePsoa\Routing\Aspect;
use TYPO3\CMS\Core\Routing\Aspect\StaticMappableAspectInterface;
use TYPO3\CMS\Core\Site\SiteLanguageAwareTrait;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Core\Database\ConnectionPool;
class BandAndYearMapper implements StaticMappableAspectInterface{
Hello i am using Zend_currency
class Currency extends Zend_View_Helper_Abstract
{
public function currency($number, $locale = 'it_IT') {
$currency = new Zend_Currency($locale);
$number = $number + 0.00;//convert to float
return $currency->toCurrency((float) $number);
}
}
in a some view .phtml file
echo $this->currency($gimme_my_money);
and this is what i get
€ 19.373,25
-€ 116,07
how can i get it to print negative numbers like
€ -116,07
Just overwrite the format option like this:
$cur = new Zend_Currency(array('format' => '¤ #,##0.00;¤ -#,##0.00'));
The trick is in the second part of the string (after the comma), I've checked it for Italian locale and the format string provided there is ¤ #,##0.00.
This is tested with ZF 1.11.7
I don't think this formatting option is built into Zend_Currency.
What you can do, is move the currency symbol to the right side:
$this->view->total = new Zend_Currency(array('value' => $total, 'position' => Zend_Currency::RIGHT));
And then your currencies will be displayed with the currency symbol on the right:
-19.373,25 €
If you want a custom formatting, with the negative sign after the symbol, (€ -116,07), you will have to write your own currency formatter or built on top of Zend_Currency
try this:
class My_View_Helper_Currency extends Zend_View_Helper_Abstract
{
/**
* Format a numeric currency value and return it as a string
*
* #param int|float $value any value that return true with is_numeric
* #param array $options additional options to pass to the currency
* constructor
* #param string $locale locale value
*
* #throws InvalidParameterException if the $value parameter is not numeric
* #return string the formatted value
*/
public function currency($value, $options = array(), $locale = null)
{
if (!is_numeric($value)) {
throw new InvalidArgumentException(
'Numeric argument expected ' . gettype($value) . ' given'
);
}
$options = array_merge($options, array('value' => $value));
$currency = new Zend_Currency($options, $locale);
return $currency->toString();
}
}
I am using this to retrieve the database connection atm.
$db = Zend_Db_Table::getDefaultAdapter();
I do set this up in my config like this:
resources.db.adapter = pdo_mysql
resources.db.isDefaultTableAdapter = true
resources.db.params.host = localhost
resources.db.params.username = root
resources.db.params.password = password
resources.db.params.dbname = db
resources.db.params.profiler.enabled = true
resources.db.params.profiler.class = Zend_Db_Profiler
I would like to output everything to a sql.log for example. Is this possible to apply on the default adapter? for example through the settings, so I can ignore it in production environment?
Much appriciated.
I did look at: How to enable SQL output to log file with Zend_Db? but it didn't seem to cover my issue.
/Marcus
There is an example of extending Zend_Db_Profiler so you can write the queries to /logs/db-queries.log file.
So you have to do the following:
Create My_Db_Profiler_Log class in the library folder
Add the following lines to the application.ini
resources.db.params.profiler.enabled = true
resources.db.params.profiler.class = My_Db_Profiler_Log
Note: be aware, that the log file will become very big, very soon! So it is a good idea to log only the queries you are interested in. And this example should be considered only as a starting point in implementation of such a logging system.
Here is the code for the custom profiler class:
<?php
class My_Db_Profiler_Log extends Zend_Db_Profiler {
/**
* Zend_Log instance
* #var Zend_Log
*/
protected $_log;
/**
* counter of the total elapsed time
* #var double
*/
protected $_totalElapsedTime;
public function __construct($enabled = false) {
parent::__construct($enabled);
$this->_log = new Zend_Log();
$writer = new Zend_Log_Writer_Stream(APPLICATION_PATH . '/logs/db-queries.log');
$this->_log->addWriter($writer);
}
/**
* Intercept the query end and log the profiling data.
*
* #param integer $queryId
* #throws Zend_Db_Profiler_Exception
* #return void
*/
public function queryEnd($queryId) {
$state = parent::queryEnd($queryId);
if (!$this->getEnabled() || $state == self::IGNORED) {
return;
}
// get profile of the current query
$profile = $this->getQueryProfile($queryId);
// update totalElapsedTime counter
$this->_totalElapsedTime += $profile->getElapsedSecs();
// create the message to be logged
$message = "\r\nElapsed Secs: " . round($profile->getElapsedSecs(), 5) . "\r\n";
$message .= "Query: " . $profile->getQuery() . "\r\n";
// log the message as INFO message
$this->_log->info($message);
}
}
?>
Extend the Zend_Db_Profiler to write to an SQL.log and attach the profiler to your db adapter
<?php
class File_Profiler extends Zend_Db_Profiler {
/**
* The filename to save the queries
*
* #var string
*/
protected $_filename;
/**
* The file handle
*
* #var resource
*/
protected $_handle = null;
/**
* Class constructor
*
* #param string $filename
*/
public function __construct( $filename ) {
$this->_filename = $filename;
}
/**
* Change the profiler status. If the profiler is not enabled no
* query will be written to the destination file
*
* #param boolean $enabled
*/
public function setEnabled( $enabled ) {
parent::setEnabled($enabled);
if( $this->getEnabled() ) {
if( !$this->_handle ) {
if( !($this->_handle = #fopen($this->_filename, "a")) ) {
throw new Exception("Unable to open filename {$this->_filename} for query profiling");
}
}
}
else {
if( $this->_handle ) {
#fclose($this->_handle);
}
}
}
/**
* Intercept parent::queryEnd to catch the query and write it to a file
*
* #param int $queryId
*/
public function queryEnd($queryId) {
$state = parent::queryEnd($queryId);
if(!$this->getEnabled() || $state == self::IGNORED) {
return;
}
$profile = $this->getQueryProfile($queryId);
#fwrite($this->_handle, round($profile->getElapsedSecs(),5) . " " . $profile->getQuery() . " " . ($params=$profile->getQueryParams())?$params:null);
}
}
Haven't test it, but it should do the trick. Give it a try and let me know.
Btw you do know that you can log all queries on the mysql as well?
this will let you see sql queries to the web page , IT MIGHT BE OFF TOPIC but it helpful
I am highly recommend you to use ZF debug bar , it will give you very handy information
i am using it to see my doctrine queries , and it had support for zend db too
https://github.com/jokkedk/ZFDebug