How do I customize a Gravity Forms upload file error message such as "There was an problem while verifying your file." - gravity-forms-plugin

My goal is to customize the error message that may occur immediately after attempting to upload a file to a Gravity Forms "File Upload" filed. In particular I would like to modify the error message that occurs when you upload a file with the wrong file extension such as uploading a PNG file with a file name that ends with '.jpg' instead of '.png'.
The following represents what I'm trying to do, but it obviously doesn't work since the AJAX error message is displayed right after the file upload has completed and before Gravity Form has even been submitted or validated.
/////////////////////////////
// CUSTOM GRAVITY FORM FILTERS
//
add_filter( 'gform_field_validation',
/**
* Fixes Gravity Forms Custom validation message.
*
* #link https://docs.gravityforms.com/gform_field_validation/
*
* #param array $result The result array.
* #param string $value The value of the field.
* #param array $form The Gravity Form array.
* #param object $field The form field object.
*
* #return array The result array.
*/
function( $result, $value, $form, $field ) {
GFCommon::log_debug( __METHOD__ . '(): Running custom gform_field_validation...' );
if ( $field->type == 'file') {
if (!$result['is_valid'] && str_contains($result['message'], 'There was an problem while verifying your file.')) {
$result['message'] = str_replace(
'was an problem',
'was a problem',
$result['message'] // e.g., "Photo.jpg - There was an problem while verifying your file."
)
. ' Please make sure that your file extension (e.g., \'.jpg\') is correct. '
.'For example a PNG file should be saved with a \'.png\' extension and not with a \'.jpg\' file extension.';
}
}
return $result;
}
, 10, 4
);

Related

Typo3 : add file to FAL when it is not presente in sys_file

I have this situation
I have a table in my db containing some file names in a field1 (eg field1: "my file.ext")
NOTE: the filename does not necessarily pass a Typo3 "sanitizeFilename" check -> it may contain spaces " " or other characters that would be removed by the sanitizeFilename () method
I have the file mentioned above, stored on the server that host typo3
In the sys_file table, the file is not present
the "update storage index" scheduler cannot process all the files, and if i launch it, it "destroy" the file name (my file.ext -> my_file.ext), so the name stored in the field of my table doensn't have much sense anymore.
I would need to absorb the above mentioned files in the FAL, in order to use them in an ext typo3.
I had thought of such a solution
<?php
// read from "field1" of my table
// $filename = the name extracted from my table (e.g. : "my file.ext")
// %path = the path of the file : e.g. "/fileadmin/user_upload")
if (file_exists($_SERVER['DOCUMENT_ROOT'] . "/" . $defaultStorage->getConfiguration()['basePath'] . $path . $filename)) {
// check the folder
if ($defaultStorage->hasFolder($path)) {
$folder = $defaultStorage->getFolder($path);
} else {
throw new \ Exception ($path . "path not found in AbstractImportCommand in method extractFile");
}
// CHECK IF FILE IS IN FAL
$file = $folder->getStorage()->getFileInFolder($filename, $folder);
if ($file) {
// the file already exists in the FAL
} else {
// create new sys_file
$file = $defaultStorage->addFile(
$_SERVER['DOCUMENT_ROOT'] . "/" . $defaultStorage->getConfiguration()['basePath'] . $path . $filename,
$folder,
DuplicationBehavior::REPLACE
);
}
}
Any suggestion?
Put your code into a command.
(optional) create a sys_file_metadata record for your file if you have information that needs to be stored there
create a sys_file_reference to your content record (before that you should adjust TCA accordingly)
For creating the sys_file_reference there is no api. A function doing so could look like this:
/**
* #param $fileUid
* #param $recordUid
* #param $table
*/
private function createSysFileReference($record, $fileUid, $tableName, $fieldName){
$data['sys_file_reference']['NEW_' . uniqid()] = [
'table_local' => 'sys_file',
'uid_local' => $fileUid,
'tablenames' => $tableName,
'uid_foreign' => $record['uid'],
'fieldname' => $fieldName,
'pid' => $record['pid']
];
$dataHandler = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\DataHandling\DataHandler::class);
$dataHandler->start($data, []);
$dataHandler->process_datamap();
}
What you consider as "destroying the filename" is indeed preserving file functionality, so sanitizing the filenames is required.
As example consider a file with white space like "My Pdf.pdf". It will be shown eventually like "My%20Pdf.pdf" in the URL and also saved like this. If you link to it, at least the link text won't show the "%20" and the expectation that links and the name of the file are always synchronized (no matter how it's stored) isn't reliable as it probably depends on several parameters like operating system or browser too. The same problem might occur for many different signs too.
Consider that the problems occur not only when a user is downloading a file but also when a file is uploaded, where the wrong url-encoded name is saved in the database, this name might be saved differently in the filesystem or not be found even if the saved value is the same as in the filesystem, due to the url-encoded signs. Your file references are broken on the server then and according links might not work and images not displayed.
So circumventing FAL beside tables is a bad decision and while it's likely possible to write an own sanitizer, I would refrain from dropping it completely.

