<?php

namespace Neag\MainBundle\Service;

use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Filesystem\Exception\IOException;
use Symfony\Component\HttpFoundation\RedirectResponse;

class Fileadmin
{
    public $session;
    
    public $kernel;
    
    public $imageHandling;
    
    /**
     * Типы файлов
     *
     * @access    public
     * @var       array
     */
    public $types = Array(
        'image' => Array('gif', 'jpg', 'jpeg', 'png', 'bmp', 'tif', 'ico'),
        'sound' => Array('mp3', 'wav', 'wma', 'ogg'),
        'video' => Array('flv', 'avi', 'mpg', 'mpeg', 'wmv', 'mov'),
        'text' => Array('txt', 'php', 'php3', 'php4', 'php5', 'phtml', 'pl', 'asp', 'htaccess', 'css', 'xml', 'sql', 'tpl', 'inc'),
        'html' => Array('html', 'htm'),
        'archive' => Array('rar', 'zip', 'arj', 'gz', '7z'),
    );
    
    /**
     * Набор кодировок
     *
     * @access    public
     * @var       array
     */
    public $fileCharsets = Array('windows-1251', 'utf-8');

    /**
     * Кодировка файла по-умолчанию
     *
     * @access    public
     * @var       array
     */
    public $defaultFileCharset = 'windows-1251';

    /**
     * Транслитерация имён файлов
     *
     * @access    public
     * @var       boolean
     */
    public $transliterateFilenames = true;

    /**
     * Разрешённые типы файлов
     *
     * @access    public
     * @var       array | null
     */
    public $allowTypes = null;

    /**
     * Запрещённые типы файлов
     *
     * @access    public
     * @var       array | null
     */
    public $denyTypes = null;

    /**
     * Разрешённые расширения файлов
     *
     * @access    public
     * @var       array | null
     */
    public $allowExtensions = null;

    /**
     * Запрещённые расширения файлов
     *
     * @access    public
     * @var       array | null
     */
    public $denyExtensions = null;

    /**
     * Кеш доступа к расширепниям файлов
     *
     * @access    public
     * @var       array
     */
    public $extensionAccessCache = Array();

    /**
     * Массив расширений скриптовых файлов
     *
     * @access    public
     * @var       array
     */
    public $allScripts = Array(
        'php',
        'phtml',
        'php3',
        'php4',
        'php5',
        'pl',
        'asp',
        'cqi',
        'htaccess'
    );

    /**
     * Основной путь к файл-менеджеру
     *
     * @access    public
     * @var       string
     */
    public $mainLink;

    /**
     * Основной путь к файл-менеджеру
     *
     * @access    public
     * @var       string
     */
    public $args = Array();

    /**
     * Путь к рабочей папке
     *
     * @access    public
     * @var       string
     */
    public $basePath = '';

    /**
     * Web-путь к рабочей папке
     *
     * @access    public
     * @var       string | null
     */
    public $baseWebPath = null;

    /**
     * Текущий путь
     *
     * @access    public
     * @var       string
     */
    public $currentPath;

    /**
     * Метод обратного вызова, возвращающий атрибуты тега a для выбранного файла
     *
     * @access    public
     * @var       Array
     */
    public $fileCallback = Array('self', 'getFileAttributes');

    /**
     * Массив привилегий
     * Если указано значение null - тогда все привилегии
     *
     * @access    public
     * @var       array | null
     */
    public $privileges = Array('upload', 'newdir', 'copy', 'copyfiles', 'delete', 'rename', 'download');

    /**
     * Переменные сессии
     *
     * @access    public
     * @var       array
     */
    public $sessionVars = Array();
    
    /**
     * Ключ элемента массива сессии, в котором хранится массив сессионных переменных
     *
     * @access    public
     * @var       string
     */
    public $sessionKey = 'fileadmin';

    /**
     * Буфер - массив помеченных файлов
     *
     * @access    public
     * @var       array
     */
    public $buffer;

    /**
     * Буфер - путь помеченных файлов
     *
     * @access    public
     * @var       string
     */
    public $bufferPath;

    /**
     * Свёрстаннный буфер
     *
     * @access    public
     * @var       string
     */
    public $bufferHtml;

    /**
     * Кол-во файлов
     *
     * @access    public
     * @var       integer
     */
    public $filescount;

    /**
     * Статус
     *
     * @access    public
     * @var       string
     */
    public $stat;

    /**
     * Имя ссылки
     *
     * @access    public
     * @var       string
     */
    public $linkname;

    /**
     * Цель
     *
     * @access    public
     * @var       string
     */
    public $target;

    /**
     * Действие
     *
     * @access    public
     * @var       string
     */
    public $act;
    
    /**
     * Ссылка переадресации
     *
     * @access    public
     * @var       string | null
     */
    public $redirectUrl = null;
    
    /**
     * Языковой массив
     *
     * @access    public
     * @var       array
     */
    public $lang = Array(
        "module" => "Файл-менеджер",
        "version" => "Версия",
        "description" => "Панель управления файлами",
        "author" => "Кравченко Тимофей (http://cubexfiles.startek.ru), Баканов Павел (http://bakanov.info)",
        "license" => "LGPL",
        "title" => "Файл-менеджер",
        "ok" => "OK", 
        "cancel" => "Отменить", 
        "refresh" => "Обновить",
        "select_all" => "Выделить всё",
        "deselect" => "Снять выделение",
        "invert_selection" => "Обратить выделение",
        "root" => "корень",
        "login_error" => "Пользователю не удалось войти",
        "create_file" => "Новый файл",
        "create_dir" => "Новая папка",
        "copy" => "Копировать",
        "cut" => "Вырезать",
        "paste" => "Вставить",
        "delete" => "Удалить",
        "upload" => "Загрузить файл",
        "attr_change" => "Атрибуты",
        "attr_change_rec" => "Атрибуты (рекурсивно)",
        "unzip" => "Распаковать файл (ы)",
        "zip" => "Архивировать (zip)",    
        "http_upload" => "Скачать (HTTP)",
        "symlink" => "Симлинк",
        "create_file_title" => "Создать новый файл",
        "create_dir_title" => "Создать новую папку",
        "create_dir_ok" => "Да",
        "create_dir_cancel" => "Нет",
        "copy_title" => "Помещаем выбранные объекты в буфер обмена для дальнейшего копирования",
        "cut_title" => "Помещаем выбранные объекты в буфер обмена для дальнейшего перемещения",
        "paste_title" => "Копируем или перемещаем объекты из буфера обмена",
        "delete_title" => "Удаляем выбранные объекты",
        "upload_title" => "Загружаем файл с вашего компьютера на сервер",
        "attr_change_title" => "Изменяем аттрибуты выбранным объектам",
        "attr_change_rec_title" => "Изменяем аттрибуты выбранным объектам, включая вложенные",
        "zip_title" => "Укажите имя zip-архива", 
        "unzip_title" => "Распаковываем выбранных zip-архивов",
        "http_upload_title" => "Получаем файл из интернета",
        "symlink_title" => "Создаём симлинк",
        "col_name" => "Имя",
        "col_type" => "Тип",
        "col_size" => "Размер",
        "col_mod" => "Модификация",
        "col_attr" => "Атрибуты",
        "edit" => "Редактировать",
        "download" => "Скачать на компьютер",
        "rename" => "Переименовать",
        "input_attr" => "Введите новые аттрибуты", 
        "input_attr_file" => "Введите новые аттрибуты для \"{filename}\"",
        "input_name" => "Введите новое имя",
        "input_zip" => "Введите имя архива",
        "buffer_action" => "Буфер обмена (действие: ",
        "buffer_copy" => "копировать",
        "buffer_move" => "переместить",
        "do_rewrite" => "Перезаписать?",
        "not_enough_privileges" => "У пользователя недостаточно привилегий",
        "current_path_not_selected" => "Текущая директория не выбрана",
        "no_events" => "Пока никаких событий не было",
        "user_not_in_list" => "Пользователь отсутствует в общем списке",
        "user_not_init" => "Пользователь не инициализирован",
        "user_not_active" => "Пользователь не активирован",
        "dir_not_exists" => "Указана несуществующая директория",
        "dir_exists" => "Такая директория уже существует",
        "cycle_copying_denied" => "Цикличное копирование недопустимо",
        "cant_delete_occupied_dir" => "Нельзя удалить директорию, занятую другим пользователем",
        "unzip_only_zip" => "Распаковываются только zip-архивы",
        "restricted_files_deleted" => "Файлы запрещённого типа были удалены",
        "you_cant_create_such_files" => "У Вас нет прав на создание файлов данного типа",
        "name_exists" => "Такое имя уже есть",
        "userlist_only_for_admin" => "Только администратор может получить список пользователей",
        "usercreation_only_for_admin" => "Только администратор может создать пользователя",
        "cant_create_user_in_service_zone" => "Нельзя создавать пользователя в служебной зоне. Выберите директорию параллельную служебной",
        "user_not_created" => "Пользователь не был создан",
        "name_not_indicated" => "Не указано имя",
        "name_occupied" => "Пользователь с таким именем уже существует",
        "current_dir_occupied_by_another_admin" => "Текущая директория занята другим администратором",
        "useredit_only_for_admin" => "Только администратор может редактировать данные пользователя",
        "user_not_edited" => "Пользователь не был изменён.",
        "user_not_in_your_zone" => "Пользователь находится вне зоны Вашего влияния",
        "userinfo_only_for_admin" => "Только администратор может загружать данные пользователя",
        "userinfo_not_loaded" => "Данные пользователя не были загружены. Не указано имя",
        "dataedit_only_for_host" => "Изменять данные может только хозяин сайта",
        "current_path_not_init" => "Текущий путь не инициализирован",
        "buffer_empty" => "Буфер пуст",
        "owner_rights" => "Права владельца",
        "group_rights" => "Групповые права",
        "public_rights" => "Публичные права",
        "reading" => "чтение",
        "writing" => "запись",
        "execution" => "выполнение",
        "edit_title" => "Редактирование файла",
        "upload_title" => "Загрузка файла",
        "httpupload_title" => "Загрузка файла с удалённого сервера",
        "symlink_title" => "Симлинк",
        "download_title" => "Получение файла",
        "admin_title" => "Администрирование",
        "file" => "Файл",
        "file_name" => "Имя файла",
        "file_rewrite" => "Перезаписать существующий",
        "file_rewrite_yes" => "Да",
        "file_rewrite_no" => "Нет",
        "file_charset" => "Кодировка",
        "file_chmod" => "Права",
        "file_charset_description" => "",
        "file_charset_refresh" => "Обновить",
        "file_content" => "Содержимое файла",
        "file_save" => "Сохранить",
        "file_apply" => "Применить",
        "file_return" => "Вернуться",
        "file_error_empty_filename" => "Не указано имя файла",
        "file_error_file_exists" => "Файл с таким именем уже существует. Введите другое имя файла, либо поставьте опцию, чтобы перезаписать существующий файл.",
        "file_error_type" => "Невозможно сохранить файл, так как у вас нет прав создавать файлы данного типа.",
        "file_error_unable_save" => "Невозможно сохранить файл. Проверьте, есть ли у вас права на запись в заданную папку.",
        "file_error_invalid_permissions" => "Неверно заданы права для файла",
        "file_save_ok" => "Файл \"{filename}\" успешно сохранён.",
        "save_symlink" => "Сохранить симлинк",
        "return_to_main" => "<< Вернуться к списку файлов",
        "error_such_file_edit" => "Вы не можете редактировать файлы такого типа",
        "error_such_file_upload" => "Вы не можете загружать файлы такого типа",
        "error_file_upload" => "Ошибка загрузки файла.",
        "error_not_enough_rights" => "Недостаточно прав для загрузки файла в заданную директорию.",
        "error_edit" => "Вы не можете редактировать файлы",
        "error_upload" => "Вы не можете загружать файлы",
        "error_symlink" => "Вы не можете редактировать симлинк",
        "error_download" => "Вы не можете получать файлы",
        "error_http_upload" => "Вы не можете загружать файлы с удалённого сервера",
        "error_admin" => "У Вас нет прав администратора",
        "file_edited" => "Файл изменён",
        "file_uploaded" => "Файл загружен. Вы можете загрузить ещё один или вернуться на главную страницу.",
        "rights" => "Права",
        "http_url" => "URL удалённого файла",
        "http_url_description" => "Начиная с http://",
        "local_file_name" => "Локальное имя файла",
        "bytes_to_upload" => "Число байт для скачивания",
        "bytes_to_upload_description" => "Размер файла иногда опредляется неправильно (оставьте пустым для автоопределения).",
        "symlink_name" => "Название",
        "error_symlink_name" => "Вы не указали название симлинка",
        "error_symlink_target" => "Вы не указали файл для симлинка",
        "error_symlink_exists" => "Симлинк с таким именем уже существует",
        "error_symlink_save" => "Симлинк сохранить не удалось",
        "symlink_saved" => "Симлинк сохранен",
        "get_file" => "Получить файл",
        "file_types" => "Типы файлов",
        "special_types" => "Специфические типы файлов (через пробел)",
        "harmless_types" => "Содержимое этого списка зависит только от Вашего административного вкуса и на безопасность системы не влияет.",
        "users_allow" => "Пользователи имеют право работать только с файлами указанных типов",
        "users_deny" => "Пользователи имеют право работать со ВСЕМИ файлами за исключением данного списка",
        "no_limitations" => "Нет ограничений",
        "guests_rights" => "Возможности гостей",
        "newdir_right" => "Создавать директории",
        "copyfiles_right" => "Копировать и перемещать файлы",
        "delete_right" => "Удалять файлы и директории",
        "upload_right" => "Загружать файлы с компьютера",
        "chmod_right" => "Изменять аттрибуты файлов",
        "unzip_right" => "Распаковывать архивы",
        "zip_right" => "Создавать архивы",
        "http_right" => "Загружать HTTP",
        "edit_right" => "Редактировать файлы",
        "rename_right" => "Переименовывать файлы и директории",
        "download_right" => "Загружать файлы на компьютер",
        "symlink_right" => "Создавать симлинки",
        "admin_save" => "Сохранить",
    );
    
