Why is "deleteBlocks" not working in phpWord? - block

I´m trying to use blocks in my Word document but I´m having some problems. First of all, when I declare a block in my document, if I don´t use the function "cloneBlock", the result appears like this:
${sec}
example
${/sec}
Maybe I must use that function to appear properly. But my main problem is that "deleteBlock" is not working. If I don´t clone the block, the generated docx is corrupted. But if I clone the block, the function "deleteBlock" doesn´t delete the block and it appear the information that is inside that block in my final docx file.
This is my code:
//Word
// Creating the new document...
$templateProcessor = new \PhpOffice\PhpWord\TemplateProcessor('../example.docx');
//set value
//$templateProcessor->setValue('title', 'Example');
//Triplicate block
$templateProcessor->cloneBlock('firstblock', 3, true, true);
$templateProcessor->setValue('firstname#1', 'John');
$templateProcessor->setValue('lastname#1', 'Doe');
$templateProcessor->setValue('firstname#2', 'John');
$templateProcessor->setValue('lastname#2', 'Doe');
$templateProcessor->setValue('firstname#3', 'John');
$templateProcessor->setValue('lastname#3', 'Doe');
//Delete Block
$templateProcessor->cloneBlock('sec', 1, true, true);
$templateProcessor->deleteBlock('sec');
$templateProcessor->saveAs('example.docx');
Docx template:
${firstblock}
Hello ${firstname} ${lastname}!
${/firstblock}
${sec}
example
${/sec}
UPDATE:
Instead of using the function "deleteBlock", I have use the function "cloneBlock" like this and it deletes the block:
//Delete Block
$templateProcessor->cloneBlock('sec', 0, true, true);
So, I have write to clone the block 0 times, so it disappears
But I have another problem. I don´t know why, but this only works sometimes

I'm not sure why user #d1845412 deleted their previous answer, but it actually solved my issue. I overwrote the deleteBlock method with the following code and it seems to work. I prefer this small change over larger changes to the replaceBlock method.
/**
* Override this method since deleteBlock doesn't seem to work.
* #param string $blockname
*/
public function deleteBlock($blockname)
{
$this->cloneBlock($blockname, 0, true, true);
}

Try this. I slightly changed the regexp in replaceBlock, I also added a function that removes unused patterns, it may come in handy) I'll warn you right away - not really testing so use it carefully
class PatchedTemplateProcessor extends TemplateProcessor
{
/**
* Remove ${*} and ${/*} from temporary document
*/
public function removeSearchPatterns()
{
preg_match_all(
'/(\$\{[^\}]*\})/',
$this->tempDocumentMainPart,
$matches,
PREG_SET_ORDER
);
foreach ($matches as $match){
$this->tempDocumentMainPart = str_replace(
$match,
'',
$this->tempDocumentMainPart
);
}
}
/**
* #param string $blockname
* #param string $replacement
*/
public function replaceBlock($blockname, $replacement)
{
$matches = array();
preg_match(
'/(<w:t.*>\${' . $blockname . '}<\/w:.*?t>)(.*)(<w:t.*\${\/' . $blockname . '}<\/w:.*?t>)/is',
$this->tempDocumentMainPart,
$matches
);
if (isset($matches[3])) {
$this->tempDocumentMainPart = str_replace(
$matches[2] . $matches[3],
$replacement,
$this->tempDocumentMainPart
);
}
}
/**
* Delete a block of text.
*
* #param string $blockname
*/
public function deleteBlock($blockname)
{
$this->replaceBlock($blockname, '');
}
}

Related

TYPO3 extension Model conflict with f:form.upload

