- Duplicate and edit the upload form template
- Duplicate and edit the download form template
- Create a controller action
- Adjust the widget, and add javascript
In this article you'll learn how to send additional form data when uploading files using XUpload widget
This are the steps that we need:
- Duplicate and edit the upload form template, so we can render the fields for the additional data
- Duplicate and edit the download form template, so we can render the additioanl data
- Create a controller action that will handle the file upload, and the additional data.
- Adjust the widget in our view, and add the corresponding javascript
Before getting started, note that we will be using a custom model named Image, which will hold all the information submitted by the widget. in our case the additional attributes defined in this model are title and description.
Duplicate and edit the upload form template ¶
We need our own version of the upload template, so we can add the new fields for the additional data that we want to send along with the file. to do that, copy the upload.php template included with the plugin, to your controller's views folder.
Assuming a default installation, the path to the template should be:
protected/extensions/xupload/views/upload.php
Assuming your controller its SiteController, your copy of the template should go to:
protected/views/site/upload.php
To add our new fields, add a new column ( td
) to the template, so it looks like this:
<!-- The template to display files available for upload -->
<script id="template-upload" type="text/x-tmpl">
{% for (var i=0, file; file=o.files[i]; i++) { %}
<tr class="template-upload fade">
<td class="preview">
<span class="fade"></span>
</td>
<td class="name">
<span>{%=file.name%}</span>
</td>
<td>//This is the new column, and below we add 2 new fields
<label>Title</label>
<input type="text" name="title" required /><br />
<label>Description</label>
<textarea name="description" rows="3" required ></textarea><br />
</td>
<td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
{% if (file.error) { %}
<td class="error" colspan="2"><span class="label label-important">{%=locale.fileupload.error%}</span> {%=locale.fileupload.errors[file.error] || file.error%}</td>
{% } else if (o.files.valid && !i) { %}
<td>
<div class="progress progress-success progress-striped active"><div class="bar" style="width:0%;"></div></div>
</td>
<td class="start">{% if (!o.options.autoUpload) { %}
<button class="btn btn-primary">
<i class="icon-upload icon-white"></i>
<span>{%=locale.fileupload.start%}</span>
</button>
{% } %}</td>
{% } else { %}
<td colspan="2"></td>
{% } %}
<td class="cancel">{% if (!i) { %}
<button class="btn btn-warning">
<i class="icon-ban-circle icon-white"></i>
<span>{%=locale.fileupload.cancel%}</span>
</button>
{% } %}</td>
</tr>
{% } %}
</script>
We added to new fields to our template - title and description - the rest of the template remains the same
Duplicate and edit the download form template ¶
To show the info in those fields after the files have been uploaded, we need to adjust our download template.
Assuming a default installation, the path to the template should be:
protected/extensions/xupload/views/download.php
Assuming your controller its SiteController, your copy of the template should go to:
protected/views/site/download.php
Now, the server returns a Json with the info of the uploaded file, in the following step we will create the action in the controller that returns that json including the additional fields. but for now, lets assume they are there so we can edit our template acordingly.
<!-- The template to display files available for download -->
<script id="template-download" type="text/x-tmpl">
{% for (var i=0, file; file=o.files[i]; i++) { %}
<tr class="template-download fade">
{% if (file.error) { %}
<td></td>
<td class="name"><span>{%=file.name%}</span></td>
<td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
<td class="error" colspan="2"><span class="label label-important">{%=locale.fileupload.error%}</span> {%=locale.fileupload.errors[file.error] || file.error%}</td>
{% } else { %}
<td class="preview">{% if (file.thumbnail_url) { %}
<a href="{%=file.url%}" title="{%=file.name%}" rel="gallery" download="{%=file.name%}"><img src="{%=file.thumbnail_url%}"></a>
{% } %}</td>
<td class="name">
<a href="{%=file.url%}" title="{%=file.name%}" rel="{%=file.thumbnail_url&&'gallery'%}" download="{%=file.name%}">{%=file.name%}</a>
</td>
<td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
<td>{%=file.title%}</td>//Here we show the file title
<td>{%=file.description%}</td>//and the file description
{% } %}
<td class="delete">
<button class="btn btn-danger" data-type="{%=file.delete_type%}" data-url="{%=file.delete_url%}">
<i class="icon-trash icon-white"></i>
<span>{%=locale.fileupload.destroy%}</span>
</button>
<input type="checkbox" name="delete" value="1">
</td>
</tr>
{% } %}
</script>
We only added the title and the description of the file to the template, nothing else was changed.
Create a controller action ¶
Now we need a controller actions that will not only handle the file upload, but also initialize our model's attributes and return the json with the additional data.
The code here is just a copy of the contents of the XUploadAction, so it doesn't save anything to the database, or does anything fancy.
public function actionUploadAdditional(){
header( 'Vary: Accept' );
if( isset( $_SERVER['HTTP_ACCEPT'] ) && (strpos( $_SERVER['HTTP_ACCEPT'], 'application/json' ) !== false) ) {
header( 'Content-type: application/json' );
} else {
header( 'Content-type: text/plain' );
}
if( isset( $_GET["_method"] ) ) {
if( $_GET["_method"] == "delete" ) {
$success = is_file( $_GET["file"] ) && $_GET["file"][0] !== '.' && unlink( $_GET["file"] );
echo json_encode( $success );
}
} else {
$this->init( );
$model = new Image;//Here we instantiate our model
//We get the uploaded instance
$model->file = CUploadedFile::getInstance( $model, 'file' );
if( $model->file !== null ) {
$model->mime_type = $model->file->getType( );
$model->size = $model->file->getSize( );
$model->name = $model->file->getName( );
//Initialize the ddditional Fields, note that we retrieve the
//fields as if they were in a normal $_POST array
$model->title = Yii::app()->request->getPost('title', '');
$model->description = Yii::app()->request->getPost('description', '');
if( $model->validate( ) ) {
$path = Yii::app() -> getBasePath() . "/../images/uploads";
$publicPath = Yii::app()->getBaseUrl()."/images/uploads";
if( !is_dir( $path ) ) {
mkdir( $path, 0777, true );
chmod ( $path , 0777 );
}
$model->file->saveAs( $path.$model->name );
chmod( $path.$model->name, 0777 );
//Now we return our json
echo json_encode( array( array(
"name" => $model->name,
"type" => $model->mime_type,
"size" => $model->size,
//Add the title
"title" => $model->title,
//And the description
"description" => $model->description,
"url" => $publicPath.$model->name,
"thumbnail_url" => $publicPath.$model->name,
"delete_url" => $this->createUrl( "upload", array(
"_method" => "delete",
"file" => $path.$model->name
) ),
"delete_type" => "POST"
) ) );
} else {
echo json_encode( array( array( "error" => $model->getErrors( 'file' ), ) ) );
Yii::log( "XUploadAction: ".CVarDumper::dumpAsString( $model->getErrors( ) ), CLogger::LEVEL_ERROR, "xupload.actions.XUploadAction" );
}
} else {
throw new CHttpException( 500, "Could not upload file" );
}
}
}
Thats all we need to get the additional fields we sent from out widget, saving to the database or changing the folders where you wish your files to be saved, or generating thumnails, etc. its up to you, you can check the XUpload workflow wiki for examples on that.
Adjust the widget, and add javascript ¶
Now that we have everything setup, we just need to configure our widget to use our templates, and point to our action. also we need some additional javascript as explained in the original jquery plugin wiki
<?php
$this->widget('xupload.XUpload', array(
'url' => Yii::app()->createUrl("site/uploadAdditional", array("parent_id" => 1)),
'model' => $model,//An instance of our model
'attribute' => 'file',
'multiple' => true,
//Our custom upload template
'uploadView' => 'application.views.site.upload',
//our custom download template
'downloadView' => 'application.views.site.download',
'options' => array(//Additional javascript options
//This is the submit callback that will gather
//the additional data corresponding to the current file
'submit' => "js:function (e, data) {
var inputs = data.context.find(':input');
data.formData = inputs.serializeArray();
return true;
}"
),
));
?>
Thats it, if you need additional help don't hesitate asking in the official forum thread.
php syntax?
maybe you'd better replace {% with the more standard <?php ?> tags?
@Maxxer
this is not php code, its javascript code, you'll notice as it is wrapped in <script> tags.
The original jquery plugin uses Javacript Templates as its templating engine. this has nothing to do with PHP.
If you want to change the javascript templating engine you can take a look this article
just want to say thanks!
thank you very much Asgaroth!
that plugin, wrapped in your extension, supported by your workflow wiki and now this one, all beside your great and kind responses in forum, made such a great resource and tool for everyone who is interested in ajax uploading.
I have so much respect for you :)
@Hesam Khaki
thank you very much, I'm glad to see that this is actually being useful to people.
xupload in CActiveform
I have been trying to insert the Xupload extension into CActiveform but I am not able to submit the form.
<?php $form = $this->beginWidget('CActiveForm', array( 'id' => 'form-form', 'enableAjaxValidation' => false, //This is very important when uploading files 'htmlOptions' => array('enctype' => 'multipart/form-data'), )); ?> <div class="row"> <?php echo $form->labelEx($model,'username'); ?> <?php echo $form->textField($model,'username'); ?> <?php echo $form->error($model,'username'); ?> </div> <!-- Other Fields... --> <div class="row"> <?php echo $form->labelEx($model,'attachment_photo'); ?> <?php $this->widget('xupload.XUpload', array( 'url' => Yii::app()->createUrl("form/upload", array("parent_id" => 1)), 'model' => $photos,//An instance of our model 'attribute' => 'file', 'multiple' => false, 'htmlOptions' => array('id'=>'form-form'), //Our custom upload template 'uploadView' => 'application.views.site.upload', //our custom download template 'downloadView' => 'application.views.site.download', 'options' => array(//Additional javascript options //This is the submit callback that will gather //the additional data corresponding to the current file 'submit' => "js:function (e, data) { var inputs = data.context.find(':input'); data.formData = inputs.serializeArray(); return true; }" ), )); ?> </div> <button type="submit">Submit</button> <?php $this->endWidget(); ?>
any problem with that?
@vincentlkl xupload in CActiveform
@vincentlkl add this option to your widget 'xupload.XUpload'
'showForm' => false,
Add id to widget
Don't forget add id to widget via htmlOptions:
<?php $this->widget('xupload.XUpload', array( /* other attributes */ 'htmlOptions' => array('id'=>'somemodel-form'), )); ?>
Without it appending new file to form after choosing won't work.
Please change it in last example.
If you have any questions, please ask in the forum instead.
Signup or Login in order to comment.