    /**
     * Конструктор
     */
    public function __construct($session = null, $kernel = null, $imageHandling = null)
    {
        $session->start();
        $this->session = $session;
        $this->kernel = $kernel;
        $this->imageHandling = $imageHandling;
    }
    
    /**
     * Возвращает объект сессии
     * 
     * @access      public
     * @return      object
     */     
    public function getSession()
    {
        return $this->session;
    }
    
    /**
     * Устанавливает $sessionKey
     * 
     * @access      public
     * @param       string
     */     
    public function setSessionKey($sessionKey)
    {
        $this->sessionKey = $sessionKey;
    }
    
    /**
     * Возвращает $sessionKey
     * 
     * @access      public
     * @return      string
     */     
    public function getSessionKey()
    {
        return $this->sessionKey;
    }
    
    /**
     * Возвращает объект обработки изображений
     * 
     * @access      public
     * @return      object
     */     
    public function getImageHandling()
    {
        return $this->imageHandling;
    }
    
    /**
     * Возвращает сервис
     * 
     * @access      public
     * @return      object
     */     
    public function get($serviceName)
    {
        return $this->kernel->getContainer()->get($serviceName);     
    }
    
    /**
     * Задание базовой директории
     *
     * @access      public
     * @param       string
     */
    public function setBasePath($basePath)
    {
        if (is_string($basePath)) {
            $basePath = trim($basePath, '/');
            if (!empty($basePath)) {
                $basePath = $basePath.'/';
            } else {
                $basePath = './';
            }
            $this->basePath = $basePath;
        }
    }

    /**
     * Получение базовой директории
     *
     * @access      public
     * @return      string
     */
    public function getBasePath()
    {
        return $this->basePath;
    }

    /**
     * Задание базовой web-директории
     *
     * @access      public
     * @param       string
     */
    public function setBaseWebPath($baseWebPath = null)
    {
        if (is_string($baseWebPath)) {
            $this->baseWebPath = $baseWebPath;
        } else {
            $this->baseWebPath = null;
        }
    }

    /**
     * Получение базовой web-директории
     *
     * @access      public
     * @return      string
     */
    public function getBaseWebPath()
    {
        return $this->baseWebPath;
    }

    /**
     * Задание времени жизни сессии
     *
     * @access      public
     * @param       integer
     */
    public function setSessionLifetime($sessionLifetime)
    {
        if (is_int($sessionLifetime)) {
            $this->sessionLifetime = $sessionLifetime;
        }
    }

    /**
     * Получение времени жизни сессии
     *
     * @access      public
     * @return      integer
     */
    public function getSessionLifetime()
    {
        return $this->sessionLifetime;
    }

    /**
     * Устанавливает допустимые кодировки файла
     *
     * @access      public
     * @param       array
     */
    public function setFileCharsets($fileCharsets)
    {
        if (is_array($fileCharsets)) {
            $this->fileCharsets = $fileCharsets;
        }
    }

    /**
     * Возвращает допустимые кодировки файла
     *
     * @access      public
     * @return      array
     */
    public function getFileCharsets()
    {
        return $this->fileCharsets;
    }

    /**
     * Устанавливает кодировку файла по-умолчанию
     *
     * @access      public
     * @param       string
     */
    public function setDefaultFileCharset($charset)
    {
        if (in_array($charset, $this->fileCharsets)) {
            $this->defaultFileCharset = $charset;
        }
    }

    /**
     * Возвращает кодировку файла по-умолчанию
     *
     * @access      public
     * @return      string
     */
    public function getDefaultFileCharset()
    {
        return $this->defaultFileCharset;
    }

    /**
     * Устанавливает флаг транслитерации имён файлов
     *
     * @access      public
     * @param       boolean
     */
    public function setTransliterateFilenames($transliterate_filenames)
    {
        $this->transliterateFilenames = (bool)$transliterate_filenames;
    }

    /**
     * Определяет транслитерацию имён файлов
     *
     * @access      public
     * @return      boolean
     */
    public function isTransliterateFilenames()
    {
        return $this->transliterateFilenames;
    }

    /**
     * Задание привилегий
     *
     * @access      public
     * @param       array | null
     */
    public function setPrivileges($privileges = null)
    {
        if (is_array($privileges)) {
            $this->privileges = $privileges;
        } elseif (is_null($privileges)) {
            $this->privileges = null;
        }
    }

    /**
     * Получение привилегий
     *
     * @access      public
     * @return      array | null
     */
    public function getPrivileges()
    {
        return $this->privileges;
    }

    /**
     * Задание метода обратного вызова для файла
     *
     * @access      public
     * @param       array
     */
    public function setFileCallback($fileCallback)
    {
        $this->fileCallback = $fileCallback;
    }

    /**
     * Возвращение метода обратного вызова для файла
     *
     * @access      public
     * @return      array
     */
    public function getFileCallback()
    {
        return $this->fileCallback;
    }

    /**
     * Есть ли данная привилегия
     *
     * @access      public
     * @return      array | null
     */
    public function isAllow($privilege)
    {
        if (is_array($this->privileges)) {
            return in_array($privilege, $this->privileges);
        } else {
            return true;
        }
    }

    /**
     * Проверяет допустимость выполнения команды
     *
     * @access      public
     * @param       string
     * @return      boolean
     */
    public function checkCommand($comm) {
        if (!$this->isAllow($comm)) {
            $this->message($this->lang['not_enough_privileges'], 'error');
            return false;
        }
        if (!isset($this->currentPath)) {
            $this->message($this->lang['current_path_not_selected'], 'error'); 
            return false;
        }
        return true;
    }

    /**
     * Задание разрешённых типов файлов
     *
     * @access      public
     * @param       array | string | null
     */
    public function setAllowTypes($allowTypes = null)
    {
        if (is_array($allowTypes) || is_null($allowTypes)) {
            $this->allowTypes = $allowTypes;
        } else if (is_string($allowTypes)) {
            $this->allowTypes = explode(' ', $allowTypes);
        }
    }

    /**
     * Возвращение разрешённых типов файлов
     *
     * @access      public
     * @return      array | null
     */
    public function getAllowTypes()
    {
        return $this->allowTypes;
    }

    /**
     * Задание запрещённых типов файлов
     *
     * @access      public
     * @param       array | string | null
     */
    public function setDenyTypes($denyTypes = null)
    {
        if (is_array($denyTypes) || is_null($denyTypes)) {
            $this->denyTypes = $denyTypes;
        } else if (is_string($denyTypes)) {
            $this->denyTypes = explode(' ', $denyTypes);
        }
    }

    /**
     * Возвращение запрещённых типов файлов
     *
     * @access      public
     * @return      array | null
     */
    public function getDenyTypes()
    {
        return $this->denyTypes;
    }

    /**
     * Задание разрешённых расширений файлов
     *
     * @access      public
     * @param       array | string | null
     */
    public function setAllowExtensions($allowExtensions = null)
    {
        if (is_array($allowExtensions) || is_null($allowExtensions)) {
            $this->allowExtensions = $allowExtensions;
        } else if (is_string($allowExtensions)) {
            $this->allowExtensions = explode(' ', $allowExtensions);
        }
    }

    /**
     * Возвращение разрешённых расширений файлов
     *
     * @access      public
     * @return      array | null
     */
    public function getAllowExtensions()
    {
        return $this->allowExtensions;
    }

    /**
     * Задание запрещённых расширений файлов
     *
     * @access      public
     * @param       array | string | null
     */
    public function setDenyExtensions($denyExtensions = null)
    {
        if (is_array($denyExtensions) || is_null($denyExtensions)) {
            $this->denyExtensions = $denyExtensions;
        } else if (is_string($denyExtensions)) {
            $this->denyExtensions = explode(' ', $denyExtensions);
        }
    }

    /**
     * Возвращение запрещённых расширений файлов
     *
     * @access      public
     * @return      array | null
     */
    public function getDenyExtensions()
    {
        return $this->denyExtensions;
    }

    /**
     * Определяет доступность расширения файла
     *
     * @access      public
     * @param       string
     */
    public function isAllowExtension($extension)
    {
        $extension = strtolower($extension);

        // Значение из кеша
        if (isset($this->extensionAccessCache[$extension])) {
            return $this->extensionAccessCache[$extension];
        }

        // Проверка по расширению
        if (is_array($this->allowExtensions) ? in_array($extension, $this->allowExtensions) : false) {
            $this->extensionAccessCache[$extension] = true;
            return true;
        }
        if (is_array($this->denyExtensions) ? in_array($extension, $this->denyExtensions) : false) {
            $this->extensionAccessCache[$extension] = false;
            return false;
        }

        // Проверка по типу
        if (!is_null($this->allowTypes)) {
            foreach ($this->allowTypes as $allow_type) {
                if (isset($this->types[$allow_type])) {
                    if (in_array($extension, $this->types[$allow_type])) {
                        $this->extensionAccessCache[$extension] = true;
                        return true;
                    }
                }
            }
        }
        if (!is_null($this->denyTypes)) {
            foreach ($this->denyTypes as $deny_type) {
                if (isset($this->types[$deny_type])) {
                    if (in_array($extension, $this->types[$deny_type])) {
                        $this->extensionAccessCache[$extension] = false;
                        return false;
                    }
                }
            }
        }

        // Если не найден в разрешённых типах, а они были указаны
        if (is_array($this->allowTypes)) {
            $this->extensionAccessCache[$extension] = false;
            return false;
        }

        // Проверка на скриптовость
        if (!$this->isAllow('script')) {
            if ($this->isScript($extension)) {
                $this->extensionAccessCache[$extension] = false;
                return false;
            }
        }

        $this->extensionAccessCache[$extension] = true;
        return true;
    }