For an own extension, I want o use the viewhelper f:form.upload
to retrieve the upload parameters I have to define the property for this viewhelper as an array. These work fine to get the name, type, tmp_name, error, and size parameters.
In the Domain Model, I read the parameters and save the uploaded file and store the file name inside the database. That works fine! Here the part of the Domain Modell for the variable iFile:
/**
* iFile
*
* #var array
* #TYPO3\CMS\Extbase\Annotation\ORM\Cascade("remove")
*/
protected $iFile = [];
/**
* Returns the iFile
*
* #return string iFile
*/
public function getIFile()
{
$temp = [];
$temp['image_name'] = $this->iFile;
error_log("Get_iFile: " . var_export($temp, true), 0);
error_log("Get_iFile0: " . var_export($this, true), 0);
return $temp;
}
/**
* Sets the iFile
*
* #param string $iFile
* #return void
*/
public function setIFile($iFile)
{
if($iFile['tmp_name'] != ""){
$docRoot = $_SERVER['DOCUMENT_ROOT'].'\\' ;
if (!is_dir($docRoot)) {
mkdir($docRoot, 07777, true);
}
$date = new \DateTime();
$fName = 'fileadmin\\Radio\\crNews\\' . $date->format('Ymd') . $iFile['name'];
if(copy($iFile['tmp_name'], $docRoot . $fName) == true);
unlink($iFile['tmp_name']);
}
error_log("Set _FILES: " . var_export($iFile, true), 0);
$this->iFile = $fName;
}
But if I try to read it from the database, I don't get back the file name from the database! Only an empty array.
'iFile' =>
array (
),
The problem seems to be, due to the definition of iFile as an array.
How can I solve the problem?
Is there any possibility, to add a new variable to the Model as an array, but not to store it in the database?
So I found a workaround. I only defined iFile as an string to read and save the file name in the database and used the $_FILES post variables to retrieve the name and tmp_name of the $_FILES.
In Typo3, access the $_FILES parameters is not so easy. So I recommend to use phpinstruction error_log("Create _FILES: " . var_export($_FILES, true), 0); to find out the structure of the array.

How do i execute SQL statements from an SQL file in Scheduler in TYPO3?