TYPO3: Extend ctype textmedia with subheader

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.

Creating an attachment for MailMessage

From my extbase 6.2 extension I want to send different e-mails.
In a controller class I wrote a mail function that looks like this:
(notice no #param for $attachment - see my question at the end)
/**
*
* #param string $to
* #param string $subject
* #param string $email_prefix
* #param string $msg
* #param string $email_suffix
*/
public function mailAction($to, $subject, $email_prefix, $msg, $email_suffix, $attachment = null) {
try {
$from = \TYPO3\CMS\Core\Utility\MailUtility::getSystemFrom();
$body = $email_prefix
. PHP_EOL . PHP_EOL
. $msg
. PHP_EOL . PHP_EOL
. $email_suffix;
$htmlBody = nl2br($body);
$toEmail = $to;
$mail = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Mail\\MailMessage');
$mail->setFrom($from)
->setTo(array($toEmail))
->setSubject($subject)
->setBody($htmlBody, 'text/html');
$mail->addPart($body, 'text/plain');
if ($attachment) {
$mail->attach($attachment);
}
if (empty($toEmail) || strpos($toEmail, '#') === FALSE) {
$this->addFlashMessage('Die Mail konnte nicht verschickt werden! Keine Email-Adresse des Empfängers', '', \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR
);
} else {
if ($mail->send()) {
$this->addFlashMessage('Die Mail für wurde verschickt!', '');
} else {
$this->addFlashMessage('Die Mail konnte nicht verschickt werden!', '', \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR
);
}
}
$this->redirect('list');
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
}
In a function that calls the mail function I tried creating an attachment like this but it failed saying: Fatal error: Class 'Swift_Attachment' not found in.../...Controller.php
$attachment = \Swift_Attachment::newInstance()
->setFilename('Termine.html')
->setContentType('text/html')
->setBody($emailView->render());
Then I call the mail function like this:
$this->redirect('mail', null, null, array(
$to,
$subject,
$email_prefix,
$msg,
$email_suffix,
$attachment));
My questions:
How can I successfully create an object of type Swift_Attachment in a controller of my extbase extension (without creating a MailMessage object beforehand and creating the attachment inside of it)?
What should I put after #param as the type of my $attachment variable in my mail function for this to work?
-- EDIT --
Ok, so apparently no one does that because it's not meant to be.
I now used Rene's approach combining it with Dimitri's scalable answer for multiple attachments. My #param is now array because I have to create the actual attachment after instantiating MailMessage - thanks!
In my extension for 6.2.25 ist works without any including:
$email->attach(\Swift_Attachment::newInstance(
$emailView->render(),
'Termine.html',
'text/html'
));
So you should check why your autoload of classes don't work. Have you tried to clear the cache complete?
To your second question: the correct param declaration should be:
#param \Swift_Mime_Attachment $attachment
But I wouldn't make an redirect, but an $this->forward. You don't need an redirection on client side for this. If this action is only called by an other action I also recommend to make it an protected function an call it directly from your action:
$this->sendMail($to, $subject, $email_prefix, $msg, $email_suffix, $attachment)
-- EDIT --
I recommend to use bypass the attachment information to the function to create the attachment object after the SwitftMailer was initialized:
/**
*
* #param string $to
* #param string $subject
* #param string $email_prefix
* #param string $msg
* #param string $email_suffix
* #param array $attachment
*/
public function mailAction($to, $subject, $email_prefix, $msg, $email_suffix, $attachment = null) {
...
if (is_array($attachment) && array_key_exist('content', $attachment) && array_key_exist('filename', $attachment) && array_key_exist('mime', $attachment)) {
$mail->attach(\Swift_Attachment::newInstance($attachment['content'], $attachment['filename'], $attachment['mime']));
}
...
}
In \TYPO3\CMS\Core\Mail\MailMessage there is a require_once for the swiftmailer classes; they don't seem to be autoloaded. Maybe you can pass the attachment as rendered HTML and create the Swift_Attachment object after instantiating the MailMessage object?
If the solution in 1. works it would be a simple string.
As already stated by Jigal van Hemert you can only create the attachment objects after you create the MailMessage object because the class is not autoloaded. I would just pass the attachment as a filepath to your mail function and it should be handled there and not outside.
if ($attachment) {
$email->attach(\Swift_Attachment::fromPath($attachment));
}
In my opinion it makes more sense if you can pass multiple files instead of one, so the $attachment should be an $attachments array
if(count($attachments)) {
foreach ($attachments as $name => $file) {
if(file_exists($file)) {
if(trim($name) && !is_numeric($name)) {
$email->attach(\Swift_Attachment::fromPath($file)->setFilename($name));
} else {
$email->attach(\Swift_Attachment::fromPath($file));
}
}
}
}

How to inserting data from install.xml file into mysql database in moodle

In moodle site (use moodle Version 2.6.3), I have generated install.xml file by XMLDB editor, but it's use only to create table in database during plugin installation. I want to insert some default rows in the table also.
Any body can help me how to edit in install.xml file for insert data
To add data after an install, create a file called yourplugin/db/install.php with
UPDATE: added xml parser
defined('MOODLE_INTERNAL') || die;
require_once($CFG->libdir . '/xmlize.php');
function xmldb_yourpluginname_install() {
global $CFG, $OUTPUT, $DB;
// Your add data code here.
$xmltext = file_get_contents('import.xml');
$records = parse_xml($xmltext, 'records', 'record');
foreach ($records as $record) {
$DB->insert_record('yourtablename', $record);
}
}
/**
* Converts XML text into an array of stdclass objects.
*
* #param type $text - xmltext
* #param type $elementnames - plural name of elements
* #param type $elementname - name of element
* #return array|boolean - array of record objects
*/
function parse_xml($text, $elementnames, $elementname) {
// Seems that xmlize needs a lot of memory.
ini_set('memory_limit', '256M');
// Ensure content is UTF-8.
$content = xmlize($text, 1, 'UTF-8');
$records = array();
if (!empty($content[$elementnames]['#'][$elementname])) {
$rows = $content[$elementnames]['#'][$elementname];
foreach ($rows as $row) {
$fields = $row['#'];
$row = new stdClass();
foreach ($fields as $fieldname => $fieldvalue) {
$row->$fieldname = $fieldvalue[0]['#'];
}
$records[] = $row;
}
return $records;
}
return false;
}

ZF2 - Show just one error on forms

I can't seem to get ZF2 to show just one error message for failed form validation messages.
For example, an EmailAddress validator can pass back up to 7 messages and typically shows the following if the user has made a typo:
oli.meffff' is not a valid hostname for the email address
The input appears to be a DNS hostname but cannot match TLD against known list
The input appears to be a local network name but local network names are not allowed
How can I override the error to show something a little more friendly, such as "Please enter a valid email address" instead of specifics like the above?
OK, managed to come up with a solution for this. Instead of using the same string as the error for all validator failures as Sam suggested above, I have overridden the error messages in the InputFilter for the elements and then used a custom form error view helper to show only the first message.
Here is the helper:
<?php
namespace Application\Form\View\Helper;
use Traversable;
use \Zend\Form\ElementInterface;
use \Zend\Form\Exception;
class FormElementSingleErrors extends \Zend\Form\View\Helper\FormElementErrors
{
/**
* Render validation errors for the provided $element
*
* #param ElementInterface $element
* #param array $attributes
* #throws Exception\DomainException
* #return string
*/
public function render(ElementInterface $element, array $attributes = array())
{
$messages = $element->getMessages();
if (empty($messages)) {
return '';
}
if (!is_array($messages) && !$messages instanceof Traversable) {
throw new Exception\DomainException(sprintf(
'%s expects that $element->getMessages() will return an array or Traversable; received "%s"',
__METHOD__,
(is_object($messages) ? get_class($messages) : gettype($messages))
));
}
// We only want a single message
$messages = array(current($messages));
// Prepare attributes for opening tag
$attributes = array_merge($this->attributes, $attributes);
$attributes = $this->createAttributesString($attributes);
if (!empty($attributes)) {
$attributes = ' ' . $attributes;
}
// Flatten message array
$escapeHtml = $this->getEscapeHtmlHelper();
$messagesToPrint = array();
array_walk_recursive($messages, function ($item) use (&$messagesToPrint, $escapeHtml) {
$messagesToPrint[] = $escapeHtml($item);
});
if (empty($messagesToPrint)) {
return '';
}
// Generate markup
$markup = sprintf($this->getMessageOpenFormat(), $attributes);
$markup .= implode($this->getMessageSeparatorString(), $messagesToPrint);
$markup .= $this->getMessageCloseString();
return $markup;
}
}
It's just an extension of FormElementErrors with the render function overridden to include this:
// We only want a single message
$messages = array(current($messages));
I then insert the helper into my application using the solution I posted to my issue here.