    /**
     * Задание запрещённых расширений файлов
     *
     * @access      public
     * @param       sring
     * @return      boolean
     * @todo        найти checkFileType и переименовать
     * @todo        добавить проверку скриптов
     */
    public function isAllowFile($file)
    {
        $current_extensions = $this->getFileExtensions($file);
        if (count($current_extensions) > 0) {
            $last_extension = $current_extensions[count($current_extensions) - 1];
            return $this->isAllowExtension($last_extension);
        } else {
            return true;
        }
    }
    
    /**
     * Проверяет наличие переадресации
     *
     * @access      public
     * @param       string
     */
    public function isRedirect ()
    {
        return !empty($this->redirectUrl);        
    }
    
    /**
     * Задаёт переадресацию
     *
     * @access      public
     * @param       string
     */
    public function setRedirect ($url)
    {
        $this->redirectUrl = $url;        
    }   
    
    /**
     * Возвращает переадресацию
     *
     * @access      public
     * @return      string
     */
    public function getRedirect ()
    {
        return $this->redirectUrl;        
    }
    
    /**
     * Выводит сообщение
     *
     * @access      public
     * @param       string
     * @param       string
     */
    public function message ($text, $type)
    {
        $globals = $this->get('globals');
        $globals->message($text, $type);
    }    
    
    /**
     * Возвращает содержание
     *
     * @access      public
     * @param       string
     * @todo        Отработать уведомление, когда не найдена сессия пользователя
     */
    public function build ()
    {
        $globals = $this->get('globals');
        
        $this->sessionVars = Array();
        $session = $globals->getSession(); 
        if ($session->has($this->sessionKey)) {
            $session_get = $session->get($this->sessionKey);
            $this->sessionVars = array_merge($this->sessionVars, $session_get);
        }        

        // Запись корневой директории в сессионную переменную
        $this->sessionVars['base_path'] = $this->getBasePath();

        // Определение текущей директории
        if (isset($_GET['fileadmin']['dir'])) {
            $this->setCurrentPath('/' . $_GET['fileadmin']['dir']);
            $this->sessionVars['CDIR'] = $this->getCurrentPath();
        } else {
            $this->setCurrentPath('');
            $this->sessionVars['CDIR'] = $this->getCurrentPath();
        }

        if (empty($_GET['fileadmin']['op'])) {
            $content = $this->getMainPage();
        } elseif ($_GET['fileadmin']['op'] === 'edit') {
            if (!$this->isAllow('edit')) {
                $this->setRedirect($this->buildLink(Array('error' => 'error_edit')));
                return;
            }
            if ($this->isAllow('upload')) {
                if (!empty($_GET['fileadmin']['file']) ? $this->isAllowFile($_GET['fileadmin']['file']) : true) {
                    $content = $this->getEditFilePage();
                }
            } else {
                $this->setRedirect($this->buildLink(Array('error' => 'error_such_file_edit')));
                return;
            }
        } elseif ($_GET['fileadmin']['op'] === 'upload') {
            if (!$this->isAllow('upload')) {
                $this->setRedirect($this->buildLink(Array('error' => 'error_upload')));
                return;
            }
            $content = $this->getUploadPage();
        } elseif ($_GET['fileadmin']['op'] === 'httpupload') {
            if (!$this->isAllow('httpupload')) {
                $this->setRedirect($this->buildLink(Array('error' => 'error_http_upload')));
                return;
            }
            $content = $this->getHttpUploadPage();
        } elseif ($_GET['fileadmin']['op'] === 'symlink') {
            if (!$this->isAllow('symlink') || ((!isset($_GET['fileadmin']['edit']) || !$this->isAllow('edit')) && isset($_GET['fileadmin']['edit']))) {
                $this>setRedirect($this->buildLink(Array('error' => 'error_symlink')));
                return;
            }
            $content = $this->getSymlinkPage();
        }
        $session->set($this->sessionKey, $this->sessionVars);

        return $content;
    }

    /**
     * Строит ссылку
     *
     * @access      public
     * @param       array
     * @return      string
     */
    public function buildLink ($args = Array(), $exclude = Array()) {
        $router = $this->get('router');
        $request = $this->get('request');
        $not_inherit = Array('desc', 'file');
        if (isset($_GET['fileadmin']) ? is_array($_GET['fileadmin']) : false) {
            foreach ($_GET['fileadmin'] as $key => $value) {
                if (!isset($args[$key]) && !in_array($key, $exclude) && !in_array($key, $not_inherit)) {
                    $args[$key] = $value;
                }
            }
        }
        if (is_array($exclude)) {
            foreach ($exclude as $key) {
                unset($args[$key]);
            }
        }
        $ckeditor_args = Array();
        if (isset($_GET['CKEditor'])) {
            $ckeditor_args['CKEditor'] = $_GET['CKEditor'];
        }
        if (isset($_GET['CKEditorFuncNum'])) {
            $ckeditor_args['CKEditorFuncNum'] = $_GET['CKEditorFuncNum'];
        }
        if (isset($_GET['langCode'])) {
            $ckeditor_args['langCode'] = $_GET['langCode'];
        }
        $currentRoute = $request->attributes->get('_route');
        $type = $request->get('type');
        //$this->get('globals')->trace($type);
        $params = Array('fileadmin' => $args);
        if (!empty($type)) {
            $params['type'] = $type;
        }
        $params = array_merge($params, $ckeditor_args);
        $link = $router->generate($currentRoute, $params);  
        //$this->get('globals')->trace($link);
        return $link;
    }

    /**
     * Получаем основную страницу панели управления файлами
     *
     * @access      public
     * @return      string
     */
    public function getMainPage()
    {
        if (isset($this->sessionVars['BUFFER_ACTION'])) {
            $this->moveFiles(
                $this->sessionVars['BUFFER_ARRAY'],
                isset($this->sessionVars['BUFFER_ACTION']) && $this->sessionVars['BUFFER_ACTION'] === 'copy', $this->sessionVars['BUFFER_PATH']
            );
        }

        if (isset($_POST['act'])) {
            $act = $_POST['act'];
        } else {
            $act = 0;
        }

        if (in_array($act, Array(2, 3, 5, 6, 7, 8, 9, 10, 11))) {
            $arr = $this->getPostFiles($this);
        }

        if ($act !== 11 && isset($this->sessionVars['checked_files'])) {
            unset($this->sessionVars['checked_files']);
        }
        
        $globals = $this->get('globals');              

        switch($act) {
            case 1:
                $ret = $this->newDir($_POST['str']);
                break;
            case 2:
                $this->sessionVars['BUFFER_ARRAY'] = $arr;
                $this->sessionVars['BUFFER_ACTION'] = 'copy';
                $this->sessionVars['BUFFER_PATH'] = $this->sessionVars['CDIR'];
                $ret = $this->moveFiles($arr, true);
                break;
            case 3:
                $this->sessionVars['BUFFER_ARRAY'] = $arr;
                $this->sessionVars['BUFFER_ACTION'] = 'move';
                $this->sessionVars['BUFFER_PATH'] = $this->sessionVars['CDIR'];
                $ret = $this->moveFiles($arr, false);
                break;
            case 4:
                $over = false;
                if (empty($this->sessionVars['BUFFER_ARRAY'])) {
                    break;
                }
                if (isset($_POST['str'])) {
                    $conf = $_POST['str'];
                    if ($conf === "Yes") {
                        $over = "Yes";
                    } elseif ($conf === "No") {
                        $over = "No";
                    }
                }
                $pasteRet = $this->paste($over, $this->sessionVars['BUFFER_ARRAY'], $this->sessionVars['BUFFER_ACTION'], $this->sessionVars['BUFFER_PATH']);
                if ($pasteRet !== "exists") {
                    unset($pasteRet);
                }
                break;
            case 5:
                $ret = $this->deleteFiles($arr);
                break;
            case 6:
                $ret = $this->changeAttributes($arr, $_POST['str'], false);
                break;
            case 7:
                $ret = $this->changeAttributes($arr, $_POST['str'], true);
                break;
            case 8:
                $ret = $this->unzip($arr);
                break;
            case 9:
                $ret = $this->zip($_POST['str'], $arr);
                break;
            case 10:
                if (count($arr) < 1 || empty($_POST['str'])) {
                    break;
                }
                $ret = $this->renameFile($arr[0], $_POST['str']);
                /*                
                if (count($arr) < 1 || empty($_POST['str'])) {
                    break;
                }
                */
                //$globals->message($_POST['str']);               
                //$ret = $this->renameFile($arr[0], $_POST['str']);
                break;
            case 11:
                if (count($arr) > 0) {
                    $this->sessionVars['checked_files'] = $arr;
                }
                if (isset($_POST['str'])) {
                    $this->sessionVars['sort'] = $_POST['str'];
                }
                if (isset($this->sessionVars['sort'])) {
                    $narr = explode("*", $this->sessionVars['sort']);
                    foreach ($narr as $v) {
                        $srt = explode("_", $v);
                        $this->sessionVars[$srt[0]] = $srt[1];
                        if (isset($this->sessionVars['sort_arr'])) {
                            $k = array_search($srt[0], $this->sessionVars['sort_arr']);
                            if ($k) {
                                unset($this->sessionVars['sort_arr'][$k]);
                            }
                            array_unshift($this->sessionVars['sort_arr'], $srt[0]);
                            $this->sessionVars['sort_arr'] = array_values($this->sessionVars['sort_arr']);
                        }
                        else {
                            $this->sessionVars['sort_arr'] = array($srt[0]);
                        }
                    }
                }
                break;
            default: break;
        }

        $this->sessionVars['BUFFER_ARRAY'] = $this->getBuffer();

        $dup = $this->isDuplication();
        if ($dup) {
            $rstr = '<hr class="fileadmin-hr"/><b>' . $this->lang['buffer_action'] ;
            switch($dup) {
                case 'copy':
                    $rstr .= $this->lang['buffer_copy'];
                    break;
                case 'move':
                    $rstr .= $this->lang['buffer_move'];
                    break;
                default:
                    $rstr .= '???';
            }
            $rstr .= ')</b><br />';
            foreach($this->sessionVars['BUFFER_ARRAY'] as $v) {
                $rstr .= $v . '<br />';
            }
            $this->bufferHtml = $rstr;
        } else {
            $this->bufferHtml = '';
        }

        $args = isset($_GET['fileadmin']) ? $_GET['fileadmin'] : Array();
        $action_link = $this->buildLink($args);

        //$meta = $this->_Module->getMeta();
        //$User = $this->getUser();
        
        $this->showDescriptions();

        //$tmpl = $this->phptemplate('main');
        $args = isset($_GET['fileadmin']) ? $_GET['fileadmin'] : Array();
        $params = Array();
        $params['lang'] = $this->lang;
        //$params['teaser'] = $this->buildTeaser();
        $params['refresh_link'] = $this->buildLink($args);
        $params['current_dir'] = $this->sessionVars['CDIR'];
        $params['current_path'] = $this->sessionVars['CDIR'];
        $params['base_web_path'] = $this->getBaseWebPath();
        $params['mainmenu'] = $this->getMainMenuItems();
        $params['dirhistory'] = $this->splitDir($this->sessionVars['CDIR']);
        $params['top_offset'] = empty($args) ? 0 : 100;
        //$params['errors'] = $errors->build();
        $params['buffer'] = $this->bufferHtml;
        $params['action'] = $action_link;
        $params['columns'] = $this->getFilelistHead();
        $params['filelist'] = $this->getFilelistBody($this->listDir($this->getUserCurrentPath()));
        $params['files_count'] = $this->filescount;
        if (isset($pasteRet) ? $pasteRet === "exists" : false) {
            $params['rewrite'] = true;
        }
        
        $templating = $this->kernel->getContainer()->get('templating');

        $view = $templating->render(
            'NeagMainBundle:Fileadmin:main.html.php',
            $params
        ); 
        return $view;
        return '';
    }
    
