I need ServerFileInput
widget without model and attribute, just to select file on a server or to upload file to the server, then select it. In advance I don't know how much inputs such widgets will be on a page. Therefore, I add temporary field to the model to use with this widget, then plan to clone it with jQuery.
<?php
class MyModel extends CActiveRecord
{
public $my_attachment; // used only to generate elFinder.ServerFileInput widget
// ...
}
Then configure connector settings in a controller:
<?php
class MyController extends Controller
{
public function accessRules()
{
return [
[
'allow',
'actions' => [ 'elFinderConnector', ],
'roles' => [ 'admin', 'authorizedUser', ],
],
[
'deny',
'users' => [ '*' ],
],
];
}
public function actionElFinderConnector()
{
// each user has own path named with ID, e.g.: /uploads/users/190/ for user with ID 190
$userPath = Yii::app()->params[ 'userUploadsFolder' ] . Yii::app()->user->id . '/';
$userRoot = Yii::getPathOfAlias( 'webroot' ) . $userPath; // full path to the user's folder
// if directory doesn't exist, create them
if ( !file_exists( $userRoot ) )
mkdir( $userRoot, 0777, true );
// initialize ElFinder
$connector = new ElFinderConnectorAction( $this, $this->action->id );
$connector->settings[ 'root' ] = $userRoot;
$connector->settings[ 'URL' ] = Yii::app()->baseUrl . $userPath;
$connector->settings[ 'rootAlias' ] = 'Home';
$connector->settings[ 'uploadAllow' ] = [ 'application/pdf' ]; // allow only PDF mime type
$connector->settings[ 'uploadDeny' ] = [ 'all' ]; // deny all other mime types
$connector->run();
}
}
In view I prepare container for attachments and template widget to clone later via JavaScript:
<?php
/* @var MyController $this */
/* @var MyModel $model */
?>
<div class="attachments-container">
<!-- here will be many ServerFileInput widgets later -->
</div>
<div class="attachment-template" style="display: none;">
<?php
$this->widget( 'ext.elfinder.ServerFileInput', [
'model' => $model,
'attribute' => 'my_attachment',
'connectorRoute' => 'myModule/myController/elFinderConnector',
'twbsControls' => true, // https://gist.github.com/umidjons/9525300
] );
/* Do not forget change this line in javascript code, if you change above settings!!!
* window.elfinderBrowse("MyModel_my_attachment", '/myModule/myController/elFinderConnector');
* */
?>
</div>
I prepare function to generate new attachment widgets. In real world applications you would generate such widgets with button clicks.
jQuery(document).ready(function($){
/**
* Generates new attachment widget.
* @param {int} idx index of attachment
*/
function newAttachment(idx){
// clone from template and show widget
var attachment=$(".attachment-template").clone(true)
.removeClass("attachment-template").addClass(".attachment")
.appendTo(".attachments-container").show();
// change name, id and value attributes
attachment.children("div").attr({name: "Attachments["+idx+"][attachment]", id: "Attachments_"+idx+"_attachmentcontainer"});
attachment.find("input[type=text]").attr({name: "Attachments["+idx+"][attachment]", id: "Attachments_"+idx+"_attachment", value: ""});
}
// generate attachments
for(var i=0; i<3; i++)
newAttachment(i);
});
When user chooses a file actual URL of the chosen file written into template's input field. So we need to handle this, and copy URL into our input value.
First, I will remember current input ID as body's property:
$("body").on("click", ".attachment input[type=button]", function(){ // save elFinder input field id as body's property
$("body").prop("elfinderInput", $(this).closest("div").find("input[type=text]")[0].id);
// open file browser
window.elfinderBrowse("MyModel_my_attachment", '/myModule/myController/elFinderConnector');
});
Default ServerFileInput
implementation do not handle file select event. So we need some hack to catch file selection, also selected URL. Open ServerFileInput.php
, then change this line:
<?php
$settings[ 'editorCallback' ] = 'js:function(url) {
$(\'#\'+aFieldId).attr(\'value\',url);
}';
with this:
<?php
$settings[ 'editorCallback' ] = 'js:function(url) {
$(\'#\'+aFieldId).attr(\'value\',url);
$("body").trigger("elfinderFileSelected", url); // trigger custom event, also send URL to that event as parameter
}';
Now we will catch elfinderFileSelected
event and write selected URL from template's input value into current input value, which ID holded in body as a property:
$("body").on("elfinderFileSelected", function(event, url){ // set elFinder input value to URL of selected file
$("#"+$("body").prop("elfinderInput")).val(url);
});
Now all things are ready to play, just handle submit request on the controller to do something with attachments, for example save their URLs in a DB:
<?php
class MyController extends Controller
{
public function actionManageAttachments()
{
if ( isset( $_POST[ 'Attachments' ] ) )
{
foreach ( $_POST[ 'Attachments' ] as $idx => $attachment )
{
$attachment_model = new MyAttachmentModel();
$attachment_model->attributes = $attachment;
$attachment_model->save();
}
}
}
}