TYPO3 6.2 - how to create FileReference in frontend (FE)? - file-upload

I have the hypothetical Zoo extension in which I've Animal model with photo field and FrontEnd (FE) plugin with typical CRUD actions. photo field is typical FAL's FileReference and it works perfectly in backend (BE) with common TCA IRRE config.
I'm able to successful upload the file to the storage, it's visible in the Filelist module, and I can use it in BE during my Animal editing, anyway I can't create FileReference within my FE plugin.
My current approach looks like this:
/**
* #param \Zoo\Zoo\Domain\Model\Animal $animal
*/
public function updateAction(\Zoo\Zoo\Domain\Model\Animal $animal) {
// It reads proper uploaded `photo` from form's $_FILES
$file = $this->getFromFILES('tx_zoo_animal', 'photo');
if ($file && is_array($file) && $file['error'] == 0) {
/** #type $storageRepository \TYPO3\CMS\Core\Resource\StorageRepository */
$storageRepository = GeneralUtility::makeInstance('\TYPO3\CMS\Core\Resource\StorageRepository');
$storage = $storageRepository->findByUid(5); // TODO: make target storage configurable
// This adds uploaded file to the storage perfectly
$fileObject = $storage->addFile($file['tmp_name'], $storage->getRootLevelFolder(), $file['name']);
// Here I stuck... below line doesn't work (throws Exception no. 1 :/)
// It's 'cause $fileObject is type of FileInterface and FileReference is required
$animal->addPhoto($fileObject);
}
$this->animalRepository->update($animal);
$this->redirect('list');
}
anyway attempt to create reference by this line throws exception:
$animal->addPhoto($fileObject);
How can I resolve this?
Checked: DataHandler approach (link) won't work also, as it's unavailable for FE users.
TL;DR
How to add FileReference to Animal model from existing (just created) FAL record?

You need to do several things. This issue on forge is where I got the info, and some stuff is taken out of Helmut Hummels frontend upload example (and the accompanying blogpost) which #derhansen already commented.
I'm not entirely sure if this is everything you need, so feel free to add things. This does not use a TypeConverter, which you should probably do. That would open further possibilities, for example it would be easily possible to implement deletion and replacement of file references.
You need to:
Create a FAL file reference object from the File object. This can be done using FALs resource factory.
Wrap it in a \TYPO3\CMS\Extbase\Domain\Model\FileReference (method ->setOriginalResource)
EDIT: This step is unnecessary as of TYPO3 6.2.11 and 7.2, you can directly use the class \TYPO3\CMS\Extbase\Domain\Model\FileReference.
But, because the extbase model misses a field ($uidLocal) in 6.2.10rc1, that won't work. You need to inherit from the extbase model, add that field, and fill it. Don't forget to add a mapping in TypoScript to map your own model to sys_file_reference.
config.tx_extbase.persistence.classes.Zoo\Zoo\Domain\Model\FileReference.mapping.tableName = sys_file_reference
The class would look like this (taken from the forge issue):
class FileReference extends \TYPO3\CMS\Extbase\Domain\Model\FileReference {
/**
* We need this property so that the Extbase persistence can properly persist the object
*
* #var integer
*/
protected $uidLocal;
/**
* #param \TYPO3\CMS\Core\Resource\ResourceInterface $originalResource
*/
public function setOriginalResource(\TYPO3\CMS\Core\Resource\ResourceInterface $originalResource) {
$this->originalResource = $originalResource;
$this->uidLocal = (int)$originalResource->getUid();
}
}
Add this to the TCA of the image field, in the config-section (adapt to your table and field names of course):
'foreign_match_fields' => array(
'fieldname' => 'photo',
'tablenames' => 'tx_zoo_domain_model_animal',
'table_local' => 'sys_file',
),
EDIT: Use \TYPO3\CMS\Extbase\Domain\Model\FileReference in this step if on TYPO3 6.2.11 or 7.2 or above.
So at the end add the created $fileRef instead of $fileObject
$fileRef = GeneralUtility::makeInstance('\Zoo\Zoo\Domain\Model\FileReference');
$fileRef->setOriginalResource($fileObject);
$animal->addPhoto($fileRef);
Don't tell anyone what you have done.

