public function set($value, &$node) { if (empty($value) or !is_array($value)) { return; } switch ($value['error']) { case UPLOAD_ERR_OK: $value = Node::create('file')->import($value); break; case UPLOAD_ERR_INI_SIZE: throw new RuntimeException(t('Размер файла превышает установленное в системе ограничение (%size).', array('%size' => ini_get('upload_max_filesize')))); case UPLOAD_ERR_FORM_SIZE: throw new RuntimeException(t('Размер файла превышает установленное в форме ограничение.')); case UPLOAD_ERR_PARTIAL: throw new RuntimeException(t('Файл получен не полностью.')); case UPLOAD_ERR_NO_FILE: if (!empty($value['id']) and is_numeric($value['id'])) { $value = Node::load($value['id']); } elseif (!empty($value['url'])) { $value = FileNode::fromURL($value['url']); } elseif (!empty($value['ftp'])) { $value = Node::create('file')->importFromFTP(array_shift($value['ftp'])); } elseif (!empty($value['unlink'])) { $node->linkRemoveChild(null, $this->value); $value = null; } else { $value = $node->{$this->value}; } } if ($this->required and empty($value)) { throw new ValidationException($this->label, t('Вы не загрузили файл в поле «%name», хотя оно отмечено как обязательное.', array('%name' => mb_strtolower($this->label)))); } $node->{$this->value} = $value; }
/** * Обработка файла. */ public static function transform(FileNode $node) { if (0 !== strpos($node->filetype, 'image/')) { return; } $result = array(); // Относительный путь к исходному файлу $source = $node->getRealURL(); // Путь к файловому хранилищу, используется только чтобы // из полного пути сделать относительный после трансформации. $storage = Context::last()->config->getPath('modules/files/storage', 'files'); // Файлы без расширений не обрабатываем, чтобы не нагенерировать каких-нибудь странных имён. /* if (!($ext = os::getFileExtension($source))) return; */ // Правила перевода расширений в типы. $typemap = array('png' => 'image/png', 'jpg' => 'image/jpeg'); foreach (Context::last()->config->getArray(self::confroot) as $name => $settings) { if (empty($ext)) { $target = $source . '.' . $name . '.' . $settings['format']; } else { $target = substr($source, 0, -strlen($ext)) . $name . '.' . $settings['format']; } if (file_exists($target)) { unlink($target); } $im = ImageMagick::getInstance(); if ($im->open($source, $node->filetype)) { $options = array('downsize' => $settings['scalemode'] != 'crop', 'crop' => $settings['scalemode'] == 'crop', 'quality' => intval($settings['quality'])); if ($im->scale($settings['width'], $settings['height'], $options)) { if ($im->save($target, $typemap[$settings['format']])) { $tmp = array('width' => $im->getWidth(), 'height' => $im->getHeight(), 'filename' => substr($target, strlen($storage) + 1), 'filesize' => filesize($target)); $result[$name] = $tmp; } } } } return $result; }
/** * Импорт файла из внешнего источника. * * Используется как для обработки полученных от браузера файлов, так и для * ручного добавления файлов в архив. Путь к файловому архиву определяется * конфигурационным файлом (параметр files). * * Внутреннее имя файла при копировании в архив формируется с использованием * md5-суммы его содержимого, поэтому в архив нельзя два раза добавить один * файл. При обнаружении попытки повторной загрузки файла (с таким же * filepath) метод прозрачно подменяет содержимое текущей ноды содержимым * уже существующей, новую не создаёт. * * При невозможности скопировать файл в архив возникает UserErrorException. * * @return void * @param array $file описание файла * @param bool $uploaded проверять, действительно ли файл пришёл от браузера. * * Обязательные ключи: tmp_name (полный путь к фалу, который нужно * скопировать в архив), опциональные: type, name, size, parent_id. При * отсутствии type, тип файла определяется эвристически. * * При указании parent_id файл автоматически прикрепляется к указанной в этом * параметре ноде, с помощью Node::linkAddParent(). */ public function import(array $file, $uploaded = true) { if (!empty($file['tmp_name'])) { $storage = os::path(MCMS_SITE_FOLDER, Context::last()->config->get('modules/files/storage')); // Немного валидации. if (!file_exists($file['tmp_name'])) { throw new Exception(t('Загружаемый файл перестал существовать.')); } // Угадваем значения некоторых полей, для упрощения скриптинга. if (!isset($file['size'])) { $file['size'] = filesize($file['tmp_name']); } if (!isset($file['name'])) { $file['name'] = basename($file['tmp_name']); } if (!isset($file['type'])) { $file['type'] = os::getFileType($file['tmp_name']); } if ($this->id === null and FileNode::isUnzipable($file)) { if (null === ($node = $this->unzip($file['tmp_name']))) { throw new InvalidArgumentException("ZIP file was empty"); } $this->data = $node->getRaw(); return $this; } $this->filepath = os::mkunique($this->getCleanFileName($file), $storage); } elseif (!empty($file['url'])) { $this->remoteurl = $file['url']; } $file = array_merge(array('name' => 'unnamed.bin', 'type' => 'application/octet-stream', 'size' => 0), $file); $this->filename = $this->name = $file['name']; $this->filetype = $file['type']; $this->filesize = $file['size']; if ($this->filepath and !empty($file['tmp_name'])) { // Сюда будем копировать файл. $dest = os::path($storage, $this->filepath); if (file_exists($dest)) { throw new RuntimeException(t('Такой файл уже есть.')); } // Создаём каталог для него. os::mkdir(dirname($dest), 'Файл не удалось сохранить, т.к. отсутствуют права на запись в каталог, где этот файл должен был бы храниться (%path). Сообщите об этой проблеме администратору сайта.', array('%path' => dirname($dest))); // Копируем файл. if (!os::copy($file['tmp_name'], $dest)) { throw new RuntimeException(t('Не удалось скопировать файл %src в %dst.', array('%src' => $file['tmp_name'], '%dst' => $dest))); } } // Прикрепляем файл к родительскому объекту. if (!empty($file['parent_id'])) { $this->linkAddParent($file['parent_id']); } return $this; }