The concept:
I have a task which imports some data on the database. The database schema can be changed at any time since it is exported to a folder. So i have to read the database file and create the structure. I would like to do it with TYPO3 API. Before TYPO3 9 i would do something like that:
$sqlQueries = explode(';', file_get_contents(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'myFile.sql'));
foreach ($sqlQueries as $sqlQuery) {
$sqlQuery = trim($sqlQuery);
if (!empty($sqlQuery) && $this->db instanceof DatabaseConnection && method_exists($this->db, 'sql_query')) {
$this->db->sql_query($sqlQuery);
}
}
How do i do that with TYPO3 10?
Best regards
TYPO3 10 has already such functionality and you can find it in the maintenance module unter the
Analyze Database Structure. Thanks to Mathias Brodala i found my solution. (It can always be improved).
In your task you call the SqlReader class in order to extract the statements from the SQL file. The use namespace is the following (As of TYPO3 10)
use TYPO3\CMS\Core\Database\Schema\SqlReader;
Now we have two problems with that
The SQL evaluated and executed by TYPO3 is limited to schemas. For content changes you need a 3rd party library/tool.
It only creates tables and doesn't drop them by default.
The first "problem" it says that you can only perform database structure changes but not the content in it. For that, one could use the QueryBuilder.
The second problem is that the getCreateTableStatementArray function in the SqlReader class does the following:
return $this->getStatementArray($dumpContent, '^CREATE TABLE');
This calls the getStatementArray function and it only returns the CREATE TABLE commands. So if you have a DROP TABLE command in the SQL file, it won't be taken into consideration. For that, we need to create our own function for the getCreateTableStatementArray. So in your Task, create this function:
/**
* #param string $dumpContent
* #return array
*/
public function getCreateTableStatementArray(string $dumpContent): array
{
$sqlReader = GeneralUtility::makeInstance(SqlReader::class);
return $sqlReader->getStatementArray($dumpContent);
}
With this, TYPO3 will not evaluate the statements by command name and it will get all the commands available in the sql file.
Now that we have done that, we need to pass the statements into our custom function to be parsed. So somewhere in your code you get file's content. I have it like this:
/**
* #return bool
* #throws \Exception
*/
public function execute()
{
...
$this->createTableStructure();
...
}
protected function createTableStructure()
{
$connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
$sqlStatements = $this->getCreateTableStatementArray(file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'myFile.sql'));
foreach ($sqlStatements as $sqlStatement) {
if (strpos($sqlStatement, ' CHARSET=utf8') !== false) {
$sqlStatement = str_replace(" DEFAULT CHARSET=utf8", "", $sqlStatement);
}
$connection = $connectionPool->getConnectionByName('Default');
try {
$connection->executeUpdate($sqlStatement);
} catch (DBALException $e) {
//Your log here
}
}
}
For this part i got an error that TYPO3 could not read the DEFAULT attribute so i had to remove it.
if (strpos($sqlStatement, ' CHARSET=utf8') !== false) {
$sqlStatement = str_replace(" DEFAULT CHARSET=utf8", "", $sqlStatement);
}
This looks like this in the SQL file:
DROP TABLE IF EXISTS `myTable`;
CREATE TABLE IF NOT EXISTS `myTable` (
`myColumn1` int(10) NOT NULL DEFAULT '0',
`myColumn2` varchar(255) DEFAULT NULL,
`myColumn3` varchar(255) DEFAULT NULL,
`myColumn4` date DEFAULT NULL,
`myColumn5` varchar(255) DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
Hope it was helpful!
Best regards

Does Laravel Input::hasfile() work on input arrays?

I'm working on a Laravel project that uses a form with multiple file inputs. If I submit the form with the first input empty and all other inputs with a file, then hasFile returns false. It will only return true if the first input contains a file.
if(Input::hasfile('file'))
{
// do something
}
This is the input array via Input::file('file). The small image input is empty, but the large is not. I'd like it to look at the whole array and if there any files present, then proceed with the "do something".
Array
(
[small] =>
[large] => Symfony\Component\HttpFoundation\File\UploadedFile Object
(
[test:Symfony\Component\HttpFoundation\File\UploadedFile:private] =>
[originalName:Symfony\Component\HttpFoundation\File\UploadedFile:private] => image_name.jpg
[mimeType:Symfony\Component\HttpFoundation\File\UploadedFile:private] => image/jpeg
[size:Symfony\Component\HttpFoundation\File\UploadedFile:private] => 44333
[error:Symfony\Component\HttpFoundation\File\UploadedFile:private] => 0
[pathName:SplFileInfo:private] => /Applications/MAMP/tmp/php/phpHILgX2
[fileName:SplFileInfo:private] => phpHILgX2
)
)
Is this expected behavior? Or, should it be looking at the entire array?
You can check by using the array key for example like below :-
HTML Input type File Element :
<input type="file" name="your_file_name[]" />
Laravel 5 : $request->hasFile('your_file_name.'.$key)
Laravel 4.2 : Input::hasFile('your_file_name.'.$key)
Taken from source:
/**
* Determine if the uploaded data contains a file.
*
* #param string $key
* #return bool
*/
public function hasFile($key)
{
if (is_array($file = $this->file($key))) $file = head($file);
return $file instanceof \SplFileInfo;
}
It seems that it only checks the first one from the array, head returns the first item from the array.
Since I can't comment, seems I'll have to post.
Ronak Shah's answer really should be marked the correct one here, and when I figured out why, it instantly had me saying "Sonnofa--" after 30-40 minutes trying to figure this... "mess" out.
Turns out to use hasFile() on an input array, you need to use dot notation.
So (using my own example) instead of
$request->hasFile("img[29][file]")
it needs to be
$request->hasFile("img.29.file")
That's certainly an eye-opener, given that PHP and dot notation don't really go together. Input arrays really are problem children.
here is a snippet that may help
if(Input::hasFile('myfile')){
$file = Input::file('myfile');
// multiple files submitted
if(is_array($file))
{
foreach($file as $part) {
$filename = $part->getClientOriginalName();
$part->move($destinationPath, $filename);
}
}
else //single file
{
$filename = $file->getClientOriginalName();
$uploadSuccess = Input::file('myfile')->move($destinationPath, $filename);
}
} else {
echo 'Error: no file submitted.';
}
Taken from
http://forumsarchive.laravel.io/viewtopic.php?id=13291
At the time of writing (Laravel 8) the Request class now supports arrays for the hasFile method, as from the source code:
/**
* Determine if the request contains the given file.
*
* #param string $name
* #param string|null $value
* #param string|null $filename
* #return bool
*/
public function hasFile($name, $value = null, $filename = null)
{
if (! $this->isMultipart()) {
return false;
}
return collect($this->data)->reject(function ($file) use ($name, $value, $filename) {
return $file['name'] != $name ||
($value && $file['contents'] != $value) ||
($filename && $file['filename'] != $filename);
})->count() > 0;
}

Using boolean fields with Magento ORM

I am working on a backend edit page for my custom entity. I have almost everything working, including saving a bunch of different text fields. I have a problem, though, when trying to set the value of a boolean field.
I have tried:
$landingPage->setEnabled(1);
$landingPage->setEnabled(TRUE);
$landingPage->setEnabled(0);
$landingPage->setEnabled(FALSE);
None seem to persist a change to my database.
How are you supposed to set a boolean field using magento ORM?
edit
Looking at my database, mysql is storing the field as a tinyint(1), so magento may be seeing this as an int not a bool. Still can't get it to set though.
This topic has bring curiosity to me. Although it has been answered, I'd like to share what I've found though I didn't do intense tracing.
It doesn't matter whether the cache is enabled / disabled, the table schema will be cached.
It will be cached during save process.
Mage_Core_Model_Abstract -> save()
Mage_Core_Model_Resource_Db_Abstract -> save(Mage_Core_Model_Abstract $object)
Mage_Core_Model_Resource_Db_Abstract
public function save(Mage_Core_Model_Abstract $object)
{
...
//any conditional will eventually call for:
$this->_prepareDataForSave($object);
...
}
protected function _prepareDataForSave(Mage_Core_Model_Abstract $object)
{
return $this->_prepareDataForTable($object, $this->getMainTable());
}
Mage_Core_Model_Resource_Abstract
protected function _prepareDataForTable(Varien_Object $object, $table)
{
$data = array();
$fields = $this->_getWriteAdapter()->describeTable($table);
foreach (array_keys($fields) as $field) {
if ($object->hasData($field)) {
$fieldValue = $object->getData($field);
if ($fieldValue instanceof Zend_Db_Expr) {
$data[$field] = $fieldValue;
} else {
if (null !== $fieldValue) {
$fieldValue = $this->_prepareTableValueForSave($fieldValue, $fields[$field]['DATA_TYPE']);
$data[$field] = $this->_getWriteAdapter()->prepareColumnValue($fields[$field], $fieldValue);
} else if (!empty($fields[$field]['NULLABLE'])) {
$data[$field] = null;
}
}
}
}
return $data;
}
See the line: $fields = $this->_getWriteAdapter()->describeTable($table);
Varien_Db_Adapter_Pdo_Mysql
public function describeTable($tableName, $schemaName = null)
{
$cacheKey = $this->_getTableName($tableName, $schemaName);
$ddl = $this->loadDdlCache($cacheKey, self::DDL_DESCRIBE);
if ($ddl === false) {
$ddl = parent::describeTable($tableName, $schemaName);
/**
* Remove bug in some MySQL versions, when int-column without default value is described as:
* having default empty string value
*/
$affected = array('tinyint', 'smallint', 'mediumint', 'int', 'bigint');
foreach ($ddl as $key => $columnData) {
if (($columnData['DEFAULT'] === '') && (array_search($columnData['DATA_TYPE'], $affected) !== FALSE)) {
$ddl[$key]['DEFAULT'] = null;
}
}
$this->saveDdlCache($cacheKey, self::DDL_DESCRIBE, $ddl);
}
return $ddl;
}
As we can see:
$ddl = $this->loadDdlCache($cacheKey, self::DDL_DESCRIBE);
will try to load the schema from cache.
If the value is not exists: if ($ddl === false)
it will create one: $this->saveDdlCache($cacheKey, self::DDL_DESCRIBE, $ddl);
So the problem that occurred in this question will be happened if we ever save the model that is going to be altered (add column, etc).
Because it has ever been $model->save(), the schema will be cached.
Later after he add new column and "do saving", it will load the schema from cache (which is not containing the new column) and resulting as: the data for new column is failed to be saved in database
Delete var/cache/* - your DB schema is cached by Magento even though the new column is already added to the MySQL table.

Is this little Doctrine2 dynamic SQL enough safe of injection?

I kwnow that using an ORM like Doctrine2 for building queries is safe, meaning that parameters are escaped by default.
But i'm guessing that this is not so obvious when using literals and when this literal comes directly from the query string:
$builder = $this->getRepository()->createQueryBuilder('e');
$request = $this->getRequest();
// Loop each allowed filter field and check if exists in $request
foreach($this->getFilterFields() as $filter) :
// Skip falsy values in $request
if(!$value = $request->get($filter)) continue;
// Add OR LIKE %$value% where $value is GET paramter
$like = $builder->expr()->literal("%$value%");
$builder->orWhere($builder->expr()->like("e.$filter", $like));
endforeach;
Should safety be improved in some way?
$queryBuilder->expr returns an ExpressionBuilder object. Inside ExpressionBuilder we find:
public function literal($input, $type = null)
{
return $this->connection->quote($input, $type);
}
So literals do get quoted and should be fine to use.
We also find:
public function like($x, $y)
{
return $this->comparison($x, 'LIKE', $y);
}
public function comparison($x, $operator, $y)
{
return $x . ' ' . $operator . ' ' . $y;
}
$y is fine because it goes through literal first. Do want to be a bit careful about $x. As long as your filterFields are internal then no problem. If they are coming from the user then you need to make sure they are valid.