Here is the complete function to upload file in TYPO3 using FAL and create filereference
/**
* Function to upload file and create file reference
*
* #var array $fileData
* #var mixed $obj foreing model object
*
* #return void
*/
private function uploadAndCreateFileReference($fileData, $obj) {
$storageUid = 2;
$resourceFactory = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance();
//Adding file to storage
$storage = $resourceFactory->getStorageObject($storageUid);
if (!is_object($storage)) {
$storage = $resourceFactory->getDefaultStorage();
}
$file = $storage->addFile(
$fileData['tmp_name'],
$storage->getRootLevelFolder(),
$fileData['name']
);
//Creating file reference
$newId = uniqid('NEW_');
$data = [];
$data['sys_file_reference'][$newId] = [
'table_local' => 'sys_file',
'uid_local' => $file->getUid(),
'tablenames' => 'tx_imageupload_domain_model_upload', //foreign table name
'uid_foreign' => $obj->getUid(),
'fieldname' => 'image', //field name of foreign table
'pid' => $obj->getPid(),
];
$data['tx_imageupload_domain_model_upload'][$obj->getUid()] = [
'image' => $newId,
];
$dataHandler = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(
'TYPO3\CMS\Core\DataHandling\DataHandler'
);
$dataHandler->start($data, []);
}
where $filedata =
$this->request->getArgument('file_input_field_name');
And
$obj = //Object of your model for which you are creating file
reference

This example does not deserve a beauty prize but it might help you. It works in 7.6.x
private function uploadLogo(){
$file['name'] = $_FILES['logo']['name'];
$file['type'] = $_FILES['logo']['type'];
$file['tmp_name'] = $_FILES['logo']['tmp_name'];
$file['size'] = $_FILES['logo']['size'];
// Store the image
$resourceFactory = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance();
$storage = $resourceFactory->getDefaultStorage();
$saveFolder = $storage->getFolder('logo-companies/');
$newFile = $storage->addFile(
$file['tmp_name'],
$saveFolder,
$file['name']
);
// remove earlier refereces
$GLOBALS['TYPO3_DB']->exec_DELETEquery('sys_file_reference', 'uid_foreign = '. $this->getCurrentUserCompanyID());
$addressRecord = $this->getUserCompanyAddressRecord();
// Create new reference
$data = array(
'table_local' => 'sys_file',
'uid_local' => $newFile->getUid(),
'tablenames' => 'tt_address',
'uid_foreign' => $addressRecord['uid'],
'fieldname' => 'image',
'pid' => $addressRecord['pid']
);
$GLOBALS['TYPO3_DB']->exec_INSERTquery('sys_file_reference', $data);
$newId = $GLOBALS['TYPO3_DB']->sql_insert_id();
$where = "tt_address.uid = ".$addressRecord['uid'];
$GLOBALS['TYPO3_DB']->exec_UPDATEquery('tt_address', $where, array('image' => $newId ));
}

Related

How to get original name of the image?

Hello guys How to get the original client name of an image in the collection?
I have this new code to upload more than three images
public function store(){
$action = '';
$data = $this->validate([
'album_title' => 'required',
'photos.*' => 'image|max:2000', // 2MB Max
]);
//New Code with collections
$filenames = collect($this->photos)->map->store('photos');
if($this->galleryId){
Gallery::find($this->galleryId)->update($data);
$action = 'edit';
}else{
// Gallery::create($data);
Gallery::create([
'album_title' => $this->album_title,
'photos' => $filenames->implode(','),
]);
$action = 'store';
}
$this->emit('showEmitedFlashMessage', $action);
$this->resetInputFields();
$this->emit('refreshParent');
$this->emit('closeGalleryModal');
}
but it save something like this
photos/xTNV0cQ7YzpyUsJ6OdrpQirwfCPdzDtKJAKoWUFm.jpg,photos/32PkRR9bFhM7BqAy9OWG05m5fe53PUXtidEMhlnn.jpg,photos/Dyps8wCsga0zdALGsKEOS9jcYSNshnixw4UWzhEL.jpg
What I want is to save the original name of the images.
What I usually do is set the file name to a uuid for storage in the system, but retain the original filename in the database so that you can rename it for downloads if you want. To get the original file name, you need to look at the post data for that.
like so:
$this->active_document->original_filename = $this->uploaded_file->getClientOriginalName();
More info here in the docs: https://laravel.com/docs/8.x/filesystem#file-uploads

Prestashop 1.7.7.5: FrameworkBundleAdminController: $this->module->getPathUri() returns an error

