Additional form data with XUpload

  1. Duplicate and edit the upload form template
  2. Duplicate and edit the download form template
  3. Create a controller action
  4. 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:

  1. Duplicate and edit the upload form template, so we can render the fields for the additional data
  2. Duplicate and edit the download form template, so we can render the additioanl data
  3. Create a controller action that will handle the file upload, and the additional data.
  4. 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.

7 0
13 followers
Viewed: 47 227 times
Version: 1.1
Category: How-tos
Written by: Asgaroth
Last updated by: Asgaroth
Created on: Sep 30, 2012
Last updated: 11 years ago
Update Article

Revisions

View all history

Related Articles