    /**
     * Строит окно с уведомлением
     *
     * @access      public
     * @return      string
     */
    public function showDescriptions()
    {
        if (isset($_GET['fileadmin']['desc'])) {
            switch ($_GET['fileadmin']['desc']) {
                case 'file_save_ok':
                    $this->message(str_replace('{filename}', (string)$_GET['fileadmin']['file'], $this->lang['file_save_ok']), 'ok');
                    break;
                case 'upload_ok': 
                    $this->message($this->lang['file_uploaded'], 'ok');
                    break;
                case 'httpupload_ok':
                    $this->message($this->lang['file_uploaded'], 'ok');
                    break;
                case 'symlink_ok':
                    $this->message($this->lang['symlink_saved'], 'ok'); 
                    break;
            }
        }
    }

    /**
     * Получаем страницу редактирования файла
     *
     * @access      public
     * @return      string
     */
    public function getEditFilePage()
    {
        $charset = $this->getDefaultFileCharset();
        $charset_changed = false;
        if (isset($this->_COOKIE['fileadmin_file_charset'])) {
            if (in_array($this->_COOKIE['fileadmin_file_charset'], $this->getFileCharsets())) {
                $charset = $this->_COOKIE['fileadmin_file_charset'];
            }
        }
        if (isset($_POST['charset'], $_POST['refresh_charset'])) {
            if (in_array($_POST['charset'], $this->getFileCharsets())) {
                $charset = $_POST['charset'];
                $charset_changed = true;
                $this->setCookie('fileadmin_file_charset', $charset, time()+60*60*24*30);
            }
        }
        $tmpl = $this->phptemplate('edit');

        $args = Array();
        $args['dir'] = $this->sessionVars['CDIR'];
        $tmpl->addVar('back_link', $this->buildLink($args, Array('op', 'desc', 'file')));
        $args['op'] = 'edit';
        if (!empty($_GET['fileadmin']['file'])) {
            $args['file'] = $_GET['fileadmin']['file'];
        }
        $action = $this->buildLink($args, Array('desc'));

        $options = Array(
            'action' => $action,
            'method' => 'post',
            'width' => '100%',
            'title' => $this->lang['edit_title'],
            'validate' => Array($this, 'editFormValidate'),
            'handle' => Array($this, 'editFormHandle'),
        );

        $values = Array();

        if (!empty($_GET['fileadmin']['file'])) {
            $values['filename'] = $_GET['fileadmin']['file'];
            $lp = $this->getUserCurrentFilePath($_GET['fileadmin']['file']);
            if (file_exists($lp) ? is_readable($lp) : false) {
                //$values['chmod'] = $this->formAttributes(@fileperms($lp));
                $content = @file_get_contents($lp);
                $content = @iconv($charset, $this->getCharset(), $content);
                $values['content'] = $content;
            }
        }

        $this->loadClass('xbkForm', 'form', true);
        $Form = $this->factory('xbkForm', 'fileadmin_edit', $options, $values);

        $def = Array(
            'title' => $this->lang['file_name'],
            'size' => 30,
        );
        $Form->addElement('text', 'filename', $def, 1);

        $def = Array(
            'title' => $this->lang['file_rewrite'],
            'default_value' => 'yes',
        );
        if (!isset($_GET['fileadmin']['file'])) {
            $def['default_value'] = 'no';
        }
        $Select = $Form->addElement('select', 'rewrite', $def, 2);
        $Select->addOption(Array('label' => $this->lang['file_rewrite_yes'], 'value' => 'yes'));
        $Select->addOption(Array('label' => $this->lang['file_rewrite_no'], 'value' => 'no'));

        $def = Array(
        'title' => $this->lang['file_charset'],
        'suffix' => '<input type="submit" name="refresh_charset" value="'.htmlspecialchars($this->lang['file_charset_refresh']).'" />',
        'default_value' => $charset,
        );
        $Select = $Form->addElement('select', 'charset', $def, 3);
        foreach ($this->getFileCharsets() as $charset) {
            $Select->addOption(Array('label' => $charset, 'value' => $charset));
        }

        $def = Array(
        //'title' => $this->lang['file_content'],
        'resizable' => true,
        );
        if ($charset_changed) {
                $def['force_input_value'] = false;
        }
        $Form->addElement('textarea', 'content', $def, 4);

        $SubmitGroup = $Form->addElement('submit_group', 'submit_group', Array('align' => 'left'), 5);

        $def = Array(
        'value' => $this->lang['file_save'],
        );
        $SubmitGroup->add('save', $def, 1);

        $def = Array(
        'value' => $this->lang['file_apply'],
        );
        $SubmitGroup->add('apply', $def, 2);

        $def = Array(
        'value' => $this->lang['file_return'],
        );
        $SubmitGroup->add('return', $def, 3);

        $tmpl->addVar('teaser', $this->buildTeaser());
        $tmpl->addVar('form', $Form->build());

        return $tmpl->build();
    }

