cakephp - Save file syncronized and send as attachment - cakephp

Hi have a strange problem with File::write() in cakephp.
I write this code to generate a pdf view and save it on a file.
private function generate( $id ){
$order = $this->Order->read( null, $id );
$this->set('order', $order);
$view = new View($this);
$viewdata = $view->render('pdf');
//set the file name to save the View's output
$path = WWW_ROOT . 'ordini/' . $id . '.pdf';
$file = new File($path, true);
//write the content to the file
$file->write( $viewdata );
//return the path
return $path;
}
I need generate this pdf and send it as attacchment so, this is my code
public function publish ($id) {
$pdf_file = $this->generate( (int)$id );
$Email = new CakeEmail();
$Email->from(array('info#.....com' => '......'));
$Email->to('....#gmail.com');
$Email->subject('Nuovo ordine');
$filepath = WWW_ROOT . 'ordini/' . $id . '.pdf';
$Email->attachments(array('Ordine.pdf' => $filepath));
$Email->send('......');
}
The first time i run this code the email doesn't works, the email works nice only if the pdf is alredy in the folder.
I think that File::write perform some kind of async writing and when system execute Email::attachments the file ins't ready.
How can i insert a while() in this code to check file avability?
thanks a lot.

if($file->write( $viewdata ))
{
return $path;
}

using the return value wasn't enough.
I changed this line
$file = new File($path, false);
Opening file without forcing worked for me.

Related

Upload multiple pictures renaming files in Codeigniter 3

I'm not able to figure out what's wrong in my code.
In my project the user is allowed to upload multiple pictures for each aircraft registration number; I want to rename each uploaded files with the following rules: registration id, minus sign, progressive number; so if the user upload a new image file for the registration id xxx, the uploaded filename becomes xxx-1.jpg
The code to upload the multiple files is the following; it works fine so far...
// Count uploaded files
$countfiles = count($_FILES['files']['name']);
// Define new image name
$image = $id . '-1.jpg';
for($i=0;$i<$countfiles;$i++)
{
if(!empty($_FILES['files']['name'][$i]))
{
// Define new $_FILES array - $_FILES['file']
$_FILES['file']['name'] = $_FILES['files']['name'][$i];
$_FILES['file']['type'] = $_FILES['files']['type'][$i];
$_FILES['file']['tmp_name'] = $_FILES['files']['tmp_name'][$i];
$_FILES['file']['error'] = $_FILES['files']['error'][$i];
$_FILES['file']['size'] = $_FILES['files']['size'][$i];
// Check if the image file exist and modify name in case
$filename = $this->_file_newname($uploaddir,$image);
// Set preference
$config['upload_path'] = $upload_path;
$config['allowed_types'] = 'jpg|jpeg|png|gif';
$config['max_size'] = '5000';
$config['file_name'] = $filename;
//Load upload library
$this->load->library('upload',$config);
$arr = array('msg' => 'something went wrong', 'success' => false);
// File upload
if($this->upload->do_upload('file'))
{
$data = $this->upload->data();
}
}
}
The _file_newname() function does the file renaming jog, here you are the code:
private function _file_newname($path, $filename)
{
if ($pos = strrpos($filename, '.')) {
$name = substr($filename, 0, $pos);
$ext = substr($filename, $pos);
} else {
$name = $filename;
}
$newpath = $path.$filename;
$newname = $filename;
if(file_exists($newpath)){
$counter = 1;
if($pos = strrpos($name, '-')) {
$oldcounter = substr($name, $pos + 1);
if(is_numeric($oldcounter)){
$counter = $oldcounter++;
$name = substr($name, 0, $pos);
}
}
$newname = $name . '-' . $counter . $ext;
$newpath = $path . $newname;
while (file_exists($newpath)) {
$newname = $name .'-'. $counter . $ext;
$newpath = $path.$newname;
$counter++;
}
}
return $newname;
}
Now...the issue....the renaming function works fine if the user upload one file each time...so the first upload set the file name xxx-1.jpg, the second upload set the filename to xxx-2.jpg and so on....but....if the user upload more then one file at time...the second file become xxx-1x.jpg.
If already exists one file on server ( for example xxx-1.jpg ) and the user upload two more files..they are renamed as xxx-2.jpg ( correct) and xxx-21.jpg (wrong...should be xxx-3.jpg).
Any hint or suggestion to fix the issue?
Thanks a lot
Your new file name is out of the loop for the uploaded photos.
So it becomes static.
Put that line inside the loop:
// Define new image name
$image = $id . '-' . $i . '.jpg';
This way you are gonna keep the $id and rename the files according to the iteration of the loop - $i.

