Commits

Joachim Werner committed 7461b95

Initial commit

  • Participants

Comments (0)

Files changed (19)

+# generic files to ignore
+# backup files (*~) and vim swap file (.swp), MacOS dir file (.DS_Store)
+*~
+*.lock
+*.DS_Store
+*.swp
+*.swo
+*.out
+moveItem.txt
+assets/*
+protected/config/db.ini
+protected/config/main-local.php
+protected/config/console-local.php
+protected/data/users/*
+lib/PEAR*
+protected/runtime/*
+# Directories
+files/*/
+runtime/gii-*
+runtime/CSS/
+runtime/HTML/
+runtime/URI/
+protected/extensions/less/lib/lessphp/test/
+# Dont't ignore:
+!.yii
+!.gitignore
+!.gitkeep
+Copyright (c) 2010, Christoffer Niska
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright
+      notice, this list of conditions and the following disclaimer in the
+      documentation and/or other materials provided with the distribution.
+    * Neither the name of the Christoffer Niska nor the
+      names of its contributors may be used to endorse or promote products
+      derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY
+DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

File MediaModule.php

+<?php
+/**
+ * MediaModule class file.
+ * @author Joachim Werner <joachim.werner@diggin-data.de>
+ * @copyright Copyright &copy; Joachim Werner 2013-
+ * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
+ * @package media
+ */
+
+/**
+ * Web module for managing media (files and folders).
+ */
+class MediaModule extends CWebModule
+{
+    // {{{ *** Members ***
+    public $logTag = 'application.modules.media';
+
+    public $baseDir;
+    // }}} 
+    // {{{ *** Methods ***
+    // {{{ init
+	public function init()
+    {
+        Yii::log("Media Module init", 'info',$this->logTag);
+		// this method is called when the module is being created
+		// you may place code here to customize the module or the application
+
+		// import the module-level models and components
+		$this->setImport(array(
+			'media.models.*',
+			'media.components.*',
+        ));
+        //register translation messages from module dbadmin
+        //so no need do add to config/main.php
+        Yii::app()->setComponents(
+            array('messages' => array(
+                    'class'=>'CPhpMessageSource',
+                    'basePath'=>'protected/modules/media/messages',
+        )));
+        
+	} // }}} 
+    // {{{ beforeControllerAction
+	public function beforeControllerAction($controller, $action)
+	{
+		if(parent::beforeControllerAction($controller, $action))
+		{
+			// this method is called before any module controller action is performed
+			// you may place customized code here
+			return true;
+		}
+		else
+			return false;
+    } // }}} 
+    // }}} End Methods
+}
+ddmedia
+=======
+
+Media is a module for the [Yii PHP framework](http://www.yiiframework.com) that provides a web user interface to manage files and folders. 
+
+Supported tasks so far:
+
+* Browse folders
+* Create new directory
+* Upload a file
+* Rename, move, delete files or folders
+
+# Setup
+
+Download the latest release from [Yii extensions](http://www.yiiframework.com/extension/ddmedia).
+
+Unzip the module under ***protected/modules/media*** and add the following to your application config:
+
+~~~
+[php]
+return array(
+    ...
+    'media'=>array(
+        // Base dir for media browser (app/files):
+        'baseDir'=>dirname(__FILE__).DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.'files',
+    ),
+    ...
+);
+~~~
+***protected/config/main.php***
+
+Please note that the module doesn't require you to use a database.

File config/messages.php

+<?php
+// This is hopefully a config array for the messages
+return array(
+    'sourcePath' => dirname(__FILE__) . DIRECTORY_SEPARATOR . '..',  //root dir of all source
+    'messagePath' => dirname(__FILE__) . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'messages',  //root dir of message translations
+    'languages'  => array('de',),  //array of lang codes to translate to, e.g. es_mx
+    'fileTypes' => array('php','js',), //array of extensions no dot all others excluded
+    'exclude' => array( //list of paths or files to exclude
+        '.svn',
+        '.git',
+        'yiic',
+        'yiic.php',
+        '/extensions/bootstrap/gii/bootstrap/templates',
+        '/extensions/JMSyntaxHighlighter',
+        '/extensions/jqplot',
+        '/extensions/simpleWorkflow',
+        '/gii',
+        '/messages',
+        '/vendors'
+    ),  
+    //'translator' => 'Yii:t',  //this is the default but lets be complete
+);
+?>

File controllers/DefaultController.php

+<?php
+/**
+ * DefaultController class file.
+ * @author Joachim Werner <joachim.werner@diggin-data.de>
+ * @copyright Copyright &copy; Joachim Werner 2013-
+ * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
+ * @package media.controllers
+ */
+
+/**
+ * Controller for managing file/folder related actions.
+ */
+class DefaultController extends Controller
+{
+    // {{{ *** Members ***
+    public $layout = '//layouts/column2';
+    // }}} 
+    // {{{ *** Methods ***
+    // {{{ actionIndex
+	public function actionIndex()
+    {
+        $msg = '';
+        $mediaAction = new DDMediaAction;
+        if(isset($_POST['DDMediaAction'])) {
+            $mediaAction->attributes = $_POST['DDMediaAction'];
+            $mediaAction->scenario = $mediaAction->action;
+            // DEBUG var_dump($mediaAction->attributes);
+            if($mediaAction->validate()) {
+                $result = $mediaAction->doAction();
+                if($result==true) {
+                    $mediaAction->mediaType = $mediaAction->mediaType=='directory' ? Yii::t('main', 'directory') : Yii::t('main', 'file');
+                    switch($mediaAction->action)
+                    {
+                        case 'rename':
+                            Yii::app()->user->setFlash('success',Yii::t('main','The {mediaType} <em>{name}</em> has been renamed to <em>{p1}</em>.',array('{mediaType}'=>$mediaAction->mediaType,'{name}'=>$mediaAction->oldName,'{p1}'=>$mediaAction->p1)));
+                            break;
+                        case 'copy':
+                            Yii::app()->user->setFlash('success',Yii::t('main','The {mediaType} <em>{name}</em> has been copied to <em>{p1}</em>.',array('{mediaType}'=>$mediaAction->mediaType,'{name}'=>$mediaAction->oldName,'{p1}'=>$mediaAction->p1)));
+                            break;
+                        case 'delete':
+                            Yii::app()->user->setFlash('success',Yii::t('main','The {mediaType} <em>{name}</em> has been deleted.',array('{mediaType}'=>$mediaAction->mediaType,'{name}'=>$mediaAction->oldName)));
+                            break;
+                        case 'move':
+                            Yii::app()->user->setFlash('success',Yii::t('main','The {mediaType} <em>{name}</em> has been moved to <em>{path}/{p1}</em>.',array(
+                                '{mediaType}'=>$mediaAction->mediaType,
+                                '{name}'=>$mediaAction->oldName,
+                                '{path}'=>$mediaAction->path,
+                                '{p1}'=>$mediaAction->p1)));
+                            break;
+                        case 'upload':
+                            Yii::app()->user->setFlash('success',Yii::t('main','The file <em>{oldName}</em> has been uploaded as <em>{name}</em>.',array('{name}'=>$mediaAction->name, '{oldName}'=>$mediaAction->oldName)));
+                            break;
+                        case 'newdir':
+                            Yii::app()->user->setFlash('success',Yii::t('main','The directory <em>{p1}</em> has been created.',array('{p1}'=>$mediaAction->p1)));
+                            break;
+                    }
+                } else {
+                    Yii::app()->user->setFlash('error',sprintf("<b>Error!</b><br />Couldn't do action <em>%s</em> on item <em>%s</em>",$mediaAction->action, $mediaAction->path));
+                }
+
+            }
+        }
+        // $basePath = DDMediaDirectory::pathToUnix(realpath(Yii::app()->basePath.'/../files'));
+        // Get base path from module settings
+        $basePath = DDMediaDirectory::pathToUnix(realpath($this->module->baseDir));
+        /*
+        if(!is_dir($basePath))
+            throw new CHttpException(500, 'Directory '.$basePath.' is not a valid directory');
+         */
+        $defaultPath = '.';
+        $path = isset($_GET['p']) ? urldecode($_GET['p']) : '.';
+        $currentPath = DDMediaDirectory::pathToUnix(realpath($basePath.'/'.$path));
+        if($currentPath==$basePath) {
+            if(isset($_GET['p']))
+                Yii::app()->user->setFlash('error',"Can't change above base dir $basePath/$defaultPath");
+            $path = $defaultPath;
+        } else {
+            $path = str_replace($basePath.'/','',$currentPath);
+        }
+        $currentPath = DDMediaDirectory::pathToUnix(realpath($basePath.'/'.$path.'/'));
+        if(!isset($_POST['DDMediaAction'])) {
+            $mediaAction->name=' ';
+            $mediaAction->mediaType = 'file';
+            $mediaAction->path = $currentPath;
+        }
+        // DEBUG var_dump(array('basePath'=>$basePath, 'defaultPath'=>$defaultPath, 'GET[path]'=>urldecode($_GET['p']), 'path'=>$path, 'currentPath'=>$currentPath));
+        try {
+            $dir = new DDMediaDirectory($basePath.'/'.$path);
+            $files = $dir->listContent();
+        } catch (Exception $e) {
+            Yii::app()->user->setFlash('error',$e->getMessage());
+            $dir = new DDMediaDirectory($basePath.'/'.$defaultPath);
+            $files = $dir->listContent();
+        }
+		$this->render('index', array('mediaAction'=>$mediaAction, 'basePath'=>$basePath, 'path'=>$path, 'currentPath'=>$currentPath, 'files'=>$files, 'msg'=>$msg));
+    } // }}}
+    // {{{ actionThumbnail
+    /**
+     * Returns a preview icon for a file or folder
+     *
+     * @param string $_GET['path'] Path to file/folder
+     */
+    public function actionThumbnail()
+    {
+        $file = urldecode($_GET['path']);
+        $mimeType = mime_content_type($file);
+        if(preg_match("/image\/(.*)/",$mimeType,$matches)) {
+            $this->imagePreview($file, isset($_GET['x']) ? (int)$_GET['x'] : 100);
+        } else {
+            $this->imagePreview($file, 100);
+        }
+    } // }}} 
+    // {{{ actionPreview
+    public function actionPreview()
+    {
+        $file = urldecode($_GET['path']);
+        $mimeType = mime_content_type($file);
+        if(preg_match("/image\/(.*)/",$mimeType,$matches)) {
+            $this->imagePreview($file, isset($_GET['x']) ? (int)$_GET['x'] : 100);
+        } elseif(preg_match("/text\/plain/",$mimeType,$matches)) {
+            ob_end_clean();
+            echo $this->renderPartial('textPreview',array('path'=>$file));
+            die;
+            Yii::app()->end();
+        } else {
+            $this->imagePreview($file, 0);
+        }
+
+    } // }}}
+    // {{{ actionDownload
+    public function actionDownload()
+    {
+        while(@ob_end_clean()){};
+        $filePath = urldecode($_GET['path']);
+        $fp = fopen($filePath, 'rb');
+        // send the right headers
+        header("Cache-Control: ");
+        header("Pragma: "); 
+        header("Content-Type: application/octet-stream"); //mime_content_type($filePath));
+        header("Content-Length: " . filesize($filePath));
+        header('Content-Disposition: attachment; filename="'.basename($filePath).'"');
+        header("Content-Transfer-Encoding: binary\n");
+        // dump the picture and stop the script
+        fpassthru($fp);
+        fclose($fp);
+        Yii::app()->end();
+        die;
+    } // }}}
+    // {{{ imagePreview
+    public function imagePreview($image=null, $x=0, $y=0, $src_x=0, $src_y=0, $src_w=0, $src_h=0, $resize=0, $aspectratio=1)
+    {
+        $argKeys = array('image', 'x', 'y', 'src_x', 'src_y', 'src_w', 'src_h', 'resize', 'aspectratio');
+        error_reporting(E_ALL ^ E_NOTICE);
+        $types = array (1 => "gif", "jpeg", "png", "swf", "psd", "wbmp");
+
+        // Set file name
+        if(!isset($image)) {
+            die('Es wurde kein Bild angegeben!');
+        } else {
+             if(!file_exists($image))
+                 die('Die angegebene Datei konnte nicht auf dem Server gefunden werden!');
+        }
+
+        // Do we have a folder?
+        if(is_dir($image)) {
+            header('Content-Type: image/png');
+            readfile('images/filetypeicons/folder.png');
+            die;
+        }
+
+        // Do we hav a non-image file?
+        $mimeType = mime_content_type($image);
+        if(strpos($mimeType,'image/')===false) {
+            // No image
+            if(preg_match("/\.([^\.]+)$/",$image,$matches)){
+                $extension = $matches[1];
+                $extensionIcon = 'images/filetypeicons/'.$extension.'.png';
+                if(!file_exists($extensionIcon))
+                    $extensionIcon='images/filetypeicons/unknown.png';
+                header('Content-Type: image/png');
+                readfile($extensionIcon);
+                die;
+            }
+        }
+
+        if(
+        (is_null($x) && is_null($y))  
+        ) {
+           die('Fehlende(r) oder ungültige(r) Größenparameter!');
+        }
+
+        $cacheSubDir = '.thumbs';
+        $fileDir = dirname($image);
+        $cacheDir = $fileDir.'/'.$cacheSubDir;
+        // Create thumbs dir?
+        if(!is_dir ($cacheDir))
+            @mkdir($cacheDir, 0777);
+
+        $imagedata = getimagesize($image);
+
+        if(!isset($imagedata[2]) || $imagedata[2] == 4 || $imagedata[2] == 5)
+            die('Bei der angegebenen Datei handelt es sich nicht um ein Bild!');
+
+        if($x==0)
+            $x = floor ($y * $imagedata[0] / $imagedata[1]);
+
+        if($y==0)
+            $y = floor ($x * $imagedata[1] / $imagedata[0]);
+
+        if ($aspectratio) {
+             if ($imagedata[0] > $imagedata[1]) { // Breite > Höhe
+                  $y = floor ($x * $imagedata[1] / $imagedata[0]); // Neue Breite * Verh. H/B
+             } else if ($imagedata[1] > $imagedata[0]) {
+                  $x = floor ($y * $imagedata[0] / $imagedata[1]); // Neue Höhe * Verh. B/H
+             }
+        }
+        // src_w
+        if($src_w==0)
+            $src_w = $imagedata[0];
+        // src_h
+        if($src_h==0)
+            $src_h = $imagedata[1];
+
+        $dst_x = 0;
+        $dst_y = 0;
+
+        $myArgs = func_get_args();
+        $tmp = array();
+        for($i=1; $i<count($myArgs); $i++) {
+            $tmp[] = $argKeys[$i].'='.$myArgs[$i];
+        }
+        $thumbfile = md5(join('&',$tmp)).'_'.basename($image);
+
+        if (file_exists ($cacheDir.'/'.$thumbfile)) {
+             $thumbdata = getimagesize ($cacheDir.'/'.$thumbfile);
+             $thumbdata[0] == $x && $thumbdata[1] == $y
+                  ? $iscached = true
+                  : $iscached = false;
+        } else {
+             $iscached = false;
+        }
+
+        if (!$iscached) {
+             ($imagedata[0] > $x || $imagedata[1] > $y) ||
+             (($imagedata[0] < $x || $imagedata[1] < $y) && $resize)
+                  ? $makethumb = true
+                  : $makethumb = false;
+        } else {
+             $makethumb = false;
+        }
+
+        Header( "Content-Type: image/".$types[$imagedata[2]] );
+
+        if ($makethumb) {
+             $image = call_user_func("imagecreatefrom".$types[$imagedata[2]], $image);
+             $thumb = imagecreatetruecolor ($x, $y);
+             // imagecopyresized ( resource $dst_image , resource $src_image , int $dst_x , int $dst_y , int $src_x , int $src_y , int $dst_w , int $dst_h , int $src_w , int $src_h ) 
+             imagecopyresized (
+                 $thumb,    // destination image
+                 $image,    // source image 
+                 $dst_x,    // dest. x
+                 $dst_y,    // dest. y
+                 $src_x,    // source x
+                 $src_y,    // source y
+                 $x,        // dest. width
+                 $y,        // dest. height
+                 $src_w, 
+                 $src_h
+             );
+             call_user_func("image".$types[$imagedata[2]], $thumb, $cacheDir.'/'.$thumbfile);
+             imagedestroy ($image);
+             imagedestroy ($thumb);
+             $image = $cacheDir.'/'.$thumbfile;
+        } else {
+             $iscached
+                  ? $image = $cacheDir.'/'.$thumbfile
+                  : $image = $image;
+        }
+        Header( "Content-Length: ".filesize($image) );
+        $image = fopen ($image, "rb");
+        fpassthru ($image);
+        fclose ($image);
+        die;
+    } // }}}
+    // }}} End Methods
+}
+// {{{ myPar
+function myPar($array)
+{
+    if(!DEBUG) return;
+    if(!is_array($array)) {
+        echo "$array<br/>";
+    } else {
+        var_dump($array);
+    }
+} // }}} 

File controllers/FileController.php

+<?php
+
+class FileController extends Controller
+{
+    public $layout = '//layouts/column2';
+	public function actionIndex()
+    {
+		$this->render('index', array());
+	}
+}

File messages/de/main.php

+<?php
+/**
+ * Message translations.
+ *
+ * This file is automatically generated by 'yiic message' command.
+ * It contains the localizable messages extracted from source code.
+ * You may modify this file by translating the extracted messages.
+ *
+ * Each array element represents the translation (value) of a message (key).
+ * If the value is empty, the message is considered as not translated.
+ * Messages that no longer need translation will have their translations
+ * enclosed between a pair of '@@' marks.
+ *
+ * Message string can be used with plural forms format. Check i18n section
+ * of the guide for details.
+ *
+ * NOTE, this file must be saved in UTF-8 encoding.
+ *
+ * @version $Id: $
+ */
+return array (
+  'Create' => 'Anlegen',
+  'Delete' => 'Löschen',
+  'Destination' => 'Ziel',
+  'File to delete' => 'Zu löschende Datei',
+  'Item: ' => 'Objekt: ',
+  'Move' => 'Verschieben',
+  'New Directory' => 'Neues Verzeichnis',
+  'New Name' => 'Neuer Name',
+  'Rename' => 'Umbenennen',
+  'Upload' => 'Hochladen',
+  'Action' => 'Aktion',
+  'Base Path' => 'Basis-Pfad',
+  'Confirm to delete this item:' => 'Bestätigen Sie das Löschen dieses Objekts:',
+  'Copy directory {dir}' => 'Verzeichnis {dir} kopieren',
+  'Copy file {file}' => 'Datei {dir} kopieren',
+  'Create New Dir' => 'Neues Verzeichnis anlegen',
+  'Date' => 'Datum',
+  'Delete directory {dir}' => 'Verzeichnis {dir} löschen',
+  'Delete file {file}' => 'Datei {file} löschen',
+  'Directory' => 'Verzeichnis',
+  'Enter the name for the new directory:' => 'Geben Sie den Namen für das neue Verzeichnis ein:',
+  'Enter the new location:' => 'Geben Sie den neuen Ort ein:',
+  'Enter the new destination and name:' => 'Geben Sie den neuen Ort und Namen ein:',
+  'Enter the new name:' => 'Geben Sie den neuen Namen ein:',
+  'Icon' => 'Icon',
+  'IconCopy' => 'C',
+  'IconDelete' => 'X',
+  'IconMove' => 'V',
+  'IconRename' => 'U',
+  'Media Browser' => 'Media-Browser',
+  'Media Type' => 'Medien-Typ',
+  'Move directory {dir}' => 'Verzeichnis {dir} verschieben',
+  'Move file {file} to another location' => 'Datei {file} an einen anderen Ort verschieben',
+  'Name' => 'Name',
+  'Old Name' => 'Bisheriger Name',
+  'Parameter 1' => 'Parameter 1',
+  'Path' => 'Pfad',
+  'Rename directory {dir}' => 'Verzeichnis {dir} umbenennen',
+  'Rename file {file}' => 'Datei {file} umbenennen',
+  'Select a file to be uploaded:' => 'Wählen Sie eine Datei zum Hochladen aus:',
+  'Size' => 'Größe',
+  'The directory <em>{p1}</em> has been created.' => 'Das Verzeichnis <em>{p1}</em> wurde angelegt.',
+  'The file <em>{oldName}</em> has been uploaded as <em>{name}</em>.' => 'Die Datei <em>{oldName}</em> wurde hochgeladen als <em>{name}</em>.',
+  'The {mediaType} <em>{name}</em> has been deleted.' => '{mediaType} <em>{name}</em> wurde gelöscht.',
+  'The {mediaType} <em>{name}</em> has been moved to <em>{path}/{p1}</em>.' => '{mediaType} <em>{name}</em> wurde verschoben nach <em>{path}/{p1}</em>.',
+  'The {mediaType} <em>{name}</em> has been renamed to <em>{p1}</em>.' => '{mediaType} <em>{name}</em> wurde umbenannt nach <em>{p1}</em>.',
+  'Type' => 'Typ',
+  'Upload File' => 'Datei hochladen',
+  'directory' => 'Verzeichnis',
+  'file' => 'Datei',
+);

File messages/de/main.php.merged

+<?php
+/**
+ * Message translations.
+ *
+ * This file is automatically generated by 'yiic message' command.
+ * It contains the localizable messages extracted from source code.
+ * You may modify this file by translating the extracted messages.
+ *
+ * Each array element represents the translation (value) of a message (key).
+ * If the value is empty, the message is considered as not translated.
+ * Messages that no longer need translation will have their translations
+ * enclosed between a pair of '@@' marks.
+ *
+ * Message string can be used with plural forms format. Check i18n section
+ * of the guide for details.
+ *
+ * NOTE, this file must be saved in UTF-8 encoding.
+ *
+ * @version $Id: $
+ */
+return array (
+  'Create' => 'Anlegen',
+  'Delete' => 'Löschen',
+  'Destination' => 'Ziel',
+  'File to delete' => 'Zu löschende Datei',
+  'Move' => 'Verschieben',
+  'New Directory' => 'Neues Verzeichnis',
+  'New Name' => 'Neuer Name',
+  'Rename' => 'Umbenennen',
+  'Upload' => 'Hochladen',
+  'Action' => 'Aktion',
+  'Base Path' => 'Basis-Pfad',
+  'Confirm to delete this item:' => 'Bestätigen Sie das Löschen dieses Objekts',
+  'Create New Dir' => 'Neues Verzeichnis anlegen',
+  'Date' => 'Datum',
+  'Delete directory {dir}' => 'Verzeichnis {dir} löschen',
+  'Delete file {file}' => 'Datei {file} löschen',
+  'Directory' => 'Verzeichnis',
+  'Enter the name for the new directory:' => 'Geben Sie den Namen für das neue Verzeichnis ein:',
+  'Enter the new location:' => 'Geben Sie den neuen Ort ein:',
+  'Enter the new name:' => 'Geben Sie den neuen Namen ein:',
+  'Icon' => 'Icon',
+  'IconDelete' => 'X',
+  'IconMove' => 'V',
+  'IconRename' => 'U',
+  'Media Browser' => 'Media-Browser',
+  'Media Type' => 'Medien-Typ',
+  'Move directory {dir}' => 'Verzeichnis {dir} verschieben',
+  'Move file {file} to another location' => 'Datei {file} an einen anderen Ort verschieben',
+  'Name' => 'Name',
+  'Old Name' => 'Bisheriger Name',
+  'Parameter 1' => 'Parameter 1',
+  'Path' => 'Pfad',
+  'Rename directory {dir}' => 'Verzeichnis {dir} umbenennen',
+  'Rename file {file}' => 'Datei {file} umbenennen',
+  'Select a file to be uploaded:' => 'Wählen Sie eine Datei zum Hochladen aus:',
+  'Size' => 'Größe',
+  'The directory <em>{p1}</em> has been created.' => 'Das Verzeichnis <em>{p1}</em> wurde angelegt.',
+  'The file <em>{name}</em> has been uploaded.' => 'Die Datei <em>{p1}</em> wurde hochgeladen.',
+  'The {mediaType} <em>{name}</em> has been deleted.' => '{mediaType} <em>{name}</em> wurde gelöscht.',
+  'The {mediaType} <em>{name}</em> has been moved to <em>{path}/{p1}</em>.' => '{mediaType} <em>{name}</em> wurde verschoben nach <em>{path}/{p1}</em>.',
+  'The {mediaType} <em>{name}</em> has been renamed to <em>{p1}</em>.' => '{mediaType} <em>{name}</em> wurde umbenannt nach <em>{p1}</em>.',
+  'Type' => 'Typ',
+  'Upload File' => 'Datei hochladen',
+  'directory' => 'Verzeichnis',
+  'file' => 'Datei',
+);

File messages/de/site.php

+<?php
+/**
+ * Message translations.
+ *
+ * This file is automatically generated by 'yiic message' command.
+ * It contains the localizable messages extracted from source code.
+ * You may modify this file by translating the extracted messages.
+ *
+ * Each array element represents the translation (value) of a message (key).
+ * If the value is empty, the message is considered as not translated.
+ * Messages that no longer need translation will have their translations
+ * enclosed between a pair of '@@' marks.
+ *
+ * Message string can be used with plural forms format. Check i18n section
+ * of the guide for details.
+ *
+ * NOTE, this file must be saved in UTF-8 encoding.
+ *
+ * @version $Id: $
+ */
+return array (
+  'The directory <em>{p1}</em> has been created.' => '',
+  'The file <em>{name}</em> has been uploaded.' => '',
+  'The {mediaType} <em>{name}</em> has been deleted.' => '',
+  'The {mediaType} <em>{name}</em> has been moved to <em>{path}/{p1}</em>.' => '',
+  'The {mediaType} <em>{name}</em> has been renamed to <em>{p1}</em>.' => '',
+);

File messages/en-us/main.php

+<?php
+/**
+ * Message translations.
+ *
+ * This file is automatically generated by 'yiic message' command.
+ * It contains the localizable messages extracted from source code.
+ * You may modify this file by translating the extracted messages.
+ *
+ * Each array element represents the translation (value) of a message (key).
+ * If the value is empty, the message is considered as not translated.
+ * Messages that no longer need translation will have their translations
+ * enclosed between a pair of '@@' marks.
+ *
+ * Message string can be used with plural forms format. Check i18n section
+ * of the guide for details.
+ *
+ * NOTE, this file must be saved in UTF-8 encoding.
+ *
+ * @version $Id: $
+ */
+return array (
+  'Action' => 'Aktion',
+  'Base Path' => 'Basis-Pfad',
+  'Date' => 'Datum',
+  'Icon' => 'Icon',
+  'IconDelete' => 'X',
+  'IconMove' => 'V',
+  'IconMoveM' => '',
+  'IconRename' => 'U',
+  'Name' => 'Name',
+  'Rename file {file}' => 'Datei {file} umbenennen',
+  'Size' => 'Größe',
+  'Type' => 'Typ',
+  'Media Browser' => 'Media-Browser',
+  'The directory <em>{p1}</em> has been created.' => 'Das Verzeichnis <em>{p1}</em> wurde angelegt.',
+  'The file <em>{name}</em> has been uploaded.' => 'Die Datei <em>{p1}</em> wurde hochgeladen.',
+  'The {mediaType} <em>{name}</em> has been deleted.' => '{mediaType} <em>{name}</em> wurde gelöscht.',
+  'The {mediaType} <em>{name}</em> has been moved to <em>{path}/{p1}</em>.' => '{mediaType} <em>{name}</em> wurde verschoben nach <em>{path}/{p1}</em>.',
+  'The {mediaType} <em>{name}</em> has been renamed to <em>{p1}</em>.' => '{mediaType} <em>{name}</em> wurde umbenannt nach <em>{p1}</em>.',
+);

File messages/en-us/main.php.merged

+<?php
+/**
+ * Message translations.
+ *
+ * This file is automatically generated by 'yiic message' command.
+ * It contains the localizable messages extracted from source code.
+ * You may modify this file by translating the extracted messages.
+ *
+ * Each array element represents the translation (value) of a message (key).
+ * If the value is empty, the message is considered as not translated.
+ * Messages that no longer need translation will have their translations
+ * enclosed between a pair of '@@' marks.
+ *
+ * Message string can be used with plural forms format. Check i18n section
+ * of the guide for details.
+ *
+ * NOTE, this file must be saved in UTF-8 encoding.
+ *
+ * @version $Id: $
+ */
+return array (
+  'Delete File {file}' => 'Datei {file} löschen',
+  'Move file {file} to another location' => 'Datei {file} an einen anderen Ort verschieben',
+  'Action' => 'Aktion',
+  'Base Path' => 'Basis-Pfad',
+  'Date' => 'Datum',
+  'Icon' => 'Icon',
+  'IconDelete' => 'X',
+  'IconMove' => 'V',
+  'IconRename' => 'U',
+  'Media Browser' => 'Media-Browser',
+  'Name' => 'Name',
+  'Rename file {file}' => 'Datei {file} umbenennen',
+  'Size' => 'Größe',
+  'The directory <em>{p1}</em> has been created.' => 'Das Verzeichnis <em>{p1}</em> wurde angelegt.',
+  'The file <em>{name}</em> has been uploaded.' => 'Die Datei <em>{p1}</em> wurde hochgeladen.',
+  'The {mediaType} <em>{name}</em> has been deleted.' => '{mediaType} <em>{name}</em> wurde gelöscht.',
+  'The {mediaType} <em>{name}</em> has been moved to <em>{path}/{p1}</em>.' => '{mediaType} <em>{name}</em> wurde verschoben nach <em>{path}/{p1}</em>.',
+  'The {mediaType} <em>{name}</em> has been renamed to <em>{p1}</em>.' => '{mediaType} <em>{name}</em> wurde umbenannt nach <em>{p1}</em>.',
+  'Type' => 'Typ',
+);

File messages/en-us/site.php

+<?php
+/**
+ * Message translations.
+ *
+ * This file is automatically generated by 'yiic message' command.
+ * It contains the localizable messages extracted from source code.
+ * You may modify this file by translating the extracted messages.
+ *
+ * Each array element represents the translation (value) of a message (key).
+ * If the value is empty, the message is considered as not translated.
+ * Messages that no longer need translation will have their translations
+ * enclosed between a pair of '@@' marks.
+ *
+ * Message string can be used with plural forms format. Check i18n section
+ * of the guide for details.
+ *
+ * NOTE, this file must be saved in UTF-8 encoding.
+ *
+ * @version $Id: $
+ */
+return array (
+  'The directory <em>{p1}</em> has been created.' => '',
+  'The file <em>{name}</em> has been uploaded.' => '',
+  'The {mediaType} <em>{name}</em> has been deleted.' => '',
+  'The {mediaType} <em>{name}</em> has been moved to <em>{path}/{p1}</em>.' => '',
+  'The {mediaType} <em>{name}</em> has been renamed to <em>{p1}</em>.' => '',
+);

File models/DDMediaAction.php

+<?php 
+/**
+ * DDMediaAction class file.
+ * @author Joachim Werner <joachim.werner@diggin-data.de>
+ * @copyright Copyright &copy; Joachim Werner 2013-
+ * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
+ * @package media.models
+ */
+
+/**
+ * Form Model for managing directory/file actions.
+ */
+class DDMediaAction extends CFormModel
+{
+    // {{{ *** Members ***
+    public $path;
+    public $name;
+    public $oldName;
+    public $mediaType;
+    public $action;
+    public $p1;
+    public $uploadedFile;
+    // }}} 
+    // {{{ rules
+    public function rules()
+    { 
+        return array(
+            array('path, mediaType, action','required'),
+            array('action','checkActionParams'),
+            array('name, oldName, p1', 'safe'),
+            array('uploadedFile','file','on'=>'upload'),
+        );
+    } // }}}
+    // {{{ checkActionParams
+    public function checkActionParams($attribute, $params=array())
+    {
+        switch($this->action)
+        {
+            case 'rename':
+                if(trim($this->p1)=='') {
+                    $this->addError('p1', 'Please enter the new name');
+                }
+                break;
+            case 'move':
+                if(trim($this->p1)=='') {
+                    $this->addError('p1', 'Please enter the destination');
+                }
+                break;
+            case 'newdir':
+                if(trim($this->p1)=='') {
+                    $this->addError('p1', 'Please enter the new directory name');
+                }
+                break;
+        }
+    } // }}}
+    // {{{ attributeLabels
+    public function attributeLabels()
+    {
+        return array(
+            'path'          => Yii::t('main','Path'),
+            'name'          => Yii::t('main','Name'),
+            'oldName'       => Yii::t('main','Old Name'),
+            'mediaType'     => Yii::t('main','Media Type'),
+            'action'        => Yii::t('main','Action'),
+            'p1'            => Yii::t('main','Parameter 1'),
+            'uploadedFile'  => Yii::t('main','Upload File'),
+        );
+    } // }}}
+    // {{{ doAction
+    public function doAction()
+    {
+        $result = false;
+        switch($this->action)
+        {
+            case 'rename':
+                $src = $this->path;
+                $dest = dirname($this->path).'/'.$this->p1;
+                if($this->isWindows()) {
+                    $src    = DDMediaDirectory::pathToWindows($src);
+                    $dest   = DDMediaDirectory::pathToWindows($dest);
+                }
+                $result = @rename($src, $dest);
+                break;
+            case 'copy':
+                $src = $this->path;
+                $dest = dirname($this->path).'/'.$this->p1;
+                if($this->isWindows()) {
+                    $src    = DDMediaDirectory::pathToWindows($src);
+                    $dest   = DDMediaDirectory::pathToWindows($dest);
+                }
+                $result = $this->rcopy($src, $dest);
+                break;
+            case 'delete':
+                $path = $this->path;
+                if($this->isWindows()) {
+                    $path    = DDMediaDirectory::pathToWindows($path);
+                }
+                //echo "<li>path: $path";
+                if(is_file($this->path))
+                    $result = @unlink($path);
+                else
+                    $result = DDMediaDirectory::rrmdir($path);
+                break;
+            case 'move':
+                $src    = $this->path;
+                $dest   = dirname($this->path).'/'.$this->p1;
+                if($this->isWindows()) {
+                    $src    = DDMediaDirectory::pathToWindows($src);
+                    $dest   = DDMediaDirectory::pathToWindows($dest);
+                }
+                $result = rename($src, $dest);
+                break;
+            case 'upload':
+                $this->uploadedFile=CUploadedFile::getInstance($this,'uploadedFile');
+                $fileName = $this->oldName = $this->uploadedFile->name;
+                // Check if file already exists?
+                $filePathAndName = $this->path.'/'.basename($fileName);
+                $i=0;
+                $add = $i==0 ? '' : '.'.($i+1);
+                while(is_file($filePathAndName.$add))
+                    $add = '.'.(++$i+1);
+                $result = $this->uploadedFile->saveAs($this->path.'/'.basename($fileName).$add);
+                if($this->oldName!==basename($fileName).$add)
+                    $this->name = basename($fileName).$add;
+                break;
+            case 'newdir':
+                $newDir = $this->path.'/'.$this->p1;
+                if(!is_dir($newDir))
+                    $result = @mkdir($newDir, 0770);
+                break;
+        }
+        return $result;
+    } // }}}
+    // {{{ isWindows
+    public function isWindows()
+    {
+        return strtoupper(substr(php_uname('s'), 0, 3)) === 'WIN';
+    } // }}} 
+    // removes files and non-empty directories
+    public function rrmdir($dir) 
+    {
+        if (is_dir($dir)) {
+            $files = scandir($dir);
+            foreach ($files as $file)
+                if ($file != "." && $file != "..") $this->rrmdir("$dir/$file");
+            rmdir($dir);
+        }
+        else if (file_exists($dir)) unlink($dir);
+        return true;
+    }
+    
+    // copies files and non-empty directories
+    public function rcopy($src, $dst) 
+    {
+        if (file_exists($dst)) $this->rrmdir($dst);
+        if (is_dir($src)) {
+            mkdir($dst);
+            $files = scandir($src);
+            foreach ($files as $file)
+                if ($file != "." && $file != "..") $this->rcopy("$src/$file", "$dst/$file"); 
+        }
+        else if (file_exists($src)) copy($src, $dst);
+        return true;
+    }
+}

File models/DDMediaDirectory.php

+<?php
+/**
+ * DDMediaDirectory class file.
+ * @author Joachim Werner <joachim.werner@diggin-data.de>
+ * @copyright Copyright &copy; Joachim Werner 2013-
+ * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
+ * @package media.models
+ */
+
+/**
+ * Model for managin directories.
+ */
+class DDMediaDirectory
+{
+    // {{{ *** Members ***
+    public $dir;
+    public $showHiddenFiles=false;
+    private $_logTag='application.modules.media.models.DDmediaDirectory';
+    // }}} 
+    // {{{ *** Methods ***
+    // {{{ __construct
+    function __construct($dir=null)
+    {
+        if($dir!==null) {
+            $this->dir = self::pathToUnix($dir);
+            if(!is_dir($this->dir)) {
+                throw new CHttpException(500, "{$this->dir} is not a directory");
+            }
+        }
+    } // }}}
+    // {{{ listContent
+    public function listContent()
+    {
+        Yii::log("Directory: {$this->dir}", 'info', $this->_logTag);
+        if ($handle = opendir($this->dir)) {
+            $dirs = $files = array();
+            /* This is the correct way to loop over the directory. */
+            while (false !== ($entry = readdir($handle))) {
+                $entryPath = $this->dir.'/'.$entry;
+                Yii::log("Entry: $entry", 'info', $this->_logTag);
+                if(is_dir($entryPath)) {
+                    $entryPathReal = $this->pathToUnix(realpath($entryPath));
+                    // Directory
+                    if(substr($entry,0,1)=='.' and !in_array($entry,array('.','..'))) {
+                        if($this->showHiddenFiles==true)
+                            $dirs[$entryPathReal] = $this->getMediaStats($entryPath);
+                    } else {
+                        $dirs[$entryPathReal] = $this->getMediaStats($entryPath);
+                    }
+                } else {
+                    $file = new DDmediaFile($entryPath);
+                    // File
+                    // dot file/hidden?
+                    if(substr($entry,0,1)=='.') {
+                        if($this->showHiddenFiles==true)
+                            $files[$entryPath] = $file->getMediaStats();
+                    } else {
+                        $files[$entryPath] = $file->getMediaStats();
+                    }
+                }
+            }
+            ksort($dirs);
+            ksort($files);
+
+            closedir($handle);
+            return array('dirs'=>$dirs, 'files'=>$files);
+        }
+
+    } // }}}
+    // {{{ getSubDirs
+    public function getSubDirs($dir)
+    {
+        if ($handle = opendir($dir)) {
+            $dirs =array();
+            /* This is the correct way to loop over the directory. */
+            while (false !== ($entry = readdir($handle))) {
+                if($entry!=='.' and $entry !=='..' and is_dir($dir.'/'.$entry))
+                    $dirs[] = $entry;
+            }
+            sort($dirs);
+            return $dirs;
+        }
+    } // }}}
+    // {{{ countItems
+    public function countItems($dir)
+    {
+        Yii::log("Directory: {$dir}", 'info', $this->_logTag);
+        if ($handle = opendir($dir)) {
+            $dirs = $files = array();
+            /* This is the correct way to loop over the directory. */
+            $count = 0;
+            while (false !== ($entry = readdir($handle))) {
+                // DEBUG echo "<li>$entry ";
+                if(substr($entry,0,1)=='.' and !in_array($entry,array('.','..'))) {
+                    if($this->showHiddenFiles==true)
+                        $count++;
+                } else {
+                    if(!in_array($entry,array('.','..')))
+                        $count++;
+                }
+                // DEBUG echo $count;
+            }
+            closedir($handle);
+            return $count;
+        }
+    } // }}}
+    // {{{ getMediaStats
+    public function getMediaStats($entryPath)
+    {
+        return array(
+            'type'=>'directory',
+            'path'=>$entryPath,
+            'name'=>basename($entryPath),
+            'size'=>$this->countItems($entryPath),
+            'mimeType'=>'',
+            'mTime'=>'',
+        );
+    } // }}}
+    // {{{ pathToUnix
+    public function pathToUnix($dir)
+    {
+        return str_replace("\\", '/', $dir);
+    } // }}} 
+    // {{{ pathToWindows
+    public function pathToWindows($dir)
+    {
+        return str_replace("/", "\\", $dir);
+    } // }}} 
+    // {{{ rrmdir
+    /**
+     *  recursively remove a directory
+     */
+    function rrmdir($dir) 
+    {
+        if (is_dir($dir)) { 
+            $objects = scandir($dir); 
+            foreach ($objects as $object) { 
+                if ($object != "." && $object != "..") { 
+                    if (filetype($dir."/".$object) == "dir") self::rrmdir($dir."/".$object); else unlink($dir."/".$object);
+                } 
+            } 
+            reset($objects); 
+            rmdir($dir); 
+        } 
+        return !is_dir($dir);
+    } // }}} 
+    // }}} End Methods
+}

File models/DDMediaFile.php

+<?php
+/**
+ * DDMediaFile class file.
+ * @author Joachim Werner <joachim.werner@diggin-data.de>
+ * @copyright Copyright &copy; Joachim Werner 2013-
+ * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
+ * @package media.models
+ */
+
+/**
+ * Model for managing files.
+ */
+class DDMediaFile
+{
+    // {{{ *** Members ***
+    public $filePath;
+    // }}} 
+    // {{{ *** Methods ***
+    // {{{ __construct
+    public function __construct($filePath)
+    {
+        if(!is_file($filePath))
+            throw new CHttpException(500, "$filePath is not a valid file path");
+        $this->filePath = $filePath;
+    } // }}} 
+    // {{{ getFileMTime
+    public function getFileMTime($filePath=null) 
+    { 
+        if($filePath==null) {
+            if(is_null($this->filePath))
+                throw new CHttpException(500, "filePath not set");
+            else
+                $filePath = $this->filePath;
+        }
+        $time = filemtime($filePath); 
+
+        $isDST = (date('I', $time) == 1); 
+        $systemDST = (date('I') == 1); 
+
+        $adjustment = 0; 
+
+        if($isDST == false && $systemDST == true) 
+            $adjustment = 3600; 
+        
+        else if($isDST == true && $systemDST == false) 
+            $adjustment = -3600; 
+
+        else 
+            $adjustment = 0; 
+
+        return ($time + $adjustment); 
+    } // }}} 
+    // {{{ getMediaStats
+    public function getMediaStats()
+    {
+        $size = filesize($this->filePath);
+        return array(
+            'type'=>'file',
+            'path'=>$this->filePath,
+            'name'=>basename($this->filePath),
+            'size'=>$size,
+            'sizeFormatted'=>$this->getSizeFormatted($size),
+            'mimeType'=>mime_content_type($this->filePath),
+            'mTime'=>$this->getFileMTime($this->filePath),
+        );
+    } // }}} 
+    // {{{ getSizeFormatted
+    public function getSizeFormatted($a_bytes)
+    {
+         if ($a_bytes < 1024) {
+             return $a_bytes .' B';
+         } elseif ($a_bytes < 1048576) {
+             return round($a_bytes / 1024, 2) .' KiB';
+         } elseif ($a_bytes < 1073741824) {
+             return round($a_bytes / 1048576, 2) . ' MiB';
+         } elseif ($a_bytes < 1099511627776) {
+             return round($a_bytes / 1073741824, 2) . ' GiB';
+         } elseif ($a_bytes < 1125899906842624) {
+             return round($a_bytes / 1099511627776, 2) .' TiB';
+         } elseif ($a_bytes < 1152921504606846976) {
+             return round($a_bytes / 1125899906842624, 2) .' PiB';
+         } elseif ($a_bytes < 1180591620717411303424) {
+             return round($a_bytes / 1152921504606846976, 2) .' EiB';
+         } elseif ($a_bytes < 1208925819614629174706176) {
+             return round($a_bytes / 1180591620717411303424, 2) .' ZiB';
+         } else {
+             return round($a_bytes / 1208925819614629174706176, 2) .' YiB';
+         }
+    } // }}} 
+    // }}} End Methods
+}

File views/default/_mediaAction.php

+<?php $this->beginWidget('zii.widgets.jui.CJuiDialog', array(
+    'id'=>'mydialog',
+    // additional javascript options for the dialog plugin
+    'options'=>array(
+        'title'=>'Selected Item',
+        'autoOpen'=>false,
+        'width'=>400,
+    ),
+)); ?>
+<div class="form" id="DDMediaActionFormContainer">
+<?php echo CHtml::beginForm('','post',array('enctype' => 'multipart/form-data')); ?>
+
+    <p class="msg" style="display:none"></p>
+
+    <?php echo CHtml::errorSummary($model); ?>
+ 
+    <?php echo CHtml::activeHiddenField($model,'path') ?>
+    <?php echo CHtml::activeHiddenField($model,'mediaType') ?>
+    <?php echo CHtml::activeHiddenField($model,'action'); ?>
+ 
+    <?php echo CHtml::activeHiddenField($model,'oldName') ?>
+
+    <div class="simple" id="nameRow" style="display:none">
+        <?php echo CHtml::activeLabel($model,'name'); ?>
+        <?php echo CHtml::activeTextField($model,'name') ?>
+    </div>
+ 
+    <div class="simple" id="nameRowDisplayOnly" style="display:none">
+        <?php echo CHtml::activeLabel($model,'name'); ?>
+        <?php //echo CHtml::activeHiddenField($model,'name') ?>
+        <span id="selectedItemName"></span>
+    </div>
+ 
+    <div class="simple" id="p1Row" style="display:none">
+        <?php echo CHtml::activeLabel($model,'p1'); ?>
+        <?php echo CHtml::activeTextField($model,'p1'); ?>
+    </div>
+
+    <div class="simple" id="uploadedFileRow" style="display:none">
+        <?php echo CHtml::activeLabel($model,'uploadedFile'); ?>
+        <?php echo CHtml::activeFileField($model,'uploadedFile'); ?>
+    </div>
+
+    <div class="simple submit">
+        <?php echo CHtml::submitButton('Submit', array('id'=>'mediaActionSubmitButton')); ?>
+        <?php echo CHtml::button('Cancel',array('onclick'=>"jQuery('#mydialog').dialog('close');")); ?>
+    </div>
+ 
+<?php echo CHtml::endForm(); ?>
+</div><!-- form -->
+
+<?php $this->endWidget('zii.widgets.jui.CJuiDialog'); ?>
+
+<?php 
+if(count($model->errors)>0) Yii::app()->clientScript->registerScript('showMediaActionForm','showDialog("'.$model->action.'");');
+?>
+

File views/default/index.php

+<style type="text/css">
+<!--
+table.media-dir-nav { width:0; }
+table.media-dir-nav td { padding: 1px; } 
+table.media-items td.folder img { width: 40px; height: 40px; } 
+.datahighlight { background-color: #ffdc87 !important; }
+.datahighlight2 { background-color: #C3D9FF !important; }
+div.ui-dialog form div.simple { margin: 5px 3px 5px 3px; }
+// -->
+</style>
+<?php
+/* @var $this DefaultController */
+
+$this->breadcrumbs=array(
+	$this->module->id,
+);
+$this->menu = array(
+    //array('label'=>'Rename','url'=>'javascript:void(0)', 'linkOptions'=>array('onclick'=>"showDialog('rename');")),
+    //array('label'=>'Move','url'=>'javascript:void(0)', 'linkOptions'=>array('onclick'=>"showDialog('move');")),
+    //array('label'=>'Delete','url'=>'javascript:void(0)', 'linkOptions'=>array('onclick'=>"showDialog('delete');")),
+    array('label'=>Yii::t('main','Create New Dir'),'url'=>'javascript:void(0)', 'linkOptions'=>array('onclick'=>"doShowDialog=true;showDialog('newdir');")),
+    array('label'=>Yii::t('main','Upload File'),'url'=>'javascript:void(0)', 'linkOptions'=>array('onclick'=>"doShowDialog=true;showDialog('upload');")),
+);
+?>
+
+<h1><?php echo CHtml::encode(Yii::t('main', 'Media Browser')); ?></h1>
+
+<?php
+    foreach(Yii::app()->user->getFlashes() as $key => $message) {
+        echo '<div class="flash-' . $key . '">' . $message . "</div>\n";
+    }
+?>
+
+<h2><?php echo str_replace(array($basePath.'/','/'),array('',' / '),$currentPath); ?></h2>
+
+<?php
+// echo "<li>basePath: $basePath";
+// echo "<li>currentPath: $currentPath";
+$relativePath = str_replace($basePath.'/','',$currentPath);
+if($relativePath==$currentPath)
+    $relativePath='';
+// echo "<li>relativePath: $relativePath";
+$subDirs = explode('/',$relativePath);
+if($subDirs==array(''))
+    $subDirs = array();
+// echo "<h3>subDirs</h3>";
+// var_dump($subDirs);
+$dirsBreadcrumbs = $dirsBreadcrumbs2 = $links = array();
+foreach($subDirs as $n=>$subDir) {
+    $dirsBreadcrumbs[$n] = $subDir;
+    if(isset($dirsBreadcrumbs[$n-1]))
+        $dirsBreadcrumbs[$n] = $dirsBreadcrumbs[$n-1].'/'.$dirsBreadcrumbs[$n];
+}
+// echo "<h3>dirsBreadcrumbs</h3>";
+// var_dump($dirsBreadcrumbs);
+foreach($dirsBreadcrumbs as $n=>$subDir) {
+    $dirsBreadcrumbs2[basename($subDir)] = $subDir;
+}
+// echo "<h3>dirsBreadcrumbs2</h3>";
+// var_dump($dirsBreadcrumbs2);
+$links[] = array(
+    'link'=>CHtml::link(
+        Yii::t('main','Base Path'),
+        array('index')),
+    'subDirs'=>DDMediaDirectory::getSubDirs($basePath)
+);
+foreach($dirsBreadcrumbs2 as $title=>$subDir) {
+    $links[] = array(
+        'path'=>urlencode($subDir),
+        'link'=>CHtml::link($title,array('index','p'=>urlencode($subDir))),
+        'subDir'=>$title,
+        'subDirs'=>DDMediaDirectory::getSubDirs($basePath.'/'.$subDir)
+    );
+}
+// echo "<h3>links</h3>";
+// var_dump($links);
+
+echo '<form id="dirForm" method="get" action="'.$this->createUrl('index').'">';
+echo '<input type="hidden" id="r" name="r" value="media" />';
+echo '<input type="hidden" id="p" name="p" size="20" />';
+echo '<table class="media-dir-nav"><tr valign="top">';
+foreach($links as $i=>$link) {
+    echo '<td nowrap="nowrap" style="vertical-align:top">';
+    echo $link['link'].'<br />';
+    if(count($link['subDirs'])>0) {
+        echo '<select size="3" onchange="if(jQuery(this).val()!=\'\') {jQuery(\'#p\').val(this.value);jQuery(\'#dirForm\').submit();}">';
+        // echo '<option value="">'.(isset($link['subDir']) ? $link['subDir'] : '(Base Path)').'</option>';
+        // echo '<option value="">-</option>';
+        foreach($link['subDirs'] as $subDir) {
+            $value='';
+            if(isset($link['path']))
+                $value = $link['path'].'/';
+            echo '<option value="'.$value.$subDir.'">'.$subDir.'</option>';
+        }
+        echo '</select>';
+    }
+    echo "</td>";
+    if(isset($links[$i+1]))
+        echo '<td style="font-weight:bold;padding: 0 3px 0 3px;vertical-align:top">/</td>';
+}
+echo "</tr></table></form>";
+?>
+
+<?php if(trim($msg)!=='') : ?>
+<p>
+<?php echo $msg; ?>
+</p>
+<?php endif; ?>
+
+<?php 
+// Yii::import('DDVarDumper');
+// Yii::app()->clientScript->registerCoreScript('jquery');
+// DDVarDumper::dumpAsList($files); 
+?>
+
+<table class="media-items">
+    <thead>
+        <tr>
+            <th><?php echo CHtml::encode(Yii::t('main', 'Icon')); ?></th>
+            <th><?php echo CHtml::encode(Yii::t('main', 'Name')); ?></th>
+            <th><?php echo CHtml::encode(Yii::t('main', 'Date')); ?></th>
+            <th><?php echo CHtml::encode(Yii::t('main', 'Type')); ?></th>
+            <th><?php echo CHtml::encode(Yii::t('main', 'Size')); ?></th>
+            <th><?php echo CHtml::encode(Yii::t('main', 'Action')); ?></th>
+        </tr>
+    </thead>
+    <tbody>
+        <!-- {{{ Dirs -->
+        <?php foreach($files['dirs'] as $dirPath=>$stats) : ?>
+        <?php /* $onclick=""; if(!in_array($stats['name'],array('.', '..'))) */ $onclick=' onclick="selectMedia(\'directory\',\''.$dirPath.'\',\''.$stats['name'].'\');"'; ?>
+        <tr class="dirsFilesRows"<?php echo $onclick; ?>>
+            <td class="folder"><?php echo CHtml::image(Yii::app()->baseUrl.'/images/filetypeicons/folder.png',$stats['name']); ?></td>
+            <td><?php echo CHtml::link($stats['name'],array('index','p'=>urlencode($path.'/'.$stats['name']))); ?></td>
+            <td>&ndash;</td>
+            <td><?php echo CHtml::encode(Yii::t('main','Directory')); ?></td>
+            <td style="white-space:nowrap;font-size:smaller;text-align:right"><?php echo $stats['size']; ?></td>
+            <td style="white-space:nowrap;font-size:smaller">
+                <?php if(!in_array($stats['name'],array('..'))) : ?>
+                <a href="javascript:void(0)" onclick="selectMedia('directory','<?php echo $dirPath; ?>','<?php echo $stats['name']; ?>');showDialog('rename');" title="<?php echo Yii::t('main', 'Rename directory {dir}', array('{dir}'=>$stats['name'])); ?>"><?php echo CHtml::encode(Yii::t('main', 'IconRename')); ?></a>&nbsp;
+                <a href="javascript:void(0)" onclick="selectMedia('directory','<?php echo $dirPath; ?>','<?php echo $stats['name']; ?>');showDialog('copy');" title="<?php echo Yii::t('main', 'Copy directory {dir}', array('{dir}'=>$stats['name'])); ?>"><?php echo CHtml::encode(Yii::t('main', 'IconCopy')); ?></a>&nbsp;
+                <a href="javascript:void(0)" onclick="selectMedia('directory','<?php echo $dirPath; ?>','<?php echo $stats['name']; ?>');showDialog('move');" title="<?php echo Yii::t('main', 'Move directory {dir}', array('{dir}'=>$stats['name'])); ?>"><?php echo CHtml::encode(Yii::t('main', 'IconMove')); ?></a>&nbsp;
+                <a href="javascript:void(0)" onclick="selectMedia('directory','<?php echo $dirPath; ?>','<?php echo $stats['name']; ?>');showDialog('delete');" title="<?php echo Yii::t('main', 'Delete directory {dir}', array('{dir}'=>$stats['name'])); ?>"><?php echo CHtml::encode(Yii::t('main', 'IconDelete')); ?></a>
+                <?php endif; ?>
+            </td>
+        </tr>
+        <?php endforeach; ?>
+        <!-- }}} -->
+        <!-- {{{ Files -->
+        <?php foreach($files['files'] as $filePath=>$stats) : ?>
+        <?php $onclick=""; if(!in_array($stats['name'],array('.', '..'))) $onclick=' onclick="selectMedia(\'file\',\''.$filePath.'\',\''.$stats['name'].'\');"'; ?>
+        <tr class="dirsFilesRows"<?php echo $onclick; ?>>
+            <td>
+<?php
+if(preg_match("/image\/(.*)/",$stats['mimeType'],$matches))
+    echo CHtml::image($this->createUrl('thumbnail',array('path'=>urlencode($stats['path']),'x'=>75)));
+else
+    echo CHtml::image($this->createUrl('thumbnail',array('path'=>urlencode($stats['path']),'x'=>0)), 'File', array( 'style'=>'width: 40px; height: 40px;' ) );
+?>
+            </td>
+            <td>
+<?php
+if(preg_match("/image\/(.*)/",$stats['mimeType'],$matches))
+    echo CHtml::link($stats['name'],'javascript:void(0)',array('onclick'=>"jQuery('#imagePreview').dialog('open');jQuery('#imagePlaceholder').html('".CHtml::image($this->createUrl('thumbnail',array('path'=>urlencode($stats['path']),'x'=>480)))."');"));
+elseif(preg_match("/text\/plain/",$stats['mimeType'],$matches)) {
+    echo CHtml::link($stats['name'],'javascript:void(0)',array('onclick'=>'
+$.ajax({
+    url: "'.$this->createUrl('preview',array('path'=>urlencode($stats['path']))).'",
+}).done(function(data) { 
+    $("#imagePreview").dialog("open");
+    $("#imagePreview").html(data);
+});')); 
+}
+else
+    echo CHtml::link($stats['name'],array('download','path'=>urlencode($stats['path'])),array('target'=>'_blank')); 
+?></td>
+            <td><?php echo date("Y-m-d H:i:s",$stats['mTime']); ?></td>
+            <td><?php echo $stats['mimeType']; ?></td>
+            <td style="white-space:nowrap;font-size:smaller;text-align:right"><?php echo $stats['sizeFormatted']; ?></td>
+            <td style="white-space:nowrap;font-size:smaller">
+                <a href="javascript:void(0)" onclick="selectMedia('file','<?php echo $filePath; ?>','<?php echo $stats['name']; ?>');showDialog('rename');" title="<?php echo Yii::t('main', 'Rename file {file}', array('{file}'=>$stats['name'])); ?>"><?php echo CHtml::encode(Yii::t('main', 'IconRename')); ?></a>&nbsp;
+                <a href="javascript:void(0)" onclick="selectMedia('file','<?php echo $filePath; ?>','<?php echo $stats['name']; ?>');showDialog('copy');" title="<?php echo Yii::t('main', 'Copy file {file}', array('{file}'=>$stats['name'])); ?>"><?php echo CHtml::encode(Yii::t('main', 'IconCopy')); ?></a>&nbsp;
+                <a href="javascript:void(0)" onclick="selectMedia('file','<?php echo $filePath; ?>','<?php echo $stats['name']; ?>');showDialog('move');" title="<?php echo Yii::t('main', 'Move file {file} to another location', array('{file}'=>$stats['name'])); ?>"><?php echo CHtml::encode(Yii::t('main', 'IconMove')); ?></a>&nbsp;
+                <a href="javascript:void(0)" onclick="selectMedia('file','<?php echo $filePath; ?>','<?php echo $stats['name']; ?>');showDialog('delete');" title="<?php echo Yii::t('main', 'Delete file {file}', array('{file}'=>$stats['name'])); ?>"><?php echo CHtml::encode(Yii::t('main', 'IconDelete')); ?></a>
+            </td>
+        </tr>
+        <?php endforeach; ?>
+        <!-- }}} -->
+    </tbody>
+</table>
+
+<?php $this->beginWidget('zii.widgets.jui.CJuiDialog', array(
+    'id'=>'imagePreview',
+    // additional javascript options for the dialog plugin
+    'options'=>array(
+        'title'=>'Selected Item',
+        'autoOpen'=>false,
+        'modal'=>true,
+        'position'=>'center',
+        'width'=>500,
+    ),
+)); ?>
+<div id="imagePlaceholder" style="width:100%"></div>
+<?php $this->endWidget(); ?>
+
+<?php $this->renderPartial('_mediaAction',array('model'=>$mediaAction)); ?>
+<?php Yii::app()->clientScript->registerScript('highlightRow',
+"$('.dirsFilesRows').hover(function(){
+			$(this).children().addClass('datahighlight2');
+		},function(){
+			$(this).children().removeClass('datahighlight2');
+		});
+$('.dirsFilesRows td').click(function() {
+        $(this).closest('tr').siblings().removeClass('datahighlight2').removeClass('datahighlight');
+        $(this).parents('tr').toggleClass('datahighlight2', this.clicked);
+    });
+"); ?>
+<script type="text/javascript">
+<!--
+    var currentPath = '<?php echo $currentPath; ?>';
+    var selectedItem = '.';
+    var doShowDialog=false;
+    // {{{ selectMedia
+    function selectMedia(mediaType, path, name)
+    {
+        if(name=='..') 
+            doShowDialog=false;
+        else
+            doShowDialog=true;
+        jQuery('#DDMediaAction_mediaType').val(mediaType);
+        jQuery('#DDMediaAction_path').val(path);
+        jQuery('#DDMediaAction_name, #DDMediaAction_oldName').val(name);
+        selectedItem = name;
+    } // }}} 
+    // {{{ showDialog
+    function showDialog(action)
+    {
+        if(doShowDialog==false)
+            return;
+        jQuery('#mydialog').dialog('open');
+        jQuery('#mydialog').dialog({title:'<?php echo CHtml::encode(Yii::t('main', 'Item: ')); ?>'+selectedItem /* +' &rArr; '+action */ });
+        jQuery('#DDMediaAction_action').val(action);
+        jQuery('#mediaActionSubmitButton').val('Submit');
+        jQuery('.msg').html('').hide();
+        jQuery('#selectedItemName').html(selectedItem);
+        jQuery('#nameRow, #nameRowDisplayOnly, #p1Row, #uploadedFileRow').hide();
+        switch(action)
+        {
+            case 'rename':
+                jQuery('#mediaActionSubmitButton').val('<?php echo CHtml::encode(Yii::t('main', 'Rename')); ?>');
+                jQuery('.msg').html('<?php echo CHtml::encode(Yii::t('main', 'Enter the new name:')); ?>').show();
+                jQuery('#nameRowDisplayOnly, #p1Row').show();
+                jQuery('label[for=DDMediaAction_p1]').html('<?php echo CHtml::encode(Yii::t('main', 'New Name')); ?>');
+                jQuery('#DDMediaAction_p1').val(jQuery('#DDMediaAction_name').val());
+                jQuery('#DDMediaAction_p1').focus().select();
+                break;
+            case 'copy':
+                jQuery('#mediaActionSubmitButton').val('<?php echo CHtml::encode(Yii::t('main', 'Copy')); ?>');
+                jQuery('.msg').html('<?php echo CHtml::encode(Yii::t('main', 'Enter the new destination and name:')); ?>').show();
+                jQuery('#nameRowDisplayOnly, #p1Row').show();
+                jQuery('label[for=DDMediaAction_p1]').html('<?php echo CHtml::encode(Yii::t('main', 'New Dest. and Name')); ?>');
+                jQuery('#DDMediaAction_p1').val('./'+jQuery('#DDMediaAction_name').val());
+                jQuery('#DDMediaAction_p1').focus().select();
+                break;
+            case 'move':
+                jQuery('#mediaActionSubmitButton').val('<?php echo CHtml::encode(Yii::t('main', 'Move')); ?>');
+                jQuery('.msg').html('<?php echo CHtml::encode(Yii::t('main', 'Enter the new location:')); ?>').show();
+                jQuery('#nameRowDisplayOnly, #p1Row').show();
+                jQuery('label[for=DDMediaAction_p1]').html('<?php echo CHtml::encode(Yii::t('main', 'Destination')); ?>');
+                jQuery('#DDMediaAction_p1').val('./'+selectedItem);
+                jQuery('#DDMediaAction_p1').focus().select();
+                break;
+            case 'delete':
+                jQuery('#mediaActionSubmitButton').val('<?php echo CHtml::encode(Yii::t('main', 'Delete')); ?>');
+                jQuery('.msg').html('<?php echo CHtml::encode(Yii::t('main', 'Confirm to delete this item:')); ?>').show();
+                jQuery('#nameRowDisplayOnly').show();
+                jQuery('label[for=DDMediaAction_p1]').html('<?php echo CHtml::encode(Yii::t('main', 'File to delete')); ?>');
+                break;
+            case 'newdir':
+                jQuery('#mediaActionSubmitButton').val('<?php echo CHtml::encode(Yii::t('main', 'Create')); ?>');
+                jQuery('.msg').html('<?php echo CHtml::encode(Yii::t('main', 'Enter the name for the new directory:')); ?>').show();
+                jQuery('#DDMediaAction_path').val(currentPath);
+                jQuery('#p1Row').show();
+                jQuery('label[for=DDMediaAction_p1]').html('<?php echo CHtml::encode(Yii::t('main', 'New Directory')); ?>');
+                jQuery('#DDMediaAction_p1').val('').focus().select();
+                break;
+            case 'upload':
+                jQuery('#mediaActionSubmitButton').val('<?php echo CHtml::encode(Yii::t('main', 'Upload')); ?>');
+                jQuery('.msg').html('<?php echo CHtml::encode(Yii::t('main', 'Select a file to be uploaded:')); ?>').show();
+                jQuery('#DDMediaAction_path').val(currentPath);
+                jQuery('#uploadedFileRow').show();
+                jQuery('#DDMediaAction_uploadedFile').focus().select();
+                break;
+        }
+        doShowDialog=false;
+    } // }}} 
+// -->
+</script>

File views/default/textPreview.php

+<pre style="font-family:'Courier New';font-size:8pt;">
+<?php echo file_get_contents($path); ?>
+</pre>