In my admin module's controller's listMappingsAction action, I want to show a Vue.JS template, passing two variables that need to call $this->module->getPathUri() to be assigned a value:
<?php
namespace XYZ\Controller;
use PrestaShopBundle\Controller\Admin\FrameworkBundleAdminController;
use PrestaShopBundle\Security\Annotation\AdminSecurity;
class AutomatizedMappingController extends FrameworkBundleAdminController
{
/**
* #AdminSecurity("is_granted('read', request.get('_legacy_controller'))", message="Access denied.")
*/
function listMappingsAction() {
return $this->render('#Modules/XYZ/views/templates/admin/app.html.twig', [
'XYZ' => [
'pathApp' => $this->module->getPathUri() . 'views/js/app.js',
'chunkVendor' => $this->module->getPathUri() . 'views/js/chunk-vendors.js',
]
]);
}
}
The use of $this->module->getPathUri() results in this error being displayed:
Attempted to call an undefined method named "getPathUri" of class "XYZ\Controller\AutomatizedMappingController". [Symfony\Component\Debug\Exception\UndefinedMethodException 0]
What could I call to make it work? The docs don't mention this use case... https://devdocs.prestashop.com/1.7/modules/concepts/templating/vuejs/faq/#how-to-send-data-from-symfony-controller-to-vuejs
First of all you need to know there is a difference between a module and a admin-controller. You cannot call $this->module->getPathUri() because your not using a extends ModuleAdminController, extends ModuleFrontController, extends Module ...
So you can only call $this->module if your actually using files inside a module.
Since i don't know which path you are trying to go to ill pass you a few variables which you might be able to use.
$this->module->name = crezzurmodulename
$this->module->getLocalPath() = C:\yourstore/modules/crezzurmodulename/
$this->module->getPathUri() = /yourstore/modules/crezzurmodulename/
_MODULE_DIR_ = /yourstore/modules/
_PS_MODULE_DIR_ = C:\yourstore/modules/
__DIR__ = C:\yourstore\modules\crezzurmodulename\controllers\front
_PS_CAT_IMG_DIR_ = C:\yourstore/img/c/
_PS_PROD_IMG_DIR_ = C:\yourstore/img/p/
_PS_TMP_IMG_DIR_ = C:\yourstore/img/tmp/
_PS_ROOT_DIR_ = C:\yourstore
_PS_CACHE_DIR_ = C:\yourstore/var/cache/dev\
_PS_BASE_URL_ = http://127.0.0.1
__PS_BASE_URI__ = /yourstore/
_PS_TRANSLATIONS_DIR_ = C:\yourstore/translations/
_PS_BASE_URL_SSL_ = http://127.0.0.1 or https://127.0.0.1
_PS_DOWNLOAD_DIR_ = C:\yourstore/download/
_PS_COL_IMG_DIR_ = C:\yourstore/img/co/
_PS_SHIP_IMG_DIR_ = C:\yourstore/img/s/
_PS_UPLOAD_DIR_ = C:\yourstore/upload/

Drupal 8: When I update the node field to a specific value, how to call my module (managefinishdate.module) to update another field?

I am having a node type with machine name to_do_item, and I want to create a module called managefinishdate to update the node: when a user edit the node's field (field_status) to "completed" and click "save", then the module will auto update the field_date_finished to current date.
I have tried to create the module and already success to install in "Extend":
function managefinishdate_todolist_update(\Drupal\Core\Entity\EntityInterface $entity) {
...
}
however, I am not sure is this module being called, because whatever I echo inside, seems nothing appeared.
<?php
use Drupal\Core\Entity\EntityInterface;
use Drupal\node\Entity\Node;
/** * Implements hook_ENTITY_TYPE_update().
* If a user update status to Completed,
* update the finished date as save date
*/
function managefinishdate_todolist_update(\Drupal\Core\Entity\EntityInterface $entity) {
$node = \Drupal::routeMatch()->getParameter('node');
print_r($node);
//$entity_type = 'node';
//$bundles = ['to_do_item'];
//$fld_finishdate = 'field_date_finished';
//if ($entity->getEntityTypeId() != $entity_type || !in_array($entity->bundle(), $bundles)) {
//return;
//}
//$current_date=date("Y-m-d");
//$entity->{$fld_finishdate}->setValue($current_date);
}
Following Drupal convention, your module named 'manage_finish_date' should contain a 'manage_finish_date.module file that sits in the root directory that should look like this:
<?php
use Drupal\node\Entity\Node;
/**
* Implements hook_ENTITY_TYPE_insert().
*/
function manage_finish_date_node_insert(Node $node) {
ManageFinishDate::update_time();
}
/**
* Implements hook_ENTITY_TYPE_update().
*/
function manage_finish_date_node_update(Node $node) {
ManageFinishDate::update_time();
}
You should also have another file called 'src/ManageFinishDate.php' that should look like this:
<?php
namespace Drupal\manage_finish_date;
use Drupal;
use Drupal\node\Entity\Node;
class ManageFinishDate {
public static function update_time($node, $action = 'create') {
// Entity bundles to act on
$bundles = array('to_do_item');
if (in_array($node->bundle(), $bundles)) {
// Load the node and update
$status = $node->field_status->value;
$node_to_update = Node::load($node);
if ($status == 'completed') {
$request_time = Drupal::time();
$node_to_update->set('field_date_finished', $request_time);
$node_to_update->save();
}
}
}
}
The code is untested, but should work. Make sure that the module name, and namespace match as well as the class filename and class name match for it to work. Also, clear you cache once uploaded.
This will handle newly created and updated nodes alike.
Please look after this sample code which may help you:
function YOUR_MODULE_entity_presave(Drupal\Core\Entity\EntityInterface $entity) {
switch ($entity->bundle()) {
//Replace CONTENT_TYPE with your actual content type
case 'CONTENT_TYPE':
$node = \Drupal::routeMatch()->getParameter('node');
if ($node instanceof \Drupal\node\NodeInterface) {
// Set the current date
}
break;
}
}