    /**
     * Валидация формы редактирования файла
     *
     * @access      public
     * @param       array
     * @param       object xbkErrorStack
     * @return      boolean
     */
    public function editFormValidate(&$values, $ErrorStack)
    {
        if (isset($values['filename'], $values['rewrite'], $values['charset'], $values['content'])) {
            if ($this->isTransliterateFilenames()) {
                $values['filename'] = $this->transliterateFilename($values['filename']);
            }
            if (isset($values['save']) || isset($values['apply'])) {
                if (empty($values['filename'])) {
                    // Не указано имя файла
                    $ErrorStack->addError($this->lang['file_error_empty_filename'], null, 'filename');
                } else {
                    if ($this->isAllowFile($values['filename'])) {                         
                        $lp = $this->getUserCurrentFilePath($values['filename']);
                        $rewrite = xbkFunctions::str2bool($values['rewrite']);
                        if (@file_exists($lp) && !$rewrite) {
                            // Невозможно перезаписать существующий файл
                            $ErrorStack->addError($this->lang['file_error_file_exists']);
                        } else {
                            $dirname = dirname($lp);
                            if (is_writable($dirname) && (@file_exists($lp) ? is_writable($lp) : true)) {
                                return true;
                            } else {
                                // Недостаточно прав, чтобы сохранить в указанную папку
                                $ErrorStack->addError($this->lang['file_error_unable_save']);
                            }
                        }
                    } else {
                        // Недопустимое имя файла
                        $ErrorStack->addError($this->lang['file_error_type'], null, 'filename');
                    }
                }
            } else if (isset($values['return'])) {
                // Переадресация на список файлов
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

    /**
     * Обработка формы редактирования файла
     *
     * @access      public
     * @param       array
     * @return      boolean
     */
    public function editFormHandle(&$values)
    {
        if (isset($values['return'])) {
            // Переадресация на список файлов
            $this->setRedirect($this->buildLink(Array('file' => $values['filename']), Array('op', 'file', 'desc')));
        } else {
            $content = @iconv($this->getCharset(), $values['charset'], $values['content']);
            $this->setCookie('fileadmin_file_charset', $values['charset'], time()+60*60*24*30);
            $lp = $this->getUserCurrentFilePath($values['filename']);
            @unlink($lp);
            $f = @fopen($lp, "w+");
            @fwrite($f, $content);
            @fclose($f);
            //@chmod($lp, $this->checkAttributes(decoct((int)$values['chmod'])));
            if (isset($values['save'])) {
                $this->setRedirect($this->buildLink(Array('file' => $values['filename'], 'desc' => 'file_save_ok'), Array('op')));
            } else {
                $this->setRedirect($this->buildLink(Array('file' => $values['filename'], 'desc' => 'file_save_ok')));
            }
        }
    }

    /**
     * Получаем страницу загрузки файла
     *
     * @access      public
     * @return      string
     */
    public function getUploadPage()
    {
        //$tmpl = $this->phptemplate('upload');

        //$tmpl->addVar('teaser', $this->buildTeaser());

        $args = Array();
        $args['dir'] = $this->sessionVars['CDIR'];
        $args['op'] = 'upload';
        $action = $this->buildLink($args, Array('desc'));

        $options = Array(
            'action' => $action,
            'method' => 'post',
            'title' => $this->lang['upload_title'],
            'width' => '400px',
            'validate' => Array($this, 'uploadFormValidate'),
            'handle' => Array($this, 'uploadFormHandle'),
        );

        /*
        $this->loadClass('xbkForm', 'form', true);
        $Form = $this->factory('xbkForm', 'fileadmin_upload', $options);

        $def = Array(
        'title' => $this->lang['file'],
        'description' => '(max '.ini_get('upload_max_filesize').')',
        );
        $Form->addElement('file', 'userfile', $def, 1);

        $def = Array(
        'title' => $this->lang['rights'],
        'size' => 3,
        'maxlength' => 3,
        'default_value' => 775,
        );
        $Form->addElement('text', 'perms', $def, 2);

        $def = Array(
        'value' => $this->lang['upload'],
        );
        $Form->addElement('submit', 'submit', $def, 3);
        */

        //$tmpl->addVar('form', $Form->build());

        //return $tmpl->build();
        
        $globals = $this->get('globals');
        
        // Валидация и обработка формы
        if (!empty($_POST['submit']) && !empty($_FILES['userfile'])) {
            $has_errors = false;
            if (isset($_FILES['userfile']['name'], $_FILES['userfile']['tmp_name'])) {
                $uploadfile = $this->getUserCurrentFilePath($_FILES['userfile']['name']);
                $to = $this->checkFileName($uploadfile);
                if ($this->isAllowFile($_FILES['userfile']['name'])) {
                    if (@is_uploaded_file($_FILES['userfile']['tmp_name'])) {
                        if (@is_writable(@dirname($this->transliterateFilename($uploadfile)))) {
                            //return true;
                        } else {
                            $has_errors = true;
                            $globals->message($this->lang['error_not_enough_rights'], 'error');
                        }
                    } else {
                        $has_errors = true;
                        $message = str_replace('{error}', $_FILES['userfile']['error'], $this->lang['error_file_upload']);
                        $globals->message($message, 'error');
                    }
                } else {
                    $has_errors = true;
                    $globals->message($this->lang['error_such_file_upload'], 'error');
                }
            } else {
                $has_errors = true;
                $globals->message('Неверные данные формы', 'error');
                //return false;
            }
            if (!$has_errors) {               
                $uploadfile = $this->getUserCurrentFilePath($_FILES['userfile']['name']);
                if ($this->isTransliterateFilenames()) {
                    $uploadfile = $this->transliterateFilename($uploadfile);
                }
                $to = $this->checkFileName($uploadfile);
                if ($uploadfile !== $to) {
                    @rename($uploadfile, $to);
                }
                if (@move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
                    //$p = $this->checkAttributes($values['perms']);
                    @chmod($uploadfile, 0666);
                    $args = Array();
                    $args['dir'] = $this->sessionVars['CDIR'];
                    $args['op'] = 'upload';
                    $args['desc'] = 'upload_ok';
                    $globals->message('Файл успешно загружен', 'ok');
                    $this->setRedirect($this->buildLink($args));
                    return;
                } else {
                    $has_errors = true;
                    $globals->message('Не удалось переместить файл', 'error');
                    //return false;
                }                
            }
        } else {
            
        }
        
        $params = Array();
        $params['action'] = $this->buildLink($args, Array('desc'));
        $params['back_link'] = $this->buildLink($args, Array('op', 'desc'));
        $params['lang'] = $this->lang;
        $templating = $this->kernel->getContainer()->get('templating');
        $view = $templating->render(
            'NeagMainBundle:Fileadmin:upload.html.php',
            $params
        ); 
        return $view;
        return '';
    }

    /**
     * Валидация формы загрузки файла
     *
     * @access      public
     * @param       array
     * @param       object xbkErrorStack
     * @return      boolean
     */
    public function uploadFormValidate($values, $ErrorStack)
    {
        if (isset($this->_FILES['userfile']['name'], $this->_FILES['userfile']['tmp_name'], $values['perms'])) {
            $uploadfile = $this->getUserCurrentFilePath($this->_FILES['userfile']['name']);
            $to = $this->checkFileName($uploadfile);
            if ($this->isAllowFile($this->_FILES['userfile']['name'])) {
                if (@is_uploaded_file($this->_FILES['userfile']['tmp_name'])) {
                    if (@is_writable(@dirname($this->transliterateFilename($uploadfile)))) {
                        return true;
                    } else {
                        $ErrorStack->add($this->lang['error_not_enough_rights'], null, 'userfile');
                    }
                } else {
                    $message = str_replace('{error}', $_FILES['userfile']['error'], $this->lang['error_file_upload']);
                    $ErrorStack->add($message, null, 'userfile');
                }
            } else {
                $ErrorStack->add($this->lang['error_such_file_upload'], null, 'userfile');
            }
        } else {
            return false;
        }
    }

    /**
     * Обработка формы загрузки файла
     *
     * @access      public
     * @param       array
     * @return      boolean
     * @todo        Транслитерация
     */
    public function uploadFormHandle($values)
    {
        $uploadfile = $this->getUserCurrentFilePath($this->_FILES['userfile']['name']);
        if ($this->isTransliterateFilenames()) {
            $uploadfile = $this->transliterateFilename($uploadfile);
        }
        $to = $this->checkFileName($uploadfile);
        if ($uploadfile !== $to) {
            @rename($uploadfile, $to);
        }
        if (@move_uploaded_file($this->_FILES['userfile']['tmp_name'], $uploadfile)) {
            $p = $this->checkAttributes($values['perms']);
            @chmod($uploadfile, $p);
            $args = Array();
            $args['dir'] = $this->sessionVars['CDIR'];
            $args['op'] = 'upload';
            $args['desc'] = 'upload_ok';
            $this->setRedirect($this->buildLink($args));
        } else {
            return false;
        }
    }

    /**
     * Получаем страницу загрузки файла с удалённого сервера
     *
     * @access      public
     * @return      string
     */
    public function getHttpUploadPage()
    {
        $tmpl = $this->phptemplate('httpupload');

        $tmpl->addVar('teaser', $this->buildTeaser());

        $args = Array();
        $args['dir'] = $this->sessionVars['CDIR'];
        $tmpl->addVar('back_link', $this->buildLink($args, Array('op', 'desc')));
        $args['op'] = 'httpupload';
        $action = $this->buildLink($args, Array('desc'));

        $options = Array(
        'action' => $action,
        'method' => 'post',
        'title' => $this->lang['httpupload_title'],
        'width' => '600px',
        'validate' => Array($this, 'httpuploadFormValidate'),
        'handle' => Array($this, 'httpuploadFormHandle'),
        );

        $this->loadClass('xbkForm', 'form', true);
        $Form = $this->factory('xbkForm', 'fileadmin_httpupload', $options);

        $def = Array(
        'title' => $this->lang['http_url'],
        'description' => $this->lang['http_url_description'],
        'size' => 40,
        );
        $Form->addElement('text', 'url', $def, 1);

        $def = Array(
        'title' => $this->lang['local_file_name'],
        'size' => 40,
        );
        $Form->addElement('text', 'local', $def, 2);

        $def = Array(
        'title' => $this->lang['bytes_to_upload'],
        'description' => $this->lang['bytes_to_upload_description'],
        'size' => 10,
        );
        $Form->addElement('text', 'size', $def, 3);

        $def = Array(
        'value' => $this->lang['upload'],
        );
        $Form->addElement('submit', 'submit', $def, 4);

        $tmpl->addVar('form', $Form->build());

        return $tmpl->build();
    }

    /**
     * Валидация формы загрузки файла с удалённого сервера
     *
     * @access      public
     * @param       array
     * @param       object xbkErrorStack
     * @return      boolean
     */
    public function httpuploadFormValidate(&$values, $ErrorStack)
    {
        if (isset($values['url'], $values['local'], $values['size'])) {
            if ($this->isTransliterateFilenames()) {
                $values['local'] = $this->transliterateFilename($values['local']);
            }
            $lp = $this->getUserCurrentFilePath($values['local']);
            if ($this->isAllowFile($lp)) {
                return true;
            } else {
                // Недопустимый файл
                $ErrorStack->addError($this->lang['error_such_file_upload'], null, 'local');
            }
        } else {
            return false;
        }
    }

    /**
     * Обработка формы загрузки файла с удалённого сервера
     *
     * @access      public
     * @param       array
     * @return      boolean
     */
    public function httpuploadFormHandle($values)
    {
        $lp = $this->getUserCurrentFilePath($values['local']);
        if (!empty($values['size']) ? (is_numeric($values['size']) || $values['size'] == 0) : false) {
            $recv = (int)$values['size'];
            $onerecv = 1;
        } else {
            $recv = 1024;
            $onerecv = 0;
        }
        $to = $this->checkFileName($lp);
        if ($lp !== $to) {
            @rename($lp, $to);
        }
        $local = @fopen($lp, "w+");
        $remote = @fopen($values['url'], "r");
        while (!feof($remote)) {
            $dat = @fread($remote, $recv);
            @fwrite($local, $dat);
            if ($onerecv) {
                break;
            }
        }
        @fclose($local);
        $this->setRedirect($this->buildLink(Array('desc' => 'httpupload_ok')));
    }

    /**
     * Получаем страницу редактирования симлинка
     *
     * @access      public
     * @return      string
     */
    public function getSymlinkPage()
    {
        $tmpl = $this->phptemplate('httpupload');

        $tmpl->addVar('teaser', $this->buildTeaser());

        $values = Array();

        $args = Array();
        $args['dir'] = $this->sessionVars['CDIR'];
        $tmpl->addVar('back_link', $this->buildLink($args, Array('op', 'desc')));
        $args['op'] = 'symlink';
        if (isset($_GET['edit'])) {
            $args['edit'] = $_GET['edit'];
        }
        $action = $this->buildLink($args, Array('desc'));

        $options = Array(
        'action' => $action,
        'method' => 'post',
        'title' => $this->lang['symlink_title'],
        'width' => '600px',
        'validate' => Array($this, 'symlinkFormValidate'),
        'handle' => Array($this, 'symlinkFormHandle'),
        );

        $this->loadClass('xbkForm', 'form', true);
        $Form = $this->factory('xbkForm', 'fileadmin_symlink', $options, $values);

        $def = Array(
        'title' => $this->lang['symlink_name'],
        'size' => 30,
        );
        $Form->addElement('text', 'link', $def);

        $def = Array(
        'title' => $this->lang['file'],
        'size' => 40,
        );
        $Form->addElement('text', 'target', $def);

        $def = Array(
        'value' => $this->lang['save_symlink'],
        );
        $Form->addElement('submit', 'submit', $def, 4);

        $tmpl->addVar('form', $Form->build());

        return $tmpl->build();
    }

    /**
     * Валидация формы симлинка
     *
     * @access      public
     * @param       array
     * @param       object xbkErrorStack
     * @return      boolean
     */
    public function symlinkFormValidate(&$values, $ErrorStack)
    {
        if (empty($values['link'])) {
            $ErrorStack->addError($this->lang['error_symlink_name'], null, 'link');
        }
        if (empty($values['target'])) {
            $ErrorStack->addError($this->lang['error_symlink_target'], null, 'target');
        }
    }

    /**
     * Обработка формы симлинка
     *
     * @access      public
     * @param       array
     * @param       object xbkErrorStack
     * @return      boolean
     */
    public function symlinkFormHandle(&$values, $ErrorStack)
    {
        if (empty($_GET['edit'])) {
            // Создание нового симлинка
            $link = $this->getUserCurrentFilePath((string)$values['link']);
            $target = (string)$values['target'];
            if (is_link($link)) {
                $ErrorStack->addError($this->lang['error_symlink_exists'], null, 'link');
                return false;
            } elseif (!symlink($target, $link)) {
                $ErrorStack->addError($this->lang['error_symlink_save'], null, 'link');
                return false;
            } else {
                $this->setRedirect($this->buildLink(Array('desc' => 'symlink_ok')));
                return true;
            }
        } else {
            // Редактирование
            $link_old = $this->getUserCurrentFilePath((string)$_GET['edit']);
            $link_new = $this->getUserCurrentFilePath((string)$values['link']);
             if (is_link($link_new)) {
                $ErrorStack->addError($this->lang['error_symlink_exists'], null, 'link');
                return false;
            }
            @unlink($link_old);
            if (!symlink($target, $link_new)) {
                $ErrorStack->addError($this->lang['error_symlink_save'], null, 'link');
                return false;
            }
            $this->setRedirect($this->buildLink(Array('desc' => 'symlink_ok', 'edit' => $values['link'])));
            return true;
        }
    }

    /**
     * Формирование основного меню
     *
     * @access      public
     * @return      array
     */
    public function getMainMenuItems()
    {
        $arr = Array();
        if ($this->isAllow('upload')) {
            //$arr[] = Array('create_file', $this->buildLink(Array('op' => 'edit')),  'new_file.gif');
        }
        if ($this->isAllow('newdir')) {
            $arr[] = Array('create_dir', 'javascript:fileadmin_create_dir()',  'new_fold.gif');
        }
        if ($this->isAllow('copy')) {
            array_push($arr, Array('copy', 'javascript:fileadmin_copy()',  'copy.gif'));
            if ($this->isAllow('delete')) {
                array_push($arr, Array('cut', 'javascript:fileadmin_cut()', 'move.gif'));
            }
            if ($this->isDuplication()) {
                array_push($arr, Array('paste', 'javascript:fileadmin_paste()', 'paste.gif'));
            }
        }
        if ($this->isAllow('delete')) {
            array_push($arr, Array('delete', 'javascript:fileadmin_delete()', 'del.gif'));
        }
        if ($this->isAllow('upload') && ini_get('file_uploads')) {
            $args = $this->args;
            $args['op'] = 'upload';
            $args['dir'] = $this->sessionVars['CDIR'];
            $arr[] = Array('upload', $this->buildLink($args),  'upload.gif');
        }
        if ($this->isAllow('chmod')) {
            array_push($arr, Array('attr_change', 'javascript:fileadmin_change_attributes(false)', 'chmod.gif'));
            array_push($arr, Array('attr_change_rec', 'javascript:fileadmin_change_attributes(true)',  'chmod.gif'));
        }
        if ($this->isAllow('unzip')) {
            array_push($arr, Array('unzip', 'javascript:fileadmin_unzip()', 'unzip.gif'));
        }
        if ($this->isAllow('zip')) {
            array_push($arr, Array('zip', 'javascript:fileadmin_zip()', 'zip.gif'));
        }
        if ($this->isAllow('httpupload') && ini_get('allow_url_fopen')) {
            $args = $this->args;
            $args['op'] = 'httpupload';
            $args['dir'] = $this->sessionVars['CDIR'];
            array_push($arr, Array('http_upload', $this->buildLink($args), 'http_up.gif'));
        }
        if (($this->isAllow('symlink')) && !stristr($_SERVER['SERVER_SOFTWARE'], "win32")) {
            $args = $this->args;
            $args['op'] = 'symlink';
            $args['dir'] = $this->sessionVars['CDIR'];
            array_push($arr, Array('symlink', $this->buildLink($args), 'symlink.gif'));
        }
        $narr = Array();
        foreach($arr as $item) {
            array_push($narr, Array(
            'name' => $this->lang[$item[0]],
            'href' => $item[1],
            'title' => $this->lang[$item[0].'_title'],
            'img' => $item[2]
            ));
        }
        return $narr;
    }

    /**
     * Нарезка текущего пути
     *
     * @access      public
     * @param       string     текущий путь
     * @return      array
     */
    public function splitDir($str) {
        $arr = explode('/', $str);
        $p = '';
        $narr = Array();
        $parents = Array();
        foreach($arr as $v) {
            $path = "/".implode(array_merge($parents, Array($v)), '/');
            array_push($narr, Array(
            'link' => $this->buildLink(Array('dir' => $path)),
            'name' => $v,
            //'name' => $p.$v
            ));
            $p .= $v;
            $parents[] = $v;
        }
        return $narr;
    }

    /**
     * Получение значений для заголовка таблицы файлов
     *
     * @access      public
     * @param       array
     * @return      array
     */
    public function getFilelistHead($arr = Array()) {
        /*
        $narr = Array(
        'COL_SORT' => Array(),
        'COL_NAME' => Array(
        $this->lang['col_name'],
        $this->lang['col_type'],
        $this->lang['col_size'],
        $this->lang['col_mod']
        )
        );
        */
        $narr = Array();

        $col = Array();
        $col['title'] = $this->lang['col_name'];
        if (isset($this->sessionVars['nm']) && $this->sessionVars['nm'] === 'dsc') {
            //array_push($narr['COL_SORT'], 'nm_asc');
            $col['sort'] = 'nm_asc';
        } else {
            //array_push($narr['COL_SORT'], 'nm_dsc');
            $col['sort'] = 'nm_dsc';
        }
        $narr[] = $col;

        $col = Array();
        $col['title'] = $this->lang['col_type'];
        if (isset($this->sessionVars['tp']) && $this->sessionVars['tp'] === 'dsc') {
            //array_push($narr['COL_SORT'], 'tp_asc');
            $col['sort'] = 'tp_asc';
        } else {
            //array_push($narr['COL_SORT'], 'tp_dsc');
            $col['sort'] = 'tp_dsc';
        }
        $narr[] = $col;

        $col = Array();
        $col['title'] = $this->lang['col_size'];
        if (!isset($this->sessionVars['sz']) || $this->sessionVars['sz'] === 'dsc') {
            //array_push($narr['COL_SORT'], 'sz_asc');
            $col['sort'] = 'sz_asc';
        } else {
            //array_push($narr['COL_SORT'], 'sz_dsc');
            $col['sort'] = 'sz_asc';
        }
        $narr[] = $col;

        $col = Array();
        $col['title'] = $this->lang['col_mod'];
        if (!isset($this->sessionVars['tm']) || $this->sessionVars['tm'] === 'dsc') {
            //array_push($narr['COL_SORT'], 'tm_asc');
            $col['sort'] = 'tm_asc';
        } else {
            //array_push($narr['COL_SORT'], 'tm_dsc');
            $col['sort'] = 'tm_dsc';
        }
        $narr[] = $col;

        if ($this->isAllow('chmod')) {
            $col = Array();
            $col['title'] = $this->lang['col_attr'];
            //array_push($narr['COL_NAME'], $this->lang['col_attr']);
            if (!isset($this->sessionVars['at']) || $this->sessionVars['at'] === 'dsc') {
                //array_push($narr['COL_SORT'], 'at_asc');
                $col['sort'] = 'at_asc';
            } else {
                //array_push($narr['COL_SORT'], 'at_dsc');
                $col['sort'] = 'at_dsc';
            }
            $narr[] = $col;
        }

        return $narr;
    }

    /**
     * Форматирование текущей директории
     *
     * @access      public
     * @param       array
     * @return      array
     */
    public function getFilelistBody($arr) {
        $globals = $this->get('globals');
        $narr = Array();
        $c = 2;
        // @todo
        //$path_to_img = '/images/fileadmin/';
        $path_to_img = $this->get('templating.helper.assets')->getUrl('images/fileadmin');
        //$globals->trace($path_to_img);
        $router = $this->get('router');
        if (isset($arr[0]) ? $arr[0] != '' : false) {
            foreach ($arr as $v) {
                $item = Array();
                $item['id'] = $c;
                $item['name'] = $v['name'];
                $item['size'] = $v['size'];
                $item['check'] = $v['checked'];
                $item['attr'] = '';
                if ($c%2 == 0) {
                    $item['class'] = 'fileadmin-fgray';
                } else {
                    $item['class'] = 'fileadmin-fwhite';
                }
                $item['chmod'] = 'chmod';
                if ($this->isAllow('chmod')) {
                    $item['attr'] = $v['at'];
                }
                if ($v['type'] == 0) {
                    $item['item'] = 'sub';
                    $parts = Array();
                    if (isset($_GET['fileadmin']['dir'])) {
                        $parts[] = trim($_GET['fileadmin']['dir'], '/');
                    }
                    $parts[] = $item['name'];
                    $args['dir'] = '/'.implode($parts, '/');
                    $item['link'] = $this->buildLink($args);
                } elseif ($v['type'] == 1) {
                    $item['edit_item'] = 'Symlink';
                    if ($this->isAllow('edit')) {
                        $item['edit'] = 'edit';
                    }
                    $item['item'] = 'link';
                    $item['target'] = $v['target'];
                    $args = $this->args;
                    $parts = Array();
                    if (!empty($args['dir'])) {
                        $parts = explode($args['dir']);
                    }
                    $parts[] = $item['name'];
                    $args['dir'] = '/'.implode($parts, '/');
                    $item['link'] = $this->buildLink($args);
                } elseif ($v['type'] == 2) {
                    $item['item'] = 'file';
                    $item['type'] = $this->getFileType($item['name']);
                    $item['time'] = $v['time'];
                    $item['ext'] = $v['ext'];
                }
                $extensions = $this->getFileExtensions($item['name']);
                $pr = $this->isAllowFile($item['name']);
                $item['actions'] = Array();
                if (($pr || $v['type'] == 0) && $this->isAllow('rename')) {
                    //$item['rename'] = 'rename';
                    $rename_action = Array();
                    $rename_action['title'] = $this->lang['rename'];
                    $rename_action['alt'] = $this->lang['rename'];
                    $rename_action['link'] = 'javascript:fileadmin_rename('.$item['id'].')';
                    $rename_action['img'] = 'rename.gif';
                    $item['actions'][] = $rename_action;
                }                
                if ($v['type'] == 2) {                    
                    if (count($extensions) > 0) {
                        $zp = $extensions[count($extensions)-1];
                    } else {
                        $zp = "";
                    }
                    if ($pr && $this->isAllow('edit') && in_array($this->getFileType($item['name']), Array('text', 'html', 'unknown'))) {
                        //$item['edit'] = 'edit';
                        $edit_action = Array();
                        $edit_action['title'] = $this->lang['edit'];
                        $edit_action['alt'] = $this->lang['edit'];
                        $edit_action['link'] = $this->buildLink(Array('op' => 'edit', 'file' => $item['name']));
                        $edit_action['img'] = 'edit.gif';
                        $item['actions'][] = $edit_action;
                    }                    
                    if ($this->isAllow('download')) {
                        $args['fileadmin'] = Array();
                        if (isset($_GET['fileadmin'])) {
                            $args['fileadmin'] = $_GET['fileadmin'];
                        }
                        //$args['fileadmin']['session_id'] = $this->Session->getId();                        
                        $args['fileadmin']['dir'] = $this->sessionVars['CDIR'];
                        $args['fileadmin']['file'] = $v['name'];
                        $args['fileadmin']['session_key'] = $this->getSessionKey();
                        $download_action = Array();
                        $download_action['title'] = $this->lang['download'];
                        $download_action['alt'] = $this->lang['download'];                                                
                        $download_action['link'] = $router->generate('fileadmin_download', $args);
                        $download_action['img'] = 'get_file.gif';
                        $item['actions'][] = $download_action;
                    }
                    $item['href'] = $path_to_img.'/'.rawurlencode($item['name']);
                    //$globals->trace($item['href']);
                    $dir = trim($this->sessionVars['CDIR'], '/') == '' ? '' : trim($this->sessionVars['CDIR'], '/').'/';
                    //$globals->trace($dir.$item['name']);
                    $attr = call_user_func($this->getFileCallback(), $dir, $item['name'], $this);
                    $attr_str = '';
                    foreach ($attr as $key => $value) {
                        $attr_str .= ' ';
                        $attr_str .= htmlspecialchars($key).'="'.htmlspecialchars($value).'"';
                    }
                    $item['a_attributes'] = $attr_str;  
                }
                $narr[] = $item;
                $c++;                
            }
            $this->filescount = $c;
        }
        return $narr;
    }

    /**
     * Возвращает атрибуты тега a для файла в списке
     *
     * @access      public
     * @param       string
     * @param       object xbkFileadmin
     * @return      array
     */
    public static function getFileAttributes($dir, $file, $FileAdmin) {
        $attr = Array();
        $attr['href'] = $FileAdmin->getBaseWebPath().$dir.rawurlencode($file);
        return $attr;
    }

    /**
     * Получает массив файлов директории
     *
     * @access      public
     * @param       string
     * @return      array
     * @todo        убрать $str != "." && $str != ".."
     */
    public function listDir($arg) {
        date_default_timezone_set('UTC');
        $i = 0;
        if ($handle = @opendir($arg)) {
            while (($str = readdir($handle)) !== false) {
                if ($str != "." && $str != "..") {
                    $a = $arg . '/' . $str;
                    $arr[$i]['name'] = htmlspecialchars($str);;
                    $arr[$i]['size'] = '---';
                    $arr[$i]['at'] = '---';
                    if (isset($this->sessionVars['checked_files'])) {
                        if (in_array($arr[$i]['name'], $this->sessionVars['checked_files'])) {
                            $arr[$i]['checked'] = "checked='checked'";
                        } else {
                            $arr[$i]['checked'] = "";
                        }
                    } else {
                        $arr[$i]['checked'] = "";
                    }
                    if (is_file($a)) {
                        $arr[$i]['type'] = 2;
                        $arr[$i]['size'] = filesize($a);
                        $arr[$i]['time'] = date ("Y.m.d / H:i:s", filemtime($a));
                        $narr = explode(".", $arr[$i]['name']);
                        if (count($narr) > 0) {
                            $arr[$i]['ext'] = $narr[count($narr)-1];
                        } else {
                            $arr[$i]['ext'] = "";
                        }
                        $arr[$i]['at'] = $this->formAttributes(fileperms($a));
                    } elseif (is_link($a)) {
                        $arr[$i]['type'] = 1;
                        $arr[$i]['target'] = readlink($a);
                    } else {
                        $arr[$i]['at'] = $this->formAttributes(fileperms($a));
                        $arr[$i]['type'] = 0;
                    }
                    $i++;
                }
            }
            closedir($handle);
            if ($i == 0) {
                $arr[0] = "";
            }
        } else {
            return array();
        }
        if (!isset($this->sessionVars['sort'])) {
            $this->sessionVars['sort_arr'] = array('tp', 'nm');
            $this->sessionVars['tp'] = 'asc';
            $this->sessionVars['nm'] = 'asc';
        }
        usort($arr, Array($this, "sortProc"));
        return $arr;
    }

    /**
     * Метод обратного вызова для сортировки
     *
     * @access      public
     * @param       array
     * @param       array
     * @return      integer
     */
    public function sortProc($a, $b) {
        $dcmp = false;
        $ret = 0;
        foreach ($this->sessionVars['sort_arr'] as $srt) {
            if ($srt === 'nm') {
                $s1 = strtolower($a['name']);
                $s2 = strtolower($b['name']);
            }
            elseif ($srt === 'at' && ($a['type'] !== 1 || $b['type'] !== 1)) {
                $s1 = strtolower($a['at']);
                $s2 = strtolower($b['at']);
            }
            elseif ($a['type'] === 2 && $b['type'] === 2) {
                if ($srt === 'tp' ) {
                    $s1 = strtolower($a['ext']);
                    $s2 = strtolower($b['ext']);
                }
                elseif ($srt === 'sz' ) {
                    $s1 = $a['size'];
                    $s2 = $b['size'];
                }
                elseif ($srt === 'tm' ) {
                    $s1 = strtolower($a['time']);
                    $s2 = strtolower($b['time']);
                }
            }
            elseif ($a['type'] !== $b['type']) {
                if ($srt === 'tp' ) {
                    $s1 = $a['type'];
                    $s2 = $b['type'];
                    $dcmp = true;
                } else {
                    continue;
                }
            } else {
                continue;
            }
            if ($srt === 'sz' || $dcmp) {
                if ($s1 < $s2) {
                    $ret = -1;
                } elseif ($s1 > $s2) {
                    $ret = 1;
                } else {
                    $ret = 0;
                }
            } else {
                $ret = strcmp($s1, $s2);
            } if ($ret === 0) {
                continue;
            } if ($this->sessionVars[$srt] === 'dsc') {
                $ret = -$ret;
            }
            return $ret;
        }
        return $ret;
    }

    /**
     * Получает тип файла
     *
     * @access      public
     * @param       string
     * @return      string
     */
    public function getFileType($fname) {
        // Типы файлов можно дописывать/удалять здесь
        /*
        $f_all_imgs = array('picture', 'sound', 'text', 'archive', 'html');
        $f_imgs['picture'] = array('gif', 'jpg', 'jpeg', 'png', 'bmp', 'tif');
        $f_imgs['sound'] = array('mp3', 'wav', 'wma', 'ogg', 'mpg');
        $f_imgs['text'] = array('txt', 'php', 'php3', 'php4', 'php5', 'phtml', 'htaccess', 'css');
        $f_imgs['archive'] = array('rar', 'zip', 'arj', 'gz', '7z');
        $f_imgs['html'] = array('html', 'htm');

        $a = explode('.', $fname);
        if (count($a) > 0) {
        $type = $a[count($a)-1];
        } else {
        $type = "";
        }
        foreach($f_all_imgs as $v) {
        if (in_array($type, $f_imgs[$v])) {
        return $v;
        }
        }
        return 'unknown';
        */

        $a = explode('.', $fname);
        if (count($a) > 0) {
            $ext = strtolower($a[count($a)-1]);
        } else {
            $ext = "";
        }

        foreach ($this->types as $filetype => $extensions) {
            foreach ($extensions as $e) {
                if ($e == $ext) {
                    return $filetype;
                }
            }
        }

        return 'unknown';
    }

    /**
     * Получает post-файлы
     *
     * @access      public
     * @return      array
     */
    public function getPostFiles() {
        if (!empty($_POST['post_files'])) {
            $str = $_POST['post_files'];
            $query_arr = explode('*', $str);
            $i = 0;
            foreach ($query_arr as $v) {
                if ($v === "") {
                    continue;
                }
                $na[$i] = $v;
                $i++;
            }
            if (isset($na)) {
                return $na;
            }
        }
        return array();
    }

    /**
     * Получает атрибуты формы
     *
     * @access      public
     * @param       string
     * @return      string
     */
    public function formAttributes($v) {
        $s = substr(sprintf('%o', $v), -4);
        return $s;
    }

    /**
     * Обрабатывает ошибку
     *
     * @access      public
     * @param       string
     * @param       string
     * @param       string
     * @param       string
     */
    public function errHandle($errno, $errstr, $errfile, $errline) {
        /*
        global $FileAdmin;
        if (!isset($FileAdmin)) {
        return;
        }
        $all = (isset($this->sessionVars['ErrLog'])) ? $this->sessionVars['ErrLog'] : "";
        $last_str = $errfile . '<br />';
        $last_str .= '&nbsp;' . $errstr;
        $last_str = str_ireplace($this->getUser()->getRoot(), '', $last_str);
        $all .= $last_str;
        $this->sessionVars['ErrLog'] = $all;
        */
        return;
    }

    /**
     * Пытается рекурсивно применить права
     *
     * @access      public
     * @param       string
     * @param       integer
     * @return      boolean
     * @todo        Ошибки загонять в стек
     */
    public function chmodBranch($from, $att) {
        if (is_file($from)) {
            $result = chmod($from, $att);
            return $result;
        } elseif (is_dir($from)) {
            $result = true;
            if ($handle = opendir($from)) {
                while (($str = readdir($handle)) !== false) {
                    if ($str !== "." && $str !== "..") {
                        $a = $from . '/' . $str;
                        if (is_file($a)) {
                            if (!chmod($a, $att)) {
                                $result = false;
                            }
                        } elseif (is_dir($a)) {
                            if (!$this->chmodBranch($a, $att)) {
                                $result = false;
                            }
                        }
                    }
                }
                closedir($handle);
                if (!chmod($from, $att)) {
                    $result = false;
                }
            }
            return $result;
        }
    }

    /**
     * Удаляет запрещённые файлы
     *
     * @access      public
     * @param       string
     * @return      boolean
     */
    public function clearDeniedTypes($from) {
        $ret = false;
        if ($handle = opendir($from)) {
            while (($str = readdir($handle)) !== false) {
                if ($str !== "." && $str !== "..") {
                    $a = $from . '/' . $str;
                    if (is_file($a)) {
                        if (!$this->isAllowFile($a)) {
                            @unlink($a);
                            $ret = true;
                        }
                    }
                    else {
                        $r = $this->clearDeniedTypes($a);
                        if ($r) {
                            $ret = true;
                        }
                    }
                }
            }
        }
        closedir($handle);
        return $ret;
    }

    /**
     * Копирует ветку
     *
     * @access      public
     * @param       string - что
     * @param       string - куда
     * @param       boolean - записывать поверх существующего
     * @return      string | null
     */
    public function copyBranch($from, $to, $over) {
        $from = $this->escapePath($from);
        $to = $this->escapePath($to);
        if ($from === $to) {
            $to = $this->checkFileName($to);
        }
        if (file_exists($to) || is_link($to)) {
            if (!$over) {
                return "exists";
            }
            if (is_file($to) || is_link($to)) {
                @unlink($to);
            }
        }
        if (is_file($from)) {
            @copy($from, $to);
            return;
        } elseif (is_link($from)) {
            $target = @readlink($from);
            @symlink($target, $to);
            return;
        } elseif (is_dir($from)) {
            if (!file_exists($to)) {
                @mkdir($to, 0777);
            }
            $this->copyBranchEx($from, $to, $over);
        }
    }

    /**
     * Копирует содержимое
     *
     * @access      public
     * @param       string - что
     * @param       string - куда
     * @param       boolean - записывать поверх существующего
     * @return      string | null
     */
    public function copyBranchEx($from, $to, $over) {
        if (is_file($from)) {
            @copy($from, $to);
            return;
        } else {
            if ($handle = opendir($from)) {
                while (($str = readdir($handle)) !== false) {
                    if ($str !== "." && $str !== "..") {
                        $a = $from . '/' . $str;
                        $b = $to . '/' . $str;
                        if (file_exists($b) || is_link($b)) {
                            if (!$over) {
                                return "exists";
                            }
                            if (is_file($b) || is_link($b)) {
                                @unlink($b);
                            }
                        }
                        if (is_file($a)) {
                            @copy($a, $b);
                            continue;
                        } elseif (is_link($a)) {
                            $target = readlink($a);
                            @symlink($target, $b);
                            continue;
                        } elseif(is_dir($a)) {
                            if (!file_exists($b)) {
                                @mkdir($b, 0777);
                            }
                            $this->copyBranchEx($a, $b, $over);
                        }
                    }
                }
                closedir($handle);
            }
        }
    }

    /**
     * Удаляет ветку
     *
     * @access      public
     * @param       string
     */
    public function deleteBranch($from) {
        if (is_file($from) || is_link($from)) {
            @unlink($from);
            return;
        } elseif ($handle = @opendir($from)) {
            while (($str = readdir($handle)) !== false) {
                if ($str !== "." && $str !== "..") {
                    $a = $from . '/' . $str;
                    if (is_file($a)) {
                        @unlink($a);
                    } else {
                        $this->deleteBranch($a);
                    }
                }
            }
            closedir($handle);
            @rmdir($from);
        }
    }

    /**
     * Сброс буфера
     *
     * @access      public
     */
    public function unsetBuffer() {
        unset($this->toDuplicate);
        $this->buffer = null;
        $this->bufferPath = null;
    }

    /**
     * Располагается ли указанный путь внутри рабочей папки
     *
     * @access      public
     * @param       string
     * @return      boolean
     * @todo        найти isMainProgramPath и переименовать
     */
    public function isInBasePath($str) {
        $to = $str . "/";
        if (substr($to, 0, strlen($this->basePath)) === $this->basePath) {
            return true;
        } elseif (substr($this->basePath, 0, strlen($to)) === $to) {
            return true;
        }
        return false;
    }

    /**
     * Возвращает типы скриптов
     *
     * @access      public
     * @return      array
     * @todo        найти getScriptTypes и переименовать
     */
    public function getScriptExtensions() {
        return $this->allScripts;
    }

    /**
     * Является ли скриптом
     *
     * @access      public
     * @param       array
     * @return      boolean
     */
    public function isScript($type) {
        if ($type == '') {
            return false;
        }
        foreach($this->allScripts as $scr) {
            if (substr($type, 0, strlen($scr)) === $scr) {
                return true;
            }
        }
        return false;
    }

    /**
     * Проверяет имя файла
     *
     * @access      public
     * @param       string
     * @return      string
     */
    public function checkFileName($file) {
        if (file_exists($file) || is_link($file)) {
            $path_parts = pathinfo($file);
            $arr = split('/', $file);
            $target = $arr[count($arr)-1];
            unset($arr[count($arr)-1]);
            $newto = implode('/', $arr);
            //$ext = end(explode('.', $newto));
            //$filename = substr($file, 0, (strlen($file)-(strlen($ext)+1)));
            $ext = $path_parts['extension'];
            $filename = $path_parts['filename'];
            if (isset($newto)) {
                $to = $newto . "/" . $filename.'_2';
            } else {
                $to = "" . $filename.'_2';
            }
            if (!empty($ext)) {
                $to .= '.'.$ext;
            }
            $i = 3;
            while(file_exists($to) || is_link($to)) {                
                if (isset($newto)) {
                    $to = $newto . "/". $filename."_".$i;
                } else {
                    $to = $filename."_".$i;
                }
                if (!empty($ext)) {
                    $to .= '.'.$ext;
                }
                $i++;
            }
            return $to;
        }
        return $file;
    }

    /**
     * Транслитерирует имя файла
     *
     * @access      public
     * @param       string
     * @return      string
     */
    public function transliterateFilename($file) {
        $parts = explode('/', $file);    
        $filename = $parts[count($parts)-1];    
        //$this->loadClass('xbkTransliteration', 'core', true);
        //$Transliteration = $this->factory('xbkTransliteration');
        $Transliteration = $this->get('transliteration');
        $filename = $Transliteration->cleanFilename($filename);
        $parts[count($parts)-1] = $filename;
        $file = implode('/', $parts);
        return $file;
    }

    /**
     * Нормализует имя
     *
     * @access      public
     * @param       string
     * @return      string
     */
    public function checkName ($name) {
        $name = preg_replace('/[^\w\s\d-]/', '', $name);
        $name = preg_replace('/ /', '%spc%', $name);
        $name = preg_replace('/\s/', '', $name);
        $name = preg_replace('/%spc%/', ' ', $name);
        $name = preg_replace('/  /', ' ', $name);
        return $name;
    }

    /**
     * Вырезает из пути недопустимые символы
     *
     * @access      public
     * @param       string
     * @return      string
     */
    public function escapePath ($path) {  
        $ret = $path;
        //$ret = preg_replace('/[^\s\d\\\\\/:\.\-\,]/', '', $path);
        $ret = preg_replace('/[^\p{L}\s\d\\\\\/:\.\_\-\,]/u', '', $path);
        $ret = preg_replace('/ /', '%spc%', $ret);
        $ret = preg_replace('/\s/', '', $ret);
        $ret = preg_replace('/%spc%/', ' ', $ret);
        $ret = preg_replace('/  /', ' ', $ret);
        $ret = preg_replace('/\\\/', '/', $ret);
        $ret = preg_replace('/\.+/', '.', $ret);
        $ret = preg_replace('/\.\//', '/', $ret);
        $ret = preg_replace('/\.$/', '', $ret);
        $ret = preg_replace('/\/\/+/', '/', $ret);
        $ret = preg_replace('/\/$/', '', $ret);
        return $ret;
    }

    /**
     * Получает текущий путь
     *
     * @access      public
     * @return      string | false
     */
    public function getCurrentPath() {
        if (!isset($this->currentPath)) {
            $this->errorState = $this->lang['current_path_not_init'];
            $this->message($this->lang['current_path_not_init'], 'error'); 
            return false;
        }
        return $this->currentPath;
    }

    /**
     * Устанавливает текущий путь
     *
     * @access      public
     * @param       string
     * @return      mixed
     */
    public function setCurrentPath($path) {
        if ($path == '/') {
            $path = '';
        }
        $d = $this->basePath.$this->escapePath($path);
        if (!is_dir($d)) {
            $this->errorState = $this->lang['dir_not_exists'];
            $this->message($this->lang['dir_not_exists'], 'error'); 
            return false;
        }
        $this->currentPath = $this->escapePath($path);
        return true;
    }

    /**
     * Получает файловый путь
     *
     * @access      public
     * @param       string
     * @param       string
     * @return      string
     */
    public function getFilePath($dir, $file) {
        $str = $this->escapePath($this->basePath . '/' . $dir . '/' . $file);
        return $str;
    }

    /**
     * Получает файловый путь для текущего пользователя
     *
     * @access      public
     * @param       string
     * @return      string
     */
    public function getUserCurrentFilePath($file) {
        return $this->getFilePath($this->currentPath, $file);
    }

    /**
     * Получает путь для текущего польователя
     *
     * @access      public
     * @return      string
     */
    public function getUserCurrentPath() {
        return $this->escapePath($this->basePath . '/' . $this->currentPath);
    }

    /**
     * Получает расширения файла
     *
     * @access      public
     * @param       string
     * @return      array
     */
    public function getFileExtensions($file) {
        $ar = explode('.', $file);
        return array_slice($ar, 1);
    }

    /**
     * Новая директория
     *
     * @access      public
     * @param       string
     * @return      boolaen
     */
    public function newDir($dir) {
        if (!$this->checkCommand('newdir')) {
            return false;
        }
        if ($dir == "") {
            return true;
        }
        $nd = $this->escapePath($this->getUserCurrentFilePath($dir));
        $nd = $this->transliterateFilename($nd);
        if (file_exists($nd)) {
            $this->errorState = $this->lang['dir_exists'];
            $this->message($this->lang['dir_exists'], 'error');
            return false;
        }        
        return @mkdir($nd, 0777);
    }

    /**
     * Перемещает файлы
     *
     * @access      public
     * @param       array
     * @param       boolean
     * @param       string | null
     * @return      boolaen
     */
    public function moveFiles($list, $duplicate, $path = NULL) {
        if (!$this->checkCommand('copyfiles')) {
            return false;
        }
        if (count($list) < 1) {
            return true;
        }
        if ($this->isAllow('delete')) {
            $this->toDuplicate = $duplicate;
        } else {
            $this->toDuplicate = true;
        }
        $this->buffer = $list;
        if (is_null($path)) {
            $this->bufferPath = $this->currentPath;
        } else {
            if ($path == '/') {
                $path = '';
            }
            $d = $this->escapePath($this->basePath . "/" . $path);
            if (!is_dir($d)) {
                $this->errorState = $this->lang['dir_not_exists'];
                $this->message($this->lang['dir_not_exists'], 'error'); 
                return false;
            }
            $this->bufferPath = $this->escapePath($path);
        }
        return true;
    }

    /**
     * Проверяет возможность дублирования
     *
     * @access      public
     * @return      string | false
     */
    public function isDuplication() {
        if (!isset($this->toDuplicate)) {
            return false;
        }
        if ($this->toDuplicate) {
            return "copy";
        }
        return "move";
    }

    /**
     * Возвращает буфер
     *
     * @access      public
     * @return      array
     */
    public function getBuffer() {
        if (!isset($this->buffer)) {
            return array();
        }
        return $this->buffer;
    }

    /**
     * Возвращает путь буфера
     *
     * @access      public
     * @return      array
     */
    public function getBufferPath() {
        return $this->bufferPath;
    }

    /**
     * Вставить
     *
     * @access      public
     * @param       boolean
     * @return      boolean
     */
    public function paste($over) {
        if (!$this->checkCommand('copyfiles')) {
            return false;
        }
        if (!isset($this->toDuplicate) || !isset($this->buffer) || !isset($this->bufferPath)) {
            $this->errorState = $this->lang['buffer_empty'];
            $this->message($this->lang['buffer_empty'], 'error');
            return false;
        }
        $ov = ($over === "Yes");
        foreach ($this->buffer as $i => $buf) {
            $v = $buf;
            $a = $this->getFilePath($this->bufferPath, $v);
            $b = $this->getUserCurrentFilePath($v);
            if (strlen($b) > strlen($a) && substr($b, 0, strlen($a.'/')) === $a.'/') {
                $this->errorState = $this->lang['cycle_copying_denied'];
                $this->message($this->lang['cycle_copying_denied'], 'error');
                return false;
            }
            if ($this->toDuplicate) {
                $ret = $this->copyBranch($a, $b, $ov);
                if ($over !== "No" && isset($ret)) {
                    return $ret;
                }
            } else if ($this->bufferPath != $this->currentPath) {
                if ($a == $b) {
                    continue;
                }
                if (file_exists($b)) {
                    if ($over === "Yes") {
                        @unlink($b);
                        @rename($a, $b);
                    } elseif (!$over) {
                        return "exists";
                    }
                } else {
                    @rename($a, $b);
                }
            }
            unset($this->buffer[$i]);
        }
        $this->unsetBuffer();
        return true;
    }

    /**
     * Проверка атрибутов
     *
     * @access      public
     * @param       integer
     * @return      string
     */
    public function checkAttributes($att) {
        $p = @octdec($att);
        if ((!is_numeric($p)) || (strlen($p)!=3)) {
            return '0775';
        }
        $a = $p;
        $p = '0' . $a;
        return $p;
    }

    /**
     * Изменить атрибуты
     *
     * @access      public
     * @param       array
     * @param       integer
     * @param       boolean
     * @return      boolean
     */
    public function changeAttributes($list, $att, $req) {
        if (!$this->checkCommand('chmod')) {
            return false;
        }
        if (count($list) < 1) {
            return true;
        }
        foreach($list as $v) {
            $vp = $this->getUserCurrentFilePath($v);
            $p = $this->checkAttributes($att);
            if ($req) {
                $this->chmodBranch($vp, $p);
            } else {
                @chmod($vp, $p);
            }
        }
        return true;
    }

    /**
     * Удалить файлы
     *
     * @access      public
     * @param       array
     * @return      boolean
     */
    public function deleteFiles($list) {
        if (!$this->checkCommand('delete')) {
            return false;
        }
        foreach($list as $v) {
            $vp = $this->getUserCurrentFilePath($v);
            if (isset($this->buffer)) {
                if ($this->currentPath == $this->bufferPath) {
                    foreach($this->buffer as $i => $b) {
                        if ($b === $v) {
                            unset($this->buffer[$i]);
                        }
                    }
                    if (count($this->buffer) < 1) {
                        $this->unsetBuffer();
                    }
                } elseif (substr($this->bufferPath, 0, strlen($this->currentPath)) === $this->currentPath) {
                    $this->unsetBuffer();
                }
            }
            $this->deleteBranch($vp);
        }
        return true;
    }

    /**
     * Распаковать
     *
     * @access      public
     * @param       array
     * @return      boolean
     */
    public function unzip($list) {
        if (!$this->checkCommand('unzip')) {
            return false;
        }
        if (count($list) < 1) {
            return true;
        }
        include_once $this->getModulePath().'zip.hid.php';
        $nonzip = false;
        foreach($list as $v) {
            $types = $this->getFileExtensions($v);
            if (count($types) > 0) {
                $zp = $types[count($types)-1];
            } else {
                $zp = "";
            }
            if ($zp !== "zip") {
                $this->errorState = $this->lang['unzip_only_zip'];
                $this->message($this->lang['unzip_only_zip'], 'error'); 
                $nonzip = true;
                continue;
            }
            $old_dir = getcwd();
            $t = microtime();
            $tmp_nam = time() . $t{2} . $t{4};
            $s = $this->escapePath($old_dir . '/tmp/' . $tmp_nam);
            @mkdir($s, 0777, true);
            $z = new PclZip($this->getUserCurrentFilePath($v));
            $z->extract($s);
            if ($this->clearDeniedTypes($s)) {
                $this->errorState = $this->lang['restricted_files_deleted'];
                $this->message($this->lang['restricted_files_deleted'], 'error'); 
                $nonzip = true;
            }
            $p = $this->getUserCurrentFilePath($v . '_unzipped');
            $i = 1;
            while(file_exists($p)) {
                $i++;
                $p = $this->getUserCurrentFilePath($v . '_unzipped' . $i);
            }
            @rename($s, $p);
        }
        if ($nonzip) {
            return false;
        }
        return true;
    }

    /**
     * Запаковать
     *
     * @access      public
     * @param       string
     * @param       array
     * @return      boolean
     */
    public function zip($name, $list) {
        if (!$this->checkCommand('zip')) {
            return false;
        }
        if (count($list) < 1) {
            return true;
        }
        $old_dir = getcwd();
        //@chdir($this->getUserCurrentPath());
        $i = 0;
        $n = $name;
        $n = $this->checkFileName($this->getUserCurrentFilePath($n));
        foreach($list as $v) {
            if (is_link($this->getUserCurrentFilePath($v))) {
                continue;
            }
            $arr2[] = $this->getUserCurrentPath().'/'.$v;
            $i++;
        }
        include_once $this->getModulePath().'zip.hid.php';
        $z = new PclZip($n);
        $z->create($arr2, PCLZIP_OPT_REMOVE_PATH, $this->getUserCurrentPath());
        @chdir($old_dir);
        return true;
    }

    /**
     * Переименовать
     *
     * @access      public
     * @param       string
     * @param       string
     * @return      boolean
     */
    public function renameFile($from, $to) {
        $globals = $this->get('globals');
        if (!$this->checkCommand('rename')) {
            $globals->message('У вас недостаточно прав', 'error');
            return false;
        }
        $exts = $this->getFileExtensions($to);
        foreach ($exts as $ext) {
            if (!$this->isAllowExtension($ext)) {
                $globals->message('Попытка указать файлу недопустимое расширение', 'warning');
                return false;
            }
        }
        $to = $this->escapePath($to);
        if ($this->isTransliterateFilenames()) {
            $to = $this->transliterateFilename($to);
        }
        //$globals->message($from.' '.$to);
        if ($from === $to) {
            return true;
        }
        if(is_file($from) && !$this->isAllowFile($to)) {
            $this->errorState = $this->lang['you_cant_create_such_files'];
            $this->message($this->lang['you_cant_create_such_files'], 'error'); 
            return false;
        }
        $to = $this->getUserCurrentFilePath($to);
        if (file_exists($to)) {
            $this->errorState = $this->lang['name_exists'];
            $this->message($this->lang['name_exists'], 'error');
            return false;
        }
        $from = $this->getUserCurrentFilePath($from);
        @rename($from, $to);
        return true;
    }   

}