1
0
Fork 0
mirror of https://github.com/Oreolek/ifhub.club.git synced 2024-06-16 23:00:51 +03:00

Наброски функционала создания превью для медиа-файлов

This commit is contained in:
Mzhelskiy Maxim 2014-03-21 17:04:59 +07:00
parent 5bdb3929e6
commit 2de215ad49
13 changed files with 405 additions and 17 deletions

View file

@ -84,6 +84,7 @@ class ActionAjax extends Action {
$this->AddEventPreg('/^media$/i','/^submit-create-photoset$/','/^$/','EventMediaSubmitCreatePhotoset');
$this->AddEventPreg('/^media$/i','/^load-gallery$/','/^$/','EventMediaLoadGallery');
$this->AddEventPreg('/^media$/i','/^remove-file$/','/^$/','EventMediaRemoveFile');
$this->AddEventPreg('/^media$/i','/^create-preview-file$/','/^$/','EventMediaCreatePreviewFile');
$this->AddEventPreg('/^media$/i','/^save-data-file$/','/^$/','EventMediaSaveDataFile');
$this->AddEventPreg('/^property$/i','/^tags$/','/^autocompleter$/','/^$/','EventPropertyTagsAutocompleter');
@ -532,6 +533,52 @@ class ActionAjax extends Action {
}
}
protected function EventMediaCreatePreviewFile() {
/**
* Пользователь авторизован?
*/
if (!$this->oUserCurrent) {
$this->Message_AddErrorSingle($this->Lang_Get('need_authorization'),$this->Lang_Get('error'));
return;
}
$sId=getRequestStr('id');
if (!$oMedia=$this->Media_GetMediaById($sId)) {
return $this->EventErrorDebug();
}
$sTargetType=getRequestStr('target_type');
$sTargetId=getRequestStr('target_id');
$sTargetTmp=getRequestStr('target_tmp');
/**
* Получаем объект связи
*/
$aFilter=array('media_id'=>$oMedia->getId(),'target_type'=>$sTargetType);
if ($sTargetTmp) {
$aFilter['target_tmp']=$sTargetTmp;
} else {
$aFilter['target_id']=$sTargetId;
}
if (!$oTarget=$this->Media_GetTargetByFilter($aFilter)) {
return $this->EventErrorDebug();
}
if ($oTarget->getIsPreview()) {
return $this->EventErrorDebug();
}
/**
* Проверяем доступ к этому медиа
*/
if (true===$res=$this->Media_CheckTarget($oTarget->getTargetType(),$oTarget->getTargetId(),ModuleMedia::TYPE_CHECK_ALLOW_PREVIEW,array('media'=>$oMedia,'user'=>$this->oUserCurrent))) {
$this->Media_CreateFilePreview($oMedia,$oTarget);
$this->Viewer_AssignAjax('bUnsetOther',true);
} else {
$this->Message_AddErrorSingle(is_string($res) ? $res : $this->Lang_Get('system_error'));
}
}
protected function EventMediaLoadGallery() {
/**
* Пользователь авторизован?

View file

@ -35,6 +35,7 @@ class ModuleMedia extends ModuleORM {
const TYPE_CHECK_ALLOW_ADD='add';
const TYPE_CHECK_ALLOW_REMOVE='remove';
const TYPE_CHECK_ALLOW_UPDATE='update';
const TYPE_CHECK_ALLOW_PREVIEW='preview';
/**
* Объект текущего пользователя
*
@ -45,7 +46,9 @@ class ModuleMedia extends ModuleORM {
protected $oMapper=null;
protected $aTargetTypes=array(
'topic'=>array(),
'topic'=>array(
'allow_preview'=>true,
),
'comment'=>array(),
'blog'=>array(),
'talk'=>array(),
@ -105,12 +108,29 @@ class ModuleMedia extends ModuleORM {
*
* @param string $sTargetType
*
* @return mixed
* @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;
}
/**
* Проверяет разрешен ли тип медиа
@ -144,6 +164,17 @@ class ModuleMedia extends ModuleORM {
}
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;
}
/**
* Возвращает параметр конфига с учетом текущего target_type
*
@ -351,7 +382,7 @@ class ModuleMedia extends ModuleORM {
$aSizes[$k]=$this->ParsedImageSize($v);
}
}
$sFileResult=null;
foreach ($aSizes as $aSize) {
/**
* Для каждого указанного в конфиге размера генерируем картинку
@ -362,11 +393,15 @@ class ModuleMedia extends ModuleORM {
$oImage->cropProportion($aSize['w']/$aSize['h'],'center');
$sNewFileName .= 'crop';
}
if (!$oImage->resize($aSize['w'],$aSize['h'],true)->saveSmart($sDirDist,$sNewFileName)) {
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) {
@ -398,11 +433,13 @@ class ModuleMedia extends ModuleORM {
*
* @param string $sTargetType
* @param string|null $sTargetId Желательно для одного типа при формировании каталога для загрузки выбрать что-то одно - использовать $sTargetId или нет
* @param string $sPostfix Дополнительный каталог для сохранения в конце цепочки
*
* @return string
*/
public function GetSaveDir($sTargetType,$sTargetId=null) {
return Config::Get('path.uploads.base')."/media/{$sTargetType}/".date('Y/m/d/H/');
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) {
@ -569,6 +606,12 @@ class ModuleMedia extends ModuleORM {
$oTarget->setTargetTmp(null);
$oTarget->setTargetId($sTargetId);
$oTarget->Update();
/**
* TODO: Уведомляем объект о создании превью
*/
if ($oTarget->getIsPreview()) {
}
}
}
}
@ -745,11 +788,99 @@ class ModuleMedia extends ModuleORM {
}
return false;
}
/**
* Создает превью у файла для определенного типа
*
* @param $oMedia
* @param $oTarget
*
* @return bool|string
*/
public function CreateFilePreview($oMedia,$oTarget) {
if (!$this->GetTargetTypeParam($oTarget->getTargetType(),'allow_preview')) {
return false;
}
/**
* TODO: нужно удалить прошлое превью
*/
if ($oMedia->getType()==self::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;
}
}
/**
* Обработка создания превью для типа '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"
* Название метода формируется автоматически
@ -764,7 +895,7 @@ class ModuleMedia extends ModuleORM {
if (!$oUser=$aParams['user']) {
return false;
}
if ($sAllowType==self::TYPE_CHECK_ALLOW_ADD) {
if (in_array($sAllowType,array(self::TYPE_CHECK_ALLOW_ADD,self::TYPE_CHECK_ALLOW_PREVIEW))) {
if (is_null($iTargetId)) {
/**
* Разрешаем для всех новых топиков

View file

@ -91,4 +91,8 @@ class ModuleMedia_EntityMedia extends EntityORM {
$aData[$sKey]=$mValue;
$this->setData($aData);
}
public function getRelationTarget() {
return $this->_getDataOne('_relation_entity');
}
}

View file

@ -35,4 +35,31 @@ class ModuleMedia_EntityTarget extends EntityORM {
}
return true;
}
public function getData() {
$aData=@unserialize($this->_getDataOne('data'));
if (!$aData) {
$aData=array();
}
return $aData;
}
public function setData($aRules) {
$this->_aData['data']=@serialize($aRules);
}
public function getDataOne($sKey) {
$aData=$this->getData();
if (isset($aData[$sKey])) {
return $aData[$sKey];
}
return null;
}
public function setDataOne($sKey,$mValue) {
$aData=$this->getData();
$aData[$sKey]=$mValue;
$this->setData($aData);
}
}

View file

@ -28,7 +28,9 @@
class ModuleMedia_MapperMedia extends Mapper {
public function GetMediaByTarget($sTargetType,$iTargetId,$iUserId=null) {
$sFieldsJoinReturn=$this->GetFieldsRelationTarget();
$sql = "SELECT
{$sFieldsJoinReturn},
m.*
FROM ".Config::Get('db.table.media_target')." AS t
JOIN ".Config::Get('db.table.media')." as m on ( m.id=t.media_id { and m.user_id = ?d } )
@ -42,15 +44,15 @@ class ModuleMedia_MapperMedia extends Mapper {
$aResult = array();
if ($aRows = $this->oDb->select($sql,$iUserId ? $iUserId : DBSIMPLE_SKIP,$iTargetId, $sTargetType)) {
foreach ($aRows as $aRow) {
$aResult[]=Engine::GetEntity('ModuleMedia_EntityMedia',$aRow);
}
$aResult=$this->PrepareResultTarget($aRows);
}
return $aResult;
}
public function GetMediaByTargetTmp($sTargetTmp,$iUserId=null) {
$sFieldsJoinReturn=$this->GetFieldsRelationTarget();
$sql = "SELECT
{$sFieldsJoinReturn},
m.*
FROM ".Config::Get('db.table.media_target')." AS t
JOIN ".Config::Get('db.table.media')." as m on ( m.id=t.media_id { and m.user_id = ?d } )
@ -62,9 +64,7 @@ class ModuleMedia_MapperMedia extends Mapper {
$aResult = array();
if ($aRows = $this->oDb->select($sql,$iUserId ? $iUserId : DBSIMPLE_SKIP,$sTargetTmp)) {
foreach ($aRows as $aRow) {
$aResult[]=Engine::GetEntity('ModuleMedia_EntityMedia',$aRow);
}
$aResult=$this->PrepareResultTarget($aRows);
}
return $aResult;
}
@ -82,4 +82,42 @@ class ModuleMedia_MapperMedia extends Mapper {
}
return false;
}
protected function GetFieldsRelationTarget() {
$oEntityJoinSample=Engine::GetEntity('ModuleMedia_EntityTarget');
/**
* Формируем список полей для возврата у таблице связей
*/
$aFieldsJoinReturn=$oEntityJoinSample->_getFields();
foreach($aFieldsJoinReturn as $k=>$sField) {
if (!is_numeric($k)) {
// Удаляем служебные (примари) поля
unset($aFieldsJoinReturn[$k]);
continue;
}
$aFieldsJoinReturn[$k]="t.`{$sField}` as t_join_{$sField}";
}
$sFieldsJoinReturn=join(', ',$aFieldsJoinReturn);
return $sFieldsJoinReturn;
}
protected function PrepareResultTarget($aRows) {
$aResult=array();
foreach ($aRows as $aRow) {
$aData=array();
$aDataRelation=array();
foreach($aRow as $k=>$v) {
if (strpos($k,'t_join_')===0) {
$aDataRelation[str_replace('t_join_','',$k)]=$v;
} else {
$aData[$k]=$v;
}
}
$aData['_relation_entity']=Engine::GetEntity('ModuleMedia_EntityTarget',$aDataRelation);
$oEntity=Engine::GetEntity('ModuleMedia_EntityMedia',$aData);
$oEntity->_SetIsNew(false);
$aResult[]=$oEntity;
}
return $aResult;
}
}

