1
0
Fork 0
mirror of https://github.com/Oreolek/ifhub.club.git synced 2024-05-17 00:08:17 +03:00
ifhub.club/application/classes/modules/media/Media.class.php

1489 lines
55 KiB
PHP
Raw Normal View History

<?php
2014-10-08 08:20:29 +03:00
/*
* LiveStreet CMS
* Copyright © 2013 OOO "ЛС-СОФТ"
*
* ------------------------------------------------------
*
* Official site: www.livestreetcms.com
* Contact e-mail: office@livestreetcms.com
*
* GNU General Public License, version 2:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
*
* ------------------------------------------------------
*
* @link http://www.livestreetcms.com
* @copyright 2013 OOO "ЛС-СОФТ"
* @author Maxim Mzhelskiy <rus.engine@gmail.com>
*
*/
/**
* Модуль управления медиа-данными (изображения, видео и т.п.)
2014-10-08 08:20:29 +03:00
*
* @package application.modules.media
* @since 2.0
*/
class ModuleMedia extends ModuleORM
{
/**
* Список типов медиа
* Свои кастомные типы необходимо нумеровать с 1000
*/
2015-02-15 08:06:59 +02:00
const MEDIA_TYPE_IMAGE = 1;
const MEDIA_TYPE_VIDEO = 2;
/**
* Список типов для проверки доступа
*/
const TYPE_CHECK_ALLOW_ADD = 'add';
const TYPE_CHECK_ALLOW_REMOVE = 'remove';
const TYPE_CHECK_ALLOW_UPDATE = 'update';
2015-01-26 13:49:42 +02:00
const TYPE_CHECK_ALLOW_PREVIEW = 'preview'; // возможность создания превью для объекта
const TYPE_CHECK_ALLOW_VIEW_LIST = 'view_list'; // просмотр списка медиа у объекта
/**
* Объект текущего пользователя
*
* @var ModuleUser_EntityUser|null
*/
protected $oUserCurrent;
protected $oMapper = null;
protected $aTargetTypes = array(
'topic' => array(
'allow_preview' => true,
),
'comment' => array(),
'blog' => array(),
'talk' => array(),
);
/**
* Список доступных типов медиа
*
* @var array
*/
protected $aMediaTypes = array(
2015-02-15 08:06:59 +02:00
self::MEDIA_TYPE_IMAGE,
self::MEDIA_TYPE_VIDEO
);
/**
* Инициализация
*
*/
public function Init()
{
parent::Init();
$this->oMapper = Engine::GetMapper(__CLASS__);
$this->oUserCurrent = $this->User_GetUserCurrent();
}
/**
* Возвращает список типов объектов
*
* @return array
*/
public function GetTargetTypes()
{
return $this->aTargetTypes;
}
/**
* Добавляет в разрешенные новый тип
*
* @param string $sTargetType Тип
* @param array $aParams Параметры
* @return bool
*/
public function AddTargetType($sTargetType, $aParams = array())
{
if (!array_key_exists($sTargetType, $this->aTargetTypes)) {
$this->aTargetTypes[$sTargetType] = $aParams;
return true;
}
return false;
}
/**
* Проверяет разрешен ли данный тип
*
* @param string $sTargetType Тип
* @return bool
*/
public function IsAllowTargetType($sTargetType)
{
return in_array($sTargetType, array_keys($this->aTargetTypes));
}
/**
* Возвращает парметры нужного типа
*
* @param string $sTargetType
*
* @return array|null
*/
public function GetTargetTypeParams($sTargetType)
{
if ($this->IsAllowTargetType($sTargetType)) {
return $this->aTargetTypes[$sTargetType];
}
return null;
}
/**
* Возвращает конкретный параметр нужного типа
*
* @param string $sTargetType
* @param string $sName
*
* @return mixed|null
*/
public function GetTargetTypeParam($sTargetType, $sName)
{
$aParams = $this->GetTargetTypeParams($sTargetType);
if ($aParams and array_key_exists($sName, $aParams)) {
return $aParams[$sName];
}
return null;
}
/**
* Проверяет разрешен ли тип медиа
*
* @param string $sType
*
* @return bool
*/
public function IsAllowMediaType($sType)
{
return in_array($sType, $this->aMediaTypes);
}
2015-02-15 08:06:59 +02:00
/**
* Возвращает тип медиа по имени файла (имя файла должно содержать его расширение)
*
* @param $sFile
* @return int|null
*/
public function GetMediaTypeByFileName($sFile)
{
$aPathInfo = pathinfo($sFile);
$sExtension = isset($aPathInfo['extension']) ? $aPathInfo['extension'] : 'unknown';
return $this->GetMediaTypeByFileExtension($sExtension);
}
/**
* Возвращает тип медиа по расширению файла
*
* @param $sExtension
* @return int|null
*/
public function GetMediaTypeByFileExtension($sExtension)
{
$sExtension = strtolower($sExtension);
if (in_array($sExtension, array('jpg', 'jpeg', 'gif', 'png'))) {
return self::MEDIA_TYPE_IMAGE;
}
return null;
}
/**
* Возвращает название медиа типа по его значению
* Название получается автоматически из константы с определением типа
*
* @param $iType
* @return null|string
*/
public function GetMediaTypeName($iType)
{
$oRefl = new ReflectionObject($this);
foreach ($oRefl->getConstants() as $sName => $mValue) {
if (strpos($sName, 'MEDIA_TYPE_') === 0 and $mValue == $iType) {
return strtolower(substr($sName, strlen('MEDIA_TYPE_')));
}
}
return null;
}
/**
* Проверка объекта target - владелец медиа
*
* @param string $sTargetType Тип
* @param int $iTargetId ID владельца
* @param string $sAllowType
* @param array $aParams
2015-09-02 12:06:50 +03:00
* @param string|null $sTargetTmp
* @return bool
*/
2015-09-02 12:06:50 +03:00
public function CheckTarget($sTargetType, $iTargetId = null, $sAllowType = null, $aParams = array(), $sTargetTmp = null)
{
if (!$this->IsAllowTargetType($sTargetType)) {
return false;
}
2015-09-02 12:06:50 +03:00
/**
* Проверка на максимальное количество файлов
*/
if ($sAllowType == self::TYPE_CHECK_ALLOW_ADD and ($iTargetId or $sTargetTmp)) {
$iCountAllow = $this->GetConfigParam('max_count_files', $sTargetType);
if (is_numeric($iCountAllow)) {
$aFilterCount = array('target_type' => $sTargetType);
if ($iTargetId) {
$aFilterCount['target_id'] = $iTargetId;
} else {
$aFilterCount['target_tmp'] = $sTargetTmp;
}
$iCount = $this->getCountFromTargetByFilter($aFilterCount);
if ($iCount >= $iCountAllow) {
return $this->Lang_Get('media.error.max_count_files');
}
}
}
$sMethod = 'CheckTarget' . func_camelize($sTargetType);
if (method_exists($this, $sMethod)) {
if (!array_key_exists('user', $aParams)) {
$aParams['user'] = $this->oUserCurrent;
}
return $this->$sMethod($iTargetId, $sAllowType, $aParams);
}
return false;
}
public function NotifyCreatePreviewTarget($sTargetType, $iTargetId, $oRelationTarget)
{
if (!$this->IsAllowTargetType($sTargetType)) {
return false;
}
$sMethod = 'NotifyCreatePreviewTarget' . func_camelize($sTargetType);
if (method_exists($this, $sMethod)) {
return $this->$sMethod($iTargetId, $oRelationTarget);
}
return false;
}
public function NotifyRemovePreviewTarget($sTargetType, $iTargetId, $oRelationTarget)
{
if (!$this->IsAllowTargetType($sTargetType)) {
return false;
}
$sMethod = 'NotifyRemovePreviewTarget' . func_camelize($sTargetType);
if (method_exists($this, $sMethod)) {
return $this->$sMethod($iTargetId, $oRelationTarget);
}
return false;
}
/**
* Возвращает параметр конфига с учетом текущего target_type
*
* @param string $sParam Ключ конфига относительно module.media
* @param string $sTargetType Тип
*
* @return mixed
*/
public function GetConfigParam($sParam, $sTargetType)
{
$mValue = Config::Get("module.media.type.{$sTargetType}.{$sParam}");
if (!$mValue) {
$mValue = Config::Get("module.media.{$sParam}");
}
return $mValue;
}
2015-02-15 08:06:59 +02:00
/**
* Проверяем файл на максимальный размер с учетом типа медиа
*
* @param $iSize
* @param $iMediaType
* @param $sTargetType
* @return bool
*/
public function CheckFileUploadSize($iSize, $iMediaType, $sTargetType)
{
$sConfigParam = 'max_size';
$sMediaTypeName = $this->Media_GetMediaTypeName($iMediaType);
if (is_null($iMaxSizeKb = $this->GetConfigParam($sMediaTypeName . '.' . $sConfigParam, $sTargetType))) {
$iMaxSizeKb = $this->GetConfigParam($sConfigParam, $sTargetType);
}
if ($iSize > $iMaxSizeKb * 1024) {
return $this->Lang_Get('media.error.too_large', array('size' => $iMaxSizeKb));
}
return true;
}
public function Upload($aFile, $sTargetType, $sTargetId, $sTargetTmp = null)
{
if (is_string($aFile)) {
return $this->UploadUrl($aFile, $sTargetType, $sTargetId, $sTargetTmp);
} else {
return $this->UploadLocal($aFile, $sTargetType, $sTargetId, $sTargetTmp);
}
}
public function UploadLocal($aFile, $sTargetType, $sTargetId, $sTargetTmp = null)
{
if (!is_array($aFile) || !isset($aFile['error']) || !isset($aFile['tmp_name']) || !is_string($aFile['tmp_name']) || !isset($aFile['name']) || !isset($aFile['size'])) {
return false;
}
if ($aFile['error'] != UPLOAD_ERR_OK) {
switch ($aFile['error']) {
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
2015-02-20 12:36:29 +02:00
return $this->Lang_Get('media.error.too_large', array('size' => @func_ini_return_bytes(ini_get('upload_max_filesize')) / 1024));
default:
return $this->Lang_Get('media.error.upload');
}
}
$aPathInfo = pathinfo($aFile['name']);
$sExtension = isset($aPathInfo['extension']) ? $aPathInfo['extension'] : 'unknown';
$sFileName = $aPathInfo['filename'] . '.' . $sExtension;
2015-02-15 08:06:59 +02:00
/**
* Проверяем на размер файла
*/
$iMediaType = $this->GetMediaTypeByFileExtension($sExtension);
if (true !== ($mRes = $this->CheckFileUploadSize($aFile['size'], $iMediaType, $sTargetType))) {
2015-02-15 08:06:59 +02:00
return $mRes;
}
/**
* Копируем загруженный файл
*/
$sDirTmp = Config::Get('path.tmp.server') . '/media/';
if (!is_dir($sDirTmp)) {
@mkdir($sDirTmp, 0777, true);
}
$sFileTmp = $sDirTmp . $sFileName;
if (!move_uploaded_file($aFile['tmp_name'], $sFileTmp)) {
return $this->Lang_Get('media.error.upload');
}
return $this->ProcessingFile($sFileTmp, $sTargetType, $sTargetId, $sTargetTmp);
}
public function UploadUrl($sFileUrl, $sTargetType, $sTargetId, $sTargetTmp = null)
{
/**
* Проверяем, является ли файл изображением
* TODO: файл может быть не только изображением, поэтому требуется рефакторинг
*/
if (!$aImageInfo = (@getimagesize($sFileUrl))) {
return $this->Lang_Get('media.error.not_image');
}
$aTypeImage = array(
1 => 'gif',
2 => 'jpg',
3 => 'png'
); // see http://php.net/manual/en/function.exif-imagetype.php
$sExtension = isset($aTypeImage[$aImageInfo[2]]) ? $aTypeImage[$aImageInfo[2]] : 'jpg';
/**
* Открываем файловый поток и считываем файл поблочно,
* контролируя максимальный размер изображения
*/
$rFile = fopen($sFileUrl, 'r');
if (!$rFile) {
return $this->Lang_Get('media.error.upload');
}
$iMaxSizeKb = $this->GetConfigParam('max_size', $sTargetType);
$iSizeKb = 0;
$sContent = '';
while (!feof($rFile) and $iSizeKb < $iMaxSizeKb) {
$sContent .= fread($rFile, 1024 * 2);
$iSizeKb++;
}
/**
* Если конец файла не достигнут,
* значит файл имеет недопустимый размер
*/
if (!feof($rFile)) {
return $this->Lang_Get('media.error.too_large', array('size' => $iMaxSizeKb));
}
fclose($rFile);
/**
* Копируем загруженный файл
*/
$sDirTmp = Config::Get('path.tmp.server') . '/media/';
if (!is_dir($sDirTmp)) {
@mkdir($sDirTmp, 0777, true);
}
$sFileTmp = $sDirTmp . func_generator() . '.' . $sExtension;
$rFile = fopen($sFileTmp, 'w');
fwrite($rFile, $sContent);
fclose($rFile);
return $this->ProcessingFile($sFileTmp, $sTargetType, $sTargetId, $sTargetTmp);
}
public function ProcessingFile($sFileTmp, $sTargetType, $sTargetId, $sTargetTmp = null)
{
/**
2015-02-15 08:06:59 +02:00
* Определяем тип медиа по файлу и запускаем обработку
*/
2015-02-15 08:06:59 +02:00
$iType = $this->GetMediaTypeByFileName($sFileTmp);
if ($iType == self::MEDIA_TYPE_IMAGE) {
return $this->ProcessingFileImage($sFileTmp, $sTargetType, $sTargetId, $sTargetTmp);
}
2015-02-14 18:56:18 +02:00
return $this->Lang_Get('media.error.incorrect_type');
}
public function ProcessingFileImage($sFileTmp, $sTargetType, $sTargetId, $sTargetTmp = null)
{
$aPathInfo = pathinfo($sFileTmp);
$aParams = $this->Image_BuildParams('media.' . $sTargetType);
/**
* Если объект изображения не создан, возвращаем ошибку
*/
if (!$oImage = $this->Image_Open($sFileTmp, $aParams)) {
$this->Fs_RemoveFileLocal($sFileTmp);
return $this->Image_GetLastError();
}
$iWidth = $oImage->getWidth();
$iHeight = $oImage->getHeight();
$sPath = $this->GetSaveDir($sTargetType, $sTargetId);
2016-05-02 17:15:30 +03:00
$iFileSize = $this->Fs_GetFileSize($sFileTmp);
/**
* Уникальное имя файла
*/
$sFileName = func_generator(20);
/**
* Сохраняем оригинальную копию
* Оригинал храним без вотермарка
*/
$sFileResult = null;
$mOriginalSize = $this->GetConfigParam('image.original', $sTargetType);
2017-03-17 08:41:28 +02:00
if ($mOriginalSize !== false && $oImage->getFormat() == 'gif') {
/**
2017-03-17 08:41:28 +02:00
* Если gif, то сохраняем без изменений
*/
2017-03-17 08:41:28 +02:00
if (!$sFileResult = $oImage->saveOriginalSmart($sPath, $sFileName)) {
$this->Fs_RemoveFileLocal($sFileTmp);
return $this->Image_GetLastError();
}
2017-03-17 08:41:28 +02:00
} else {
if ($mOriginalSize === true) {
if (!$sFileResult = $oImage->saveSmart($sPath, $sFileName, array('skip_watermark' => true))) {
$this->Fs_RemoveFileLocal($sFileTmp);
return $this->Image_GetLastError();
}
} elseif (is_string($mOriginalSize)) {
/**
* Ресайзим оригинал
*/
$aOriginalSize = $this->ParsedImageSize($mOriginalSize);
if ($aOriginalSize['crop']) {
$oImage->cropProportion($aOriginalSize['w'] / $aOriginalSize['h'], 'center');
}
if (!$sFileResult = $oImage->resize($aOriginalSize['w'], $aOriginalSize['h'], true)->saveSmart($sPath, $sFileName,
array('skip_watermark' => true))
) {
$this->Fs_RemoveFileLocal($sFileTmp);
return $this->Image_GetLastError();
}
$iFileSize = $this->Fs_GetFileSize($sFileResult);
}
}
$aSizes = $this->GetConfigParam('image.sizes', $sTargetType);
/**
* Перед запуском генерации подчищаем память
*/
unset($oImage);
/**
* Генерируем варианты с необходимыми размерами
*/
$sFileResultLast = $this->GenerateImageBySizes($sFileTmp, $sPath, $sFileName, $aSizes, $aParams);
if (!$sFileResult) {
/**
* Оригинала нет, поэтому получаем фейковый путь до основного файла (нужен для получения файлов других размеров)
*/
$aPathInfoLast = pathinfo($sFileResultLast);
$aFileNamePart = explode('_', $aPathInfoLast['filename']);
$sFileResult = $aPathInfoLast['dirname'] . '/' . $aFileNamePart[0] . '.' . $aPathInfoLast['extension'];
}
/**
* Сохраняем медиа
*/
$oMedia = Engine::GetEntity('ModuleMedia_EntityMedia');
$oMedia->setUserId($this->oUserCurrent ? $this->oUserCurrent->getId() : null);
2015-02-15 08:06:59 +02:00
$oMedia->setType(self::MEDIA_TYPE_IMAGE);
$oMedia->setTargetType($sTargetType);
$oMedia->setFilePath($sFileResult);
$oMedia->setFileName($aPathInfo['filename']);
2016-05-02 17:15:30 +03:00
$oMedia->setFileSize($iFileSize);
$oMedia->setWidth($iWidth);
$oMedia->setHeight($iHeight);
$oMedia->setDataOne('image_sizes', $aSizes);
/**
* Теперь можно удалить временный файл
*/
$this->Fs_RemoveFileLocal($sFileTmp);
/**
* Добавляем в БД
*/
if ($oMedia->Add()) {
/**
* Создаем связь с владельцем
*/
if ($oTarget = $this->AttachMediaToTarget($oMedia, $sTargetType, $sTargetId, $sTargetTmp)) {
$oMedia->_setData(array('_relation_entity' => $oTarget));
return $oMedia;
}
}
return false;
}
/**
* Присоединяет медиа файл к объекту
*
* @param $oMedia
* @param $sTargetType
* @param $sTargetId
* @param $sTargetTmp
* @return bool|Entity
*/
public function AttachMediaToTarget($oMedia, $sTargetType, $sTargetId, $sTargetTmp)
{
/**
* Создаем связь с владельцем
*/
$oTarget = Engine::GetEntity('ModuleMedia_EntityTarget');
$oTarget->setMediaId($oMedia->getId());
$oTarget->setTargetType($sTargetType);
$oTarget->setTargetId($sTargetId ?: null);
$oTarget->setTargetTmp($sTargetTmp ?: null);
if ($oTarget->Add()) {
return $oTarget;
}
return false;
}
/**
* Создает набор отресайзанных изображений
* Варианты наименований результирующих файлов в зависимости от размеров:
* file_100x150 - w=100 h=150 crop=false
* file_100x150crop - w=100 h=150 crop=true
* file_x150 - w=null h=150 crop=false
* file_100x - w=100 h=null crop=false
*
* @param $sFileSource
* @param $sDirDist
* @param $sFileName
* @param $aSizes
* @param null $aParams
*/
public function GenerateImageBySizes($sFileSource, $sDirDist, $sFileName, $aSizes, $aParams = null)
{
if (!$aSizes) {
return;
}
/**
* Преобразуем упрощенную запись списка размеров в полную
*/
foreach ($aSizes as $k => $v) {
if (!is_array($v)) {
$aSizes[$k] = $this->ParsedImageSize($v);
}
}
$sFileResult = null;
foreach ($aSizes as $aSize) {
/**
* Для каждого указанного в конфиге размера генерируем картинку
*/
$sNewFileName = $sFileName . '_' . $aSize['w'] . 'x' . $aSize['h'];
if ($oImage = $this->Image_Open($sFileSource, $aParams)) {
if ($aSize['crop']) {
$oImage->cropProportion($aSize['w'] / $aSize['h'], 'center');
$sNewFileName .= 'crop';
}
if (!$sFileResult = $oImage->resize($aSize['w'], $aSize['h'], true)->saveSmart($sDirDist,
$sNewFileName)
) {
// TODO: прерывать и возвращать false?
}
}
}
/**
* Возвращаем путь до последнего созданного файла
*/
return $sFileResult;
}
public function RemoveImageBySizes($sPath, $aSizes, $bRemoveOriginal = true)
{
if ($aSizes) {
/**
* Преобразуем упрощенную запись списка размеров в полную
*/
foreach ($aSizes as $k => $v) {
if (!is_array($v)) {
$aSizes[$k] = $this->ParsedImageSize($v);
}
}
foreach ($aSizes as $aSize) {
$sSize = $aSize['w'] . 'x' . $aSize['h'];
if ($aSize['crop']) {
$sSize .= 'crop';
}
$this->Image_RemoveFile($this->GetImagePathBySize($sPath, $sSize));
}
}
/**
* Удаляем оригинал
*/
if ($bRemoveOriginal) {
$this->Image_RemoveFile($sPath);
}
}
/**
* Возвращает каталог для сохранения контента медиа
*
* @param string $sTargetType
* @param string|null $sTargetId Желательно для одного типа при формировании каталога для загрузки выбрать что-то одно - использовать $sTargetId или нет
* @param string $sPostfix Дополнительный каталог для сохранения в конце цепочки
*
* @return string
*/
public function GetSaveDir($sTargetType, $sTargetId = null, $sPostfix = '')
{
$sPostfix = trim($sPostfix, '/');
return Config::Get('path.uploads.base') . "/media/{$sTargetType}/" . date('Y/m/d/H/') . ($sPostfix ? "{$sPostfix}/" : '');
}
public function BuildCodeForEditor($oMedia, $aParams)
{
$sCode = '';
2015-02-15 08:06:59 +02:00
if ($oMedia->getType() == self::MEDIA_TYPE_IMAGE) {
$aSizes = (array)$oMedia->getDataOne('image_sizes');
$sSizeParam = isset($aParams['size']) ? (string)$aParams['size'] : '';
$sSize = 'original';
$bNeedHref = false;
/**
* Проверяем корректность размера
*/
foreach ($aSizes as $aSizeAllow) {
$sSizeKey = $aSizeAllow['w'] . 'x' . $aSizeAllow['h'] . ($aSizeAllow['crop'] ? 'crop' : '');
if ($sSizeKey == $sSizeParam) {
$sSize = $sSizeKey;
/**
* Необходимость лайтбокса
*/
if ($aSizeAllow['w'] < $oMedia->getWidth()) {
$bNeedHref = true;
}
}
}
$sPath = $oMedia->getFileWebPath($sSize == 'original' ? null : $sSize);
$aParams['image_url'] = (isset($aParams['relative_web']) and $aParams['relative_web']) ? $this->GetPathRelativeWebFromWeb($sPath) : $sPath;
$aParams['href_url'] = (isset($aParams['relative_web']) and $aParams['relative_web']) ? $this->GetPathRelativeWebFromWeb($oMedia->getFileWebPath()) : $oMedia->getFileWebPath();
$aParams['need_href'] = $bNeedHref;
if (!isset($aParams['title'])) {
$aParams['title'] = $oMedia->getDataOne('title');
}
/**
* Формируем HTML изображения
*/
$sCode = $this->BuildImageCodeForEditor($aParams);
}
return $sCode;
}
/**
* Возвращает относительный веб путь до изображения
*
* @param string $sPath Полный веб путь до изображения
* @return string
*/
public function GetPathRelativeWebFromWeb($sPath)
{
return '/' . ltrim(parse_url($sPath, PHP_URL_PATH), '/');
}
/**
* Формирует HTML изображения
*
* @param $aParams
* @return string
*/
public function BuildImageCodeForEditor($aParams)
{
$sCode = '<img src="' . htmlspecialchars($aParams['image_url']) . '" ';
if (!isset($aParams['skip_title']) and isset($aParams['title']) and $aParams['title']) {
$sCode .= ' title="' . htmlspecialchars($aParams['title']) . '" ';
$sCode .= ' alt="' . htmlspecialchars($aParams['title']) . '" ';
}
if (isset($aParams['align']) and in_array($aParams['align'], array('left', 'right', 'center'))) {
if ($aParams['align'] == 'center') {
$sCode .= ' class="image-center"';
} else {
$sCode .= ' align="' . htmlspecialchars($aParams['align']) . '" ';
}
}
$sDataParams = '';
if (isset($aParams['data']) and is_array($aParams['data'])) {
foreach ($aParams['data'] as $sDataName => $sDataValue) {
if ($sDataValue) {
$sDataParams .= ' data-' . $sDataName . '="' . htmlspecialchars($sDataValue) . '"';
}
}
}
if (isset($aParams['need_href']) and $aParams['need_href']) {
$sCode .= ' />';
$sLbxGroup = '';
if (isset($aParams['lbx_group'])) {
$sLbxGroup = ' data-rel="' . htmlspecialchars($aParams['lbx_group']) . '"';
}
$sCode = '<a class="js-lbx" ' . $sLbxGroup . ' href="' . htmlspecialchars($aParams['href_url']) . '" ' . $sDataParams . '>' . $sCode . '</a>';
} else {
$sCode .= $sDataParams . ' />';
}
return $sCode;
}
public function GetMediaByTarget($sTargetType, $iTargetId, $iUserId = null)
{
return $this->oMapper->GetMediaByTarget($sTargetType, $iTargetId, $iUserId);
}
public function GetMediaByTargetTmp($sTargetTmp, $iUserId = null)
{
return $this->oMapper->GetMediaByTargetTmp($sTargetTmp, $iUserId);
}
/**
* Выполняет удаление файлов медиа-объекта
*
* @param $oMedia
*/
public function DeleteFiles($oMedia)
{
/**
* Сначала удаляем все файлы
*/
2015-02-15 08:06:59 +02:00
if ($oMedia->getType() == self::MEDIA_TYPE_IMAGE) {
$aSizes = $oMedia->getDataOne('image_sizes');
$this->RemoveImageBySizes($oMedia->getFilePath(), $aSizes);
}
}
/**
* Возвращает список media с учетов прав доступа текущего пользователя
2015-01-26 13:49:42 +02:00
* В методе происходит проверка на права:
* 1. разрешаем объекты, которые создал пользователь
* 2. разрешаем объекты, которые связаны таргетом, к которому у пользователя есть доступ на редактирование
* @param array $aId
*
* @return array
*/
public function GetAllowMediaItemsById($aId)
{
$aIdItems = array();
foreach ((array)$aId as $iId) {
$aIdItems[] = (int)$iId;
}
2015-01-26 13:49:42 +02:00
$aIdItemsAll = $aIdItems;
if (is_array($aIdItems) and count($aIdItems)) {
$iUserId = $this->oUserCurrent ? $this->oUserCurrent->getId() : null;
2015-01-26 13:49:42 +02:00
$aMediaItems = $this->Media_GetMediaItemsByFilter(array(
'#where' => array(
'id in (?a) AND ( user_id is null OR user_id = ?d )' => array(
$aIdItems,
$iUserId
)
2015-01-26 13:49:42 +02:00
),
'#index-from-primary'
)
);
2015-01-26 13:49:42 +02:00
/**
* Смотрим что осталось
*/
if (!is_null($iUserId) and $aIdItems = array_diff($aIdItems, array_keys($aMediaItems))) {
$aMediaAllowIds = array();
$aTargetMediaGroup = $this->GetTargetItemsByFilter(array(
'media_id in' => $aIdItems,
2015-04-19 12:40:01 +03:00
'#with' => 'media',
2015-01-26 13:49:42 +02:00
'#index-group' => 'media_id'
));
$_this = $this;
foreach ($aTargetMediaGroup as $iMediaId => $aTargetMedia) {
/**
* Проверяем каждый таргет
*/
foreach ($aTargetMedia as $oTargetMedia) {
if ($this->Cache_Remember("media_check_target_{$oTargetMedia->getTargetType()}_{$oTargetMedia->getTargetId()}",
2015-04-19 12:40:01 +03:00
function () use ($_this, $oTargetMedia, $iUserId) {
if ($oTargetMedia->getTargetId()) {
return $_this->CheckTarget($oTargetMedia->getTargetType(), $oTargetMedia->getTargetId(), ModuleMedia::TYPE_CHECK_ALLOW_ADD);
}
if ($oMedia = $oTargetMedia->getMedia() and $oMedia->getUserId() == $iUserId) {
return true;
}
return false;
2015-01-26 13:49:42 +02:00
}, false, array(), 'life', true)
) {
$aMediaAllowIds[] = $oTargetMedia->getMediaId();
break;
}
}
}
if ($aMediaAllowIds) {
$aMediaItems = $aMediaItems + $this->GetMediaItemsByFilter(array(
'id in' => $aMediaAllowIds,
'#index-from-primary'
));
}
}
/**
* Нужно отсортировать по первоначальному массиву $aIdItems
*/
$aReturn = array();
foreach ($aIdItemsAll as $iId) {
if (isset($aMediaItems[$iId])) {
$aReturn[$iId] = $aMediaItems[$iId];
}
}
return $aReturn;
}
return array();
}
/**
* Обработка тега gallery в тексте
* <pre>
* <gallery items="12,55,38" />
* </pre>
*
* @param string $sTag Тег на ктором сработал колбэк
* @param array $aParams Список параметров тега
* @return string
*/
public function CallbackParserTagGallery($sTag, $aParams)
{
if (isset($aParams['items'])) {
$aItems = explode(',', $aParams['items']);
2015-01-26 13:49:42 +02:00
$aItems = array_unique($aItems);
} else {
return '';
}
2015-01-26 13:49:42 +02:00
if (!($aMediaItems = $this->GetAllowMediaItemsById($aItems))) {
return '';
}
$aParamsMedia = array(
'size' => '100crop',
'skip_title' => true,
'relative_web' => true
);
$sProperties = '';
if (isset($aParams['nav']) and in_array($aParams['nav'], array('thumbs'))) {
$sProperties .= ' data-nav="' . $aParams['nav'] . '" ';
}
$sTextResult = '<div class="fotorama" ' . $sProperties . '>' . "\r\n";
foreach ($aMediaItems as $oMedia) {
if (isset($aParams['caption']) and $aParams['caption']) {
$aParamsMedia['data']['caption'] = htmlspecialchars($oMedia->getDataOne('title'));
}
2015-01-26 13:49:42 +02:00
$sTextResult .= "\t" . $this->BuildCodeForEditor($oMedia, $aParamsMedia) . "\r\n";
}
$sTextResult .= "</div>\r\n";
return $sTextResult;
}
/**
* Заменяет временный идентификатор на необходимый ID объекта
*
* @param string $sTargetType
* @param string $sTargetId
* @param null|string $sTargetTmp Если не задан, то берется их куки "media_target_tmp_{$sTargetType}"
2016-05-02 14:18:37 +03:00
* @param null|array $aMediaId Необязательный список конкретных media id
*/
2016-05-02 14:18:37 +03:00
public function ReplaceTargetTmpById($sTargetType, $sTargetId, $sTargetTmp = null, $aMediaId = null)
{
$sCookieKey = 'media_target_tmp_' . $sTargetType;
if (is_null($sTargetTmp) and $this->Session_GetCookie($sCookieKey)) {
$sTargetTmp = $this->Session_GetCookie($sCookieKey);
2016-05-02 14:18:37 +03:00
if (is_null($aMediaId)) {
$this->Session_DropCookie($sCookieKey);
}
}
if (is_string($sTargetTmp)) {
2016-05-02 14:18:37 +03:00
$aFilter = array(
'target_tmp' => $sTargetTmp,
'target_type' => $sTargetType,
);
if (!is_null($aMediaId)) {
$aNeedId = array(-1);
foreach ($aMediaId as $sId) {
if (is_numeric($sId)) {
$aNeedId[] = $sId;
}
}
$aFilter['media_id in'] = $aNeedId;
}
$aTargetItems = $this->Media_GetTargetItemsByFilter($aFilter);
foreach ($aTargetItems as $oTarget) {
$oTarget->setTargetTmp(null);
$oTarget->setTargetId($sTargetId);
$oTarget->Update();
/**
* Уведомляем объект о создании превью
*/
if ($oTarget->getIsPreview()) {
$this->NotifyCreatePreviewTarget($oTarget->getTargetType(), $oTarget->getTargetId(), $oTarget);
}
}
}
}
/**
* Удаляет связь с медиа данными + при необходимости удаляет сами медиа данные
*
* @param string $sTargetType
* @param int $sTargetId
* @param bool $bMediaRemove Удалять медиа данные оставшиеся без связей
*/
public function RemoveTarget($sTargetType, $sTargetId, $bMediaRemove = true)
{
/**
* Получаем прикрепленные медиа
*/
$aMediaItems = $this->GetMediaByTarget($sTargetType, $sTargetId);
/**
* Удаляем все связи текущего таргета
*/
$this->RemoveTargetByTypeAndId($sTargetType, $sTargetId);
if ($bMediaRemove and $aMediaItems) {
/**
* Проверяем с какими медиа данными еще остались связи
*/
$aMediaIds = array();
foreach ($aMediaItems as $oMediaItem) {
$aMediaIds[] = $oMediaItem->getId();
}
$aTargetItems = $this->GetTargetItemsByFilter(array(
'media_id in' => $aMediaIds,
'#index-group' => 'media_id'
));
/**
* Удаляем медиа данные без оставшихся связей
*/
foreach ($aMediaItems as $oMediaItem) {
if (!isset($aTargetItems[$oMediaItem->getId()])) {
$oMediaItem->Delete();
}
}
}
}
public function RemoveTargetByTypeAndId($sTargetType, $iTargetId)
{
$bRes = $this->oMapper->RemoveTargetByTypeAndId($sTargetType, $iTargetId);
/**
* Сбрасываем кеши
*/
$this->Cache_Clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array(
'ModuleMedia_EntityTarget_delete'
));
return $bRes;
}
public function GetFileWebPath($oMedia, $sSize = null)
{
2015-02-15 08:06:59 +02:00
if ($oMedia->getType() == self::MEDIA_TYPE_IMAGE) {
/**
* Проверяем необходимость автоматического создания превью нужного размера - если разрешено настройками и файл НЕ существует
*/
2015-08-18 11:55:38 +03:00
if ($sSize and $this->GetConfigParam('image.autoresize',
$oMedia->getTargetType()) and !$this->Image_IsExistsFile($this->GetImagePathBySize($oMedia->getFilePath(),
$sSize))
) {
/**
* Запускаем генерацию изображения нужного размера
*/
$aSize = $this->ParsedImageSize($sSize);
$aParams = $this->Image_BuildParams('media.' . $oMedia->getTargetType());
$sNewFileName = $this->GetImagePathBySize($oMedia->getFilePath(), $sSize);
if ($oImage = $this->Image_OpenFrom($oMedia->getFilePath(), $aParams)) {
if ($aSize['crop']) {
$oImage->cropProportion($aSize['w'] / $aSize['h'], 'center');
}
$oImage->resize($aSize['w'], $aSize['h'], true)->save($sNewFileName);
/**
* Обновляем список размеров
*/
$aSizeOld = (array)$oMedia->getDataOne('image_sizes');
$aSizeOld[] = $aSize;
$oMedia->setDataOne('image_sizes', $aSizeOld);
$oMedia->Update();
}
}
return $this->GetImageWebPath($oMedia->getFilePath(), $sSize);
}
return null;
}
/**
* Возвращает веб путь до файла изображения
*
* @param $sPath
* @param null $sSize
* @return string
*/
public function GetImageWebPath($sPath, $sSize = null)
{
$sPath = $this->Fs_GetPathWeb($sPath);
if ($sSize) {
return $this->GetImagePathBySize($sPath, $sSize);
} else {
return $sPath;
}
}
/**
* Возвращает путь до изображения конкретного размера
* Варианты преобразования размеров в имя файла:
* 100 - file_100x100
* 100crop - file_100x100crop
* 100x150 - file_100x150
* 100x150crop - file_100x150crop
* x150 - file_x150
* 100x - file_100x
*
* @param string $sPath
* @param string $sSize
*
* @return string
*/
public function GetImagePathBySize($sPath, $sSize)
{
$aPathInfo = pathinfo($sPath);
if (is_array($sSize)) {
$aSize = $sSize;
$sSize = $aSize['w'] . 'x' . $aSize['h'];
if ($aSize['crop']) {
$sSize .= 'crop';
}
} else {
if (preg_match('#^(\d+)([a-z]{2,10})?$#i', $sSize, $aMatch)) {
$sSize = $aMatch[1] . 'x' . $aMatch[1];
if (isset($aMatch[2])) {
$sSize .= strtolower($aMatch[2]);
}
}
}
return $aPathInfo['dirname'] . '/' . $aPathInfo['filename'] . '_' . $sSize . '.' . $aPathInfo['extension'];
}
/**
* Парсит строку с размером изображения
* Варианты входной строки:
* 100
* 100crop
* 100x150
* 100x150crop
* x150
* 100x
*
* @param string $sSize
*
* @return array Массив вида array('w'=>100,'h'=>150,'crop'=>true)
*/
public function ParsedImageSize($sSize)
{
$aSize = array(
'w' => null,
'h' => null,
'crop' => false,
);
if (preg_match('#^(\d+)?(x)?(\d+)?([a-z]{2,10})?$#Ui', $sSize, $aMatch)) {
$iW = (isset($aMatch[1]) and $aMatch[1]) ? $aMatch[1] : null;
$iH = (isset($aMatch[3]) and $aMatch[3]) ? $aMatch[3] : null;
$bDelim = (isset($aMatch[2]) and $aMatch[2]) ? true : false;
$sMod = (isset($aMatch[4]) and $aMatch[4]) ? $aMatch[4] : '';
if (!$bDelim) {
$iW = $iH;
}
$aSize['w'] = $iW;
$aSize['h'] = $iH;
if ($sMod) {
$aSize[$sMod] = true;
}
}
return $aSize;
}
/**
* Производит стандартнуе проверку на определенное действие с конкретным объектом Media
*
* @param $sAllowType
* @param $aParams
*
* @return bool
*/
public function CheckStandartMediaAllow($sAllowType, $aParams)
{
if (!$oUser = $aParams['user']) {
return false;
}
$oMedia = isset($aParams['media']) ? $aParams['media'] : null;
if (!$oMedia) {
return false;
}
if (in_array($sAllowType, array(self::TYPE_CHECK_ALLOW_REMOVE, self::TYPE_CHECK_ALLOW_UPDATE))) {
if ($oMedia->getUserId() == $oUser->getId()) {
return true;
}
}
return false;
}
/**
* Перегенерация всех превью
*
* @param array|string|null $aTypes Список типов для перегенерации
*/
public function ReCreateFilePreviewAll($aTypes = null)
{
$iPage = 1;
$aFilter = array(
'is_preview' => 1,
'target_id <>' => null,
'#with' => array('media'),
'#page' => array($iPage, 100)
);
if ($aTypes) {
if (!is_array($aTypes)) {
$aTypes = array($aTypes);
}
$aFilter['target_type in'] = $aTypes;
}
while ($aRes = $this->GetTargetItemsByFilter($aFilter) and $aRes['collection']) {
foreach ($aRes['collection'] as $oTarget) {
if ($oMedia = $oTarget->getMedia()) {
$this->CreateFilePreview($oMedia, $oTarget);
}
}
$iPage++;
$aFilter['#page'][0] = $iPage;
}
}
/**
* Создает превью у файла для определенного типа
*
* @param $oMedia
* @param $oTarget
*
* @return bool|string
*/
public function CreateFilePreview($oMedia, $oTarget)
{
if (!$this->GetTargetTypeParam($oTarget->getTargetType(), 'allow_preview')) {
return false;
}
/**
* Нужно удалить прошлое превью (если оно есть)
*/
$this->RemoveFilePreview($oMedia, $oTarget);
2015-02-15 08:06:59 +02:00
if ($oMedia->getType() == self::MEDIA_TYPE_IMAGE) {
$aParams = $this->Image_BuildParams('media.preview_' . $oTarget->getTargetType());
if (!$oImage = $this->Image_OpenFrom($oMedia->getFilePath(), $aParams)) {
return $this->Image_GetLastError();
}
/**
* Сохраняем во временный файл
*/
if (!$sFileTmp = $oImage->saveTmp()) {
return $this->Image_GetLastError();
}
unset($oImage);
/**
* Получаем список необходимых размеров превью
*/
$aSizes = $this->GetConfigParam('image.preview.sizes', $oTarget->getTargetType());
/**
* Каталог для сохранения превью
*/
$sPath = $this->GetSaveDir($oTarget->getTargetType(), $oTarget->getTargetId(), 'preview');
/**
* Уникальное имя файла
*/
$sFileName = func_generator(20);
/**
* Генерируем варианты с необходимыми размерами
*/
$sFileLast = $this->GenerateImageBySizes($sFileTmp, $sPath, $sFileName, $aSizes, $aParams);
$aSizeLast = end($aSizes);
$sReplaceSize = '_' . $aSizeLast['w'] . 'x' . $aSizeLast['h'];
if ($aSizeLast['crop']) {
$sReplaceSize .= 'crop';
}
$sFileLast = str_replace($sReplaceSize, '', $sFileLast);
/**
* Теперь можно удалить временный файл
*/
$this->Fs_RemoveFileLocal($sFileTmp);
/**
* Сохраняем данные во связи
*/
$oTarget->setDataOne('image_preview_sizes', $aSizes);
$oTarget->setDataOne('image_preview', $sFileLast);
$oTarget->setIsPreview(1);
$oTarget->Update();
/**
* Уведомляем объект о создании нового превью
*/
if ($oTarget->getTargetId()) {
$this->NotifyCreatePreviewTarget($oTarget->getTargetType(), $oTarget->getTargetId(), $oTarget);
}
return true;
}
}
public function RemoveFilePreview($oMedia, $oTarget)
{
2015-02-15 08:06:59 +02:00
if ($oMedia->getType() == self::MEDIA_TYPE_IMAGE) {
if ($oTarget->getDataOne('image_preview')) {
/**
* Уведомляем объект о удалении превью
*/
if ($oTarget->getTargetId()) {
$this->NotifyRemovePreviewTarget($oTarget->getTargetType(), $oTarget->getTargetId(), $oTarget);
}
$this->RemoveImageBySizes($oTarget->getDataOne('image_preview'),
$oTarget->getDataOne('image_preview_sizes'));
$oTarget->setDataOne('image_preview', null);
$oTarget->setDataOne('image_preview_sizes', array());
$oTarget->setIsPreview(0);
$oTarget->Update();
return true;
}
}
}
/**
* Удаляет все превью у конкретного объекта
*
* @param $sTargetType
* @param $sTargetId
* @param null $sTargetTmp
*/
public function RemoveAllPreviewByTarget($sTargetType, $sTargetId, $sTargetTmp = null)
{
$aFilter = array(
'target_type' => $sTargetType,
'is_preview' => 1,
'#with' => array('media')
);
if ($sTargetId) {
$aFilter['target_id'] = $sTargetId;
} else {
$aFilter['target_tmp'] = $sTargetTmp;
}
$aTargetItems = $this->Media_GetTargetItemsByFilter($aFilter);
foreach ($aTargetItems as $oTarget) {
$this->RemoveFilePreview($oTarget->getMedia(), $oTarget);
}
}
/**
* Обработка создания превью для типа 'topic'
* Название метода формируется автоматически
*
* @param int $iTargetId
* @param ModuleMedia_EntityTarget $oRelationTarget
*/
public function NotifyCreatePreviewTargetTopic($iTargetId, $oRelationTarget)
{
if ($oTopic = $this->Topic_GetTopicById($iTargetId)) {
$oTopic->setPreviewImage($oRelationTarget->getDataOne('image_preview'));
$this->Topic_UpdateTopic($oTopic);
}
}
/**
* Обработка удаления превью для типа 'topic'
* Название метода формируется автоматически
*
* @param int $iTargetId
* @param ModuleMedia_EntityTarget $oRelationTarget
*/
public function NotifyRemovePreviewTargetTopic($iTargetId, $oRelationTarget)
{
if ($oTopic = $this->Topic_GetTopicById($iTargetId)) {
$oTopic->setPreviewImage(null);
$this->Topic_UpdateTopic($oTopic);
}
}
/**
* Проверка владельца с типом "topic"
* Название метода формируется автоматически
*
* @param int|null $iTargetId ID владельца, для новых объектов может быть не определен
* @param string $sAllowType Тип доступа, константа self::TYPE_CHECK_ALLOW_*
* @param array $aParams Дополнительные параметры, всегда есть ключ 'user'
*
* @return bool
*/
public function CheckTargetTopic($iTargetId, $sAllowType, $aParams)
{
if (!$oUser = $aParams['user']) {
return false;
}
2015-01-26 13:49:42 +02:00
if (in_array($sAllowType,
array(self::TYPE_CHECK_ALLOW_ADD, self::TYPE_CHECK_ALLOW_PREVIEW, self::TYPE_CHECK_ALLOW_VIEW_LIST))) {
if (is_null($iTargetId)) {
/**
* Разрешаем для всех новых топиков
*/
return true;
}
if ($oTopic = $this->Topic_GetTopicById($iTargetId)) {
/**
* Проверяем права на редактирование топика
*/
if ($this->ACL_IsAllowEditTopic($oTopic, $oUser)) {
/**
* Дополнительно возможность исползования превью (настраивается отдельно для каждого типа топика)
*/
if ($sAllowType == self::TYPE_CHECK_ALLOW_PREVIEW) {
if ($oTopicType = $this->Topic_GetTopicType($oTopic->getType())) {
if (!$oTopicType->getParam('allow_preview')) {
return false;
}
}
}
return true;
}
}
} else {
return $this->CheckStandartMediaAllow($sAllowType, $aParams);
}
return false;
}
/**
* Проверка владельца с типом "comment"
* Название метода формируется автоматически
*
* @param int|null $iTargetId ID владельца, для новых объектов может быть не определен
* @param string $sAllowType Тип доступа, константа self::TYPE_CHECK_ALLOW_*
* @param array $aParams Дополнительные параметры, всегда есть ключ 'user'
*
* @return bool
*/
public function CheckTargetComment($iTargetId, $sAllowType, $aParams)
{
if (!$oUser = $aParams['user']) {
return false;
}
2015-01-26 13:49:42 +02:00
if (in_array($sAllowType, array(self::TYPE_CHECK_ALLOW_ADD, self::TYPE_CHECK_ALLOW_VIEW_LIST))) {
if (is_null($iTargetId)) {
/**
* Разрешаем для всех новых комментариев
*/
return true;
}
if ($oComment = $this->Comment_GetCommentById($iTargetId)) {
/**
* Проверяем права на редактирование комментария
*/
if ($this->ACL_IsAllowEditComment($oComment, $oUser)) {
return true;
}
}
} else {
return $this->CheckStandartMediaAllow($sAllowType, $aParams);
}
return false;
}
/**
* Проверка владельца с типом "blog"
* Название метода формируется автоматически
*
* @param int|null $iTargetId ID владельца, для новых объектов может быть не определен
* @param string $sAllowType Тип доступа, константа self::TYPE_CHECK_ALLOW_*
* @param array $aParams Дополнительные параметры, всегда есть ключ 'user'
*
* @return bool
*/
public function CheckTargetBlog($iTargetId, $sAllowType, $aParams)
{
if (!$oUser = $aParams['user']) {
return false;
}
2015-01-26 13:49:42 +02:00
if (in_array($sAllowType, array(self::TYPE_CHECK_ALLOW_ADD, self::TYPE_CHECK_ALLOW_VIEW_LIST))) {
if (is_null($iTargetId)) {
/**
* Разрешаем для всех новых блогов
*/
return true;
}
if ($oBlog = $this->Blog_GetBlogById($iTargetId)) {
/**
* Проверяем права на редактирование блога
*/
if ($this->ACL_IsAllowEditBlog($oBlog, $oUser)) {
return true;
}
}
} else {
return $this->CheckStandartMediaAllow($sAllowType, $aParams);
}
return false;
}
/**
* Проверка владельца с типом "talk"
* Название метода формируется автоматически
*
* @param int|null $iTargetId ID владельца, для новых объектов может быть не определен
* @param string $sAllowType Тип доступа, константа self::TYPE_CHECK_ALLOW_*
* @param array $aParams Дополнительные параметры, всегда есть ключ 'user'
*
* @return bool
*/
public function CheckTargetTalk($iTargetId, $sAllowType, $aParams)
{
if (!$oUser = $aParams['user']) {
return false;
}
2015-01-26 13:49:42 +02:00
if (in_array($sAllowType, array(self::TYPE_CHECK_ALLOW_ADD, self::TYPE_CHECK_ALLOW_VIEW_LIST))) {
if (is_null($iTargetId)) {
/**
2015-01-26 13:49:42 +02:00
* Разрешаем для всех новых сообщений
*/
return true;
}
/**
* Редактировать сообщения нельзя
*/
return false;
} else {
return $this->CheckStandartMediaAllow($sAllowType, $aParams);
}
return false;
}
}