JoomGallery.net | image ordering asc

i would like to use this simple image slider for the joomgallery for my art students:
https://github.com/danielhpavey/joomgallery-slider
the only problem is the ordering. how do i get an ascending image ordering by id and not by filename ?
thanks peter
<?php
class images
{
public function __construct()
{
$file = JPATH_ROOT. '/components/com_joomgallery/interface.php';
if(!file_exists($file)){
JError::raiseError(500, 'JoomGallery seems not to be installed');
} else {
require_once $file;
$this ->interface = new JoomInterface();
}
}
public function getFirstImage()
{
$images = $this ->talkToJoomgallery();
return $images[0];
}
public function getImages()
{
$images = $this ->talkToJoomgallery();
return $images;
}
public function talkToJoomgallery()
{
$images = $this ->interface ->getPicsByCategory( $this ->categoryid );
$imagepath = $this ->joomgalleryImagePath();
$theimages = array();
$c = 0;
foreach ($images as $i){
$theimages[$c]= array(
'imgpath' => JURI::base() . $imagepath . $i->catpath . '/' . $i->imgfilename
,'imgtitle' => $i->imgtitle
,'imgtext' => $i->imgtext
);
$c ++;
}
shuffle($theimages);
return $theimages;
}
private function joomgalleryImagePath()
{
return $this ->interface ->getJConfig( 'jg_pathoriginalimages' );
}
public function __set($property, $value){
$this->$property = $value;
}
}
Your images are loaded according to image id only but this command is reindexing the array i.e shuffle($theimages);
You can comment out that line by
//shuffle($theimages)
Also for ordering images you can change this line in helper.php file
$images = $this->interface->getPicsByCategory($this->categoryid);
to
$images = $this->interface->getPicsByCategory($this->categoryid,null,'ordering' );
This will do the ordering of images the way you did drag and drop of images at the joomla administrator backend.
UPDATE AS PER YOUR LATEST QUERY
Suppose you want to add a param value (sorting) that can be controlled through admin. You ned to change the xml file mod_joomgallery_slider.xml
Just add a new field like this
<field
name = "sorting"
type = "radio"
label = "Sorting"
description = "Sort by Ordering or random"
default = "ordering"
>
<option value = "ordering">Ordering</option>
<option value = "rand()">Random</option>
</field>
Next to get the param in helper.php file then change the function talkToJoomgallery() like this
public function talkToJoomgallery()
{
//Externally calling a module param
jimport( 'joomla.html.parameter' );
jimport( 'joomla.application.module.helper' );
$module = JModuleHelper::getModule('mod_joomgallery_slider');
$moduleParams = new JRegistry();
$moduleParams->loadString($module->params);
$sorting = $moduleParams->get( 'sorting' );
$images = $this ->interface ->getPicsByCategory( $this ->categoryid,null,$sorting );
$imagepath = $this ->joomgalleryImagePath();
$theimages = array();
$c = 0;
foreach ($images as $i){
$theimages[$c]= array(
'imgpath' => JURI::base() . $imagepath . $i->catpath . '/' . $i->imgfilename
,'imgtitle' => $i->imgtitle
,'imgtext' => $i->imgtext
);
$c ++;
}
//var_dump($theimages); exit;
//shuffle($theimages);
return $theimages;
}
UPDATE: To display 2 slider modules on a single page.
Some files needs to changed:
In mod_joomgallery_slider.php file change this line at top
include('helper.php');
To
include_once('helper.php');
This ensures that the file is included once.
Another change will be to remove the function imageText in default.php and including it in helper.php class else a function redeclaration error will be thrown. But now the default.php file still give error, as function imageText will be not defined, but you already added that function to helper.php. So default.php will only work if you change
echo imageText( $i, $params );
To
echo $image->imageText( $i, $params );// You are calling helper object
Remember to change in both if and else conditions.