Phalcon mongodb cannot update

I can't update a document with Phalcon MongoCollection from Incubator 3.3
I don't get any error after save() but data are not updated.
My code is:
$category = CategoryModel::findById($id);
$category->title = 'uno';
$category->save();
I have also tried with incubator 3.4 and 3.2
I don't know why but after calling the singleton:
MyModel::findById($id)
...the source collection I defined on the model constructor changes from 'myCustomCollection' to 'my_model'
I fixed it by adding some temporary code inside the _getResultSet method from app/vendor/phalcon/incubator/Library/Phalcon/Mvc/MongoCollection.php (Line 310)
$cursor->setTypeMap(['root' => get_class($base), 'document' => 'array']);
if (true === $unique) {
/**
* Looking for only the first result.
*/
$output = current($cursor->toArray());
$output->setSource($base->getSource());
return $output;
}

symfony setPostValidator with sfValidatorFile

I am going through an issue to setup a file upload validator on callback.
I want to achieve this:
I have a form, where user choose the type of the file they are uploading and upload the file.
So I want to set the validator to handle images in case they chose "img" as type, and pdf if they chose "pdf".
Moreover I want to specify the mime type and path and validatedFileClass according to the type.
I tried this.. but i can't get it to work
$this->validatorSchema->setPostValidator(
new sfValidatorCallback(array('callback' => array($this, 'validateUploadedFiles')))
);
the function:
public function validateUploadedFiles($form_validator, $values){
$this_year = date("Y");
$this_month = date("m");
$basic_validator = array(
'required' => true,
'path' => sfConfig::get('sf_upload_dir').'restaurant/media/'.$this_year.'/'.$this_month.'/'
);
$doc_validator = $video_validator = $img_validator = $pdf_validator = $basic_validator;
$pdf_validator['mime_types'] = array ('application/pdf');
$doc_validator['mime_types'] = array ('application/msword', 'application/vnd.openxmlformats');
$img_validator['mime_types'] = 'web_images';
//$img_validator['validated_file_class'] = 'imgHandling';
$video_validator['mime_types'] = array('video/mpeg', 'video/3gpp', 'video/mp4', 'video/quicktime');
switch( $values['type'] ):
case 'pdf' : $validator = $pdf_validator; break;
case 'img' : $validator = $img_validator; break;
case 'word' : $validator = $doc_validator; break;
case 'video' : $validator = $video_validator; break;
endswitch;
//$form->getValidatorSchema()->offsetUnset('url') ;
//print_r($validator_fields);
$validator = new sfValidatorFile( $validator );
$validator_schema = new sfValidatorSchema();
$validator_schema->offsetSet('url', $validator);
//$validator_fields = $form->getValidatorSchema()->getFields();
$schema = parent::getValidatorSchema();
$schema->offsetUnset('url') ;
$schema->offsetSet('url', $validator);
// $path = sfConfig::get('sf_upload_dir').'restaurant/media/'.$this_year.'/'.$this_month.'/';
// $values['url'] = new sfValidatedFile( $values['url']['name'], $values['url']['type'], $values['url']['tmp_name'], $values['url']['size'] , $path);
//TODO get this url value run through the new added validator
//$values['url'] = $validator_schema->clean(array( 'url' => $values['url'] ));
return $values;
}
the problem i am facing is that , this function receives the url value as array, and even if I update the validators schema, it's not validating the url and keeps on sending it as array to the object saving method.
So how to make something like
url.validateFile() from inside this function
Not sure about the best solution, but I'd prefer to split validation process into 2 parts:
Validate mime type
If mime is ok, then validate URL
Or vise versa, your choice.
The glue would be sfValidatorAnd.
Did I understand you right?