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:
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.
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
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.
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.
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
$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.
Total 5 comments
I have been trying to insert the Xupload extension into CActiveform but I am not able to submit the form.
any problem with that?
thank you very much, I'm glad to see that this is actually being useful to people.
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 :)
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
maybe you'd better replace {% with the more standard <?php ?> tags?
Leave a comment
Please login to leave your comment.