Find all in CakePHP is dumping a semicolon in my view

I don't know what exactly is happening with my CakePHP app. It worked last week and I have literally changed nothing with that particular file.
When I use find 'all' in my controller it dumps a semi-colon in my view even if there is nothing in the view file.
Here is my code
$evts = $this->Event->find('all');
There is nothing in my view file. I don't know if it makes a difference but I'm using a json view.
As requested here is the complete code for the action
public function search(){
$this->Event->recursive = 2;
$conditions = array();
if(!empty($this->request->query['name'])){
$conditions = array('Event.name LIKE ' => "%" . str_replace(" ","%", $this->request->query['name']) . "%");
}
if(!empty($this->request->query['home'])){
$conditions = array('Event.home_team_id' => $this->request->query['home']);
}
if(!empty($this->request->query['away'])){
$conditions = array('Event.away_team_id' => $this->request->query['away']);
}
$limit = 25;
if(!empty($this->request->query['limit'])){
$limit = $this->request->query['limit'];
}
//$evts = $this->Event->find('all',array('conditions'=>array($conditions),'order' => array('Event.start_time'),'limit'=>$limit));
$evts = $this->Event->find('all');
$this->set('events',$evts);
}
Everything in the view has been commented out ... but here is the code anyway
$results = array();
$i = 0;
foreach ($events as $event) {
$results[$i]['id'] = $event['Event']['id'];
$results[$i]['label'] = $event['Event']['name'] . "(" . date_format(date_create($event['Event']['start_time']), 'D, jS M Y') . ")";
$results[$i]['value'] = $event['Event']['name'];
$results[$i]['home_team_name'] = $event['HomeTeam']['name'];
$results[$i]['away_team_name'] = $event['AwayTeam']['name'];
$results[$i]['sport_name'] = $event['Sport']['name'];
$results[$i]['tournament_name'] = $event['Tournament']['name'];
$results[$i]['start_time'] = $event['Event']['start_time'];
$results[$i]['img'] = $event['Event']['img_path'];
$results[$i]['listener_count'] = 0; //TODO Get the follower count
$i++;
}
echo json_encode($results);
Display
If you changed nothing with that particular file then perhaps you have installed callbacks (either in the controller or in the model) that echo that ";". Look for 'afterFind' calls...
Search your model and your controller folders for "echo statement". In fact you should search your entire app folder except for the Views folder.

upload a file in cakePHP