View file

@ -517,6 +517,29 @@ class ModuleTopic_EntityTopic extends Entity {
return null;
}
/**
* Сохраняет путь до превью
*
* @param $data
*/
public function setPreviewImage($data) {
$this->setExtraValue('preview_image',$data);
}
/**
* Возвращает веб путь до превью нужного размера
*
* @param $sSize
*
* @return null
*/
public function getPreviewImageWebPath($sSize) {
if ($sPath=$this->getExtraValue('preview_image')) {
return $this->Media_GetImageWebPath($sPath,$sSize);
} else {
return null;
}
}
//*************************************************************************************************************************************************

View file

@ -217,6 +217,18 @@ $config['module']['media']['image']['sizes'] = array( // список разм
'crop' => true,
)
);
$config['module']['media']['image']['preview']['sizes'] = array( // список размеров, которые необходимо делать при создании превью
array(
'w' => 500,
'h' => 300,
'crop' => true,
),
array(
'w' => 250,
'h' => 150,
'crop' => true,
),
);
// Какие модули должны быть загружены на старте
$config['module']['autoLoad'] = array('Hook','Cache','Security','Session','Lang','Message','User');

View file

@ -17,6 +17,7 @@ ls.media = (function ($) {
* @private
*/
var _defaults = {
target_params: {},
target_type: '',
target_id: '',
target_tmp: '',
@ -47,6 +48,8 @@ ls.media = (function ($) {
info: {
self: '.js-media-item-info',
remove: '.js-media-item-info-remove',
create_preview: '.js-media-item-info-create-preview',
remove_preview: '.js-media-item-info-remove-preview',
sizes: 'select[name=size]',
empty: '.js-media-item-info-empty'
},
@ -67,6 +70,8 @@ ls.media = (function ($) {
routers: {
upload: aRouter['ajax'] + "media/upload/",
remove_file: aRouter['ajax'] + "media/remove-file/",
create_preview_file: aRouter['ajax'] + "media/create-preview-file/",
remove_preview_file: aRouter['ajax'] + "media/remove-preview-file/",
load_gallery: aRouter['ajax'] + "media/load-gallery/",
generate_target_tmp: aRouter['ajax'] + "media/generate-target-tmp/",
submit_insert: aRouter['ajax'] + "media/submit-insert/",
@ -115,6 +120,8 @@ ls.media = (function ($) {
info: {
self: $(this.options.selectors.info.self),
remove: $(this.options.selectors.info.remove),
create_preview: $(this.options.selectors.info.create_preview),
remove_preview: $(this.options.selectors.info.remove_preview),
sizes: $(this.options.selectors.info.sizes),
empty: $(this.options.selectors.info.empty)
}
@ -264,6 +271,18 @@ ls.media = (function ($) {
e.preventDefault();
}.bind(this));
// Создание превью из файла
this.elements.info.create_preview.on('click', function(e) {
ls.media.createPreviewActiveFile();
e.preventDefault();
}.bind(this));
// Удаление превью
this.elements.info.remove_preview.on('click', function(e) {
ls.media.removePreviewActiveFile();
e.preventDefault();
}.bind(this));
// После показа модального подгружаем контент
this.elements.modal.on('modalaftershow',function(){
this.loadImageList();
@ -411,6 +430,66 @@ ls.media = (function ($) {
});
};
/**
* Создание превью из активного файла
*/
this.createPreviewActiveFile = function() {
var item = this.getActive();
return item.length ? this.createPreviewFile(item.data('mediaId')) : false;
};
/**
* Создание превью
*
* @param {Number} id
*/
this.createPreviewFile = function(id) {
var _this = this;
ls.ajax.load(this.options.routers.create_preview_file, { id: id, target_type: this.options.target_type, target_id: this.options.target_id, target_tmp: this.options.target_tmp }, function(result) {
if (result.bStateError) {
ls.msg.error(null, result.sMsg);
} else {
if (result.bUnsetOther) {
$(_this.options.selectors.gallery.file).data('mediaRelationIsPreview',0);
}
var item=$(_this.options.selectors.gallery.file + '[data-media-id=' + id + ']');
item.data('mediaRelationIsPreview',1);
/**
* Обновляем отображение информации
*/
this.showDetail(item);
}
}.bind(this));
};
/**
* Удаление превью у активного файла
*/
this.removePreviewActiveFile = function() {
var item = this.getActive();
return item.length ? this.removePreviewFile(item.data('mediaId')) : false;
};
/**
* Удаление превью
*
* @param {Number} id
*/
this.removePreviewFile = function(id) {
var _this = this;
ls.ajax.load(this.options.routers.remove_preview_file, { id: id, target_type: this.options.target_type, target_id: this.options.target_id, target_tmp: this.options.target_tmp }, function(result) {
if (result.bStateError) {
ls.msg.error(null, result.sMsg);
} else {
}
});
};
/**
* Подгрузка списка файлов
*/
@ -601,6 +680,14 @@ ls.media = (function ($) {
$('.js-media-detail-area .js-input-title').val(item.data('mediaDataTitle'));
$('.js-media-detail-area').show();
if (item.data('mediaRelationIsPreview')) {
$('.js-media-item-info-create-preview').hide();
$('.js-media-item-info-remove-preview').show();
} else {
$('.js-media-item-info-create-preview').show();
$('.js-media-item-info-remove-preview').hide();
}
this.showSettingsMode(item);
};

View file

@ -4,6 +4,7 @@
* @styles css/modals.css
*}
{$oRelationTarget=$oMediaItem->getRelationTarget()}
<li class="media-gallery-list-item {if $bIsSelected}is-selected{/if} {if $bIsActive}active{/if} js-media-upload-gallery-item"
data-media-id="{$oMediaItem->getId()}"
data-media-type="{$oMediaItem->getType()}"
@ -14,7 +15,9 @@
data-media-width="{$oMediaItem->getWidth()}"
data-media-height="{$oMediaItem->getHeight()}"
data-media-preview="{$oMediaItem->getFileWebPath('100crop')}"
data-media-image-sizes={json var=$oMediaItem->getDataOne('image_sizes')}>
data-media-image-sizes={json var=$oMediaItem->getDataOne('image_sizes')}
data-media-relation-is-preview={json var=$oRelationTarget->getIsPreview()}
data-media-relation-is-preview3={json var=$oRelationTarget->getIsPreview()} >
<img src="{$oMediaItem->getFileWebPath('100crop')}" alt="Image">
<input id="checkbox_02" type="checkbox">

View file

@ -31,7 +31,14 @@
<li><strong class="word-wrap js-media-detail-name"></strong></li>
<li class="js-media-detail-date"></li>
<li><span class="js-media-detail-dimensions"></span>, <span class="js-media-detail-file-size"></span></li>
<li><a href="#" class="link-dotted js-media-item-info-remove">{$aLang.delete}</a></li>
<li>
<a href="#" class="link-dotted js-media-item-info-remove">{$aLang.delete}</a>
&nbsp;&nbsp;
{if $aTargetParams.allow_preview}
<a href="#" class="link-dotted js-media-item-info-create-preview">Создать превью</a>
<a href="#" class="link-dotted js-media-item-info-remove-preview">Удалить превью</a>
{/if}
</li>
</ul>
{* Описание *}

View file

@ -20,9 +20,12 @@
[ 'value' => 'center', 'text' => $aLang.uploadimg_align_center ]
]}
{$aTargetParams=$LS->Media_GetTargetTypeParams($sMediaTargetType)}
<script type="text/javascript">
jQuery(function($){
ls.media.init({
target_params: {json var=$aTargetParams},
target_type: {json var=$sMediaTargetType},
target_id: {json var=$sMediaTargetId},
target_tmp: {json var=$sMediaTargetTmp}

@ -1 +1 @@
Subproject commit bb1e9297a361f7cee3391029cb53cb6bac5ecba7
Subproject commit 54177f20c44f65c47ffce2f0c27028f74f3fd6fd

View file

@ -493,4 +493,10 @@ ADD INDEX ( `value_date` ) ;
-- 17.02.2014
ALTER TABLE `prefix_media` ADD `target_type` VARCHAR( 50 ) NOT NULL AFTER `type` ,
ADD INDEX ( `target_type` ) ;
ADD INDEX ( `target_type` ) ;
-- 21.03.2014
ALTER TABLE `prefix_media_target` ADD `is_preview` TINYINT( 1 ) NOT NULL DEFAULT '0',
ADD INDEX ( `is_preview` ) ;
ALTER TABLE `prefix_media_target` ADD `data` TEXT NOT NULL ;