Hi I want to upload a file using cake php, i got the file and i am using
move_uploaded_file() to move to a specific location but it is not moving my simple logic is
shown below
if (move_uploaded_file($this->data['Add']['upload']['tmp_name'], APP . 'views' . DS .
'static' . DS.'uploads'.DS.'Rajaram'.DS )) {
LogUtil::$logger->debug('KMP File upload Url :
'.var_export($this->data, true));
}
Thanks in Advance.
File uploading is something CakePHP doesn’t do out of the box, which is one of the only thing that annoys me about the framework.
I tackled this by adding file handling to a model using callback methods. I upload the actual file with beforeSave(), and delete the file from the file system with beforeDelete(). A sample model looks like this:
<?php
App::uses('File', 'Utility');
class Image extends AppModel {
public $name = 'Image';
public function beforeSave($options = array()) {
$fieldName = 'filename';
$field = $this->data[$this->alias][$fieldName];
if (!is_array($field)) {
$this->validationErrors[$fieldName][] = 'No file detected';
return false;
}
switch ($field['error']) {
case UPLOAD_ERR_OK:
$newFilename = time() . '.jpg';
$uploadDir = WWW_ROOT . 'files/';
$source = $field['tmp_name'];
$destination = $uploadDir . $newFilename;
if (move_uploaded_file($source, $destination)) {
$this->data[$this->alias][$fieldName] = $newFilename;
return true;
}
else {
$this->validationErrors[$fieldName][] = 'No file detected';
return false;
}
break;
default:
$this->validationErrors[$fieldName][] = 'No file detected';
return false;
break;
}
}
public function beforeDelete($cascade = true) {
$image = $this->findById($this->id);
$file = new File(WWW_ROOT . 'files/' . $image['Image']['filename']);
return $file->delete();
}
}
Obviously this isn’t a perfect implementation, so feel free to take from it, learn from it, adapt it.
This was written off-the-cuff for a project recently where there’s only one model that has images attached, but on a larger project I’d more than likely wrap it up into a nice model behavior.
In your model you can use the afterSave callback method to handle your file upload:-
public function afterSave($created) {
if (isset($_FILES['data']['name'][$this->alias]['filename'])) {
$filename = $_FILES['data']['name'][$this->alias]['filename'];
$fileInfo = pathinfo($filename);
$fileExt = isset($fileInfo['extension']) ? $fileInfo['extension'] : '';
$filename = $fileInfo['filename'];
$newFilename = "$filename.$fileExt";
$dir = WWW_ROOT . 'files' . DS . 'uploads';
$target = $dir . DS . $newFilename;
move_uploaded_file($source, $target);
}
}
You can use $newFilename to change the filename to something appropriate if need be (I tend to check if a file with the same name already exists and rename the new one to avoid overwriting it.

Model Callback beforeDelete

I'm trying to delete images when deleting the container of those images with a cascading model::delete
The cascading works fine, but I can't get the model call back afterDelete to work properly so I can delete the actual image files when doing the delete.
function beforeDelete() {
$containerId = $this->id;
$numberOfImages = $this->RelatedImage->find('count', array('conditions' => array('RelatedImage.container_id' => 'containerId')));
if ($numberOfImages > 0){
$relatedImages = $this->RelatedImage->find('all', array('conditions' => array('RelatedImage.container_id' => 'containerId')));
foreach ($relatedImages as $image) {
$myFile = WWW_ROOT . 'image' . $containerId . '_i' . $image['RelatedImage']['id'] . '.jpg';
unlink($myFile);
$myThumb = WWW_ROOT . 'img/' . $image['RelatedImage']['thumbnail'];
unlink($myThumb);
}
return true;
} else{
return false;
}
}
The if statement fails each time, even though I know there are images in the table. If I can get the if statement to at least execute i will add further validation on the unlink.
I would do it in this way:
in beforeDelete get the images data
function beforeDelete(){
$relatedImages = $this->RelatedImage->find('all', array('conditions' => array('RelatedImage.container_id' => 'containerId')));
$this->relatedImages = $relatedImages;
$this->currentId = $this->id; //I am not sure if this is necessary
return true;
}
then in the afterDelete() as Oscar suggest do the actual delete of the image:
function afterDelete(){
$relatedImages = $this->relatedImages;
$containerId = $this->currentId; //probably this could be just $this->id;
foreach ($relatedImages as $image) {
$myFile = WWW_ROOT . 'image' . $containerId . '_i' . $image['RelatedImage']['id'] . '.jpg';
unlink($myFile);
$myThumb = WWW_ROOT . 'img/' . $image['RelatedImage']['thumbnail'];
unlink($myThumb);
}
}
this way you are save, even if the model fail to delete the record you will delete images only if the delete was actually happen.
HTH
If the models are related with hasMany/hasOne, both RelatedImage::beforeDelete() and RelatedImage::afterDelete() should be called when they are removed. Try putting the delete logic there instead?

Resources